From 41df25dd623f76844d64f64670b8ff8b87291f6c Mon Sep 17 00:00:00 2001 From: pei-mysten <147538877+pei-mysten@users.noreply.github.com> Date: Thu, 25 Jul 2024 20:09:06 -0700 Subject: [PATCH 001/232] Sui v1.30.0 snapshot (#18809) ## Description Sui version bump v1.30.0 --- ...00000000000000000000000000000000000000000001 | Bin 0 -> 11210 bytes ...00000000000000000000000000000000000000000002 | Bin 0 -> 66623 bytes ...00000000000000000000000000000000000000000003 | Bin 0 -> 41861 bytes ...0000000000000000000000000000000000000000000b | Bin 0 -> 19848 bytes ...0000000000000000000000000000000000000000dee9 | Bin 0 -> 33346 bytes crates/sui-framework-snapshot/manifest.json | 10 ++++++++++ 6 files changed, 10 insertions(+) create mode 100644 crates/sui-framework-snapshot/bytecode_snapshot/53/0x0000000000000000000000000000000000000000000000000000000000000001 create mode 100644 crates/sui-framework-snapshot/bytecode_snapshot/53/0x0000000000000000000000000000000000000000000000000000000000000002 create mode 100644 crates/sui-framework-snapshot/bytecode_snapshot/53/0x0000000000000000000000000000000000000000000000000000000000000003 create mode 100644 crates/sui-framework-snapshot/bytecode_snapshot/53/0x000000000000000000000000000000000000000000000000000000000000000b create mode 100644 crates/sui-framework-snapshot/bytecode_snapshot/53/0x000000000000000000000000000000000000000000000000000000000000dee9 diff --git a/crates/sui-framework-snapshot/bytecode_snapshot/53/0x0000000000000000000000000000000000000000000000000000000000000001 b/crates/sui-framework-snapshot/bytecode_snapshot/53/0x0000000000000000000000000000000000000000000000000000000000000001 new file mode 100644 index 0000000000000000000000000000000000000000..6aae601648984df646a52a28b178d6962f81f810 GIT binary patch literal 11210 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 i4=2YVB+y1J{Zsvf2h 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 literal 0 HcmV?d00001 diff --git a/crates/sui-framework-snapshot/bytecode_snapshot/53/0x0000000000000000000000000000000000000000000000000000000000000003 b/crates/sui-framework-snapshot/bytecode_snapshot/53/0x0000000000000000000000000000000000000000000000000000000000000003 new file mode 100644 index 0000000000000000000000000000000000000000..f5e57b11051a4720bd5f640d5007c4da8d763695 GIT binary patch literal 41861 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 ZfizyVoN2~tCSB5{(N|xkg7^EW{~yrTbHD%q literal 0 HcmV?d00001 diff --git a/crates/sui-framework-snapshot/bytecode_snapshot/53/0x000000000000000000000000000000000000000000000000000000000000000b b/crates/sui-framework-snapshot/bytecode_snapshot/53/0x000000000000000000000000000000000000000000000000000000000000000b new file mode 100644 index 0000000000000000000000000000000000000000..7a2443d5f54132e627ef68943f13b9a7ad506532 GIT binary patch literal 19848 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 J_lb)8{|#>VMj`+J literal 0 HcmV?d00001 diff --git a/crates/sui-framework-snapshot/bytecode_snapshot/53/0x000000000000000000000000000000000000000000000000000000000000dee9 b/crates/sui-framework-snapshot/bytecode_snapshot/53/0x000000000000000000000000000000000000000000000000000000000000dee9 new file mode 100644 index 0000000000000000000000000000000000000000..d6568ff2626faa32de6303366338f09330d01be9 GIT binary patch literal 33346 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ˈp`w;Dcw~u(su6h5*!rk&2tjk=>$W8Ef3H+7; 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 Date: Thu, 25 Jul 2024 21:06:17 -0700 Subject: [PATCH 002/232] Sui Version Bump v1.31 (#18810) ## Description Sui Version Bump v1.31 --- Cargo.lock | 112 +++++++++++++------------- Cargo.toml | 2 +- crates/sui-open-rpc/spec/openrpc.json | 2 +- 3 files changed, 58 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3d2e77e5b25e9..272aa06ace5f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1824,7 +1824,7 @@ checksum = "bc0455254eb5c6964c4545d8bac815e1a1be4f3afe0ae695ea539c12d728d44b" [[package]] name = "bin-version" -version = "1.30.0" +version = "1.31.0" dependencies = [ "const-str", "git-version", @@ -12075,7 +12075,7 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" [[package]] name = "sui" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anemo", "anyhow", @@ -12146,7 +12146,7 @@ dependencies = [ "sui-package-management", "sui-protocol-config", "sui-replay", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-simulator", "sui-source-validation", "sui-swarm", @@ -12290,7 +12290,7 @@ dependencies = [ [[package]] name = "sui-analytics-indexer" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "arrow", @@ -12344,7 +12344,7 @@ dependencies = [ [[package]] name = "sui-analytics-indexer-derive" -version = "1.30.0" +version = "1.31.0" dependencies = [ "proc-macro2 1.0.78", "quote 1.0.35", @@ -12353,7 +12353,7 @@ dependencies = [ [[package]] name = "sui-archival" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "byteorder", @@ -12461,7 +12461,7 @@ dependencies = [ "sui-macros", "sui-network", "sui-protocol-config", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-simulator", "sui-storage", "sui-surfer", @@ -12479,7 +12479,7 @@ dependencies = [ [[package]] name = "sui-bridge" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "arc-swap", @@ -12514,7 +12514,7 @@ dependencies = [ "sui-json-rpc-api", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-test-transaction-builder", "sui-types", "tap", @@ -12530,7 +12530,7 @@ dependencies = [ [[package]] name = "sui-bridge-cli" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "clap", @@ -12547,7 +12547,7 @@ dependencies = [ "sui-config", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-types", "telemetry-subscribers", "tokio", @@ -12576,7 +12576,7 @@ dependencies = [ "sui-config", "sui-data-ingestion-core", "sui-json-rpc-types", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-test-transaction-builder", "sui-types", "tap", @@ -12588,7 +12588,7 @@ dependencies = [ [[package]] name = "sui-cluster-test" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "async-trait", @@ -12612,7 +12612,7 @@ dependencies = [ "sui-json", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-swarm", "sui-swarm-config", "sui-test-transaction-builder", @@ -12788,7 +12788,7 @@ dependencies = [ [[package]] name = "sui-data-ingestion" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "async-trait", @@ -12850,7 +12850,7 @@ dependencies = [ [[package]] name = "sui-e2e-tests" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "assert_cmd", @@ -12894,7 +12894,7 @@ dependencies = [ "sui-node", "sui-protocol-config", "sui-rest-api", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-simulator", "sui-storage", "sui-swarm", @@ -12967,7 +12967,7 @@ dependencies = [ [[package]] name = "sui-faucet" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "async-recursion", @@ -12987,7 +12987,7 @@ dependencies = [ "sui-config", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-types", "tap", "telemetry-subscribers", @@ -13025,7 +13025,7 @@ dependencies = [ [[package]] name = "sui-framework-snapshot" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "bcs", @@ -13087,7 +13087,7 @@ dependencies = [ [[package]] name = "sui-graphql-config" -version = "1.30.0" +version = "1.31.0" dependencies = [ "quote 1.0.35", "syn 1.0.107", @@ -13164,7 +13164,7 @@ dependencies = [ "sui-package-resolver", "sui-protocol-config", "sui-rest-api", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-swarm-config", "sui-test-transaction-builder", "sui-types", @@ -13204,7 +13204,7 @@ dependencies = [ [[package]] name = "sui-indexer" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "async-trait", @@ -13247,7 +13247,7 @@ dependencies = [ "sui-package-resolver", "sui-protocol-config", "sui-rest-api", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-test-transaction-builder", "sui-transaction-builder", "sui-types", @@ -13380,7 +13380,7 @@ dependencies = [ "sui-open-rpc", "sui-open-rpc-macros", "sui-protocol-config", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-simulator", "sui-swarm-config", "sui-test-transaction-builder", @@ -13441,7 +13441,7 @@ dependencies = [ [[package]] name = "sui-light-client" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "async-trait", @@ -13458,7 +13458,7 @@ dependencies = [ "sui-json-rpc-types", "sui-package-resolver", "sui-rest-api", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-types", "tokio", ] @@ -13475,7 +13475,7 @@ dependencies = [ [[package]] name = "sui-metric-checker" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "backoff", @@ -13496,7 +13496,7 @@ dependencies = [ [[package]] name = "sui-move" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "assert_cmd", @@ -13538,7 +13538,7 @@ dependencies = [ [[package]] name = "sui-move-build" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "datatest-stable", @@ -13562,7 +13562,7 @@ dependencies = [ [[package]] name = "sui-move-lsp" -version = "1.30.0" +version = "1.31.0" dependencies = [ "bin-version", "clap", @@ -13692,7 +13692,7 @@ dependencies = [ [[package]] name = "sui-node" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anemo", "anemo-tower", @@ -13743,7 +13743,7 @@ dependencies = [ [[package]] name = "sui-open-rpc" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "bcs", @@ -13779,7 +13779,7 @@ dependencies = [ [[package]] name = "sui-oracle" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "bcs", @@ -13799,7 +13799,7 @@ dependencies = [ "sui-json-rpc-types", "sui-keys", "sui-move-build", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-types", "tap", "telemetry-subscribers", @@ -13809,14 +13809,14 @@ dependencies = [ [[package]] name = "sui-package-management" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "move-core-types", "move-package", "move-symbol-pool", "sui-json-rpc-types", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-types", "tracing", ] @@ -13958,7 +13958,7 @@ dependencies = [ "sui-json-rpc-api", "sui-json-rpc-types", "sui-protocol-config", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-storage", "sui-transaction-checks", "sui-types", @@ -14002,7 +14002,7 @@ dependencies = [ [[package]] name = "sui-rosetta" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "async-trait", @@ -14030,7 +14030,7 @@ dependencies = [ "sui-keys", "sui-move-build", "sui-node", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-swarm-config", "sui-types", "telemetry-subscribers", @@ -14044,7 +14044,7 @@ dependencies = [ [[package]] name = "sui-rpc-loadgen" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "async-trait", @@ -14062,7 +14062,7 @@ dependencies = [ "sui-json-rpc", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-types", "telemetry-subscribers", "test-cluster", @@ -14093,7 +14093,7 @@ dependencies = [ [[package]] name = "sui-sdk" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "async-recursion", @@ -14129,7 +14129,7 @@ dependencies = [ [[package]] name = "sui-security-watchdog" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "arrow-array", @@ -14176,7 +14176,7 @@ dependencies = [ [[package]] name = "sui-single-node-benchmark" -version = "1.30.0" +version = "1.31.0" dependencies = [ "async-trait", "bcs", @@ -14238,7 +14238,7 @@ dependencies = [ [[package]] name = "sui-source-validation" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "colored", @@ -14255,7 +14255,7 @@ dependencies = [ "rand 0.8.5", "sui-json-rpc-types", "sui-move-build", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-test-transaction-builder", "sui-types", "tar", @@ -14291,7 +14291,7 @@ dependencies = [ "sui-json-rpc-types", "sui-move", "sui-move-build", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-source-validation", "telemetry-subscribers", "tempfile", @@ -14363,7 +14363,7 @@ dependencies = [ [[package]] name = "sui-surfer" -version = "1.30.0" +version = "1.31.0" dependencies = [ "async-trait", "bcs", @@ -14461,13 +14461,13 @@ dependencies = [ "shared-crypto", "sui-genesis-builder", "sui-move-build", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-types", ] [[package]] name = "sui-test-validator" -version = "1.30.0" +version = "1.31.0" [[package]] name = "sui-tls" @@ -14492,7 +14492,7 @@ dependencies = [ [[package]] name = "sui-tool" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anemo", "anemo-cli", @@ -14528,7 +14528,7 @@ dependencies = [ "sui-network", "sui-protocol-config", "sui-replay", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-snapshot", "sui-storage", "sui-types", @@ -14782,7 +14782,7 @@ dependencies = [ [[package]] name = "suins-indexer" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "async-trait", @@ -15140,7 +15140,7 @@ dependencies = [ "sui-macros", "sui-node", "sui-protocol-config", - "sui-sdk 1.30.0", + "sui-sdk 1.31.0", "sui-simulator", "sui-swarm", "sui-swarm-config", @@ -16904,7 +16904,7 @@ dependencies = [ [[package]] name = "x" -version = "1.30.0" +version = "1.31.0" dependencies = [ "anyhow", "camino", diff --git a/Cargo.toml b/Cargo.toml index eae984ca47082..cc92f12a0c4a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -198,7 +198,7 @@ members = [ [workspace.package] # This version string will be inherited by sui-core, sui-faucet, sui-node, sui-tools, sui-sdk, sui-move-build, and sui crates. -version = "1.30.0" +version = "1.31.0" [profile.release] # debug = 1 means line charts only, which is minimum needed for good stack traces diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index 244d7016f3af5..8a50f8f5a8cf5 100644 --- a/crates/sui-open-rpc/spec/openrpc.json +++ b/crates/sui-open-rpc/spec/openrpc.json @@ -12,7 +12,7 @@ "name": "Apache-2.0", "url": "https://raw.githubusercontent.com/MystenLabs/sui/main/LICENSE" }, - "version": "1.30.0" + "version": "1.31.0" }, "methods": [ { From 07446bac38a8a3892633069e9a1deb683052d42e Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 24 Jul 2024 09:54:42 -0500 Subject: [PATCH 003/232] chore: update hyper et al Update a number of http related libraries, including but not limited to: - hyper - rustls - axum --- Cargo.lock | 594 +++++++++--------- Cargo.toml | 44 +- consensus/core/Cargo.toml | 1 + consensus/core/src/network/tonic_network.rs | 31 +- consensus/core/src/network/tonic_tls.rs | 1 + crates/mysten-metrics/src/lib.rs | 4 +- crates/mysten-network/src/server.rs | 8 +- crates/mysten-service/src/service.rs | 8 +- .../mysten-service/tests/integration_test.rs | 5 +- crates/sui-bridge/src/server/mock_handler.rs | 14 +- crates/sui-bridge/src/server/mod.rs | 11 +- .../traffic_controller/nodefw_test_server.rs | 11 +- crates/sui-faucet/src/server.rs | 5 +- .../tests/call/simple.exp | 4 +- crates/sui-graphql-rpc/Cargo.toml | 1 + .../schema/current_progress_schema.graphql | 2 + crates/sui-graphql-rpc/src/server/builder.rs | 41 +- crates/sui-graphql-rpc/src/server/version.rs | 19 +- .../snapshot_tests__schema_sdl_export.snap | 2 + crates/sui-indexer/src/metrics.rs | 6 +- .../sui-json-rpc-tests/tests/routing_tests.rs | 3 +- crates/sui-json-rpc/Cargo.toml | 1 + crates/sui-json-rpc/src/axum_router.rs | 9 +- crates/sui-json-rpc/src/lib.rs | 19 +- crates/sui-json-rpc/src/metrics.rs | 2 +- crates/sui-node/src/admin.rs | 11 +- crates/sui-node/src/lib.rs | 16 +- crates/sui-proxy/Cargo.toml | 2 +- crates/sui-proxy/src/admin.rs | 14 +- crates/sui-proxy/src/histogram_relay.rs | 10 +- crates/sui-proxy/src/lib.rs | 8 +- crates/sui-proxy/src/metrics.rs | 10 +- crates/sui-proxy/src/middleware.rs | 33 +- crates/sui-rest-api/Cargo.toml | 2 +- crates/sui-rest-api/src/accept.rs | 6 +- crates/sui-rest-api/src/lib.rs | 10 +- crates/sui-rest-api/src/response.rs | 10 +- crates/sui-rosetta/src/lib.rs | 21 +- crates/sui-rosetta/src/main.rs | 6 +- crates/sui-rosetta/tests/rosetta_client.rs | 16 +- .../sui-source-validation-service/Cargo.toml | 2 +- .../sui-source-validation-service/src/lib.rs | 30 +- .../sui-source-validation-service/src/main.rs | 2 +- .../tests/tests.rs | 3 +- crates/sui-storage/Cargo.toml | 1 + crates/sui-storage/tests/key_value_tests.rs | 60 +- crates/sui-swarm/src/memory/container.rs | 7 +- crates/sui-swarm/src/memory/node.rs | 1 + crates/sui-swarm/src/memory/swarm.rs | 2 + crates/sui-tls/src/acceptor.rs | 5 +- crates/sui-tls/src/certgen.rs | 71 +-- crates/sui-tls/src/lib.rs | 53 +- crates/sui-tls/src/verifier.rs | 170 +++-- crates/suiop-cli/src/cli/lib/oauth/mod.rs | 5 +- deny.toml | 5 - narwhal/network/Cargo.toml | 1 - narwhal/network/src/admin.rs | 22 +- narwhal/node/src/metrics.rs | 6 +- .../tests/nodes_bootstrapping_tests.rs | 7 +- 59 files changed, 765 insertions(+), 709 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 272aa06ace5f6..56bf0f8753d44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -176,7 +176,7 @@ dependencies = [ "quinn", "quinn-proto", "rand 0.8.5", - "rcgen 0.13.1", + "rcgen", "ring 0.17.3", "rustls 0.23.12", "rustls-webpki 0.102.6", @@ -828,38 +828,27 @@ dependencies = [ "wait-timeout", ] -[[package]] -name = "async-compression" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" -dependencies = [ - "brotli 3.3.4", - "flate2", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", -] - [[package]] name = "async-compression" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a116f46a969224200a0a97f29cfd4c50e7534e4b4826bd23ea2c3c533039c82c" dependencies = [ + "brotli 3.3.4", "flate2", "futures-core", "memchr", "pin-project-lite", "tokio", + "zstd 0.13.0", + "zstd-safe 7.0.0", ] [[package]] name = "async-graphql" -version = "6.0.7" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1addb0b551c59640e15de99e7566a4e3a1186cf42269e160c485ba6d8b43fe30" +checksum = "b16926f97f683ff3b47b035cc79622f3d6a374730b07a5d9051e81e88b5f1904" dependencies = [ "async-graphql-derive", "async-graphql-parser", @@ -875,20 +864,20 @@ dependencies = [ "futures-timer", "futures-util", "handlebars", - "http 0.2.9", + "http 1.1.0", "indexmap 2.2.6", "lru 0.7.8", "mime", "multer", "num-traits", "once_cell", - "opentelemetry 0.19.0", + "opentelemetry 0.21.0", "pin-project-lite", "regex", "serde", "serde_json", "serde_urlencoded", - "static_assertions", + "static_assertions_next", "tempfile", "thiserror", "tracing", @@ -897,13 +886,13 @@ dependencies = [ [[package]] name = "async-graphql-axum" -version = "6.0.7" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c21af134ab9419aae6658298f819a28e4737ac81f96cde8008f9d49db1802662" +checksum = "de3415c9dbaf54397292da0bb81a907e2b989661ce068e4ccfebac33dc9e245e" dependencies = [ "async-graphql", "async-trait", - "axum", + "axum 0.7.5", "bytes", "futures-util", "serde_json", @@ -915,9 +904,9 @@ dependencies = [ [[package]] name = "async-graphql-derive" -version = "6.0.7" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e1121ff0be2feea705c24f6940162c4f14a077e50a217b16e091e6534a8c08a" +checksum = "a6a7349168b79030e3172a620f4f0e0062268a954604e41475eff082380fe505" dependencies = [ "Inflector", "async-graphql-parser", @@ -932,9 +921,9 @@ dependencies = [ [[package]] name = "async-graphql-parser" -version = "6.0.7" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0b6713fd4ffd610b8b6f6e911bf31277cbb84b7c2a9cdeeb39d1b3eed3b88e4" +checksum = "58fdc0adf9f53c2b65bb0ff5170cba1912299f248d0e48266f444b6f005deb1d" dependencies = [ "async-graphql-value", "pest", @@ -944,9 +933,9 @@ dependencies = [ [[package]] name = "async-graphql-value" -version = "6.0.7" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d74240f9daa8c1e8f73e9cfcc338d20a88d00bbeb83ded49ce8e5b4dcec0f5" +checksum = "7cf4d4e86208f4f9b81a503943c07e6e7f29ad3505e6c9ce6431fe64dc241681" dependencies = [ "bytes", "indexmap 2.2.6", @@ -1552,12 +1541,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", - "axum-core", - "base64 0.21.2", + "axum-core 0.3.4", "bitflags 1.3.2", "bytes", "futures-util", - "headers", "http 0.2.9", "http-body 0.4.5", "hyper 0.14.26", @@ -1569,16 +1556,47 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", + "sync_wrapper 0.1.2", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core 0.4.3", + "base64 0.21.7", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "itoa", + "matchit 0.7.0", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", "serde_json", "serde_path_to_error", "serde_urlencoded", "sha1", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tokio", "tokio-tungstenite", "tower", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -1598,42 +1616,70 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "axum-extra" -version = "0.4.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a320103719de37b7b4da4c8eb629d4573f6bcfd3dfe80d3208806895ccf81d" +checksum = "0be6ea09c9b96cb5076af0de2e383bd2bc0c18f827cf1967bdd353e0b910d733" dependencies = [ - "axum", + "axum 0.7.5", + "axum-core 0.4.3", "bytes", "futures-util", - "http 0.2.9", + "headers", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", "mime", "pin-project-lite", - "tokio", + "serde", "tower", - "tower-http", "tower-layer", "tower-service", + "tracing", ] [[package]] name = "axum-server" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447f28c85900215cc1bea282f32d4a2f22d55c5a300afdfbc661c8d6a632e063" +version = "0.6.1" +source = "git+https://github.com/bmwill/axum-server.git?rev=f44323e271afdd1365fd0c8b0a4c0bbdf4956cb7#f44323e271afdd1365fd0c8b0a4c0bbdf4956cb7" dependencies = [ "arc-swap", "bytes", "futures-util", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.26", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-util", "pin-project-lite", - "rustls 0.21.12", - "rustls-pemfile 1.0.2", + "rustls 0.23.12", + "rustls-pemfile 2.1.2", + "rustls-pki-types", "tokio", - "tokio-rustls 0.24.0", + "tokio-rustls 0.26.0", + "tower", "tower-service", ] @@ -1701,9 +1747,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.2" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" @@ -1727,7 +1773,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c5b0a88aa36e9f095ee2e2b13fb8c5e4313e022783aedacc123328c0084916d" dependencies = [ - "base64 0.21.2", + "base64 0.21.7", ] [[package]] @@ -2528,7 +2574,7 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" dependencies = [ - "base64 0.21.2", + "base64 0.21.7", "bech32", "bs58 0.5.0", "digest 0.10.7", @@ -2647,7 +2693,7 @@ dependencies = [ "anyhow", "arc-swap", "async-trait", - "base64 0.21.2", + "base64 0.21.7", "bcs", "bytes", "cfg-if", @@ -2656,9 +2702,10 @@ dependencies = [ "enum_dispatch", "fastcrypto", "futures", - "http 0.2.9", - "hyper 0.14.26", - "hyper-rustls 0.24.0", + "http 1.1.0", + "hyper 1.4.1", + "hyper-rustls 0.27.2", + "hyper-util", "itertools 0.10.5", "mockall", "mysten-common", @@ -2667,11 +2714,11 @@ dependencies = [ "nom", "parking_lot 0.12.1", "prometheus", - "prost 0.12.3", + "prost 0.13.1", "quinn-proto", "rand 0.8.5", "rstest", - "rustls 0.21.12", + "rustls 0.23.12", "serde", "shared-crypto", "strum_macros 0.24.3", @@ -2683,10 +2730,10 @@ dependencies = [ "tempfile", "thiserror", "tokio", - "tokio-rustls 0.24.0", + "tokio-rustls 0.26.0", "tokio-stream", "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tonic 0.11.0", + "tonic 0.12.1", "tonic-build", "tower", "tower-http", @@ -2715,7 +2762,7 @@ checksum = "fd326812b3fd01da5bb1af7d340d0d555fd3d4b641e7f1dfcf5962a902952787" dependencies = [ "futures-core", "prost 0.12.3", - "prost-types", + "prost-types 0.12.3", "tonic 0.10.0", "tracing-core", ] @@ -2732,7 +2779,7 @@ dependencies = [ "futures-task", "hdrhistogram", "humantime", - "prost-types", + "prost-types 0.12.3", "serde", "serde_json", "thread_local", @@ -4331,7 +4378,7 @@ checksum = "c2fa0857eaad0c1678f982a2f4cfbe33ebd51d273cc93de0182b7c693f2a84a1" dependencies = [ "async-trait", "auto_impl", - "base64 0.21.2", + "base64 0.21.7", "bytes", "enr", "ethers-core", @@ -5293,15 +5340,14 @@ dependencies = [ [[package]] name = "headers" -version = "0.3.8" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", + "base64 0.21.7", "bytes", "headers-core", - "http 0.2.9", + "http 1.1.0", "httpdate", "mime", "sha1", @@ -5309,11 +5355,11 @@ dependencies = [ [[package]] name = "headers-core" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http 0.2.9", + "http 1.1.0", ] [[package]] @@ -5465,9 +5511,9 @@ dependencies = [ [[package]] name = "http-range-header" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" +checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" [[package]] name = "httparse" @@ -5524,6 +5570,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -5560,7 +5607,6 @@ dependencies = [ "rustls-native-certs 0.6.2", "tokio", "tokio-rustls 0.24.0", - "webpki-roots 0.23.1", ] [[package]] @@ -5594,6 +5640,19 @@ dependencies = [ "tokio-io-timeout", ] +[[package]] +name = "hyper-timeout" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +dependencies = [ + "hyper 1.4.1", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.6" @@ -5910,11 +5969,12 @@ dependencies = [ [[package]] name = "iri-string" -version = "0.4.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f0f7638c1e223529f1bfdc48c8b133b9e0b434094d1d28473161ee48b235f78" +checksum = "7f5f6c2df22c009ac44f6f1499308e7a3ac7ba42cd2378475cc691510e1eef1b" dependencies = [ - "nom", + "memchr", + "serde", ] [[package]] @@ -6204,7 +6264,7 @@ version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ - "base64 0.21.2", + "base64 0.21.7", "pem 1.1.0", "ring 0.16.20", "serde", @@ -7582,16 +7642,15 @@ dependencies = [ [[package]] name = "multer" -version = "2.1.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 0.2.9", + "http 1.1.0", "httparse", - "log", "memchr", "mime", "spin 0.9.8", @@ -7682,7 +7741,7 @@ name = "mysten-metrics" version = "0.7.0" dependencies = [ "async-trait", - "axum", + "axum 0.7.5", "dashmap", "futures", "once_cell", @@ -7705,14 +7764,14 @@ dependencies = [ "bytes", "eyre", "futures", - "http 0.2.9", + "http 1.1.0", "multiaddr", "pin-project-lite", "serde", "snap", "tokio", "tokio-stream", - "tonic 0.11.0", + "tonic 0.12.1", "tonic-health", "tower", "tower-http", @@ -7724,7 +7783,7 @@ name = "mysten-service" version = "0.0.1" dependencies = [ "anyhow", - "axum", + "axum 0.7.5", "mysten-metrics", "prometheus", "serde", @@ -7846,7 +7905,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", - "tonic 0.11.0", + "tonic 0.12.1", "tracing", "typed-store", ] @@ -7859,8 +7918,7 @@ dependencies = [ "anemo-tower", "anyhow", "async-trait", - "axum", - "axum-server", + "axum 0.7.5", "backoff", "bincode", "bytes", @@ -7888,7 +7946,7 @@ dependencies = [ "anemo", "arc-swap", "async-trait", - "axum", + "axum 0.7.5", "bytes", "cfg-if", "clap", @@ -8027,7 +8085,7 @@ dependencies = [ "telemetry-subscribers", "tempfile", "tokio", - "tonic 0.11.0", + "tonic 0.12.1", "tracing", "typed-store", ] @@ -8039,7 +8097,7 @@ dependencies = [ "anemo", "anemo-build", "anyhow", - "base64 0.21.2", + "base64 0.21.7", "bcs", "bytes", "criterion", @@ -8060,7 +8118,7 @@ dependencies = [ "prometheus", "proptest", "proptest-derive", - "prost 0.12.3", + "prost 0.13.1", "prost-build", "protobuf-src", "rand 0.8.5", @@ -8072,7 +8130,7 @@ dependencies = [ "sui-protocol-config", "thiserror", "tokio", - "tonic 0.11.0", + "tonic 0.12.1", "tonic-build", "tracing", "typed-store", @@ -8113,7 +8171,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", - "tonic 0.11.0", + "tonic 0.12.1", "tower", "tracing", "typed-store", @@ -8655,22 +8713,28 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "opentelemetry" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4b8347cc26099d3aeee044065ecc3ae11469796b4d65d065a23a584ed92a6f" +checksum = "9591d937bc0e6d2feb6f71a559540ab300ea49955229c347a517a28d27784c54" dependencies = [ - "opentelemetry_api 0.19.0", - "opentelemetry_sdk 0.19.0", + "opentelemetry_api", + "opentelemetry_sdk", ] [[package]] name = "opentelemetry" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9591d937bc0e6d2feb6f71a559540ab300ea49955229c347a517a28d27784c54" +checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" dependencies = [ - "opentelemetry_api 0.20.0", - "opentelemetry_sdk 0.20.0", + "futures-core", + "futures-sink", + "indexmap 2.2.6", + "js-sys", + "once_cell", + "pin-project-lite", + "thiserror", + "urlencoding", ] [[package]] @@ -8684,8 +8748,8 @@ dependencies = [ "http 0.2.9", "opentelemetry-proto", "opentelemetry-semantic-conventions", - "opentelemetry_api 0.20.0", - "opentelemetry_sdk 0.20.0", + "opentelemetry_api", + "opentelemetry_sdk", "prost 0.11.9", "thiserror", "tokio", @@ -8698,8 +8762,8 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1e3f814aa9f8c905d0ee4bde026afd3b2577a97c10e1699912e3e44f0c4cbeb" dependencies = [ - "opentelemetry_api 0.20.0", - "opentelemetry_sdk 0.20.0", + "opentelemetry_api", + "opentelemetry_sdk", "prost 0.11.9", "tonic 0.9.2", ] @@ -8713,21 +8777,6 @@ dependencies = [ "opentelemetry 0.20.0", ] -[[package]] -name = "opentelemetry_api" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed41783a5bf567688eb38372f2b7a8530f5a607a4b49d38dd7573236c23ca7e2" -dependencies = [ - "futures-channel", - "futures-util", - "indexmap 1.9.3", - "once_cell", - "pin-project-lite", - "thiserror", - "urlencoding", -] - [[package]] name = "opentelemetry_api" version = "0.20.0" @@ -8744,24 +8793,6 @@ dependencies = [ "urlencoding", ] -[[package]] -name = "opentelemetry_sdk" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b3a2a91fdbfdd4d212c0dcc2ab540de2c2bcbbd90be17de7a7daf8822d010c1" -dependencies = [ - "async-trait", - "crossbeam-channel", - "futures-channel", - "futures-executor", - "futures-util", - "once_cell", - "opentelemetry_api 0.19.0", - "percent-encoding", - "rand 0.8.5", - "thiserror", -] - [[package]] name = "opentelemetry_sdk" version = "0.20.0" @@ -8774,7 +8805,7 @@ dependencies = [ "futures-executor", "futures-util", "once_cell", - "opentelemetry_api 0.20.0", + "opentelemetry_api", "ordered-float 3.9.1", "percent-encoding", "rand 0.8.5", @@ -9204,10 +9235,11 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.2" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" +checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" dependencies = [ + "memchr", "thiserror", "ucd-trie", ] @@ -9767,26 +9799,35 @@ dependencies = [ "prost-derive 0.12.3", ] +[[package]] +name = "prost" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc" +dependencies = [ + "bytes", + "prost-derive 0.13.1", +] + [[package]] name = "prost-build" -version = "0.12.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2" +checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1" dependencies = [ "bytes", - "heck 0.4.1", - "itertools 0.11.0", + "heck 0.5.0", + "itertools 0.13.0", "log", "multimap", "once_cell", "petgraph 0.6.2", "prettyplease 0.2.17", - "prost 0.12.3", - "prost-types", + "prost 0.13.1", + "prost-types 0.13.1", "regex", "syn 2.0.48", "tempfile", - "which", ] [[package]] @@ -9815,6 +9856,19 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "prost-derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca" +dependencies = [ + "anyhow", + "itertools 0.13.0", + "proc-macro2 1.0.78", + "quote 1.0.35", + "syn 2.0.48", +] + [[package]] name = "prost-types" version = "0.12.3" @@ -9824,6 +9878,15 @@ dependencies = [ "prost 0.12.3", ] +[[package]] +name = "prost-types" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2" +dependencies = [ + "prost 0.13.1", +] + [[package]] name = "protobuf" version = "2.28.0" @@ -10123,18 +10186,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "rcgen" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" -dependencies = [ - "pem 1.1.0", - "ring 0.16.20", - "time", - "yasna", -] - [[package]] name = "rcgen" version = "0.13.1" @@ -10279,7 +10330,7 @@ version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" dependencies = [ - "base64 0.21.2", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", @@ -10318,7 +10369,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" dependencies = [ - "async-compression 0.4.6", + "async-compression", "base64 0.22.1", "bytes", "futures-channel", @@ -10914,7 +10965,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64 0.21.2", + "base64 0.21.7", ] [[package]] @@ -10933,16 +10984,6 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" -[[package]] -name = "rustls-webpki" -version = "0.100.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", -] - [[package]] name = "rustls-webpki" version = "0.101.7" @@ -11867,7 +11908,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13a6dfdd7c433e0f4bb96d777c88d900c5abe3dc4d2f26d2340fd6c7caadcc6c" dependencies = [ - "base64 0.21.2", + "base64 0.21.7", "jsonwebtoken", "rsa 0.9.1", "serde", @@ -11980,6 +12021,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "static_assertions_next" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7beae5182595e9a8b683fa98c4317f956c9a2dec3b9716990d20023cc60c766" + [[package]] name = "str-buf" version = "1.0.6" @@ -12082,7 +12129,7 @@ dependencies = [ "assert_cmd", "async-recursion", "async-trait", - "axum", + "axum 0.7.5", "bcs", "bin-version", "bip32", @@ -12097,7 +12144,7 @@ dependencies = [ "fastcrypto-zkp", "fs_extra", "futures", - "http 0.2.9", + "http 1.1.0", "im", "inquire", "insta", @@ -12296,7 +12343,7 @@ dependencies = [ "arrow", "arrow-array", "async-trait", - "axum", + "axum 0.7.5", "bcs", "byteorder", "bytes", @@ -12484,7 +12531,7 @@ dependencies = [ "anyhow", "arc-swap", "async-trait", - "axum", + "axum 0.7.5", "backoff", "bcs", "bin-version", @@ -12662,7 +12709,7 @@ dependencies = [ "anyhow", "arc-swap", "async-trait", - "axum", + "axum 0.7.5", "bcs", "bytes", "chrono", @@ -12972,11 +13019,11 @@ dependencies = [ "anyhow", "async-recursion", "async-trait", - "axum", + "axum 0.7.5", "clap", "eyre", "futures", - "http 0.2.9", + "http 1.1.0", "mysten-metrics", "parking_lot 0.12.1", "prometheus", @@ -13113,7 +13160,8 @@ dependencies = [ "async-graphql-axum", "async-graphql-value", "async-trait", - "axum", + "axum 0.7.5", + "axum-extra", "bcs", "bin-version", "chrono", @@ -13127,8 +13175,8 @@ dependencies = [ "fastcrypto-zkp", "futures", "hex", - "http 0.2.9", - "hyper 0.14.26", + "http 1.1.0", + "hyper 1.4.1", "im", "insta", "itertools 0.10.5", @@ -13187,8 +13235,8 @@ name = "sui-graphql-rpc-client" version = "0.1.0" dependencies = [ "async-graphql", - "axum", - "hyper 0.14.26", + "axum 0.7.5", + "hyper 1.4.1", "reqwest 0.12.5", "serde_json", "sui-graphql-rpc-headers", @@ -13199,7 +13247,7 @@ dependencies = [ name = "sui-graphql-rpc-headers" version = "0.1.0" dependencies = [ - "axum", + "axum 0.7.5", ] [[package]] @@ -13208,7 +13256,7 @@ version = "1.31.0" dependencies = [ "anyhow", "async-trait", - "axum", + "axum 0.7.5", "backoff", "bcs", "cached", @@ -13288,7 +13336,7 @@ dependencies = [ "anyhow", "arc-swap", "async-trait", - "axum", + "axum 0.7.5", "bcs", "cached", "chrono", @@ -13296,7 +13344,8 @@ dependencies = [ "eyre", "fastcrypto", "futures", - "hyper 0.14.26", + "http-body 0.4.5", + "hyper 1.4.1", "indexmap 2.2.6", "itertools 0.10.5", "jsonrpsee", @@ -13361,7 +13410,7 @@ dependencies = [ "anyhow", "async-trait", "bcs", - "hyper 0.14.26", + "hyper 1.4.1", "jsonrpsee", "move-core-types", "move-package", @@ -13479,7 +13528,7 @@ version = "1.31.0" dependencies = [ "anyhow", "backoff", - "base64 0.21.2", + "base64 0.21.7", "chrono", "clap", "humantime", @@ -13684,7 +13733,7 @@ dependencies = [ "telemetry-subscribers", "tempfile", "tokio", - "tonic 0.11.0", + "tonic 0.12.1", "tonic-build", "tower", "tracing", @@ -13698,8 +13747,8 @@ dependencies = [ "anemo-tower", "anyhow", "arc-swap", - "axum", - "base64 0.21.2", + "axum 0.7.5", + "base64 0.21.7", "bcs", "bin-version", "clap", @@ -13828,7 +13877,7 @@ dependencies = [ "async-trait", "bcs", "eyre", - "hyper 0.14.26", + "hyper 1.4.1", "insta", "lru 0.10.0", "move-binary-format", @@ -13884,7 +13933,8 @@ name = "sui-proxy" version = "0.0.2" dependencies = [ "anyhow", - "axum", + "axum 0.7.5", + "axum-extra", "axum-server", "bin-version", "bytes", @@ -13892,8 +13942,7 @@ dependencies = [ "const-str", "fastcrypto", "hex", - "http-body 0.4.5", - "hyper 0.14.26", + "hyper 1.4.1", "ipnetwork", "itertools 0.10.5", "mime", @@ -13901,13 +13950,13 @@ dependencies = [ "mysten-metrics", "once_cell", "prometheus", - "prost 0.12.3", + "prost 0.13.1", "prost-build", "protobuf", "rand 0.8.5", "reqwest 0.12.5", - "rustls 0.21.12", - "rustls-pemfile 1.0.2", + "rustls 0.23.12", + "rustls-pemfile 2.1.2", "serde", "serde_json", "serde_with 3.8.1", @@ -13932,7 +13981,7 @@ dependencies = [ "bcs", "clap", "futures", - "http 0.2.9", + "http 1.1.0", "jsonrpsee", "lru 0.10.0", "move-binary-format", @@ -13976,7 +14025,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", - "axum", + "axum 0.7.5", "bcs", "diffy", "fastcrypto", @@ -14006,14 +14055,14 @@ version = "1.31.0" dependencies = [ "anyhow", "async-trait", - "axum", + "axum 0.7.5", "axum-extra", "bcs", "clap", "eyre", "fastcrypto", "futures", - "hyper 0.14.26", + "hyper 1.4.1", "move-core-types", "mysten-metrics", "once_cell", @@ -14067,7 +14116,7 @@ dependencies = [ "telemetry-subscribers", "test-cluster", "tokio", - "tonic 0.11.0", + "tonic 0.12.1", "tracing", ] @@ -14098,7 +14147,7 @@ dependencies = [ "anyhow", "async-recursion", "async-trait", - "base64 0.21.2", + "base64 0.21.7", "bcs", "clap", "colored", @@ -14272,12 +14321,12 @@ name = "sui-source-validation-service" version = "0.1.0" dependencies = [ "anyhow", - "axum", + "axum 0.7.5", "bin-version", "clap", "expect-test", "fs_extra", - "hyper 0.14.26", + "hyper 1.4.1", "jsonrpsee", "move-compiler", "move-core-types", @@ -14310,6 +14359,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "axum 0.7.5", "backoff", "base64-url", "bcs", @@ -14321,8 +14371,8 @@ dependencies = [ "eyre", "fastcrypto", "futures", - "hyper 0.14.26", - "hyper-rustls 0.24.0", + "hyper 1.4.1", + "hyper-rustls 0.27.2", "indicatif", "integer-encoding", "itertools 0.10.5", @@ -14474,18 +14524,18 @@ name = "sui-tls" version = "0.0.0" dependencies = [ "anyhow", - "axum", + "axum 0.7.5", "axum-server", "ed25519 1.5.3", "fastcrypto", "pkcs8 0.9.0", "rand 0.8.5", - "rcgen 0.9.3", + "rcgen", "reqwest 0.12.5", - "rustls 0.21.12", - "rustls-webpki 0.101.7", + "rustls 0.23.12", + "rustls-webpki 0.102.6", "tokio", - "tokio-rustls 0.24.0", + "tokio-rustls 0.26.0", "tower-layer", "x509-parser", ] @@ -14692,7 +14742,7 @@ dependencies = [ "tap", "thiserror", "tokio", - "tonic 0.11.0", + "tonic 0.12.1", "tracing", "typed-store-error", "url", @@ -14819,8 +14869,8 @@ name = "suiop-cli" version = "0.2.5" dependencies = [ "anyhow", - "axum", - "base64 0.21.2", + "axum 0.7.5", + "base64 0.21.7", "chrono", "clap", "colored", @@ -15056,7 +15106,7 @@ dependencies = [ "opentelemetry 0.20.0", "opentelemetry-otlp", "opentelemetry-proto", - "opentelemetry_api 0.20.0", + "opentelemetry_api", "prometheus", "prost 0.11.9", "tokio", @@ -15474,9 +15524,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" dependencies = [ "futures-util", "log", @@ -15600,8 +15650,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" dependencies = [ "async-trait", - "axum", - "base64 0.21.2", + "axum 0.6.20", + "base64 0.21.7", "bytes", "futures-core", "futures-util", @@ -15609,7 +15659,7 @@ dependencies = [ "http 0.2.9", "http-body 0.4.5", "hyper 0.14.26", - "hyper-timeout", + "hyper-timeout 0.4.1", "percent-encoding", "pin-project", "prost 0.11.9", @@ -15629,14 +15679,14 @@ checksum = "5469afaf78a11265c343a88969045c1568aa8ecc6c787dbf756e92e70f199861" dependencies = [ "async-stream", "async-trait", - "axum", - "base64 0.21.2", + "axum 0.6.20", + "base64 0.21.7", "bytes", "h2 0.3.26", "http 0.2.9", "http-body 0.4.5", "hyper 0.14.26", - "hyper-timeout", + "hyper-timeout 0.4.1", "percent-encoding", "pin-project", "prost 0.12.3", @@ -15650,23 +15700,26 @@ dependencies = [ [[package]] name = "tonic" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +checksum = "38659f4a91aba8598d27821589f5db7dddd94601e7a01b1e485a50e5484c7401" dependencies = [ "async-stream", "async-trait", - "axum", - "base64 0.21.2", + "axum 0.7.5", + "base64 0.22.1", "bytes", - "h2 0.3.26", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.26", - "hyper-timeout", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-timeout 0.5.1", + "hyper-util", "percent-encoding", "pin-project", - "prost 0.12.3", + "prost 0.13.1", + "socket2 0.5.6", "tokio", "tokio-stream", "tower", @@ -15677,9 +15730,9 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4ef6dd70a610078cb4e338a0f79d06bc759ff1b22d2120c2ff02ae264ba9c2" +checksum = "568392c5a2bd0020723e3f387891176aabafe36fd9fcd074ad309dfa0c8eb964" dependencies = [ "prettyplease 0.2.17", "proc-macro2 1.0.78", @@ -15690,15 +15743,15 @@ dependencies = [ [[package]] name = "tonic-health" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cef6e24bc96871001a7e48e820ab240b3de2201e59b517cf52835df2f1d2350" +checksum = "e1e10e6a96ee08b6ce443487d4368442d328d0e746f3681f81127f7dc41b4955" dependencies = [ "async-stream", - "prost 0.12.3", + "prost 0.13.1", "tokio", "tokio-stream", - "tonic 0.11.0", + "tonic 0.12.1", ] [[package]] @@ -15737,18 +15790,19 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.3.5" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ - "async-compression 0.3.15", - "base64 0.13.1", - "bitflags 1.3.2", + "async-compression", + "base64 0.21.7", + "bitflags 2.4.1", "bytes", "futures-core", "futures-util", - "http 0.2.9", - "http-body 0.4.5", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", "http-range-header", "httpdate", "iri-string", @@ -15862,7 +15916,7 @@ checksum = "75327c6b667828ddc28f5e3f169036cb793c3f588d83bf0f262a7f062ffed3c8" dependencies = [ "once_cell", "opentelemetry 0.20.0", - "opentelemetry_sdk 0.20.0", + "opentelemetry_sdk", "smallvec", "tracing", "tracing-core", @@ -15972,14 +16026,14 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" dependencies = [ "byteorder", "bytes", "data-encoding", - "http 0.2.9", + "http 1.1.0", "httparse", "log", "rand 0.8.5", @@ -15996,7 +16050,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", - "rand 0.7.3", + "rand 0.8.5", "static_assertions", ] @@ -16226,7 +16280,7 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" dependencies = [ - "base64 0.21.2", + "base64 0.21.7", "flate2", "log", "once_cell", @@ -16534,15 +16588,6 @@ dependencies = [ "webpki", ] -[[package]] -name = "webpki-roots" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" -dependencies = [ - "rustls-webpki 0.100.3", -] - [[package]] name = "webpki-roots" version = "0.25.2" @@ -16558,17 +16603,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "which" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" -dependencies = [ - "either", - "libc", - "once_cell", -] - [[package]] name = "whoami" version = "1.5.0" diff --git a/Cargo.toml b/Cargo.toml index cc92f12a0c4a3..06a78c6547ade 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -241,9 +241,9 @@ arrow = "52" arrow-array = "52" arc-swap = { version = "1.5.1", features = ["serde"] } assert_cmd = "2.0.6" -async-graphql = "6.0.7" -async-graphql-axum = "6.0.7" -async-graphql-value = "6.0.7" +async-graphql = "=7.0.1" +async-graphql-axum = "=7.0.1" +async-graphql-value = "=7.0.1" async-recursion = "1.0.4" async-trait = "0.1.61" atomic_float = "0.1" @@ -253,8 +253,7 @@ aws-sdk-dynamodb = "0.29.0" aws-sdk-s3 = "0.29.0" aws-smithy-http = "0.56" aws-smithy-runtime-api = "0.56" -axum = { version = "0.6.6", default-features = false, features = [ - "headers", +axum = { version = "0.7", default-features = false, features = [ "tokio", "http1", "http2", @@ -265,8 +264,8 @@ axum = { version = "0.6.6", default-features = false, features = [ "query", "ws", ] } -axum-extra = "0.4.2" -axum-server = { version = "0.5.1", default-features = false, features = [ +axum-extra = { version = "0.9", features = ["typed-header"] } +axum-server = { git = "https://github.com/bmwill/axum-server.git", rev = "f44323e271afdd1365fd0c8b0a4c0bbdf4956cb7", version = "0.6", default-features = false, features = [ "tls-rustls", ] } backoff = { version = "0.4.0", features = [ @@ -342,11 +341,12 @@ hdrhistogram = "7.5.1" hex = "0.4.3" hex-literal = "0.3.4" highlight = "all" -http = "0.2.8" -http-body = "0.4.5" +http = "1" +http-body = "1" humantime = "2.1.0" -hyper = "0.14.20" -hyper-rustls = { version = "0.24", features = ["webpki-roots", "http2"] } +hyper = "1" +hyper-util = "0.1.6" +hyper-rustls = { version = "0.27", default-features = false, features = ["webpki-roots", "http2", "ring", "tls12"] } im = "15" impl-trait-for-tuples = "0.2.0" indexmap = { version = "2.1.0", features = ["serde"] } @@ -405,15 +405,15 @@ prometheus-http-query = { version = "0.8", default_features = false, features = prometheus-parse = { git = "https://github.com/asonnino/prometheus-parser.git", rev = "75334db" } proptest = "1.1.0" proptest-derive = "0.3.0" -prost = "0.12.3" -prost-build = "0.12.3" +prost = "0.13" +prost-build = "0.13" protobuf = { version = "2.28", features = ["with-bytes"] } protobuf-src = "1.1.0" quinn-proto = "0.11" quote = "1.0.23" rand = "0.8.5" rayon = "1.5.3" -rcgen = "0.9.2" +rcgen = "0.13" regex = "1.7.1" reqwest = { version = "0.12", default_features = false, features = [ "blocking", @@ -439,8 +439,8 @@ rusoto_kms = { version = "0.48.0", default_features = false, features = [ russh = "0.38.0" russh-keys = "0.38.0" rust-version = "1.56.1" -rustls = { version = "0.21.12", features = ["dangerous_configuration"] } -rustls-pemfile = "1.0.2" +rustls = { version = "0.23", default-features = false, features = ["std", "tls12", "ring"] } +rustls-pemfile = "2" rustversion = "1.0.9" rustyline = "9.1.2" rustyline-derive = "0.7.0" @@ -483,7 +483,7 @@ thiserror = "1.0.40" tiny-bip39 = "1.0.0" tokio = "1.36.0" tokio-retry = "0.3" -tokio-rustls = "0.24" +tokio-rustls = { version = "0.26", default-features = false, features = ["tls12", "ring"] } tokio-stream = { version = "0.1.14", features = ["sync", "net"] } tokio-util = "0.7.10" toml = { version = "0.7.4", features = ["preserve_order"] } @@ -491,9 +491,9 @@ toml_edit = { version = "0.19.10" } # NOTE: do not enable the `tls` feature on tonic. It will break custom TLS handling # for self signed certificates. Unit tests under consensus/core and other integration # tests will fail. -tonic = { version = "0.11", features = ["transport"] } -tonic-build = { version = "0.11", features = ["prost", "transport"] } -tonic-health = "0.11" +tonic = { version = "0.12", features = ["transport"] } +tonic-build = { version = "0.12", features = ["prost", "transport"] } +tonic-health = "0.12" tower = { version = "0.4.12", features = [ "full", "util", @@ -501,7 +501,7 @@ tower = { version = "0.4.12", features = [ "load-shed", "limit", ] } -tower-http = { version = "0.3.4", features = [ +tower-http = { version = "0.5", features = [ "cors", "full", "trace", @@ -528,7 +528,7 @@ unescape = "0.1.0" ureq = "2.9.1" url = "2.3.1" uuid = { version = "1.1.2", features = ["v4", "fast-rng"] } -webpki = { version = "0.101.0", package = "rustls-webpki", features = [ +webpki = { version = "0.102", package = "rustls-webpki", features = [ "alloc", "std", ] } diff --git a/consensus/core/Cargo.toml b/consensus/core/Cargo.toml index c37c0be3415d2..067901f002a16 100644 --- a/consensus/core/Cargo.toml +++ b/consensus/core/Cargo.toml @@ -23,6 +23,7 @@ fastcrypto.workspace = true futures.workspace = true http.workspace = true hyper.workspace = true +hyper-util.workspace = true hyper-rustls.workspace = true itertools.workspace = true quinn-proto.workspace = true diff --git a/consensus/core/src/network/tonic_network.rs b/consensus/core/src/network/tonic_network.rs index b6c184c069e65..44b81406e99ef 100644 --- a/consensus/core/src/network/tonic_network.rs +++ b/consensus/core/src/network/tonic_network.rs @@ -14,7 +14,8 @@ use bytes::Bytes; use cfg_if::cfg_if; use consensus_config::{AuthorityIndex, NetworkKeyPair, NetworkPublicKey}; use futures::{stream, Stream, StreamExt as _}; -use hyper::server::conn::Http; +use hyper_util::rt::tokio::TokioIo; +use hyper_util::service::TowerToHyperService; use mysten_common::sync::notify_once::NotifyOnce; use mysten_metrics::monitored_future; use mysten_network::{ @@ -144,15 +145,18 @@ impl NetworkClient for TonicClient { let response = client.subscribe_blocks(request).await.map_err(|e| { ConsensusError::NetworkRequest(format!("subscribe_blocks failed: {e:?}")) })?; - let stream = response.into_inner().filter_map(move |b| async move { - match b { - Ok(response) => Some(response.block), - Err(e) => { - debug!("Network error received from {}: {e:?}", peer); - None + let stream = response + .into_inner() + .take_while(|b| futures::future::ready(b.is_ok())) + .filter_map(move |b| async move { + match b { + Ok(response) => Some(response.block), + Err(e) => { + debug!("Network error received from {}: {e:?}", peer); + None + } } - } - }); + }); let rate_limited_stream = tokio_stream::StreamExt::throttle(stream, self.context.parameters.min_round_delay / 2) .boxed(); @@ -692,13 +696,14 @@ impl NetworkManager for TonicManager { .max_encoding_message_size(config.message_size_limit) .max_decoding_message_size(config.message_size_limit), ) - .into_service(); + .into_router(); let inbound_metrics = self.context.metrics.network_metrics.inbound.clone(); let excessive_message_size = self.context.parameters.tonic.excessive_message_size; - let mut http = Http::new(); - http.http2_only(true); + let http = + hyper_util::server::conn::auto::Builder::new(hyper_util::rt::TokioExecutor::new()) + .http2_only(); let http = Arc::new(http); let tls_server_config = @@ -875,7 +880,7 @@ impl NetworkManager for TonicManager { .service(consensus_service.clone()); pin! { - let connection = http.serve_connection(tls_stream, svc); + let connection = http.serve_connection(TokioIo::new(tls_stream), TowerToHyperService::new(svc)); } trace!("Connection ready. Starting to serve requests for {peer_addr:?}"); diff --git a/consensus/core/src/network/tonic_tls.rs b/consensus/core/src/network/tonic_tls.rs index 1b5587ba459eb..88a40f88c4103 100644 --- a/consensus/core/src/network/tonic_tls.rs +++ b/consensus/core/src/network/tonic_tls.rs @@ -57,6 +57,7 @@ pub(crate) fn create_rustls_client_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, diff --git a/crates/mysten-metrics/src/lib.rs b/crates/mysten-metrics/src/lib.rs index 4a2c01c97f01a..ae45b80f98e4f 100644 --- a/crates/mysten-metrics/src/lib.rs +++ b/crates/mysten-metrics/src/lib.rs @@ -460,8 +460,8 @@ pub fn start_prometheus_server(addr: SocketAddr) -> RegistryService { .layer(Extension(registry_service.clone())); tokio::spawn(async move { - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); + axum::serve(listener, app.into_make_service()) .await .unwrap(); }); diff --git a/crates/mysten-network/src/server.rs b/crates/mysten-network/src/server.rs index 0a0d4ac70a7bc..4bac6fe61ae52 100644 --- a/crates/mysten-network/src/server.rs +++ b/crates/mysten-network/src/server.rs @@ -22,7 +22,7 @@ use tonic::{ BoxFuture, }, server::NamedService, - transport::{server::Router, Body}, + transport::server::Router, }; use tower::{ layer::util::{Identity, Stack}, @@ -41,7 +41,7 @@ pub struct ServerBuilder) -> Option; +type AddPathToHeaderFunction = fn(&Request) -> Option; type WrapperService = Stack< Stack< @@ -103,7 +103,7 @@ impl ServerBuilder { .global_concurrency_limit .map(tower::limit::GlobalConcurrencyLimitLayer::new); - fn add_path_to_request_header(request: &Request) -> Option { + fn add_path_to_request_header(request: &Request) -> Option { let path = request.uri().path(); Some(HeaderValue::from_str(path).unwrap()) } @@ -144,7 +144,7 @@ impl ServerBuilder { /// Add a new service to this Server. pub fn add_service(mut self, svc: S) -> Self where - S: Service, Response = Response, Error = Infallible> + S: Service, Response = Response, Error = Infallible> + NamedService + Clone + Send diff --git a/crates/mysten-service/src/service.rs b/crates/mysten-service/src/service.rs index 599796b1f4578..f6ba299b48d7b 100644 --- a/crates/mysten-service/src/service.rs +++ b/crates/mysten-service/src/service.rs @@ -23,8 +23,10 @@ where pub async fn serve(app: Router) -> Result<()> { // run it with hyper on localhost:3000 debug!("listening on http://localhost:{}", DEFAULT_PORT); - axum::Server::bind(&format!("0.0.0.0:{}", DEFAULT_PORT).parse()?) - .serve(app.into_make_service()) - .await?; + + let listener = tokio::net::TcpListener::bind(&format!("0.0.0.0:{}", DEFAULT_PORT)) + .await + .unwrap(); + axum::serve(listener, app).await?; Ok(()) } diff --git a/crates/mysten-service/tests/integration_test.rs b/crates/mysten-service/tests/integration_test.rs index f661be895e228..5ad9cd7f47a3d 100644 --- a/crates/mysten-service/tests/integration_test.rs +++ b/crates/mysten-service/tests/integration_test.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use axum::body::Body; -use axum::body::HttpBody; use axum::http::Request; use tower::ServiceExt; @@ -21,8 +20,8 @@ async fn test_mysten_service() { .unwrap(); assert_eq!(res.status(), 200); - let mut body = res.into_body(); - let body_data = body.data().await.unwrap().unwrap(); + let body = res.into_body(); + let body_data = axum::body::to_bytes(body, usize::MAX).await.unwrap(); println!("{}", std::str::from_utf8(&body_data).unwrap()); assert_eq!( &body_data[..], diff --git a/crates/sui-bridge/src/server/mock_handler.rs b/crates/sui-bridge/src/server/mock_handler.rs index 980d77123e8ea..f8b5e01fc912c 100644 --- a/crates/sui-bridge/src/server/mock_handler.rs +++ b/crates/sui-bridge/src/server/mock_handler.rs @@ -133,13 +133,15 @@ pub fn run_mock_server( mock_handler: BridgeRequestMockHandler, ) -> tokio::task::JoinHandle<()> { tracing::info!("Starting mock server at {}", socket_address); - let server = axum::Server::bind(&socket_address).serve( - make_router( + let listener = std::net::TcpListener::bind(socket_address).unwrap(); + listener.set_nonblocking(true).unwrap(); + let listener = tokio::net::TcpListener::from_std(listener).unwrap(); + tokio::spawn(async move { + let router = make_router( Arc::new(mock_handler), Arc::new(BridgeMetrics::new_for_testing()), Arc::new(BridgeNodePublicMetadata::empty_for_testing()), - ) - .into_make_service(), - ); - tokio::spawn(async move { server.await.unwrap() }) + ); + axum::serve(listener, router).await.unwrap() + }) } diff --git a/crates/sui-bridge/src/server/mod.rs b/crates/sui-bridge/src/server/mod.rs index 6f73472fbe362..9fad0cb0a6d30 100644 --- a/crates/sui-bridge/src/server/mod.rs +++ b/crates/sui-bridge/src/server/mod.rs @@ -83,10 +83,15 @@ pub fn run_server( metrics: Arc, metadata: Arc, ) -> tokio::task::JoinHandle<()> { - let service = axum::Server::bind(socket_address) - .serve(make_router(Arc::new(handler), metrics, metadata).into_make_service()); + let socket_address = *socket_address; tokio::spawn(async move { - service.await.unwrap(); + let listener = tokio::net::TcpListener::bind(socket_address).await.unwrap(); + axum::serve( + listener, + make_router(Arc::new(handler), metrics, metadata).into_make_service(), + ) + .await + .unwrap(); }) } diff --git a/crates/sui-core/src/traffic_controller/nodefw_test_server.rs b/crates/sui-core/src/traffic_controller/nodefw_test_server.rs index 57af7897c13b9..7b8c1f41f1763 100644 --- a/crates/sui-core/src/traffic_controller/nodefw_test_server.rs +++ b/crates/sui-core/src/traffic_controller/nodefw_test_server.rs @@ -44,18 +44,11 @@ impl NodeFwTestServer { .route("/block_addresses", post(Self::block_addresses)) .with_state(app_state.clone()); - let shutdown_signal = self.shutdown_signal.clone(); let addr = SocketAddr::from(([127, 0, 0, 1], port)); - let server = axum::Server::bind(&addr) - .serve(app.into_make_service()) - .with_graceful_shutdown(async move { - shutdown_signal.notified().await; - }); let handle = tokio::spawn(async move { - if let Err(e) = server.await { - panic!("Server error: {}", e); - } + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app).await.unwrap(); }); tokio::spawn(Self::periodically_remove_expired_addresses( diff --git a/crates/sui-faucet/src/server.rs b/crates/sui-faucet/src/server.rs index 4b4f2400f6d13..cea9242ad9e64 100644 --- a/crates/sui-faucet/src/server.rs +++ b/crates/sui-faucet/src/server.rs @@ -85,9 +85,8 @@ pub async fn start_faucet( let addr = SocketAddr::new(IpAddr::V4(host_ip), port); info!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await?; + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app).await?; Ok(()) } diff --git a/crates/sui-graphql-e2e-tests/tests/call/simple.exp b/crates/sui-graphql-e2e-tests/tests/call/simple.exp index 9ac1393da7ca2..c9fc964fa9eb3 100644 --- a/crates/sui-graphql-e2e-tests/tests/call/simple.exp +++ b/crates/sui-graphql-e2e-tests/tests/call/simple.exp @@ -115,10 +115,8 @@ Headers: { "content-type": "application/json", "content-length": "157", "x-sui-rpc-version": "2024.7.0-testing-no-sha", + "vary": "origin, access-control-request-method, access-control-request-headers", "access-control-allow-origin": "*", - "vary": "origin", - "vary": "access-control-request-method", - "vary": "access-control-request-headers", } Service version: 2024.7.0-testing-no-sha Response: { diff --git a/crates/sui-graphql-rpc/Cargo.toml b/crates/sui-graphql-rpc/Cargo.toml index aa58529dab532..87c8a1571a9ef 100644 --- a/crates/sui-graphql-rpc/Cargo.toml +++ b/crates/sui-graphql-rpc/Cargo.toml @@ -14,6 +14,7 @@ async-graphql-axum.workspace = true async-graphql-value.workspace = true async-trait.workspace = true axum.workspace = true +axum-extra.workspace = true bin-version.workspace = true chrono.workspace = true clap.workspace = true diff --git a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql b/crates/sui-graphql-rpc/schema/current_progress_schema.graphql index c5b5ae6e526c0..688f1490b5f64 100644 --- a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql +++ b/crates/sui-graphql-rpc/schema/current_progress_schema.graphql @@ -4402,6 +4402,8 @@ type ZkLoginVerifyResult { errors: [String!]! } +directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT schema { query: Query mutation: Mutation diff --git a/crates/sui-graphql-rpc/src/server/builder.rs b/crates/sui-graphql-rpc/src/server/builder.rs index 51fd2ada86411..d4726439de4a1 100644 --- a/crates/sui-graphql-rpc/src/server/builder.rs +++ b/crates/sui-graphql-rpc/src/server/builder.rs @@ -36,10 +36,9 @@ use async_graphql::extensions::Tracing; use async_graphql::EmptySubscription; use async_graphql::{extensions::ExtensionFactory, Schema, SchemaBuilder}; use async_graphql_axum::{GraphQLRequest, GraphQLResponse}; +use axum::body::Body; use axum::extract::FromRef; -use axum::extract::{ - connect_info::IntoMakeServiceWithConnectInfo, ConnectInfo, Query as AxumQuery, State, -}; +use axum::extract::{ConnectInfo, Query as AxumQuery, State}; use axum::http::{HeaderMap, StatusCode}; use axum::middleware::{self}; use axum::response::IntoResponse; @@ -48,9 +47,6 @@ use axum::Extension; use axum::Router; use chrono::Utc; use http::{HeaderValue, Method, Request}; -use hyper::server::conn::AddrIncoming as HyperAddrIncoming; -use hyper::Body; -use hyper::Server as HyperServer; use mysten_metrics::spawn_monitored_task; use mysten_network::callback::{CallbackLayer, MakeCallbackHandler, ResponseHandler}; use std::convert::Infallible; @@ -73,7 +69,9 @@ use uuid::Uuid; const DEFAULT_MAX_CHECKPOINT_LAG: Duration = Duration::from_secs(300); pub(crate) struct Server { - pub server: HyperServer>, + // pub server: HyperServer>, + router: Router, + address: String, watermark_task: WatermarkTask, system_package_task: SystemPackageTask, trigger_exchange_rates_task: TriggerExchangeRatesTask, @@ -120,13 +118,18 @@ impl Server { info!("Starting graphql service"); let cancellation_token = self.state.cancellation_token.clone(); spawn_monitored_task!(async move { - self.server - .with_graceful_shutdown(async { - cancellation_token.cancelled().await; - info!("Shutdown signal received, terminating graphql service"); - }) - .await - .map_err(|e| Error::Internal(format!("Server run failed: {}", e))) + let listener = tokio::net::TcpListener::bind(&self.address).await.unwrap(); + axum::serve( + listener, + self.router + .into_make_service_with_connect_info::(), + ) + .with_graceful_shutdown(async move { + cancellation_token.cancelled().await; + info!("Shutdown signal received, terminating graphql service"); + }) + .await + .map_err(|e| Error::Internal(format!("Server run failed: {}", e))) }) }; @@ -339,7 +342,7 @@ impl ServerBuilder { state.cancellation_token.clone(), ); - let app = router + let router = router .route_layer(middleware::from_fn_with_state( state.version, set_version_middleware, @@ -353,12 +356,8 @@ impl ServerBuilder { .layer(Self::cors()?); Ok(Server { - server: axum::Server::bind( - &address - .parse() - .map_err(|_| Error::Internal(format!("Failed to parse address {}", address)))?, - ) - .serve(app.into_make_service_with_connect_info::()), + router, + address, watermark_task, system_package_task, trigger_exchange_rates_task, diff --git a/crates/sui-graphql-rpc/src/server/version.rs b/crates/sui-graphql-rpc/src/server/version.rs index 46b7bf32be05b..0dcb46a5ba891 100644 --- a/crates/sui-graphql-rpc/src/server/version.rs +++ b/crates/sui-graphql-rpc/src/server/version.rs @@ -2,12 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 use axum::{ + body::Body, extract::{Path, State}, - headers, http::{HeaderName, HeaderValue, Request, StatusCode}, middleware::Next, response::{IntoResponse, Response}, }; +use axum_extra::headers; use crate::{ config::Version, @@ -49,11 +50,11 @@ impl headers::Header for SuiRpcVersion { /// that this instance of the RPC matches that version constraint. Each RPC instance only supports /// one version of the RPC software, and it is the responsibility of the load balancer to make sure /// version constraints are met. -pub(crate) async fn check_version_middleware( +pub(crate) async fn check_version_middleware( version: Option>, State(service_version): State, - request: Request, - next: Next, + request: Request, + next: Next, ) -> Response { let Some(Path(version)) = version else { return next.run(request).await; @@ -91,10 +92,10 @@ pub(crate) async fn check_version_middleware( /// Mark every outgoing response with a header indicating the precise version of the RPC that was /// used (including the patch version and sha). -pub(crate) async fn set_version_middleware( +pub(crate) async fn set_version_middleware( State(version): State, - request: Request, - next: Next, + request: Request, + next: Next, ) -> Response { let mut response = next.run(request).await; let headers = response.headers_mut(); @@ -196,7 +197,9 @@ mod tests { } async fn response_body(response: Response) -> String { - let bytes = hyper::body::to_bytes(response.into_body()).await.unwrap(); + let bytes = axum::body::to_bytes(response.into_body(), usize::MAX) + .await + .unwrap(); let value: serde_json::Value = serde_json::from_slice(bytes.as_ref()).unwrap(); serde_json::to_string_pretty(&value).unwrap() } diff --git a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap index 7746f8124de1c..fae839c9b487a 100644 --- a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap +++ b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap @@ -4406,6 +4406,8 @@ type ZkLoginVerifyResult { errors: [String!]! } +directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT schema { query: Query mutation: Mutation diff --git a/crates/sui-indexer/src/metrics.rs b/crates/sui-indexer/src/metrics.rs index 67dbf7fe1ff43..36d788f5fd580 100644 --- a/crates/sui-indexer/src/metrics.rs +++ b/crates/sui-indexer/src/metrics.rs @@ -40,10 +40,8 @@ pub fn start_prometheus_server( .layer(Extension(registry_service.clone())); tokio::spawn(async move { - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await - .unwrap(); + let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); + axum::serve(listener, app).await.unwrap(); }); Ok((registry_service, registry)) } diff --git a/crates/sui-json-rpc-tests/tests/routing_tests.rs b/crates/sui-json-rpc-tests/tests/routing_tests.rs index 12d8c82795336..c5bc6a23edae8 100644 --- a/crates/sui-json-rpc-tests/tests/routing_tests.rs +++ b/crates/sui-json-rpc-tests/tests/routing_tests.rs @@ -2,11 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use async_trait::async_trait; -use hyper::header::HeaderValue; -use hyper::HeaderMap; use jsonrpsee::core::client::ClientT; use jsonrpsee::core::RpcResult; use jsonrpsee::http_client::HttpClientBuilder; +use jsonrpsee::http_client::{HeaderMap, HeaderValue}; use jsonrpsee::proc_macros::rpc; use jsonrpsee::rpc_params; use jsonrpsee::RpcModule; diff --git a/crates/sui-json-rpc/Cargo.toml b/crates/sui-json-rpc/Cargo.toml index e03790cecfaa4..25ddb1b48ea60 100644 --- a/crates/sui-json-rpc/Cargo.toml +++ b/crates/sui-json-rpc/Cargo.toml @@ -12,6 +12,7 @@ chrono.workspace = true fastcrypto.workspace = true jsonrpsee.workspace = true hyper.workspace = true +http-body = "0.4" itertools.workspace = true indexmap.workspace = true tower.workspace = true diff --git a/crates/sui-json-rpc/src/axum_router.rs b/crates/sui-json-rpc/src/axum_router.rs index c46cc04a74494..8ddebab687b2c 100644 --- a/crates/sui-json-rpc/src/axum_router.rs +++ b/crates/sui-json-rpc/src/axum_router.rs @@ -7,6 +7,7 @@ use std::{net::SocketAddr, sync::Arc}; use sui_types::traffic_control::RemoteFirewallConfig; use axum::extract::{ConnectInfo, Json, State}; +use axum::response::Response; use futures::StreamExt; use hyper::header::HeaderValue; use hyper::HeaderMap; @@ -101,12 +102,12 @@ impl JsonRpcService { } /// Create a response body. -fn from_template>( +fn from_template>( status: hyper::StatusCode, body: S, content_type: &'static str, -) -> hyper::Response { - hyper::Response::builder() +) -> Response { + Response::builder() .status(status) .header( "content-type", @@ -119,7 +120,7 @@ fn from_template>( } /// Create a valid JSON response. -pub(crate) fn ok_response(body: String) -> hyper::Response { +pub(crate) fn ok_response(body: String) -> Response { const JSON: &str = "application/json; charset=utf-8"; from_template(hyper::StatusCode::OK, body, JSON) } diff --git a/crates/sui-json-rpc/src/lib.rs b/crates/sui-json-rpc/src/lib.rs index 694cb2ff3e17b..08adfcfa44469 100644 --- a/crates/sui-json-rpc/src/lib.rs +++ b/crates/sui-json-rpc/src/lib.rs @@ -5,9 +5,9 @@ use std::env; use std::net::SocketAddr; use std::str::FromStr; +use axum::body::Body; use hyper::header::HeaderName; use hyper::header::HeaderValue; -use hyper::Body; use hyper::Method; use hyper::Request; use jsonrpsee::RpcModule; @@ -127,7 +127,7 @@ impl JsonRpcServerBuilder { fn trace_layer() -> TraceLayer< tower_http::classify::SharedClassifier, - impl tower_http::trace::MakeSpan + Clone, + impl tower_http::trace::MakeSpan + Clone, (), (), (), @@ -259,13 +259,18 @@ impl JsonRpcServerBuilder { ) -> Result { let app = self.to_router(server_type).await?; - let server = axum::Server::bind(&listen_address) - .serve(app.into_make_service_with_connect_info::()); - - let addr = server.local_addr(); + let listener = tokio::net::TcpListener::bind(&listen_address) + .await + .unwrap(); + let addr = listener.local_addr().unwrap(); let handle = tokio::spawn(async move { - server.await.unwrap(); + axum::serve( + listener, + app.into_make_service_with_connect_info::(), + ) + .await + .unwrap(); if let Some(cancel) = cancel { // Signal that the server is shutting down, so other tasks can clean-up. cancel.cancel(); diff --git a/crates/sui-json-rpc/src/metrics.rs b/crates/sui-json-rpc/src/metrics.rs index d4d693301906c..26b8bd6a745f1 100644 --- a/crates/sui-json-rpc/src/metrics.rs +++ b/crates/sui-json-rpc/src/metrics.rs @@ -1,10 +1,10 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use hyper::body::HttpBody; use std::collections::HashSet; use std::net::SocketAddr; +use http_body::Body; use jsonrpsee::server::logger::{HttpRequest, Logger, MethodKind, TransportProtocol}; use jsonrpsee::types::Params; use prometheus::{ diff --git a/crates/sui-node/src/admin.rs b/crates/sui-node/src/admin.rs index af0d710e229b4..228707cebd2b4 100644 --- a/crates/sui-node/src/admin.rs +++ b/crates/sui-node/src/admin.rs @@ -128,10 +128,15 @@ pub async fn run_admin_server(node: Arc, port: u16, tracing_handle: Tra "starting admin server" ); - axum::Server::bind(&socket_address) - .serve(app.into_make_service_with_connect_info::()) + let listener = tokio::net::TcpListener::bind(&socket_address) .await - .unwrap() + .unwrap(); + axum::serve( + listener, + app.into_make_service_with_connect_info::(), + ) + .await + .unwrap(); } #[derive(Deserialize)] diff --git a/crates/sui-node/src/lib.rs b/crates/sui-node/src/lib.rs index 46badb2d4ce1f..fb2b4f8416329 100644 --- a/crates/sui-node/src/lib.rs +++ b/crates/sui-node/src/lib.rs @@ -2044,11 +2044,19 @@ pub async fn build_http_server( .nest("/v2", rest_router); } - let server = axum::Server::bind(&config.json_rpc_address) - .serve(router.into_make_service_with_connect_info::()); + let listener = tokio::net::TcpListener::bind(&config.json_rpc_address) + .await + .unwrap(); + let addr = listener.local_addr().unwrap(); - let addr = server.local_addr(); - let handle = tokio::spawn(async move { server.await.unwrap() }); + let handle = tokio::spawn(async move { + axum::serve( + listener, + router.into_make_service_with_connect_info::(), + ) + .await + .unwrap() + }); info!(local_addr =? addr, "Sui JSON-RPC server listening on {addr}"); diff --git a/crates/sui-proxy/Cargo.toml b/crates/sui-proxy/Cargo.toml index 1441e047fa56c..2a05739c8491a 100644 --- a/crates/sui-proxy/Cargo.toml +++ b/crates/sui-proxy/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] axum.workspace = true +axum-extra.workspace = true axum-server.workspace = true anyhow.workspace = true bytes.workspace = true @@ -37,7 +38,6 @@ rustls.workspace = true rustls-pemfile.workspace = true prost.workspace = true once_cell.workspace = true -http-body.workspace = true hex.workspace = true ipnetwork.workspace = true diff --git a/crates/sui-proxy/src/admin.rs b/crates/sui-proxy/src/admin.rs index 5516c98ab16f1..d6f149d043a51 100644 --- a/crates/sui-proxy/src/admin.rs +++ b/crates/sui-proxy/src/admin.rs @@ -166,28 +166,26 @@ pub fn generate_self_cert(hostname: String) -> CertKeyPair { } /// Load a certificate for use by the listening service -fn load_certs(filename: &str) -> Vec { +fn load_certs(filename: &str) -> Vec> { let certfile = fs::File::open(filename) .unwrap_or_else(|e| panic!("cannot open certificate file: {}; {}", filename, e)); let mut reader = BufReader::new(certfile); rustls_pemfile::certs(&mut reader) + .collect::, _>>() .unwrap() - .iter() - .map(|v| rustls::Certificate(v.clone())) - .collect() } /// Load a private key -fn load_private_key(filename: &str) -> rustls::PrivateKey { +fn load_private_key(filename: &str) -> rustls::pki_types::PrivateKeyDer<'static> { let keyfile = fs::File::open(filename) .unwrap_or_else(|e| panic!("cannot open private key file {}; {}", filename, e)); let mut reader = BufReader::new(keyfile); loop { match rustls_pemfile::read_one(&mut reader).expect("cannot parse private key .pem file") { - Some(rustls_pemfile::Item::RSAKey(key)) => return rustls::PrivateKey(key), - Some(rustls_pemfile::Item::PKCS8Key(key)) => return rustls::PrivateKey(key), - Some(rustls_pemfile::Item::ECKey(key)) => return rustls::PrivateKey(key), + Some(rustls_pemfile::Item::Pkcs1Key(key)) => return key.into(), + Some(rustls_pemfile::Item::Pkcs8Key(key)) => return key.into(), + Some(rustls_pemfile::Item::Sec1Key(key)) => return key.into(), None => break, _ => {} } diff --git a/crates/sui-proxy/src/histogram_relay.rs b/crates/sui-proxy/src/histogram_relay.rs index 5ed3e302cce6c..41635819fefb9 100644 --- a/crates/sui-proxy/src/histogram_relay.rs +++ b/crates/sui-proxy/src/histogram_relay.rs @@ -45,7 +45,7 @@ static RELAY_DURATION: Lazy = Lazy::new(|| { // Creates a new http server that has as a sole purpose to expose // and endpoint that prometheus agent can use to poll for the metrics. // A RegistryService is returned that can be used to get access in prometheus Registries. -pub fn start_prometheus_server(addr: TcpListener) -> HistogramRelay { +pub fn start_prometheus_server(listener: TcpListener) -> HistogramRelay { let relay = HistogramRelay::new(); let app = Router::new() .route(METRICS_ROUTE, get(metrics)) @@ -61,11 +61,9 @@ pub fn start_prometheus_server(addr: TcpListener) -> HistogramRelay { ); tokio::spawn(async move { - axum::Server::from_tcp(addr) - .unwrap() - .serve(app.into_make_service()) - .await - .unwrap(); + listener.set_nonblocking(true).unwrap(); + let listener = tokio::net::TcpListener::from_std(listener).unwrap(); + axum::serve(listener, app).await.unwrap(); }); relay } diff --git a/crates/sui-proxy/src/lib.rs b/crates/sui-proxy/src/lib.rs index c858b4568ef2d..149eba1a31156 100644 --- a/crates/sui-proxy/src/lib.rs +++ b/crates/sui-proxy/src/lib.rs @@ -61,11 +61,9 @@ mod tests { let app = Router::new().route("/v1/push", post(handler)); // run it - axum::Server::from_tcp(listener) - .unwrap() - .serve(app.into_make_service()) - .await - .unwrap(); + listener.set_nonblocking(true).unwrap(); + let listener = tokio::net::TcpListener::from_std(listener).unwrap(); + axum::serve(listener, app).await.unwrap(); } /// axum_acceptor is a basic e2e test that creates a mock remote_write post endpoint and has a simple diff --git a/crates/sui-proxy/src/metrics.rs b/crates/sui-proxy/src/metrics.rs index 5ee5110e9db30..063d7fefd967c 100644 --- a/crates/sui-proxy/src/metrics.rs +++ b/crates/sui-proxy/src/metrics.rs @@ -14,7 +14,7 @@ const METRICS_ROUTE: &str = "/metrics"; // Creates a new http server that has as a sole purpose to expose // and endpoint that prometheus agent can use to poll for the metrics. // A RegistryService is returned that can be used to get access in prometheus Registries. -pub fn start_prometheus_server(addr: TcpListener) -> RegistryService { +pub fn start_prometheus_server(listener: TcpListener) -> RegistryService { let registry = Registry::new(); let registry_service = RegistryService::new(registry); @@ -33,11 +33,9 @@ pub fn start_prometheus_server(addr: TcpListener) -> RegistryService { ); tokio::spawn(async move { - axum::Server::from_tcp(addr) - .unwrap() - .serve(app.into_make_service()) - .await - .unwrap(); + listener.set_nonblocking(true).unwrap(); + let listener = tokio::net::TcpListener::from_std(listener).unwrap(); + axum::serve(listener, app).await.unwrap(); }); registry_service diff --git a/crates/sui-proxy/src/middleware.rs b/crates/sui-proxy/src/middleware.rs index 6af09d11ed6ae..840deb9ef1a20 100644 --- a/crates/sui-proxy/src/middleware.rs +++ b/crates/sui-proxy/src/middleware.rs @@ -3,14 +3,15 @@ use crate::{consumer::ProtobufDecoder, peers::SuiNodeProvider}; use axum::{ async_trait, + body::Body, body::Bytes, extract::{Extension, FromRequest}, - headers::{ContentLength, ContentType}, http::{Request, StatusCode}, middleware::Next, response::Response, - BoxError, TypedHeader, }; +use axum_extra::headers::{ContentLength, ContentType}; +use axum_extra::typed_header::TypedHeader; use bytes::Buf; use hyper::header::CONTENT_ENCODING; use once_cell::sync::Lazy; @@ -38,20 +39,20 @@ static MIDDLEWARE_HEADERS: Lazy = Lazy::new(|| { }); /// we expect sui-node to send us an http header content-length encoding. -pub async fn expect_content_length( +pub async fn expect_content_length( TypedHeader(content_length): TypedHeader, - request: Request, - next: Next, + request: Request, + next: Next, ) -> Result { MIDDLEWARE_HEADERS.with_label_values(&["content-length", &format!("{}", content_length.0)]); Ok(next.run(request).await) } /// we expect sui-node to send us an http header content-type encoding. -pub async fn expect_mysten_proxy_header( +pub async fn expect_mysten_proxy_header( TypedHeader(content_type): TypedHeader, - request: Request, - next: Next, + request: Request, + next: Next, ) -> Result { match format!("{content_type}").as_str() { prometheus::PROTOBUF_FORMAT => Ok(next.run(request).await), @@ -67,11 +68,11 @@ pub async fn expect_mysten_proxy_header( /// we expect that calling sui-nodes are known on the blockchain and we enforce /// their pub key tls creds here -pub async fn expect_valid_public_key( +pub async fn expect_valid_public_key( Extension(allower): Extension>, Extension(tls_connect_info): Extension, - mut request: Request, - next: Next, + mut request: Request, + next: Next, ) -> Result { let Some(public_key) = tls_connect_info.public_key() else { error!("unable to obtain public key from connecting client"); @@ -99,16 +100,16 @@ pub async fn expect_valid_public_key( pub struct LenDelimProtobuf(pub Vec); #[async_trait] -impl FromRequest for LenDelimProtobuf +impl FromRequest for LenDelimProtobuf where S: Send + Sync, - B: http_body::Body + Send + 'static, - B::Data: Send, - B::Error: Into, { type Rejection = (StatusCode, String); - async fn from_request(req: Request, state: &S) -> Result { + async fn from_request( + req: Request, + state: &S, + ) -> Result { let should_be_snappy = req .headers() .get(CONTENT_ENCODING) diff --git a/crates/sui-rest-api/Cargo.toml b/crates/sui-rest-api/Cargo.toml index eb9e78ab71dfe..95e375770bd2a 100644 --- a/crates/sui-rest-api/Cargo.toml +++ b/crates/sui-rest-api/Cargo.toml @@ -18,6 +18,7 @@ serde_yaml.workspace = true serde_with.workspace = true tap.workspace = true thiserror.workspace = true +tokio.workspace = true async-trait.workspace = true itertools.workspace = true sui-sdk2.workspace = true @@ -32,5 +33,4 @@ sui-protocol-config.workspace = true [dev-dependencies] -tokio.workspace = true diffy = "0.3" diff --git a/crates/sui-rest-api/src/accept.rs b/crates/sui-rest-api/src/accept.rs index 0348388cc60ba..b4a0d954660cd 100644 --- a/crates/sui-rest-api/src/accept.rs +++ b/crates/sui-rest-api/src/accept.rs @@ -96,7 +96,7 @@ mod tests { header::ACCEPT, "text/html, text/yaml;q=0.5, application/xhtml+xml, application/xml;q=0.9, */*;q=0.1", ) - .body(()) + .body(axum::body::Body::empty()) .unwrap(); let accept = Accept::from_request(req, &()).await.unwrap(); assert_eq!( @@ -115,14 +115,14 @@ mod tests { async fn test_accept_format() { let req = Request::builder() .header(header::ACCEPT, "*/*, application/bcs") - .body(()) + .body(axum::body::Body::empty()) .unwrap(); let accept = AcceptFormat::from_request(req, &()).await.unwrap(); assert_eq!(accept, AcceptFormat::Bcs); let req = Request::builder() .header(header::ACCEPT, "*/*") - .body(()) + .body(axum::body::Body::empty()) .unwrap(); let accept = AcceptFormat::from_request(req, &()).await.unwrap(); assert_eq!(accept, AcceptFormat::Json); diff --git a/crates/sui-rest-api/src/lib.rs b/crates/sui-rest-api/src/lib.rs index f0926fe79e6cc..9efd7f35325a1 100644 --- a/crates/sui-rest-api/src/lib.rs +++ b/crates/sui-rest-api/src/lib.rs @@ -169,10 +169,8 @@ impl RestService { app = Router::new().nest(&base, app); } - axum::Server::bind(&socket_address) - .serve(app.into_make_service()) - .await - .unwrap(); + let listener = tokio::net::TcpListener::bind(socket_address).await.unwrap(); + axum::serve(listener, app).await.unwrap(); } } @@ -285,9 +283,9 @@ mod test { let router = openapi::OpenApiDocument::new(openapi).into_router(); - axum::Server::bind(&"127.0.0.1:8000".parse().unwrap()) - .serve(router.into_make_service()) + let listener = tokio::net::TcpListener::bind("127.0.0.1:8000") .await .unwrap(); + axum::serve(listener, router).await.unwrap(); } } diff --git a/crates/sui-rest-api/src/response.rs b/crates/sui-rest-api/src/response.rs index deb57c8dd5dcf..5b3866d68db18 100644 --- a/crates/sui-rest-api/src/response.rs +++ b/crates/sui-rest-api/src/response.rs @@ -52,17 +52,17 @@ where } #[axum::async_trait] -impl axum::extract::FromRequest for Bcs +impl axum::extract::FromRequest for Bcs where T: serde::de::DeserializeOwned, S: Send + Sync, - B: axum::body::HttpBody + Send + 'static, - B::Data: Send, - B::Error: Into, { type Rejection = BcsRejection; - async fn from_request(req: axum::http::Request, state: &S) -> Result { + async fn from_request( + req: axum::http::Request, + state: &S, + ) -> Result { if bcs_content_type(req.headers()) { let bytes = axum::body::Bytes::from_request(req, state) .await diff --git a/crates/sui-rosetta/src/lib.rs b/crates/sui-rosetta/src/lib.rs index 1a1cbd34e07a2..9887a8b95a8ca 100644 --- a/crates/sui-rosetta/src/lib.rs +++ b/crates/sui-rosetta/src/lib.rs @@ -7,10 +7,8 @@ use std::sync::Arc; use axum::routing::post; use axum::{Extension, Router}; use once_cell::sync::Lazy; -use tokio::task::JoinHandle; use tracing::info; -use mysten_metrics::spawn_monitored_task; use sui_sdk::SuiClient; use crate::errors::Error; @@ -46,7 +44,7 @@ impl RosettaOnlineServer { } } - pub fn serve(self, addr: SocketAddr) -> JoinHandle> { + pub async fn serve(self, addr: SocketAddr) { // Online endpoints let app = Router::new() .route("/account/balance", post(account::balance)) @@ -60,12 +58,14 @@ impl RosettaOnlineServer { .route("/network/options", post(network::options)) .layer(Extension(self.env)) .with_state(self.context); - let server = axum::Server::bind(&addr).serve(app.into_make_service()); + + let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); + info!( "Sui Rosetta online server listening on {}", - server.local_addr() + listener.local_addr().unwrap() ); - spawn_monitored_task!(server) + axum::serve(listener, app).await.unwrap(); } } @@ -78,7 +78,7 @@ impl RosettaOfflineServer { Self { env } } - pub fn serve(self, addr: SocketAddr) -> JoinHandle> { + pub async fn serve(self, addr: SocketAddr) { // Online endpoints let app = Router::new() .route("/construction/derive", post(construction::derive)) @@ -90,11 +90,12 @@ impl RosettaOfflineServer { .route("/network/list", post(network::list)) .route("/network/options", post(network::options)) .layer(Extension(self.env)); - let server = axum::Server::bind(&addr).serve(app.into_make_service()); + let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); + info!( "Sui Rosetta offline server listening on {}", - server.local_addr() + listener.local_addr().unwrap() ); - spawn_monitored_task!(server) + axum::serve(listener, app).await.unwrap(); } } diff --git a/crates/sui-rosetta/src/main.rs b/crates/sui-rosetta/src/main.rs index 2ef4d8e0b7efb..e666929c546df 100644 --- a/crates/sui-rosetta/src/main.rs +++ b/crates/sui-rosetta/src/main.rs @@ -129,7 +129,7 @@ impl RosettaServerCommand { RosettaServerCommand::StartOfflineServer { env, addr } => { info!("Starting Rosetta Offline Server."); let server = RosettaOfflineServer::new(env); - server.serve(addr).await??; + server.serve(addr).await; } RosettaServerCommand::StartOnlineRemoteServer { env, @@ -144,7 +144,7 @@ impl RosettaServerCommand { let rosetta_path = data_path.join("rosetta_db"); info!("Rosetta db path : {rosetta_path:?}"); let rosetta = RosettaOnlineServer::new(env, sui_client); - rosetta.serve(addr).await??; + rosetta.serve(addr).await; } RosettaServerCommand::StartOnlineServer { @@ -177,7 +177,7 @@ impl RosettaServerCommand { let rosetta_path = data_path.join("rosetta_db"); info!("Rosetta db path : {rosetta_path:?}"); let rosetta = RosettaOnlineServer::new(env, sui_client); - rosetta.serve(addr).await??; + rosetta.serve(addr).await; } }; Ok(()) diff --git a/crates/sui-rosetta/tests/rosetta_client.rs b/crates/sui-rosetta/tests/rosetta_client.rs index 855fb4d86dcbe..05c3703c0a584 100644 --- a/crates/sui-rosetta/tests/rosetta_client.rs +++ b/crates/sui-rosetta/tests/rosetta_client.rs @@ -28,18 +28,24 @@ use sui_sdk::SuiClient; use sui_types::base_types::SuiAddress; use sui_types::crypto::SuiSignature; -pub async fn start_rosetta_test_server( - client: SuiClient, -) -> (RosettaClient, Vec>>) { +pub async fn start_rosetta_test_server(client: SuiClient) -> (RosettaClient, Vec>) { let online_server = RosettaOnlineServer::new(SuiEnv::LocalNet, client); let offline_server = RosettaOfflineServer::new(SuiEnv::LocalNet); let local_ip = local_ip_utils::localhost_for_testing(); let port = local_ip_utils::get_available_port(&local_ip); let rosetta_address = format!("{}:{}", local_ip, port); - let online_handle = online_server.serve(SocketAddr::from_str(&rosetta_address).unwrap()); + let online_handle = tokio::spawn(async move { + online_server + .serve(SocketAddr::from_str(&rosetta_address).unwrap()) + .await + }); let offline_port = local_ip_utils::get_available_port(&local_ip); let offline_address = format!("{}:{}", local_ip, offline_port); - let offline_handle = offline_server.serve(SocketAddr::from_str(&offline_address).unwrap()); + let offline_handle = tokio::spawn(async move { + offline_server + .serve(SocketAddr::from_str(&offline_address).unwrap()) + .await + }); // allow rosetta to process the genesis block. tokio::task::yield_now().await; diff --git a/crates/sui-source-validation-service/Cargo.toml b/crates/sui-source-validation-service/Cargo.toml index 56b3b24c04e8b..53dc311ffc3d2 100644 --- a/crates/sui-source-validation-service/Cargo.toml +++ b/crates/sui-source-validation-service/Cargo.toml @@ -16,7 +16,7 @@ name = "sui-source-validation-service" [dependencies] anyhow = { version = "1.0.64", features = ["backtrace"] } clap.workspace = true -hyper = "0.14" +hyper.workspace = true jsonrpsee.workspace = true tempfile = "3.3.0" tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/crates/sui-source-validation-service/src/lib.rs b/crates/sui-source-validation-service/src/lib.rs index c866ad899012d..2654a92f72373 100644 --- a/crates/sui-source-validation-service/src/lib.rs +++ b/crates/sui-source-validation-service/src/lib.rs @@ -14,11 +14,10 @@ use tokio::sync::oneshot::Sender; use anyhow::{anyhow, bail}; use axum::extract::{Query, State}; use axum::response::{IntoResponse, Response}; -use axum::routing::{get, IntoMakeService}; +use axum::routing::get; use axum::Extension; -use axum::{Json, Router, Server}; +use axum::{Json, Router}; use hyper::http::{HeaderName, HeaderValue, Method}; -use hyper::server::conn::AddrIncoming; use hyper::{HeaderMap, StatusCode}; use mysten_metrics::RegistryService; use prometheus::{register_int_counter_with_registry, IntCounter, Registry}; @@ -445,9 +444,7 @@ pub struct AppState { pub sources_list: NetworkLookup, } -pub fn serve( - app_state: Arc>, -) -> anyhow::Result>> { +pub async fn serve(app_state: Arc>) -> anyhow::Result<()> { let app = Router::new() .route("/api", get(api_route)) .route("/api/list", get(list_route)) @@ -462,7 +459,10 @@ pub fn serve( ) .with_state(app_state); let listener = TcpListener::bind(host_port())?; - Ok(Server::from_tcp(listener)?.serve(app.into_make_service())) + listener.set_nonblocking(true).unwrap(); + let listener = tokio::net::TcpListener::from_std(listener)?; + axum::serve(listener, app).await?; + Ok(()) } #[derive(Deserialize)] @@ -535,10 +535,10 @@ async fn api_route( } } -async fn check_version_header( +async fn check_version_header( headers: HeaderMap, - req: hyper::Request, - next: Next, + req: hyper::Request, + next: Next, ) -> Response { let version = headers .get(SUI_SOURCE_VALIDATION_VERSION_HEADER) @@ -599,7 +599,7 @@ impl SourceServiceMetrics { } } -pub fn start_prometheus_server(addr: TcpListener) -> RegistryService { +pub fn start_prometheus_server(listener: TcpListener) -> RegistryService { let registry = Registry::new(); let registry_service = RegistryService::new(registry); @@ -609,11 +609,9 @@ pub fn start_prometheus_server(addr: TcpListener) -> RegistryService { .layer(Extension(registry_service.clone())); tokio::spawn(async move { - axum::Server::from_tcp(addr) - .unwrap() - .serve(app.into_make_service()) - .await - .unwrap(); + listener.set_nonblocking(true).unwrap(); + let listener = tokio::net::TcpListener::from_std(listener).unwrap(); + axum::serve(listener, app).await.unwrap(); }); registry_service diff --git a/crates/sui-source-validation-service/src/main.rs b/crates/sui-source-validation-service/src/main.rs index a49fe85017f18..2039976e950d7 100644 --- a/crates/sui-source-validation-service/src/main.rs +++ b/crates/sui-source-validation-service/src/main.rs @@ -80,7 +80,7 @@ pub async fn main() -> anyhow::Result<()> { } let app_state_copy = app_state.clone(); - let server = tokio::spawn(async { serve(app_state_copy)?.await.map_err(anyhow::Error::from) }); + let server = tokio::spawn(async { serve(app_state_copy).await }); threads.push(server); info!("serving on {}", host_port()); for t in threads { diff --git a/crates/sui-source-validation-service/tests/tests.rs b/crates/sui-source-validation-service/tests/tests.rs index 3fe162b11ccdd..bc6c06c2190b2 100644 --- a/crates/sui-source-validation-service/tests/tests.rs +++ b/crates/sui-source-validation-service/tests/tests.rs @@ -299,7 +299,8 @@ async fn test_api_route() -> anyhow::Result<()> { metrics: None, sources_list, })); - tokio::spawn(serve(app_state).expect("Cannot start service.")); + tokio::spawn(async move { serve(app_state).await.expect("Cannot start service.") }); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; let client = Client::new(); diff --git a/crates/sui-storage/Cargo.toml b/crates/sui-storage/Cargo.toml index 77a34512daf46..9536a8d64746a 100644 --- a/crates/sui-storage/Cargo.toml +++ b/crates/sui-storage/Cargo.toml @@ -55,6 +55,7 @@ move-binary-format.workspace = true move-bytecode-utils.workspace = true [dev-dependencies] +axum.workspace = true anyhow.workspace = true criterion.workspace = true tempfile.workspace = true diff --git a/crates/sui-storage/tests/key_value_tests.rs b/crates/sui-storage/tests/key_value_tests.rs index 8d3fb1b051917..5e2957c290d14 100644 --- a/crates/sui-storage/tests/key_value_tests.rs +++ b/crates/sui-storage/tests/key_value_tests.rs @@ -428,13 +428,9 @@ async fn test_get_tx_from_fallback() { #[cfg(msim)] mod simtests { - use super::*; - use hyper::{ - service::{make_service_fn, service_fn}, - Body, Request, Response, Server, - }; - use std::convert::Infallible; + use axum::routing::get; + use axum::{body::Body, extract::Request, extract::State, response::Response}; use std::net::SocketAddr; use std::sync::Mutex; use std::time::{Duration, Instant}; @@ -443,6 +439,23 @@ mod simtests { use sui_storage::http_key_value_store::*; use tracing::info; + async fn svc( + State(state): State>>>>, + request: Request, + ) -> Response { + let path = request.uri().path().to_string(); + let key = path.trim_start_matches('/'); + let value = state.lock().unwrap().get(key).cloned(); + info!("Got request for key: {:?}, value: {:?}", key, value); + match value { + Some(v) => Response::new(Body::from(v)), + None => Response::builder() + .status(hyper::StatusCode::NOT_FOUND) + .body(Body::empty()) + .unwrap(), + } + } + async fn test_server(data: Arc>>>) { let handle = sui_simulator::runtime::Handle::current(); let builder = handle.create_node(); @@ -456,41 +469,12 @@ mod simtests { let data = data.clone(); let startup_sender = startup_sender.clone(); async move { - let make_svc = make_service_fn(move |_| { - let data = data.clone(); - async { - Ok::<_, Infallible>(service_fn(move |req: Request| { - let data = data.clone(); - async move { - let path = req.uri().path().to_string(); - let key = path.trim_start_matches('/'); - let value = data.lock().unwrap().get(key).cloned(); - info!("Got request for key: {:?}, value: {:?}", key, value); - match value { - Some(v) => { - Ok::<_, Infallible>(Response::new(Body::from(v))) - } - None => Ok::<_, Infallible>( - Response::builder() - .status(hyper::StatusCode::NOT_FOUND) - .body(Body::empty()) - .unwrap(), - ), - } - } - })) - } - }); - + let router = get(svc).with_state(data); let addr = SocketAddr::from(([10, 10, 10, 10], 8080)); - let server = Server::bind(&addr).serve(make_svc); - - let graceful = server.with_graceful_shutdown(async { - tokio::time::sleep(Duration::from_secs(86400)).await; - }); + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); tokio::spawn(async { - let _ = graceful.await; + axum::serve(listener, router).await.unwrap(); }); startup_sender.send(true).ok(); diff --git a/crates/sui-swarm/src/memory/container.rs b/crates/sui-swarm/src/memory/container.rs index 9833d150421a4..edbb5e4b4f146 100644 --- a/crates/sui-swarm/src/memory/container.rs +++ b/crates/sui-swarm/src/memory/container.rs @@ -43,8 +43,11 @@ impl Container { pub async fn spawn(config: NodeConfig, runtime: RuntimeType) -> Self { let (startup_sender, startup_receiver) = tokio::sync::oneshot::channel(); let (cancel_sender, cancel_receiver) = tokio::sync::oneshot::channel(); + let name = AuthorityPublicKeyBytes::from(config.protocol_key_pair().public()) + .concise() + .to_string(); - let thread = thread::spawn(move || { + let thread = thread::Builder::new().name(name).spawn(move || { let span = if get_global_telemetry_config() .map(|c| c.enable_otlp_tracing) .unwrap_or(false) @@ -103,7 +106,7 @@ impl Container { trace!("cancellation received; shutting down thread"); }); - }); + }).unwrap(); let node = startup_receiver.await.unwrap(); diff --git a/crates/sui-swarm/src/memory/node.rs b/crates/sui-swarm/src/memory/node.rs index 2c92966d74ca7..5cc10a6b1f7b5 100644 --- a/crates/sui-swarm/src/memory/node.rs +++ b/crates/sui-swarm/src/memory/node.rs @@ -72,6 +72,7 @@ impl Node { pub fn stop(&self) { info!(name =% self.name().concise(), "stopping in-memory node"); *self.container.lock().unwrap() = None; + info!(name =% self.name().concise(), "node stopped"); } /// If this Node is currently running diff --git a/crates/sui-swarm/src/memory/swarm.rs b/crates/sui-swarm/src/memory/swarm.rs index d6a4cb2c7911b..5c0df441d2a03 100644 --- a/crates/sui-swarm/src/memory/swarm.rs +++ b/crates/sui-swarm/src/memory/swarm.rs @@ -578,5 +578,7 @@ mod test { for fullnode in swarm.fullnodes() { fullnode.health_check(false).await.unwrap(); } + + println!("hello"); } } diff --git a/crates/sui-tls/src/acceptor.rs b/crates/sui-tls/src/acceptor.rs index 8ea59cc62a052..04f84346199b7 100644 --- a/crates/sui-tls/src/acceptor.rs +++ b/crates/sui-tls/src/acceptor.rs @@ -7,6 +7,7 @@ use axum_server::{ tls_rustls::{RustlsAcceptor, RustlsConfig}, }; use fastcrypto::ed25519::Ed25519PublicKey; +use rustls::pki_types::CertificateDer; use std::{io, sync::Arc}; use tokio::io::{AsyncRead, AsyncWrite}; use tokio_rustls::server::TlsStream; @@ -15,7 +16,7 @@ use tower_layer::Layer; #[derive(Debug, Clone)] pub struct TlsConnectionInfo { sni_hostname: Option>, - peer_certificates: Option>, + peer_certificates: Option]>>, public_key: Option, } @@ -24,7 +25,7 @@ impl TlsConnectionInfo { self.sni_hostname.as_deref() } - pub fn peer_certificates(&self) -> Option<&[rustls::Certificate]> { + pub fn peer_certificates(&self) -> Option<&[CertificateDer<'static>]> { self.peer_certificates.as_deref() } diff --git a/crates/sui-tls/src/certgen.rs b/crates/sui-tls/src/certgen.rs index 80f8c8d494485..89ee06a4126ad 100644 --- a/crates/sui-tls/src/certgen.rs +++ b/crates/sui-tls/src/certgen.rs @@ -3,44 +3,43 @@ use fastcrypto::ed25519::{Ed25519PrivateKey, Ed25519PublicKey}; use pkcs8::EncodePrivateKey; -use rcgen::{CertificateParams, KeyPair, SignatureAlgorithm}; +use rcgen::{CertificateParams, KeyPair}; +use rustls::pki_types::CertificateDer; +use rustls::pki_types::PrivateKeyDer; pub struct SelfSignedCertificate { inner: rcgen::Certificate, + key: KeyPair, } impl SelfSignedCertificate { pub fn new(private_key: Ed25519PrivateKey, server_name: &str) -> Self { - Self { - inner: generate_self_signed_tls_certificate(private_key, server_name), - } + let (cert, key) = generate_self_signed_tls_certificate(private_key, server_name); + Self { inner: cert, key } } - pub fn rustls_certificate(&self) -> rustls::Certificate { - let cert_bytes = self.inner.serialize_der().unwrap(); - rustls::Certificate(cert_bytes) + pub fn rustls_certificate(&self) -> CertificateDer<'static> { + self.inner.der().to_owned() } - pub fn rustls_private_key(&self) -> rustls::PrivateKey { - let private_key_bytes = self.inner.serialize_private_key_der(); - rustls::PrivateKey(private_key_bytes) + pub fn rustls_private_key(&self) -> PrivateKeyDer<'static> { + PrivateKeyDer::Pkcs8(self.key.serialize_der().into()) } pub fn reqwest_identity(&self) -> reqwest::tls::Identity { - let pem = self.inner.serialize_pem().unwrap() + &self.inner.serialize_private_key_pem(); + let pem = self.inner.pem() + &self.key.serialize_pem(); reqwest::tls::Identity::from_pem(pem.as_ref()).unwrap() } pub fn reqwest_certificate(&self) -> reqwest::tls::Certificate { - let cert = self.inner.serialize_der().unwrap(); - reqwest::tls::Certificate::from_der(&cert).unwrap() + reqwest::tls::Certificate::from_der(self.inner.der()).unwrap() } } fn generate_self_signed_tls_certificate( private_key: Ed25519PrivateKey, server_name: &str, -) -> rcgen::Certificate { +) -> (rcgen::Certificate, KeyPair) { let keypair = ed25519::KeypairBytes { secret_key: private_key.0.to_bytes(), // ring cannot handle the optional public key that would be legal der here @@ -48,49 +47,29 @@ fn generate_self_signed_tls_certificate( public_key: None, }; - generate_cert(&keypair, server_name) -} - -fn generate_cert(keypair: &ed25519::KeypairBytes, server_name: &str) -> rcgen::Certificate { let pkcs8 = keypair.to_pkcs8_der().unwrap(); - let key_der = rustls::PrivateKey(pkcs8.as_bytes().to_vec()); - private_key_to_certificate(vec![server_name.to_owned()], &key_der).unwrap() -} + let key_der = PrivateKeyDer::Pkcs8(pkcs8.as_bytes().to_vec().into()); + let keypair = KeyPair::from_der_and_sign_algo(&key_der, &rcgen::PKCS_ED25519).unwrap(); -fn private_key_to_certificate( - subject_names: impl Into>, - private_key: &rustls::PrivateKey, -) -> Result { - let alg = &rcgen::PKCS_ED25519; - - let certificate = gen_certificate(subject_names, (private_key.0.as_ref(), alg))?; - Ok(certificate) + (generate_cert(&keypair, server_name), keypair) } -fn gen_certificate( - subject_names: impl Into>, - key_pair: (&[u8], &'static SignatureAlgorithm), -) -> Result { - let kp = KeyPair::from_der_and_sign_algo(key_pair.0, key_pair.1)?; - - let mut cert_params = CertificateParams::new(subject_names); - cert_params.key_pair = Some(kp); - cert_params.distinguished_name = rcgen::DistinguishedName::new(); - cert_params.alg = key_pair.1; - - let cert = rcgen::Certificate::from_params(cert_params).expect( - "unreachable! from_params should only fail if the key is incompatible with params.algo", - ); - Ok(cert) +fn generate_cert(keypair: &KeyPair, server_name: &str) -> rcgen::Certificate { + CertificateParams::new(vec![server_name.to_owned()]) + .unwrap() + .self_signed(keypair) + .expect( + "unreachable! from_params should only fail if the key is incompatible with params.algo", + ) } pub(crate) fn public_key_from_certificate( - certificate: &rustls::Certificate, + certificate: &CertificateDer, ) -> Result { use fastcrypto::traits::ToFromBytes; use x509_parser::{certificate::X509Certificate, prelude::FromDer}; - let cert = X509Certificate::from_der(certificate.0.as_ref()) + let cert = X509Certificate::from_der(certificate.as_ref()) .map_err(|e| rustls::Error::General(e.to_string()))?; let spki = cert.1.public_key(); let public_key_bytes = diff --git a/crates/sui-tls/src/lib.rs b/crates/sui-tls/src/lib.rs index 39d576959ab2b..91a6cb666b267 100644 --- a/crates/sui-tls/src/lib.rs +++ b/crates/sui-tls/src/lib.rs @@ -21,8 +21,10 @@ mod tests { use super::*; use fastcrypto::ed25519::Ed25519KeyPair; use fastcrypto::traits::KeyPair; - use rustls::client::ServerCertVerifier as _; - use rustls::server::ClientCertVerifier as _; + use rustls::client::danger::ServerCertVerifier as _; + use rustls::pki_types::ServerName; + use rustls::pki_types::UnixTime; + use rustls::server::danger::ClientCertVerifier as _; #[test] fn verify_allowall() { @@ -38,11 +40,7 @@ mod tests { // The bob passes validation verifier - .verify_client_cert( - &random_cert_bob.rustls_certificate(), - &[], - std::time::SystemTime::now(), - ) + .verify_client_cert(&random_cert_bob.rustls_certificate(), &[], UnixTime::now()) .unwrap(); // The alice passes validation @@ -50,7 +48,7 @@ mod tests { .verify_client_cert( &random_cert_alice.rustls_certificate(), &[], - std::time::SystemTime::now(), + UnixTime::now(), ) .unwrap(); } @@ -74,10 +72,9 @@ mod tests { .verify_server_cert( &random_cert_bob.rustls_certificate(), &[], - &rustls::ServerName::try_from("example.com").unwrap(), - &mut Vec::<&[u8]>::new().iter().cloned(), + &ServerName::try_from("example.com").unwrap(), &[], - std::time::SystemTime::now(), + UnixTime::now(), ) .unwrap(); @@ -86,10 +83,9 @@ mod tests { .verify_server_cert( &random_cert_alice.rustls_certificate(), &[], - &rustls::ServerName::try_from("example.com").unwrap(), - &mut Vec::<&[u8]>::new().iter().cloned(), + &ServerName::try_from("example.com").unwrap(), &[], - std::time::SystemTime::now(), + UnixTime::now(), ) .unwrap_err(); assert!( @@ -123,20 +119,12 @@ mod tests { // The allowed cert passes validation verifier - .verify_client_cert( - &allowed_cert.rustls_certificate(), - &[], - std::time::SystemTime::now(), - ) + .verify_client_cert(&allowed_cert.rustls_certificate(), &[], UnixTime::now()) .unwrap(); // The disallowed cert fails validation let err = verifier - .verify_client_cert( - &disallowed_cert.rustls_certificate(), - &[], - std::time::SystemTime::now(), - ) + .verify_client_cert(&disallowed_cert.rustls_certificate(), &[], UnixTime::now()) .unwrap_err(); assert!( matches!(err, rustls::Error::General(_)), @@ -146,11 +134,7 @@ mod tests { // After removing the allowed public key from the set it now fails validation allowlist.inner_mut().write().unwrap().clear(); let err = verifier - .verify_client_cert( - &allowed_cert.rustls_certificate(), - &[], - std::time::SystemTime::now(), - ) + .verify_client_cert(&allowed_cert.rustls_certificate(), &[], UnixTime::now()) .unwrap_err(); assert!( matches!(err, rustls::Error::General(_)), @@ -178,11 +162,7 @@ mod tests { // 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(), - &[], - std::time::SystemTime::now(), - ) + .verify_client_cert(&cert.rustls_certificate(), &[], UnixTime::now()) .unwrap_err(); assert_eq!( err, @@ -198,10 +178,9 @@ mod tests { .verify_server_cert( &cert.rustls_certificate(), &[], - &rustls::ServerName::try_from("example.com").unwrap(), - &mut Vec::<&[u8]>::new().iter().cloned(), + &ServerName::try_from("example.com").unwrap(), &[], - std::time::SystemTime::now(), + UnixTime::now(), ) .unwrap_err(); assert_eq!( diff --git a/crates/sui-tls/src/verifier.rs b/crates/sui-tls/src/verifier.rs index e3cb37ef717c0..b2dff221ffd7c 100644 --- a/crates/sui-tls/src/verifier.rs +++ b/crates/sui-tls/src/verifier.rs @@ -3,12 +3,24 @@ use fastcrypto::ed25519::Ed25519PublicKey; use fastcrypto::traits::ToFromBytes; +use rustls::crypto::WebPkiSupportedAlgorithms; +use rustls::pki_types::CertificateDer; +use rustls::pki_types::PrivateKeyDer; +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}, }; -static SUPPORTED_SIG_ALGS: &[&webpki::SignatureAlgorithm] = &[&webpki::ED25519]; +static SUPPORTED_SIG_ALGS: &[&dyn SignatureVerificationAlgorithm] = &[webpki::ring::ED25519]; + +static SUPPORTED_ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms { + all: SUPPORTED_SIG_ALGS, + mapping: &[(rustls::SignatureScheme::ED25519, SUPPORTED_SIG_ALGS)], +}; pub type ValidatorAllowlist = Arc>>; @@ -16,12 +28,12 @@ pub type ValidatorAllowlist = Arc>>; /// 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: Send + Sync { +pub trait Allower: std::fmt::Debug + Send + Sync { fn allowed(&self, key: &Ed25519PublicKey) -> bool; } /// AllowAll will allow all public certificates to be validated, it fails open -#[derive(Clone, Default)] +#[derive(Debug, Clone, Default)] pub struct AllowAll; impl Allower for AllowAll { @@ -32,7 +44,7 @@ impl Allower for AllowAll { /// HashSetAllow restricts keys to those that are found in the member set. non-members will not be /// allowed. -#[derive(Clone, Default)] +#[derive(Debug, Clone, Default)] pub struct HashSetAllow { inner: ValidatorAllowlist, } @@ -76,20 +88,22 @@ impl ClientCertVerifier { impl ClientCertVerifier { pub fn rustls_server_config( self, - certificates: Vec, - private_key: rustls::PrivateKey, + certificates: Vec>, + private_key: PrivateKeyDer<'static>, ) -> Result { - let mut config = rustls::ServerConfig::builder() - .with_safe_defaults() - .with_client_cert_verifier(std::sync::Arc::new(self)) - .with_single_cert(certificates, private_key)?; + let mut config = rustls::ServerConfig::builder_with_provider(Arc::new( + rustls::crypto::ring::default_provider(), + )) + .with_safe_default_protocol_versions()? + .with_client_cert_verifier(std::sync::Arc::new(self)) + .with_single_cert(certificates, private_key)?; config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; Ok(config) } } -impl rustls::server::ClientCertVerifier for ClientCertVerifier { +impl rustls::server::danger::ClientCertVerifier for ClientCertVerifier { fn offer_client_auth(&self) -> bool { true } @@ -98,7 +112,7 @@ impl rustls::server::ClientCertVerifier for ClientCertVerifier { true } - fn client_auth_root_subjects(&self) -> &[rustls::DistinguishedName] { + fn root_hint_subjects(&self) -> &[rustls::DistinguishedName] { // Since we're relying on self-signed certificates and not on CAs, continue the handshake // without passing a list of CA DNs &[] @@ -110,10 +124,10 @@ impl rustls::server::ClientCertVerifier for ClientCertVerifier { // 2. we call webpki's certificate verification fn verify_client_cert( &self, - end_entity: &rustls::Certificate, - intermediates: &[rustls::Certificate], - now: std::time::SystemTime, - ) -> Result { + end_entity: &CertificateDer, + intermediates: &[CertificateDer], + now: UnixTime, + ) -> Result { // Step 1: Check this matches the key we expect let public_key = public_key_from_certificate(end_entity)?; if !self.allower.allowed(&public_key) { @@ -131,7 +145,29 @@ impl rustls::server::ClientCertVerifier for ClientCertVerifier { &self.name, now, ) - .map(|_| rustls::server::ClientCertVerified::assertion()) + .map(|_| rustls::server::danger::ClientCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + rustls::crypto::verify_tls12_signature(message, cert, dss, &SUPPORTED_ALGORITHMS) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + rustls::crypto::verify_tls13_signature(message, cert, dss, &SUPPORTED_ALGORITHMS) + } + + fn supported_verify_schemes(&self) -> Vec { + SUPPORTED_ALGORITHMS.supported_schemes() } } @@ -150,28 +186,30 @@ impl ServerCertVerifier { pub fn rustls_client_config( self, - certificates: Vec, - private_key: rustls::PrivateKey, + certificates: Vec>, + private_key: PrivateKeyDer<'static>, ) -> Result { - let mut config = rustls::ClientConfig::builder() - .with_safe_defaults() - .with_custom_certificate_verifier(std::sync::Arc::new(self)) - .with_client_auth_cert(certificates, private_key)?; + let mut config = rustls::ClientConfig::builder_with_provider(Arc::new( + rustls::crypto::ring::default_provider(), + )) + .with_safe_default_protocol_versions()? + .dangerous() + .with_custom_certificate_verifier(std::sync::Arc::new(self)) + .with_client_auth_cert(certificates, private_key)?; config.alpn_protocols = vec![b"h2".to_vec()]; Ok(config) } } -impl rustls::client::ServerCertVerifier for ServerCertVerifier { +impl rustls::client::danger::ServerCertVerifier for ServerCertVerifier { fn verify_server_cert( &self, - end_entity: &rustls::Certificate, - intermediates: &[rustls::Certificate], - _server_name: &rustls::ServerName, - _scts: &mut dyn Iterator, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + _server_name: &ServerName, _ocsp_response: &[u8], - now: std::time::SystemTime, - ) -> Result { + now: UnixTime, + ) -> Result { let public_key = public_key_from_certificate(end_entity)?; if public_key != self.public_key { return Err(rustls::Error::General(format!( @@ -187,7 +225,29 @@ impl rustls::client::ServerCertVerifier for ServerCertVerifier { &self.name, now, ) - .map(|_| rustls::client::ServerCertVerified::assertion()) + .map(|_| rustls::client::danger::ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + rustls::crypto::verify_tls12_signature(message, cert, dss, &SUPPORTED_ALGORITHMS) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + rustls::crypto::verify_tls13_signature(message, cert, dss, &SUPPORTED_ALGORITHMS) + } + + fn supported_verify_schemes(&self) -> Vec { + SUPPORTED_ALGORITHMS.supported_schemes() } } @@ -196,49 +256,55 @@ impl rustls::client::ServerCertVerifier for ServerCertVerifier { // placing the public key at the root of the certificate chain (as it should be for a self-signed certificate) // 2. we call webpki's certificate verification fn verify_self_signed_cert( - end_entity: &rustls::Certificate, - intermediates: &[rustls::Certificate], + end_entity: &CertificateDer, + intermediates: &[CertificateDer], usage: webpki::KeyUsage, name: &str, - now: std::time::SystemTime, + now: UnixTime, ) -> Result<(), rustls::Error> { // Check we're receiving correctly signed data with the expected key // Step 1: prepare arguments let (cert, chain, trustroots) = prepare_for_self_signed(end_entity, intermediates)?; - let now = webpki::Time::try_from(now).map_err(|_| rustls::Error::FailedToGetCurrentTime)?; // Step 2: call verification from webpki - let cert = cert - .verify_for_usage(SUPPORTED_SIG_ALGS, &trustroots, &chain, now, usage, &[]) - .map_err(pki_error) - .map(|_| cert)?; + let verified_cert = cert + .verify_for_usage( + SUPPORTED_SIG_ALGS, + &trustroots, + chain, + now, + usage, + None, + None, + ) + .map_err(pki_error)?; // Ensure the cert is valid for the network name - let dns_nameref = webpki::SubjectNameRef::try_from_ascii_str(name) - .map_err(|_| rustls::Error::UnsupportedNameType)?; - cert.verify_is_valid_for_subject_name(dns_nameref) + let subject_name = + ServerName::try_from(name).map_err(|_| rustls::Error::UnsupportedNameType)?; + verified_cert + .end_entity() + .verify_is_valid_for_subject_name(&subject_name) .map_err(pki_error) } type CertChainAndRoots<'a> = ( webpki::EndEntityCert<'a>, - Vec<&'a [u8]>, - Vec>, + &'a [CertificateDer<'a>], + Vec>, ); // This prepares arguments for webpki, including a trust anchor which is the end entity of the certificate // (which embodies a self-signed certificate by definition) fn prepare_for_self_signed<'a>( - end_entity: &'a rustls::Certificate, - intermediates: &'a [rustls::Certificate], + end_entity: &'a CertificateDer, + intermediates: &'a [CertificateDer], ) -> Result, rustls::Error> { // EE cert must appear first. - let cert = webpki::EndEntityCert::try_from(end_entity.0.as_ref()).map_err(pki_error)?; - - let intermediates: Vec<&'a [u8]> = intermediates.iter().map(|cert| cert.0.as_ref()).collect(); + let cert = webpki::EndEntityCert::try_from(end_entity).map_err(pki_error)?; // reinterpret the certificate as a root, materializing the self-signed policy - let root = webpki::TrustAnchor::try_from_cert_der(end_entity.0.as_ref()).map_err(pki_error)?; + let root = webpki::anchor_from_trusted_cert(end_entity).map_err(pki_error)?; Ok((cert, intermediates, vec![root])) } @@ -263,11 +329,11 @@ fn pki_error(error: webpki::Error) -> rustls::Error { /// Extracts the public key from a certificate. pub fn public_key_from_certificate( - certificate: &rustls::Certificate, + certificate: &CertificateDer, ) -> Result { use x509_parser::{certificate::X509Certificate, prelude::FromDer}; - let cert = X509Certificate::from_der(certificate.0.as_ref()) + let cert = X509Certificate::from_der(certificate.as_ref()) .map_err(|e| rustls::Error::General(e.to_string()))?; let spki = cert.1.public_key(); let public_key_bytes = diff --git a/crates/suiop-cli/src/cli/lib/oauth/mod.rs b/crates/suiop-cli/src/cli/lib/oauth/mod.rs index f33908e25c5f6..d882d0029efd8 100644 --- a/crates/suiop-cli/src/cli/lib/oauth/mod.rs +++ b/crates/suiop-cli/src/cli/lib/oauth/mod.rs @@ -10,7 +10,6 @@ use axum::response::IntoResponse; use axum::{extract::Query, routing::get, Router}; use chrono; use dirs; -use reqwest; use reqwest::header::{HeaderMap, HeaderValue}; use serde::{Deserialize, Serialize}; use std::fs::{self, File}; @@ -155,8 +154,8 @@ fn spawn_local_server( ); let addr = SocketAddr::from(([127, 0, 0, 1], 17846)); - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app) .await .expect("couldn't start local auth server on port 17846"); }) diff --git a/deny.toml b/deny.toml index 372fba7ae9b05..b4392cda79f08 100644 --- a/deny.toml +++ b/deny.toml @@ -57,15 +57,10 @@ ignore = [ "RUSTSEC-2023-0049", # ansi_term is Unmaintained "RUSTSEC-2021-0139", - # atty - # "RUSTSEC-2021-0145", # webpki "RUSTSEC-2023-0052", # we don't do RSA signing on Sui (only verifying for zklogin) "RUSTSEC-2023-0071", - # Sui does not use object_store with authentication. - # Upgrade to object_store >= 10.2 to fix. - "RUSTSEC-2024-0358", # A few dependencies use unpatched rustls. "RUSTSEC-2024-0336", ] diff --git a/narwhal/network/Cargo.toml b/narwhal/network/Cargo.toml index 4a97a99051dbc..f13a037099151 100644 --- a/narwhal/network/Cargo.toml +++ b/narwhal/network/Cargo.toml @@ -28,7 +28,6 @@ anemo.workspace = true anemo-tower.workspace = true anyhow.workspace = true axum.workspace = true -axum-server.workspace = true tower.workspace = true [dev-dependencies] diff --git a/narwhal/network/src/admin.rs b/narwhal/network/src/admin.rs index ddf660d4dbe05..02ba0b8f7a811 100644 --- a/narwhal/network/src/admin.rs +++ b/narwhal/network/src/admin.rs @@ -2,9 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use axum::{extract::Extension, http::StatusCode, routing::get, Json, Router}; -use mysten_metrics::{spawn_logged_monitored_task, spawn_monitored_task}; -use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener}; +use mysten_metrics::spawn_logged_monitored_task; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::time::Duration; +use tokio::net::TcpListener; use tokio::task::JoinHandle; use tokio::time::sleep; use tracing::{error, info}; @@ -27,15 +28,7 @@ pub fn start_admin_server( "starting admin server" ); - let handle = axum_server::Handle::new(); - let shutdown_handle = handle.clone(); - let mut handles = Vec::new(); - // Spawn a task to shutdown server. - handles.push(spawn_monitored_task!(async move { - _ = tr_shutdown.receiver.recv().await; - handle.clone().shutdown(); - })); handles.push(spawn_logged_monitored_task!( async move { @@ -45,11 +38,12 @@ pub fn start_admin_server( loop { total_retries -= 1; - match TcpListener::bind(socket_address) { + match TcpListener::bind(socket_address).await { Ok(listener) => { - axum_server::from_tcp(listener) - .handle(shutdown_handle) - .serve(router.into_make_service()) + axum::serve(listener, router) + .with_graceful_shutdown(async move { + _ = tr_shutdown.receiver.recv().await; + }) .await .unwrap_or_else(|err| { panic!("Failed to boot admin {}: {err}", socket_address) diff --git a/narwhal/node/src/metrics.rs b/narwhal/node/src/metrics.rs index 772c07a48aab1..8e3e06794bdc8 100644 --- a/narwhal/node/src/metrics.rs +++ b/narwhal/node/src/metrics.rs @@ -105,10 +105,8 @@ pub fn start_prometheus_server(addr: Multiaddr, registry: &Registry) -> JoinHand spawn_logged_monitored_task!( async move { - axum::Server::bind(&socket_addr) - .serve(app.into_make_service()) - .await - .unwrap(); + let listener = tokio::net::TcpListener::bind(&socket_addr).await.unwrap(); + axum::serve(listener, app).await.unwrap(); }, "MetricsServerTask" ) diff --git a/narwhal/primary/tests/nodes_bootstrapping_tests.rs b/narwhal/primary/tests/nodes_bootstrapping_tests.rs index 60c0c2293c5d5..373676bfe7cb1 100644 --- a/narwhal/primary/tests/nodes_bootstrapping_tests.rs +++ b/narwhal/primary/tests/nodes_bootstrapping_tests.rs @@ -40,12 +40,7 @@ async fn test_response_error_after_shutdown_internal_consensus() { let Err(e) = client.submit_transaction(txn).await else { panic!("Submitting transactions after Narwhal shutdown should fail!"); }; - assert!( - e.message() - .contains("error trying to connect: tcp connect error:"), - "Actual: {}", - e - ); + assert!(e.message().contains("tcp connect error:"), "Actual: {}", e); } /// Nodes will be started in a staggered fashion. This is simulating From 5b8b0505cd36da65fad25181a2c311dac74da61b Mon Sep 17 00:00:00 2001 From: Andrew Schran Date: Fri, 26 Jul 2024 11:33:28 -0400 Subject: [PATCH 004/232] Corrected filter for `validator_tx_finalizer_e2e_tests` (#18811) --- scripts/simtest/simtest-run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/simtest/simtest-run.sh b/scripts/simtest/simtest-run.sh index 36f22837df2bd..0272501f62f52 100755 --- a/scripts/simtest/simtest-run.sh +++ b/scripts/simtest/simtest-run.sh @@ -20,7 +20,7 @@ if [ -z "$NUM_CPUS" ]; then fi # filter out some tests that give spurious failures. -TEST_FILTER="(not (test(~batch_verification_tests) | test(~validator_tx_finalizer_e2e_tests)))" +TEST_FILTER="(not (test(~batch_verification_tests) | test(~test_validator_tx_finalizer_)))" DATE=$(date +%s) SEED="$DATE" From a45f461a10c54b1688d1a874e7a16129d110d5b0 Mon Sep 17 00:00:00 2001 From: hayes-mysten <135670682+hayes-mysten@users.noreply.github.com> Date: Fri, 26 Jul 2024 09:57:51 -0700 Subject: [PATCH 005/232] =?UTF-8?q?[sdk]=20mark=20shared=20inputs=20passed?= =?UTF-8?q?=20to=20makeMoveVec,=20mergeCoins,=20and=20split=E2=80=A6=20(#1?= =?UTF-8?q?8812)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …Coins as mutable ## Description Describe the changes or additions included in this PR. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .changeset/shaggy-dolls-smash.md | 5 +++++ sdk/typescript/src/transactions/json-rpc-resolver.ts | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 .changeset/shaggy-dolls-smash.md diff --git a/.changeset/shaggy-dolls-smash.md b/.changeset/shaggy-dolls-smash.md new file mode 100644 index 0000000000000..4371ff8816542 --- /dev/null +++ b/.changeset/shaggy-dolls-smash.md @@ -0,0 +1,5 @@ +--- +'@mysten/sui': patch +--- + +Shared objects passed to MakeMoveVec, MergeCoins, and SplitCoin are now marked as mutable diff --git a/sdk/typescript/src/transactions/json-rpc-resolver.ts b/sdk/typescript/src/transactions/json-rpc-resolver.ts index 7ca134f1d54b8..ee364f012d3a8 100644 --- a/sdk/typescript/src/transactions/json-rpc-resolver.ts +++ b/sdk/typescript/src/transactions/json-rpc-resolver.ts @@ -436,6 +436,10 @@ function isUsedAsMutable(transactionData: TransactionDataBuilder, index: number) const argIndex = tx.MoveCall.arguments.indexOf(arg); usedAsMutable = tx.MoveCall._argumentTypes[argIndex].ref !== '&' || usedAsMutable; } + + if (tx.$kind === 'MakeMoveVec' || tx.$kind === 'MergeCoins' || tx.$kind === 'SplitCoins') { + usedAsMutable = true; + } }); return usedAsMutable; From c1a75c997fcbff71a09e4156b27b2d3ee2ef58df Mon Sep 17 00:00:00 2001 From: shangchenglumetro Date: Sat, 27 Jul 2024 02:07:25 +0900 Subject: [PATCH 006/232] chore: fix some comments (#18803) ## Description fix some comments ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- apps/wallet/src/background/accounts/zklogin/utils.ts | 2 +- crates/sui-package-resolver/src/lib.rs | 2 +- examples/move/crypto/bls_signature/sources/example.move | 2 +- examples/tic-tac-toe/README.md | 2 +- external-crates/move/crates/move-vm-runtime/src/loader.rs | 2 +- sdk/docs/pages/typescript/executors.mdx | 2 +- sdk/docs/pages/typescript/migrations/sui-1.0.mdx | 2 +- sui-execution/README.md | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/wallet/src/background/accounts/zklogin/utils.ts b/apps/wallet/src/background/accounts/zklogin/utils.ts index 1b937090ed32d..e4234f6334f98 100644 --- a/apps/wallet/src/background/accounts/zklogin/utils.ts +++ b/apps/wallet/src/background/accounts/zklogin/utils.ts @@ -34,7 +34,7 @@ export function prepareZkLogin(currentEpoch: number) { const forceSilentGetProviders: ZkLoginProvider[] = ['twitch']; /** - * This method does a get request to the authorize url and is used as a workarround + * This method does a get request to the authorize url and is used as a workaround * for `forceSilentGetProviders` that they do the silent login/token refresh using * html directives or js code to redirect to the redirect_url (instead of response headers) and that forces the launchWebAuthFlow * to open and close quickly a new window. Which closes the popup window when open but also creates a weird flickering effect. diff --git a/crates/sui-package-resolver/src/lib.rs b/crates/sui-package-resolver/src/lib.rs index e23916ec8ffd4..17f1118e591d1 100644 --- a/crates/sui-package-resolver/src/lib.rs +++ b/crates/sui-package-resolver/src/lib.rs @@ -126,7 +126,7 @@ pub enum ErrorConstants { /// No constant information is available, only a line number. None, /// The error is a complete error, with an error identifier and constant that can be rendered. - /// The the rendered string representation of the constant is returned only when the contant + /// The rendered string representation of the constant is returned only when the contant /// value is one of the following types: /// * A vector of bytes convertible to a valid UTF-8 string; or /// * A numeric value (u8, u16, u32, u64, u128, u256); or diff --git a/examples/move/crypto/bls_signature/sources/example.move b/examples/move/crypto/bls_signature/sources/example.move index 5df1bc4b65678..387ba93089b1d 100644 --- a/examples/move/crypto/bls_signature/sources/example.move +++ b/examples/move/crypto/bls_signature/sources/example.move @@ -1,7 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -/// Examples of BLS siganture related operations. +/// Examples of BLS signature related operations. module bls_signature::example { use sui::bls12381; use std::hash::sha2_256; diff --git a/examples/tic-tac-toe/README.md b/examples/tic-tac-toe/README.md index 4f70705d9b466..a197f0f1d0ebe 100644 --- a/examples/tic-tac-toe/README.md +++ b/examples/tic-tac-toe/README.md @@ -109,7 +109,7 @@ The multisig account does not own anything other than the game object (it does not have any gas coins of its own), so the player sponsors the transaction, using one of its own gas coins. -Sharing a resource while avoiding consensus by transfering it to a +Sharing a resource while avoiding consensus by transferring it to a multisig account can be generalized from two accounts to a max of ten (the limit being the number of keys that can be associated with one multisig). diff --git a/external-crates/move/crates/move-vm-runtime/src/loader.rs b/external-crates/move/crates/move-vm-runtime/src/loader.rs index 572aa9e787ed1..dd23015d2ab64 100644 --- a/external-crates/move/crates/move-vm-runtime/src/loader.rs +++ b/external-crates/move/crates/move-vm-runtime/src/loader.rs @@ -2355,7 +2355,7 @@ pub const VALUE_DEPTH_MAX: u64 = 128; /// Maximal nodes which are allowed when converting to layout. This includes the types of /// fields for struct types. -/// Maximal nodes which are allowed when converting to layout. This includes the the types of +/// Maximal nodes which are allowed when converting to layout. This includes the types of /// fields for datatypes. const MAX_TYPE_TO_LAYOUT_NODES: u64 = 256; diff --git a/sdk/docs/pages/typescript/executors.mdx b/sdk/docs/pages/typescript/executors.mdx index 381130b5b91fb..2080f26af6908 100644 --- a/sdk/docs/pages/typescript/executors.mdx +++ b/sdk/docs/pages/typescript/executors.mdx @@ -83,7 +83,7 @@ way that avoids conflicts between transactions using the same object ids. (default 20) - `initialCoinBalance`: The balance of new coins created for the gas pool in MIST (default `200_000_000n`), -- `minimumCoinBalance`: After executing a transaction, the the gasCoin will be reused unless it's +- `minimumCoinBalance`: After executing a transaction, the gasCoin will be reused unless it's balance is below this value (default `50_000_000n`), - `defaultBudget`: The default budget for transactions, which will be used if the transaction does not specify a budget (default `minimumCoinBalance`), diff --git a/sdk/docs/pages/typescript/migrations/sui-1.0.mdx b/sdk/docs/pages/typescript/migrations/sui-1.0.mdx index 5c19479925551..5bc1abde61337 100644 --- a/sdk/docs/pages/typescript/migrations/sui-1.0.mdx +++ b/sdk/docs/pages/typescript/migrations/sui-1.0.mdx @@ -352,7 +352,7 @@ of transactionBlockBytes ## `useSignAndExecuteTransactionBlock` The `useSignAndExecuteTransactionBlock` has been named to `useSignAndExecuteTransaction` and -redesigned to work the the updated wallet-standard methods. +redesigned to work the updated wallet-standard methods. `useSignAndExecuteTransaction` no-longer accepts the options passed to `suiClient.executeTransactionBlock` for returning additional data. diff --git a/sui-execution/README.md b/sui-execution/README.md index bdf9f62b26882..6ea2ee002b5ba 100644 --- a/sui-execution/README.md +++ b/sui-execution/README.md @@ -3,7 +3,7 @@ The `sui-execution` crate is responsible for abstracting access to the execution layer. It allows us to isolate big changes to the execution layer that need to be gated behind protocol config changes, to -minimise the risk of inadvertantly changing behaviour that is relevant +minimise the risk of inadvertently changing behaviour that is relevant for state sync (which would cause a fork). The Execution Layer include: From e3de95064e9afa3c603721c667e9bdaf8474a533 Mon Sep 17 00:00:00 2001 From: "sui-merge-bot[bot]" <114704316+sui-merge-bot[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 17:19:37 +0000 Subject: [PATCH 007/232] Version Packages (#18814) This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @mysten/create-dapp@0.3.13 ### Patch Changes - Updated dependencies [a45f461] - @mysten/sui@1.3.1 - @mysten/dapp-kit@0.14.13 ## @mysten/dapp-kit@0.14.13 ### Patch Changes - Updated dependencies [a45f461] - @mysten/sui@1.3.1 - @mysten/wallet-standard@0.12.13 - @mysten/zksend@0.10.2 ## @mysten/deepbook@0.8.12 ### Patch Changes - Updated dependencies [a45f461] - @mysten/sui@1.3.1 ## @mysten/enoki@0.3.12 ### Patch Changes - Updated dependencies [a45f461] - @mysten/sui@1.3.1 - @mysten/zklogin@0.7.12 ## @mysten/graphql-transport@0.2.12 ### Patch Changes - Updated dependencies [a45f461] - @mysten/sui@1.3.1 ## @mysten/kiosk@0.9.12 ### Patch Changes - Updated dependencies [a45f461] - @mysten/sui@1.3.1 ## @mysten/suins-toolkit@0.5.12 ### Patch Changes - Updated dependencies [a45f461] - @mysten/sui@1.3.1 ## @mysten/sui@1.3.1 ### Patch Changes - a45f461: Shared objects passed to MakeMoveVec, MergeCoins, and SplitCoin are now marked as mutable ## @mysten/wallet-standard@0.12.13 ### Patch Changes - Updated dependencies [a45f461] - @mysten/sui@1.3.1 ## @mysten/zklogin@0.7.12 ### Patch Changes - Updated dependencies [a45f461] - @mysten/sui@1.3.1 ## @mysten/zksend@0.10.2 ### Patch Changes - Updated dependencies [a45f461] - @mysten/sui@1.3.1 - @mysten/wallet-standard@0.12.13 Co-authored-by: github-actions[bot] --- .changeset/shaggy-dolls-smash.md | 5 ----- sdk/create-dapp/CHANGELOG.md | 8 ++++++++ sdk/create-dapp/package.json | 2 +- sdk/dapp-kit/CHANGELOG.md | 9 +++++++++ sdk/dapp-kit/package.json | 2 +- sdk/deepbook/CHANGELOG.md | 7 +++++++ sdk/deepbook/package.json | 2 +- sdk/enoki/CHANGELOG.md | 8 ++++++++ sdk/enoki/package.json | 2 +- sdk/graphql-transport/CHANGELOG.md | 7 +++++++ sdk/graphql-transport/package.json | 2 +- sdk/kiosk/CHANGELOG.md | 7 +++++++ sdk/kiosk/package.json | 2 +- sdk/suins-toolkit/CHANGELOG.md | 7 +++++++ sdk/suins-toolkit/package.json | 2 +- sdk/typescript/CHANGELOG.md | 6 ++++++ sdk/typescript/package.json | 2 +- sdk/typescript/src/version.ts | 4 ++-- sdk/wallet-standard/CHANGELOG.md | 7 +++++++ sdk/wallet-standard/package.json | 2 +- sdk/zklogin/CHANGELOG.md | 7 +++++++ sdk/zklogin/package.json | 2 +- sdk/zksend/CHANGELOG.md | 8 ++++++++ sdk/zksend/package.json | 2 +- 24 files changed, 94 insertions(+), 18 deletions(-) delete mode 100644 .changeset/shaggy-dolls-smash.md diff --git a/.changeset/shaggy-dolls-smash.md b/.changeset/shaggy-dolls-smash.md deleted file mode 100644 index 4371ff8816542..0000000000000 --- a/.changeset/shaggy-dolls-smash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@mysten/sui': patch ---- - -Shared objects passed to MakeMoveVec, MergeCoins, and SplitCoin are now marked as mutable diff --git a/sdk/create-dapp/CHANGELOG.md b/sdk/create-dapp/CHANGELOG.md index cb300f30d4b5d..7dc995b870935 100644 --- a/sdk/create-dapp/CHANGELOG.md +++ b/sdk/create-dapp/CHANGELOG.md @@ -1,5 +1,13 @@ # @mysten/create-dapp +## 0.3.13 + +### Patch Changes + +- Updated dependencies [a45f461] + - @mysten/sui@1.3.1 + - @mysten/dapp-kit@0.14.13 + ## 0.3.12 ### Patch Changes diff --git a/sdk/create-dapp/package.json b/sdk/create-dapp/package.json index 1416a064bb092..6659c4ad1b4f6 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.12", + "version": "0.3.13", "license": "Apache-2.0", "files": [ "CHANGELOG.md", diff --git a/sdk/dapp-kit/CHANGELOG.md b/sdk/dapp-kit/CHANGELOG.md index 36fef53cf62cc..f135f0a73ec72 100644 --- a/sdk/dapp-kit/CHANGELOG.md +++ b/sdk/dapp-kit/CHANGELOG.md @@ -1,5 +1,14 @@ # @mysten/dapp-kit +## 0.14.13 + +### Patch Changes + +- Updated dependencies [a45f461] + - @mysten/sui@1.3.1 + - @mysten/wallet-standard@0.12.13 + - @mysten/zksend@0.10.2 + ## 0.14.12 ### Patch Changes diff --git a/sdk/dapp-kit/package.json b/sdk/dapp-kit/package.json index 595fc54315cca..e0434880360d3 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.12", + "version": "0.14.13", "license": "Apache-2.0", "files": [ "CHANGELOG.md", diff --git a/sdk/deepbook/CHANGELOG.md b/sdk/deepbook/CHANGELOG.md index 5868cf6fddd58..af43215170667 100644 --- a/sdk/deepbook/CHANGELOG.md +++ b/sdk/deepbook/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/deepbook +## 0.8.12 + +### Patch Changes + +- Updated dependencies [a45f461] + - @mysten/sui@1.3.1 + ## 0.8.11 ### Patch Changes diff --git a/sdk/deepbook/package.json b/sdk/deepbook/package.json index 95e1079380dc0..13a37f92d87a8 100644 --- a/sdk/deepbook/package.json +++ b/sdk/deepbook/package.json @@ -2,7 +2,7 @@ "name": "@mysten/deepbook", "author": "Mysten Labs ", "description": "Sui Deepbook SDK", - "version": "0.8.11", + "version": "0.8.12", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", diff --git a/sdk/enoki/CHANGELOG.md b/sdk/enoki/CHANGELOG.md index 0b95b8580721e..6eef2d643958d 100644 --- a/sdk/enoki/CHANGELOG.md +++ b/sdk/enoki/CHANGELOG.md @@ -1,5 +1,13 @@ # @mysten/enoki +## 0.3.12 + +### Patch Changes + +- Updated dependencies [a45f461] + - @mysten/sui@1.3.1 + - @mysten/zklogin@0.7.12 + ## 0.3.11 ### Patch Changes diff --git a/sdk/enoki/package.json b/sdk/enoki/package.json index 74e053d1c6ccc..0e3a43927fbcb 100644 --- a/sdk/enoki/package.json +++ b/sdk/enoki/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/enoki", - "version": "0.3.11", + "version": "0.3.12", "description": "TODO: Description", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/graphql-transport/CHANGELOG.md b/sdk/graphql-transport/CHANGELOG.md index 020caa3d4755d..f3bfe224d25a0 100644 --- a/sdk/graphql-transport/CHANGELOG.md +++ b/sdk/graphql-transport/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/graphql-transport +## 0.2.12 + +### Patch Changes + +- Updated dependencies [a45f461] + - @mysten/sui@1.3.1 + ## 0.2.11 ### Patch Changes diff --git a/sdk/graphql-transport/package.json b/sdk/graphql-transport/package.json index c22203491491f..0be11e4a40531 100644 --- a/sdk/graphql-transport/package.json +++ b/sdk/graphql-transport/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/graphql-transport", - "version": "0.2.11", + "version": "0.2.12", "description": "A GraphQL transport to allow SuiClient to work with RPC 2.0", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/kiosk/CHANGELOG.md b/sdk/kiosk/CHANGELOG.md index d21c10dc613d6..891253731a6ba 100644 --- a/sdk/kiosk/CHANGELOG.md +++ b/sdk/kiosk/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/kiosk +## 0.9.12 + +### Patch Changes + +- Updated dependencies [a45f461] + - @mysten/sui@1.3.1 + ## 0.9.11 ### Patch Changes diff --git a/sdk/kiosk/package.json b/sdk/kiosk/package.json index d11095d07d8b2..695df31903cc8 100644 --- a/sdk/kiosk/package.json +++ b/sdk/kiosk/package.json @@ -2,7 +2,7 @@ "name": "@mysten/kiosk", "author": "Mysten Labs ", "description": "Sui Kiosk library", - "version": "0.9.11", + "version": "0.9.12", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", diff --git a/sdk/suins-toolkit/CHANGELOG.md b/sdk/suins-toolkit/CHANGELOG.md index 807a6c1735e60..4acb88d72e4d5 100644 --- a/sdk/suins-toolkit/CHANGELOG.md +++ b/sdk/suins-toolkit/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/suins-toolkit +## 0.5.12 + +### Patch Changes + +- Updated dependencies [a45f461] + - @mysten/sui@1.3.1 + ## 0.5.11 ### Patch Changes diff --git a/sdk/suins-toolkit/package.json b/sdk/suins-toolkit/package.json index f0b2527f0a660..9b1317563af6d 100644 --- a/sdk/suins-toolkit/package.json +++ b/sdk/suins-toolkit/package.json @@ -2,7 +2,7 @@ "name": "@mysten/suins-toolkit", "author": "Mysten Labs ", "description": "SuiNS TypeScript SDK", - "version": "0.5.11", + "version": "0.5.12", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", diff --git a/sdk/typescript/CHANGELOG.md b/sdk/typescript/CHANGELOG.md index 8185e0a3ba7e1..50a7a169572fd 100644 --- a/sdk/typescript/CHANGELOG.md +++ b/sdk/typescript/CHANGELOG.md @@ -1,5 +1,11 @@ # @mysten/sui.js +## 1.3.1 + +### Patch Changes + +- a45f461: Shared objects passed to MakeMoveVec, MergeCoins, and SplitCoin are now marked as mutable + ## 1.3.0 ### Minor Changes diff --git a/sdk/typescript/package.json b/sdk/typescript/package.json index 8720d52d8b12b..3a36f2dbc3bb1 100644 --- a/sdk/typescript/package.json +++ b/sdk/typescript/package.json @@ -3,7 +3,7 @@ "author": "Mysten Labs ", "description": "Sui TypeScript API(Work in Progress)", "homepage": "https://sdk.mystenlabs.com", - "version": "1.3.0", + "version": "1.3.1", "license": "Apache-2.0", "sideEffects": false, "files": [ diff --git a/sdk/typescript/src/version.ts b/sdk/typescript/src/version.ts index 86359db32790b..65c5ca24d0111 100644 --- a/sdk/typescript/src/version.ts +++ b/sdk/typescript/src/version.ts @@ -3,5 +3,5 @@ // This file is generated by genversion.mjs. Do not edit it directly. -export const PACKAGE_VERSION = '1.3.0'; -export const TARGETED_RPC_VERSION = '1.30.0'; +export const PACKAGE_VERSION = '1.3.1'; +export const TARGETED_RPC_VERSION = '1.31.0'; diff --git a/sdk/wallet-standard/CHANGELOG.md b/sdk/wallet-standard/CHANGELOG.md index c984dbf0f97d6..28dcd06a3c85e 100644 --- a/sdk/wallet-standard/CHANGELOG.md +++ b/sdk/wallet-standard/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/wallet-standard +## 0.12.13 + +### Patch Changes + +- Updated dependencies [a45f461] + - @mysten/sui@1.3.1 + ## 0.12.12 ### Patch Changes diff --git a/sdk/wallet-standard/package.json b/sdk/wallet-standard/package.json index fa19fdad28ca9..557e319fb512f 100644 --- a/sdk/wallet-standard/package.json +++ b/sdk/wallet-standard/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/wallet-standard", - "version": "0.12.12", + "version": "0.12.13", "description": "A suite of standard utilities for implementing wallets based on the Wallet Standard.", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/zklogin/CHANGELOG.md b/sdk/zklogin/CHANGELOG.md index 378a140cbe213..e1631060c8a43 100644 --- a/sdk/zklogin/CHANGELOG.md +++ b/sdk/zklogin/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/zklogin +## 0.7.12 + +### Patch Changes + +- Updated dependencies [a45f461] + - @mysten/sui@1.3.1 + ## 0.7.11 ### Patch Changes diff --git a/sdk/zklogin/package.json b/sdk/zklogin/package.json index 1f0ed57361b28..dbf79fc4a363c 100644 --- a/sdk/zklogin/package.json +++ b/sdk/zklogin/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/zklogin", - "version": "0.7.11", + "version": "0.7.12", "description": "Utilities for interacting with zkLogin in Sui", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/zksend/CHANGELOG.md b/sdk/zksend/CHANGELOG.md index dc8bfd9852092..f651293603f8c 100644 --- a/sdk/zksend/CHANGELOG.md +++ b/sdk/zksend/CHANGELOG.md @@ -1,5 +1,13 @@ # @mysten/zksend +## 0.10.2 + +### Patch Changes + +- Updated dependencies [a45f461] + - @mysten/sui@1.3.1 + - @mysten/wallet-standard@0.12.13 + ## 0.10.1 ### Patch Changes diff --git a/sdk/zksend/package.json b/sdk/zksend/package.json index 8ab8fbae955c5..7e4cef01284a2 100644 --- a/sdk/zksend/package.json +++ b/sdk/zksend/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/zksend", - "version": "0.10.1", + "version": "0.10.2", "description": "TODO: Write Description", "license": "Apache-2.0", "author": "Mysten Labs ", From 83b9cd6097f028262189e4f3adefaf88ce69d815 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 26 Jul 2024 13:08:41 -0500 Subject: [PATCH 008/232] suiop: fix test to use axum v0.7 --- .../mysten-service-boilerplate/Cargo-ext.toml | 2 +- crates/suiop-cli/Cargo.lock | 2458 ----------------- crates/suiop-cli/tests/integration_tests.rs | 2 +- 3 files changed, 2 insertions(+), 2460 deletions(-) delete mode 100644 crates/suiop-cli/Cargo.lock diff --git a/crates/mysten-service-boilerplate/Cargo-ext.toml b/crates/mysten-service-boilerplate/Cargo-ext.toml index 58014f8a08f47..fe577700f9b31 100644 --- a/crates/mysten-service-boilerplate/Cargo-ext.toml +++ b/crates/mysten-service-boilerplate/Cargo-ext.toml @@ -8,7 +8,7 @@ publish = false [dependencies] anyhow = "1.0.79" -axum = { version = "0.6.6", features = ["macros"] } +axum = { version = "0.7", features = ["macros"] } mysten-service = { git = "https://github.com/mystenlabs/sui.git", branch = "main", package = "mysten-service" } prometheus = "0.13.3" tracing = "0.1.40" diff --git a/crates/suiop-cli/Cargo.lock b/crates/suiop-cli/Cargo.lock deleted file mode 100644 index deb5cfb996e0b..0000000000000 --- a/crates/suiop-cli/Cargo.lock +++ /dev/null @@ -1,2458 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - -[[package]] -name = "asynchronous-codec" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182" -dependencies = [ - "bytes", - "futures-sink", - "futures-util", - "memchr", - "pin-project-lite", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide 0.7.1", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" - -[[package]] -name = "bumpalo" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "jobserver", - "libc", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets 0.48.5", -] - -[[package]] -name = "clap" -version = "4.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" -dependencies = [ - "bitflags 1.3.2", - "clap_derive", - "clap_lex", - "is-terminal", - "once_cell", - "strsim", - "termcolor", -] - -[[package]] -name = "clap_derive" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "clap_lex" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - -[[package]] -name = "colored" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" -dependencies = [ - "is-terminal", - "lazy_static", - "windows-sys 0.48.0", -] - -[[package]] -name = "containers-api" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56082e32f18a6d60f06c736b49e234deffb93b13cb87091a39e0dec053d03819" -dependencies = [ - "chrono", - "flate2", - "futures-util", - "http", - "hyper", - "hyperlocal", - "log", - "mime", - "paste", - "pin-project", - "serde", - "serde_json", - "tar", - "thiserror", - "tokio", - "url", -] - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossterm" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" -dependencies = [ - "bitflags 1.3.2", - "crossterm_winapi", - "libc", - "mio", - "parking_lot", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - -[[package]] -name = "csv" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" -dependencies = [ - "memchr", -] - -[[package]] -name = "cxx" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 1.0.107", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", -] - -[[package]] -name = "darling" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8" -dependencies = [ - "darling_core 0.14.3", - "darling_macro 0.14.3", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 1.0.107", -] - -[[package]] -name = "darling_core" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 1.0.107", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core 0.13.4", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "darling_macro" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" -dependencies = [ - "darling_core 0.14.3", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "docker-api" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "522a3ae33cf4fc165de192eb9c563b755bc43cda9bd6f442b2d511b84514b917" -dependencies = [ - "asynchronous-codec", - "base64 0.13.1", - "byteorder", - "bytes", - "chrono", - "containers-api", - "docker-api-stubs", - "futures-util", - "http", - "hyper", - "log", - "paste", - "serde", - "serde_json", - "tar", - "thiserror", - "url", -] - -[[package]] -name = "docker-api-stubs" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5872f5e057625a972acce1a51b461284eab32b7e594e18f8fc2f63724075da47" -dependencies = [ - "chrono", - "serde", - "serde_json", - "serde_with", -] - -[[package]] -name = "dyn-clone" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" - -[[package]] -name = "encode_unicode" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" - -[[package]] -name = "encoding_rs" -version = "0.8.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "field_names" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cca4fdab1b9b7e274e7de51202e37f9cfa542b28c77f8d09b817d77a726b4807" -dependencies = [ - "darling 0.13.4", - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "filetime" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.2.16", - "windows-sys 0.45.0", -] - -[[package]] -name = "flate2" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" -dependencies = [ - "crc32fast", - "miniz_oxide 0.6.2", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - -[[package]] -name = "futures-channel" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" - -[[package]] -name = "futures-io" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" - -[[package]] -name = "futures-macro" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "futures-sink" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" - -[[package]] -name = "futures-task" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" - -[[package]] -name = "futures-util" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" -dependencies = [ - "futures-core", - "futures-io", - "futures-macro", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" - -[[package]] -name = "git2" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf7f68c2995f392c49fffb4f95ae2c873297830eb25c6bc4c114ce8f4562acc" -dependencies = [ - "bitflags 1.3.2", - "libc", - "libgit2-sys", - "log", - "openssl-probe", - "openssl-sys", - "url", -] - -[[package]] -name = "h2" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap 1.9.2", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "http" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "hyper" -version = "0.14.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" -dependencies = [ - "futures-util", - "http", - "hyper", - "rustls", - "tokio", - "tokio-rustls", -] - -[[package]] -name = "hyperlocal" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c" -dependencies = [ - "futures-util", - "hex", - "hyper", - "pin-project", - "tokio", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "winapi", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" -dependencies = [ - "cxx", - "cxx-build", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" -dependencies = [ - "equivalent", - "hashbrown 0.14.2", -] - -[[package]] -name = "inquire" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33e7c1ddeb15c9abcbfef6029d8e29f69b52b6d6c891031b88ed91b5065803b" -dependencies = [ - "bitflags 1.3.2", - "crossterm", - "dyn-clone", - "lazy_static", - "newline-converter", - "thiserror", - "unicode-segmentation", - "unicode-width", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" -dependencies = [ - "libc", - "windows-sys 0.45.0", -] - -[[package]] -name = "ipnet" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" - -[[package]] -name = "is-terminal" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys 0.45.0", -] - -[[package]] -name = "itoa" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" - -[[package]] -name = "jobserver" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" - -[[package]] -name = "libgit2-sys" -version = "0.14.2+1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" -dependencies = [ - "cc", - "libc", - "libssh2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", -] - -[[package]] -name = "libredox" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" -dependencies = [ - "bitflags 2.4.1", - "libc", - "redox_syscall 0.4.1", -] - -[[package]] -name = "libssh2-sys" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libz-sys" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "miniz_oxide" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" -dependencies = [ - "adler", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - -[[package]] -name = "newline-converter" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f71d09d5c87634207f894c6b31b6a2b2c64ea3bdcf71bd5599fdbbe1600c00f" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] - -[[package]] -name = "object" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "windows-sys 0.45.0", -] - -[[package]] -name = "paste" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" - -[[package]] -name = "percent-encoding" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "pin-project" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" - -[[package]] -name = "prettytable" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46480520d1b77c9a3482d39939fcf96831537a250ec62d4fd8fbdf8e0302e781" -dependencies = [ - "csv", - "encode_unicode", - "is-terminal", - "lazy_static", - "term", - "unicode-width", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.107", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -dependencies = [ - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_users" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "reqwest" -version = "0.11.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" -dependencies = [ - "base64 0.21.0", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-rustls", - "ipnet", - "js-sys", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "system-configuration", - "tokio", - "tokio-rustls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", - "winreg", -] - -[[package]] -name = "ring" -version = "0.17.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" -dependencies = [ - "cc", - "getrandom", - "libc", - "spin", - "untrusted", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustix" -version = "0.36.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.45.0", -] - -[[package]] -name = "rustls" -version = "0.21.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" -dependencies = [ - "log", - "ring", - "rustls-webpki", - "sct", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" -dependencies = [ - "base64 0.21.0", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" - -[[package]] -name = "ryu" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scratch" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" - -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "semver" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" - -[[package]] -name = "serde" -version = "1.0.191" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a834c4821019838224821468552240d4d95d14e751986442c816572d39a080c9" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.191" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fa52d5646bce91b680189fe5b1c049d2ea38dabb4e2e7c8d00ca12cfbfbcfd" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "serde_json" -version = "1.0.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d904179146de381af4c93d3af6ca4984b3152db687dacb9c3c35e86f39809c" -dependencies = [ - "base64 0.13.1", - "chrono", - "hex", - "indexmap 1.9.2", - "serde", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1966009f3c05f095697c537312f5415d1e3ed31ce0a56942bac4c771c5c335e" -dependencies = [ - "darling 0.14.3", - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "serde_yaml" -version = "0.9.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" -dependencies = [ - "indexmap 2.1.0", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - -[[package]] -name = "sharded-slab" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" -dependencies = [ - "libc", -] - -[[package]] -name = "slab" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "spinners" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08615eea740067d9899969bc2891c68a19c315cb1f66640af9a9ecb91b13bcab" -dependencies = [ - "lazy_static", - "maplit", - "strum", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 1.0.107", -] - -[[package]] -name = "suiop" -version = "0.1.4" -dependencies = [ - "anyhow", - "chrono", - "clap", - "colored", - "docker-api", - "field_names", - "git2", - "inquire", - "prettytable", - "regex", - "reqwest", - "semver", - "serde", - "serde_json", - "serde_yaml", - "spinners", - "strum", - "tempdir", - "tokio", - "toml_edit", - "tracing", - "tracing-subscriber", - "vultr-api", -] - -[[package]] -name = "syn" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tar" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" -dependencies = [ - "filetime", - "libc", - "xattr", -] - -[[package]] -name = "tempdir" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -dependencies = [ - "rand", - "remove_dir_all", -] - -[[package]] -name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "thread_local" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" -dependencies = [ - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" -dependencies = [ - "itoa", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" - -[[package]] -name = "time-macros" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" -dependencies = [ - "time-core", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" -dependencies = [ - "autocfg", - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6a3b08b64e6dfad376fa2432c7b1f01522e37a623c3050bc95db2d3ff21583" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml_datetime" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.1.0", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "unicode-bidi" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" - -[[package]] -name = "unicode-ident" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "unsafe-libyaml" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "vultr-api" -version = "0.1.0" -dependencies = [ - "anyhow", - "field_names", - "reqwest", - "serde", - "serde_json", - "tokio", -] - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 1.0.107", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" - -[[package]] -name = "web-sys" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-roots" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.1", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "winnow" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "xattr" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" -dependencies = [ - "libc", -] diff --git a/crates/suiop-cli/tests/integration_tests.rs b/crates/suiop-cli/tests/integration_tests.rs index ff4d84a4a5083..205bab2e32d0a 100644 --- a/crates/suiop-cli/tests/integration_tests.rs +++ b/crates/suiop-cli/tests/integration_tests.rs @@ -26,7 +26,7 @@ fn test_initialize_service_ext() -> Result<()> { .current_dir(svc_dir) .output()?; - debug!("cargo build output: {:?}", output); + println!("cargo build output: {:?}", output); assert!(output.status.success()); Ok(()) } From 82c3ec2a859bc8bbfceb761783868ae9b9c61619 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 26 Jul 2024 14:03:42 -0500 Subject: [PATCH 009/232] cargo-deny: update deny.toml to clear out warnings --- Cargo.lock | 89 +++++++++++++++++++++++++++++++++--------------------- deny.toml | 20 +++++++++--- 2 files changed, 71 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 56bf0f8753d44..e82d6bbf5e3f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,9 +75,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom 0.2.9", "once_cell", @@ -86,15 +86,16 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.2" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "const-random", "getrandom 0.2.9", "once_cell", "version_check", + "zerocopy", ] [[package]] @@ -596,7 +597,7 @@ version = "52.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81c16ec702d3898c2f5cfdc148443c6cd7dbe5bac28399859eb0a3d38f072827" dependencies = [ - "ahash 0.8.2", + "ahash 0.8.11", "arrow-buffer", "arrow-data", "arrow-schema", @@ -723,7 +724,7 @@ version = "52.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca5e3a6b7fda8d9fe03f3b18a2d946354ea7f3c8e4076dbdb502ad50d9d44824" dependencies = [ - "ahash 0.8.2", + "ahash 0.8.11", "arrow-array", "arrow-buffer", "arrow-data", @@ -744,7 +745,7 @@ version = "52.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e80159088ffe8c48965cb9b1a7c968b2729f29f37363df7eca177fc3281fe7c3" dependencies = [ - "ahash 0.8.2", + "ahash 0.8.11", "arrow-array", "arrow-buffer", "arrow-data", @@ -2799,9 +2800,9 @@ checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" [[package]] name = "const-random" -version = "0.1.16" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11df32a13d7892ec42d51d3d175faba5211ffe13ed25d4fb348ac9e9ce835593" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" dependencies = [ "const-random-macro", ] @@ -3869,7 +3870,7 @@ checksum = "a48e5d537b8a30c0b023116d981b16334be1485af7ca68db3a2b7024cbc957fd" dependencies = [ "der 0.7.5", "digest 0.10.7", - "elliptic-curve 0.13.4", + "elliptic-curve 0.13.8", "rfc6979 0.4.0", "signature 2.0.0", ] @@ -3951,9 +3952,9 @@ dependencies = [ [[package]] name = "elliptic-curve" -version = "0.13.4" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct 0.2.0", "base64ct", @@ -4305,7 +4306,7 @@ dependencies = [ "cargo_metadata 0.15.4", "chrono", "convert_case 0.6.0", - "elliptic-curve 0.13.4", + "elliptic-curve 0.13.8", "ethabi", "generic-array", "getrandom 0.2.9", @@ -4415,7 +4416,7 @@ dependencies = [ "async-trait", "coins-bip32", "coins-bip39", - "elliptic-curve 0.13.4", + "elliptic-curve 0.13.8", "eth-keystore", "ethers-core", "hex", @@ -4502,7 +4503,7 @@ dependencies = [ "digest 0.10.7", "ecdsa 0.16.6", "ed25519-consensus", - "elliptic-curve 0.13.4", + "elliptic-curve 0.13.8", "fastcrypto-derive", "generic-array", "hex", @@ -5293,7 +5294,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.6", + "ahash 0.7.8", ] [[package]] @@ -5302,7 +5303,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.2", + "ahash 0.8.11", ] [[package]] @@ -5311,7 +5312,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" dependencies = [ - "ahash 0.8.2", + "ahash 0.8.11", "allocator-api2", ] @@ -5587,7 +5588,7 @@ dependencies = [ "http 0.2.9", "hyper 0.14.26", "log", - "rustls 0.20.7", + "rustls 0.20.9", "rustls-native-certs 0.6.2", "tokio", "tokio-rustls 0.23.4", @@ -6293,7 +6294,7 @@ checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", "ecdsa 0.16.6", - "elliptic-curve 0.13.4", + "elliptic-curve 0.13.8", "once_cell", "sha2 0.10.6", "signature 2.0.0", @@ -7605,7 +7606,7 @@ name = "msim" version = "0.1.0" source = "git+https://github.com/MystenLabs/mysten-sim.git?rev=220f52a15804a768610ac0ae3b8da7de4a5c4d2b#220f52a15804a768610ac0ae3b8da7de4a5c4d2b" dependencies = [ - "ahash 0.7.6", + "ahash 0.7.8", "async-task", "bincode", "bytes", @@ -8904,7 +8905,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ "ecdsa 0.16.6", - "elliptic-curve 0.13.4", + "elliptic-curve 0.13.8", "primeorder", "sha2 0.10.6", ] @@ -9041,7 +9042,7 @@ version = "52.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f22ba0d95db56dde8685e3fadcb915cdaadda31ab8abbe3ff7f0ad1ef333267" dependencies = [ - "ahash 0.8.2", + "ahash 0.8.11", "arrow-array", "arrow-buffer", "arrow-cast", @@ -9611,7 +9612,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7613fdcc0831c10060fa69833ea8fa2caa94b6456f51e25356a885b530a2e3d0" dependencies = [ - "elliptic-curve 0.13.4", + "elliptic-curve 0.13.8", ] [[package]] @@ -10898,9 +10899,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.7" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ "log", "ring 0.16.20", @@ -15484,7 +15485,7 @@ version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls 0.20.7", + "rustls 0.20.9", "tokio", "webpki", ] @@ -16050,7 +16051,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", - "rand 0.8.5", + "rand 0.7.3", "static_assertions", ] @@ -16241,9 +16242,9 @@ dependencies = [ [[package]] name = "unsafe-libyaml" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "unsigned-varint" @@ -16571,12 +16572,12 @@ dependencies = [ [[package]] name = "webpki" -version = "0.22.0" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", + "ring 0.17.3", + "untrusted 0.9.0", ] [[package]] @@ -17041,6 +17042,26 @@ dependencies = [ "url", ] +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2 1.0.78", + "quote 1.0.35", + "syn 2.0.48", +] + [[package]] name = "zeroize" version = "1.7.0" diff --git a/deny.toml b/deny.toml index b4392cda79f08..4d26ba89ccb0e 100644 --- a/deny.toml +++ b/deny.toml @@ -57,12 +57,12 @@ ignore = [ "RUSTSEC-2023-0049", # ansi_term is Unmaintained "RUSTSEC-2021-0139", - # webpki - "RUSTSEC-2023-0052", # we don't do RSA signing on Sui (only verifying for zklogin) "RUSTSEC-2023-0071", # A few dependencies use unpatched rustls. "RUSTSEC-2024-0336", + # allow yaml-rust being unmaintained + "RUSTSEC-2024-0320", ] # Threshold for security vulnerabilities, any vulnerability with a CVSS score # lower than the range specified will be ignored. Note that ignored advisories @@ -192,7 +192,7 @@ registries = [ # https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html [bans] # Lint level for when multiple versions of the same crate are detected -multiple-versions = "warn" +multiple-versions = "allow" # Lint level for when a crate version requirement is `*` wildcards = "allow" # The graph highlighting used when creating dotgraphs for crates @@ -241,6 +241,18 @@ unknown-git = "warn" # if not specified. If it is specified but empty, no registries are allowed. allow-registry = ["https://github.com/rust-lang/crates.io-index"] # List of URLs for allowed Git repositories -allow-git = [] +allow-git = [ + "https://github.com/asonnino/prometheus-parser", + "https://github.com/zhiburt/tabled", +] [sources.allow-org] +github = [ + "mystenmark", + "bmwill", + "mystenlabs", + "MystenLabs", + "nextest-rs", + "wlmyng", # jsonrpsee fork + "quinn-rs", +] From eca7e4551560670a62b331b54b463e145d820532 Mon Sep 17 00:00:00 2001 From: Lu Zhang <8418040+longbowlu@users.noreply.github.com> Date: Fri, 26 Jul 2024 12:54:44 -0700 Subject: [PATCH 010/232] [bridge] add BridgeMonitor and handle url change event (#18790) ## Description This PR adds `BridgeMonitor` which receives all `SuiBridgeEvent` (and probably `EthBridgeEvent` as well soon) and handles them accordingly. In this PR we add the handling for `CommitteeMemberUrlUpdateEvent` by retrieving the latest committee onchain and swap it in. ## Test plan added unit tests --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .../src/client/bridge_authority_aggregator.rs | 2 +- crates/sui-bridge/src/e2e_tests/basic.rs | 1 + crates/sui-bridge/src/lib.rs | 1 + crates/sui-bridge/src/monitor.rs | 368 ++++++++++++++++++ crates/sui-bridge/src/node.rs | 16 +- crates/sui-bridge/src/orchestrator.rs | 76 +++- crates/sui-bridge/src/sui_mock_client.rs | 21 +- crates/sui-bridge/src/test_utils.rs | 33 +- 8 files changed, 502 insertions(+), 16 deletions(-) create mode 100644 crates/sui-bridge/src/monitor.rs diff --git a/crates/sui-bridge/src/client/bridge_authority_aggregator.rs b/crates/sui-bridge/src/client/bridge_authority_aggregator.rs index e4e7947622198..882e689f341f2 100644 --- a/crates/sui-bridge/src/client/bridge_authority_aggregator.rs +++ b/crates/sui-bridge/src/client/bridge_authority_aggregator.rs @@ -31,7 +31,7 @@ pub struct BridgeAuthorityAggregator { impl BridgeAuthorityAggregator { pub fn new(committee: Arc) -> Self { - let clients = committee + let clients: BTreeMap> = committee .members() .iter() .filter_map(|(name, authority)| { diff --git a/crates/sui-bridge/src/e2e_tests/basic.rs b/crates/sui-bridge/src/e2e_tests/basic.rs index b56e322cd585e..e1d773ac81187 100644 --- a/crates/sui-bridge/src/e2e_tests/basic.rs +++ b/crates/sui-bridge/src/e2e_tests/basic.rs @@ -85,6 +85,7 @@ async fn test_bridge_from_eth_to_sui_to_eth() { .expect("Recipient should have received ETH coin now") .clone(); assert_eq!(eth_coin.balance, sui_amount); + info!("Eth to sui bridge transfer finished"); // Now let the recipient send the coin back to ETH let eth_address_1 = EthAddress::random(); diff --git a/crates/sui-bridge/src/lib.rs b/crates/sui-bridge/src/lib.rs index 0d24936eda2c4..71013b2c8d046 100644 --- a/crates/sui-bridge/src/lib.rs +++ b/crates/sui-bridge/src/lib.rs @@ -13,6 +13,7 @@ pub mod eth_syncer; pub mod eth_transaction_builder; pub mod events; pub mod metrics; +pub mod monitor; pub mod node; pub mod orchestrator; pub mod server; diff --git a/crates/sui-bridge/src/monitor.rs b/crates/sui-bridge/src/monitor.rs new file mode 100644 index 0000000000000..4ee9dde82c687 --- /dev/null +++ b/crates/sui-bridge/src/monitor.rs @@ -0,0 +1,368 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! `BridgeMonitor` receives all `SuiBridgeEvent` and handles them accordingly. + +use arc_swap::ArcSwap; +use std::sync::Arc; +use tokio::time::Duration; + +use crate::client::bridge_authority_aggregator::BridgeAuthorityAggregator; +use crate::crypto::BridgeAuthorityPublicKeyBytes; +use crate::events::CommitteeMemberUrlUpdateEvent; +use crate::events::SuiBridgeEvent; +use crate::retry_with_max_elapsed_time; +use crate::sui_client::{SuiClient, SuiClientInner}; +use crate::types::BridgeCommittee; +use tracing::{error, info, warn}; + +const REFRESH_COMMITTEE_RETRY_TIMES: u64 = 3; + +pub struct BridgeMonitor { + sui_client: Arc>, + monitor_rx: mysten_metrics::metered_channel::Receiver, + bridge_auth_agg: Arc>, +} + +impl BridgeMonitor +where + C: SuiClientInner + 'static, +{ + pub fn new( + sui_client: Arc>, + monitor_rx: mysten_metrics::metered_channel::Receiver, + bridge_auth_agg: Arc>, + ) -> Self { + Self { + sui_client, + monitor_rx, + bridge_auth_agg, + } + } + + pub async fn run(self) { + tracing::info!("Starting BridgeMonitor"); + let Self { + sui_client, + mut monitor_rx, + bridge_auth_agg, + } = self; + + while let Some(events) = monitor_rx.recv().await { + match events { + SuiBridgeEvent::SuiToEthTokenBridgeV1(_) => (), + SuiBridgeEvent::TokenTransferApproved(_) => (), + SuiBridgeEvent::TokenTransferClaimed(_) => (), + SuiBridgeEvent::TokenTransferAlreadyApproved(_) => (), + SuiBridgeEvent::TokenTransferAlreadyClaimed(_) => (), + SuiBridgeEvent::TokenTransferLimitExceed(_) => { + // TODO + } + SuiBridgeEvent::EmergencyOpEvent(_) => { + // TODO + } + SuiBridgeEvent::CommitteeMemberRegistration(_) => (), + SuiBridgeEvent::CommitteeUpdateEvent(_) => (), + SuiBridgeEvent::CommitteeMemberUrlUpdateEvent(event) => { + info!("Received CommitteeMemberUrlUpdateEvent: {:?}", event); + let new_committee = get_latest_bridge_committee_with_url_update_event( + sui_client.clone(), + event, + Duration::from_secs(10), + ) + .await; + bridge_auth_agg.store(Arc::new(BridgeAuthorityAggregator::new(Arc::new( + new_committee, + )))); + info!("Committee updated"); + } + SuiBridgeEvent::BlocklistValidatorEvent(_) => { + // TODO + } + SuiBridgeEvent::TokenRegistrationEvent(_) => (), + SuiBridgeEvent::NewTokenEvent(_) => { + // TODO + } + SuiBridgeEvent::UpdateTokenPriceEvent(_) => (), + } + } + + panic!("BridgeMonitor channel was closed unexpectedly"); + } +} + +async fn get_latest_bridge_committee_with_url_update_event( + sui_client: Arc>, + event: CommitteeMemberUrlUpdateEvent, + staleness_retry_interval: Duration, +) -> BridgeCommittee { + let mut remaining_retry_times = REFRESH_COMMITTEE_RETRY_TIMES; + loop { + let Ok(Ok(committee)) = retry_with_max_elapsed_time!( + sui_client.get_bridge_committee(), + Duration::from_secs(600) + ) else { + error!("Failed to get bridge committee after retry"); + continue; + }; + let member = committee.member(&BridgeAuthorityPublicKeyBytes::from(&event.member)); + let Some(member) = member else { + // This is possible when a node is processing an older event while the member quitted at a later point, which is fine. + // Or fullnode returns a stale committee that the member hasn't joined, which is rare and tricy to handle so we just log it. + warn!( + "Committee member not found in the committee: {:?}", + event.member + ); + return committee; + }; + if member.base_url == event.new_url { + return committee; + } + // If url does not match, it could be: + // 1. the query is sent to a stale fullnode that does not have the latest data yet + // 2. the node is processing an older message, and the latest url has changed again + // In either case, we retry a few times. If it still fails to match, we assume it's the latter case. + tokio::time::sleep(staleness_retry_interval).await; + remaining_retry_times -= 1; + if remaining_retry_times == 0 { + warn!( + "Committee member url {:?} does not match onchain record {:?} after retry", + event.member, member + ); + return committee; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::events::init_all_struct_tags; + use crate::test_utils::{ + bridge_committee_to_bridge_committee_summary, get_test_authority_and_key, + }; + use fastcrypto::traits::KeyPair; + use prometheus::Registry; + use sui_types::base_types::SuiAddress; + use sui_types::bridge::BridgeCommitteeSummary; + use sui_types::bridge::MoveTypeCommitteeMember; + use sui_types::crypto::get_key_pair; + + use crate::{sui_mock_client::SuiMockClient, types::BridgeCommittee}; + use sui_types::crypto::ToFromBytes; + + #[tokio::test] + async fn test_get_latest_bridge_committee_with_url_update_event() { + telemetry_subscribers::init_for_testing(); + let sui_client_mock = SuiMockClient::default(); + let sui_client = Arc::new(SuiClient::new_for_testing(sui_client_mock.clone())); + let (_, kp): (_, fastcrypto::secp256k1::Secp256k1KeyPair) = get_key_pair(); + let pk = kp.public().clone(); + let pk_as_bytes = BridgeAuthorityPublicKeyBytes::from(&pk); + let pk_bytes = pk_as_bytes.as_bytes().to_vec(); + let event = CommitteeMemberUrlUpdateEvent { + member: pk, + new_url: "http://new.url".to_string(), + }; + let summary = BridgeCommitteeSummary { + members: vec![( + pk_bytes.clone(), + MoveTypeCommitteeMember { + sui_address: SuiAddress::random_for_testing_only(), + bridge_pubkey_bytes: pk_bytes.clone(), + voting_power: 10000, + http_rest_url: "http://new.url".to_string().as_bytes().to_vec(), + blocklisted: false, + }, + )], + member_registration: vec![], + last_committee_update_epoch: 0, + }; + + // Test the regular case, the onchain url matches + sui_client_mock.set_bridge_committee(summary.clone()); + let timer = std::time::Instant::now(); + let committee = get_latest_bridge_committee_with_url_update_event( + sui_client.clone(), + event.clone(), + Duration::from_secs(2), + ) + .await; + assert_eq!( + committee.member(&pk_as_bytes).unwrap().base_url, + "http://new.url" + ); + assert!(timer.elapsed().as_millis() < 500); + + // Test the case where the onchain url is older. Then update onchain url in 1 second. + // Since the retry interval is 2 seconds, it should return the next retry. + let old_summary = BridgeCommitteeSummary { + members: vec![( + pk_bytes.clone(), + MoveTypeCommitteeMember { + sui_address: SuiAddress::random_for_testing_only(), + bridge_pubkey_bytes: pk_bytes.clone(), + voting_power: 10000, + http_rest_url: "http://old.url".to_string().as_bytes().to_vec(), + blocklisted: false, + }, + )], + member_registration: vec![], + last_committee_update_epoch: 0, + }; + sui_client_mock.set_bridge_committee(old_summary.clone()); + let timer = std::time::Instant::now(); + // update the url to "http://new.url" in 1 second + let sui_client_mock_clone = sui_client_mock.clone(); + tokio::spawn(async move { + tokio::time::sleep(Duration::from_secs(1)).await; + sui_client_mock_clone.set_bridge_committee(summary.clone()); + }); + let committee = get_latest_bridge_committee_with_url_update_event( + sui_client.clone(), + event.clone(), + Duration::from_secs(2), + ) + .await; + assert_eq!( + committee.member(&pk_as_bytes).unwrap().base_url, + "http://new.url" + ); + let elapsed = timer.elapsed().as_millis(); + assert!(elapsed > 1000 && elapsed < 3000); + + // Test the case where the onchain url is newer. It should retry up to + // REFRESH_COMMITTEE_RETRY_TIMES time then return the onchain record. + let newer_summary = BridgeCommitteeSummary { + members: vec![( + pk_bytes.clone(), + MoveTypeCommitteeMember { + sui_address: SuiAddress::random_for_testing_only(), + bridge_pubkey_bytes: pk_bytes.clone(), + voting_power: 10000, + http_rest_url: "http://newer.url".to_string().as_bytes().to_vec(), + blocklisted: false, + }, + )], + member_registration: vec![], + last_committee_update_epoch: 0, + }; + sui_client_mock.set_bridge_committee(newer_summary.clone()); + let timer = std::time::Instant::now(); + let committee = get_latest_bridge_committee_with_url_update_event( + sui_client.clone(), + event.clone(), + Duration::from_millis(500), + ) + .await; + assert_eq!( + committee.member(&pk_as_bytes).unwrap().base_url, + "http://newer.url" + ); + let elapsed = timer.elapsed().as_millis(); + assert!(elapsed > 500 * REFRESH_COMMITTEE_RETRY_TIMES as u128); + + // Test the case where the member is not found in the committee + // It should return the onchain record. + let (_, kp2): (_, fastcrypto::secp256k1::Secp256k1KeyPair) = get_key_pair(); + let pk2 = kp2.public().clone(); + let pk_as_bytes2 = BridgeAuthorityPublicKeyBytes::from(&pk2); + let pk_bytes2 = pk_as_bytes2.as_bytes().to_vec(); + let newer_summary = BridgeCommitteeSummary { + members: vec![( + pk_bytes2.clone(), + MoveTypeCommitteeMember { + sui_address: SuiAddress::random_for_testing_only(), + bridge_pubkey_bytes: pk_bytes2.clone(), + voting_power: 10000, + http_rest_url: "http://newer.url".to_string().as_bytes().to_vec(), + blocklisted: false, + }, + )], + member_registration: vec![], + last_committee_update_epoch: 0, + }; + sui_client_mock.set_bridge_committee(newer_summary.clone()); + let timer = std::time::Instant::now(); + let committee = get_latest_bridge_committee_with_url_update_event( + sui_client.clone(), + event.clone(), + Duration::from_secs(1), + ) + .await; + assert_eq!( + committee.member(&pk_as_bytes2).unwrap().base_url, + "http://newer.url" + ); + assert!(committee.member(&pk_as_bytes).is_none()); + let elapsed = timer.elapsed().as_millis(); + assert!(elapsed < 1000); + } + + #[tokio::test] + async fn test_update_bridge_authority_aggregation_with_url_change_event() { + let (monitor_tx, monitor_rx, sui_client_mock, sui_client) = setup(); + let mut authorities = vec![ + get_test_authority_and_key(2500, 0 /* port, dummy value */).0, + get_test_authority_and_key(2500, 0 /* port, dummy value */).0, + get_test_authority_and_key(2500, 0 /* port, dummy value */).0, + get_test_authority_and_key(2500, 0 /* port, dummy value */).0, + ]; + let old_committee = BridgeCommittee::new(authorities.clone()).unwrap(); + let agg = Arc::new(ArcSwap::new(Arc::new(BridgeAuthorityAggregator::new( + Arc::new(old_committee), + )))); + let _handle = tokio::task::spawn( + BridgeMonitor::new(sui_client.clone(), monitor_rx, agg.clone()).run(), + ); + let new_url = "http://new.url".to_string(); + authorities[0].base_url = new_url.clone(); + let new_committee = BridgeCommittee::new(authorities.clone()).unwrap(); + let new_committee_summary = + bridge_committee_to_bridge_committee_summary(new_committee.clone()); + sui_client_mock.set_bridge_committee(new_committee_summary.clone()); + monitor_tx + .send(SuiBridgeEvent::CommitteeMemberUrlUpdateEvent( + CommitteeMemberUrlUpdateEvent { + member: authorities[0].pubkey.clone(), + new_url: new_url.clone(), + }, + )) + .await + .unwrap(); + // Wait for the monitor to process the event + tokio::time::sleep(Duration::from_secs(1)).await; + // Now expect the committee to be updated + assert_eq!( + agg.load() + .committee + .member(&BridgeAuthorityPublicKeyBytes::from(&authorities[0].pubkey)) + .unwrap() + .base_url, + new_url + ); + } + + fn setup() -> ( + mysten_metrics::metered_channel::Sender, + mysten_metrics::metered_channel::Receiver, + SuiMockClient, + Arc>, + ) { + telemetry_subscribers::init_for_testing(); + let registry = Registry::new(); + mysten_metrics::init_metrics(®istry); + init_all_struct_tags(); + + let sui_client_mock = SuiMockClient::default(); + let sui_client = Arc::new(SuiClient::new_for_testing(sui_client_mock.clone())); + let (monitor_tx, monitor_rx) = mysten_metrics::metered_channel::channel( + 10000, + &mysten_metrics::get_metrics() + .unwrap() + .channel_inflight + .with_label_values(&["monitor_queue"]), + ); + (monitor_tx, monitor_rx, sui_client_mock, sui_client) + } +} diff --git a/crates/sui-bridge/src/node.rs b/crates/sui-bridge/src/node.rs index 6614941e9b0fc..b9442efb4e9a6 100644 --- a/crates/sui-bridge/src/node.rs +++ b/crates/sui-bridge/src/node.rs @@ -8,6 +8,7 @@ use crate::{ eth_syncer::EthSyncer, events::init_all_struct_tags, metrics::BridgeMetrics, + monitor::BridgeMonitor, orchestrator::BridgeOrchestrator, server::{handler::BridgeRequestHandler, run_server, BridgeNodePublicMetadata}, storage::BridgeOrchestratorTables, @@ -15,6 +16,7 @@ use crate::{ }; use arc_swap::ArcSwap; use ethers::types::Address as EthAddress; +use mysten_metrics::spawn_logged_monitored_task; use std::{ collections::HashMap, net::{IpAddr, Ipv4Addr, SocketAddr}, @@ -111,9 +113,17 @@ async fn start_client_components( let sui_token_type_tags = sui_client.get_token_id_map().await.unwrap(); let (token_type_tags_tx, token_type_tags_rx) = tokio::sync::watch::channel(sui_token_type_tags); + let (monitor_tx, monitor_rx) = mysten_metrics::metered_channel::channel( + 10000, + &mysten_metrics::get_metrics() + .unwrap() + .channel_inflight + .with_label_values(&["monitor_queue"]), + ); + let bridge_action_executor = BridgeActionExecutor::new( sui_client.clone(), - bridge_auth_agg, + bridge_auth_agg.clone(), store.clone(), client_config.key, client_config.sui_address, @@ -123,12 +133,16 @@ async fn start_client_components( ) .await; + let monitor = BridgeMonitor::new(sui_client.clone(), monitor_rx, bridge_auth_agg.clone()); + all_handles.push(spawn_logged_monitored_task!(monitor.run())); + let orchestrator = BridgeOrchestrator::new( sui_client, sui_events_rx, eth_events_rx, store.clone(), token_type_tags_tx, + monitor_tx, metrics, ); diff --git a/crates/sui-bridge/src/orchestrator.rs b/crates/sui-bridge/src/orchestrator.rs index b519b43573ff5..db437bb509017 100644 --- a/crates/sui-bridge/src/orchestrator.rs +++ b/crates/sui-bridge/src/orchestrator.rs @@ -31,6 +31,7 @@ pub struct BridgeOrchestrator { eth_events_rx: mysten_metrics::metered_channel::Receiver<(EthAddress, u64, Vec)>, store: Arc, token_type_tags_tx: tokio::sync::watch::Sender>, + monitor_tx: mysten_metrics::metered_channel::Sender, metrics: Arc, } @@ -44,6 +45,7 @@ where eth_events_rx: mysten_metrics::metered_channel::Receiver<(EthAddress, u64, Vec)>, store: Arc, token_type_tags_tx: tokio::sync::watch::Sender>, + monitor_tx: mysten_metrics::metered_channel::Sender, metrics: Arc, ) -> Self { Self { @@ -52,6 +54,7 @@ where eth_events_rx, store, token_type_tags_tx, + monitor_tx, metrics, } } @@ -74,6 +77,7 @@ where executor_sender_clone, self.sui_events_rx, self.token_type_tags_tx, + self.monitor_tx, metrics_clone, ))); let store_clone = self.store.clone(); @@ -106,6 +110,7 @@ where executor_tx: mysten_metrics::metered_channel::Sender, mut sui_events_rx: mysten_metrics::metered_channel::Receiver<(Identifier, Vec)>, token_type_tags_tx: tokio::sync::watch::Sender>, + monitor_tx: mysten_metrics::metered_channel::Sender, metrics: Arc, ) { info!("Starting sui watcher task"); @@ -150,7 +155,14 @@ where let bridge_event: SuiBridgeEvent = opt_bridge_event.unwrap(); info!("Observed Sui bridge event: {:?}", bridge_event); + // Send event to monitor + monitor_tx + .send(bridge_event.clone()) + .await + .expect("Sending event to monitor channel should not fail"); + // Handle NewTokenEvent + // TODO: broadcast this event and let the downstream services handle it if let SuiBridgeEvent::NewTokenEvent(e) = &bridge_event { if let std::collections::hash_map::Entry::Vacant(entry) = latest_token_config.entry(e.token_id) @@ -299,8 +311,16 @@ mod tests { // Note: this test may fail because of the following reasons: // the SuiEvent's struct tag does not match the ones in events.rs - let (sui_events_tx, sui_events_rx, _eth_events_tx, eth_events_rx, sui_client, store) = - setup(); + let ( + sui_events_tx, + sui_events_rx, + _eth_events_tx, + eth_events_rx, + monitor_tx, + _monitor_rx, + sui_client, + store, + ) = setup(); let (executor, mut executor_requested_action_rx) = MockExecutor::new(); let (token_type_tags_tx, _token_type_tags_rx) = tokio::sync::watch::channel(HashMap::new()); // start orchestrator @@ -312,6 +332,7 @@ mod tests { eth_events_rx, store.clone(), token_type_tags_tx, + monitor_tx, metrics, ) .run(executor) @@ -352,8 +373,16 @@ mod tests { #[tokio::test] async fn test_sui_watcher_task_add_new_token() { - let (sui_events_tx, sui_events_rx, _eth_events_tx, eth_events_rx, sui_client, store) = - setup(); + let ( + sui_events_tx, + sui_events_rx, + _eth_events_tx, + eth_events_rx, + monitor_tx, + _monitor_rx, + sui_client, + store, + ) = setup(); let (executor, _executor_requested_action_rx) = MockExecutor::new(); let (token_type_tags_tx, mut token_type_tags_rx) = @@ -367,6 +396,7 @@ mod tests { eth_events_rx, store.clone(), token_type_tags_tx, + monitor_tx, metrics, ) .run(executor) @@ -418,8 +448,16 @@ mod tests { // 1. Log and BridgeAction returned from `get_test_log_and_action` are not in sync // 2. Log returned from `get_test_log_and_action` is not parseable log (not abigen!, check abi.rs) - let (_sui_events_tx, sui_events_rx, eth_events_tx, eth_events_rx, sui_client, store) = - setup(); + let ( + _sui_events_tx, + sui_events_rx, + eth_events_tx, + eth_events_rx, + monitor_tx, + _monitor_rx, + sui_client, + store, + ) = setup(); let (token_type_tags_tx, _token_type_tags_rx) = tokio::sync::watch::channel(HashMap::new()); let (executor, mut executor_requested_action_rx) = MockExecutor::new(); // start orchestrator @@ -431,6 +469,7 @@ mod tests { eth_events_rx, store.clone(), token_type_tags_tx, + monitor_tx, metrics, ) .run(executor) @@ -481,8 +520,16 @@ mod tests { #[tokio::test] /// Test that when orchestrator starts, all pending actions are sent to executor async fn test_resume_actions_in_pending_logs() { - let (_sui_events_tx, sui_events_rx, _eth_events_tx, eth_events_rx, sui_client, store) = - setup(); + let ( + _sui_events_tx, + sui_events_rx, + _eth_events_tx, + eth_events_rx, + monitor_tx, + _monitor_rx, + sui_client, + store, + ) = setup(); let (executor, mut executor_requested_action_rx) = MockExecutor::new(); let (token_type_tags_tx, _token_type_tags_rx) = tokio::sync::watch::channel(HashMap::new()); @@ -510,6 +557,7 @@ mod tests { eth_events_rx, store.clone(), token_type_tags_tx, + monitor_tx, metrics, ) .run(executor) @@ -530,6 +578,8 @@ mod tests { mysten_metrics::metered_channel::Receiver<(Identifier, Vec)>, mysten_metrics::metered_channel::Sender<(EthAddress, u64, Vec)>, mysten_metrics::metered_channel::Receiver<(EthAddress, u64, Vec)>, + mysten_metrics::metered_channel::Sender, + mysten_metrics::metered_channel::Receiver, SuiClient, Arc, ) { @@ -560,12 +610,20 @@ mod tests { .channel_inflight .with_label_values(&["unit_test_sui_events_queue"]), ); - + let (monitor_tx, monitor_rx) = mysten_metrics::metered_channel::channel( + 10000, + &mysten_metrics::get_metrics() + .unwrap() + .channel_inflight + .with_label_values(&["monitor_queue"]), + ); ( sui_events_tx, sui_events_rx, eth_events_tx, eth_events_rx, + monitor_tx, + monitor_rx, sui_client, store, ) diff --git a/crates/sui-bridge/src/sui_mock_client.rs b/crates/sui-bridge/src/sui_mock_client.rs index 7f64be20003c4..835bd51eac7f9 100644 --- a/crates/sui-bridge/src/sui_mock_client.rs +++ b/crates/sui-bridge/src/sui_mock_client.rs @@ -12,7 +12,9 @@ use sui_json_rpc_types::SuiTransactionBlockResponse; use sui_json_rpc_types::{EventFilter, EventPage, SuiEvent}; use sui_types::base_types::ObjectID; use sui_types::base_types::ObjectRef; -use sui_types::bridge::{BridgeSummary, MoveTypeParsedTokenTransferMessage}; +use sui_types::bridge::{ + BridgeCommitteeSummary, BridgeSummary, MoveTypeParsedTokenTransferMessage, +}; use sui_types::digests::TransactionDigest; use sui_types::event::EventID; use sui_types::gas_coin::GasCoin; @@ -40,7 +42,7 @@ pub struct SuiMockClient { wildcard_transaction_response: Arc>>>, get_object_info: Arc>>, onchain_status: Arc>>, - + bridge_committee_summary: Arc>>, requested_transactions_tx: tokio::sync::broadcast::Sender, } @@ -56,6 +58,7 @@ impl SuiMockClient { wildcard_transaction_response: Default::default(), get_object_info: Default::default(), onchain_status: Default::default(), + bridge_committee_summary: Default::default(), requested_transactions_tx: tokio::sync::broadcast::channel(10000).0, } } @@ -105,6 +108,13 @@ impl SuiMockClient { .insert((action.chain_id() as u8, action.seq_number()), status); } + pub fn set_bridge_committee(&self, committee: BridgeCommitteeSummary) { + self.bridge_committee_summary + .lock() + .unwrap() + .replace(committee); + } + pub fn set_wildcard_transaction_response( &self, response: BridgeResult, @@ -200,7 +210,12 @@ impl SuiClientInner for SuiMockClient { bridge_records_id: ObjectID::random(), is_frozen: false, limiter: Default::default(), - committee: Default::default(), + committee: self + .bridge_committee_summary + .lock() + .unwrap() + .clone() + .unwrap_or_default(), treasury: Default::default(), }) } diff --git a/crates/sui-bridge/src/test_utils.rs b/crates/sui-bridge/src/test_utils.rs index 64d7b8ba02dc7..8b71664908777 100644 --- a/crates/sui-bridge/src/test_utils.rs +++ b/crates/sui-bridge/src/test_utils.rs @@ -7,7 +7,8 @@ use crate::events::SuiBridgeEvent; use crate::server::mock_handler::run_mock_server; use crate::sui_transaction_builder::build_sui_transaction; use crate::types::{ - BridgeCommitteeValiditySignInfo, CertifiedBridgeAction, VerifiedCertifiedBridgeAction, + BridgeCommittee, BridgeCommitteeValiditySignInfo, CertifiedBridgeAction, + VerifiedCertifiedBridgeAction, }; use crate::{ crypto::{BridgeAuthorityKeyPair, BridgeAuthorityPublicKey, BridgeAuthoritySignInfo}, @@ -38,7 +39,9 @@ use sui_sdk::wallet_context::WalletContext; use sui_test_transaction_builder::TestTransactionBuilder; use sui_types::base_types::ObjectRef; use sui_types::base_types::SequenceNumber; -use sui_types::bridge::{BridgeChainId, TOKEN_ID_USDC}; +use sui_types::bridge::MoveTypeCommitteeMember; +use sui_types::bridge::{BridgeChainId, BridgeCommitteeSummary, TOKEN_ID_USDC}; +use sui_types::crypto::ToFromBytes; use sui_types::object::Owner; use sui_types::transaction::{CallArg, ObjectArg}; use sui_types::{base_types::SuiAddress, crypto::get_key_pair, digests::TransactionDigest}; @@ -392,3 +395,29 @@ pub async fn approve_action_with_validator_secrets( expected_token_receiver ); } + +pub fn bridge_committee_to_bridge_committee_summary( + committee: BridgeCommittee, +) -> BridgeCommitteeSummary { + BridgeCommitteeSummary { + members: committee + .members() + .iter() + .map(|(k, v)| { + let bytes = k.as_bytes().to_vec(); + ( + bytes.clone(), + MoveTypeCommitteeMember { + sui_address: SuiAddress::random_for_testing_only(), + bridge_pubkey_bytes: bytes, + voting_power: v.voting_power, + http_rest_url: v.base_url.as_bytes().to_vec(), + blocklisted: v.is_blocklisted, + }, + ) + }) + .collect(), + member_registration: vec![], + last_committee_update_epoch: 0, + } +} From 3dd9ddd81f2ecba0b700fc19e18b80f20663f488 Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Fri, 26 Jul 2024 21:39:53 +0100 Subject: [PATCH 011/232] [Move/Examples] Switch to datatest (#18813) ## Description Use `datatest-stable` to find all the Move examples we might want to build and test, instead of stashing this away in a rust test. ## Test plan ``` sui$ cargo nextest run -p sui-framework-tests --test move_tests ``` + CI Closes #18802 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .github/workflows/rust.yml | 2 +- Cargo.lock | 1 + crates/sui-framework-tests/Cargo.toml | 5 + crates/sui-framework-tests/src/lib.rs | 3 - crates/sui-framework-tests/src/unit_tests.rs | 114 ------------------ .../sui-framework-tests/tests/move_tests.rs | 80 ++++++++++++ 6 files changed, 87 insertions(+), 118 deletions(-) delete mode 100644 crates/sui-framework-tests/src/unit_tests.rs create mode 100644 crates/sui-framework-tests/tests/move_tests.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4d36c3c5d0740..0192d2ed9e24f 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -262,7 +262,7 @@ jobs: - uses: taiki-e/install-action@nextest - name: Run move tests run: | - cargo nextest run -p sui-framework-tests -- unit_tests:: + cargo nextest run -p sui-framework-tests --test move_tests # # Disabled # rosetta-validation: diff --git a/Cargo.lock b/Cargo.lock index e82d6bbf5e3f7..bbe62c92437e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13090,6 +13090,7 @@ dependencies = [ name = "sui-framework-tests" version = "0.1.0" dependencies = [ + "datatest-stable", "move-bytecode-verifier", "move-bytecode-verifier-meter", "move-cli", diff --git a/crates/sui-framework-tests/Cargo.toml b/crates/sui-framework-tests/Cargo.toml index cb9cc2aec3529..ad593b3634716 100644 --- a/crates/sui-framework-tests/Cargo.toml +++ b/crates/sui-framework-tests/Cargo.toml @@ -7,7 +7,12 @@ description = "Runs Move tests for sui-framework" license = "Apache-2.0" publish = false +[[test]] +name = "move_tests" +harness = false + [dev-dependencies] +datatest-stable.workspace = true prometheus.workspace = true sui-framework.workspace = true diff --git a/crates/sui-framework-tests/src/lib.rs b/crates/sui-framework-tests/src/lib.rs index d41a90db7c637..9bc362dc24b5a 100644 --- a/crates/sui-framework-tests/src/lib.rs +++ b/crates/sui-framework-tests/src/lib.rs @@ -1,8 +1,5 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -#[cfg(test)] -mod unit_tests; - #[cfg(test)] mod metered_verifier; diff --git a/crates/sui-framework-tests/src/unit_tests.rs b/crates/sui-framework-tests/src/unit_tests.rs deleted file mode 100644 index c02aad95a77ee..0000000000000 --- a/crates/sui-framework-tests/src/unit_tests.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use move_cli::base::test::UnitTestResult; -use move_package::LintFlag; -use move_unit_test::UnitTestingConfig; -use std::{ - fs, io, - path::{Path, PathBuf}, -}; -use sui_move::unit_test::run_move_unit_tests; -use sui_move_build::BuildConfig; - -const FILTER_ENV: &str = "FILTER"; - -#[test] -#[cfg_attr(msim, ignore)] -fn run_move_stdlib_unit_tests() { - let mut buf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - buf.extend(["..", "sui-framework", "packages", "move-stdlib"]); - check_move_unit_tests(&buf); -} - -#[test] -#[cfg_attr(msim, ignore)] -fn run_sui_framework_tests() { - let mut buf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - buf.extend(["..", "sui-framework", "packages", "sui-framework"]); - check_move_unit_tests(&buf); -} - -#[test] -#[cfg_attr(msim, ignore)] -fn run_sui_system_tests() { - let mut buf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - buf.extend(["..", "sui-framework", "packages", "sui-system"]); - check_move_unit_tests(&buf); -} - -#[test] -#[cfg_attr(msim, ignore)] -fn run_deepbook_tests() { - let mut buf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - buf.extend(["..", "sui-framework", "packages", "deepbook"]); - check_move_unit_tests(&buf); -} -#[test] -#[cfg_attr(msim, ignore)] -fn run_bridge_tests() { - let mut buf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - buf.extend(["..", "sui-framework", "packages", "bridge"]); - check_move_unit_tests(&buf); -} - -fn check_packages_recursively(path: &Path) -> io::Result<()> { - for entry in fs::read_dir(path).unwrap() { - let entry = entry?; - if entry.path().join("Move.toml").exists() { - check_package_builds(&entry.path()); - check_move_unit_tests(&entry.path()); - } else if entry.file_type()?.is_dir() { - check_packages_recursively(&entry.path())?; - } - } - Ok(()) -} - -#[test] -#[cfg_attr(msim, ignore)] -fn run_examples_move_unit_tests() -> io::Result<()> { - let examples = { - let mut buf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - buf.extend(["..", "..", "examples"]); - buf - }; - - check_packages_recursively(&examples)?; - - Ok(()) -} - -/// Ensure packages build outside of test mode. -fn check_package_builds(path: &Path) { - let mut config = BuildConfig::new_for_testing(); - config.config.dev_mode = true; - config.run_bytecode_verifier = true; - config.print_diags_to_stderr = true; - config.config.warnings_are_errors = true; - config.config.silence_warnings = false; - config.config.lint_flag = LintFlag::LEVEL_DEFAULT; - config - .build(path) - .unwrap_or_else(|e| panic!("Building package {}.\nWith error {e}", path.display())); -} - -fn check_move_unit_tests(path: &Path) { - let mut config = BuildConfig::new_for_testing(); - // Make sure to verify tests - config.config.dev_mode = true; - config.config.test_mode = true; - config.run_bytecode_verifier = true; - config.print_diags_to_stderr = true; - config.config.warnings_are_errors = true; - config.config.silence_warnings = false; - config.config.lint_flag = LintFlag::LEVEL_DEFAULT; - let move_config = config.config.clone(); - let mut testing_config = UnitTestingConfig::default_with_bound(Some(3_000_000)); - testing_config.filter = std::env::var(FILTER_ENV).ok().map(|s| s.to_string()); - - assert_eq!( - run_move_unit_tests(path, move_config, Some(testing_config), false).unwrap(), - UnitTestResult::Success - ); -} diff --git a/crates/sui-framework-tests/tests/move_tests.rs b/crates/sui-framework-tests/tests/move_tests.rs new file mode 100644 index 0000000000000..f589f31e372ae --- /dev/null +++ b/crates/sui-framework-tests/tests/move_tests.rs @@ -0,0 +1,80 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::path::Path; + +use move_cli::base::test::UnitTestResult; +use move_package::LintFlag; +use move_unit_test::UnitTestingConfig; +use sui_move::unit_test::run_move_unit_tests; +use sui_move_build::BuildConfig; + +pub(crate) const EXAMPLES: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../examples"); +pub(crate) const FRAMEWORK: &str = concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../crates/sui-framework/packages" +); + +/// Ensure packages build outside of test mode. +pub(crate) fn build(path: &Path) -> datatest_stable::Result<()> { + let Some(path) = path.parent() else { + panic!("No parent for Move.toml file at: {}", path.display()); + }; + + let mut config = BuildConfig::new_for_testing(); + config.config.dev_mode = true; + config.run_bytecode_verifier = true; + config.print_diags_to_stderr = true; + config.config.warnings_are_errors = true; + config.config.silence_warnings = false; + config.config.lint_flag = LintFlag::LEVEL_DEFAULT; + + config + .build(path) + .unwrap_or_else(|e| panic!("Building package {}.\nWith error {e}", path.display())); + + Ok(()) +} + +/// Ensure package sbuild under test mode and all the tests pass. +pub(crate) fn tests(path: &Path) -> datatest_stable::Result<()> { + let Some(path) = path.parent() else { + panic!("No parent for Move.toml file at: {}", path.display()); + }; + + let mut config = BuildConfig::new_for_testing(); + + config.config.dev_mode = true; + config.config.test_mode = true; + config.run_bytecode_verifier = true; + config.print_diags_to_stderr = true; + config.config.warnings_are_errors = true; + config.config.silence_warnings = false; + config.config.lint_flag = LintFlag::LEVEL_DEFAULT; + + let move_config = config.config.clone(); + let mut testing_config = UnitTestingConfig::default_with_bound(Some(3_000_000)); + testing_config.filter = std::env::var("FILTER").ok().map(|s| s.to_string()); + + assert_eq!( + run_move_unit_tests(path, move_config, Some(testing_config), false).unwrap(), + UnitTestResult::Success + ); + + Ok(()) +} + +datatest_stable::harness!( + build, + EXAMPLES, + r".*/Move.toml$", + tests, + EXAMPLES, + r".*/Move.toml$", + build, + FRAMEWORK, + r".*/Move.toml$", + tests, + FRAMEWORK, + r".*/Move.toml$", +); From 93caf44bab65241f3417db15135ba5919a45f6a7 Mon Sep 17 00:00:00 2001 From: Lu Zhang <8418040+longbowlu@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:53:07 -0700 Subject: [PATCH 012/232] [bridge] let monitor handle blocklist event (#18792) ## Description as title ## Test plan added unit tests --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-bridge/src/monitor.rs | 327 ++++++++++++++++++++++++++++++- 1 file changed, 323 insertions(+), 4 deletions(-) diff --git a/crates/sui-bridge/src/monitor.rs b/crates/sui-bridge/src/monitor.rs index 4ee9dde82c687..0713f6586ec2b 100644 --- a/crates/sui-bridge/src/monitor.rs +++ b/crates/sui-bridge/src/monitor.rs @@ -9,8 +9,8 @@ use tokio::time::Duration; use crate::client::bridge_authority_aggregator::BridgeAuthorityAggregator; use crate::crypto::BridgeAuthorityPublicKeyBytes; -use crate::events::CommitteeMemberUrlUpdateEvent; use crate::events::SuiBridgeEvent; +use crate::events::{BlocklistValidatorEvent, CommitteeMemberUrlUpdateEvent}; use crate::retry_with_max_elapsed_time; use crate::sui_client::{SuiClient, SuiClientInner}; use crate::types::BridgeCommittee; @@ -74,10 +74,20 @@ where bridge_auth_agg.store(Arc::new(BridgeAuthorityAggregator::new(Arc::new( new_committee, )))); - info!("Committee updated"); + info!("Committee updated with CommitteeMemberUrlUpdateEvent"); } - SuiBridgeEvent::BlocklistValidatorEvent(_) => { - // TODO + SuiBridgeEvent::BlocklistValidatorEvent(event) => { + info!("Received BlocklistValidatorEvent: {:?}", event); + let new_committee = get_latest_bridge_committee_with_blocklist_event( + sui_client.clone(), + event, + Duration::from_secs(10), + ) + .await; + bridge_auth_agg.store(Arc::new(BridgeAuthorityAggregator::new(Arc::new( + new_committee, + )))); + info!("Committee updated with BlocklistValidatorEvent"); } SuiBridgeEvent::TokenRegistrationEvent(_) => (), SuiBridgeEvent::NewTokenEvent(_) => { @@ -134,6 +144,59 @@ async fn get_latest_bridge_committee_with_url_update_event( } } +async fn get_latest_bridge_committee_with_blocklist_event( + sui_client: Arc>, + event: BlocklistValidatorEvent, + staleness_retry_interval: Duration, +) -> BridgeCommittee { + let mut remaining_retry_times = REFRESH_COMMITTEE_RETRY_TIMES; + loop { + let Ok(Ok(committee)) = retry_with_max_elapsed_time!( + sui_client.get_bridge_committee(), + Duration::from_secs(600) + ) else { + error!("Failed to get bridge committee after retry"); + continue; + }; + let mut any_mismatch = false; + for pk in &event.public_keys { + let member = committee.member(&BridgeAuthorityPublicKeyBytes::from(pk)); + let Some(member) = member else { + // This is possible when a node is processing an older event while the member + // quitted at a later point. Or fullnode returns a stale committee that + // the member hasn't joined. + warn!("Committee member not found in the committee: {:?}", pk); + any_mismatch = true; + break; + }; + if member.is_blocklisted != event.blocklisted { + warn!( + "Committee member blocklist status does not match onchain record: {:?}", + member + ); + any_mismatch = true; + break; + } + } + if !any_mismatch { + return committee; + } + // If there is any match, it could be: + // 1. the query is sent to a stale fullnode that does not have the latest data yet + // 2. the node is processing an older message, and the latest blocklist status has changed again + // In either case, we retry a few times. If it still fails to match, we assume it's the latter case. + tokio::time::sleep(staleness_retry_interval).await; + remaining_retry_times -= 1; + if remaining_retry_times == 0 { + warn!( + "Committee member blocklist status {:?} does not match onchain record after retry", + event + ); + return committee; + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -299,6 +362,219 @@ mod tests { assert!(elapsed < 1000); } + #[tokio::test] + async fn test_get_latest_bridge_committee_with_blocklist_event() { + telemetry_subscribers::init_for_testing(); + let sui_client_mock = SuiMockClient::default(); + let sui_client = Arc::new(SuiClient::new_for_testing(sui_client_mock.clone())); + let (_, kp): (_, fastcrypto::secp256k1::Secp256k1KeyPair) = get_key_pair(); + let pk = kp.public().clone(); + let pk_as_bytes = BridgeAuthorityPublicKeyBytes::from(&pk); + let pk_bytes = pk_as_bytes.as_bytes().to_vec(); + + // Test the case where the onchain status is the same as the event (blocklisted) + let event = BlocklistValidatorEvent { + blocklisted: true, + public_keys: vec![pk.clone()], + }; + let summary = BridgeCommitteeSummary { + members: vec![( + pk_bytes.clone(), + MoveTypeCommitteeMember { + sui_address: SuiAddress::random_for_testing_only(), + bridge_pubkey_bytes: pk_bytes.clone(), + voting_power: 10000, + http_rest_url: "http://new.url".to_string().as_bytes().to_vec(), + blocklisted: true, + }, + )], + member_registration: vec![], + last_committee_update_epoch: 0, + }; + sui_client_mock.set_bridge_committee(summary.clone()); + let timer = std::time::Instant::now(); + let committee = get_latest_bridge_committee_with_blocklist_event( + sui_client.clone(), + event.clone(), + Duration::from_secs(2), + ) + .await; + assert!(committee.member(&pk_as_bytes).unwrap().is_blocklisted); + assert!(timer.elapsed().as_millis() < 500); + + // Test the case where the onchain status is the same as the event (unblocklisted) + let event = BlocklistValidatorEvent { + blocklisted: false, + public_keys: vec![pk.clone()], + }; + let summary = BridgeCommitteeSummary { + members: vec![( + pk_bytes.clone(), + MoveTypeCommitteeMember { + sui_address: SuiAddress::random_for_testing_only(), + bridge_pubkey_bytes: pk_bytes.clone(), + voting_power: 10000, + http_rest_url: "http://new.url".to_string().as_bytes().to_vec(), + blocklisted: false, + }, + )], + member_registration: vec![], + last_committee_update_epoch: 0, + }; + sui_client_mock.set_bridge_committee(summary.clone()); + let timer = std::time::Instant::now(); + let committee = get_latest_bridge_committee_with_blocklist_event( + sui_client.clone(), + event.clone(), + Duration::from_secs(2), + ) + .await; + assert!(!committee.member(&pk_as_bytes).unwrap().is_blocklisted); + assert!(timer.elapsed().as_millis() < 500); + + // Test the case where the onchain status is older. Then update onchain status in 1 second. + // Since the retry interval is 2 seconds, it should return the next retry. + let old_summary = BridgeCommitteeSummary { + members: vec![( + pk_bytes.clone(), + MoveTypeCommitteeMember { + sui_address: SuiAddress::random_for_testing_only(), + bridge_pubkey_bytes: pk_bytes.clone(), + voting_power: 10000, + http_rest_url: "http://new.url".to_string().as_bytes().to_vec(), + blocklisted: true, + }, + )], + member_registration: vec![], + last_committee_update_epoch: 0, + }; + sui_client_mock.set_bridge_committee(old_summary.clone()); + let timer = std::time::Instant::now(); + // update unblocklisted in 1 second + let sui_client_mock_clone = sui_client_mock.clone(); + tokio::spawn(async move { + tokio::time::sleep(Duration::from_secs(1)).await; + sui_client_mock_clone.set_bridge_committee(summary.clone()); + }); + let committee = get_latest_bridge_committee_with_blocklist_event( + sui_client.clone(), + event.clone(), + Duration::from_secs(2), + ) + .await; + assert!(!committee.member(&pk_as_bytes).unwrap().is_blocklisted); + let elapsed = timer.elapsed().as_millis(); + assert!(elapsed > 1000 && elapsed < 3000); + + // Test the case where the onchain url is newer. It should retry up to + // REFRESH_COMMITTEE_RETRY_TIMES time then return the onchain record. + let newer_summary = BridgeCommitteeSummary { + members: vec![( + pk_bytes.clone(), + MoveTypeCommitteeMember { + sui_address: SuiAddress::random_for_testing_only(), + bridge_pubkey_bytes: pk_bytes.clone(), + voting_power: 10000, + http_rest_url: "http://new.url".to_string().as_bytes().to_vec(), + blocklisted: true, + }, + )], + member_registration: vec![], + last_committee_update_epoch: 0, + }; + sui_client_mock.set_bridge_committee(newer_summary.clone()); + let timer = std::time::Instant::now(); + let committee = get_latest_bridge_committee_with_blocklist_event( + sui_client.clone(), + event.clone(), + Duration::from_millis(500), + ) + .await; + assert!(committee.member(&pk_as_bytes).unwrap().is_blocklisted); + let elapsed = timer.elapsed().as_millis(); + assert!(elapsed > 500 * REFRESH_COMMITTEE_RETRY_TIMES as u128); + + // Test the case where the member onchain url is not found in the committee + // It should return the onchain record after retrying a few times. + let (_, kp2): (_, fastcrypto::secp256k1::Secp256k1KeyPair) = get_key_pair(); + let pk2 = kp2.public().clone(); + let pk_as_bytes2 = BridgeAuthorityPublicKeyBytes::from(&pk2); + let pk_bytes2 = pk_as_bytes2.as_bytes().to_vec(); + let summary = BridgeCommitteeSummary { + members: vec![( + pk_bytes2.clone(), + MoveTypeCommitteeMember { + sui_address: SuiAddress::random_for_testing_only(), + bridge_pubkey_bytes: pk_bytes2.clone(), + voting_power: 10000, + http_rest_url: "http://newer.url".to_string().as_bytes().to_vec(), + blocklisted: false, + }, + )], + member_registration: vec![], + last_committee_update_epoch: 0, + }; + sui_client_mock.set_bridge_committee(summary.clone()); + let timer = std::time::Instant::now(); + let committee = get_latest_bridge_committee_with_blocklist_event( + sui_client.clone(), + event.clone(), + Duration::from_secs(1), + ) + .await; + assert_eq!( + committee.member(&pk_as_bytes2).unwrap().base_url, + "http://newer.url" + ); + assert!(committee.member(&pk_as_bytes).is_none()); + let elapsed = timer.elapsed().as_millis(); + assert!(elapsed > 500 * REFRESH_COMMITTEE_RETRY_TIMES as u128); + + // Test any mismtach in the blocklist status should retry a few times + let event = BlocklistValidatorEvent { + blocklisted: true, + public_keys: vec![pk, pk2], + }; + let summary = BridgeCommitteeSummary { + members: vec![ + ( + pk_bytes.clone(), + MoveTypeCommitteeMember { + sui_address: SuiAddress::random_for_testing_only(), + bridge_pubkey_bytes: pk_bytes.clone(), + voting_power: 5000, + http_rest_url: "http://pk.url".to_string().as_bytes().to_vec(), + blocklisted: true, + }, + ), + ( + pk_bytes2.clone(), + MoveTypeCommitteeMember { + sui_address: SuiAddress::random_for_testing_only(), + bridge_pubkey_bytes: pk_bytes2.clone(), + voting_power: 5000, + http_rest_url: "http://pk2.url".to_string().as_bytes().to_vec(), + blocklisted: false, + }, + ), + ], + member_registration: vec![], + last_committee_update_epoch: 0, + }; + sui_client_mock.set_bridge_committee(summary.clone()); + let timer = std::time::Instant::now(); + let committee = get_latest_bridge_committee_with_blocklist_event( + sui_client.clone(), + event.clone(), + Duration::from_millis(500), + ) + .await; + assert!(committee.member(&pk_as_bytes).unwrap().is_blocklisted); + assert!(!committee.member(&pk_as_bytes2).unwrap().is_blocklisted); + let elapsed = timer.elapsed().as_millis(); + assert!(elapsed > 500 * REFRESH_COMMITTEE_RETRY_TIMES as u128); + } + #[tokio::test] async fn test_update_bridge_authority_aggregation_with_url_change_event() { let (monitor_tx, monitor_rx, sui_client_mock, sui_client) = setup(); @@ -343,6 +619,49 @@ mod tests { ); } + #[tokio::test] + async fn test_update_bridge_authority_aggregation_with_blocklist_event() { + let (monitor_tx, monitor_rx, sui_client_mock, sui_client) = setup(); + let mut authorities = vec![ + get_test_authority_and_key(2500, 0 /* port, dummy value */).0, + get_test_authority_and_key(2500, 0 /* port, dummy value */).0, + get_test_authority_and_key(2500, 0 /* port, dummy value */).0, + get_test_authority_and_key(2500, 0 /* port, dummy value */).0, + ]; + let old_committee = BridgeCommittee::new(authorities.clone()).unwrap(); + let agg = Arc::new(ArcSwap::new(Arc::new(BridgeAuthorityAggregator::new( + Arc::new(old_committee), + )))); + let _handle = tokio::task::spawn( + BridgeMonitor::new(sui_client.clone(), monitor_rx, agg.clone()).run(), + ); + authorities[0].is_blocklisted = true; + let to_blocklist = &authorities[0]; + let new_committee = BridgeCommittee::new(authorities.clone()).unwrap(); + let new_committee_summary = + bridge_committee_to_bridge_committee_summary(new_committee.clone()); + sui_client_mock.set_bridge_committee(new_committee_summary.clone()); + monitor_tx + .send(SuiBridgeEvent::BlocklistValidatorEvent( + BlocklistValidatorEvent { + public_keys: vec![to_blocklist.pubkey.clone()], + blocklisted: true, + }, + )) + .await + .unwrap(); + // Wait for the monitor to process the event + tokio::time::sleep(Duration::from_secs(1)).await; + // Now expect the committee to be updated + assert!( + agg.load() + .committee + .member(&BridgeAuthorityPublicKeyBytes::from(&to_blocklist.pubkey)) + .unwrap() + .is_blocklisted, + ); + } + fn setup() -> ( mysten_metrics::metered_channel::Sender, mysten_metrics::metered_channel::Receiver, From 50a0d38d6af54fbd0d4364d2fec12412ca681e60 Mon Sep 17 00:00:00 2001 From: Lu Zhang <8418040+longbowlu@users.noreply.github.com> Date: Fri, 26 Jul 2024 14:36:37 -0700 Subject: [PATCH 013/232] [bridge] let monitor handle emergecny op (#18791) ## Description as title. Note the actual usage of the watch channel hasn't been wired up in ActionExecutor. It will be in the next PR. ## Test plan unit tests --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-bridge/src/action_executor.rs | 15 ++ crates/sui-bridge/src/monitor.rs | 233 ++++++++++++++++++++--- crates/sui-bridge/src/node.rs | 12 +- crates/sui-bridge/src/sui_mock_client.rs | 10 +- crates/sui-bridge/src/types.rs | 4 + 5 files changed, 240 insertions(+), 34 deletions(-) diff --git a/crates/sui-bridge/src/action_executor.rs b/crates/sui-bridge/src/action_executor.rs index ea53a7e02ad8f..9fb45367b6ad5 100644 --- a/crates/sui-bridge/src/action_executor.rs +++ b/crates/sui-bridge/src/action_executor.rs @@ -5,6 +5,7 @@ //! collects bridge authority signatures and submit signatures on chain. use crate::retry_with_max_elapsed_time; +use crate::types::IsBridgePaused; use arc_swap::ArcSwap; use mysten_metrics::spawn_logged_monitored_task; use shared_crypto::intent::{Intent, IntentMessage}; @@ -78,6 +79,7 @@ pub struct BridgeActionExecutor { store: Arc, bridge_object_arg: ObjectArg, token_config_rx: tokio::sync::watch::Receiver>, + bridge_pause_rx: tokio::sync::watch::Receiver, metrics: Arc, } @@ -108,6 +110,7 @@ where sui_address: SuiAddress, gas_object_id: ObjectID, token_config_rx: tokio::sync::watch::Receiver>, + bridge_pause_rx: tokio::sync::watch::Receiver, metrics: Arc, ) -> Self { let bridge_object_arg = sui_client @@ -122,6 +125,7 @@ where sui_address, bridge_object_arg, token_config_rx, + bridge_pause_rx, metrics, } } @@ -181,6 +185,7 @@ where execution_rx, self.bridge_object_arg, self.token_config_rx, + self.bridge_pause_rx, metrics, ) )); @@ -403,6 +408,8 @@ where >, bridge_object_arg: ObjectArg, mut token_config_rx: tokio::sync::watch::Receiver>, + // TODO: wire this up + _bridge_pause_rx: tokio::sync::watch::Receiver, metrics: Arc, ) { info!("Starting run_onchain_execution_loop"); @@ -701,6 +708,7 @@ mod tests { gas_object_ref, sui_address, token_tx, + _bridge_pause_tx, ) = setup().await; let (action_certificate, _, _) = get_bridge_authority_approved_action( vec![&mock0, &mock1, &mock2, &mock3], @@ -892,6 +900,7 @@ mod tests { gas_object_ref, sui_address, token_tx, + _bridge_pause_tx, ) = setup().await; let id_token_map = token_tx.borrow(); let (action_certificate, sui_tx_digest, sui_tx_event_index) = @@ -1012,6 +1021,7 @@ mod tests { _gas_object_ref, _sui_address, _token_tx, + _bridge_pause_tx, ) = setup().await; let sui_tx_digest = TransactionDigest::random(); @@ -1079,6 +1089,7 @@ mod tests { gas_object_ref, sui_address, token_tx, + _bridge_pause_tx, ) = setup().await; let id_token_map = token_tx.borrow(); let (action_certificate, _, _) = get_bridge_authority_approved_action( @@ -1270,6 +1281,7 @@ mod tests { ObjectRef, SuiAddress, tokio::sync::watch::Sender>, + tokio::sync::watch::Sender, ) { telemetry_subscribers::init_for_testing(); let registry = Registry::new(); @@ -1309,6 +1321,7 @@ mod tests { let sui_token_type_tags = sui_client.get_token_id_map().await.unwrap(); let (token_type_tags_tx, token_type_tags_rx) = tokio::sync::watch::channel(sui_token_type_tags); + let (bridge_pause_tx, bridge_pause_rx) = tokio::sync::watch::channel(false); let executor = BridgeActionExecutor::new( sui_client.clone(), agg.clone(), @@ -1317,6 +1330,7 @@ mod tests { sui_address, gas_object_ref.0, token_type_tags_rx, + bridge_pause_rx, metrics, ) .await; @@ -1340,6 +1354,7 @@ mod tests { gas_object_ref, sui_address, token_type_tags_tx, + bridge_pause_tx, ) } } diff --git a/crates/sui-bridge/src/monitor.rs b/crates/sui-bridge/src/monitor.rs index 0713f6586ec2b..5119b115a6a2e 100644 --- a/crates/sui-bridge/src/monitor.rs +++ b/crates/sui-bridge/src/monitor.rs @@ -9,19 +9,20 @@ use tokio::time::Duration; use crate::client::bridge_authority_aggregator::BridgeAuthorityAggregator; use crate::crypto::BridgeAuthorityPublicKeyBytes; -use crate::events::SuiBridgeEvent; use crate::events::{BlocklistValidatorEvent, CommitteeMemberUrlUpdateEvent}; +use crate::events::{EmergencyOpEvent, SuiBridgeEvent}; use crate::retry_with_max_elapsed_time; use crate::sui_client::{SuiClient, SuiClientInner}; -use crate::types::BridgeCommittee; +use crate::types::{BridgeCommittee, IsBridgePaused}; use tracing::{error, info, warn}; -const REFRESH_COMMITTEE_RETRY_TIMES: u64 = 3; +const REFRESH_BRIDGE_RETRY_TIMES: u64 = 3; pub struct BridgeMonitor { sui_client: Arc>, monitor_rx: mysten_metrics::metered_channel::Receiver, bridge_auth_agg: Arc>, + bridge_paused_watch_tx: tokio::sync::watch::Sender, } impl BridgeMonitor @@ -32,11 +33,13 @@ where sui_client: Arc>, monitor_rx: mysten_metrics::metered_channel::Receiver, bridge_auth_agg: Arc>, + bridge_paused_watch_tx: tokio::sync::watch::Sender, ) -> Self { Self { sui_client, monitor_rx, bridge_auth_agg, + bridge_paused_watch_tx, } } @@ -46,6 +49,7 @@ where sui_client, mut monitor_rx, bridge_auth_agg, + bridge_paused_watch_tx, } = self; while let Some(events) = monitor_rx.recv().await { @@ -58,8 +62,17 @@ where SuiBridgeEvent::TokenTransferLimitExceed(_) => { // TODO } - SuiBridgeEvent::EmergencyOpEvent(_) => { - // TODO + SuiBridgeEvent::EmergencyOpEvent(event) => { + info!("Received EmergencyOpEvent: {:?}", event); + let is_paused = get_latest_bridge_pause_status_with_emergency_event( + sui_client.clone(), + event, + Duration::from_secs(10), + ) + .await; + bridge_paused_watch_tx + .send(is_paused) + .expect("Bridge pause status watch channel should not be closed"); } SuiBridgeEvent::CommitteeMemberRegistration(_) => (), SuiBridgeEvent::CommitteeUpdateEvent(_) => (), @@ -106,7 +119,7 @@ async fn get_latest_bridge_committee_with_url_update_event( event: CommitteeMemberUrlUpdateEvent, staleness_retry_interval: Duration, ) -> BridgeCommittee { - let mut remaining_retry_times = REFRESH_COMMITTEE_RETRY_TIMES; + let mut remaining_retry_times = REFRESH_BRIDGE_RETRY_TIMES; loop { let Ok(Ok(committee)) = retry_with_max_elapsed_time!( sui_client.get_bridge_committee(), @@ -149,7 +162,7 @@ async fn get_latest_bridge_committee_with_blocklist_event( event: BlocklistValidatorEvent, staleness_retry_interval: Duration, ) -> BridgeCommittee { - let mut remaining_retry_times = REFRESH_COMMITTEE_RETRY_TIMES; + let mut remaining_retry_times = REFRESH_BRIDGE_RETRY_TIMES; loop { let Ok(Ok(committee)) = retry_with_max_elapsed_time!( sui_client.get_bridge_committee(), @@ -197,6 +210,38 @@ async fn get_latest_bridge_committee_with_blocklist_event( } } +async fn get_latest_bridge_pause_status_with_emergency_event( + sui_client: Arc>, + event: EmergencyOpEvent, + staleness_retry_interval: Duration, +) -> IsBridgePaused { + let mut remaining_retry_times = REFRESH_BRIDGE_RETRY_TIMES; + loop { + let Ok(Ok(summary)) = + retry_with_max_elapsed_time!(sui_client.get_bridge_summary(), Duration::from_secs(600)) + else { + error!("Failed to get bridge summary after retry"); + continue; + }; + if summary.is_frozen == event.frozen { + return summary.is_frozen; + } + // If the onchain status does not match, it could be: + // 1. the query is sent to a stale fullnode that does not have the latest data yet + // 2. the node is processing an older message, and the latest status has changed again + // In either case, we retry a few times. If it still fails to match, we assume it's the latter case. + tokio::time::sleep(staleness_retry_interval).await; + remaining_retry_times -= 1; + if remaining_retry_times == 0 { + warn!( + "Bridge pause status {:?} does not match onchain record {:?} after retry", + event, summary.is_frozen + ); + return summary.is_frozen; + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -204,6 +249,7 @@ mod tests { use crate::test_utils::{ bridge_committee_to_bridge_committee_summary, get_test_authority_and_key, }; + use crate::types::{BridgeAuthority, BRIDGE_PAUSED, BRIDGE_UNPAUSED}; use fastcrypto::traits::KeyPair; use prometheus::Registry; use sui_types::base_types::SuiAddress; @@ -295,7 +341,7 @@ mod tests { assert!(elapsed > 1000 && elapsed < 3000); // Test the case where the onchain url is newer. It should retry up to - // REFRESH_COMMITTEE_RETRY_TIMES time then return the onchain record. + // REFRESH_BRIDGE_RETRY_TIMES time then return the onchain record. let newer_summary = BridgeCommitteeSummary { members: vec![( pk_bytes.clone(), @@ -323,7 +369,7 @@ mod tests { "http://newer.url" ); let elapsed = timer.elapsed().as_millis(); - assert!(elapsed > 500 * REFRESH_COMMITTEE_RETRY_TIMES as u128); + assert!(elapsed > 500 * REFRESH_BRIDGE_RETRY_TIMES as u128); // Test the case where the member is not found in the committee // It should return the onchain record. @@ -467,7 +513,7 @@ mod tests { assert!(elapsed > 1000 && elapsed < 3000); // Test the case where the onchain url is newer. It should retry up to - // REFRESH_COMMITTEE_RETRY_TIMES time then return the onchain record. + // REFRESH_BRIDGE_RETRY_TIMES time then return the onchain record. let newer_summary = BridgeCommitteeSummary { members: vec![( pk_bytes.clone(), @@ -492,7 +538,7 @@ mod tests { .await; assert!(committee.member(&pk_as_bytes).unwrap().is_blocklisted); let elapsed = timer.elapsed().as_millis(); - assert!(elapsed > 500 * REFRESH_COMMITTEE_RETRY_TIMES as u128); + assert!(elapsed > 500 * REFRESH_BRIDGE_RETRY_TIMES as u128); // Test the case where the member onchain url is not found in the committee // It should return the onchain record after retrying a few times. @@ -528,7 +574,7 @@ mod tests { ); assert!(committee.member(&pk_as_bytes).is_none()); let elapsed = timer.elapsed().as_millis(); - assert!(elapsed > 500 * REFRESH_COMMITTEE_RETRY_TIMES as u128); + assert!(elapsed > 500 * REFRESH_BRIDGE_RETRY_TIMES as u128); // Test any mismtach in the blocklist status should retry a few times let event = BlocklistValidatorEvent { @@ -572,24 +618,96 @@ mod tests { assert!(committee.member(&pk_as_bytes).unwrap().is_blocklisted); assert!(!committee.member(&pk_as_bytes2).unwrap().is_blocklisted); let elapsed = timer.elapsed().as_millis(); - assert!(elapsed > 500 * REFRESH_COMMITTEE_RETRY_TIMES as u128); + assert!(elapsed > 500 * REFRESH_BRIDGE_RETRY_TIMES as u128); + } + + #[tokio::test] + async fn test_get_bridge_pause_status_with_emergency_event() { + telemetry_subscribers::init_for_testing(); + let sui_client_mock = SuiMockClient::default(); + let sui_client = Arc::new(SuiClient::new_for_testing(sui_client_mock.clone())); + + // Test event and onchain status match + let event = EmergencyOpEvent { frozen: true }; + sui_client_mock.set_is_bridge_paused(BRIDGE_PAUSED); + let timer = std::time::Instant::now(); + assert!( + get_latest_bridge_pause_status_with_emergency_event( + sui_client.clone(), + event.clone(), + Duration::from_secs(2), + ) + .await + ); + assert!(timer.elapsed().as_millis() < 500); + + let event = EmergencyOpEvent { frozen: false }; + sui_client_mock.set_is_bridge_paused(BRIDGE_UNPAUSED); + let timer = std::time::Instant::now(); + assert!( + !get_latest_bridge_pause_status_with_emergency_event( + sui_client.clone(), + event.clone(), + Duration::from_secs(2), + ) + .await + ); + assert!(timer.elapsed().as_millis() < 500); + + // Test the case where the onchain status (paused) is older. Then update onchain status in 1 second. + // Since the retry interval is 2 seconds, it should return the next retry. + sui_client_mock.set_is_bridge_paused(BRIDGE_PAUSED); + let timer = std::time::Instant::now(); + // update the bridge to unpaused in 1 second + let sui_client_mock_clone = sui_client_mock.clone(); + tokio::spawn(async move { + tokio::time::sleep(Duration::from_secs(1)).await; + sui_client_mock_clone.set_is_bridge_paused(BRIDGE_UNPAUSED); + }); + assert!( + !get_latest_bridge_pause_status_with_emergency_event( + sui_client.clone(), + event.clone(), + Duration::from_secs(2), + ) + .await + ); + let elapsed = timer.elapsed().as_millis(); + assert!(elapsed > 1000 && elapsed < 3000, "{}", elapsed); + + // Test the case where the onchain status (paused) is newer. It should retry up to + // REFRESH_BRIDGE_RETRY_TIMES time then return the onchain record. + sui_client_mock.set_is_bridge_paused(BRIDGE_PAUSED); + let timer = std::time::Instant::now(); + assert!( + get_latest_bridge_pause_status_with_emergency_event( + sui_client.clone(), + event.clone(), + Duration::from_secs(2), + ) + .await + ); + let elapsed = timer.elapsed().as_millis(); + assert!(elapsed > 500 * REFRESH_BRIDGE_RETRY_TIMES as u128); } #[tokio::test] async fn test_update_bridge_authority_aggregation_with_url_change_event() { - let (monitor_tx, monitor_rx, sui_client_mock, sui_client) = setup(); - let mut authorities = vec![ - get_test_authority_and_key(2500, 0 /* port, dummy value */).0, - get_test_authority_and_key(2500, 0 /* port, dummy value */).0, - get_test_authority_and_key(2500, 0 /* port, dummy value */).0, - get_test_authority_and_key(2500, 0 /* port, dummy value */).0, - ]; + let ( + monitor_tx, + monitor_rx, + sui_client_mock, + sui_client, + bridge_pause_tx, + _bridge_pause_rx, + mut authorities, + ) = setup(); let old_committee = BridgeCommittee::new(authorities.clone()).unwrap(); let agg = Arc::new(ArcSwap::new(Arc::new(BridgeAuthorityAggregator::new( Arc::new(old_committee), )))); let _handle = tokio::task::spawn( - BridgeMonitor::new(sui_client.clone(), monitor_rx, agg.clone()).run(), + BridgeMonitor::new(sui_client.clone(), monitor_rx, agg.clone(), bridge_pause_tx).run(), ); let new_url = "http://new.url".to_string(); authorities[0].base_url = new_url.clone(); @@ -621,19 +739,21 @@ mod tests { #[tokio::test] async fn test_update_bridge_authority_aggregation_with_blocklist_event() { - let (monitor_tx, monitor_rx, sui_client_mock, sui_client) = setup(); - let mut authorities = vec![ - get_test_authority_and_key(2500, 0 /* port, dummy value */).0, - get_test_authority_and_key(2500, 0 /* port, dummy value */).0, - get_test_authority_and_key(2500, 0 /* port, dummy value */).0, - get_test_authority_and_key(2500, 0 /* port, dummy value */).0, - ]; + let ( + monitor_tx, + monitor_rx, + sui_client_mock, + sui_client, + bridge_pause_tx, + _bridge_pause_rx, + mut authorities, + ) = setup(); let old_committee = BridgeCommittee::new(authorities.clone()).unwrap(); let agg = Arc::new(ArcSwap::new(Arc::new(BridgeAuthorityAggregator::new( Arc::new(old_committee), )))); let _handle = tokio::task::spawn( - BridgeMonitor::new(sui_client.clone(), monitor_rx, agg.clone()).run(), + BridgeMonitor::new(sui_client.clone(), monitor_rx, agg.clone(), bridge_pause_tx).run(), ); authorities[0].is_blocklisted = true; let to_blocklist = &authorities[0]; @@ -652,7 +772,6 @@ mod tests { .unwrap(); // Wait for the monitor to process the event tokio::time::sleep(Duration::from_secs(1)).await; - // Now expect the committee to be updated assert!( agg.load() .committee @@ -662,11 +781,48 @@ mod tests { ); } + #[tokio::test] + async fn test_update_bridge_pause_status_with_emergency_event() { + let ( + monitor_tx, + monitor_rx, + sui_client_mock, + sui_client, + bridge_pause_tx, + bridge_pause_rx, + authorities, + ) = setup(); + let event = EmergencyOpEvent { + frozen: !*bridge_pause_tx.borrow(), // toggle the bridge pause status + }; + let committee = BridgeCommittee::new(authorities.clone()).unwrap(); + let agg = Arc::new(ArcSwap::new(Arc::new(BridgeAuthorityAggregator::new( + Arc::new(committee), + )))); + let _handle = tokio::task::spawn( + BridgeMonitor::new(sui_client.clone(), monitor_rx, agg.clone(), bridge_pause_tx).run(), + ); + + sui_client_mock.set_is_bridge_paused(event.frozen); + monitor_tx + .send(SuiBridgeEvent::EmergencyOpEvent(event.clone())) + .await + .unwrap(); + // Wait for the monitor to process the event + tokio::time::sleep(Duration::from_secs(1)).await; + // Now expect the committee to be updated + assert!(*bridge_pause_rx.borrow() == event.frozen); + } + + #[allow(clippy::type_complexity)] fn setup() -> ( mysten_metrics::metered_channel::Sender, mysten_metrics::metered_channel::Receiver, SuiMockClient, Arc>, + tokio::sync::watch::Sender, + tokio::sync::watch::Receiver, + Vec, ) { telemetry_subscribers::init_for_testing(); let registry = Registry::new(); @@ -682,6 +838,21 @@ mod tests { .channel_inflight .with_label_values(&["monitor_queue"]), ); - (monitor_tx, monitor_rx, sui_client_mock, sui_client) + let (bridge_pause_tx, bridge_pause_rx) = tokio::sync::watch::channel(false); + let authorities = vec![ + get_test_authority_and_key(2500, 0 /* port, dummy value */).0, + get_test_authority_and_key(2500, 0 /* port, dummy value */).0, + get_test_authority_and_key(2500, 0 /* port, dummy value */).0, + get_test_authority_and_key(2500, 0 /* port, dummy value */).0, + ]; + ( + monitor_tx, + monitor_rx, + sui_client_mock, + sui_client, + bridge_pause_tx, + bridge_pause_rx, + authorities, + ) } } diff --git a/crates/sui-bridge/src/node.rs b/crates/sui-bridge/src/node.rs index b9442efb4e9a6..ee0c9be68542f 100644 --- a/crates/sui-bridge/src/node.rs +++ b/crates/sui-bridge/src/node.rs @@ -110,8 +110,12 @@ async fn start_client_components( let bridge_auth_agg = Arc::new(ArcSwap::from(Arc::new(BridgeAuthorityAggregator::new( committee, )))); + // TODO: should we use one query instead of two? let sui_token_type_tags = sui_client.get_token_id_map().await.unwrap(); + let is_bridge_paused = sui_client.is_bridge_paused().await.unwrap(); + let (token_type_tags_tx, token_type_tags_rx) = tokio::sync::watch::channel(sui_token_type_tags); + let (bridge_pause_tx, bridge_pause_rx) = tokio::sync::watch::channel(is_bridge_paused); let (monitor_tx, monitor_rx) = mysten_metrics::metered_channel::channel( 10000, @@ -129,11 +133,17 @@ async fn start_client_components( client_config.sui_address, client_config.gas_object_ref.0, token_type_tags_rx, + bridge_pause_rx, metrics.clone(), ) .await; - let monitor = BridgeMonitor::new(sui_client.clone(), monitor_rx, bridge_auth_agg.clone()); + let monitor = BridgeMonitor::new( + sui_client.clone(), + monitor_rx, + bridge_auth_agg.clone(), + bridge_pause_tx, + ); all_handles.push(spawn_logged_monitored_task!(monitor.run())); let orchestrator = BridgeOrchestrator::new( diff --git a/crates/sui-bridge/src/sui_mock_client.rs b/crates/sui-bridge/src/sui_mock_client.rs index 835bd51eac7f9..f3094742d184f 100644 --- a/crates/sui-bridge/src/sui_mock_client.rs +++ b/crates/sui-bridge/src/sui_mock_client.rs @@ -24,7 +24,7 @@ use sui_types::transaction::Transaction; use sui_types::Identifier; use crate::sui_client::SuiClientInner; -use crate::types::{BridgeAction, BridgeActionStatus}; +use crate::types::{BridgeAction, BridgeActionStatus, IsBridgePaused}; /// Mock client used in test environments. #[allow(clippy::type_complexity)] @@ -43,6 +43,7 @@ pub struct SuiMockClient { get_object_info: Arc>>, onchain_status: Arc>>, bridge_committee_summary: Arc>>, + is_paused: Arc>>, requested_transactions_tx: tokio::sync::broadcast::Sender, } @@ -59,6 +60,7 @@ impl SuiMockClient { get_object_info: Default::default(), onchain_status: Default::default(), bridge_committee_summary: Default::default(), + is_paused: Default::default(), requested_transactions_tx: tokio::sync::broadcast::channel(10000).0, } } @@ -115,6 +117,10 @@ impl SuiMockClient { .replace(committee); } + pub fn set_is_bridge_paused(&self, value: IsBridgePaused) { + self.is_paused.lock().unwrap().replace(value); + } + pub fn set_wildcard_transaction_response( &self, response: BridgeResult, @@ -208,7 +214,7 @@ impl SuiClientInner for SuiMockClient { chain_id: 0, sequence_nums: vec![], bridge_records_id: ObjectID::random(), - is_frozen: false, + is_frozen: self.is_paused.lock().unwrap().unwrap_or_default(), limiter: Default::default(), committee: self .bridge_committee_summary diff --git a/crates/sui-bridge/src/types.rs b/crates/sui-bridge/src/types.rs index 7bdd6bf92586e..c6f024be7a306 100644 --- a/crates/sui-bridge/src/types.rs +++ b/crates/sui-bridge/src/types.rs @@ -43,6 +43,10 @@ pub const BRIDGE_AUTHORITY_TOTAL_VOTING_POWER: u64 = 10000; pub const USD_MULTIPLIER: u64 = 10000; // decimal places = 4 +pub type IsBridgePaused = bool; +pub const BRIDGE_PAUSED: bool = true; +pub const BRIDGE_UNPAUSED: bool = false; + #[derive(Debug, Eq, PartialEq, Clone)] pub struct BridgeAuthority { pub pubkey: BridgeAuthorityPublicKey, From 344117ced2a10643d4a2cb8eb4f72e9adde1bd36 Mon Sep 17 00:00:00 2001 From: John Martin Date: Fri, 26 Jul 2024 15:06:29 -0700 Subject: [PATCH 014/232] log the file that we fail to fetch (#18822) --- crates/sui-snapshot/src/reader.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/sui-snapshot/src/reader.rs b/crates/sui-snapshot/src/reader.rs index e6f9d19a747ed..23617301e97a4 100644 --- a/crates/sui-snapshot/src/reader.rs +++ b/crates/sui-snapshot/src/reader.rs @@ -389,8 +389,9 @@ impl StateSnapshotReaderV1 { ); if timeout > max_timeout { panic!( - "Failed to get obj file after {} attempts", - attempts + "Failed to get obj file {} after {} attempts", + file_metadata.file_path(&epoch_dir), + attempts, ); } else { attempts += 1; From 2f48f32149a25923acadab6d5234a5e566165d94 Mon Sep 17 00:00:00 2001 From: mwtian <81660174+mwtian@users.noreply.github.com> Date: Fri, 26 Jul 2024 15:26:27 -0700 Subject: [PATCH 015/232] [Consensus] bind to localhost if requested (#18823) ## Description This avoids external network on local cluster tests. ## Test plan `cargo nextest run -p sui-swarm` --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- consensus/core/src/network/anemo_network.rs | 18 +++++++----------- consensus/core/src/network/tonic_network.rs | 17 +++++++---------- crates/mysten-network/src/multiaddr.rs | 12 ++++++++++++ 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/consensus/core/src/network/anemo_network.rs b/consensus/core/src/network/anemo_network.rs index 54ffff9c15d16..6a156691cd375 100644 --- a/consensus/core/src/network/anemo_network.rs +++ b/consensus/core/src/network/anemo_network.rs @@ -22,7 +22,6 @@ use anemo_tower::{ use arc_swap::ArcSwapOption; use async_trait::async_trait; use bytes::Bytes; -use cfg_if::cfg_if; use consensus_config::{AuthorityIndex, NetworkKeyPair}; use serde::{Deserialize, Serialize}; use tokio::sync::broadcast::error::RecvError; @@ -452,16 +451,13 @@ impl NetworkManager for AnemoManager { let server = ConsensusRpcServer::new(AnemoServiceProxy::new(self.context.clone(), service)); let authority = self.context.committee.authority(self.context.own_index); - // Bind to localhost in unit tests since only local networking is needed. - // Bind to the unspecified address to allow the actual address to be assigned, - // in simtest and production. - cfg_if!( - if #[cfg(test)] { - let own_address = authority.address.with_localhost_ip(); - } else { - let own_address = authority.address.with_zero_ip(); - } - ); + // By default, bind to the unspecified address to allow the actual address to be assigned. + // But bind to localhost if it is requested. + let own_address = if authority.address.is_localhost_ip() { + authority.address.clone() + } else { + authority.address.with_zero_ip() + }; let epoch_string: String = self.context.committee.epoch().to_string(); let inbound_network_metrics = self.context.metrics.network_metrics.inbound.clone(); let outbound_network_metrics = self.context.metrics.network_metrics.outbound.clone(); diff --git a/consensus/core/src/network/tonic_network.rs b/consensus/core/src/network/tonic_network.rs index 44b81406e99ef..656813aca6f18 100644 --- a/consensus/core/src/network/tonic_network.rs +++ b/consensus/core/src/network/tonic_network.rs @@ -666,16 +666,13 @@ impl NetworkManager for TonicManager { info!("Starting tonic service"); let authority = self.context.committee.authority(self.context.own_index); - // Bind to localhost in unit tests since only local networking is needed. - // Bind to the unspecified address to allow the actual address to be assigned, - // in simtest and production. - cfg_if!( - if #[cfg(test)] { - let own_address = authority.address.with_localhost_ip(); - } else { - let own_address = authority.address.with_zero_ip(); - } - ); + // By default, bind to the unspecified address to allow the actual address to be assigned. + // But bind to localhost if it is requested. + let own_address = if authority.address.is_localhost_ip() { + authority.address.clone() + } else { + authority.address.with_zero_ip() + }; let own_address = to_socket_addr(&own_address).unwrap(); let service = TonicServiceProxy::new(self.context.clone(), service); let config = &self.context.parameters.tonic; diff --git a/crates/mysten-network/src/multiaddr.rs b/crates/mysten-network/src/multiaddr.rs index 181ec0deb0031..0fbb1f7fd68c5 100644 --- a/crates/mysten-network/src/multiaddr.rs +++ b/crates/mysten-network/src/multiaddr.rs @@ -168,6 +168,18 @@ impl Multiaddr { Self(new_address) } + pub fn is_localhost_ip(&self) -> bool { + let Some(protocol) = self.0.iter().next() else { + error!("Multiaddr is empty"); + return false; + }; + match protocol { + multiaddr::Protocol::Ip4(addr) => addr == Ipv4Addr::LOCALHOST, + multiaddr::Protocol::Ip6(addr) => addr == Ipv6Addr::LOCALHOST, + _ => false, + } + } + pub fn hostname(&self) -> Option { for component in self.iter() { match component { From db844c3aeaf4378eeb003177c0d3973145608051 Mon Sep 17 00:00:00 2001 From: Joe Hrbek <123987499+suiwombat@users.noreply.github.com> Date: Fri, 26 Jul 2024 17:28:31 -0500 Subject: [PATCH 016/232] [docker/tidb-indexer] (#18824) ## Description add dockerfile for tidb indexer ## Test Plan tsia --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- docker/sui-indexer-tidb/Dockerfile | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 docker/sui-indexer-tidb/Dockerfile diff --git a/docker/sui-indexer-tidb/Dockerfile b/docker/sui-indexer-tidb/Dockerfile new file mode 100644 index 0000000000000..d1d46bf81024c --- /dev/null +++ b/docker/sui-indexer-tidb/Dockerfile @@ -0,0 +1,37 @@ +# Build application +# +# Copy in all crates, Cargo.toml and Cargo.lock unmodified, +# and build the application. +FROM rust:1.75-bullseye AS builder +ARG PROFILE=release +ARG GIT_REVISION +ENV GIT_REVISION=$GIT_REVISION +WORKDIR "$WORKDIR/sui" + +# sui-indexer needs postgres libpq5 and ca-certificates +RUN apt update && apt install -y libpq5 ca-certificates libpq-dev postgresql + +RUN apt-get update && apt-get install -y cmake clang + +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-indexer --features mysql-feature --no-default-features + +# Production Image +FROM debian:bullseye-slim AS runtime +# Use jemalloc as memory allocator +RUN apt-get update && apt-get install -y libjemalloc-dev ca-certificates curl +ENV LD_PRELOAD /usr/lib/x86_64-linux-gnu/libjemalloc.so +WORKDIR "$WORKDIR/sui" +COPY --from=builder /sui/target/release/sui-indexer /usr/local/bin +RUN apt update && apt install -y libpq5 ca-certificates libpq-dev postgresql + +ARG BUILD_DATE +ARG GIT_REVISION +LABEL build-date=$BUILD_DATE +LABEL git-revision=$GIT_REVISION From 25017335f15a4b5bda591b78364faf50d1ffe40a Mon Sep 17 00:00:00 2001 From: Rijnard van Tonder Date: Fri, 26 Jul 2024 17:33:14 -0700 Subject: [PATCH 017/232] move: dump-bytecode-as-base64 uses Move.lock addresses (#18794) ## Description `sui move build` does not ordinarily require a network connection because it doesn't need to know about a chain's ID. The exception is when `--dump-bytecode-as-base64` is specified: In this case, we should resolve the correct addresses for the respective chain (e.g., testnet, mainnet) from the `Move.lock` under automated address management. Two options to fix `sui move build --dump-bytecode-as-base64` to work with automated addresses / by resolving from the `Move.lock`: 1. Require an extra `--chain-id` flag on `sui move build`, which a user must specify along with `--dump-bytecode-as-base64`. E.g., ``` sui move build --dump-bytecode-as-base64 --chain-id "$(sui client chain-identifier)" ``` OR 2. Require a network connection _only when_ `--dump-bytecode-as-base64` is set and and resolve the chain-id without requiring a flag. This PR opts for **(2)**, but it is trivial to change the implementation and test to do **(1)** instead. **(1)** should come with an accompanying doc change though. Context: encountered when running, e.g., ``` execSync(`${SUI} move build --dump-bytecode-as-base64 --path ${packagePath}` ``` ## Test plan Added test --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [x] CLI: Fixed an issue where `--dump-bytecode-as-base64` did not work as expected if [package addresses are automatically managed](https://docs.sui.io/concepts/sui-move-concepts/packages/automated-address-management#adopting-automated-address-management-for-published-packages). - [ ] Rust SDK: - [ ] REST API: --- crates/sui-move/src/build.rs | 9 +++- crates/sui/src/sui_commands.rs | 27 +++++++++- crates/sui/tests/cli_tests.rs | 54 +++++++++++++++++++ .../tests/data/depends_on_simple/Move.toml | 9 ++++ .../sources/depends_on_simple.move | 4 ++ crates/sui/tests/data/simple/Move.toml | 8 +++ .../sui/tests/data/simple/sources/simple.move | 4 ++ sdk/deepbook/test/e2e/setup.ts | 16 ++++-- sdk/kiosk/test/e2e/setup.ts | 16 ++++-- sdk/typescript/test/e2e/utils/setup.ts | 4 +- 10 files changed, 140 insertions(+), 11 deletions(-) create mode 100644 crates/sui/tests/data/depends_on_simple/Move.toml create mode 100644 crates/sui/tests/data/depends_on_simple/sources/depends_on_simple.move create mode 100644 crates/sui/tests/data/simple/Move.toml create mode 100644 crates/sui/tests/data/simple/sources/simple.move diff --git a/crates/sui-move/src/build.rs b/crates/sui-move/src/build.rs index 421ff6ca964fe..5baa78625b08b 100644 --- a/crates/sui-move/src/build.rs +++ b/crates/sui-move/src/build.rs @@ -29,6 +29,11 @@ pub struct Build { /// and events. #[clap(long, global = true)] pub generate_struct_layouts: bool, + /// The chain ID, if resolved. Required when the dump_bytecode_as_base64 is true, + /// for automated address management, where package addresses are resolved for the + /// respective chain in the Move.lock file. + #[clap(skip)] + pub chain_id: Option, } impl Build { @@ -45,6 +50,7 @@ impl Build { self.with_unpublished_dependencies, self.dump_bytecode_as_base64, self.generate_struct_layouts, + self.chain_id.clone(), ) } @@ -54,12 +60,13 @@ impl Build { with_unpublished_deps: bool, dump_bytecode_as_base64: bool, generate_struct_layouts: bool, + chain_id: Option, ) -> anyhow::Result<()> { let pkg = BuildConfig { config, run_bytecode_verifier: true, print_diags_to_stderr: true, - chain_id: None, + chain_id, } .build(rerooted_path)?; if dump_bytecode_as_base64 { diff --git a/crates/sui/src/sui_commands.rs b/crates/sui/src/sui_commands.rs index b59ca8a50ad86..3b6aed25f268e 100644 --- a/crates/sui/src/sui_commands.rs +++ b/crates/sui/src/sui_commands.rs @@ -287,6 +287,10 @@ pub enum SuiCommand { /// Path to a package which the command should be run with respect to. #[clap(long = "path", short = 'p', global = true)] package_path: Option, + /// Sets the file storing the state of our user accounts (an empty one will be created if missing) + /// Only used when the `--dump-bytecode-as-base64` is set. + #[clap(long = "client.config")] + config: Option, /// Package build options #[clap(flatten)] build_config: BuildConfig, @@ -448,8 +452,27 @@ impl SuiCommand { SuiCommand::Move { package_path, build_config, - cmd, - } => execute_move_command(package_path.as_deref(), build_config, cmd), + mut cmd, + config: client_config, + } => { + match &mut cmd { + sui_move::Command::Build(build) if build.dump_bytecode_as_base64 => { + // `sui move build` does not ordinarily require a network connection. + // The exception is when --dump-bytecode-as-base64 is specified: In this + // case, we should resolve the correct addresses for the respective chain + // (e.g., testnet, mainnet) from the Move.lock under automated address management. + let config = + client_config.unwrap_or(sui_config_dir()?.join(SUI_CLIENT_CONFIG)); + prompt_if_no_config(&config, false).await?; + let context = WalletContext::new(&config, None, None)?; + let client = context.get_client().await?; + let chain_id = client.read_api().get_chain_identifier().await.ok(); + build.chain_id = chain_id.clone(); + } + _ => (), + }; + execute_move_command(package_path.as_deref(), build_config, cmd) + } SuiCommand::BridgeInitialize { network_config, client_config, diff --git a/crates/sui/tests/cli_tests.rs b/crates/sui/tests/cli_tests.rs index 59a13bc6e4c7e..cc0f05e41e3af 100644 --- a/crates/sui/tests/cli_tests.rs +++ b/crates/sui/tests/cli_tests.rs @@ -7,6 +7,7 @@ use std::net::SocketAddr; use std::os::unix::prelude::FileExt; use std::{fmt::Write, fs::read_dir, path::PathBuf, str, thread, time::Duration}; +use std::env; #[cfg(not(msim))] use std::str::FromStr; @@ -3934,6 +3935,59 @@ async fn test_clever_errors() -> Result<(), anyhow::Error> { Ok(()) } +#[tokio::test] +async fn test_move_build_bytecode_with_address_resolution() -> Result<(), anyhow::Error> { + let test_cluster = TestClusterBuilder::new().build().await; + let config_path = test_cluster.swarm.dir().join(SUI_CLIENT_CONFIG); + + // Package setup: a simple package depends on another and copied to tmpdir + let mut simple_package_path = PathBuf::from(TEST_DATA_DIR); + simple_package_path.push("simple"); + + let mut depends_on_simple_package_path = PathBuf::from(TEST_DATA_DIR); + depends_on_simple_package_path.push("depends_on_simple"); + + let tmp_dir = tempfile::tempdir().unwrap(); + + fs_extra::dir::copy( + &simple_package_path, + &tmp_dir, + &fs_extra::dir::CopyOptions::default(), + )?; + + fs_extra::dir::copy( + &depends_on_simple_package_path, + &tmp_dir, + &fs_extra::dir::CopyOptions::default(), + )?; + + // Publish simple package. + let simple_tmp_dir = tmp_dir.path().join("simple"); + test_with_sui_binary(&[ + "client", + "--client.config", + config_path.to_str().unwrap(), + "publish", + simple_tmp_dir.to_str().unwrap(), + ]) + .await?; + + // Build the package that depends on 'simple' package. Addresses must resolve successfully + // from the `Move.lock` for this command to succeed at all. + let depends_on_simple_tmp_dir = tmp_dir.path().join("depends_on_simple"); + test_with_sui_binary(&[ + "move", + "--client.config", + config_path.to_str().unwrap(), + "build", + "--dump-bytecode-as-base64", + "--path", + depends_on_simple_tmp_dir.to_str().unwrap(), + ]) + .await?; + Ok(()) +} + #[tokio::test] async fn test_parse_host_port() { let input = "127.0.0.0"; diff --git a/crates/sui/tests/data/depends_on_simple/Move.toml b/crates/sui/tests/data/depends_on_simple/Move.toml new file mode 100644 index 0000000000000..0688373ff4de6 --- /dev/null +++ b/crates/sui/tests/data/depends_on_simple/Move.toml @@ -0,0 +1,9 @@ +[package] +name = "depends_on_simple" +edition = "2024.beta" + +[dependencies] +simple = { local = "../simple" } + +[addresses] +depends_on_simple = "0x0" diff --git a/crates/sui/tests/data/depends_on_simple/sources/depends_on_simple.move b/crates/sui/tests/data/depends_on_simple/sources/depends_on_simple.move new file mode 100644 index 0000000000000..dee8c33579f69 --- /dev/null +++ b/crates/sui/tests/data/depends_on_simple/sources/depends_on_simple.move @@ -0,0 +1,4 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +module depends_on_simple::depends_on_simple {} diff --git a/crates/sui/tests/data/simple/Move.toml b/crates/sui/tests/data/simple/Move.toml new file mode 100644 index 0000000000000..1775a6b3741ef --- /dev/null +++ b/crates/sui/tests/data/simple/Move.toml @@ -0,0 +1,8 @@ +[package] +name = "simple" +edition = "2024.beta" + +[dependencies] + +[addresses] +simple = "0x0" diff --git a/crates/sui/tests/data/simple/sources/simple.move b/crates/sui/tests/data/simple/sources/simple.move new file mode 100644 index 0000000000000..40d3377133359 --- /dev/null +++ b/crates/sui/tests/data/simple/sources/simple.move @@ -0,0 +1,4 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +module simple::simple {} diff --git a/sdk/deepbook/test/e2e/setup.ts b/sdk/deepbook/test/e2e/setup.ts index e6503cf356aad..65839279c1187 100644 --- a/sdk/deepbook/test/e2e/setup.ts +++ b/sdk/deepbook/test/e2e/setup.ts @@ -2,6 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import { execSync } from 'child_process'; +import { mkdtemp } from 'fs/promises'; +import { tmpdir } from 'os'; +import path from 'path'; import type { DevInspectResults, SuiObjectChangeCreated, @@ -30,10 +33,12 @@ export const DEFAULT_LOT_SIZE = 1n; export class TestToolbox { keypair: Ed25519Keypair; client: SuiClient; + configPath: string; - constructor(keypair: Ed25519Keypair, client: SuiClient) { + constructor(keypair: Ed25519Keypair, client: SuiClient, configPath: string) { this.keypair = keypair; this.client = client; + this.configPath = configPath; } address() { @@ -64,7 +69,12 @@ export async function setupSuiClient() { retryIf: (error: any) => !(error instanceof FaucetRateLimitError), logger: (msg) => console.warn('Retrying requesting from faucet: ' + msg), }); - return new TestToolbox(keypair, client); + + const tmpDirPath = path.join(tmpdir(), 'config-'); + const tmpDir = await mkdtemp(tmpDirPath); + const configPath = path.join(tmpDir, 'client.yaml'); + execSync(`${SUI_BIN} client --yes --client.config ${configPath}`, { encoding: 'utf-8' }); + return new TestToolbox(keypair, client, configPath); } // TODO: expose these testing utils from @mysten/sui @@ -81,7 +91,7 @@ export async function publishPackage(packagePath: string, toolbox?: TestToolbox) const { modules, dependencies } = JSON.parse( execSync( - `${SUI_BIN} move build --dump-bytecode-as-base64 --path ${packagePath} --install-dir ${tmpobj.name}`, + `${SUI_BIN} move move --client.config ${toolbox.configPath} build --dump-bytecode-as-base64 --path ${packagePath} --install-dir ${tmpobj.name}`, { encoding: 'utf-8' }, ), ); diff --git a/sdk/kiosk/test/e2e/setup.ts b/sdk/kiosk/test/e2e/setup.ts index d15c9180a38ef..42da2e190e5cd 100644 --- a/sdk/kiosk/test/e2e/setup.ts +++ b/sdk/kiosk/test/e2e/setup.ts @@ -2,6 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import { execSync } from 'child_process'; +import { mkdtemp } from 'fs/promises'; +import { tmpdir } from 'os'; +import path from 'path'; import type { DevInspectResults, SuiObjectChangePublished, @@ -28,10 +31,12 @@ const SUI_BIN = import.meta.env.VITE_SUI_BIN ?? 'cargo run --bin sui'; export class TestToolbox { keypair: Ed25519Keypair; client: SuiClient; + configPath: string; - constructor(keypair: Ed25519Keypair, client: SuiClient) { + constructor(keypair: Ed25519Keypair, client: SuiClient, configPath: string) { this.keypair = keypair; this.client = client; + this.configPath = configPath; } address() { @@ -62,7 +67,12 @@ export async function setupSuiClient() { retryIf: (error: any) => !(error instanceof FaucetRateLimitError), logger: (msg) => console.warn('Retrying requesting from faucet: ' + msg), }); - return new TestToolbox(keypair, client); + + const tmpDirPath = path.join(tmpdir(), 'config-'); + const tmpDir = await mkdtemp(tmpDirPath); + const configPath = path.join(tmpDir, 'client.yaml'); + execSync(`${SUI_BIN} client --yes --client.config ${configPath}`, { encoding: 'utf-8' }); + return new TestToolbox(keypair, client, configPath); } // TODO: expose these testing utils from @mysten/sui @@ -79,7 +89,7 @@ export async function publishPackage(packagePath: string, toolbox?: TestToolbox) const { modules, dependencies } = JSON.parse( execSync( - `${SUI_BIN} move build --dump-bytecode-as-base64 --path ${packagePath} --install-dir ${tmpobj.name}`, + `${SUI_BIN} move --client.config ${toolbox.configPath} build --dump-bytecode-as-base64 --path ${packagePath} --install-dir ${tmpobj.name}`, { encoding: 'utf-8' }, ), ); diff --git a/sdk/typescript/test/e2e/utils/setup.ts b/sdk/typescript/test/e2e/utils/setup.ts index 037e490f013d5..63362790eee31 100644 --- a/sdk/typescript/test/e2e/utils/setup.ts +++ b/sdk/typescript/test/e2e/utils/setup.ts @@ -174,7 +174,7 @@ export async function publishPackage(packagePath: string, toolbox?: TestToolbox) const { modules, dependencies } = JSON.parse( execSync( - `${SUI_BIN} move build --dump-bytecode-as-base64 --path ${packagePath} --install-dir ${tmpobj.name}`, + `${SUI_BIN} move --client.config ${toolbox.configPath} build --dump-bytecode-as-base64 --path ${packagePath} --install-dir ${tmpobj.name}`, { encoding: 'utf-8' }, ), ); @@ -228,7 +228,7 @@ export async function upgradePackage( const { modules, dependencies, digest } = JSON.parse( execSync( - `${SUI_BIN} move build --dump-bytecode-as-base64 --path ${packagePath} --install-dir ${tmpobj.name}`, + `${SUI_BIN} move --client.config ${toolbox.configPath} build --dump-bytecode-as-base64 --path ${packagePath} --install-dir ${tmpobj.name}`, { encoding: 'utf-8' }, ), ); From eb55624e7a5decda7f4946fe86d2b96b3aed6fb1 Mon Sep 17 00:00:00 2001 From: Todd Nowacki Date: Sun, 28 Jul 2024 13:56:41 -0700 Subject: [PATCH 018/232] [move] Copy Sui's Move stdlib into external-crates. Bump the default package version to 2024 beta. (#18827) ## Description - Copied Sui's stdlib over to external-crates - Changed the default edition for package-less files in the compiler to 2024 beta. ## Test plan - Ran tests, updated where necessary --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- bridge/move/tokens/btc/Move.toml | 2 +- bridge/move/tokens/eth/Move.toml | 2 +- bridge/move/tokens/mock/ka/Move.toml | 2 +- bridge/move/tokens/usdc/Move.toml | 2 +- bridge/move/tokens/usdt/Move.toml | 2 +- .../tests/size_limits/event_limits_tests.exp | 4 +- .../size_limits/move_object_size_limit.exp | 4 +- .../move_package_management_tests.rs | 6 +- crates/sui-move/src/unit_test.rs | 5 +- crates/sui-open-rpc/spec/openrpc.json | 31 +- crates/sui-protocol-config/src/lib.rs | 96 + ...ocol_config__test__Mainnet_version_53.snap | 29 + ...ocol_config__test__Testnet_version_53.snap | 29 + ...sui_protocol_config__test__version_53.snap | 30 + .../reference_safety/imm_borrow_loc.mvir | 1 - .../imm_borrow_loc_valid.mvir | 1 - .../reference_safety/imm_borrow_on_mut.mvir | 1 - .../tests/type_safety/cant_deref_resource.exp | 8 +- .../type_safety/cant_deref_resource.mvir | 18 +- .../mut_call_from_get_resource.mvir | 18 +- .../crates/language-benchmarks/src/move_vm.rs | 1 + .../move-analyzer/tests/completion/Move.toml | 1 - .../move-analyzer/tests/dot_completion.exp | 92 + .../move-analyzer/tests/enums/Move.toml | 1 - .../move-analyzer/tests/implicit_uses.exp | 6 +- .../move-analyzer/tests/inlay-hints/Move.toml | 1 - .../crates/move-analyzer/tests/macros.exp | 74 +- .../crates/move-analyzer/tests/macros.ide | 6 +- .../tests/mod-ident-uniform/Move.toml | 2 +- .../move-analyzer/tests/move-2024/Move.toml | 1 - .../tests/parse-error-dep/Move.toml | 2 +- .../move-analyzer/tests/parse-error/Move.toml | 2 +- .../move-analyzer/tests/partial-dot/Move.toml | 2 +- .../tests/partial-function/Move.toml | 2 +- .../tests/pkg-naming-error/Move.toml | 2 +- .../tests/pre-type-error-dep/Move.toml | 2 +- .../tests/pre-type-error/Move.toml | 2 +- .../move-analyzer/tests/symbols/Move.toml | 2 +- .../move/crates/move-cli/src/main.rs | 11 +- .../build_with_bytecode/B/Move.toml | 1 + .../build_with_bytecode/C/Move.toml | 1 + .../build_tests/build_with_bytecode/Move.toml | 1 + .../build_with_dep_warnings/Move.toml | 1 + .../build_with_dep_warnings/dep/Move.toml | 1 + .../build_tests/build_with_warnings/Move.toml | 1 + .../build_tests/canonicalize_module/Move.toml | 1 + .../canonicalize_module/sources/m.move | 8 +- .../circular_dependencies/Move.toml | 1 + .../circular_dependencies/bar/Move.toml | 1 + .../build_tests/dependency_chain/Move.toml | 1 + .../dependency_chain/bar/Move.toml | 1 + .../dependency_chain/foo/Move.toml | 1 + .../tests/build_tests/dev_address/Move.toml | 1 + .../build_tests/disassemble_module/Move.toml | 1 + .../disassemble_module/sources/m.move | 7 +- .../empty_module_no_deps/Move.toml | 1 + .../include_exclude_stdlib/Move.toml | 1 + .../include_exclude_stdlib/args.exp | 22 +- .../sources/UseSigner.move | 8 +- .../tests/build_tests/json_errors/Move.toml | 1 + .../tests/build_tests/migration/Move.toml | 1 + .../rebuild_after_adding_new_source/Move.toml | 1 + .../Move.toml | 1 + .../rebuild_after_touching_manifest/Move.toml | 1 + .../rebuild_after_touching_source/Move.toml | 1 + .../rebuild_no_modification/Move.toml | 1 + .../build_tests/unbound_address/Move.toml | 1 + .../build_tests/unbound_dependency/Move.toml | 1 + .../tests/metatests/cov/plain/Move.toml | 1 + .../cov/two-runs-diff-module/Move.toml | 1 + .../cov/two-runs-diff-module/sources/M1.move | 4 +- .../cov/two-runs-diff-module/sources/M2.move | 4 +- .../cov/two-runs-same-module/Move.toml | 1 + .../cov/two-runs-same-module/sources/M.move | 4 +- .../assign_dev_addr_for_dep/Move.toml | 1 + .../assign_dev_addr_for_dep/dep/Move.toml | 1 + .../Move.toml | 1 + .../Move.toml | 1 + .../build_modules_and_scripts/Move.toml | 1 + .../build_modules_and_scripts/sources/M.move | 4 +- .../doctor_with_stdlib/Move.toml | 3 +- .../doctor_with_stdlib/sources/M.move | 4 +- .../explain_arithmetic_failure/Move.toml | 1 + .../explain_stdlib_abort/Move.toml | 1 + .../sources/bad_borrow.move | 1 - .../explain_user_module_abort/Move.toml | 1 + .../sources/Fail.move | 4 +- .../sandbox_tests/gas_metering/Move.toml | 1 + .../generate_struct_layout/Move.toml | 1 + .../generate_struct_layout/sources/M1.move | 8 +- .../generate_struct_layout/sources/M2.move | 2 +- .../sources/phantoms.move | 10 +- .../module_disassemble/Move.toml | 1 + .../module_disassemble/deps1/Move.toml | 1 + .../module_disassemble/deps2/Move.toml | 1 + .../module_disassemble/sources/M1.move | 3 +- .../module_publish_view/Move.toml | 1 + .../module_publish_view/sources/Module.move | 6 +- .../multi_module_publish/Move.toml | 1 + .../sources/GoodFriends.move | 3 +- .../Move.toml | 1 + .../dep/Move.toml | 1 + .../dep/sources/m.move | 4 +- .../sandbox_tests/package_basics/Move.toml | 1 + .../sandbox_tests/package_basics/args.exp | 13 +- .../sandbox_tests/package_basics/args.txt | 2 +- .../sandbox_tests/print_stack_trace/Move.toml | 3 +- .../print_stack_trace/sources/M.move | 4 +- .../print_stack_trace/sources/N.move | 6 +- .../sources/print_stack_trace.move | 3 +- .../sandbox_tests/print_values/Move.toml | 3 +- .../tests/sandbox_tests/print_values/args.exp | 7 - .../sandbox_tests/print_values/sources/M.move | 40 +- .../sandbox_tests/publish_then_run/Move.toml | 1 + .../publish_then_run/sources/M.move | 4 +- .../random_test_flag_correctness/Move.toml | 1 + .../sandbox_tests/use_named_address/Move.toml | 1 + .../Move.toml | 1 + .../Move.toml | 1 + .../no_git_remote_package/Move.toml | 1 + .../upload_tests/valid_package1/Move.toml | 1 + .../upload_tests/valid_package2/Move.toml | 1 + .../upload_tests/valid_package3/Move.toml | 1 + .../crates/move-compiler/src/editions/mod.rs | 2 +- .../move_2024/typing/dot_call_non_struct.exp | 2 +- .../typing/duplicate_defines_primitive.exp | 4 +- .../typing/duplicate_defines_primitive.move | 4 +- .../tests/move_check_testsuite.rs | 2 +- .../tests/sources/different_visbilities.move | 4 +- ...t_template_AnotherTypeOfScript.notest_move | 4 +- .../root_template_OneTypeOfScript.notest_move | 4 +- .../src/resolution/resolution_graph.rs | 3 +- .../tests/borrow/basic_test.move | 22 +- .../tests/borrow/function_call.exp | 4192 +++++++++++-- .../tests/borrow/function_call.move | 10 +- .../tests/borrow/hyper_edge.exp | 4194 +++++++++++-- .../tests/borrow/hyper_edge.move | 13 +- .../tests/borrow_strong/basic_test.move | 28 +- .../tests/eliminate_imm_refs/basic_test.move | 4 +- .../escape_analysis/return_internal_refs.move | 4 +- .../escape_analysis/return_refs_into_vec.exp | 5250 +++++++++++++++-- .../escape_analysis/return_refs_into_vec.move | 6 +- .../tests/escape_analysis/struct_eq.exp | 7 - .../tests/escape_analysis/struct_eq.move | 4 +- .../tests/from_move/smoke_test.exp | 16 +- .../tests/from_move/smoke_test.move | 8 +- .../tests/livevar/basic_test.move | 6 +- .../tests/memory_instr/basic_test.move | 22 +- .../mut_ref_instrumentation/basic_test.move | 22 +- .../crates/move-stdlib-natives/src/lib.rs | 89 +- .../move/crates/move-stdlib/Move.toml | 1 + .../move/crates/move-stdlib/docs/address.md | 44 + .../move/crates/move-stdlib/docs/ascii.md | 372 +- .../move/crates/move-stdlib/docs/bcs.md | 4 +- .../crates/move-stdlib/docs/bit_vector.md | 81 +- .../move-stdlib/{nursery => }/docs/debug.md | 5 +- .../move/crates/move-stdlib/docs/error.md | 475 -- .../crates/move-stdlib/docs/fixed_point32.md | 235 +- .../move/crates/move-stdlib/docs/hash.md | 8 +- .../move/crates/move-stdlib/docs/macros.md | 14 + .../move/crates/move-stdlib/docs/option.md | 62 +- .../move/crates/move-stdlib/docs/overview.md | 11 +- .../move/crates/move-stdlib/docs/signer.md | 63 - .../move/crates/move-stdlib/docs/string.md | 268 +- .../move/crates/move-stdlib/docs/type_name.md | 222 +- .../move/crates/move-stdlib/docs/u128.md | 195 + .../move/crates/move-stdlib/docs/u16.md | 195 + .../move/crates/move-stdlib/docs/u256.md | 145 + .../move/crates/move-stdlib/docs/u32.md | 195 + .../move/crates/move-stdlib/docs/u64.md | 195 + .../move/crates/move-stdlib/docs/u8.md | 195 + .../move/crates/move-stdlib/docs/vector.md | 100 +- .../move-stdlib/error_description.errmap | Bin 1310 -> 0 bytes .../move/crates/move-stdlib/nursery/Move.toml | 8 - .../move-stdlib/nursery/docs/compare.md | 167 - .../move-stdlib/nursery/sources/compare.move | 70 - .../move-stdlib/nursery/sources/debug.move | 18 - .../nursery/tests/compare_tests.move | 86 - .../crates/move-stdlib/sources/address.move | 12 + .../crates/move-stdlib/sources/ascii.move | 167 +- .../move/crates/move-stdlib/sources/bcs.move | 3 + .../move-stdlib/sources/bit_vector.move | 45 +- .../crates/move-stdlib/sources/debug.move | 9 + .../crates/move-stdlib/sources/error.move | 82 - .../move-stdlib/sources/fixed_point32.move | 70 +- .../move/crates/move-stdlib/sources/hash.move | 3 + .../crates/move-stdlib/sources/macros.move | 102 + .../crates/move-stdlib/sources/option.move | 162 +- .../crates/move-stdlib/sources/signer.move | 15 - .../crates/move-stdlib/sources/string.move | 118 +- .../crates/move-stdlib/sources/type_name.move | 97 +- .../move/crates/move-stdlib/sources/u128.move | 79 + .../move/crates/move-stdlib/sources/u16.move | 79 + .../move/crates/move-stdlib/sources/u256.move | 50 + .../move/crates/move-stdlib/sources/u32.move | 79 + .../move/crates/move-stdlib/sources/u64.move | 79 + .../move/crates/move-stdlib/sources/u8.move | 79 + .../crates/move-stdlib/sources/unit_test.move | 22 + .../crates/move-stdlib/sources/vector.move | 300 +- .../move/crates/move-stdlib/src/lib.rs | 24 - .../move/crates/move-stdlib/src/main.rs | 5 - .../crates/move-stdlib/tests/ascii_tests.move | 188 +- .../crates/move-stdlib/tests/bcs_tests.move | 44 +- .../move-stdlib/tests/bit_vector_tests.move | 133 +- .../move-stdlib/tests/fixedpoint32_tests.move | 100 +- .../crates/move-stdlib/tests/hash_tests.move | 9 +- .../move-stdlib/tests/integer_tests.move | 220 + .../move-stdlib/tests/move_unit_test.rs | 50 - .../tests/move_verification_test.rs | 13 - .../move-stdlib/tests/option_tests.move | 156 +- .../move-stdlib/tests/string_tests.move | 80 +- .../move-stdlib/tests/type_name_tests.move | 103 +- .../crates/move-stdlib/tests/u128_tests.move | 78 + .../crates/move-stdlib/tests/u16_tests.move | 78 + .../crates/move-stdlib/tests/u256_tests.move | 72 + .../crates/move-stdlib/tests/u32_tests.move | 78 + .../crates/move-stdlib/tests/u64_tests.move | 79 + .../crates/move-stdlib/tests/u8_tests.move | 77 + .../move-stdlib/tests/vector_tests.move | 840 ++- .../src/vm_test_harness.rs | 1 + .../crates/move-unit-test/src/test_runner.rs | 1 + .../tests/test_sources/address_args.move | 4 +- .../tests/test_sources/arithmetic_errors.move | 4 +- .../tests/test_sources/construct_data.move | 8 +- .../test_sources/cross_module_aborts.exp | 8 +- .../test_sources/cross_module_aborts.move | 6 +- .../tests/test_sources/do_nothing.move | 4 +- .../test_sources/expected_abort_no_abort.move | 4 +- .../tests/test_sources/native_abort.exp | 12 +- .../tests/test_sources/native_abort.move | 1 - .../test_sources/non_exsistent_native.move | 4 +- .../tests/test_sources/proposal_test.move | 8 +- .../tests/test_sources/signer_args.exp | 6 +- .../tests/test_sources/signer_args.move | 10 +- .../tests/test_sources/timeout.exp | 4 +- .../tests/test_sources/timeout.move | 6 +- .../tests/test_sources/unexpected_abort.exp | 38 +- .../tests/test_sources/unexpected_abort.move | 4 +- .../move-vm-integration-tests/Cargo.toml | 2 +- .../src/tests/binary_format_version.rs | 2 + .../src/tests/depth_tests_modules.move | 66 +- .../src/tests/function_arg_tests.rs | 4 +- .../src/tests/loader_tests_modules.move | 268 +- .../src/tests/nested_loop_tests.rs | 6 +- .../src/tests/relinking_tests_b_v1.move | 2 +- .../src/tests/relinking_tests_c_v0.move | 2 +- .../src/tests/relinking_tests_c_v1.move | 4 +- .../src/tests/relinking_tests_c_v2.move | 6 +- .../tests/builtins/get_txn_sender.exp | 4 + .../tests/builtins/get_txn_sender.mvir | 10 +- .../tests/entry_points/call_native.exp | 4 +- .../tests/entry_points/call_native.move | 2 +- .../move/crates/test-generation/src/lib.rs | 1 + .../sui-adapter/src/execution_engine.rs | 2 +- .../latest/sui-move-natives/src/lib.rs | 116 +- sui-execution/src/latest.rs | 2 +- 256 files changed, 18603 insertions(+), 4224 deletions(-) create mode 100644 external-crates/move/crates/move-stdlib/docs/address.md rename external-crates/move/crates/move-stdlib/{nursery => }/docs/debug.md (86%) delete mode 100644 external-crates/move/crates/move-stdlib/docs/error.md create mode 100644 external-crates/move/crates/move-stdlib/docs/macros.md delete mode 100644 external-crates/move/crates/move-stdlib/docs/signer.md create mode 100644 external-crates/move/crates/move-stdlib/docs/u128.md create mode 100644 external-crates/move/crates/move-stdlib/docs/u16.md create mode 100644 external-crates/move/crates/move-stdlib/docs/u256.md create mode 100644 external-crates/move/crates/move-stdlib/docs/u32.md create mode 100644 external-crates/move/crates/move-stdlib/docs/u64.md create mode 100644 external-crates/move/crates/move-stdlib/docs/u8.md delete mode 100644 external-crates/move/crates/move-stdlib/error_description.errmap delete mode 100644 external-crates/move/crates/move-stdlib/nursery/Move.toml delete mode 100644 external-crates/move/crates/move-stdlib/nursery/docs/compare.md delete mode 100644 external-crates/move/crates/move-stdlib/nursery/sources/compare.move delete mode 100644 external-crates/move/crates/move-stdlib/nursery/sources/debug.move delete mode 100644 external-crates/move/crates/move-stdlib/nursery/tests/compare_tests.move create mode 100644 external-crates/move/crates/move-stdlib/sources/address.move create mode 100644 external-crates/move/crates/move-stdlib/sources/debug.move delete mode 100644 external-crates/move/crates/move-stdlib/sources/error.move create mode 100644 external-crates/move/crates/move-stdlib/sources/macros.move delete mode 100644 external-crates/move/crates/move-stdlib/sources/signer.move create mode 100644 external-crates/move/crates/move-stdlib/sources/u128.move create mode 100644 external-crates/move/crates/move-stdlib/sources/u16.move create mode 100644 external-crates/move/crates/move-stdlib/sources/u256.move create mode 100644 external-crates/move/crates/move-stdlib/sources/u32.move create mode 100644 external-crates/move/crates/move-stdlib/sources/u64.move create mode 100644 external-crates/move/crates/move-stdlib/sources/u8.move create mode 100644 external-crates/move/crates/move-stdlib/tests/integer_tests.move delete mode 100644 external-crates/move/crates/move-stdlib/tests/move_unit_test.rs delete mode 100644 external-crates/move/crates/move-stdlib/tests/move_verification_test.rs create mode 100644 external-crates/move/crates/move-stdlib/tests/u128_tests.move create mode 100644 external-crates/move/crates/move-stdlib/tests/u16_tests.move create mode 100644 external-crates/move/crates/move-stdlib/tests/u256_tests.move create mode 100644 external-crates/move/crates/move-stdlib/tests/u32_tests.move create mode 100644 external-crates/move/crates/move-stdlib/tests/u64_tests.move create mode 100644 external-crates/move/crates/move-stdlib/tests/u8_tests.move diff --git a/bridge/move/tokens/btc/Move.toml b/bridge/move/tokens/btc/Move.toml index cbd69d8556431..47824231423d3 100644 --- a/bridge/move/tokens/btc/Move.toml +++ b/bridge/move/tokens/btc/Move.toml @@ -1,6 +1,7 @@ [package] name = "BridgedBTC" version = "0.0.1" +edition = "legacy" [dependencies] MoveStdlib = { local = "../../../../crates/sui-framework/packages/move-stdlib" } @@ -8,4 +9,3 @@ Sui = { local = "../../../../crates/sui-framework/packages/sui-framework" } [addresses] bridged_btc = "0x0" - diff --git a/bridge/move/tokens/eth/Move.toml b/bridge/move/tokens/eth/Move.toml index 215aa37ce8687..1a21898cc0355 100644 --- a/bridge/move/tokens/eth/Move.toml +++ b/bridge/move/tokens/eth/Move.toml @@ -1,6 +1,7 @@ [package] name = "BridgedETH" version = "0.0.1" +edition = "legacy" [dependencies] MoveStdlib = { local = "../../../../crates/sui-framework/packages/move-stdlib" } @@ -8,4 +9,3 @@ Sui = { local = "../../../../crates/sui-framework/packages/sui-framework" } [addresses] bridged_eth = "0x0" - diff --git a/bridge/move/tokens/mock/ka/Move.toml b/bridge/move/tokens/mock/ka/Move.toml index ad08496bd39e1..1fbdf04ca2975 100644 --- a/bridge/move/tokens/mock/ka/Move.toml +++ b/bridge/move/tokens/mock/ka/Move.toml @@ -1,6 +1,7 @@ [package] name = "BridgedKa" version = "0.0.1" +edition = "legacy" [dependencies] MoveStdlib = { local = "../../../../../crates/sui-framework/packages/move-stdlib" } @@ -8,4 +9,3 @@ Sui = { local = "../../../../../crates/sui-framework/packages/sui-framework" } [addresses] bridged_ka = "0x0" - diff --git a/bridge/move/tokens/usdc/Move.toml b/bridge/move/tokens/usdc/Move.toml index e98ae4d71c621..a956409c0839f 100644 --- a/bridge/move/tokens/usdc/Move.toml +++ b/bridge/move/tokens/usdc/Move.toml @@ -1,6 +1,7 @@ [package] name = "BridgedUSDC" version = "0.0.1" +edition = "legacy" [dependencies] MoveStdlib = { local = "../../../../crates/sui-framework/packages/move-stdlib" } @@ -8,4 +9,3 @@ Sui = { local = "../../../../crates/sui-framework/packages/sui-framework" } [addresses] bridged_usdc = "0x0" - diff --git a/bridge/move/tokens/usdt/Move.toml b/bridge/move/tokens/usdt/Move.toml index 699123ddb0325..1d86a908fdd8e 100644 --- a/bridge/move/tokens/usdt/Move.toml +++ b/bridge/move/tokens/usdt/Move.toml @@ -1,6 +1,7 @@ [package] name = "BridgedUSDT" version = "0.0.1" +edition = "legacy" [dependencies] MoveStdlib = { local = "../../../../crates/sui-framework/packages/move-stdlib" } @@ -8,4 +9,3 @@ Sui = { local = "../../../../crates/sui-framework/packages/sui-framework" } [addresses] bridged_usdt = "0x0" - diff --git a/crates/sui-adapter-transactional-tests/tests/size_limits/event_limits_tests.exp b/crates/sui-adapter-transactional-tests/tests/size_limits/event_limits_tests.exp index 4b2c0d8cf17e2..bddf4a6d99846 100644 --- a/crates/sui-adapter-transactional-tests/tests/size_limits/event_limits_tests.exp +++ b/crates/sui-adapter-transactional-tests/tests/size_limits/event_limits_tests.exp @@ -32,13 +32,13 @@ task 6, lines 68-70: //# run Test::M1::emit_event_with_size --args 200000 --gas-budget 100000000000000 --summarize events: 1 mutated: 1 -gas summary: computation_cost: 1393000000, storage_cost: 988000, storage_rebate: 978120, non_refundable_storage_fee: 9880 +gas summary: computation_cost: 1394000000, storage_cost: 988000, storage_rebate: 978120, non_refundable_storage_fee: 9880 task 7, lines 71-73: //# run Test::M1::emit_event_with_size --args 256000 --gas-budget 100000000000000 --summarize events: 1 mutated: 1 -gas summary: computation_cost: 1814000000, storage_cost: 988000, storage_rebate: 978120, non_refundable_storage_fee: 9880 +gas summary: computation_cost: 1815000000, storage_cost: 988000, storage_rebate: 978120, non_refundable_storage_fee: 9880 task 8, lines 74-76: //# run Test::M1::emit_event_with_size --args 256001 --gas-budget 100000000000000 --summarize diff --git a/crates/sui-adapter-transactional-tests/tests/size_limits/move_object_size_limit.exp b/crates/sui-adapter-transactional-tests/tests/size_limits/move_object_size_limit.exp index dfa9be123b60f..7767e2f318584 100644 --- a/crates/sui-adapter-transactional-tests/tests/size_limits/move_object_size_limit.exp +++ b/crates/sui-adapter-transactional-tests/tests/size_limits/move_object_size_limit.exp @@ -18,10 +18,10 @@ task 3, lines 82-84: //# run Test::M1::transfer_object_with_size --args 255999 --sender A --gas-budget 100000000000000 created: object(3,0) mutated: object(0,0) -gas summary: computation_cost: 1863000000, storage_cost: 1947553200, storage_rebate: 978120, non_refundable_storage_fee: 9880 +gas summary: computation_cost: 1864000000, storage_cost: 1947553200, storage_rebate: 978120, non_refundable_storage_fee: 9880 task 4, line 85: //# run Test::M1::transfer_object_with_size --args 256000 --sender A --gas-budget 100000000000000 created: object(4,0) mutated: object(0,0) -gas summary: computation_cost: 1863000000, storage_cost: 1947560800, storage_rebate: 978120, non_refundable_storage_fee: 9880 +gas summary: computation_cost: 1864000000, storage_cost: 1947560800, storage_rebate: 978120, non_refundable_storage_fee: 9880 diff --git a/crates/sui-core/src/unit_tests/move_package_management_tests.rs b/crates/sui-core/src/unit_tests/move_package_management_tests.rs index e3c4dd8df6344..feb538ca0fb32 100644 --- a/crates/sui-core/src/unit_tests/move_package_management_tests.rs +++ b/crates/sui-core/src/unit_tests/move_package_management_tests.rs @@ -46,7 +46,7 @@ async fn test_manage_package_update() { .read_to_string(&mut lock_file_contents) .expect("Error reading Move.lock file"); - let expected = expect![[r#" + let expected = expect![[r##" # @generated by Move, please check-in and do not edit manually. [move] @@ -56,7 +56,7 @@ async fn test_manage_package_update() { [move.toolchain-version] compiler-version = "0.0.1" - edition = "legacy" + edition = "2024.beta" flavor = "sui" [env] @@ -66,6 +66,6 @@ async fn test_manage_package_update() { original-published-id = "0x000000000000000000000000000000000000000000000000000000000000000a" latest-published-id = "0x000000000000000000000000000000000000000000000000000000000000000b" published-version = "5" - "#]]; + "##]]; expected.assert_eq(lock_file_contents.as_str()); } diff --git a/crates/sui-move/src/unit_test.rs b/crates/sui-move/src/unit_test.rs index a5d0bf3b192ef..3bb4da1f2da0f 100644 --- a/crates/sui-move/src/unit_test.rs +++ b/crates/sui-move/src/unit_test.rs @@ -114,7 +114,10 @@ pub fn run_move_unit_tests( report_stacktrace_on_abort: true, ..config }, - sui_move_natives::all_natives(/* silent */ false), + sui_move_natives::all_natives( + /* silent */ false, + &ProtocolConfig::get_for_max_version_UNSAFE(), + ), Some(initial_cost_schedule_for_unit_tests()), compute_coverage, &mut std::io::stdout(), diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index 8a50f8f5a8cf5..438c4b2332f02 100644 --- a/crates/sui-open-rpc/spec/openrpc.json +++ b/crates/sui-open-rpc/spec/openrpc.json @@ -1368,6 +1368,9 @@ "base_tx_cost_per_byte": { "u64": "0" }, + "bcs_failure_cost": null, + "bcs_legacy_min_output_size_cost": null, + "bcs_per_byte_serialized_cost": null, "binary_address_identifiers": null, "binary_constant_pool": null, "binary_enum_def_instantiations": null, @@ -1419,6 +1422,8 @@ "crypto_invalid_arguments_cost": { "u64": "100" }, + "debug_print_base_cost": null, + "debug_print_stack_trace_base_cost": null, "dynamic_field_add_child_object_cost_base": { "u64": "100" }, @@ -1652,6 +1657,12 @@ "hash_keccak256_data_cost_per_byte": { "u64": "2" }, + "hash_sha2_256_base_cost": null, + "hash_sha2_256_legacy_min_input_len_cost": null, + "hash_sha2_256_per_byte_cost": null, + "hash_sha3_256_base_cost": null, + "hash_sha3_256_legacy_min_input_len_cost": null, + "hash_sha3_256_per_byte_cost": null, "hmac_hmac_sha3_256_cost_base": { "u64": "52" }, @@ -1875,6 +1886,14 @@ "storage_rebate_rate": { "u64": "9900" }, + "string_check_utf8_base_cost": null, + "string_check_utf8_per_byte_cost": null, + "string_index_of_base_cost": null, + "string_index_of_per_byte_pattern_cost": null, + "string_index_of_per_byte_searched_cost": null, + "string_is_char_boundary_base_cost": null, + "string_sub_string_base_cost": null, + "string_sub_string_per_byte_cost": null, "transfer_freeze_object_cost_base": { "u64": "52" }, @@ -1888,6 +1907,8 @@ "tx_context_derive_id_cost_base": { "u64": "52" }, + "type_name_get_base_cost": null, + "type_name_get_per_byte_cost": null, "types_is_one_time_witness_cost_base": { "u64": "52" }, @@ -1904,7 +1925,15 @@ "u64": "2" }, "vdf_hash_to_input_cost": null, - "vdf_verify_vdf_cost": null + "vdf_verify_vdf_cost": null, + "vector_borrow_base_cost": null, + "vector_destroy_empty_base_cost": null, + "vector_empty_base_cost": null, + "vector_length_base_cost": null, + "vector_pop_back_base_cost": null, + "vector_push_back_base_cost": null, + "vector_push_back_legacy_per_abstract_memory_unit_cost": null, + "vector_swap_base_cost": null } } } diff --git a/crates/sui-protocol-config/src/lib.rs b/crates/sui-protocol-config/src/lib.rs index 0a630e7a87b44..0a1ddec049c8b 100644 --- a/crates/sui-protocol-config/src/lib.rs +++ b/crates/sui-protocol-config/src/lib.rs @@ -164,6 +164,7 @@ const MAX_PROTOCOL_VERSION: u64 = 53; // Version 53: Add feature flag to decide whether to attempt to finalize bridge committee // Enable consensus commit prologue V3 on testnet. // Turn on shared object congestion control in testnet. +// Update stdlib natives costs #[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct ProtocolVersion(u64); @@ -1101,6 +1102,40 @@ pub struct ProtocolConfig { vdf_verify_vdf_cost: Option, vdf_hash_to_input_cost: Option, + // Stdlib costs + bcs_per_byte_serialized_cost: Option, + bcs_legacy_min_output_size_cost: Option, + bcs_failure_cost: Option, + + hash_sha2_256_base_cost: Option, + hash_sha2_256_per_byte_cost: Option, + hash_sha2_256_legacy_min_input_len_cost: Option, + hash_sha3_256_base_cost: Option, + hash_sha3_256_per_byte_cost: Option, + hash_sha3_256_legacy_min_input_len_cost: Option, + type_name_get_base_cost: Option, + type_name_get_per_byte_cost: Option, + + string_check_utf8_base_cost: Option, + string_check_utf8_per_byte_cost: Option, + string_is_char_boundary_base_cost: Option, + string_sub_string_base_cost: Option, + string_sub_string_per_byte_cost: Option, + string_index_of_base_cost: Option, + string_index_of_per_byte_pattern_cost: Option, + string_index_of_per_byte_searched_cost: Option, + + vector_empty_base_cost: Option, + vector_length_base_cost: Option, + vector_push_back_base_cost: Option, + vector_push_back_legacy_per_abstract_memory_unit_cost: Option, + vector_borrow_base_cost: Option, + vector_pop_back_base_cost: Option, + vector_destroy_empty_base_cost: Option, + vector_swap_base_cost: Option, + debug_print_base_cost: Option, + debug_print_stack_trace_base_cost: Option, + // ==== Ephemeral (consensus only) params deleted ==== // // Const params for consensus scoring decision @@ -1909,6 +1944,36 @@ impl ProtocolConfig { vdf_verify_vdf_cost: None, vdf_hash_to_input_cost: None, + bcs_per_byte_serialized_cost: None, + bcs_legacy_min_output_size_cost: None, + bcs_failure_cost: None, + hash_sha2_256_base_cost: None, + hash_sha2_256_per_byte_cost: None, + hash_sha2_256_legacy_min_input_len_cost: None, + hash_sha3_256_base_cost: None, + hash_sha3_256_per_byte_cost: None, + hash_sha3_256_legacy_min_input_len_cost: None, + type_name_get_base_cost: None, + type_name_get_per_byte_cost: None, + string_check_utf8_base_cost: None, + string_check_utf8_per_byte_cost: None, + string_is_char_boundary_base_cost: None, + string_sub_string_base_cost: None, + string_sub_string_per_byte_cost: None, + string_index_of_base_cost: None, + string_index_of_per_byte_pattern_cost: None, + string_index_of_per_byte_searched_cost: None, + vector_empty_base_cost: None, + vector_length_base_cost: None, + vector_push_back_base_cost: None, + vector_push_back_legacy_per_abstract_memory_unit_cost: None, + vector_borrow_base_cost: None, + vector_pop_back_base_cost: None, + vector_destroy_empty_base_cost: None, + vector_swap_base_cost: None, + debug_print_base_cost: None, + debug_print_stack_trace_base_cost: None, + max_size_written_objects: None, max_size_written_objects_system_tx: None, @@ -2536,6 +2601,37 @@ impl ProtocolConfig { cfg.feature_flags.per_object_congestion_control_mode = PerObjectCongestionControlMode::TotalTxCount; } + + // Adjust stdlib gas costs + cfg.bcs_per_byte_serialized_cost = Some(2); + cfg.bcs_legacy_min_output_size_cost = Some(1); + cfg.bcs_failure_cost = Some(52); + cfg.debug_print_base_cost = Some(52); + cfg.debug_print_stack_trace_base_cost = Some(52); + cfg.hash_sha2_256_base_cost = Some(52); + cfg.hash_sha2_256_per_byte_cost = Some(2); + cfg.hash_sha2_256_legacy_min_input_len_cost = Some(1); + cfg.hash_sha3_256_base_cost = Some(52); + cfg.hash_sha3_256_per_byte_cost = Some(2); + cfg.hash_sha3_256_legacy_min_input_len_cost = Some(1); + cfg.type_name_get_base_cost = Some(52); + cfg.type_name_get_per_byte_cost = Some(2); + cfg.string_check_utf8_base_cost = Some(52); + cfg.string_check_utf8_per_byte_cost = Some(2); + cfg.string_is_char_boundary_base_cost = Some(52); + cfg.string_sub_string_base_cost = Some(52); + cfg.string_sub_string_per_byte_cost = Some(2); + cfg.string_index_of_base_cost = Some(52); + cfg.string_index_of_per_byte_pattern_cost = Some(2); + cfg.string_index_of_per_byte_searched_cost = Some(2); + cfg.vector_empty_base_cost = Some(52); + cfg.vector_length_base_cost = Some(52); + cfg.vector_push_back_base_cost = Some(52); + cfg.vector_push_back_legacy_per_abstract_memory_unit_cost = Some(2); + cfg.vector_borrow_base_cost = Some(52); + cfg.vector_pop_back_base_cost = Some(52); + cfg.vector_destroy_empty_base_cost = Some(52); + cfg.vector_swap_base_cost = Some(52); } // Use this template when making changes: // diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_53.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_53.snap index 2761b97f0f926..5317182e71a72 100644 --- a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_53.snap +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_53.snap @@ -268,6 +268,35 @@ 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 diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_53.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_53.snap index 0419b3548b467..671eb71756050 100644 --- a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_53.snap +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_53.snap @@ -272,6 +272,35 @@ 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 diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_53.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_53.snap index 095c918715c91..b686158e23286 100644 --- a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_53.snap +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_53.snap @@ -281,6 +281,35 @@ 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 @@ -299,3 +328,4 @@ 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: 10 + diff --git a/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/reference_safety/imm_borrow_loc.mvir b/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/reference_safety/imm_borrow_loc.mvir index ba1630fa4c4be..1e66a269fd30c 100644 --- a/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/reference_safety/imm_borrow_loc.mvir +++ b/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/reference_safety/imm_borrow_loc.mvir @@ -1,6 +1,5 @@ //# publish module 0x1.Tester { - import 0x1.signer; struct Data has key { v1: u64, v2: u64 } struct Box has key { f: u64 } diff --git a/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/reference_safety/imm_borrow_loc_valid.mvir b/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/reference_safety/imm_borrow_loc_valid.mvir index c7bef57b0cf67..1721acce33997 100644 --- a/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/reference_safety/imm_borrow_loc_valid.mvir +++ b/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/reference_safety/imm_borrow_loc_valid.mvir @@ -1,6 +1,5 @@ //# publish module 0x1.Tester { - import 0x1.signer; struct Data has key { v1: u64, v2: u64 } struct Box has key { f: u64 } diff --git a/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/reference_safety/imm_borrow_on_mut.mvir b/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/reference_safety/imm_borrow_on_mut.mvir index a3e85b9b6bb64..17f91c9d54e84 100644 --- a/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/reference_safety/imm_borrow_on_mut.mvir +++ b/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/reference_safety/imm_borrow_on_mut.mvir @@ -1,6 +1,5 @@ //# publish module 0x1.Tester { - import 0x1.signer; struct Initializer has key { x: u64, y: u64 } struct Point { x: u64, y: u64 } diff --git a/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/type_safety/cant_deref_resource.exp b/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/type_safety/cant_deref_resource.exp index b435375c82a0f..e121d67ab237c 100644 --- a/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/type_safety/cant_deref_resource.exp +++ b/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/type_safety/cant_deref_resource.exp @@ -1,21 +1,21 @@ processed 2 tasks -task 0, lines 1-42: +task 0, lines 1-39: //# publish Error: Unable to publish module '0000000000000000000000000000000000000000000000000000000000000001::Token'. Got VMError: { major_status: READREF_WITHOUT_COPY_ABILITY, sub_status: None, location: 0x1::Token, indices: [(FunctionDefinition, 4)], - offsets: [(FunctionDefinitionIndex(4), 13)], + offsets: [(FunctionDefinitionIndex(4), 10)], } -task 1, lines 44-85: +task 1, lines 41-79: //# publish Error: Unable to publish module '0000000000000000000000000000000000000000000000000000000000000002::Token'. Got VMError: { major_status: READREF_WITHOUT_COPY_ABILITY, sub_status: None, location: 0x2::Token, indices: [(FunctionDefinition, 4)], - offsets: [(FunctionDefinitionIndex(4), 13)], + offsets: [(FunctionDefinitionIndex(4), 10)], } diff --git a/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/type_safety/cant_deref_resource.mvir b/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/type_safety/cant_deref_resource.mvir index 92835730f53c6..dcd42a7d76848 100644 --- a/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/type_safety/cant_deref_resource.mvir +++ b/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/type_safety/cant_deref_resource.mvir @@ -1,6 +1,5 @@ //# publish module 0x1.Token { - import 0x1.signer; struct T has key {v: u64} @@ -19,23 +18,21 @@ module 0x1.Token { return move(res); } - public publish(account: &signer, t: Self.T) { + public publish(account: address, t: Self.T) { label b0: abort(0); } fake(addr: address): &mut Self.T { label b0: abort(0); } - public test(account: &signer) { - let addr: address; + public test(account: address) { let t: Self.T; let tref: &mut Self.T; let y: Self.T; label b0: - addr = signer.address_of(copy(account)); t = Self.new(0); Self.publish(copy(account), move(t)); - tref = Self.fake(move(addr)); + tref = Self.fake(move(account)); y = *move(tref); return; } @@ -43,7 +40,6 @@ module 0x1.Token { //# publish module 0x2.Token { - import 0x1.signer; enum T has key { V{v: u64}} @@ -62,23 +58,21 @@ module 0x2.Token { return move(res); } - public publish(account: &signer, t: Self.T) { + public publish(account: address, t: Self.T) { label b0: abort(0); } fake(addr: address): &mut Self.T { label b0: abort(0); } - public test(account: &signer) { - let addr: address; + public test(account: address) { let t: Self.T; let tref: &mut Self.T; let y: Self.T; label b0: - addr = signer.address_of(copy(account)); t = Self.new(0); Self.publish(copy(account), move(t)); - tref = Self.fake(move(addr)); + tref = Self.fake(move(account)); y = *move(tref); return; } diff --git a/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/type_safety/mut_call_from_get_resource.mvir b/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/type_safety/mut_call_from_get_resource.mvir index b953092719047..75bacb56d4435 100644 --- a/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/type_safety/mut_call_from_get_resource.mvir +++ b/external-crates/move/crates/bytecode-verifier-transactional-tests/tests/type_safety/mut_call_from_get_resource.mvir @@ -1,6 +1,5 @@ //# publish module 0x42.Token { - import 0x1.signer; struct T has key {balance: u64} @@ -28,16 +27,15 @@ module 0x42.Token { return; } - public publish(account: &signer, t: Self.T) { + public publish(account: address, t: Self.T) { label b0: abort(0); } fake(addr: address): &mut Self.T { label b0: abort(0); } - public test(account: &signer) { + public test(account: address) { let z: Self.T; - let addr1: address; let struct1: &mut Self.T; let imm_struct1: &Self.T; let struct1_original_balance: u64; @@ -46,9 +44,8 @@ module 0x42.Token { z = Self.new(0); Self.publish(copy(account), move(z)); - addr1 = signer.address_of(move(account)); // returns mut reference, test its usage - struct1 = Self.fake(copy(addr1)); + struct1 = Self.fake(copy(account)); imm_struct1 = freeze(copy(struct1)); struct1_original_balance = Self.value(move(imm_struct1)); @@ -66,7 +63,6 @@ module 0x42.Token { //# publish module 0x43.Token { - import 0x1.signer; enum T has key {V { balance: u64} } @@ -94,16 +90,15 @@ module 0x43.Token { return; } - public publish(account: &signer, t: Self.T) { + public publish(account: address, t: Self.T) { label b0: abort(0); } fake(addr: address): &mut Self.T { label b0: abort(0); } - public test(account: &signer) { + public test(account: address) { let z: Self.T; - let addr1: address; let struct1: &mut Self.T; let imm_struct1: &Self.T; let struct1_original_balance: u64; @@ -112,9 +107,8 @@ module 0x43.Token { z = Self.new(0); Self.publish(copy(account), move(z)); - addr1 = signer.address_of(move(account)); // returns mut reference, test its usage - struct1 = Self.fake(copy(addr1)); + struct1 = Self.fake(copy(account)); imm_struct1 = freeze(copy(struct1)); struct1_original_balance = Self.value(move(imm_struct1)); diff --git a/external-crates/move/crates/language-benchmarks/src/move_vm.rs b/external-crates/move/crates/language-benchmarks/src/move_vm.rs index 2927af76b9acc..68cdeca91dc0f 100644 --- a/external-crates/move/crates/language-benchmarks/src/move_vm.rs +++ b/external-crates/move/crates/language-benchmarks/src/move_vm.rs @@ -28,6 +28,7 @@ pub fn bench(c: &mut Criterion, fun: &str) { let move_vm = MoveVM::new(move_stdlib_natives::all_natives( AccountAddress::from_hex_literal("0x1").unwrap(), move_stdlib_natives::GasParameters::zeros(), + /* silent debug */ true, )) .unwrap(); execute(c, &move_vm, modules, fun); diff --git a/external-crates/move/crates/move-analyzer/tests/completion/Move.toml b/external-crates/move/crates/move-analyzer/tests/completion/Move.toml index c2c82541db7ae..cddaad6938c8e 100644 --- a/external-crates/move/crates/move-analyzer/tests/completion/Move.toml +++ b/external-crates/move/crates/move-analyzer/tests/completion/Move.toml @@ -1,6 +1,5 @@ [package] name = "Completion" -version = "0.0.1" edition = "2024.beta" [dependencies] diff --git a/external-crates/move/crates/move-analyzer/tests/dot_completion.exp b/external-crates/move/crates/move-analyzer/tests/dot_completion.exp index e0e9e954c6635..ad426b6f92568 100644 --- a/external-crates/move/crates/move-analyzer/tests/dot_completion.exp +++ b/external-crates/move/crates/move-analyzer/tests/dot_completion.exp @@ -52,6 +52,14 @@ Method 'test()' == other_mod_dot.move ======================================================== -- test 0 ------------------- use line: 4, use_col: 10 +Method 'all!()' + INSERT TEXT: 'all!(|${1}| ${2})' + TARGET : '(std::vector::all)' + TYPE : 'fun <$T>(&vector<$T>, |&$T| -> bool): bool' +Method 'any!()' + INSERT TEXT: 'any!(|${1}| ${2})' + TARGET : '(std::vector::any)' + TYPE : 'fun <$T>(&vector<$T>, |&$T| -> bool): bool' Method 'append()' INSERT TEXT: 'append(${1:other})' TARGET : '(std::vector::append)' @@ -68,10 +76,42 @@ Method 'contains()' INSERT TEXT: 'contains(${1:e})' TARGET : '(std::vector::contains)' TYPE : 'fun (&vector, &Element): bool' +Method 'count!()' + INSERT TEXT: 'count!(|${1}| ${2})' + TARGET : '(std::vector::count)' + TYPE : 'fun <$T>(&vector<$T>, |&$T| -> bool): u64' +Method 'destroy!()' + INSERT TEXT: 'destroy!(|${1}| ${2})' + TARGET : '(std::vector::destroy)' + TYPE : 'fun <$T>(vector<$T>, |$T| -> ())' Method 'destroy_empty()' INSERT TEXT: 'destroy_empty()' TARGET : '(std::vector::destroy_empty)' TYPE : 'fun (vector)' +Method 'do!()' + INSERT TEXT: 'do!(|${1}| ${2})' + TARGET : '(std::vector::do)' + TYPE : 'fun <$T>(vector<$T>, |$T| -> ())' +Method 'do_mut!()' + INSERT TEXT: 'do_mut!(|${1}| ${2})' + TARGET : '(std::vector::do_mut)' + TYPE : 'fun <$T>(&mut vector<$T>, |&mut $T| -> ())' +Method 'do_ref!()' + INSERT TEXT: 'do_ref!(|${1}| ${2})' + TARGET : '(std::vector::do_ref)' + TYPE : 'fun <$T>(&vector<$T>, |&$T| -> ())' +Method 'filter!()' + INSERT TEXT: 'filter!(|${1}| ${2})' + TARGET : '(std::vector::filter)' + TYPE : 'fun <$T>(vector<$T>, |&$T| -> bool): vector<$T>' +Method 'find_index!()' + INSERT TEXT: 'find_index!(|${1}| ${2})' + TARGET : '(std::vector::find_index)' + TYPE : 'fun <$T>(vector<$T>, |&$T| -> bool): Option' +Method 'fold!()' + INSERT TEXT: 'fold!(${1:init}, |${2}, ${3}| ${4})' + TARGET : '(std::vector::fold)' + TYPE : 'fun <$T, $Acc>(vector<$T>, $Acc, |$Acc, $T| -> $Acc): $Acc' Method 'index_of()' INSERT TEXT: 'index_of(${1:e})' TARGET : '(std::vector::index_of)' @@ -88,6 +128,18 @@ Method 'length()' INSERT TEXT: 'length()' TARGET : '(std::vector::length)' TYPE : 'fun (&vector): u64' +Method 'map!()' + INSERT TEXT: 'map!(|${1}| ${2})' + TARGET : '(std::vector::map)' + TYPE : 'fun <$T, $U>(vector<$T>, |$T| -> $U): vector<$U>' +Method 'map_ref!()' + INSERT TEXT: 'map_ref!(|${1}| ${2})' + TARGET : '(std::vector::map_ref)' + TYPE : 'fun <$T, $U>(&vector<$T>, |&$T| -> $U): vector<$U>' +Method 'partition!()' + INSERT TEXT: 'partition!(|${1}| ${2})' + TARGET : '(std::vector::partition)' + TYPE : 'fun <$T>(vector<$T>, |&$T| -> bool): (vector<$T>, vector<$T>)' Method 'pop_back()' INSERT TEXT: 'pop_back()' TARGET : '(std::vector::pop_back)' @@ -112,4 +164,44 @@ Method 'swap_remove()' INSERT TEXT: 'swap_remove(${1:i})' TARGET : '(std::vector::swap_remove)' TYPE : 'fun (&mut vector, u64): Element' +Method 'to_ascii_string()' + INSERT TEXT: 'to_ascii_string()' + TARGET : '(std::ascii::string)' + TYPE : 'fun (vector): String' +Method 'to_string()' + INSERT TEXT: 'to_string()' + TARGET : '(std::string::utf8)' + TYPE : 'fun (vector): String' +Method 'try_to_ascii_string()' + INSERT TEXT: 'try_to_ascii_string()' + TARGET : '(std::ascii::try_string)' + TYPE : 'fun (vector): Option' +Method 'try_to_string()' + INSERT TEXT: 'try_to_string()' + TARGET : '(std::string::try_utf8)' + TYPE : 'fun (vector): Option' +Method 'zip_do!()' + INSERT TEXT: 'zip_do!(${1:v2}, |${2}, ${3}| ${4})' + TARGET : '(std::vector::zip_do)' + TYPE : 'fun <$T1, $T2>(vector<$T1>, vector<$T2>, |$T1, $T2| -> ())' +Method 'zip_do_mut!()' + INSERT TEXT: 'zip_do_mut!(${1:v2}, |${2}, ${3}| ${4})' + TARGET : '(std::vector::zip_do_mut)' + TYPE : 'fun <$T1, $T2>(&mut vector<$T1>, &mut vector<$T2>, |&mut $T1, &mut $T2| -> ())' +Method 'zip_do_ref!()' + INSERT TEXT: 'zip_do_ref!(${1:v2}, |${2}, ${3}| ${4})' + TARGET : '(std::vector::zip_do_ref)' + TYPE : 'fun <$T1, $T2>(&vector<$T1>, &vector<$T2>, |&$T1, &$T2| -> ())' +Method 'zip_do_reverse!()' + INSERT TEXT: 'zip_do_reverse!(${1:v2}, |${2}, ${3}| ${4})' + TARGET : '(std::vector::zip_do_reverse)' + TYPE : 'fun <$T1, $T2>(vector<$T1>, vector<$T2>, |$T1, $T2| -> ())' +Method 'zip_map!()' + INSERT TEXT: 'zip_map!(${1:v2}, |${2}, ${3}| ${4})' + TARGET : '(std::vector::zip_map)' + TYPE : 'fun <$T1, $T2, $U>(vector<$T1>, vector<$T2>, |$T1, $T2| -> $U): vector<$U>' +Method 'zip_map_ref!()' + INSERT TEXT: 'zip_map_ref!(${1:v2}, |${2}, ${3}| ${4})' + TARGET : '(std::vector::zip_map_ref)' + TYPE : 'fun <$T1, $T2, $U>(&vector<$T1>, &vector<$T2>, |&$T1, &$T2| -> $U): vector<$U>' diff --git a/external-crates/move/crates/move-analyzer/tests/enums/Move.toml b/external-crates/move/crates/move-analyzer/tests/enums/Move.toml index f1a3c05aaa2cf..0fa60b5d2ecf3 100644 --- a/external-crates/move/crates/move-analyzer/tests/enums/Move.toml +++ b/external-crates/move/crates/move-analyzer/tests/enums/Move.toml @@ -1,6 +1,5 @@ [package] name = "Enums" -version = "0.0.1" edition = "2024.alpha" [dependencies] diff --git a/external-crates/move/crates/move-analyzer/tests/implicit_uses.exp b/external-crates/move/crates/move-analyzer/tests/implicit_uses.exp index 71a24a5239fe0..1ae18e4ecb1c0 100644 --- a/external-crates/move/crates/move-analyzer/tests/implicit_uses.exp +++ b/external-crates/move/crates/move-analyzer/tests/implicit_uses.exp @@ -2,8 +2,8 @@ -- test 0 ------------------- use line: 4, use_ndx: 1 Use: 'Option', start: 13, end: 19 -Def: 'Option', line: 6, def char: 11 -TypeDef: 'Option', line: 6, char: 11 +Def: 'Option', line: 7, def char: 18 +TypeDef: 'Option', line: 7, char: 18 On Hover: public struct std::option::Option has copy, drop, store { vec: vector @@ -16,7 +16,7 @@ zero or one because Move bytecode does not have ADTs. -- test 1 ------------------- use line: 8, use_ndx: 2 Use: 'option', start: 26, end: 32 -Def: 'option', line: 1, def char: 12 +Def: 'option', line: 4, def char: 12 TypeDef: no info On Hover: module std::option diff --git a/external-crates/move/crates/move-analyzer/tests/inlay-hints/Move.toml b/external-crates/move/crates/move-analyzer/tests/inlay-hints/Move.toml index f5aa6ee913e03..7920464fd5d5a 100644 --- a/external-crates/move/crates/move-analyzer/tests/inlay-hints/Move.toml +++ b/external-crates/move/crates/move-analyzer/tests/inlay-hints/Move.toml @@ -1,6 +1,5 @@ [package] name = "InlayHints" -version = "0.0.1" edition = "2024.beta" [dependencies] diff --git a/external-crates/move/crates/move-analyzer/tests/macros.exp b/external-crates/move/crates/move-analyzer/tests/macros.exp index 10f0e3341fc97..d0e0324404abf 100644 --- a/external-crates/move/crates/move-analyzer/tests/macros.exp +++ b/external-crates/move/crates/move-analyzer/tests/macros.exp @@ -34,29 +34,41 @@ macro fun Macros::fun_type::macro_fun() == macros.move ======================================================== -- test 0 ------------------- use line: 7, use_ndx: 0 +Use: 'n foo(', start: 12, end: 18 +Def: 'vector', line: 6, def char: 12 +TypeDef: no info +On Hover: +module std::vector + +A variable-sized container that can hold any type. Indexing is 0-based, and +vectors are growable. This module has many native functions. + + +-- test 1 ------------------- +use line: 7, use_ndx: 1 Use: 'foo', start: 14, end: 17 Def: 'foo', line: 6, def char: 14 TypeDef: no info On Hover: macro fun Macros::macros::foo($i: u64, $body: |u64| -> u64): u64 --- test 1 ------------------- -use line: 7, use_ndx: 1 +-- test 2 ------------------- +use line: 7, use_ndx: 2 Use: '$i', start: 18, end: 20 Def: '$i', line: 6, def char: 18 TypeDef: no info On Hover: $i: u64 --- test 2 ------------------- -use line: 7, use_ndx: 2 +-- test 3 ------------------- +use line: 7, use_ndx: 3 Use: '$body', start: 27, end: 32 Def: '$body', line: 6, def char: 27 TypeDef: no info On Hover: $body: |u64| -> u64 --- test 3 ------------------- +-- test 4 ------------------- use line: 15, use_ndx: 0 Use: 'bar', start: 14, end: 17 Def: 'bar', line: 14, def char: 14 @@ -64,7 +76,7 @@ TypeDef: no info On Hover: macro fun Macros::macros::bar($i: Macros::macros::SomeStruct, $body: |Macros::macros::SomeStruct| -> Macros::macros::SomeStruct): Macros::macros::SomeStruct --- test 4 ------------------- +-- test 5 ------------------- use line: 15, use_ndx: 1 Use: '$i', start: 18, end: 20 Def: '$i', line: 14, def char: 18 @@ -72,7 +84,7 @@ TypeDef: 'SomeStruct', line: 2, char: 18 On Hover: $i: Macros::macros::SomeStruct --- test 5 ------------------- +-- test 6 ------------------- use line: 15, use_ndx: 2 Use: 'SomeStruct', start: 22, end: 32 Def: 'SomeStruct', line: 2, def char: 18 @@ -82,7 +94,7 @@ public struct Macros::macros::SomeStruct has drop { some_field: u64 } --- test 6 ------------------- +-- test 7 ------------------- use line: 15, use_ndx: 3 Use: '$body', start: 34, end: 39 Def: '$body', line: 14, def char: 34 @@ -90,7 +102,7 @@ TypeDef: no info On Hover: $body: |Macros::macros::SomeStruct| -> Macros::macros::SomeStruct --- test 7 ------------------- +-- test 8 ------------------- use line: 15, use_ndx: 4 Use: 'SomeStruct', start: 42, end: 52 Def: 'SomeStruct', line: 2, def char: 18 @@ -100,7 +112,7 @@ public struct Macros::macros::SomeStruct has drop { some_field: u64 } --- test 8 ------------------- +-- test 9 ------------------- use line: 15, use_ndx: 5 Use: 'SomeStruct', start: 57, end: 67 Def: 'SomeStruct', line: 2, def char: 18 @@ -110,7 +122,7 @@ public struct Macros::macros::SomeStruct has drop { some_field: u64 } --- test 9 ------------------- +-- test 10 ------------------- use line: 15, use_ndx: 6 Use: 'SomeStruct', start: 70, end: 80 Def: 'SomeStruct', line: 2, def char: 18 @@ -120,7 +132,7 @@ public struct Macros::macros::SomeStruct has drop { some_field: u64 } --- test 10 ------------------- +-- test 11 ------------------- use line: 19, use_ndx: 0 Use: 'for_each', start: 14, end: 22 Def: 'for_each', line: 18, def char: 14 @@ -128,7 +140,7 @@ TypeDef: no info On Hover: macro fun Macros::macros::for_each<$T>($v: &vector<$T>, $body: |&$T| -> ()) --- test 11 ------------------- +-- test 12 ------------------- use line: 19, use_ndx: 1 Use: '$T', start: 23, end: 25 Def: '$T', line: 18, def char: 23 @@ -136,7 +148,7 @@ TypeDef: no info On Hover: $T --- test 12 ------------------- +-- test 13 ------------------- use line: 19, use_ndx: 2 Use: '$v', start: 27, end: 29 Def: '$v', line: 18, def char: 27 @@ -144,7 +156,7 @@ TypeDef: no info On Hover: let $v: &vector --- test 13 ------------------- +-- test 14 ------------------- use line: 19, use_ndx: 3 Use: '$T', start: 39, end: 41 Def: '$T', line: 18, def char: 23 @@ -152,7 +164,7 @@ TypeDef: no info On Hover: $T --- test 14 ------------------- +-- test 15 ------------------- use line: 19, use_ndx: 4 Use: '$body', start: 44, end: 49 Def: '$body', line: 18, def char: 44 @@ -160,7 +172,7 @@ TypeDef: no info On Hover: $body: |&$T| -> () --- test 15 ------------------- +-- test 16 ------------------- use line: 19, use_ndx: 5 Use: '$T', start: 53, end: 55 Def: '$T', line: 18, def char: 23 @@ -168,7 +180,7 @@ TypeDef: no info On Hover: $T --- test 16 ------------------- +-- test 17 ------------------- use line: 33, use_ndx: 0 Use: 'macros', start: 16, end: 22 Def: 'macros', line: 0, def char: 15 @@ -176,7 +188,7 @@ TypeDef: no info On Hover: module Macros::macros --- test 17 ------------------- +-- test 18 ------------------- use line: 33, use_ndx: 1 Use: 'foo', start: 24, end: 27 Def: 'foo', line: 6, def char: 14 @@ -184,7 +196,7 @@ TypeDef: no info On Hover: macro fun Macros::macros::foo($i: u64, $body: |u64| -> u64): u64 --- test 18 ------------------- +-- test 19 ------------------- use line: 33, use_ndx: 2 Use: 'p', start: 29, end: 30 Def: 'p', line: 31, def char: 12 @@ -192,7 +204,7 @@ TypeDef: no info On Hover: let p: u64 --- test 19 ------------------- +-- test 20 ------------------- use line: 33, use_ndx: 3 Use: 'x', start: 33, end: 34 Def: 'x', line: 32, def char: 33 @@ -200,7 +212,7 @@ TypeDef: no info On Hover: let x: u64 --- test 20 ------------------- +-- test 21 ------------------- use line: 33, use_ndx: 4 Use: 'x', start: 36, end: 37 Def: 'x', line: 32, def char: 33 @@ -208,7 +220,7 @@ TypeDef: no info On Hover: let x: u64 --- test 21 ------------------- +-- test 22 ------------------- use line: 38, use_ndx: 5 Use: 'y', start: 49, end: 50 Def: 'y', line: 37, def char: 49 @@ -216,7 +228,7 @@ TypeDef: no info On Hover: let y: u64 --- test 22 ------------------- +-- test 23 ------------------- use line: 38, use_ndx: 7 Use: 'foo', start: 68, end: 71 Def: 'foo', line: 6, def char: 14 @@ -224,7 +236,7 @@ TypeDef: no info On Hover: macro fun Macros::macros::foo($i: u64, $body: |u64| -> u64): u64 --- test 23 ------------------- +-- test 24 ------------------- use line: 38, use_ndx: 8 Use: 'y', start: 73, end: 74 Def: 'y', line: 37, def char: 49 @@ -232,7 +244,7 @@ TypeDef: no info On Hover: let y: u64 --- test 24 ------------------- +-- test 25 ------------------- use line: 38, use_ndx: 9 Use: 'z', start: 77, end: 78 Def: 'z', line: 37, def char: 77 @@ -240,7 +252,7 @@ TypeDef: no info On Hover: let z: u64 --- test 25 ------------------- +-- test 26 ------------------- use line: 38, use_ndx: 10 Use: 'z', start: 80, end: 81 Def: 'z', line: 37, def char: 77 @@ -248,7 +260,7 @@ TypeDef: no info On Hover: let z: u64 --- test 26 ------------------- +-- test 27 ------------------- use line: 44, use_ndx: 4 Use: 'sum', start: 48, end: 51 Def: 'sum', line: 42, def char: 16 @@ -256,7 +268,7 @@ TypeDef: no info On Hover: let mut sum: u64 --- test 27 ------------------- +-- test 28 ------------------- use line: 45, use_ndx: 0 Use: 'es', start: 8, end: 10 Def: 'es', line: 41, def char: 12 @@ -264,7 +276,7 @@ TypeDef: no info On Hover: let es: vector --- test 28 ------------------- +-- test 29 ------------------- use line: 45, use_ndx: 1 Use: 'feach', start: 11, end: 16 Def: 'for_each', line: 18, def char: 14 @@ -272,7 +284,7 @@ TypeDef: no info On Hover: macro fun Macros::macros::for_each<$T>($v: &vector<$T>, $body: |&$T| -> ()) --- test 29 ------------------- +-- test 30 ------------------- use line: 52, use_ndx: 2 Use: 'SomeStruct', start: 34, end: 44 Def: 'SomeStruct', line: 2, def char: 18 diff --git a/external-crates/move/crates/move-analyzer/tests/macros.ide b/external-crates/move/crates/move-analyzer/tests/macros.ide index 76471b5f246f1..2a2dc12a448e5 100644 --- a/external-crates/move/crates/move-analyzer/tests/macros.ide +++ b/external-crates/move/crates/move-analyzer/tests/macros.ide @@ -35,6 +35,10 @@ "use_line": 7, "use_ndx": 2 }, + { + "use_line": 7, + "use_ndx": 3 + }, { "use_line": 15, "use_ndx": 0 @@ -146,4 +150,4 @@ ] } } -} \ No newline at end of file +} diff --git a/external-crates/move/crates/move-analyzer/tests/mod-ident-uniform/Move.toml b/external-crates/move/crates/move-analyzer/tests/mod-ident-uniform/Move.toml index 4d4ae972dec2a..4c87e222272d8 100644 --- a/external-crates/move/crates/move-analyzer/tests/mod-ident-uniform/Move.toml +++ b/external-crates/move/crates/move-analyzer/tests/mod-ident-uniform/Move.toml @@ -1,6 +1,6 @@ [package] name = "ModIdentUniform" -version = "0.0.1" +edition = "legacy" [dependencies] MoveStdlib = { local = "../../../move-stdlib/", addr_subst = { "std" = "0x1" } } diff --git a/external-crates/move/crates/move-analyzer/tests/move-2024/Move.toml b/external-crates/move/crates/move-analyzer/tests/move-2024/Move.toml index 6e8e5c1001331..0d679d0771239 100644 --- a/external-crates/move/crates/move-analyzer/tests/move-2024/Move.toml +++ b/external-crates/move/crates/move-analyzer/tests/move-2024/Move.toml @@ -1,6 +1,5 @@ [package] name = "Move2024" -version = "0.0.1" edition = "2024.beta" [dependencies] diff --git a/external-crates/move/crates/move-analyzer/tests/parse-error-dep/Move.toml b/external-crates/move/crates/move-analyzer/tests/parse-error-dep/Move.toml index bd210e0806f65..1149112ed88b1 100644 --- a/external-crates/move/crates/move-analyzer/tests/parse-error-dep/Move.toml +++ b/external-crates/move/crates/move-analyzer/tests/parse-error-dep/Move.toml @@ -1,6 +1,6 @@ [package] name = "ParseErrorDep" -version = "0.0.1" +edition = "legacy" [dependencies] MoveStdlib = { local = "../../../move-stdlib/", addr_subst = { "std" = "0x1" } } diff --git a/external-crates/move/crates/move-analyzer/tests/parse-error/Move.toml b/external-crates/move/crates/move-analyzer/tests/parse-error/Move.toml index d0dd5b9d61241..a27b40aaebcb9 100644 --- a/external-crates/move/crates/move-analyzer/tests/parse-error/Move.toml +++ b/external-crates/move/crates/move-analyzer/tests/parse-error/Move.toml @@ -1,6 +1,6 @@ [package] name = "ParseError" -version = "0.0.1" +edition = "legacy" [dependencies] MoveStdlib = { local = "../../../move-stdlib/", addr_subst = { "std" = "0x1" } } diff --git a/external-crates/move/crates/move-analyzer/tests/partial-dot/Move.toml b/external-crates/move/crates/move-analyzer/tests/partial-dot/Move.toml index 66aeb5dcdcfa7..da0955faa0d8e 100644 --- a/external-crates/move/crates/move-analyzer/tests/partial-dot/Move.toml +++ b/external-crates/move/crates/move-analyzer/tests/partial-dot/Move.toml @@ -1,6 +1,6 @@ [package] name = "PartialDot" -version = "0.0.1" +edition = "legacy" [dependencies] MoveStdlib = { local = "../../../move-stdlib/", addr_subst = { "std" = "0x1" } } diff --git a/external-crates/move/crates/move-analyzer/tests/partial-function/Move.toml b/external-crates/move/crates/move-analyzer/tests/partial-function/Move.toml index 917b1f61cf461..b9876d64b3a67 100644 --- a/external-crates/move/crates/move-analyzer/tests/partial-function/Move.toml +++ b/external-crates/move/crates/move-analyzer/tests/partial-function/Move.toml @@ -1,6 +1,6 @@ [package] name = "PartialFunction" -version = "0.0.1" +edition = "legacy" [dependencies] MoveStdlib = { local = "../../../move-stdlib/", addr_subst = { "std" = "0x1" } } diff --git a/external-crates/move/crates/move-analyzer/tests/pkg-naming-error/Move.toml b/external-crates/move/crates/move-analyzer/tests/pkg-naming-error/Move.toml index f8feb6145014d..9463d76e478b3 100644 --- a/external-crates/move/crates/move-analyzer/tests/pkg-naming-error/Move.toml +++ b/external-crates/move/crates/move-analyzer/tests/pkg-naming-error/Move.toml @@ -1,6 +1,6 @@ [package] name = "PkgNamingError" -version = "0.0.1" +edition = "legacy" [dependencies] MoveStdlib = { local = "../../../move-stdlib/", addr_subst = { "std" = "0x1" } } diff --git a/external-crates/move/crates/move-analyzer/tests/pre-type-error-dep/Move.toml b/external-crates/move/crates/move-analyzer/tests/pre-type-error-dep/Move.toml index 7b2f8dce30660..b9ebf9b55b5bf 100644 --- a/external-crates/move/crates/move-analyzer/tests/pre-type-error-dep/Move.toml +++ b/external-crates/move/crates/move-analyzer/tests/pre-type-error-dep/Move.toml @@ -1,6 +1,6 @@ [package] name = "PreTypeErrorDep" -version = "0.0.1" +edition = "legacy" [dependencies] MoveStdlib = { local = "../../../move-stdlib/", addr_subst = { "std" = "0x1" } } diff --git a/external-crates/move/crates/move-analyzer/tests/pre-type-error/Move.toml b/external-crates/move/crates/move-analyzer/tests/pre-type-error/Move.toml index 8ff3b2be186ee..05bd4a4a6a41d 100644 --- a/external-crates/move/crates/move-analyzer/tests/pre-type-error/Move.toml +++ b/external-crates/move/crates/move-analyzer/tests/pre-type-error/Move.toml @@ -1,6 +1,6 @@ [package] name = "PreTypeError" -version = "0.0.1" +edition = "legacy" [addresses] PreTypeError = "0xCAFE" diff --git a/external-crates/move/crates/move-analyzer/tests/symbols/Move.toml b/external-crates/move/crates/move-analyzer/tests/symbols/Move.toml index ddd0a6d3582c2..858e952648a42 100644 --- a/external-crates/move/crates/move-analyzer/tests/symbols/Move.toml +++ b/external-crates/move/crates/move-analyzer/tests/symbols/Move.toml @@ -1,6 +1,6 @@ [package] name = "Symbols" -version = "0.0.1" +edition = "legacy" [dependencies] MoveStdlib = { local = "../../../move-stdlib/", addr_subst = { "std" = "0x1" } } diff --git a/external-crates/move/crates/move-cli/src/main.rs b/external-crates/move/crates/move-cli/src/main.rs index acbbbe6d23a8d..11eb3d0699814 100644 --- a/external-crates/move/crates/move-cli/src/main.rs +++ b/external-crates/move/crates/move-cli/src/main.rs @@ -4,19 +4,12 @@ use anyhow::Result; use move_core_types::account_address::AccountAddress; -use move_stdlib_natives::{all_natives, nursery_natives, GasParameters, NurseryGasParameters}; +use move_stdlib_natives::{all_natives, GasParameters}; fn main() -> Result<()> { let cost_table = &move_vm_test_utils::gas_schedule::INITIAL_COST_SCHEDULE; let addr = AccountAddress::from_hex_literal("0x1").unwrap(); - let natives = all_natives(addr, GasParameters::zeros()) - .into_iter() - .chain(nursery_natives( - /* silent */ false, - addr, - NurseryGasParameters::zeros(), - )) - .collect(); + let natives = all_natives(addr, GasParameters::zeros(), /* silent */ false); move_cli::move_cli(natives, cost_table) } diff --git a/external-crates/move/crates/move-cli/tests/build_tests/build_with_bytecode/B/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/build_with_bytecode/B/Move.toml index a6ad126c76313..cd03254de0669 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/build_with_bytecode/B/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/build_with_bytecode/B/Move.toml @@ -1,5 +1,6 @@ [package] name = "Bar" +edition = "2024.beta" [addresses] B = "0x2" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/build_with_bytecode/C/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/build_with_bytecode/C/Move.toml index db95e4a84a73b..6f2a8b5a3aa38 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/build_with_bytecode/C/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/build_with_bytecode/C/Move.toml @@ -1,5 +1,6 @@ [package] name = "Foo" +edition = "2024.beta" [addresses] C = "0x3" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/build_with_bytecode/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/build_with_bytecode/Move.toml index 3b41905ce8d07..959dd1966ec1f 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/build_with_bytecode/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/build_with_bytecode/Move.toml @@ -1,5 +1,6 @@ [package] name = "A" +edition = "2024.beta" [addresses] A = "0x2" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/build_with_dep_warnings/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/build_with_dep_warnings/Move.toml index 18ae248f3b8c4..92679a7fff084 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/build_with_dep_warnings/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/build_with_dep_warnings/Move.toml @@ -1,5 +1,6 @@ [package] name = "Test" +edition = "2024.beta" [dependencies] SomeDep = { local = "dep" } diff --git a/external-crates/move/crates/move-cli/tests/build_tests/build_with_dep_warnings/dep/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/build_with_dep_warnings/dep/Move.toml index a03576b363c0b..2d3274e227456 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/build_with_dep_warnings/dep/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/build_with_dep_warnings/dep/Move.toml @@ -1,2 +1,3 @@ [package] name = "SomeDep" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/build_with_warnings/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/build_with_warnings/Move.toml index cb7453ae43828..c8f2665caf8cb 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/build_with_warnings/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/build_with_warnings/Move.toml @@ -1,2 +1,3 @@ [package] name = "Test" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/canonicalize_module/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/canonicalize_module/Move.toml index 799d21da627c2..eb51a49a36279 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/canonicalize_module/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/canonicalize_module/Move.toml @@ -1,5 +1,6 @@ [package] name = "Test" +edition = "2024.beta" [addresses] foo = "0x1" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/canonicalize_module/sources/m.move b/external-crates/move/crates/move-cli/tests/build_tests/canonicalize_module/sources/m.move index 0947c77107c1c..f055c1d054ec4 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/canonicalize_module/sources/m.move +++ b/external-crates/move/crates/move-cli/tests/build_tests/canonicalize_module/sources/m.move @@ -12,9 +12,9 @@ module bar::b { module bar::c { #[allow(unused_field)] - struct B { x: u64 } + public struct B { x: u64 } #[allow(unused_field)] - struct A { b: vector } + public struct A { b: vector } public fun g(): u64 { foo::a::f() + @@ -35,8 +35,8 @@ module baz::d { } module qux::e { - struct B has drop { x: u64 } - struct A has drop { x: u64 } + public struct B has drop { x: u64 } + public struct A has drop { x: u64 } public fun a(): A { A { x: 46 } diff --git a/external-crates/move/crates/move-cli/tests/build_tests/circular_dependencies/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/circular_dependencies/Move.toml index 6aaff66ce5b5a..d0203b9635df7 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/circular_dependencies/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/circular_dependencies/Move.toml @@ -1,5 +1,6 @@ [package] name = "Foo" +edition = "2024.beta" [dependencies] Bar = { local = "bar" } diff --git a/external-crates/move/crates/move-cli/tests/build_tests/circular_dependencies/bar/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/circular_dependencies/bar/Move.toml index 4dba0ffcdc0b3..765a6cfe26577 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/circular_dependencies/bar/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/circular_dependencies/bar/Move.toml @@ -1,5 +1,6 @@ [package] name = "Bar" +edition = "2024.beta" [dependencies] Foo = { local = ".." } diff --git a/external-crates/move/crates/move-cli/tests/build_tests/dependency_chain/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/dependency_chain/Move.toml index 9555addf395da..074036f200787 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/dependency_chain/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/dependency_chain/Move.toml @@ -1,5 +1,6 @@ [package] name = "A" +edition = "2024.beta" [addresses] A = "0x1" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/dependency_chain/bar/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/dependency_chain/bar/Move.toml index b1a9789ad67de..55f3cce59b4b1 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/dependency_chain/bar/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/dependency_chain/bar/Move.toml @@ -1,5 +1,6 @@ [package] name = "Bar" +edition = "2024.beta" [addresses] A = "_" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/dependency_chain/foo/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/dependency_chain/foo/Move.toml index 54db278827612..1d171931f5650 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/dependency_chain/foo/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/dependency_chain/foo/Move.toml @@ -1,5 +1,6 @@ [package] name = "Foo" +edition = "2024.beta" [addresses] A = "_" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/dev_address/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/dev_address/Move.toml index bf79537d599bd..1fe668353dcbf 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/dev_address/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/dev_address/Move.toml @@ -1,5 +1,6 @@ [package] name = "A" +edition = "2024.beta" [addresses] A = "_" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/disassemble_module/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/disassemble_module/Move.toml index cb7453ae43828..c8f2665caf8cb 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/disassemble_module/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/disassemble_module/Move.toml @@ -1,2 +1,3 @@ [package] name = "Test" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/disassemble_module/sources/m.move b/external-crates/move/crates/move-cli/tests/build_tests/disassemble_module/sources/m.move index 35af0da97addc..b8b8c95c001ae 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/disassemble_module/sources/m.move +++ b/external-crates/move/crates/move-cli/tests/build_tests/disassemble_module/sources/m.move @@ -1,7 +1,7 @@ -module 0x42::m { +module 0x42::m; -struct Zs {} -struct As {} +public struct Zs {} +public struct As {} const Zc: u64 = 1; const Ac: u32 = 0; @@ -12,4 +12,3 @@ public fun zf(): u64 { Zc } public fun af(): u32 { Ac } public fun sf(): vector { AString } public fun nf(): vector { NotAString } -} diff --git a/external-crates/move/crates/move-cli/tests/build_tests/empty_module_no_deps/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/empty_module_no_deps/Move.toml index 1cf5dd8272d40..c3f61cd7e71ad 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/empty_module_no_deps/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/empty_module_no_deps/Move.toml @@ -1,2 +1,3 @@ [package] name = "A" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/include_exclude_stdlib/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/include_exclude_stdlib/Move.toml index 4fe19dd013e56..6e3fa396834c9 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/include_exclude_stdlib/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/include_exclude_stdlib/Move.toml @@ -1,5 +1,6 @@ [package] name = "build_include_exclude_stdlib" +edition = "2024.beta" [addresses] std = "0x1" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/include_exclude_stdlib/args.exp b/external-crates/move/crates/move-cli/tests/build_tests/include_exclude_stdlib/args.exp index 5620f9e5d8cf6..2089ac58027a6 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/include_exclude_stdlib/args.exp +++ b/external-crates/move/crates/move-cli/tests/build_tests/include_exclude_stdlib/args.exp @@ -1,24 +1,16 @@ Command `build -v`: BUILDING build_include_exclude_stdlib error[E03002]: unbound module - ┌─ ./sources/UseSigner.move:2:7 + ┌─ ./sources/UseSigner.move:2:9 │ -2 │ use std::signer; - │ ^^^^^^^^^^^ Invalid 'use'. Unbound module: 'std::signer' +2 │ use std::address; + │ ^^^^^^^^^^^^ Invalid 'use'. Unbound module: 'std::address' -warning[W09002]: unused variable - ┌─ ./sources/UseSigner.move:4:16 +error[E03006]: unexpected name in this position + ┌─ ./sources/UseSigner.move:5:9 │ -4 │ public fun f(account: &signer): address { - │ ^^^^^^^ Unused parameter 'account'. Consider removing or prefixing with an underscore: '_account' - │ - = This warning can be suppressed with '#[allow(unused_variable)]' applied to the 'module' or module member ('const', 'fun', or 'struct') - -error[E03002]: unbound module - ┌─ ./sources/UseSigner.move:5:5 - │ -5 │ signer::address_of(account) - │ ^^^^^^ Unbound module alias 'signer' +5 │ address::length() + │ ^^^^^^^ Could not resolve the name 'address' Command `-d -v build`: INCLUDING DEPENDENCY MoveStdlib diff --git a/external-crates/move/crates/move-cli/tests/build_tests/include_exclude_stdlib/sources/UseSigner.move b/external-crates/move/crates/move-cli/tests/build_tests/include_exclude_stdlib/sources/UseSigner.move index fdec0d78b1d5b..471e9ac62e941 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/include_exclude_stdlib/sources/UseSigner.move +++ b/external-crates/move/crates/move-cli/tests/build_tests/include_exclude_stdlib/sources/UseSigner.move @@ -1,7 +1,7 @@ module 0x1::Example { - use std::signer; + use std::address; - public fun f(account: &signer): address { - signer::address_of(account) - } + public fun f(): u64 { + address::length() + } } diff --git a/external-crates/move/crates/move-cli/tests/build_tests/json_errors/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/json_errors/Move.toml index cb7453ae43828..c8f2665caf8cb 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/json_errors/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/json_errors/Move.toml @@ -1,2 +1,3 @@ [package] name = "Test" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/migration/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/migration/Move.toml index a1c46ce0fd4e6..dba667925d24a 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/migration/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/migration/Move.toml @@ -3,6 +3,7 @@ [package] name = "A" +edition = "2024.beta" # this is a comment [addresses] diff --git a/external-crates/move/crates/move-cli/tests/build_tests/rebuild_after_adding_new_source/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/rebuild_after_adding_new_source/Move.toml index 38c8578e70c5d..fb502b9d63a8b 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/rebuild_after_adding_new_source/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/rebuild_after_adding_new_source/Move.toml @@ -1,2 +1,3 @@ [package] name = "Foo" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/rebuild_after_deleting_output_artifact/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/rebuild_after_deleting_output_artifact/Move.toml index 38c8578e70c5d..fb502b9d63a8b 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/rebuild_after_deleting_output_artifact/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/rebuild_after_deleting_output_artifact/Move.toml @@ -1,2 +1,3 @@ [package] name = "Foo" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/rebuild_after_touching_manifest/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/rebuild_after_touching_manifest/Move.toml index 38c8578e70c5d..fb502b9d63a8b 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/rebuild_after_touching_manifest/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/rebuild_after_touching_manifest/Move.toml @@ -1,2 +1,3 @@ [package] name = "Foo" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/rebuild_after_touching_source/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/rebuild_after_touching_source/Move.toml index 38c8578e70c5d..fb502b9d63a8b 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/rebuild_after_touching_source/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/rebuild_after_touching_source/Move.toml @@ -1,2 +1,3 @@ [package] name = "Foo" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/rebuild_no_modification/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/rebuild_no_modification/Move.toml index 38c8578e70c5d..fb502b9d63a8b 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/rebuild_no_modification/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/rebuild_no_modification/Move.toml @@ -1,2 +1,3 @@ [package] name = "Foo" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/unbound_address/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/unbound_address/Move.toml index eb004df99fa61..d3b1acf0e1ca2 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/unbound_address/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/unbound_address/Move.toml @@ -1,5 +1,6 @@ [package] name = "A" +edition = "2024.beta" [addresses] A = "_" diff --git a/external-crates/move/crates/move-cli/tests/build_tests/unbound_dependency/Move.toml b/external-crates/move/crates/move-cli/tests/build_tests/unbound_dependency/Move.toml index 34150a5abc8c8..72e580593a81d 100644 --- a/external-crates/move/crates/move-cli/tests/build_tests/unbound_dependency/Move.toml +++ b/external-crates/move/crates/move-cli/tests/build_tests/unbound_dependency/Move.toml @@ -1,5 +1,6 @@ [package] name = "A" +edition = "2024.beta" [dependencies] Foo = { local = "foo" } diff --git a/external-crates/move/crates/move-cli/tests/metatests/cov/plain/Move.toml b/external-crates/move/crates/move-cli/tests/metatests/cov/plain/Move.toml index 22033b4afee83..1a5af0edcf304 100644 --- a/external-crates/move/crates/move-cli/tests/metatests/cov/plain/Move.toml +++ b/external-crates/move/crates/move-cli/tests/metatests/cov/plain/Move.toml @@ -1,2 +1,3 @@ [package] name = "plain" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-diff-module/Move.toml b/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-diff-module/Move.toml index 4e912138c202f..c338346e40e39 100644 --- a/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-diff-module/Move.toml +++ b/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-diff-module/Move.toml @@ -1,2 +1,3 @@ [package] name = "two-runs-diff-module" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-diff-module/sources/M1.move b/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-diff-module/sources/M1.move index 1bd0d579ed22c..9fd7c6bce4215 100644 --- a/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-diff-module/sources/M1.move +++ b/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-diff-module/sources/M1.move @@ -1,5 +1,3 @@ -address 0x42 { -module M1 { +module 0x42::M1 { public entry fun test() {} } -} diff --git a/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-diff-module/sources/M2.move b/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-diff-module/sources/M2.move index efe2e57019bd7..9b7708349de42 100644 --- a/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-diff-module/sources/M2.move +++ b/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-diff-module/sources/M2.move @@ -1,5 +1,3 @@ -address 0x42 { -module M2 { +module 0x42::M2 { public entry fun test() {} } -} diff --git a/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-same-module/Move.toml b/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-same-module/Move.toml index 5b3ff32dba14f..e2281d4558338 100644 --- a/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-same-module/Move.toml +++ b/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-same-module/Move.toml @@ -1,2 +1,3 @@ [package] name = "two-runs-same-module" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-same-module/sources/M.move b/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-same-module/sources/M.move index 2bf7c4f6e69ff..63bfb22fe0cfc 100644 --- a/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-same-module/sources/M.move +++ b/external-crates/move/crates/move-cli/tests/metatests/cov/two-runs-same-module/sources/M.move @@ -1,5 +1,4 @@ -address 0x42 { -module M { +module 0x42::M { public entry fun test(x: u8) { if (x == 0) { return () @@ -8,4 +7,3 @@ module M { } } } -} diff --git a/external-crates/move/crates/move-cli/tests/move_unit_tests/assign_dev_addr_for_dep/Move.toml b/external-crates/move/crates/move-cli/tests/move_unit_tests/assign_dev_addr_for_dep/Move.toml index 9807273047ad1..de9900102115b 100644 --- a/external-crates/move/crates/move-cli/tests/move_unit_tests/assign_dev_addr_for_dep/Move.toml +++ b/external-crates/move/crates/move-cli/tests/move_unit_tests/assign_dev_addr_for_dep/Move.toml @@ -1,5 +1,6 @@ [package] name = "Foo" +edition = "2024.beta" [addresses] A = "_" diff --git a/external-crates/move/crates/move-cli/tests/move_unit_tests/assign_dev_addr_for_dep/dep/Move.toml b/external-crates/move/crates/move-cli/tests/move_unit_tests/assign_dev_addr_for_dep/dep/Move.toml index 504a96e8b969e..b2e5ff722fc15 100644 --- a/external-crates/move/crates/move-cli/tests/move_unit_tests/assign_dev_addr_for_dep/dep/Move.toml +++ b/external-crates/move/crates/move-cli/tests/move_unit_tests/assign_dev_addr_for_dep/dep/Move.toml @@ -1,5 +1,6 @@ [package] name = "Bar" +edition = "2024.beta" [addresses] B = "_" diff --git a/external-crates/move/crates/move-cli/tests/move_unit_tests/standalone_module_with_dev_addr_assignment/Move.toml b/external-crates/move/crates/move-cli/tests/move_unit_tests/standalone_module_with_dev_addr_assignment/Move.toml index 9eda613c2785f..ec94f223d05ea 100644 --- a/external-crates/move/crates/move-cli/tests/move_unit_tests/standalone_module_with_dev_addr_assignment/Move.toml +++ b/external-crates/move/crates/move-cli/tests/move_unit_tests/standalone_module_with_dev_addr_assignment/Move.toml @@ -1,5 +1,6 @@ [package] name = "StandaloneModule" +edition = "2024.beta" [addresses] A = "_" diff --git a/external-crates/move/crates/move-cli/tests/move_unit_tests/standalone_module_with_regular_addr_assignment/Move.toml b/external-crates/move/crates/move-cli/tests/move_unit_tests/standalone_module_with_regular_addr_assignment/Move.toml index b9d25c351bf5d..125b57dc7e2c4 100644 --- a/external-crates/move/crates/move-cli/tests/move_unit_tests/standalone_module_with_regular_addr_assignment/Move.toml +++ b/external-crates/move/crates/move-cli/tests/move_unit_tests/standalone_module_with_regular_addr_assignment/Move.toml @@ -1,5 +1,6 @@ [package] name = "StandaloneModule" +edition = "2024.beta" [addresses] A = "0x2" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/build_modules_and_scripts/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/build_modules_and_scripts/Move.toml index 227421ece9413..213deb0302a27 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/build_modules_and_scripts/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/build_modules_and_scripts/Move.toml @@ -1,2 +1,3 @@ [package] name = "build_modules_and_scripts" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/build_modules_and_scripts/sources/M.move b/external-crates/move/crates/move-cli/tests/sandbox_tests/build_modules_and_scripts/sources/M.move index 323f521c5fa9b..9b26c6106d495 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/build_modules_and_scripts/sources/M.move +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/build_modules_and_scripts/sources/M.move @@ -1,4 +1,2 @@ -address 0x42 { -module M { -} +module 0x42::M { } diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/doctor_with_stdlib/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/doctor_with_stdlib/Move.toml index 54d0c7b5cf984..2e8f58e6a36bd 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/doctor_with_stdlib/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/doctor_with_stdlib/Move.toml @@ -1,8 +1,9 @@ [package] name = "doctor_with_stdlib" +edition = "2024.beta" [addresses] std = "0x1" [dependencies] -MoveNursery = { local = "../../../../move-stdlib/nursery" } +MoveStdlib = { local = "../../../../move-stdlib" } diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/doctor_with_stdlib/sources/M.move b/external-crates/move/crates/move-cli/tests/sandbox_tests/doctor_with_stdlib/sources/M.move index 2a3b4679e1879..8d8f2c2eec2a6 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/doctor_with_stdlib/sources/M.move +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/doctor_with_stdlib/sources/M.move @@ -1,5 +1,4 @@ -address 0x2 { -module M { +module 0x42::M { use std::debug; #[allow(unused_function)] @@ -7,4 +6,3 @@ module M { debug::print(&7); } } -} diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_arithmetic_failure/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_arithmetic_failure/Move.toml index 82f9ed8591dc3..19fb61793939a 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_arithmetic_failure/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_arithmetic_failure/Move.toml @@ -1,2 +1,3 @@ [package] name = "explain_arithmetic_failure" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_stdlib_abort/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_stdlib_abort/Move.toml index 30d499f2e0e75..0997e9909a1ed 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_stdlib_abort/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_stdlib_abort/Move.toml @@ -1,5 +1,6 @@ [package] name = "explain_stdlib_abort" +edition = "2024.beta" [addresses] std = "0x1" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_stdlib_abort/sources/bad_borrow.move b/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_stdlib_abort/sources/bad_borrow.move index a88ee72f325ca..4305fd427223b 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_stdlib_abort/sources/bad_borrow.move +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_stdlib_abort/sources/bad_borrow.move @@ -1,5 +1,4 @@ module 0x42::m { - use std::vector; entry fun bad_borrow() { let v = vector::empty(); let _ref = vector::borrow(&v, 0); diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_user_module_abort/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_user_module_abort/Move.toml index 59b849a5b413c..af4f2f265a5db 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_user_module_abort/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_user_module_abort/Move.toml @@ -1,2 +1,3 @@ [package] name = "explain_user_module_abort" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_user_module_abort/sources/Fail.move b/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_user_module_abort/sources/Fail.move index ca5da8df3229e..a60f498cb193a 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_user_module_abort/sources/Fail.move +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/explain_user_module_abort/sources/Fail.move @@ -1,7 +1,5 @@ -address 0x2 { -module Fail { +module 0x2::Fail { public entry fun f() { abort 77 } } -} diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/gas_metering/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/gas_metering/Move.toml index f712729a5a34a..2a77d599f217f 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/gas_metering/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/gas_metering/Move.toml @@ -1,2 +1,3 @@ [package] name = "gas_metering" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/generate_struct_layout/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/generate_struct_layout/Move.toml index a504af284913b..4113037316681 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/generate_struct_layout/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/generate_struct_layout/Move.toml @@ -1,2 +1,3 @@ [package] name = "generate_struct_layout" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/generate_struct_layout/sources/M1.move b/external-crates/move/crates/move-cli/tests/sandbox_tests/generate_struct_layout/sources/M1.move index 7c6f5a31f98c8..c171a80fb3ccf 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/generate_struct_layout/sources/M1.move +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/generate_struct_layout/sources/M1.move @@ -2,11 +2,11 @@ module 0x1::M1 { use 0x1::M2::C; - struct A { f: u64, v: vector, b: B } + public struct A { f: u64, v: vector, b: B } - struct B { a: address, c: C, t: T } + public struct B { a: address, c: C, t: T } - struct S { t: T } + public struct S { t: T } - struct G { x: u64, s: S } + public struct G { x: u64, s: S } } diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/generate_struct_layout/sources/M2.move b/external-crates/move/crates/move-cli/tests/sandbox_tests/generate_struct_layout/sources/M2.move index 8e8baa58fe0e4..f3cc297417d58 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/generate_struct_layout/sources/M2.move +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/generate_struct_layout/sources/M2.move @@ -1,5 +1,5 @@ module 0x1::M2 { #[allow(unused_field)] - struct C { t: T, b: bool } + public struct C { t: T, b: bool } } diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/generate_struct_layout/sources/phantoms.move b/external-crates/move/crates/move-cli/tests/sandbox_tests/generate_struct_layout/sources/phantoms.move index 36c04fd66ca1b..6c177ab6c6454 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/generate_struct_layout/sources/phantoms.move +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/generate_struct_layout/sources/phantoms.move @@ -1,19 +1,19 @@ #[allow(unused_field)] module 0x1::phantoms { - struct A {} + public struct A {} - struct B {} + public struct B {} - struct C { + public struct C { a: A, b: B } - struct D { + public struct D { v: vector } - struct E { + public struct E { v1: vector, v2: vector } diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/module_disassemble/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/module_disassemble/Move.toml index c2cd982ee3515..2d1699293e385 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/module_disassemble/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/module_disassemble/Move.toml @@ -1,5 +1,6 @@ [package] name = "module_publish_view" +edition = "2024.beta" [addresses] std = "0x1" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/module_disassemble/deps1/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/module_disassemble/deps1/Move.toml index c0a24ac0f10be..295dd96c2e4cf 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/module_disassemble/deps1/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/module_disassemble/deps1/Move.toml @@ -1,2 +1,3 @@ [package] name = "Deps1" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/module_disassemble/deps2/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/module_disassemble/deps2/Move.toml index 75c9ff86ecfa0..a34d60f2624e0 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/module_disassemble/deps2/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/module_disassemble/deps2/Move.toml @@ -1,2 +1,3 @@ [package] name = "Deps2" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/module_disassemble/sources/M1.move b/external-crates/move/crates/move-cli/tests/sandbox_tests/module_disassemble/sources/M1.move index f5aed00e50400..7c289c5e51e5d 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/module_disassemble/sources/M1.move +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/module_disassemble/sources/M1.move @@ -1,8 +1,7 @@ module 0xa::M1 { - use std::vector; #[allow(unused_field)] - struct S { i: u64 } + public struct S { i: u64 } public fun foo(x: u64): vector { let y = bar(); diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/module_publish_view/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/module_publish_view/Move.toml index a9da3b168aaf4..fb820c1d04de9 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/module_publish_view/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/module_publish_view/Move.toml @@ -1,2 +1,3 @@ [package] name = "module_publish_view" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/module_publish_view/sources/Module.move b/external-crates/move/crates/move-cli/tests/sandbox_tests/module_publish_view/sources/Module.move index 3bde7f18c916b..0dc254cd9b2ae 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/module_publish_view/sources/Module.move +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/module_publish_view/sources/Module.move @@ -1,9 +1,7 @@ -address 0x42 { -module Module { - struct S { i: u64 } +module 0x42::Module { + public struct S { i: u64 } public fun foo(i: u64): S { S { i } } } -} diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/multi_module_publish/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/multi_module_publish/Move.toml index ce353a9bdb159..584de0746e044 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/multi_module_publish/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/multi_module_publish/Move.toml @@ -1,2 +1,3 @@ [package] name = "multi_module_publish" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/multi_module_publish/sources/GoodFriends.move b/external-crates/move/crates/move-cli/tests/sandbox_tests/multi_module_publish/sources/GoodFriends.move index c96b84ac900bc..74c3ff22d4e9b 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/multi_module_publish/sources/GoodFriends.move +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/multi_module_publish/sources/GoodFriends.move @@ -1,6 +1,5 @@ module 0x2::A { - friend 0x2::B; - public(friend) fun foo() {} + public(package) fun foo() {} } module 0x2::B { diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/named_address_conflicts_in_error/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/named_address_conflicts_in_error/Move.toml index 387bb7c90102f..5cd377a29dca2 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/named_address_conflicts_in_error/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/named_address_conflicts_in_error/Move.toml @@ -1,5 +1,6 @@ [package] name = "use_named_address" +edition = "2024.beta" [addresses] a = "0x42" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/named_address_conflicts_in_error/dep/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/named_address_conflicts_in_error/dep/Move.toml index 0c63f88d75992..c13bafbb000c8 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/named_address_conflicts_in_error/dep/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/named_address_conflicts_in_error/dep/Move.toml @@ -1,5 +1,6 @@ [package] name = "Dep" +edition = "2024.beta" [addresses] a = "0x41" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/named_address_conflicts_in_error/dep/sources/m.move b/external-crates/move/crates/move-cli/tests/sandbox_tests/named_address_conflicts_in_error/dep/sources/m.move index 51a295bafbfbf..276a1997bf484 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/named_address_conflicts_in_error/dep/sources/m.move +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/named_address_conflicts_in_error/dep/sources/m.move @@ -1,6 +1,6 @@ module a::m { - struct X {} - struct Y {} + public struct X {} + public struct Y {} public fun x(): X { X {} } diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/package_basics/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/package_basics/Move.toml index cba2b427b2f55..e1714b99487c0 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/package_basics/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/package_basics/Move.toml @@ -1,5 +1,6 @@ [package] name = "PackageBasics" +edition = "2024.beta" [addresses] std = "0x1" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/package_basics/args.exp b/external-crates/move/crates/move-cli/tests/sandbox_tests/package_basics/args.exp index a46298ad88699..f036924fe828f 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/package_basics/args.exp +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/package_basics/args.exp @@ -77,20 +77,17 @@ Constants [ 0 => u64: 0 ] } -Command `disassemble --package MoveStdlib --name signer`: +Command `disassemble --package MoveStdlib --name address`: // Move bytecode v6 -module 1.signer { +module 1.address { -native public borrow_address(s#0#0: &signer): &address -public address_of(s#0#0: &signer): address { +public length(): u64 { B0: - 0: MoveLoc[0](s#0#0: &signer) - 1: Call borrow_address(&signer): &address - 2: ReadRef - 3: Ret + 0: LdU64(32) + 1: Ret } } diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/package_basics/args.txt b/external-crates/move/crates/move-cli/tests/sandbox_tests/package_basics/args.txt index a80887c977c7f..803155024e873 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/package_basics/args.txt +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/package_basics/args.txt @@ -3,7 +3,7 @@ test --coverage --threads 1 coverage summary --summarize-functions coverage source --module AModule coverage bytecode --module AModule -disassemble --package MoveStdlib --name signer +disassemble --package MoveStdlib --name address info test double_two test one_one diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/print_stack_trace/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/print_stack_trace/Move.toml index 446c3c961dab2..fcc74b457f32b 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/print_stack_trace/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/print_stack_trace/Move.toml @@ -1,8 +1,9 @@ [package] name = "print_stack_trace" +edition = "2024.beta" [addresses] std = "0x1" [dependencies] -MoveNursery = { local = "../../../../move-stdlib/nursery" } +MoveStdlib = { local = "../../../../move-stdlib" } diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/print_stack_trace/sources/M.move b/external-crates/move/crates/move-cli/tests/sandbox_tests/print_stack_trace/sources/M.move index 3cc1425e225a1..978efd65b36a7 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/print_stack_trace/sources/M.move +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/print_stack_trace/sources/M.move @@ -1,5 +1,4 @@ -address 0x2 { -module M { +module 0x2::M { use std::debug; public fun sum(n: u64): u64 { @@ -11,4 +10,3 @@ module M { } } } -} diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/print_stack_trace/sources/N.move b/external-crates/move/crates/move-cli/tests/sandbox_tests/print_stack_trace/sources/N.move index 24f61a218d635..08e2467602b16 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/print_stack_trace/sources/N.move +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/print_stack_trace/sources/N.move @@ -1,14 +1,12 @@ -address 0x2 { #[allow(unused_type_parameter, unused_mut_ref)] -module N { +module 0x2::N { use 0x2::M; public fun foo(): u64 { - let x = 3; + let mut x = 3; let y = &mut x; let z = M::sum(4); _ = y; z } } -} diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/print_stack_trace/sources/print_stack_trace.move b/external-crates/move/crates/move-cli/tests/sandbox_tests/print_stack_trace/sources/print_stack_trace.move index 6cc01bb23aa7c..cda19d634c1e7 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/print_stack_trace/sources/print_stack_trace.move +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/print_stack_trace/sources/print_stack_trace.move @@ -1,11 +1,10 @@ module 0x42::print_stack_trace { use std::debug; - use std::vector; use 0x2::N; #[allow(unused_mut_ref)] entry fun print_stack_trace() { - let v = vector::empty(); + let mut v = vector::empty(); vector::push_back(&mut v, true); vector::push_back(&mut v, false); let r = vector::borrow(&mut v, 1); diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/print_values/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/print_values/Move.toml index 1f13306640b9f..32e1a378ac3a8 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/print_values/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/print_values/Move.toml @@ -1,8 +1,9 @@ [package] name = "print_values" +edition = "2024.beta" [addresses] std = "0x1" [dependencies] -MoveNursery = { local = "../../../../move-stdlib/nursery" } +MoveStdlib = { local = "../../../../move-stdlib" } diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/print_values/args.exp b/external-crates/move/crates/move-cli/tests/sandbox_tests/print_values/args.exp index c6d94de4f98f7..327546f02d9de 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/print_values/args.exp +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/print_values/args.exp @@ -1,6 +1,5 @@ Command `sandbox publish`: Command `test`: -INCLUDING DEPENDENCY MoveNursery INCLUDING DEPENDENCY MoveStdlib BUILDING print_values Running Move unit tests @@ -38,7 +37,6 @@ Running Move unit tests [debug] false [debug] true [debug] @0x1234c0ffee -[debug] signer(0x0) [debug] "test_print_struct" [debug] 0x2::M::TestInner { val: 100, @@ -62,7 +60,6 @@ Running Move unit tests [debug] [ 256, 257, 258, 259 ] [debug] [ true, false ] [debug] [ @0x1234, @0x5678, @0xabcdef ] -[debug] [ signer(0x0), signer(0x100000000000000000000000000000000000000000000000000000000000000), signer(0x200000000000000000000000000000000000000000000000000000000000000), signer(0x300000000000000000000000000000000000000000000000000000000000000) ] [debug] [ 0x2::M::TestInner { val: 4, @@ -113,10 +110,6 @@ Running Move unit tests [ @0x1234, @0x5678 ], [ @0xabcdef, @0x9999 ] ] -[debug] [ - [ signer(0x0), signer(0x100000000000000000000000000000000000000000000000000000000000000) ], - [ signer(0x0), signer(0x100000000000000000000000000000000000000000000000000000000000000) ] -] [debug] [ [ 0x2::M::TestInner { diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/print_values/sources/M.move b/external-crates/move/crates/move-cli/tests/sandbox_tests/print_values/sources/M.move index e4c8ba1f3b1c2..cb661dfb65ad2 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/print_values/sources/M.move +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/print_values/sources/M.move @@ -1,33 +1,26 @@ -address 0x2 { #[allow(unused_field)] -module M { +module 0x2::M { #[test_only] use std::ascii; #[test_only] use std::debug::print; - #[test_only] - use std::debug::print_string; use std::string; - #[test_only] - use std::unit_test::create_signers_for_testing; - #[test_only] - use std::vector; - struct Foo has drop {} - struct Bar has drop { x: u128, y: Foo, z: bool } - struct Box has drop { x: T } + public struct Foo has drop {} + public struct Bar has drop { x: u128, y: Foo, z: bool } + public struct Box has drop { x: T } - struct GenericStruct has drop { + public struct GenericStruct has drop { val: u64, } - struct TestInner has drop { + public struct TestInner has drop { val: u128, vec: vector, msgs: vector> } - struct TestStruct has drop { + public struct TestStruct has drop { addr: address, number: u8, bytes: vector, @@ -40,7 +33,7 @@ module M { let x = 42; print(&x); - let v = vector::empty(); + let mut v = vector::empty(); vector::push_back(&mut v, 100); vector::push_back(&mut v, 200); vector::push_back(&mut v, 300); @@ -124,11 +117,6 @@ module M { let a = @0x1234c0ffee; print(&a); - - // print a signer - let senders = create_signers_for_testing(1); - let sender = vector::pop_back(&mut senders); - print(&sender); } #[allow(unused_const)] @@ -185,9 +173,6 @@ module M { let v_addr = vector[@0x1234, @0x5678, @0xabcdef]; print(&v_addr); - let v_signer = create_signers_for_testing(4); - print(&v_signer); - let v = vector[ TestInner { val: 4u128, @@ -231,9 +216,6 @@ module M { let v_addr = vector[vector[@0x1234, @0x5678], vector[@0xabcdef, @0x9999]]; print(&v_addr); - let v_signer = vector[create_signers_for_testing(2), create_signers_for_testing(2)]; - print(&v_signer); - let v = vector[ vector[ TestInner { val: 4u128, vec: vector[127u128, 128u128], msgs: vector[] }, @@ -275,5 +257,9 @@ module M { print(&obj); } -} + + #[test_only] + public fun print_string(utf8_bytes: vector) { + print(&string::utf8(utf8_bytes)); + } } diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/publish_then_run/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/publish_then_run/Move.toml index e6ea5df3aeac9..2c09426b415c0 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/publish_then_run/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/publish_then_run/Move.toml @@ -1,2 +1,3 @@ [package] name = "publish_then_run" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/publish_then_run/sources/M.move b/external-crates/move/crates/move-cli/tests/sandbox_tests/publish_then_run/sources/M.move index f25c55efc332b..7dd1a97c43805 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/publish_then_run/sources/M.move +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/publish_then_run/sources/M.move @@ -1,5 +1,3 @@ -address 0x2 { -module M { +module 0x2::M { public entry fun f() {} } -} diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/random_test_flag_correctness/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/random_test_flag_correctness/Move.toml index c88176fe599b1..ec5322a2c6e18 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/random_test_flag_correctness/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/random_test_flag_correctness/Move.toml @@ -1,5 +1,6 @@ [package] name = "random_test_flag_correctness" +edition = "2024.beta" [addresses] std = "0x1" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/use_named_address/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/use_named_address/Move.toml index cbec350b0c2ea..076d820a22706 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/use_named_address/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/use_named_address/Move.toml @@ -1,5 +1,6 @@ [package] name = "use_named_address" +edition = "2024.beta" [addresses] A = "0x42" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/verify_native_functions_in_multi_module_publish/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/verify_native_functions_in_multi_module_publish/Move.toml index e162affa0e604..939bd5cabd656 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/verify_native_functions_in_multi_module_publish/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/verify_native_functions_in_multi_module_publish/Move.toml @@ -1,2 +1,3 @@ [package] name = "verify_native_functions_in_publish" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/sandbox_tests/verify_native_functions_in_publish/Move.toml b/external-crates/move/crates/move-cli/tests/sandbox_tests/verify_native_functions_in_publish/Move.toml index e162affa0e604..939bd5cabd656 100644 --- a/external-crates/move/crates/move-cli/tests/sandbox_tests/verify_native_functions_in_publish/Move.toml +++ b/external-crates/move/crates/move-cli/tests/sandbox_tests/verify_native_functions_in_publish/Move.toml @@ -1,2 +1,3 @@ [package] name = "verify_native_functions_in_publish" +edition = "2024.beta" diff --git a/external-crates/move/crates/move-cli/tests/upload_tests/no_git_remote_package/Move.toml b/external-crates/move/crates/move-cli/tests/upload_tests/no_git_remote_package/Move.toml index 56dceb553f5b5..8d8563b904bcf 100644 --- a/external-crates/move/crates/move-cli/tests/upload_tests/no_git_remote_package/Move.toml +++ b/external-crates/move/crates/move-cli/tests/upload_tests/no_git_remote_package/Move.toml @@ -1,5 +1,6 @@ [package] name = "Package1" +edition = "2024.beta" [addresses] Std = "0x1" diff --git a/external-crates/move/crates/move-cli/tests/upload_tests/valid_package1/Move.toml b/external-crates/move/crates/move-cli/tests/upload_tests/valid_package1/Move.toml index 56dceb553f5b5..8d8563b904bcf 100644 --- a/external-crates/move/crates/move-cli/tests/upload_tests/valid_package1/Move.toml +++ b/external-crates/move/crates/move-cli/tests/upload_tests/valid_package1/Move.toml @@ -1,5 +1,6 @@ [package] name = "Package1" +edition = "2024.beta" [addresses] Std = "0x1" diff --git a/external-crates/move/crates/move-cli/tests/upload_tests/valid_package2/Move.toml b/external-crates/move/crates/move-cli/tests/upload_tests/valid_package2/Move.toml index 56dceb553f5b5..8d8563b904bcf 100644 --- a/external-crates/move/crates/move-cli/tests/upload_tests/valid_package2/Move.toml +++ b/external-crates/move/crates/move-cli/tests/upload_tests/valid_package2/Move.toml @@ -1,5 +1,6 @@ [package] name = "Package1" +edition = "2024.beta" [addresses] Std = "0x1" diff --git a/external-crates/move/crates/move-cli/tests/upload_tests/valid_package3/Move.toml b/external-crates/move/crates/move-cli/tests/upload_tests/valid_package3/Move.toml index 56dceb553f5b5..8d8563b904bcf 100644 --- a/external-crates/move/crates/move-cli/tests/upload_tests/valid_package3/Move.toml +++ b/external-crates/move/crates/move-cli/tests/upload_tests/valid_package3/Move.toml @@ -1,5 +1,6 @@ [package] name = "Package1" +edition = "2024.beta" [addresses] Std = "0x1" diff --git a/external-crates/move/crates/move-compiler/src/editions/mod.rs b/external-crates/move/crates/move-compiler/src/editions/mod.rs index 7793a573480e9..5cfea4d99d59d 100644 --- a/external-crates/move/crates/move-compiler/src/editions/mod.rs +++ b/external-crates/move/crates/move-compiler/src/editions/mod.rs @@ -397,6 +397,6 @@ impl Serialize for Flavor { impl Default for Edition { fn default() -> Self { - Edition::LEGACY + Edition::E2024_BETA } } diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/dot_call_non_struct.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/dot_call_non_struct.exp index 494f2b8aa3740..72686085d4c6e 100644 --- a/external-crates/move/crates/move-compiler/tests/move_2024/typing/dot_call_non_struct.exp +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/dot_call_non_struct.exp @@ -25,7 +25,7 @@ error[E04023]: invalid method call 10 │ 0u64.f(); │ ^^^^^^^^ │ │ │ - │ │ No local 'use fun' alias was found for 'u64.f' + │ │ No local 'use fun' alias was found for 'u64.f', and no function 'f' was found in the defining module 'std::u64' │ Invalid method call. No known method 'f' on type 'u64' error[E04023]: invalid method call diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/duplicate_defines_primitive.exp b/external-crates/move/crates/move-compiler/tests/move_2024/typing/duplicate_defines_primitive.exp index 2a0680e60c94a..8d45aad08e11a 100644 --- a/external-crates/move/crates/move-compiler/tests/move_2024/typing/duplicate_defines_primitive.exp +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/duplicate_defines_primitive.exp @@ -4,6 +4,6 @@ error[E10004]: invalid usage of known attribute 2 │ module a::m {} │ - Previously declared here 3 │ -4 │ #[defines_primitive(u64)] - │ ^^^^^^^^^^^^^^^^^^^^^^ Duplicate definer annotated for primitive type 'u64' +4 │ #[defines_primitive(signer)] + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ Duplicate definer annotated for primitive type 'signer' diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/typing/duplicate_defines_primitive.move b/external-crates/move/crates/move-compiler/tests/move_2024/typing/duplicate_defines_primitive.move index f2f7f25da2bd9..565d0fdeae2e4 100644 --- a/external-crates/move/crates/move-compiler/tests/move_2024/typing/duplicate_defines_primitive.move +++ b/external-crates/move/crates/move-compiler/tests/move_2024/typing/duplicate_defines_primitive.move @@ -1,5 +1,5 @@ -#[defines_primitive(u64)] +#[defines_primitive(signer)] module a::m {} -#[defines_primitive(u64)] +#[defines_primitive(signer)] module a::n {} diff --git a/external-crates/move/crates/move-compiler/tests/move_check_testsuite.rs b/external-crates/move/crates/move-compiler/tests/move_check_testsuite.rs index 1296da8588f72..104ea80dd79f8 100644 --- a/external-crates/move/crates/move-compiler/tests/move_check_testsuite.rs +++ b/external-crates/move/crates/move-compiler/tests/move_check_testsuite.rs @@ -64,7 +64,7 @@ fn move_check_testsuite(path: &Path) -> datatest_stable::Result<()> { } else if path_contains(DEV_DIR) { Edition::DEVELOPMENT } else { - Edition::default() + Edition::LEGACY }; let config = PackageConfig { flavor, diff --git a/external-crates/move/crates/move-docgen/tests/sources/different_visbilities.move b/external-crates/move/crates/move-docgen/tests/sources/different_visbilities.move index 3d1aaad29ed34..0bb17bf6ad845 100644 --- a/external-crates/move/crates/move-docgen/tests/sources/different_visbilities.move +++ b/external-crates/move/crates/move-docgen/tests/sources/different_visbilities.move @@ -1,5 +1,4 @@ -address 0x2 { -module TestViz { +module 0x2::TestViz { /// This is a public function public fun this_is_a_public_fun() { } @@ -13,4 +12,3 @@ module TestViz { /// This is a private function fun this_is_a_private_fun() {} } -} diff --git a/external-crates/move/crates/move-docgen/tests/sources/root_template_AnotherTypeOfScript.notest_move b/external-crates/move/crates/move-docgen/tests/sources/root_template_AnotherTypeOfScript.notest_move index eb7dbd3b1d754..aeeb87e9a1268 100644 --- a/external-crates/move/crates/move-docgen/tests/sources/root_template_AnotherTypeOfScript.notest_move +++ b/external-crates/move/crates/move-docgen/tests/sources/root_template_AnotherTypeOfScript.notest_move @@ -1,5 +1,4 @@ -address 0x1 { -module AnotherTypeOfScript { +module 0x1::AnotherTypeOfScript { /// This is a script entry fun script3() {} @@ -7,4 +6,3 @@ module AnotherTypeOfScript { /// This is another script entry fun script4() {} } -} diff --git a/external-crates/move/crates/move-docgen/tests/sources/root_template_OneTypeOfScript.notest_move b/external-crates/move/crates/move-docgen/tests/sources/root_template_OneTypeOfScript.notest_move index e793028b23b20..2de14cdce79fd 100644 --- a/external-crates/move/crates/move-docgen/tests/sources/root_template_OneTypeOfScript.notest_move +++ b/external-crates/move/crates/move-docgen/tests/sources/root_template_OneTypeOfScript.notest_move @@ -1,5 +1,4 @@ -address 0x1 { -module OneTypeOfScript { +module 0x1::OneTypeOfScript { /// This is a script entry fun script1() {} @@ -7,4 +6,3 @@ module OneTypeOfScript { /// This is another script entry fun script2() {} } -} diff --git a/external-crates/move/crates/move-package/src/resolution/resolution_graph.rs b/external-crates/move/crates/move-package/src/resolution/resolution_graph.rs index 8b8e45ca8a0ec..603d4c07f7493 100644 --- a/external-crates/move/crates/move-package/src/resolution/resolution_graph.rs +++ b/external-crates/move/crates/move-package/src/resolution/resolution_graph.rs @@ -7,6 +7,7 @@ use move_command_line_common::files::{ extension_equals, find_filenames, find_move_filenames, FileHash, MOVE_COMPILED_EXTENSION, }; use move_compiler::command_line::DEFAULT_OUTPUT_DIR; +use move_compiler::editions::Edition; use move_compiler::{diagnostics::WarningFilters, shared::PackageConfig}; use move_core_types::account_address::AccountAddress; use move_symbol_pool::Symbol; @@ -574,7 +575,7 @@ impl Package { .package .edition .or(config.default_edition) - .unwrap_or_default(), + .unwrap_or(Edition::LEGACY), // TODO require edition warning_filter: WarningFilters::new_for_source(), } } diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/borrow/basic_test.move b/external-crates/move/crates/move-stackless-bytecode/tests/borrow/basic_test.move index 49592ecd81250..f4c8062d09456 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/borrow/basic_test.move +++ b/external-crates/move/crates/move-stackless-bytecode/tests/borrow/basic_test.move @@ -1,10 +1,10 @@ module 0x42::TestBorrow { - struct R has copy, drop { + public struct R has copy, drop { x: u64 } fun test1() : R { - let r = R {x: 3}; + let mut r = R {x: 3}; let r_ref = &mut r; let x_ref = &mut r_ref.x; *x_ref = 0; @@ -21,7 +21,7 @@ module 0x42::TestBorrow { } fun test4() : R { - let r = R {x: 3}; + let mut r = R {x: 3}; let r_ref = &mut r; test3(r_ref, 0); r @@ -32,7 +32,7 @@ module 0x42::TestBorrow { } fun test6() : R { - let r = R {x: 3}; + let mut r = R {x: 3}; let r_ref = &mut r; let x_ref = test5(r_ref); test2(x_ref, 0); @@ -40,19 +40,19 @@ module 0x42::TestBorrow { } fun test7(b: bool) { - let r1 = R {x: 3}; - let r2 = R {x: 4}; - let r_ref = &mut r1; + let mut r1 = R {x: 3}; + let mut r2 = R {x: 4}; + let mut r_ref = &mut r1; if (b) { r_ref = &mut r2; }; test3(r_ref, 0) } - fun test8(b: bool, n: u64, r_ref: &mut R) { - let r1 = R {x: 3}; - let r2 = R {x: 4}; - let t_ref = &mut r2; + fun test8(b: bool, mut n: u64, r_ref: &mut R) { + let mut r1 = R {x: 3}; + let mut r2 = R {x: 4}; + let mut t_ref = &mut r2; while (0 < n) { if (n/2 == 0) { t_ref = &mut r1 diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/borrow/function_call.exp b/external-crates/move/crates/move-stackless-bytecode/tests/borrow/function_call.exp index 30226bd8972fb..0559d22411800 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/borrow/function_call.exp +++ b/external-crates/move/crates/move-stackless-bytecode/tests/borrow/function_call.exp @@ -1,5 +1,339 @@ ============ initial translation from Move ================ +[variant baseline] +public fun u64::diff($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: bool + var $t10: u64 + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: u64 + var $t16: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := move($t1) + 3: $t4 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := copy($t4) + 6: $t9 := >($t7, $t8) + 7: if ($t9) goto 8 else goto 14 + 8: label L1 + 9: $t10 := move($t3) + 10: $t11 := move($t4) + 11: $t12 := -($t10, $t11) + 12: $t2 := $t12 + 13: goto 20 + 14: label L0 + 15: $t13 := move($t4) + 16: $t14 := move($t3) + 17: $t15 := -($t13, $t14) + 18: $t2 := $t15 + 19: goto 20 + 20: label L2 + 21: $t16 := move($t2) + 22: return $t16 +} + + +[variant baseline] +public fun u64::divide_and_round_up($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: u64 + var $t10: u64 + var $t11: bool + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: u64 + var $t16: u64 + var $t17: u64 + var $t18: u64 + var $t19: u64 + var $t20: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := move($t1) + 3: $t4 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := copy($t4) + 6: $t9 := %($t7, $t8) + 7: $t10 := 0 + 8: $t11 := ==($t9, $t10) + 9: if ($t11) goto 10 else goto 16 + 10: label L1 + 11: $t12 := move($t3) + 12: $t13 := move($t4) + 13: $t14 := /($t12, $t13) + 14: $t2 := $t14 + 15: goto 24 + 16: label L0 + 17: $t15 := move($t3) + 18: $t16 := move($t4) + 19: $t17 := /($t15, $t16) + 20: $t18 := 1 + 21: $t19 := +($t17, $t18) + 22: $t2 := $t19 + 23: goto 24 + 24: label L2 + 25: $t20 := move($t2) + 26: return $t20 +} + + +[variant baseline] +public fun u64::max($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: bool + var $t10: u64 + var $t11: u64 + var $t12: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := move($t1) + 3: $t4 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := copy($t4) + 6: $t9 := >($t7, $t8) + 7: if ($t9) goto 8 else goto 12 + 8: label L1 + 9: $t10 := move($t3) + 10: $t2 := $t10 + 11: goto 16 + 12: label L0 + 13: $t11 := move($t4) + 14: $t2 := $t11 + 15: goto 16 + 16: label L2 + 17: $t12 := move($t2) + 18: return $t12 +} + + +[variant baseline] +public fun u64::min($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: bool + var $t10: u64 + var $t11: u64 + var $t12: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := move($t1) + 3: $t4 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := copy($t4) + 6: $t9 := <($t7, $t8) + 7: if ($t9) goto 8 else goto 12 + 8: label L1 + 9: $t10 := move($t3) + 10: $t2 := $t10 + 11: goto 16 + 12: label L0 + 13: $t11 := move($t4) + 14: $t2 := $t11 + 15: goto 16 + 16: label L2 + 17: $t12 := move($t2) + 18: return $t12 +} + + +[variant baseline] +public fun u64::pow($t0|base: u64, $t1|exponent: u8): u64 { + var $t2|base#1#1: u64 + var $t3|exponent#1#1: u8 + var $t4|res#1#1: u64 + var $t5: u64 + var $t6: u8 + var $t7: u64 + var $t8: u8 + var $t9: u8 + var $t10: bool + var $t11: u8 + var $t12: u8 + var $t13: u8 + var $t14: u8 + var $t15: bool + var $t16: u64 + var $t17: u64 + var $t18: u64 + var $t19: u8 + var $t20: u8 + var $t21: u8 + var $t22: u64 + var $t23: u64 + var $t24: u64 + var $t25: u8 + var $t26: u8 + var $t27: u8 + var $t28: u64 + 0: $t5 := move($t0) + 1: $t2 := $t5 + 2: $t6 := move($t1) + 3: $t3 := $t6 + 4: $t7 := 1 + 5: $t4 := $t7 + 6: goto 7 + 7: label L5 + 8: $t8 := copy($t3) + 9: $t9 := 1 + 10: $t10 := >=($t8, $t9) + 11: if ($t10) goto 12 else goto 41 + 12: label L1 + 13: goto 14 + 14: label L2 + 15: $t11 := copy($t3) + 16: $t12 := 2 + 17: $t13 := %($t11, $t12) + 18: $t14 := 0 + 19: $t15 := ==($t13, $t14) + 20: if ($t15) goto 21 else goto 31 + 21: label L4 + 22: $t16 := copy($t2) + 23: $t17 := move($t2) + 24: $t18 := *($t16, $t17) + 25: $t2 := $t18 + 26: $t19 := move($t3) + 27: $t20 := 2 + 28: $t21 := /($t19, $t20) + 29: $t3 := $t21 + 30: goto 7 + 31: label L3 + 32: $t22 := move($t4) + 33: $t23 := copy($t2) + 34: $t24 := *($t22, $t23) + 35: $t4 := $t24 + 36: $t25 := move($t3) + 37: $t26 := 1 + 38: $t27 := -($t25, $t26) + 39: $t3 := $t27 + 40: goto 7 + 41: label L0 + 42: $t28 := move($t4) + 43: return $t28 +} + + +[variant baseline] +public fun u64::sqrt($t0|x: u64): u64 { + var $t1|bit#1#1: u128 + var $t2|res#1#1: u128 + var $t3|x#1#1: u64 + var $t4|x#2#1: u128 + var $t5: u64 + var $t6: u128 + var $t7: u128 + var $t8: u64 + var $t9: u128 + var $t10: u128 + var $t11: u128 + var $t12: bool + var $t13: u128 + var $t14: u128 + var $t15: u128 + var $t16: u128 + var $t17: bool + var $t18: u128 + var $t19: u128 + var $t20: u128 + var $t21: u128 + var $t22: u128 + var $t23: u128 + var $t24: u8 + var $t25: u128 + var $t26: u128 + var $t27: u128 + var $t28: u128 + var $t29: u8 + var $t30: u128 + var $t31: u128 + var $t32: u8 + var $t33: u128 + var $t34: u128 + var $t35: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := 18446744073709551616 + 3: $t1 := $t6 + 4: $t7 := 0 + 5: $t2 := $t7 + 6: $t8 := move($t3) + 7: $t9 := (u128)($t8) + 8: $t4 := $t9 + 9: goto 10 + 10: label L6 + 11: $t10 := copy($t1) + 12: $t11 := 0 + 13: $t12 := !=($t10, $t11) + 14: if ($t12) goto 15 else goto 50 + 15: label L1 + 16: goto 17 + 17: label L2 + 18: $t13 := copy($t4) + 19: $t14 := copy($t2) + 20: $t15 := copy($t1) + 21: $t16 := +($t14, $t15) + 22: $t17 := >=($t13, $t16) + 23: if ($t17) goto 24 else goto 38 + 24: label L4 + 25: $t18 := move($t4) + 26: $t19 := copy($t2) + 27: $t20 := copy($t1) + 28: $t21 := +($t19, $t20) + 29: $t22 := -($t18, $t21) + 30: $t4 := $t22 + 31: $t23 := move($t2) + 32: $t24 := 1 + 33: $t25 := >>($t23, $t24) + 34: $t26 := copy($t1) + 35: $t27 := +($t25, $t26) + 36: $t2 := $t27 + 37: goto 44 + 38: label L3 + 39: $t28 := move($t2) + 40: $t29 := 1 + 41: $t30 := >>($t28, $t29) + 42: $t2 := $t30 + 43: goto 44 + 44: label L5 + 45: $t31 := move($t1) + 46: $t32 := 2 + 47: $t33 := >>($t31, $t32) + 48: $t1 := $t33 + 49: goto 10 + 50: label L0 + 51: $t34 := move($t2) + 52: $t35 := (u64)($t34) + 53: return $t35 +} + + [variant baseline] public fun vector::append<#0>($t0|lhs: &mut vector<#0>, $t1|other: vector<#0>) { var $t2: &mut vector<#0> @@ -231,27 +565,25 @@ public fun vector::insert<#0>($t0|v: &mut vector<#0>, $t1|e: #0, $t2|i: u64) { 15: $t13 := move($t1) 16: vector::push_back<#0>($t12, $t13) 17: goto 18 - 18: label L5 + 18: label L4 19: $t14 := copy($t2) 20: $t15 := copy($t3) 21: $t16 := <($t14, $t15) - 22: if ($t16) goto 23 else goto 35 + 22: if ($t16) goto 23 else goto 33 23: label L3 - 24: goto 25 - 25: label L4 - 26: $t17 := copy($t0) - 27: $t18 := copy($t2) - 28: $t19 := copy($t3) - 29: vector::swap<#0>($t17, $t18, $t19) - 30: $t20 := move($t2) - 31: $t21 := 1 - 32: $t22 := +($t20, $t21) - 33: $t2 := $t22 - 34: goto 18 - 35: label L2 - 36: $t23 := move($t0) - 37: destroy($t23) - 38: return () + 24: $t17 := copy($t0) + 25: $t18 := copy($t2) + 26: $t19 := copy($t3) + 27: vector::swap<#0>($t17, $t18, $t19) + 28: $t20 := move($t2) + 29: $t21 := 1 + 30: $t22 := +($t20, $t21) + 31: $t2 := $t22 + 32: goto 18 + 33: label L2 + 34: $t23 := move($t0) + 35: destroy($t23) + 36: return () } @@ -329,31 +661,29 @@ public fun vector::remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { 16: $t15 := -($t13, $t14) 17: $t4 := $t15 18: goto 19 - 19: label L5 + 19: label L4 20: $t16 := copy($t1) 21: $t17 := copy($t4) 22: $t18 := <($t16, $t17) - 23: if ($t18) goto 24 else goto 40 + 23: if ($t18) goto 24 else goto 38 24: label L3 - 25: goto 26 - 26: label L4 - 27: $t19 := copy($t0) - 28: $t3 := $t19 - 29: $t20 := copy($t1) - 30: $t2 := $t20 - 31: $t21 := move($t1) - 32: $t22 := 1 - 33: $t23 := +($t21, $t22) - 34: $t1 := $t23 - 35: $t24 := move($t3) - 36: $t25 := move($t2) - 37: $t26 := copy($t1) - 38: vector::swap<#0>($t24, $t25, $t26) - 39: goto 19 - 40: label L2 - 41: $t27 := move($t0) - 42: $t28 := vector::pop_back<#0>($t27) - 43: return $t28 + 25: $t19 := copy($t0) + 26: $t3 := $t19 + 27: $t20 := copy($t1) + 28: $t2 := $t20 + 29: $t21 := move($t1) + 30: $t22 := 1 + 31: $t23 := +($t21, $t22) + 32: $t1 := $t23 + 33: $t24 := move($t3) + 34: $t25 := move($t2) + 35: $t26 := copy($t1) + 36: vector::swap<#0>($t24, $t25, $t26) + 37: goto 19 + 38: label L2 + 39: $t27 := move($t0) + 40: $t28 := vector::pop_back<#0>($t27) + 41: return $t28 } @@ -406,31 +736,29 @@ public fun vector::reverse<#0>($t0|v: &mut vector<#0>) { 17: $t14 := -($t12, $t13) 18: $t1 := $t14 19: goto 20 - 20: label L5 + 20: label L4 21: $t15 := copy($t2) 22: $t16 := copy($t1) 23: $t17 := <($t15, $t16) - 24: if ($t17) goto 25 else goto 41 + 24: if ($t17) goto 25 else goto 39 25: label L3 - 26: goto 27 - 27: label L4 - 28: $t18 := copy($t0) - 29: $t19 := copy($t2) - 30: $t20 := copy($t1) - 31: vector::swap<#0>($t18, $t19, $t20) - 32: $t21 := move($t2) - 33: $t22 := 1 - 34: $t23 := +($t21, $t22) - 35: $t2 := $t23 - 36: $t24 := move($t1) - 37: $t25 := 1 - 38: $t26 := -($t24, $t25) - 39: $t1 := $t26 - 40: goto 20 - 41: label L2 - 42: $t27 := move($t0) - 43: destroy($t27) - 44: return () + 26: $t18 := copy($t0) + 27: $t19 := copy($t2) + 28: $t20 := copy($t1) + 29: vector::swap<#0>($t18, $t19, $t20) + 30: $t21 := move($t2) + 31: $t22 := 1 + 32: $t23 := +($t21, $t22) + 33: $t2 := $t23 + 34: $t24 := move($t1) + 35: $t25 := 1 + 36: $t26 := -($t24, $t25) + 37: $t1 := $t26 + 38: goto 20 + 39: label L2 + 40: $t27 := move($t0) + 41: destroy($t27) + 42: return () } @@ -504,364 +832,3550 @@ public fun vector::swap_remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { [variant baseline] -fun MultiLayerCalling::inner($t0|has_vector: &mut MultiLayerCalling::HasVector): &mut MultiLayerCalling::HasAnotherVector { - var $t1: &mut MultiLayerCalling::HasVector - var $t2: &mut vector - var $t3: u64 - var $t4: &mut MultiLayerCalling::HasAnotherVector - 0: $t1 := move($t0) - 1: $t2 := borrow_field.v($t1) - 2: $t3 := 7 - 3: $t4 := vector::borrow_mut($t2, $t3) - 4: return $t4 +public fun option::borrow<#0>($t0|t: &option::Option<#0>): � { + var $t1: &option::Option<#0> + var $t2: bool + var $t3: &option::Option<#0> + var $t4: u64 + var $t5: &option::Option<#0> + var $t6: &vector<#0> + var $t7: u64 + var $t8: � + 0: $t1 := copy($t0) + 1: $t2 := option::is_some<#0>($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 10 + 5: label L0 + 6: $t3 := move($t0) + 7: destroy($t3) + 8: $t4 := 262145 + 9: abort($t4) + 10: label L2 + 11: $t5 := move($t0) + 12: $t6 := borrow_field>.vec($t5) + 13: $t7 := 0 + 14: $t8 := vector::borrow<#0>($t6, $t7) + 15: return $t8 } [variant baseline] -fun MultiLayerCalling::mid($t0|has_vector: &mut MultiLayerCalling::HasVector): &mut MultiLayerCalling::HasAnotherVector { - var $t1: &mut MultiLayerCalling::HasVector - var $t2: &mut MultiLayerCalling::HasAnotherVector - 0: $t1 := move($t0) - 1: $t2 := MultiLayerCalling::inner($t1) - 2: return $t2 +public fun option::borrow_mut<#0>($t0|t: &mut option::Option<#0>): &mut #0 { + var $t1: &mut option::Option<#0> + var $t2: &option::Option<#0> + var $t3: bool + var $t4: &mut option::Option<#0> + var $t5: u64 + var $t6: &mut option::Option<#0> + var $t7: &mut vector<#0> + var $t8: u64 + var $t9: &mut #0 + 0: $t1 := copy($t0) + 1: $t2 := freeze_ref($t1) + 2: $t3 := option::is_some<#0>($t2) + 3: if ($t3) goto 4 else goto 6 + 4: label L1 + 5: goto 11 + 6: label L0 + 7: $t4 := move($t0) + 8: destroy($t4) + 9: $t5 := 262145 + 10: abort($t5) + 11: label L2 + 12: $t6 := move($t0) + 13: $t7 := borrow_field>.vec($t6) + 14: $t8 := 0 + 15: $t9 := vector::borrow_mut<#0>($t7, $t8) + 16: return $t9 } [variant baseline] -fun MultiLayerCalling::outer($t0|has_vector: &mut MultiLayerCalling::HasVector) { - var $t1: &mut MultiLayerCalling::HasVector - var $t2: &mut MultiLayerCalling::HasAnotherVector - var $t3: &mut vector - var $t4: u8 - 0: $t1 := move($t0) - 1: $t2 := MultiLayerCalling::mid($t1) - 2: $t3 := borrow_field.v($t2) - 3: $t4 := 42 - 4: vector::push_back($t3, $t4) - 5: return () +public fun option::contains<#0>($t0|t: &option::Option<#0>, $t1|e_ref: �): bool { + var $t2: &option::Option<#0> + var $t3: &vector<#0> + var $t4: � + var $t5: bool + 0: $t2 := move($t0) + 1: $t3 := borrow_field>.vec($t2) + 2: $t4 := move($t1) + 3: $t5 := vector::contains<#0>($t3, $t4) + 4: return $t5 } -============ after pipeline `borrow` ================ [variant baseline] -public fun vector::append<#0>($t0|lhs: &mut vector<#0>, $t1|other: vector<#0>) { - var $t2: &mut vector<#0> - var $t3: vector<#0> - var $t4: bool - var $t5: bool - var $t6: &mut vector<#0> - var $t7: #0 - var $t8: vector<#0> - 0: $t2 := borrow_local($t1) - 1: vector::reverse<#0>($t2) - 2: label L3 - 3: $t3 := copy($t1) - 4: $t4 := vector::is_empty<#0>($t3) - 5: $t5 := !($t4) - 6: if ($t5) goto 7 else goto 13 - 7: label L1 - 8: label L2 - 9: $t6 := borrow_local($t1) - 10: $t7 := vector::pop_back<#0>($t6) - 11: vector::push_back<#0>($t0, $t7) - 12: goto 2 - 13: label L0 - 14: destroy($t0) - 15: $t8 := move($t1) - 16: vector::destroy_empty<#0>($t8) - 17: trace_local[lhs]($t0) - 18: return () +public fun option::swap<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0): #0 { + var $t2|old_value#1#0: #0 + var $t3|vec_ref#1#0: &mut vector<#0> + var $t4: &mut option::Option<#0> + var $t5: &option::Option<#0> + var $t6: bool + var $t7: &mut option::Option<#0> + var $t8: u64 + var $t9: &mut option::Option<#0> + var $t10: &mut vector<#0> + var $t11: &mut vector<#0> + var $t12: #0 + var $t13: &mut vector<#0> + var $t14: #0 + var $t15: #0 + 0: $t4 := copy($t0) + 1: $t5 := freeze_ref($t4) + 2: $t6 := option::is_some<#0>($t5) + 3: if ($t6) goto 4 else goto 6 + 4: label L1 + 5: goto 11 + 6: label L0 + 7: $t7 := move($t0) + 8: destroy($t7) + 9: $t8 := 262145 + 10: abort($t8) + 11: label L2 + 12: $t9 := move($t0) + 13: $t10 := borrow_field>.vec($t9) + 14: $t3 := $t10 + 15: $t11 := copy($t3) + 16: $t12 := vector::pop_back<#0>($t11) + 17: $t2 := $t12 + 18: $t13 := move($t3) + 19: $t14 := move($t1) + 20: vector::push_back<#0>($t13, $t14) + 21: $t15 := move($t2) + 22: return $t15 } [variant baseline] -public native fun vector::borrow<#0>($t0|v: vector<#0>, $t1|i: u64): #0; +public fun option::borrow_with_default<#0>($t0|t: &option::Option<#0>, $t1|default_ref: �): � { + var $t2|tmp#$2: � + var $t3|vec_ref#1#0: &vector<#0> + var $t4: &option::Option<#0> + var $t5: &vector<#0> + var $t6: &vector<#0> + var $t7: bool + var $t8: &vector<#0> + var $t9: � + var $t10: � + var $t11: &vector<#0> + var $t12: u64 + var $t13: � + var $t14: � + 0: $t4 := move($t0) + 1: $t5 := borrow_field>.vec($t4) + 2: $t3 := $t5 + 3: $t6 := copy($t3) + 4: $t7 := vector::is_empty<#0>($t6) + 5: if ($t7) goto 6 else goto 12 + 6: label L1 + 7: $t8 := move($t3) + 8: destroy($t8) + 9: $t9 := move($t1) + 10: $t2 := $t9 + 11: goto 20 + 12: label L0 + 13: $t10 := move($t1) + 14: destroy($t10) + 15: $t11 := move($t3) + 16: $t12 := 0 + 17: $t13 := vector::borrow<#0>($t11, $t12) + 18: $t2 := $t13 + 19: goto 20 + 20: label L2 + 21: $t14 := move($t2) + 22: return $t14 +} [variant baseline] -public native fun vector::borrow_mut<#0>($t0|v: &mut vector<#0>, $t1|i: u64): &mut #0; +public fun option::destroy_none<#0>($t0|t: option::Option<#0>) { + var $t1: &option::Option<#0> + var $t2: bool + var $t3: u64 + var $t4: option::Option<#0> + var $t5: vector<#0> + 0: $t1 := borrow_local($t0) + 1: $t2 := option::is_none<#0>($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t3 := 262144 + 7: abort($t3) + 8: label L2 + 9: $t4 := move($t0) + 10: $t5 := unpack option::Option<#0>($t4) + 11: vector::destroy_empty<#0>($t5) + 12: return () +} [variant baseline] -public fun vector::contains<#0>($t0|v: vector<#0>, $t1|e: #0): bool { - var $t2|i#1#0: u64 - var $t3|len#1#0: u64 - var $t4: u64 +public fun option::destroy_some<#0>($t0|t: option::Option<#0>): #0 { + var $t1|elem#1#0: #0 + var $t2|vec#1#0: vector<#0> + var $t3: &option::Option<#0> + var $t4: bool var $t5: u64 - var $t6: bool - var $t7: #0 - var $t8: bool - var $t9: bool - var $t10: u64 - var $t11: bool - 0: $t4 := 0 - 1: $t2 := $t4 - 2: $t5 := vector::length<#0>($t0) - 3: label L5 - 4: $t6 := <($t2, $t5) - 5: if ($t6) goto 6 else goto 18 - 6: label L1 - 7: label L2 - 8: $t7 := vector::borrow<#0>($t0, $t2) - 9: $t8 := ==($t7, $t1) - 10: if ($t8) goto 11 else goto 14 - 11: label L4 - 12: $t9 := true - 13: return $t9 - 14: label L3 - 15: $t10 := 1 - 16: $t2 := +($t2, $t10) - 17: goto 3 - 18: label L0 - 19: $t11 := false - 20: return $t11 + var $t6: option::Option<#0> + var $t7: vector<#0> + var $t8: &mut vector<#0> + var $t9: #0 + var $t10: vector<#0> + var $t11: #0 + 0: $t3 := borrow_local($t0) + 1: $t4 := option::is_some<#0>($t3) + 2: if ($t4) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t5 := 262145 + 7: abort($t5) + 8: label L2 + 9: $t6 := move($t0) + 10: $t7 := unpack option::Option<#0>($t6) + 11: $t2 := $t7 + 12: $t8 := borrow_local($t2) + 13: $t9 := vector::pop_back<#0>($t8) + 14: $t1 := $t9 + 15: $t10 := move($t2) + 16: vector::destroy_empty<#0>($t10) + 17: $t11 := move($t1) + 18: return $t11 } [variant baseline] -public native fun vector::destroy_empty<#0>($t0|v: vector<#0>); +public fun option::destroy_with_default<#0>($t0|t: option::Option<#0>, $t1|default: #0): #0 { + var $t2|tmp#$2: #0 + var $t3|vec#1#0: vector<#0> + var $t4: option::Option<#0> + var $t5: vector<#0> + var $t6: &vector<#0> + var $t7: bool + var $t8: #0 + var $t9: &mut vector<#0> + var $t10: #0 + var $t11: #0 + 0: $t4 := move($t0) + 1: $t5 := unpack option::Option<#0>($t4) + 2: $t3 := $t5 + 3: $t6 := borrow_local($t3) + 4: $t7 := vector::is_empty<#0>($t6) + 5: if ($t7) goto 6 else goto 10 + 6: label L1 + 7: $t8 := move($t1) + 8: $t2 := $t8 + 9: goto 15 + 10: label L0 + 11: $t9 := borrow_local($t3) + 12: $t10 := vector::pop_back<#0>($t9) + 13: $t2 := $t10 + 14: goto 15 + 15: label L2 + 16: $t11 := move($t2) + 17: return $t11 +} [variant baseline] -public native fun vector::empty<#0>(): vector<#0>; - +public fun option::extract<#0>($t0|t: &mut option::Option<#0>): #0 { + var $t1: &mut option::Option<#0> + var $t2: &option::Option<#0> + var $t3: bool + var $t4: &mut option::Option<#0> + var $t5: u64 + var $t6: &mut option::Option<#0> + var $t7: &mut vector<#0> + var $t8: #0 + 0: $t1 := copy($t0) + 1: $t2 := freeze_ref($t1) + 2: $t3 := option::is_some<#0>($t2) + 3: if ($t3) goto 4 else goto 6 + 4: label L1 + 5: goto 11 + 6: label L0 + 7: $t4 := move($t0) + 8: destroy($t4) + 9: $t5 := 262145 + 10: abort($t5) + 11: label L2 + 12: $t6 := move($t0) + 13: $t7 := borrow_field>.vec($t6) + 14: $t8 := vector::pop_back<#0>($t7) + 15: return $t8 +} + + +[variant baseline] +public fun option::fill<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0) { + var $t2|vec_ref#1#0: &mut vector<#0> + var $t3: &mut option::Option<#0> + var $t4: &mut vector<#0> + var $t5: &mut vector<#0> + var $t6: &vector<#0> + var $t7: bool + var $t8: &mut vector<#0> + var $t9: u64 + var $t10: &mut vector<#0> + var $t11: #0 + 0: $t3 := move($t0) + 1: $t4 := borrow_field>.vec($t3) + 2: $t2 := $t4 + 3: $t5 := copy($t2) + 4: $t6 := freeze_ref($t5) + 5: $t7 := vector::is_empty<#0>($t6) + 6: if ($t7) goto 7 else goto 9 + 7: label L1 + 8: goto 14 + 9: label L0 + 10: $t8 := move($t2) + 11: destroy($t8) + 12: $t9 := 262144 + 13: abort($t9) + 14: label L2 + 15: $t10 := move($t2) + 16: $t11 := move($t1) + 17: vector::push_back<#0>($t10, $t11) + 18: return () +} + + +[variant baseline] +public fun option::get_with_default<#0>($t0|t: &option::Option<#0>, $t1|default: #0): #0 { + var $t2|tmp#$2: #0 + var $t3|vec_ref#1#0: &vector<#0> + var $t4: &option::Option<#0> + var $t5: &vector<#0> + var $t6: &vector<#0> + var $t7: bool + var $t8: &vector<#0> + var $t9: #0 + var $t10: &vector<#0> + var $t11: u64 + var $t12: � + var $t13: #0 + var $t14: #0 + 0: $t4 := move($t0) + 1: $t5 := borrow_field>.vec($t4) + 2: $t3 := $t5 + 3: $t6 := copy($t3) + 4: $t7 := vector::is_empty<#0>($t6) + 5: if ($t7) goto 6 else goto 12 + 6: label L1 + 7: $t8 := move($t3) + 8: destroy($t8) + 9: $t9 := move($t1) + 10: $t2 := $t9 + 11: goto 19 + 12: label L0 + 13: $t10 := move($t3) + 14: $t11 := 0 + 15: $t12 := vector::borrow<#0>($t10, $t11) + 16: $t13 := read_ref($t12) + 17: $t2 := $t13 + 18: goto 19 + 19: label L2 + 20: $t14 := move($t2) + 21: return $t14 +} + + +[variant baseline] +public fun option::is_none<#0>($t0|t: &option::Option<#0>): bool { + var $t1: &option::Option<#0> + var $t2: &vector<#0> + var $t3: bool + 0: $t1 := move($t0) + 1: $t2 := borrow_field>.vec($t1) + 2: $t3 := vector::is_empty<#0>($t2) + 3: return $t3 +} + + +[variant baseline] +public fun option::is_some<#0>($t0|t: &option::Option<#0>): bool { + var $t1: &option::Option<#0> + var $t2: &vector<#0> + var $t3: bool + var $t4: bool + 0: $t1 := move($t0) + 1: $t2 := borrow_field>.vec($t1) + 2: $t3 := vector::is_empty<#0>($t2) + 3: $t4 := !($t3) + 4: return $t4 +} + + +[variant baseline] +public fun option::none<#0>(): option::Option<#0> { + var $t0: vector<#0> + var $t1: option::Option<#0> + 0: $t0 := vector::empty<#0>() + 1: $t1 := pack option::Option<#0>($t0) + 2: return $t1 +} + + +[variant baseline] +public fun option::some<#0>($t0|e: #0): option::Option<#0> { + var $t1: #0 + var $t2: vector<#0> + var $t3: option::Option<#0> + 0: $t1 := move($t0) + 1: $t2 := vector::singleton<#0>($t1) + 2: $t3 := pack option::Option<#0>($t2) + 3: return $t3 +} + + +[variant baseline] +public fun option::swap_or_fill<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0): option::Option<#0> { + var $t2|tmp#$2: option::Option<#0> + var $t3|old_value#1#0: option::Option<#0> + var $t4|vec_ref#1#0: &mut vector<#0> + var $t5: &mut option::Option<#0> + var $t6: &mut vector<#0> + var $t7: &mut vector<#0> + var $t8: &vector<#0> + var $t9: bool + var $t10: option::Option<#0> + var $t11: &mut vector<#0> + var $t12: #0 + var $t13: option::Option<#0> + var $t14: option::Option<#0> + var $t15: &mut vector<#0> + var $t16: #0 + var $t17: option::Option<#0> + 0: $t5 := move($t0) + 1: $t6 := borrow_field>.vec($t5) + 2: $t4 := $t6 + 3: $t7 := copy($t4) + 4: $t8 := freeze_ref($t7) + 5: $t9 := vector::is_empty<#0>($t8) + 6: if ($t9) goto 7 else goto 11 + 7: label L1 + 8: $t10 := option::none<#0>() + 9: $t2 := $t10 + 10: goto 17 + 11: label L0 + 12: $t11 := copy($t4) + 13: $t12 := vector::pop_back<#0>($t11) + 14: $t13 := option::some<#0>($t12) + 15: $t2 := $t13 + 16: goto 17 + 17: label L2 + 18: $t14 := move($t2) + 19: $t3 := $t14 + 20: $t15 := move($t4) + 21: $t16 := move($t1) + 22: vector::push_back<#0>($t15, $t16) + 23: $t17 := move($t3) + 24: return $t17 +} + + +[variant baseline] +public fun option::to_vec<#0>($t0|t: option::Option<#0>): vector<#0> { + var $t1: option::Option<#0> + var $t2: vector<#0> + 0: $t1 := move($t0) + 1: $t2 := unpack option::Option<#0>($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::append($t0|string: &mut ascii::String, $t1|other: ascii::String) { + var $t2: &mut ascii::String + var $t3: &mut vector + var $t4: ascii::String + var $t5: vector + 0: $t2 := move($t0) + 1: $t3 := borrow_field.bytes($t2) + 2: $t4 := move($t1) + 3: $t5 := ascii::into_bytes($t4) + 4: vector::append($t3, $t5) + 5: return () +} + + +[variant baseline] +public fun ascii::index_of($t0|string: &ascii::String, $t1|substr: &ascii::String): u64 { + var $t2|tmp#$2: bool + var $t3|i#1#0: u64 + var $t4|j#1#0: u64 + var $t5|m#1#0: u64 + var $t6|n#1#0: u64 + var $t7: u64 + var $t8: &ascii::String + var $t9: u64 + var $t10: &ascii::String + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: bool + var $t15: &ascii::String + var $t16: &ascii::String + var $t17: u64 + var $t18: u64 + var $t19: u64 + var $t20: u64 + var $t21: u64 + var $t22: bool + var $t23: u64 + var $t24: u64 + var $t25: u64 + var $t26: bool + var $t27: &ascii::String + var $t28: &vector + var $t29: u64 + var $t30: u64 + var $t31: u64 + var $t32: &u8 + var $t33: u8 + var $t34: &ascii::String + var $t35: &vector + var $t36: u64 + var $t37: &u8 + var $t38: u8 + var $t39: bool + var $t40: bool + var $t41: bool + var $t42: u64 + var $t43: u64 + var $t44: u64 + var $t45: u64 + var $t46: u64 + var $t47: bool + var $t48: &ascii::String + var $t49: &ascii::String + var $t50: u64 + var $t51: u64 + var $t52: u64 + var $t53: u64 + var $t54: &ascii::String + var $t55: &ascii::String + var $t56: u64 + 0: $t7 := 0 + 1: $t3 := $t7 + 2: $t8 := copy($t0) + 3: $t9 := ascii::length($t8) + 4: $t10 := copy($t1) + 5: $t11 := ascii::length($t10) + 6: $t5 := $t11 + 7: $t6 := $t9 + 8: $t12 := copy($t6) + 9: $t13 := copy($t5) + 10: $t14 := <($t12, $t13) + 11: if ($t14) goto 12 else goto 19 + 12: label L1 + 13: $t15 := move($t1) + 14: destroy($t15) + 15: $t16 := move($t0) + 16: destroy($t16) + 17: $t17 := move($t6) + 18: return $t17 + 19: label L0 + 20: $t18 := copy($t3) + 21: $t19 := copy($t6) + 22: $t20 := copy($t5) + 23: $t21 := -($t19, $t20) + 24: $t22 := <=($t18, $t21) + 25: if ($t22) goto 26 else goto 84 + 26: label L3 + 27: $t23 := 0 + 28: $t4 := $t23 + 29: goto 30 + 30: label L10 + 31: $t24 := copy($t4) + 32: $t25 := copy($t5) + 33: $t26 := <($t24, $t25) + 34: if ($t26) goto 35 else goto 53 + 35: label L5 + 36: goto 37 + 37: label L6 + 38: $t27 := copy($t0) + 39: $t28 := borrow_field.bytes($t27) + 40: $t29 := copy($t3) + 41: $t30 := copy($t4) + 42: $t31 := +($t29, $t30) + 43: $t32 := vector::borrow($t28, $t31) + 44: $t33 := read_ref($t32) + 45: $t34 := copy($t1) + 46: $t35 := borrow_field.bytes($t34) + 47: $t36 := copy($t4) + 48: $t37 := vector::borrow($t35, $t36) + 49: $t38 := read_ref($t37) + 50: $t39 := ==($t33, $t38) + 51: $t2 := $t39 + 52: goto 57 + 53: label L4 + 54: $t40 := false + 55: $t2 := $t40 + 56: goto 57 + 57: label L7 + 58: $t41 := move($t2) + 59: if ($t41) goto 60 else goto 66 + 60: label L9 + 61: $t42 := move($t4) + 62: $t43 := 1 + 63: $t44 := +($t42, $t43) + 64: $t4 := $t44 + 65: goto 30 + 66: label L8 + 67: $t45 := move($t4) + 68: $t46 := copy($t5) + 69: $t47 := ==($t45, $t46) + 70: if ($t47) goto 71 else goto 78 + 71: label L12 + 72: $t48 := move($t1) + 73: destroy($t48) + 74: $t49 := move($t0) + 75: destroy($t49) + 76: $t50 := move($t3) + 77: return $t50 + 78: label L11 + 79: $t51 := move($t3) + 80: $t52 := 1 + 81: $t53 := +($t51, $t52) + 82: $t3 := $t53 + 83: goto 19 + 84: label L2 + 85: $t54 := move($t1) + 86: destroy($t54) + 87: $t55 := move($t0) + 88: destroy($t55) + 89: $t56 := move($t6) + 90: return $t56 +} + + +[variant baseline] +public fun ascii::insert($t0|s: &mut ascii::String, $t1|at: u64, $t2|o: ascii::String) { + var $t3|e#1#2: u8 + var $t4|v#1#1: vector + var $t5: u64 + var $t6: &mut ascii::String + var $t7: &ascii::String + var $t8: u64 + var $t9: bool + var $t10: &mut ascii::String + var $t11: u64 + var $t12: ascii::String + var $t13: vector + var $t14: &vector + var $t15: bool + var $t16: bool + var $t17: &mut vector + var $t18: u8 + var $t19: &mut ascii::String + var $t20: &mut vector + var $t21: u8 + var $t22: u64 + var $t23: &mut ascii::String + var $t24: vector + 0: $t5 := copy($t1) + 1: $t6 := copy($t0) + 2: $t7 := freeze_ref($t6) + 3: $t8 := ascii::length($t7) + 4: $t9 := <=($t5, $t8) + 5: if ($t9) goto 6 else goto 8 + 6: label L1 + 7: goto 13 + 8: label L0 + 9: $t10 := move($t0) + 10: destroy($t10) + 11: $t11 := 65537 + 12: abort($t11) + 13: label L2 + 14: $t12 := move($t2) + 15: $t13 := ascii::into_bytes($t12) + 16: $t4 := $t13 + 17: goto 18 + 18: label L5 + 19: $t14 := borrow_local($t4) + 20: $t15 := vector::is_empty($t14) + 21: $t16 := !($t15) + 22: if ($t16) goto 23 else goto 33 + 23: label L4 + 24: $t17 := borrow_local($t4) + 25: $t18 := vector::pop_back($t17) + 26: $t3 := $t18 + 27: $t19 := copy($t0) + 28: $t20 := borrow_field.bytes($t19) + 29: $t21 := move($t3) + 30: $t22 := copy($t1) + 31: vector::insert($t20, $t21, $t22) + 32: goto 18 + 33: label L3 + 34: $t23 := move($t0) + 35: destroy($t23) + 36: $t24 := move($t4) + 37: vector::destroy_empty($t24) + 38: return () +} + + +[variant baseline] +public fun ascii::is_empty($t0|string: &ascii::String): bool { + var $t1: &ascii::String + var $t2: &vector + var $t3: bool + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: $t3 := vector::is_empty($t2) + 3: return $t3 +} + + +[variant baseline] +public fun ascii::length($t0|string: &ascii::String): u64 { + var $t1: &ascii::String + var $t2: &vector + var $t3: u64 + 0: $t1 := move($t0) + 1: $t2 := ascii::as_bytes($t1) + 2: $t3 := vector::length($t2) + 3: return $t3 +} + + +[variant baseline] +public fun ascii::all_characters_printable($t0|string: &ascii::String): bool { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: bool + var $t3|i#1#12: u64 + var $t4|i#1#9: u64 + var $t5|stop#1#9: u64 + var $t6|v#1#3: &vector + var $t7: &ascii::String + var $t8: &vector + var $t9: &vector + var $t10: u64 + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: bool + var $t16: u64 + var $t17: &vector + var $t18: u64 + var $t19: &u8 + var $t20: u8 + var $t21: bool + var $t22: bool + var $t23: &vector + var $t24: bool + var $t25: u64 + var $t26: u64 + var $t27: u64 + var $t28: &vector + var $t29: bool + var $t30: bool + 0: $t7 := move($t0) + 1: $t8 := borrow_field.bytes($t7) + 2: $t6 := $t8 + 3: $t9 := copy($t6) + 4: $t10 := vector::length($t9) + 5: $t1 := $t10 + 6: $t11 := 0 + 7: $t4 := $t11 + 8: $t12 := move($t1) + 9: $t5 := $t12 + 10: goto 11 + 11: label L5 + 12: $t13 := copy($t4) + 13: $t14 := copy($t5) + 14: $t15 := <($t13, $t14) + 15: if ($t15) goto 16 else goto 38 + 16: label L1 + 17: $t16 := copy($t4) + 18: $t3 := $t16 + 19: $t17 := copy($t6) + 20: $t18 := move($t3) + 21: $t19 := vector::borrow($t17, $t18) + 22: $t20 := read_ref($t19) + 23: $t21 := ascii::is_printable_char($t20) + 24: $t22 := !($t21) + 25: if ($t22) goto 26 else goto 32 + 26: label L3 + 27: $t23 := move($t6) + 28: destroy($t23) + 29: $t24 := false + 30: $t2 := $t24 + 31: goto 44 + 32: label L2 + 33: $t25 := move($t4) + 34: $t26 := 1 + 35: $t27 := +($t25, $t26) + 36: $t4 := $t27 + 37: goto 11 + 38: label L0 + 39: $t28 := move($t6) + 40: destroy($t28) + 41: $t29 := true + 42: $t2 := $t29 + 43: goto 44 + 44: label L4 + 45: $t30 := move($t2) + 46: return $t30 +} + + +[variant baseline] +public fun ascii::string($t0|bytes: vector): ascii::String { + var $t1|x#1#0: option::Option + var $t2: vector + var $t3: option::Option + var $t4: &option::Option + var $t5: bool + var $t6: u64 + var $t7: option::Option + var $t8: ascii::String + 0: $t2 := move($t0) + 1: $t3 := ascii::try_string($t2) + 2: $t1 := $t3 + 3: $t4 := borrow_local($t1) + 4: $t5 := option::is_some($t4) + 5: if ($t5) goto 6 else goto 8 + 6: label L1 + 7: goto 11 + 8: label L0 + 9: $t6 := 65536 + 10: abort($t6) + 11: label L2 + 12: $t7 := move($t1) + 13: $t8 := option::destroy_some($t7) + 14: return $t8 +} + + +[variant baseline] +public fun ascii::as_bytes($t0|string: &ascii::String): &vector { + var $t1: &ascii::String + var $t2: &vector + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::byte($t0|char: ascii::Char): u8 { + var $t1: ascii::Char + var $t2: u8 + 0: $t1 := move($t0) + 1: $t2 := unpack ascii::Char($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::char($t0|byte: u8): ascii::Char { + var $t1: u8 + var $t2: bool + var $t3: u64 + var $t4: u8 + var $t5: ascii::Char + 0: $t1 := copy($t0) + 1: $t2 := ascii::is_valid_char($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t3 := 65536 + 7: abort($t3) + 8: label L2 + 9: $t4 := move($t0) + 10: $t5 := pack ascii::Char($t4) + 11: return $t5 +} + + +[variant baseline] +fun ascii::char_to_lowercase($t0|byte: u8): u8 { + var $t1|tmp#$1: bool + var $t2|tmp#$2: u8 + var $t3: u8 + var $t4: u8 + var $t5: bool + var $t6: u8 + var $t7: u8 + var $t8: bool + var $t9: bool + var $t10: bool + var $t11: u8 + var $t12: u8 + var $t13: u8 + var $t14: u8 + var $t15: u8 + 0: $t3 := copy($t0) + 1: $t4 := 65 + 2: $t5 := >=($t3, $t4) + 3: if ($t5) goto 4 else goto 10 + 4: label L1 + 5: $t6 := copy($t0) + 6: $t7 := 90 + 7: $t8 := <=($t6, $t7) + 8: $t1 := $t8 + 9: goto 14 + 10: label L0 + 11: $t9 := false + 12: $t1 := $t9 + 13: goto 14 + 14: label L2 + 15: $t10 := move($t1) + 16: if ($t10) goto 17 else goto 23 + 17: label L4 + 18: $t11 := move($t0) + 19: $t12 := 32 + 20: $t13 := +($t11, $t12) + 21: $t2 := $t13 + 22: goto 27 + 23: label L3 + 24: $t14 := move($t0) + 25: $t2 := $t14 + 26: goto 27 + 27: label L5 + 28: $t15 := move($t2) + 29: return $t15 +} + + +[variant baseline] +fun ascii::char_to_uppercase($t0|byte: u8): u8 { + var $t1|tmp#$1: bool + var $t2|tmp#$2: u8 + var $t3: u8 + var $t4: u8 + var $t5: bool + var $t6: u8 + var $t7: u8 + var $t8: bool + var $t9: bool + var $t10: bool + var $t11: u8 + var $t12: u8 + var $t13: u8 + var $t14: u8 + var $t15: u8 + 0: $t3 := copy($t0) + 1: $t4 := 97 + 2: $t5 := >=($t3, $t4) + 3: if ($t5) goto 4 else goto 10 + 4: label L1 + 5: $t6 := copy($t0) + 6: $t7 := 122 + 7: $t8 := <=($t6, $t7) + 8: $t1 := $t8 + 9: goto 14 + 10: label L0 + 11: $t9 := false + 12: $t1 := $t9 + 13: goto 14 + 14: label L2 + 15: $t10 := move($t1) + 16: if ($t10) goto 17 else goto 23 + 17: label L4 + 18: $t11 := move($t0) + 19: $t12 := 32 + 20: $t13 := -($t11, $t12) + 21: $t2 := $t13 + 22: goto 27 + 23: label L3 + 24: $t14 := move($t0) + 25: $t2 := $t14 + 26: goto 27 + 27: label L5 + 28: $t15 := move($t2) + 29: return $t15 +} + + +[variant baseline] +public fun ascii::into_bytes($t0|string: ascii::String): vector { + var $t1: ascii::String + var $t2: vector + 0: $t1 := move($t0) + 1: $t2 := unpack ascii::String($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::is_printable_char($t0|byte: u8): bool { + var $t1|tmp#$1: bool + var $t2: u8 + var $t3: u8 + var $t4: bool + var $t5: u8 + var $t6: u8 + var $t7: bool + var $t8: bool + var $t9: bool + 0: $t2 := copy($t0) + 1: $t3 := 32 + 2: $t4 := >=($t2, $t3) + 3: if ($t4) goto 4 else goto 10 + 4: label L1 + 5: $t5 := move($t0) + 6: $t6 := 126 + 7: $t7 := <=($t5, $t6) + 8: $t1 := $t7 + 9: goto 14 + 10: label L0 + 11: $t8 := false + 12: $t1 := $t8 + 13: goto 14 + 14: label L2 + 15: $t9 := move($t1) + 16: return $t9 +} + + +[variant baseline] +public fun ascii::is_valid_char($t0|b: u8): bool { + var $t1: u8 + var $t2: u8 + var $t3: bool + 0: $t1 := move($t0) + 1: $t2 := 127 + 2: $t3 := <=($t1, $t2) + 3: return $t3 +} + + +[variant baseline] +public fun ascii::pop_char($t0|string: &mut ascii::String): ascii::Char { + var $t1: &mut ascii::String + var $t2: &mut vector + var $t3: u8 + var $t4: ascii::Char + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: $t3 := vector::pop_back($t2) + 3: $t4 := pack ascii::Char($t3) + 4: return $t4 +} + + +[variant baseline] +public fun ascii::push_char($t0|string: &mut ascii::String, $t1|char: ascii::Char) { + var $t2: &mut ascii::String + var $t3: &mut vector + var $t4: &ascii::Char + var $t5: &u8 + var $t6: u8 + 0: $t2 := move($t0) + 1: $t3 := borrow_field.bytes($t2) + 2: $t4 := borrow_local($t1) + 3: $t5 := borrow_field.byte($t4) + 4: $t6 := read_ref($t5) + 5: vector::push_back($t3, $t6) + 6: return () +} + + +[variant baseline] +public fun ascii::substring($t0|string: &ascii::String, $t1|i: u64, $t2|j: u64): ascii::String { + var $t3|tmp#$3: bool + var $t4|bytes#1#0: vector + var $t5|i#1#3: u64 + var $t6|i#1#6: u64 + var $t7|stop#1#3: u64 + var $t8: u64 + var $t9: u64 + var $t10: bool + var $t11: u64 + var $t12: &ascii::String + var $t13: u64 + var $t14: bool + var $t15: bool + var $t16: bool + var $t17: &ascii::String + var $t18: u64 + var $t19: vector + var $t20: u64 + var $t21: u64 + var $t22: u64 + var $t23: u64 + var $t24: bool + var $t25: u64 + var $t26: &mut vector + var $t27: &ascii::String + var $t28: &vector + var $t29: u64 + var $t30: &u8 + var $t31: u8 + var $t32: u64 + var $t33: u64 + var $t34: u64 + var $t35: &ascii::String + var $t36: vector + var $t37: ascii::String + 0: $t8 := copy($t1) + 1: $t9 := copy($t2) + 2: $t10 := <=($t8, $t9) + 3: if ($t10) goto 4 else goto 11 + 4: label L1 + 5: $t11 := copy($t2) + 6: $t12 := copy($t0) + 7: $t13 := ascii::length($t12) + 8: $t14 := <=($t11, $t13) + 9: $t3 := $t14 + 10: goto 15 + 11: label L0 + 12: $t15 := false + 13: $t3 := $t15 + 14: goto 15 + 15: label L2 + 16: $t16 := move($t3) + 17: if ($t16) goto 18 else goto 20 + 18: label L4 + 19: goto 25 + 20: label L3 + 21: $t17 := move($t0) + 22: destroy($t17) + 23: $t18 := 65537 + 24: abort($t18) + 25: label L5 + 26: $t19 := [] + 27: $t4 := $t19 + 28: $t20 := move($t1) + 29: $t5 := $t20 + 30: $t21 := move($t2) + 31: $t7 := $t21 + 32: goto 33 + 33: label L8 + 34: $t22 := copy($t5) + 35: $t23 := copy($t7) + 36: $t24 := <($t22, $t23) + 37: if ($t24) goto 38 else goto 53 + 38: label L7 + 39: $t25 := copy($t5) + 40: $t6 := $t25 + 41: $t26 := borrow_local($t4) + 42: $t27 := copy($t0) + 43: $t28 := borrow_field.bytes($t27) + 44: $t29 := move($t6) + 45: $t30 := vector::borrow($t28, $t29) + 46: $t31 := read_ref($t30) + 47: vector::push_back($t26, $t31) + 48: $t32 := move($t5) + 49: $t33 := 1 + 50: $t34 := +($t32, $t33) + 51: $t5 := $t34 + 52: goto 33 + 53: label L6 + 54: $t35 := move($t0) + 55: destroy($t35) + 56: $t36 := move($t4) + 57: $t37 := pack ascii::String($t36) + 58: return $t37 +} + + +[variant baseline] +public fun ascii::to_lowercase($t0|string: &ascii::String): ascii::String { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: u8 + var $t3|tmp#$3: &mut vector + var $t4|e#1#13: &u8 + var $t5|i#1#12: u64 + var $t6|i#1#9: u64 + var $t7|r#1#1: vector + var $t8|stop#1#9: u64 + var $t9|v#1#1: &vector + var $t10|v#1#3: &vector + var $t11: &ascii::String + var $t12: &vector + var $t13: vector + var $t14: &vector + var $t15: &vector + var $t16: u64 + var $t17: u64 + var $t18: u64 + var $t19: u64 + var $t20: u64 + var $t21: bool + var $t22: u64 + var $t23: &vector + var $t24: u64 + var $t25: &u8 + var $t26: &mut vector + var $t27: &u8 + var $t28: u8 + var $t29: u8 + var $t30: &mut vector + var $t31: u8 + var $t32: u64 + var $t33: u64 + var $t34: u64 + var $t35: &vector + var $t36: vector + var $t37: ascii::String + 0: $t11 := move($t0) + 1: $t12 := ascii::as_bytes($t11) + 2: $t9 := $t12 + 3: $t13 := [] + 4: $t7 := $t13 + 5: $t14 := move($t9) + 6: $t10 := $t14 + 7: $t15 := copy($t10) + 8: $t16 := vector::length($t15) + 9: $t1 := $t16 + 10: $t17 := 0 + 11: $t6 := $t17 + 12: $t18 := move($t1) + 13: $t8 := $t18 + 14: goto 15 + 15: label L2 + 16: $t19 := copy($t6) + 17: $t20 := copy($t8) + 18: $t21 := <($t19, $t20) + 19: if ($t21) goto 20 else goto 41 + 20: label L1 + 21: $t22 := copy($t6) + 22: $t5 := $t22 + 23: $t23 := copy($t10) + 24: $t24 := move($t5) + 25: $t25 := vector::borrow($t23, $t24) + 26: $t4 := $t25 + 27: $t26 := borrow_local($t7) + 28: $t3 := $t26 + 29: $t27 := move($t4) + 30: $t28 := read_ref($t27) + 31: $t29 := ascii::char_to_lowercase($t28) + 32: $t2 := $t29 + 33: $t30 := move($t3) + 34: $t31 := move($t2) + 35: vector::push_back($t30, $t31) + 36: $t32 := move($t6) + 37: $t33 := 1 + 38: $t34 := +($t32, $t33) + 39: $t6 := $t34 + 40: goto 15 + 41: label L0 + 42: $t35 := move($t10) + 43: destroy($t35) + 44: $t36 := move($t7) + 45: $t37 := pack ascii::String($t36) + 46: return $t37 +} + + +[variant baseline] +public fun ascii::to_uppercase($t0|string: &ascii::String): ascii::String { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: u8 + var $t3|tmp#$3: &mut vector + var $t4|e#1#13: &u8 + var $t5|i#1#12: u64 + var $t6|i#1#9: u64 + var $t7|r#1#1: vector + var $t8|stop#1#9: u64 + var $t9|v#1#1: &vector + var $t10|v#1#3: &vector + var $t11: &ascii::String + var $t12: &vector + var $t13: vector + var $t14: &vector + var $t15: &vector + var $t16: u64 + var $t17: u64 + var $t18: u64 + var $t19: u64 + var $t20: u64 + var $t21: bool + var $t22: u64 + var $t23: &vector + var $t24: u64 + var $t25: &u8 + var $t26: &mut vector + var $t27: &u8 + var $t28: u8 + var $t29: u8 + var $t30: &mut vector + var $t31: u8 + var $t32: u64 + var $t33: u64 + var $t34: u64 + var $t35: &vector + var $t36: vector + var $t37: ascii::String + 0: $t11 := move($t0) + 1: $t12 := ascii::as_bytes($t11) + 2: $t9 := $t12 + 3: $t13 := [] + 4: $t7 := $t13 + 5: $t14 := move($t9) + 6: $t10 := $t14 + 7: $t15 := copy($t10) + 8: $t16 := vector::length($t15) + 9: $t1 := $t16 + 10: $t17 := 0 + 11: $t6 := $t17 + 12: $t18 := move($t1) + 13: $t8 := $t18 + 14: goto 15 + 15: label L2 + 16: $t19 := copy($t6) + 17: $t20 := copy($t8) + 18: $t21 := <($t19, $t20) + 19: if ($t21) goto 20 else goto 41 + 20: label L1 + 21: $t22 := copy($t6) + 22: $t5 := $t22 + 23: $t23 := copy($t10) + 24: $t24 := move($t5) + 25: $t25 := vector::borrow($t23, $t24) + 26: $t4 := $t25 + 27: $t26 := borrow_local($t7) + 28: $t3 := $t26 + 29: $t27 := move($t4) + 30: $t28 := read_ref($t27) + 31: $t29 := ascii::char_to_uppercase($t28) + 32: $t2 := $t29 + 33: $t30 := move($t3) + 34: $t31 := move($t2) + 35: vector::push_back($t30, $t31) + 36: $t32 := move($t6) + 37: $t33 := 1 + 38: $t34 := +($t32, $t33) + 39: $t6 := $t34 + 40: goto 15 + 41: label L0 + 42: $t35 := move($t10) + 43: destroy($t35) + 44: $t36 := move($t7) + 45: $t37 := pack ascii::String($t36) + 46: return $t37 +} + + +[variant baseline] +public fun ascii::try_string($t0|bytes: vector): option::Option { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: bool + var $t3|tmp#$3: option::Option + var $t4|i#1#12: u64 + var $t5|i#1#9: u64 + var $t6|stop#1#9: u64 + var $t7|v#1#3: &vector + var $t8: &vector + var $t9: &vector + var $t10: u64 + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: bool + var $t16: u64 + var $t17: &vector + var $t18: u64 + var $t19: &u8 + var $t20: u8 + var $t21: bool + var $t22: bool + var $t23: &vector + var $t24: bool + var $t25: u64 + var $t26: u64 + var $t27: u64 + var $t28: &vector + var $t29: bool + var $t30: bool + var $t31: vector + var $t32: ascii::String + var $t33: option::Option + var $t34: option::Option + var $t35: option::Option + 0: $t8 := borrow_local($t0) + 1: $t7 := $t8 + 2: $t9 := copy($t7) + 3: $t10 := vector::length($t9) + 4: $t1 := $t10 + 5: $t11 := 0 + 6: $t5 := $t11 + 7: $t12 := move($t1) + 8: $t6 := $t12 + 9: goto 10 + 10: label L5 + 11: $t13 := copy($t5) + 12: $t14 := copy($t6) + 13: $t15 := <($t13, $t14) + 14: if ($t15) goto 15 else goto 37 + 15: label L1 + 16: $t16 := copy($t5) + 17: $t4 := $t16 + 18: $t17 := copy($t7) + 19: $t18 := move($t4) + 20: $t19 := vector::borrow($t17, $t18) + 21: $t20 := read_ref($t19) + 22: $t21 := ascii::is_valid_char($t20) + 23: $t22 := !($t21) + 24: if ($t22) goto 25 else goto 31 + 25: label L3 + 26: $t23 := move($t7) + 27: destroy($t23) + 28: $t24 := false + 29: $t2 := $t24 + 30: goto 43 + 31: label L2 + 32: $t25 := move($t5) + 33: $t26 := 1 + 34: $t27 := +($t25, $t26) + 35: $t5 := $t27 + 36: goto 10 + 37: label L0 + 38: $t28 := move($t7) + 39: destroy($t28) + 40: $t29 := true + 41: $t2 := $t29 + 42: goto 43 + 43: label L4 + 44: $t30 := move($t2) + 45: if ($t30) goto 46 else goto 52 + 46: label L7 + 47: $t31 := move($t0) + 48: $t32 := pack ascii::String($t31) + 49: $t33 := option::some($t32) + 50: $t3 := $t33 + 51: goto 56 + 52: label L6 + 53: $t34 := option::none() + 54: $t3 := $t34 + 55: goto 56 + 56: label L8 + 57: $t35 := move($t3) + 58: return $t35 +} + + +[variant baseline] +public fun string::append($t0|s: &mut string::String, $t1|r: string::String) { + var $t2: &mut string::String + var $t3: &mut vector + var $t4: &string::String + var $t5: &vector + var $t6: vector + 0: $t2 := move($t0) + 1: $t3 := borrow_field.bytes($t2) + 2: $t4 := borrow_local($t1) + 3: $t5 := borrow_field.bytes($t4) + 4: $t6 := read_ref($t5) + 5: vector::append($t3, $t6) + 6: return () +} + + +[variant baseline] +public fun string::index_of($t0|s: &string::String, $t1|r: &string::String): u64 { + var $t2: &string::String + var $t3: &vector + var $t4: &string::String + var $t5: &vector + var $t6: u64 + 0: $t2 := move($t0) + 1: $t3 := borrow_field.bytes($t2) + 2: $t4 := move($t1) + 3: $t5 := borrow_field.bytes($t4) + 4: $t6 := string::internal_index_of($t3, $t5) + 5: return $t6 +} + + +[variant baseline] +public fun string::insert($t0|s: &mut string::String, $t1|at: u64, $t2|o: string::String) { + var $t3|tmp#$3: bool + var $t4|bytes#1#0: &vector + var $t5|end#1#0: string::String + var $t6|front#1#0: string::String + var $t7|l#1#0: u64 + var $t8: &mut string::String + var $t9: &vector + var $t10: u64 + var $t11: &vector + var $t12: u64 + var $t13: bool + var $t14: &vector + var $t15: u64 + var $t16: bool + var $t17: &vector + var $t18: bool + var $t19: bool + var $t20: &mut string::String + var $t21: u64 + var $t22: &mut string::String + var $t23: &string::String + var $t24: u64 + var $t25: &mut string::String + var $t26: &string::String + var $t27: u64 + var $t28: u64 + var $t29: string::String + var $t30: &mut string::String + var $t31: &string::String + var $t32: u64 + var $t33: u64 + var $t34: string::String + var $t35: &mut string::String + var $t36: string::String + var $t37: &mut string::String + var $t38: string::String + var $t39: string::String + var $t40: &mut string::String + 0: $t8 := copy($t0) + 1: $t9 := borrow_field.bytes($t8) + 2: $t4 := $t9 + 3: $t10 := copy($t1) + 4: $t11 := copy($t4) + 5: $t12 := vector::length($t11) + 6: $t13 := <=($t10, $t12) + 7: if ($t13) goto 8 else goto 14 + 8: label L1 + 9: $t14 := move($t4) + 10: $t15 := copy($t1) + 11: $t16 := string::internal_is_char_boundary($t14, $t15) + 12: $t3 := $t16 + 13: goto 20 + 14: label L0 + 15: $t17 := move($t4) + 16: destroy($t17) + 17: $t18 := false + 18: $t3 := $t18 + 19: goto 20 + 20: label L2 + 21: $t19 := move($t3) + 22: if ($t19) goto 23 else goto 25 + 23: label L4 + 24: goto 30 + 25: label L3 + 26: $t20 := move($t0) + 27: destroy($t20) + 28: $t21 := 2 + 29: abort($t21) + 30: label L5 + 31: $t22 := copy($t0) + 32: $t23 := freeze_ref($t22) + 33: $t24 := string::length($t23) + 34: $t7 := $t24 + 35: $t25 := copy($t0) + 36: $t26 := freeze_ref($t25) + 37: $t27 := 0 + 38: $t28 := copy($t1) + 39: $t29 := string::substring($t26, $t27, $t28) + 40: $t6 := $t29 + 41: $t30 := copy($t0) + 42: $t31 := freeze_ref($t30) + 43: $t32 := move($t1) + 44: $t33 := move($t7) + 45: $t34 := string::substring($t31, $t32, $t33) + 46: $t5 := $t34 + 47: $t35 := borrow_local($t6) + 48: $t36 := move($t2) + 49: string::append($t35, $t36) + 50: $t37 := borrow_local($t6) + 51: $t38 := move($t5) + 52: string::append($t37, $t38) + 53: $t39 := move($t6) + 54: $t40 := move($t0) + 55: write_ref($t40, $t39) + 56: return () +} + + +[variant baseline] +public fun string::is_empty($t0|s: &string::String): bool { + var $t1: &string::String + var $t2: &vector + var $t3: bool + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: $t3 := vector::is_empty($t2) + 3: return $t3 +} + + +[variant baseline] +public fun string::length($t0|s: &string::String): u64 { + var $t1: &string::String + var $t2: &vector + var $t3: u64 + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: $t3 := vector::length($t2) + 3: return $t3 +} + + +[variant baseline] +public fun string::as_bytes($t0|s: &string::String): &vector { + var $t1: &string::String + var $t2: &vector + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: return $t2 +} + + +[variant baseline] +public fun string::into_bytes($t0|s: string::String): vector { + var $t1: string::String + var $t2: vector + 0: $t1 := move($t0) + 1: $t2 := unpack string::String($t1) + 2: return $t2 +} + + +[variant baseline] +public fun string::bytes($t0|s: &string::String): &vector { + var $t1: &string::String + var $t2: &vector + 0: $t1 := move($t0) + 1: $t2 := string::as_bytes($t1) + 2: return $t2 +} + + +[variant baseline] +public fun string::substring($t0|s: &string::String, $t1|i: u64, $t2|j: u64): string::String { + var $t3|tmp#$3: bool + var $t4|tmp#$4: bool + var $t5|tmp#$5: bool + var $t6|bytes#1#0: &vector + var $t7|l#1#0: u64 + var $t8: &string::String + var $t9: &vector + var $t10: &vector + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: bool + var $t15: u64 + var $t16: u64 + var $t17: bool + var $t18: bool + var $t19: bool + var $t20: &vector + var $t21: u64 + var $t22: bool + var $t23: bool + var $t24: bool + var $t25: &vector + var $t26: u64 + var $t27: bool + var $t28: bool + var $t29: bool + var $t30: &vector + var $t31: u64 + var $t32: &vector + var $t33: u64 + var $t34: u64 + var $t35: vector + var $t36: string::String + 0: $t8 := move($t0) + 1: $t9 := borrow_field.bytes($t8) + 2: $t6 := $t9 + 3: $t10 := copy($t6) + 4: $t11 := vector::length($t10) + 5: $t7 := $t11 + 6: $t12 := copy($t2) + 7: $t13 := move($t7) + 8: $t14 := <=($t12, $t13) + 9: if ($t14) goto 10 else goto 16 + 10: label L1 + 11: $t15 := copy($t1) + 12: $t16 := copy($t2) + 13: $t17 := <=($t15, $t16) + 14: $t3 := $t17 + 15: goto 20 + 16: label L0 + 17: $t18 := false + 18: $t3 := $t18 + 19: goto 20 + 20: label L2 + 21: $t19 := move($t3) + 22: if ($t19) goto 23 else goto 29 + 23: label L4 + 24: $t20 := copy($t6) + 25: $t21 := copy($t1) + 26: $t22 := string::internal_is_char_boundary($t20, $t21) + 27: $t4 := $t22 + 28: goto 33 + 29: label L3 + 30: $t23 := false + 31: $t4 := $t23 + 32: goto 33 + 33: label L5 + 34: $t24 := move($t4) + 35: if ($t24) goto 36 else goto 42 + 36: label L7 + 37: $t25 := copy($t6) + 38: $t26 := copy($t2) + 39: $t27 := string::internal_is_char_boundary($t25, $t26) + 40: $t5 := $t27 + 41: goto 46 + 42: label L6 + 43: $t28 := false + 44: $t5 := $t28 + 45: goto 46 + 46: label L8 + 47: $t29 := move($t5) + 48: if ($t29) goto 49 else goto 51 + 49: label L10 + 50: goto 56 + 51: label L9 + 52: $t30 := move($t6) + 53: destroy($t30) + 54: $t31 := 2 + 55: abort($t31) + 56: label L11 + 57: $t32 := move($t6) + 58: $t33 := move($t1) + 59: $t34 := move($t2) + 60: $t35 := string::internal_sub_string($t32, $t33, $t34) + 61: $t36 := pack string::String($t35) + 62: return $t36 +} + + +[variant baseline] +public fun string::append_utf8($t0|s: &mut string::String, $t1|bytes: vector) { + var $t2: &mut string::String + var $t3: vector + var $t4: string::String + 0: $t2 := move($t0) + 1: $t3 := move($t1) + 2: $t4 := string::utf8($t3) + 3: string::append($t2, $t4) + 4: return () +} + + +[variant baseline] +public fun string::from_ascii($t0|s: ascii::String): string::String { + var $t1: ascii::String + var $t2: vector + var $t3: string::String + 0: $t1 := move($t0) + 1: $t2 := ascii::into_bytes($t1) + 2: $t3 := pack string::String($t2) + 3: return $t3 +} + + +[variant baseline] +native fun string::internal_check_utf8($t0|v: &vector): bool; + + +[variant baseline] +native fun string::internal_index_of($t0|v: &vector, $t1|r: &vector): u64; + + +[variant baseline] +native fun string::internal_is_char_boundary($t0|v: &vector, $t1|i: u64): bool; + + +[variant baseline] +native fun string::internal_sub_string($t0|v: &vector, $t1|i: u64, $t2|j: u64): vector; + + +[variant baseline] +public fun string::sub_string($t0|s: &string::String, $t1|i: u64, $t2|j: u64): string::String { + var $t3: &string::String + var $t4: u64 + var $t5: u64 + var $t6: string::String + 0: $t3 := move($t0) + 1: $t4 := move($t1) + 2: $t5 := move($t2) + 3: $t6 := string::substring($t3, $t4, $t5) + 4: return $t6 +} + + +[variant baseline] +public fun string::to_ascii($t0|s: string::String): ascii::String { + var $t1: string::String + var $t2: vector + var $t3: ascii::String + 0: $t1 := move($t0) + 1: $t2 := unpack string::String($t1) + 2: $t3 := ascii::string($t2) + 3: return $t3 +} + + +[variant baseline] +public fun string::try_utf8($t0|bytes: vector): option::Option { + var $t1|tmp#$1: option::Option + var $t2: &vector + var $t3: bool + var $t4: vector + var $t5: string::String + var $t6: option::Option + var $t7: option::Option + var $t8: option::Option + 0: $t2 := borrow_local($t0) + 1: $t3 := string::internal_check_utf8($t2) + 2: if ($t3) goto 3 else goto 9 + 3: label L1 + 4: $t4 := move($t0) + 5: $t5 := pack string::String($t4) + 6: $t6 := option::some($t5) + 7: $t1 := $t6 + 8: goto 13 + 9: label L0 + 10: $t7 := option::none() + 11: $t1 := $t7 + 12: goto 13 + 13: label L2 + 14: $t8 := move($t1) + 15: return $t8 +} + + +[variant baseline] +public fun string::utf8($t0|bytes: vector): string::String { + var $t1: &vector + var $t2: bool + var $t3: u64 + var $t4: vector + var $t5: string::String + 0: $t1 := borrow_local($t0) + 1: $t2 := string::internal_check_utf8($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t3 := 1 + 7: abort($t3) + 8: label L2 + 9: $t4 := move($t0) + 10: $t5 := pack string::String($t4) + 11: return $t5 +} + + +[variant baseline] +fun MultiLayerCalling::inner($t0|has_vector: &mut MultiLayerCalling::HasVector): &mut MultiLayerCalling::HasAnotherVector { + var $t1: &mut MultiLayerCalling::HasVector + var $t2: &mut vector + var $t3: u64 + var $t4: &mut MultiLayerCalling::HasAnotherVector + 0: $t1 := move($t0) + 1: $t2 := borrow_field.v($t1) + 2: $t3 := 7 + 3: $t4 := vector::borrow_mut($t2, $t3) + 4: return $t4 +} + + +[variant baseline] +fun MultiLayerCalling::mid($t0|has_vector: &mut MultiLayerCalling::HasVector): &mut MultiLayerCalling::HasAnotherVector { + var $t1: &mut MultiLayerCalling::HasVector + var $t2: &mut MultiLayerCalling::HasAnotherVector + 0: $t1 := move($t0) + 1: $t2 := MultiLayerCalling::inner($t1) + 2: return $t2 +} + + +[variant baseline] +fun MultiLayerCalling::outer($t0|has_vector: &mut MultiLayerCalling::HasVector) { + var $t1: &mut MultiLayerCalling::HasVector + var $t2: &mut MultiLayerCalling::HasAnotherVector + var $t3: &mut vector + var $t4: u8 + 0: $t1 := move($t0) + 1: $t2 := MultiLayerCalling::mid($t1) + 2: $t3 := borrow_field.v($t2) + 3: $t4 := 42 + 4: vector::push_back($t3, $t4) + 5: return () +} + +============ after pipeline `borrow` ================ + +[variant baseline] +public fun u64::diff($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: bool + 0: $t5 := >($t0, $t1) + 1: if ($t5) goto 2 else goto 5 + 2: label L1 + 3: $t2 := -($t0, $t1) + 4: goto 7 + 5: label L0 + 6: $t2 := -($t1, $t0) + 7: label L2 + 8: return $t2 +} + + +[variant baseline] +public fun u64::divide_and_round_up($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: bool + var $t8: u64 + var $t9: u64 + 0: $t5 := %($t0, $t1) + 1: $t6 := 0 + 2: $t7 := ==($t5, $t6) + 3: if ($t7) goto 4 else goto 7 + 4: label L1 + 5: $t2 := /($t0, $t1) + 6: goto 11 + 7: label L0 + 8: $t8 := /($t0, $t1) + 9: $t9 := 1 + 10: $t2 := +($t8, $t9) + 11: label L2 + 12: return $t2 +} + + +[variant baseline] +public fun u64::max($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: bool + 0: $t5 := >($t0, $t1) + 1: if ($t5) goto 2 else goto 5 + 2: label L1 + 3: $t2 := $t0 + 4: goto 7 + 5: label L0 + 6: $t2 := $t1 + 7: label L2 + 8: return $t2 +} + + +[variant baseline] +public fun u64::min($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: bool + 0: $t5 := <($t0, $t1) + 1: if ($t5) goto 2 else goto 5 + 2: label L1 + 3: $t2 := $t0 + 4: goto 7 + 5: label L0 + 6: $t2 := $t1 + 7: label L2 + 8: return $t2 +} + + +[variant baseline] +public fun u64::pow($t0|base: u64, $t1|exponent: u8): u64 { + var $t2|base#1#1: u64 + var $t3|exponent#1#1: u8 + var $t4|res#1#1: u64 + var $t5: u64 + var $t6: u8 + var $t7: bool + var $t8: u8 + var $t9: u8 + var $t10: u8 + var $t11: bool + var $t12: u8 + var $t13: u8 + 0: $t2 := $t0 + 1: $t3 := $t1 + 2: $t5 := 1 + 3: $t4 := $t5 + 4: label L5 + 5: $t6 := 1 + 6: $t7 := >=($t3, $t6) + 7: if ($t7) goto 8 else goto 25 + 8: label L1 + 9: label L2 + 10: $t8 := 2 + 11: $t9 := %($t3, $t8) + 12: $t10 := 0 + 13: $t11 := ==($t9, $t10) + 14: if ($t11) goto 15 else goto 20 + 15: label L4 + 16: $t2 := *($t2, $t2) + 17: $t12 := 2 + 18: $t3 := /($t3, $t12) + 19: goto 4 + 20: label L3 + 21: $t4 := *($t4, $t2) + 22: $t13 := 1 + 23: $t3 := -($t3, $t13) + 24: goto 4 + 25: label L0 + 26: return $t4 +} + + +[variant baseline] +public fun u64::sqrt($t0|x: u64): u64 { + var $t1|bit#1#1: u128 + var $t2|res#1#1: u128 + var $t3|x#1#1: u64 + var $t4|x#2#1: u128 + var $t5: u128 + var $t6: u128 + var $t7: u128 + var $t8: bool + var $t9: u128 + var $t10: bool + var $t11: u128 + var $t12: u8 + var $t13: u128 + var $t14: u8 + var $t15: u8 + var $t16: u64 + 0: $t5 := 18446744073709551616 + 1: $t1 := $t5 + 2: $t6 := 0 + 3: $t2 := $t6 + 4: $t4 := (u128)($t0) + 5: label L6 + 6: $t7 := 0 + 7: $t8 := !=($t1, $t7) + 8: if ($t8) goto 9 else goto 28 + 9: label L1 + 10: label L2 + 11: $t9 := +($t2, $t1) + 12: $t10 := >=($t4, $t9) + 13: if ($t10) goto 14 else goto 21 + 14: label L4 + 15: $t11 := +($t2, $t1) + 16: $t4 := -($t4, $t11) + 17: $t12 := 1 + 18: $t13 := >>($t2, $t12) + 19: $t2 := +($t13, $t1) + 20: goto 24 + 21: label L3 + 22: $t14 := 1 + 23: $t2 := >>($t2, $t14) + 24: label L5 + 25: $t15 := 2 + 26: $t1 := >>($t1, $t15) + 27: goto 5 + 28: label L0 + 29: $t16 := (u64)($t2) + 30: return $t16 +} + + +[variant baseline] +public fun vector::append<#0>($t0|lhs: &mut vector<#0>, $t1|other: vector<#0>) { + var $t2: &mut vector<#0> + var $t3: vector<#0> + var $t4: bool + var $t5: bool + var $t6: &mut vector<#0> + var $t7: #0 + var $t8: vector<#0> + 0: $t2 := borrow_local($t1) + 1: vector::reverse<#0>($t2) + 2: label L3 + 3: $t3 := copy($t1) + 4: $t4 := vector::is_empty<#0>($t3) + 5: $t5 := !($t4) + 6: if ($t5) goto 7 else goto 13 + 7: label L1 + 8: label L2 + 9: $t6 := borrow_local($t1) + 10: $t7 := vector::pop_back<#0>($t6) + 11: vector::push_back<#0>($t0, $t7) + 12: goto 2 + 13: label L0 + 14: destroy($t0) + 15: $t8 := move($t1) + 16: vector::destroy_empty<#0>($t8) + 17: trace_local[lhs]($t0) + 18: return () +} + + +[variant baseline] +public native fun vector::borrow<#0>($t0|v: vector<#0>, $t1|i: u64): #0; + + +[variant baseline] +public native fun vector::borrow_mut<#0>($t0|v: &mut vector<#0>, $t1|i: u64): &mut #0; + + +[variant baseline] +public fun vector::contains<#0>($t0|v: vector<#0>, $t1|e: #0): bool { + var $t2|i#1#0: u64 + var $t3|len#1#0: u64 + var $t4: u64 + var $t5: u64 + var $t6: bool + var $t7: #0 + var $t8: bool + var $t9: bool + var $t10: u64 + var $t11: bool + 0: $t4 := 0 + 1: $t2 := $t4 + 2: $t5 := vector::length<#0>($t0) + 3: label L5 + 4: $t6 := <($t2, $t5) + 5: if ($t6) goto 6 else goto 18 + 6: label L1 + 7: label L2 + 8: $t7 := vector::borrow<#0>($t0, $t2) + 9: $t8 := ==($t7, $t1) + 10: if ($t8) goto 11 else goto 14 + 11: label L4 + 12: $t9 := true + 13: return $t9 + 14: label L3 + 15: $t10 := 1 + 16: $t2 := +($t2, $t10) + 17: goto 3 + 18: label L0 + 19: $t11 := false + 20: return $t11 +} + + +[variant baseline] +public native fun vector::destroy_empty<#0>($t0|v: vector<#0>); + + +[variant baseline] +public native fun vector::empty<#0>(): vector<#0>; + + +[variant baseline] +public fun vector::index_of<#0>($t0|v: vector<#0>, $t1|e: #0): (bool, u64) { + var $t2|i#1#0: u64 + var $t3|len#1#0: u64 + var $t4: u64 + var $t5: u64 + var $t6: bool + var $t7: #0 + var $t8: bool + var $t9: bool + var $t10: u64 + var $t11: bool + var $t12: u64 + 0: $t4 := 0 + 1: $t2 := $t4 + 2: $t5 := vector::length<#0>($t0) + 3: label L5 + 4: $t6 := <($t2, $t5) + 5: if ($t6) goto 6 else goto 18 + 6: label L1 + 7: label L2 + 8: $t7 := vector::borrow<#0>($t0, $t2) + 9: $t8 := ==($t7, $t1) + 10: if ($t8) goto 11 else goto 14 + 11: label L4 + 12: $t9 := true + 13: return ($t9, $t2) + 14: label L3 + 15: $t10 := 1 + 16: $t2 := +($t2, $t10) + 17: goto 3 + 18: label L0 + 19: $t11 := false + 20: $t12 := 0 + 21: return ($t11, $t12) +} + + +[variant baseline] +public fun vector::insert<#0>($t0|v: &mut vector<#0>, $t1|e: #0, $t2|i: u64) { + var $t3|len#1#0: u64 + var $t4: vector<#0> + var $t5: u64 + var $t6: bool + var $t7: u64 + var $t8: bool + var $t9: u64 + 0: $t4 := read_ref($t0) + 1: $t5 := vector::length<#0>($t4) + 2: $t6 := >($t2, $t5) + 3: if ($t6) goto 4 else goto 8 + 4: label L1 + 5: destroy($t0) + 6: $t7 := 131072 + 7: abort($t7) + 8: label L0 + 9: vector::push_back<#0>($t0, $t1) + 10: label L4 + 11: $t8 := <($t2, $t5) + 12: if ($t8) goto 13 else goto 18 + 13: label L3 + 14: vector::swap<#0>($t0, $t2, $t5) + 15: $t9 := 1 + 16: $t2 := +($t2, $t9) + 17: goto 10 + 18: label L2 + 19: destroy($t0) + 20: trace_local[v]($t0) + 21: return () +} + + +[variant baseline] +public fun vector::is_empty<#0>($t0|v: vector<#0>): bool { + var $t1: u64 + var $t2: u64 + var $t3: bool + 0: $t1 := vector::length<#0>($t0) + 1: $t2 := 0 + 2: $t3 := ==($t1, $t2) + 3: return $t3 +} + + +[variant baseline] +public native fun vector::length<#0>($t0|v: vector<#0>): u64; + + +[variant baseline] +public native fun vector::pop_back<#0>($t0|v: &mut vector<#0>): #0; + + +[variant baseline] +public native fun vector::push_back<#0>($t0|v: &mut vector<#0>, $t1|e: #0); + + +[variant baseline] +public fun vector::remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { + var $t2|tmp#$2: u64 + var $t3|tmp#$3: &mut vector<#0> + var $t4|len#1#0: u64 + var $t5: vector<#0> + var $t6: u64 + var $t7: bool + var $t8: u64 + var $t9: u64 + var $t10: u64 + var $t11: bool + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: #0 + 0: $t5 := read_ref($t0) + 1: $t6 := vector::length<#0>($t5) + 2: $t7 := >=($t1, $t6) + 3: if ($t7) goto 4 else goto 8 + 4: label L1 + 5: destroy($t0) + 6: $t8 := 131072 + 7: abort($t8) + 8: label L0 + 9: $t9 := 1 + 10: $t10 := -($t6, $t9) + 11: label L4 + 12: $t11 := <($t1, $t10) + 13: if ($t11) goto 14 else goto 21 + 14: label L3 + 15: $t12 := copy($t1) + 16: $t13 := 1 + 17: $t14 := +($t1, $t13) + 18: $t1 := $t14 + 19: vector::swap<#0>($t0, $t12, $t14) + 20: goto 11 + 21: label L2 + 22: $t15 := vector::pop_back<#0>($t0) + 23: trace_local[v]($t0) + 24: return $t15 +} + + +[variant baseline] +public fun vector::reverse<#0>($t0|v: &mut vector<#0>) { + var $t1|back_index#1#0: u64 + var $t2|front_index#1#0: u64 + var $t3|len#1#0: u64 + var $t4: vector<#0> + var $t5: u64 + var $t6: u64 + var $t7: bool + var $t8: u64 + var $t9: u64 + var $t10: bool + var $t11: u64 + var $t12: u64 + 0: $t4 := read_ref($t0) + 1: $t5 := vector::length<#0>($t4) + 2: $t6 := 0 + 3: $t7 := ==($t5, $t6) + 4: if ($t7) goto 5 else goto 9 + 5: label L1 + 6: destroy($t0) + 7: trace_local[v]($t0) + 8: return () + 9: label L0 + 10: $t8 := 0 + 11: $t2 := $t8 + 12: $t9 := 1 + 13: $t1 := -($t5, $t9) + 14: label L4 + 15: $t10 := <($t2, $t1) + 16: if ($t10) goto 17 else goto 24 + 17: label L3 + 18: vector::swap<#0>($t0, $t2, $t1) + 19: $t11 := 1 + 20: $t2 := +($t2, $t11) + 21: $t12 := 1 + 22: $t1 := -($t1, $t12) + 23: goto 14 + 24: label L2 + 25: destroy($t0) + 26: trace_local[v]($t0) + 27: return () +} + + +[variant baseline] +public fun vector::singleton<#0>($t0|e: #0): vector<#0> { + var $t1|v#1#0: vector<#0> + var $t2: &mut vector<#0> + var $t3: vector<#0> + 0: $t1 := vector::empty<#0>() + 1: $t2 := borrow_local($t1) + 2: vector::push_back<#0>($t2, $t0) + 3: $t3 := move($t1) + 4: return $t3 +} + + +[variant baseline] +public native fun vector::swap<#0>($t0|v: &mut vector<#0>, $t1|i: u64, $t2|j: u64); + + +[variant baseline] +public fun vector::swap_remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { + var $t2|last_idx#1#0: u64 + var $t3: vector<#0> + var $t4: bool + var $t5: bool + var $t6: u64 + var $t7: vector<#0> + var $t8: u64 + var $t9: u64 + var $t10: u64 + var $t11: #0 + 0: $t3 := read_ref($t0) + 1: $t4 := vector::is_empty<#0>($t3) + 2: $t5 := !($t4) + 3: if ($t5) goto 4 else goto 6 + 4: label L1 + 5: goto 10 + 6: label L0 + 7: destroy($t0) + 8: $t6 := 131072 + 9: abort($t6) + 10: label L2 + 11: $t7 := read_ref($t0) + 12: $t8 := vector::length<#0>($t7) + 13: $t9 := 1 + 14: $t10 := -($t8, $t9) + 15: vector::swap<#0>($t0, $t1, $t10) + 16: $t11 := vector::pop_back<#0>($t0) + 17: trace_local[v]($t0) + 18: return $t11 +} + + +[variant baseline] +public fun option::borrow<#0>($t0|t: option::Option<#0>): #0 { + var $t1: bool + var $t2: u64 + var $t3: vector<#0> + var $t4: u64 + var $t5: #0 + 0: $t1 := option::is_some<#0>($t0) + 1: if ($t1) goto 2 else goto 4 + 2: label L1 + 3: goto 7 + 4: label L0 + 5: $t2 := 262145 + 6: abort($t2) + 7: label L2 + 8: $t3 := get_field>.vec($t0) + 9: $t4 := 0 + 10: $t5 := vector::borrow<#0>($t3, $t4) + 11: return $t5 +} + + +[variant baseline] +public fun option::borrow_mut<#0>($t0|t: &mut option::Option<#0>): &mut #0 { + var $t1: option::Option<#0> + var $t2: bool + var $t3: u64 + var $t4: &mut vector<#0> + var $t5: u64 + var $t6: &mut #0 + 0: $t1 := read_ref($t0) + 1: $t2 := option::is_some<#0>($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 9 + 5: label L0 + 6: destroy($t0) + 7: $t3 := 262145 + 8: abort($t3) + 9: label L2 + 10: $t4 := borrow_field>.vec($t0) + 11: $t5 := 0 + 12: $t6 := vector::borrow_mut<#0>($t4, $t5) + 13: trace_local[t]($t0) + 14: return $t6 +} + + +[variant baseline] +public fun option::contains<#0>($t0|t: option::Option<#0>, $t1|e_ref: #0): bool { + var $t2: vector<#0> + var $t3: bool + 0: $t2 := get_field>.vec($t0) + 1: $t3 := vector::contains<#0>($t2, $t1) + 2: return $t3 +} + + +[variant baseline] +public fun option::swap<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0): #0 { + var $t2|old_value#1#0: #0 + var $t3|vec_ref#1#0: &mut vector<#0> + var $t4: option::Option<#0> + var $t5: bool + var $t6: u64 + var $t7: &mut vector<#0> + var $t8: #0 + 0: $t4 := read_ref($t0) + 1: $t5 := option::is_some<#0>($t4) + 2: if ($t5) goto 3 else goto 5 + 3: label L1 + 4: goto 9 + 5: label L0 + 6: destroy($t0) + 7: $t6 := 262145 + 8: abort($t6) + 9: label L2 + 10: $t7 := borrow_field>.vec($t0) + 11: $t8 := vector::pop_back<#0>($t7) + 12: vector::push_back<#0>($t7, $t1) + 13: trace_local[t]($t0) + 14: return $t8 +} + + +[variant baseline] +public fun option::borrow_with_default<#0>($t0|t: option::Option<#0>, $t1|default_ref: #0): #0 { + var $t2|tmp#$2: #0 + var $t3|vec_ref#1#0: vector<#0> + var $t4: vector<#0> + var $t5: bool + var $t6: u64 + 0: $t4 := get_field>.vec($t0) + 1: $t5 := vector::is_empty<#0>($t4) + 2: if ($t5) goto 3 else goto 6 + 3: label L1 + 4: $t2 := $t1 + 5: goto 9 + 6: label L0 + 7: $t6 := 0 + 8: $t2 := vector::borrow<#0>($t4, $t6) + 9: label L2 + 10: return $t2 +} + + +[variant baseline] +public fun option::destroy_none<#0>($t0|t: option::Option<#0>) { + var $t1: bool + var $t2: u64 + var $t3: vector<#0> + 0: $t1 := option::is_none<#0>($t0) + 1: if ($t1) goto 2 else goto 4 + 2: label L1 + 3: goto 7 + 4: label L0 + 5: $t2 := 262144 + 6: abort($t2) + 7: label L2 + 8: $t3 := unpack option::Option<#0>($t0) + 9: vector::destroy_empty<#0>($t3) + 10: return () +} + + +[variant baseline] +public fun option::destroy_some<#0>($t0|t: option::Option<#0>): #0 { + var $t1|elem#1#0: #0 + var $t2|vec#1#0: vector<#0> + var $t3: bool + var $t4: u64 + var $t5: &mut vector<#0> + var $t6: #0 + var $t7: vector<#0> + 0: $t3 := option::is_some<#0>($t0) + 1: if ($t3) goto 2 else goto 4 + 2: label L1 + 3: goto 7 + 4: label L0 + 5: $t4 := 262145 + 6: abort($t4) + 7: label L2 + 8: $t2 := unpack option::Option<#0>($t0) + 9: $t5 := borrow_local($t2) + 10: $t6 := vector::pop_back<#0>($t5) + 11: $t7 := move($t2) + 12: vector::destroy_empty<#0>($t7) + 13: return $t6 +} + + +[variant baseline] +public fun option::destroy_with_default<#0>($t0|t: option::Option<#0>, $t1|default: #0): #0 { + var $t2|tmp#$2: #0 + var $t3|vec#1#0: vector<#0> + var $t4: vector<#0> + var $t5: bool + var $t6: &mut vector<#0> + 0: $t3 := unpack option::Option<#0>($t0) + 1: $t4 := copy($t3) + 2: $t5 := vector::is_empty<#0>($t4) + 3: if ($t5) goto 4 else goto 7 + 4: label L1 + 5: $t2 := $t1 + 6: goto 10 + 7: label L0 + 8: $t6 := borrow_local($t3) + 9: $t2 := vector::pop_back<#0>($t6) + 10: label L2 + 11: return $t2 +} + + +[variant baseline] +public fun option::extract<#0>($t0|t: &mut option::Option<#0>): #0 { + var $t1: option::Option<#0> + var $t2: bool + var $t3: u64 + var $t4: &mut vector<#0> + var $t5: #0 + 0: $t1 := read_ref($t0) + 1: $t2 := option::is_some<#0>($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 9 + 5: label L0 + 6: destroy($t0) + 7: $t3 := 262145 + 8: abort($t3) + 9: label L2 + 10: $t4 := borrow_field>.vec($t0) + 11: $t5 := vector::pop_back<#0>($t4) + 12: trace_local[t]($t0) + 13: return $t5 +} + + +[variant baseline] +public fun option::fill<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0) { + var $t2|vec_ref#1#0: &mut vector<#0> + var $t3: &mut vector<#0> + var $t4: vector<#0> + var $t5: bool + var $t6: u64 + 0: $t3 := borrow_field>.vec($t0) + 1: $t4 := read_ref($t3) + 2: $t5 := vector::is_empty<#0>($t4) + 3: if ($t5) goto 4 else goto 14 + 4: label L1 + 5: goto 10 + 6: label L0 + 7: destroy($t3) + 8: $t6 := 262144 + 9: abort($t6) + 10: label L2 + 11: vector::push_back<#0>($t3, $t1) + 12: trace_local[t]($t0) + 13: return () + 14: label L3 + 15: destroy($t0) + 16: goto 6 +} + + +[variant baseline] +public fun option::get_with_default<#0>($t0|t: option::Option<#0>, $t1|default: #0): #0 { + var $t2|tmp#$2: #0 + var $t3|vec_ref#1#0: vector<#0> + var $t4: vector<#0> + var $t5: bool + var $t6: u64 + var $t7: #0 + 0: $t4 := get_field>.vec($t0) + 1: $t5 := vector::is_empty<#0>($t4) + 2: if ($t5) goto 3 else goto 6 + 3: label L1 + 4: $t2 := $t1 + 5: goto 10 + 6: label L0 + 7: $t6 := 0 + 8: $t7 := vector::borrow<#0>($t4, $t6) + 9: $t2 := $t7 + 10: label L2 + 11: return $t2 +} + + +[variant baseline] +public fun option::is_none<#0>($t0|t: option::Option<#0>): bool { + var $t1: vector<#0> + var $t2: bool + 0: $t1 := get_field>.vec($t0) + 1: $t2 := vector::is_empty<#0>($t1) + 2: return $t2 +} + + +[variant baseline] +public fun option::is_some<#0>($t0|t: option::Option<#0>): bool { + var $t1: vector<#0> + var $t2: bool + var $t3: bool + 0: $t1 := get_field>.vec($t0) + 1: $t2 := vector::is_empty<#0>($t1) + 2: $t3 := !($t2) + 3: return $t3 +} + + +[variant baseline] +public fun option::none<#0>(): option::Option<#0> { + var $t0: vector<#0> + var $t1: option::Option<#0> + 0: $t0 := vector::empty<#0>() + 1: $t1 := pack option::Option<#0>($t0) + 2: return $t1 +} + + +[variant baseline] +public fun option::some<#0>($t0|e: #0): option::Option<#0> { + var $t1: vector<#0> + var $t2: option::Option<#0> + 0: $t1 := vector::singleton<#0>($t0) + 1: $t2 := pack option::Option<#0>($t1) + 2: return $t2 +} + + +[variant baseline] +public fun option::swap_or_fill<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0): option::Option<#0> { + var $t2|tmp#$2: option::Option<#0> + var $t3|old_value#1#0: option::Option<#0> + var $t4|vec_ref#1#0: &mut vector<#0> + var $t5: &mut vector<#0> + var $t6: vector<#0> + var $t7: bool + var $t8: #0 + 0: $t5 := borrow_field>.vec($t0) + 1: $t6 := read_ref($t5) + 2: $t7 := vector::is_empty<#0>($t6) + 3: if ($t7) goto 4 else goto 7 + 4: label L1 + 5: $t2 := option::none<#0>() + 6: goto 10 + 7: label L0 + 8: $t8 := vector::pop_back<#0>($t5) + 9: $t2 := option::some<#0>($t8) + 10: label L2 + 11: vector::push_back<#0>($t5, $t1) + 12: trace_local[t]($t0) + 13: return $t2 +} + + +[variant baseline] +public fun option::to_vec<#0>($t0|t: option::Option<#0>): vector<#0> { + var $t1: vector<#0> + 0: $t1 := unpack option::Option<#0>($t0) + 1: return $t1 +} + + +[variant baseline] +public fun ascii::append($t0|string: &mut ascii::String, $t1|other: ascii::String) { + var $t2: &mut vector + var $t3: vector + 0: $t2 := borrow_field.bytes($t0) + 1: $t3 := ascii::into_bytes($t1) + 2: vector::append($t2, $t3) + 3: trace_local[string]($t0) + 4: return () +} + + +[variant baseline] +public fun ascii::index_of($t0|string: ascii::String, $t1|substr: ascii::String): u64 { + var $t2|tmp#$2: bool + var $t3|i#1#0: u64 + var $t4|j#1#0: u64 + var $t5|m#1#0: u64 + var $t6|n#1#0: u64 + var $t7: u64 + var $t8: u64 + var $t9: u64 + var $t10: bool + var $t11: u64 + var $t12: bool + var $t13: u64 + var $t14: bool + var $t15: vector + var $t16: u64 + var $t17: u8 + var $t18: vector + var $t19: u8 + var $t20: bool + var $t21: u64 + var $t22: bool + var $t23: u64 + 0: $t7 := 0 + 1: $t3 := $t7 + 2: $t8 := ascii::length($t0) + 3: $t9 := ascii::length($t1) + 4: $t10 := <($t8, $t9) + 5: if ($t10) goto 6 else goto 8 + 6: label L1 + 7: return $t8 + 8: label L0 + 9: $t11 := -($t8, $t9) + 10: $t12 := <=($t3, $t11) + 11: if ($t12) goto 12 else goto 45 + 12: label L3 + 13: $t13 := 0 + 14: $t4 := $t13 + 15: label L10 + 16: $t14 := <($t4, $t9) + 17: if ($t14) goto 18 else goto 27 + 18: label L5 + 19: label L6 + 20: $t15 := get_field.bytes($t0) + 21: $t16 := +($t3, $t4) + 22: $t17 := vector::borrow($t15, $t16) + 23: $t18 := get_field.bytes($t1) + 24: $t19 := vector::borrow($t18, $t4) + 25: $t2 := ==($t17, $t19) + 26: goto 30 + 27: label L4 + 28: $t20 := false + 29: $t2 := $t20 + 30: label L7 + 31: if ($t2) goto 32 else goto 36 + 32: label L9 + 33: $t21 := 1 + 34: $t4 := +($t4, $t21) + 35: goto 15 + 36: label L8 + 37: $t22 := ==($t4, $t9) + 38: if ($t22) goto 39 else goto 41 + 39: label L12 + 40: return $t3 + 41: label L11 + 42: $t23 := 1 + 43: $t3 := +($t3, $t23) + 44: goto 8 + 45: label L2 + 46: return $t8 +} + + +[variant baseline] +public fun ascii::insert($t0|s: &mut ascii::String, $t1|at: u64, $t2|o: ascii::String) { + var $t3|e#1#2: u8 + var $t4|v#1#1: vector + var $t5: ascii::String + var $t6: u64 + var $t7: bool + var $t8: u64 + var $t9: vector + var $t10: bool + var $t11: bool + var $t12: &mut vector + var $t13: u8 + var $t14: &mut vector + var $t15: vector + 0: $t5 := read_ref($t0) + 1: $t6 := ascii::length($t5) + 2: $t7 := <=($t1, $t6) + 3: if ($t7) goto 4 else goto 6 + 4: label L1 + 5: goto 10 + 6: label L0 + 7: destroy($t0) + 8: $t8 := 65537 + 9: abort($t8) + 10: label L2 + 11: $t4 := ascii::into_bytes($t2) + 12: label L5 + 13: $t9 := copy($t4) + 14: $t10 := vector::is_empty($t9) + 15: $t11 := !($t10) + 16: if ($t11) goto 17 else goto 23 + 17: label L4 + 18: $t12 := borrow_local($t4) + 19: $t13 := vector::pop_back($t12) + 20: $t14 := borrow_field.bytes($t0) + 21: vector::insert($t14, $t13, $t1) + 22: goto 12 + 23: label L3 + 24: destroy($t0) + 25: $t15 := move($t4) + 26: vector::destroy_empty($t15) + 27: trace_local[s]($t0) + 28: return () +} + + +[variant baseline] +public fun ascii::is_empty($t0|string: ascii::String): bool { + var $t1: vector + var $t2: bool + 0: $t1 := get_field.bytes($t0) + 1: $t2 := vector::is_empty($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::length($t0|string: ascii::String): u64 { + var $t1: vector + var $t2: u64 + 0: $t1 := ascii::as_bytes($t0) + 1: $t2 := vector::length($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::all_characters_printable($t0|string: ascii::String): bool { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: bool + var $t3|i#1#12: u64 + var $t4|i#1#9: u64 + var $t5|stop#1#9: u64 + var $t6|v#1#3: vector + var $t7: vector + var $t8: u64 + var $t9: u64 + var $t10: bool + var $t11: u8 + var $t12: bool + var $t13: bool + var $t14: bool + var $t15: u64 + var $t16: bool + 0: $t7 := get_field.bytes($t0) + 1: $t8 := vector::length($t7) + 2: $t9 := 0 + 3: $t4 := $t9 + 4: label L5 + 5: $t10 := <($t4, $t8) + 6: if ($t10) goto 7 else goto 20 + 7: label L1 + 8: $t11 := vector::borrow($t7, $t4) + 9: $t12 := ascii::is_printable_char($t11) + 10: $t13 := !($t12) + 11: if ($t13) goto 12 else goto 16 + 12: label L3 + 13: $t14 := false + 14: $t2 := $t14 + 15: goto 23 + 16: label L2 + 17: $t15 := 1 + 18: $t4 := +($t4, $t15) + 19: goto 4 + 20: label L0 + 21: $t16 := true + 22: $t2 := $t16 + 23: label L4 + 24: return $t2 +} + + +[variant baseline] +public fun ascii::string($t0|bytes: vector): ascii::String { + var $t1|x#1#0: option::Option + var $t2: option::Option + var $t3: bool + var $t4: u64 + var $t5: ascii::String + 0: $t2 := ascii::try_string($t0) + 1: $t3 := option::is_some($t2) + 2: if ($t3) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t4 := 65536 + 7: abort($t4) + 8: label L2 + 9: $t5 := option::destroy_some($t2) + 10: return $t5 +} + + +[variant baseline] +public fun ascii::as_bytes($t0|string: ascii::String): vector { + var $t1: vector + 0: $t1 := get_field.bytes($t0) + 1: return $t1 +} + + +[variant baseline] +public fun ascii::byte($t0|char: ascii::Char): u8 { + var $t1: u8 + 0: $t1 := unpack ascii::Char($t0) + 1: return $t1 +} + + +[variant baseline] +public fun ascii::char($t0|byte: u8): ascii::Char { + var $t1: bool + var $t2: u64 + var $t3: ascii::Char + 0: $t1 := ascii::is_valid_char($t0) + 1: if ($t1) goto 2 else goto 4 + 2: label L1 + 3: goto 7 + 4: label L0 + 5: $t2 := 65536 + 6: abort($t2) + 7: label L2 + 8: $t3 := pack ascii::Char($t0) + 9: return $t3 +} + + +[variant baseline] +fun ascii::char_to_lowercase($t0|byte: u8): u8 { + var $t1|tmp#$1: bool + var $t2|tmp#$2: u8 + var $t3: u8 + var $t4: bool + var $t5: u8 + var $t6: bool + var $t7: u8 + 0: $t3 := 65 + 1: $t4 := >=($t0, $t3) + 2: if ($t4) goto 3 else goto 7 + 3: label L1 + 4: $t5 := 90 + 5: $t1 := <=($t0, $t5) + 6: goto 10 + 7: label L0 + 8: $t6 := false + 9: $t1 := $t6 + 10: label L2 + 11: if ($t1) goto 12 else goto 16 + 12: label L4 + 13: $t7 := 32 + 14: $t2 := +($t0, $t7) + 15: goto 18 + 16: label L3 + 17: $t2 := $t0 + 18: label L5 + 19: return $t2 +} + + +[variant baseline] +fun ascii::char_to_uppercase($t0|byte: u8): u8 { + var $t1|tmp#$1: bool + var $t2|tmp#$2: u8 + var $t3: u8 + var $t4: bool + var $t5: u8 + var $t6: bool + var $t7: u8 + 0: $t3 := 97 + 1: $t4 := >=($t0, $t3) + 2: if ($t4) goto 3 else goto 7 + 3: label L1 + 4: $t5 := 122 + 5: $t1 := <=($t0, $t5) + 6: goto 10 + 7: label L0 + 8: $t6 := false + 9: $t1 := $t6 + 10: label L2 + 11: if ($t1) goto 12 else goto 16 + 12: label L4 + 13: $t7 := 32 + 14: $t2 := -($t0, $t7) + 15: goto 18 + 16: label L3 + 17: $t2 := $t0 + 18: label L5 + 19: return $t2 +} + + +[variant baseline] +public fun ascii::into_bytes($t0|string: ascii::String): vector { + var $t1: vector + 0: $t1 := unpack ascii::String($t0) + 1: return $t1 +} + + +[variant baseline] +public fun ascii::is_printable_char($t0|byte: u8): bool { + var $t1|tmp#$1: bool + var $t2: u8 + var $t3: bool + var $t4: u8 + var $t5: bool + 0: $t2 := 32 + 1: $t3 := >=($t0, $t2) + 2: if ($t3) goto 3 else goto 7 + 3: label L1 + 4: $t4 := 126 + 5: $t1 := <=($t0, $t4) + 6: goto 10 + 7: label L0 + 8: $t5 := false + 9: $t1 := $t5 + 10: label L2 + 11: return $t1 +} + + +[variant baseline] +public fun ascii::is_valid_char($t0|b: u8): bool { + var $t1: u8 + var $t2: bool + 0: $t1 := 127 + 1: $t2 := <=($t0, $t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::pop_char($t0|string: &mut ascii::String): ascii::Char { + var $t1: &mut vector + var $t2: u8 + var $t3: ascii::Char + 0: $t1 := borrow_field.bytes($t0) + 1: $t2 := vector::pop_back($t1) + 2: $t3 := pack ascii::Char($t2) + 3: trace_local[string]($t0) + 4: return $t3 +} + [variant baseline] -public fun vector::index_of<#0>($t0|v: vector<#0>, $t1|e: #0): (bool, u64) { - var $t2|i#1#0: u64 - var $t3|len#1#0: u64 - var $t4: u64 - var $t5: u64 - var $t6: bool - var $t7: #0 - var $t8: bool - var $t9: bool - var $t10: u64 - var $t11: bool - var $t12: u64 - 0: $t4 := 0 - 1: $t2 := $t4 - 2: $t5 := vector::length<#0>($t0) - 3: label L5 - 4: $t6 := <($t2, $t5) - 5: if ($t6) goto 6 else goto 18 - 6: label L1 - 7: label L2 - 8: $t7 := vector::borrow<#0>($t0, $t2) - 9: $t8 := ==($t7, $t1) - 10: if ($t8) goto 11 else goto 14 - 11: label L4 - 12: $t9 := true - 13: return ($t9, $t2) - 14: label L3 - 15: $t10 := 1 - 16: $t2 := +($t2, $t10) - 17: goto 3 - 18: label L0 - 19: $t11 := false - 20: $t12 := 0 - 21: return ($t11, $t12) +public fun ascii::push_char($t0|string: &mut ascii::String, $t1|char: ascii::Char) { + var $t2: &mut vector + var $t3: u8 + 0: $t2 := borrow_field.bytes($t0) + 1: $t3 := get_field.byte($t1) + 2: vector::push_back($t2, $t3) + 3: trace_local[string]($t0) + 4: return () } [variant baseline] -public fun vector::insert<#0>($t0|v: &mut vector<#0>, $t1|e: #0, $t2|i: u64) { - var $t3|len#1#0: u64 - var $t4: vector<#0> - var $t5: u64 - var $t6: bool - var $t7: u64 +public fun ascii::substring($t0|string: ascii::String, $t1|i: u64, $t2|j: u64): ascii::String { + var $t3|tmp#$3: bool + var $t4|bytes#1#0: vector + var $t5|i#1#3: u64 + var $t6|i#1#6: u64 + var $t7|stop#1#3: u64 var $t8: bool var $t9: u64 - 0: $t4 := read_ref($t0) - 1: $t5 := vector::length<#0>($t4) - 2: $t6 := >($t2, $t5) - 3: if ($t6) goto 4 else goto 8 - 4: label L1 - 5: destroy($t0) - 6: $t7 := 131072 - 7: abort($t7) - 8: label L0 - 9: vector::push_back<#0>($t0, $t1) - 10: label L5 - 11: $t8 := <($t2, $t5) - 12: if ($t8) goto 13 else goto 19 + var $t10: bool + var $t11: u64 + var $t12: vector + var $t13: bool + var $t14: &mut vector + var $t15: vector + var $t16: u8 + var $t17: u64 + var $t18: vector + var $t19: ascii::String + 0: $t8 := <=($t1, $t2) + 1: if ($t8) goto 2 else goto 6 + 2: label L1 + 3: $t9 := ascii::length($t0) + 4: $t3 := <=($t2, $t9) + 5: goto 9 + 6: label L0 + 7: $t10 := false + 8: $t3 := $t10 + 9: label L2 + 10: if ($t3) goto 11 else goto 13 + 11: label L4 + 12: goto 16 13: label L3 - 14: label L4 - 15: vector::swap<#0>($t0, $t2, $t5) - 16: $t9 := 1 - 17: $t2 := +($t2, $t9) - 18: goto 10 - 19: label L2 - 20: destroy($t0) - 21: trace_local[v]($t0) - 22: return () + 14: $t11 := 65537 + 15: abort($t11) + 16: label L5 + 17: $t12 := [] + 18: $t4 := $t12 + 19: $t5 := $t1 + 20: label L8 + 21: $t13 := <($t5, $t2) + 22: if ($t13) goto 23 else goto 31 + 23: label L7 + 24: $t14 := borrow_local($t4) + 25: $t15 := get_field.bytes($t0) + 26: $t16 := vector::borrow($t15, $t5) + 27: vector::push_back($t14, $t16) + 28: $t17 := 1 + 29: $t5 := +($t5, $t17) + 30: goto 20 + 31: label L6 + 32: $t18 := move($t4) + 33: $t19 := pack ascii::String($t18) + 34: return $t19 } [variant baseline] -public fun vector::is_empty<#0>($t0|v: vector<#0>): bool { - var $t1: u64 - var $t2: u64 - var $t3: bool - 0: $t1 := vector::length<#0>($t0) - 1: $t2 := 0 - 2: $t3 := ==($t1, $t2) - 3: return $t3 +public fun ascii::to_lowercase($t0|string: ascii::String): ascii::String { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: u8 + var $t3|tmp#$3: &mut vector + var $t4|e#1#13: u8 + var $t5|i#1#12: u64 + var $t6|i#1#9: u64 + var $t7|r#1#1: vector + var $t8|stop#1#9: u64 + var $t9|v#1#1: vector + var $t10|v#1#3: vector + var $t11: vector + var $t12: vector + var $t13: u64 + var $t14: u64 + var $t15: bool + var $t16: u8 + var $t17: &mut vector + var $t18: u8 + var $t19: u64 + var $t20: vector + var $t21: ascii::String + 0: $t11 := ascii::as_bytes($t0) + 1: $t12 := [] + 2: $t7 := $t12 + 3: $t13 := vector::length($t11) + 4: $t14 := 0 + 5: $t6 := $t14 + 6: label L2 + 7: $t15 := <($t6, $t13) + 8: if ($t15) goto 9 else goto 17 + 9: label L1 + 10: $t16 := vector::borrow($t11, $t6) + 11: $t17 := borrow_local($t7) + 12: $t18 := ascii::char_to_lowercase($t16) + 13: vector::push_back($t17, $t18) + 14: $t19 := 1 + 15: $t6 := +($t6, $t19) + 16: goto 6 + 17: label L0 + 18: $t20 := move($t7) + 19: $t21 := pack ascii::String($t20) + 20: return $t21 } [variant baseline] -public native fun vector::length<#0>($t0|v: vector<#0>): u64; +public fun ascii::to_uppercase($t0|string: ascii::String): ascii::String { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: u8 + var $t3|tmp#$3: &mut vector + var $t4|e#1#13: u8 + var $t5|i#1#12: u64 + var $t6|i#1#9: u64 + var $t7|r#1#1: vector + var $t8|stop#1#9: u64 + var $t9|v#1#1: vector + var $t10|v#1#3: vector + var $t11: vector + var $t12: vector + var $t13: u64 + var $t14: u64 + var $t15: bool + var $t16: u8 + var $t17: &mut vector + var $t18: u8 + var $t19: u64 + var $t20: vector + var $t21: ascii::String + 0: $t11 := ascii::as_bytes($t0) + 1: $t12 := [] + 2: $t7 := $t12 + 3: $t13 := vector::length($t11) + 4: $t14 := 0 + 5: $t6 := $t14 + 6: label L2 + 7: $t15 := <($t6, $t13) + 8: if ($t15) goto 9 else goto 17 + 9: label L1 + 10: $t16 := vector::borrow($t11, $t6) + 11: $t17 := borrow_local($t7) + 12: $t18 := ascii::char_to_uppercase($t16) + 13: vector::push_back($t17, $t18) + 14: $t19 := 1 + 15: $t6 := +($t6, $t19) + 16: goto 6 + 17: label L0 + 18: $t20 := move($t7) + 19: $t21 := pack ascii::String($t20) + 20: return $t21 +} [variant baseline] -public native fun vector::pop_back<#0>($t0|v: &mut vector<#0>): #0; +public fun ascii::try_string($t0|bytes: vector): option::Option { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: bool + var $t3|tmp#$3: option::Option + var $t4|i#1#12: u64 + var $t5|i#1#9: u64 + var $t6|stop#1#9: u64 + var $t7|v#1#3: vector + var $t8: u64 + var $t9: u64 + var $t10: bool + var $t11: u8 + var $t12: bool + var $t13: bool + var $t14: bool + var $t15: u64 + var $t16: bool + var $t17: ascii::String + 0: $t8 := vector::length($t0) + 1: $t9 := 0 + 2: $t5 := $t9 + 3: label L5 + 4: $t10 := <($t5, $t8) + 5: if ($t10) goto 6 else goto 19 + 6: label L1 + 7: $t11 := vector::borrow($t0, $t5) + 8: $t12 := ascii::is_valid_char($t11) + 9: $t13 := !($t12) + 10: if ($t13) goto 11 else goto 15 + 11: label L3 + 12: $t14 := false + 13: $t2 := $t14 + 14: goto 22 + 15: label L2 + 16: $t15 := 1 + 17: $t5 := +($t5, $t15) + 18: goto 3 + 19: label L0 + 20: $t16 := true + 21: $t2 := $t16 + 22: label L4 + 23: if ($t2) goto 24 else goto 28 + 24: label L7 + 25: $t17 := pack ascii::String($t0) + 26: $t3 := option::some($t17) + 27: goto 30 + 28: label L6 + 29: $t3 := option::none() + 30: label L8 + 31: return $t3 +} [variant baseline] -public native fun vector::push_back<#0>($t0|v: &mut vector<#0>, $t1|e: #0); +public fun string::append($t0|s: &mut string::String, $t1|r: string::String) { + var $t2: &mut vector + var $t3: vector + 0: $t2 := borrow_field.bytes($t0) + 1: $t3 := get_field.bytes($t1) + 2: vector::append($t2, $t3) + 3: trace_local[s]($t0) + 4: return () +} [variant baseline] -public fun vector::remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { - var $t2|tmp#$2: u64 - var $t3|tmp#$3: &mut vector<#0> - var $t4|len#1#0: u64 - var $t5: vector<#0> - var $t6: u64 - var $t7: bool - var $t8: u64 +public fun string::index_of($t0|s: string::String, $t1|r: string::String): u64 { + var $t2: vector + var $t3: vector + var $t4: u64 + 0: $t2 := get_field.bytes($t0) + 1: $t3 := get_field.bytes($t1) + 2: $t4 := string::internal_index_of($t2, $t3) + 3: return $t4 +} + + +[variant baseline] +public fun string::insert($t0|s: &mut string::String, $t1|at: u64, $t2|o: string::String) { + var $t3|tmp#$3: bool + var $t4|bytes#1#0: vector + var $t5|end#1#0: string::String + var $t6|front#1#0: string::String + var $t7|l#1#0: u64 + var $t8: vector var $t9: u64 - var $t10: u64 + var $t10: bool var $t11: bool var $t12: u64 - var $t13: u64 + var $t13: string::String var $t14: u64 - var $t15: #0 - 0: $t5 := read_ref($t0) - 1: $t6 := vector::length<#0>($t5) - 2: $t7 := >=($t1, $t6) - 3: if ($t7) goto 4 else goto 8 + var $t15: string::String + var $t16: u64 + var $t17: string::String + var $t18: string::String + var $t19: &mut string::String + var $t20: &mut string::String + var $t21: string::String + 0: $t8 := get_field.bytes($t0) + 1: $t9 := vector::length($t8) + 2: $t10 := <=($t1, $t9) + 3: if ($t10) goto 4 else goto 7 4: label L1 - 5: destroy($t0) - 6: $t8 := 131072 - 7: abort($t8) - 8: label L0 - 9: $t9 := 1 - 10: $t10 := -($t6, $t9) - 11: label L5 - 12: $t11 := <($t1, $t10) - 13: if ($t11) goto 14 else goto 22 + 5: $t3 := string::internal_is_char_boundary($t8, $t1) + 6: goto 10 + 7: label L0 + 8: $t11 := false + 9: $t3 := $t11 + 10: label L2 + 11: if ($t3) goto 12 else goto 14 + 12: label L4 + 13: goto 18 14: label L3 - 15: label L4 - 16: $t12 := copy($t1) - 17: $t13 := 1 - 18: $t14 := +($t1, $t13) - 19: $t1 := $t14 - 20: vector::swap<#0>($t0, $t12, $t14) - 21: goto 11 - 22: label L2 - 23: $t15 := vector::pop_back<#0>($t0) - 24: trace_local[v]($t0) - 25: return $t15 + 15: destroy($t0) + 16: $t12 := 2 + 17: abort($t12) + 18: label L5 + 19: $t13 := read_ref($t0) + 20: $t14 := string::length($t13) + 21: $t15 := read_ref($t0) + 22: $t16 := 0 + 23: $t6 := string::substring($t15, $t16, $t1) + 24: $t17 := read_ref($t0) + 25: $t18 := string::substring($t17, $t1, $t14) + 26: $t19 := borrow_local($t6) + 27: string::append($t19, $t2) + 28: $t20 := borrow_local($t6) + 29: string::append($t20, $t18) + 30: $t21 := move($t6) + 31: write_ref($t0, $t21) + 32: trace_local[s]($t0) + 33: return () } [variant baseline] -public fun vector::reverse<#0>($t0|v: &mut vector<#0>) { - var $t1|back_index#1#0: u64 - var $t2|front_index#1#0: u64 - var $t3|len#1#0: u64 - var $t4: vector<#0> - var $t5: u64 - var $t6: u64 - var $t7: bool - var $t8: u64 - var $t9: u64 - var $t10: bool - var $t11: u64 - var $t12: u64 - 0: $t4 := read_ref($t0) - 1: $t5 := vector::length<#0>($t4) - 2: $t6 := 0 - 3: $t7 := ==($t5, $t6) - 4: if ($t7) goto 5 else goto 9 - 5: label L1 - 6: destroy($t0) - 7: trace_local[v]($t0) - 8: return () - 9: label L0 - 10: $t8 := 0 - 11: $t2 := $t8 - 12: $t9 := 1 - 13: $t1 := -($t5, $t9) - 14: label L5 - 15: $t10 := <($t2, $t1) - 16: if ($t10) goto 17 else goto 25 - 17: label L3 - 18: label L4 - 19: vector::swap<#0>($t0, $t2, $t1) - 20: $t11 := 1 - 21: $t2 := +($t2, $t11) - 22: $t12 := 1 - 23: $t1 := -($t1, $t12) - 24: goto 14 - 25: label L2 - 26: destroy($t0) - 27: trace_local[v]($t0) - 28: return () +public fun string::is_empty($t0|s: string::String): bool { + var $t1: vector + var $t2: bool + 0: $t1 := get_field.bytes($t0) + 1: $t2 := vector::is_empty($t1) + 2: return $t2 } [variant baseline] -public fun vector::singleton<#0>($t0|e: #0): vector<#0> { - var $t1|v#1#0: vector<#0> - var $t2: &mut vector<#0> - var $t3: vector<#0> - 0: $t1 := vector::empty<#0>() - 1: $t2 := borrow_local($t1) - 2: vector::push_back<#0>($t2, $t0) - 3: $t3 := move($t1) - 4: return $t3 +public fun string::length($t0|s: string::String): u64 { + var $t1: vector + var $t2: u64 + 0: $t1 := get_field.bytes($t0) + 1: $t2 := vector::length($t1) + 2: return $t2 } [variant baseline] -public native fun vector::swap<#0>($t0|v: &mut vector<#0>, $t1|i: u64, $t2|j: u64); +public fun string::as_bytes($t0|s: string::String): vector { + var $t1: vector + 0: $t1 := get_field.bytes($t0) + 1: return $t1 +} [variant baseline] -public fun vector::swap_remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { - var $t2|last_idx#1#0: u64 - var $t3: vector<#0> - var $t4: bool - var $t5: bool - var $t6: u64 - var $t7: vector<#0> - var $t8: u64 +public fun string::into_bytes($t0|s: string::String): vector { + var $t1: vector + 0: $t1 := unpack string::String($t0) + 1: return $t1 +} + + +[variant baseline] +public fun string::bytes($t0|s: string::String): vector { + var $t1: vector + 0: $t1 := string::as_bytes($t0) + 1: return $t1 +} + + +[variant baseline] +public fun string::substring($t0|s: string::String, $t1|i: u64, $t2|j: u64): string::String { + var $t3|tmp#$3: bool + var $t4|tmp#$4: bool + var $t5|tmp#$5: bool + var $t6|bytes#1#0: vector + var $t7|l#1#0: u64 + var $t8: vector var $t9: u64 - var $t10: u64 - var $t11: #0 - 0: $t3 := read_ref($t0) - 1: $t4 := vector::is_empty<#0>($t3) - 2: $t5 := !($t4) - 3: if ($t5) goto 4 else goto 6 + var $t10: bool + var $t11: bool + var $t12: bool + var $t13: bool + var $t14: u64 + var $t15: vector + var $t16: string::String + 0: $t8 := get_field.bytes($t0) + 1: $t9 := vector::length($t8) + 2: $t10 := <=($t2, $t9) + 3: if ($t10) goto 4 else goto 7 4: label L1 - 5: goto 10 - 6: label L0 - 7: destroy($t0) - 8: $t6 := 131072 - 9: abort($t6) + 5: $t3 := <=($t1, $t2) + 6: goto 10 + 7: label L0 + 8: $t11 := false + 9: $t3 := $t11 10: label L2 - 11: $t7 := read_ref($t0) - 12: $t8 := vector::length<#0>($t7) - 13: $t9 := 1 - 14: $t10 := -($t8, $t9) - 15: vector::swap<#0>($t0, $t1, $t10) - 16: $t11 := vector::pop_back<#0>($t0) - 17: trace_local[v]($t0) - 18: return $t11 + 11: if ($t3) goto 12 else goto 15 + 12: label L4 + 13: $t4 := string::internal_is_char_boundary($t8, $t1) + 14: goto 18 + 15: label L3 + 16: $t12 := false + 17: $t4 := $t12 + 18: label L5 + 19: if ($t4) goto 20 else goto 23 + 20: label L7 + 21: $t5 := string::internal_is_char_boundary($t8, $t2) + 22: goto 26 + 23: label L6 + 24: $t13 := false + 25: $t5 := $t13 + 26: label L8 + 27: if ($t5) goto 28 else goto 30 + 28: label L10 + 29: goto 33 + 30: label L9 + 31: $t14 := 2 + 32: abort($t14) + 33: label L11 + 34: $t15 := string::internal_sub_string($t8, $t1, $t2) + 35: $t16 := pack string::String($t15) + 36: return $t16 +} + + +[variant baseline] +public fun string::append_utf8($t0|s: &mut string::String, $t1|bytes: vector) { + var $t2: string::String + 0: $t2 := string::utf8($t1) + 1: string::append($t0, $t2) + 2: trace_local[s]($t0) + 3: return () +} + + +[variant baseline] +public fun string::from_ascii($t0|s: ascii::String): string::String { + var $t1: vector + var $t2: string::String + 0: $t1 := ascii::into_bytes($t0) + 1: $t2 := pack string::String($t1) + 2: return $t2 +} + + +[variant baseline] +native fun string::internal_check_utf8($t0|v: vector): bool; + + +[variant baseline] +native fun string::internal_index_of($t0|v: vector, $t1|r: vector): u64; + + +[variant baseline] +native fun string::internal_is_char_boundary($t0|v: vector, $t1|i: u64): bool; + + +[variant baseline] +native fun string::internal_sub_string($t0|v: vector, $t1|i: u64, $t2|j: u64): vector; + + +[variant baseline] +public fun string::sub_string($t0|s: string::String, $t1|i: u64, $t2|j: u64): string::String { + var $t3: string::String + 0: $t3 := string::substring($t0, $t1, $t2) + 1: return $t3 +} + + +[variant baseline] +public fun string::to_ascii($t0|s: string::String): ascii::String { + var $t1: vector + var $t2: ascii::String + 0: $t1 := unpack string::String($t0) + 1: $t2 := ascii::string($t1) + 2: return $t2 +} + + +[variant baseline] +public fun string::try_utf8($t0|bytes: vector): option::Option { + var $t1|tmp#$1: option::Option + var $t2: bool + var $t3: string::String + 0: $t2 := string::internal_check_utf8($t0) + 1: if ($t2) goto 2 else goto 6 + 2: label L1 + 3: $t3 := pack string::String($t0) + 4: $t1 := option::some($t3) + 5: goto 8 + 6: label L0 + 7: $t1 := option::none() + 8: label L2 + 9: return $t1 +} + + +[variant baseline] +public fun string::utf8($t0|bytes: vector): string::String { + var $t1: bool + var $t2: u64 + var $t3: string::String + 0: $t1 := string::internal_check_utf8($t0) + 1: if ($t1) goto 2 else goto 4 + 2: label L1 + 3: goto 7 + 4: label L0 + 5: $t2 := 1 + 6: abort($t2) + 7: label L2 + 8: $t3 := pack string::String($t0) + 9: return $t3 } @@ -908,6 +4422,10 @@ fun vector::borrow_mut[baseline] borrowed_by: Reference($t0) -> {([], Return(0))} borrows_from: Return(0) -> {([], Reference($t0))} +fun option::borrow_mut[baseline] +borrowed_by: Reference($t0) -> {(.vec (vector<#0>)/[], Return(0))} +borrows_from: Return(0) -> {(.vec (vector<#0>)/[], Reference($t0))} + fun MultiLayerCalling::inner[baseline] borrowed_by: Reference($t0) -> {(.v (vector)/[], Return(0))} borrows_from: Return(0) -> {(.v (vector)/[], Reference($t0))} diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/borrow/function_call.move b/external-crates/move/crates/move-stackless-bytecode/tests/borrow/function_call.move index 003dbbcee8dab..b2f766c47e846 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/borrow/function_call.move +++ b/external-crates/move/crates/move-stackless-bytecode/tests/borrow/function_call.move @@ -1,13 +1,17 @@ +// dep: ../move-stdlib/sources/macros.move +// dep: ../move-stdlib/sources/u64.move +// dep: ../move-stdlib/sources/option.move +// dep: ../move-stdlib/sources/ascii.move +// dep: ../move-stdlib/sources/string.move // dep: ../move-stdlib/sources/vector.move module 0x2::MultiLayerCalling { - use std::vector; - struct HasVector { + public struct HasVector { v: vector, } - struct HasAnotherVector { + public struct HasAnotherVector { v: vector, } diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/borrow/hyper_edge.exp b/external-crates/move/crates/move-stackless-bytecode/tests/borrow/hyper_edge.exp index e367f9ebff288..f3986a7ff742b 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/borrow/hyper_edge.exp +++ b/external-crates/move/crates/move-stackless-bytecode/tests/borrow/hyper_edge.exp @@ -1,5 +1,339 @@ ============ initial translation from Move ================ +[variant baseline] +public fun u64::diff($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: bool + var $t10: u64 + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: u64 + var $t16: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := move($t1) + 3: $t4 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := copy($t4) + 6: $t9 := >($t7, $t8) + 7: if ($t9) goto 8 else goto 14 + 8: label L1 + 9: $t10 := move($t3) + 10: $t11 := move($t4) + 11: $t12 := -($t10, $t11) + 12: $t2 := $t12 + 13: goto 20 + 14: label L0 + 15: $t13 := move($t4) + 16: $t14 := move($t3) + 17: $t15 := -($t13, $t14) + 18: $t2 := $t15 + 19: goto 20 + 20: label L2 + 21: $t16 := move($t2) + 22: return $t16 +} + + +[variant baseline] +public fun u64::divide_and_round_up($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: u64 + var $t10: u64 + var $t11: bool + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: u64 + var $t16: u64 + var $t17: u64 + var $t18: u64 + var $t19: u64 + var $t20: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := move($t1) + 3: $t4 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := copy($t4) + 6: $t9 := %($t7, $t8) + 7: $t10 := 0 + 8: $t11 := ==($t9, $t10) + 9: if ($t11) goto 10 else goto 16 + 10: label L1 + 11: $t12 := move($t3) + 12: $t13 := move($t4) + 13: $t14 := /($t12, $t13) + 14: $t2 := $t14 + 15: goto 24 + 16: label L0 + 17: $t15 := move($t3) + 18: $t16 := move($t4) + 19: $t17 := /($t15, $t16) + 20: $t18 := 1 + 21: $t19 := +($t17, $t18) + 22: $t2 := $t19 + 23: goto 24 + 24: label L2 + 25: $t20 := move($t2) + 26: return $t20 +} + + +[variant baseline] +public fun u64::max($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: bool + var $t10: u64 + var $t11: u64 + var $t12: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := move($t1) + 3: $t4 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := copy($t4) + 6: $t9 := >($t7, $t8) + 7: if ($t9) goto 8 else goto 12 + 8: label L1 + 9: $t10 := move($t3) + 10: $t2 := $t10 + 11: goto 16 + 12: label L0 + 13: $t11 := move($t4) + 14: $t2 := $t11 + 15: goto 16 + 16: label L2 + 17: $t12 := move($t2) + 18: return $t12 +} + + +[variant baseline] +public fun u64::min($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: bool + var $t10: u64 + var $t11: u64 + var $t12: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := move($t1) + 3: $t4 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := copy($t4) + 6: $t9 := <($t7, $t8) + 7: if ($t9) goto 8 else goto 12 + 8: label L1 + 9: $t10 := move($t3) + 10: $t2 := $t10 + 11: goto 16 + 12: label L0 + 13: $t11 := move($t4) + 14: $t2 := $t11 + 15: goto 16 + 16: label L2 + 17: $t12 := move($t2) + 18: return $t12 +} + + +[variant baseline] +public fun u64::pow($t0|base: u64, $t1|exponent: u8): u64 { + var $t2|base#1#1: u64 + var $t3|exponent#1#1: u8 + var $t4|res#1#1: u64 + var $t5: u64 + var $t6: u8 + var $t7: u64 + var $t8: u8 + var $t9: u8 + var $t10: bool + var $t11: u8 + var $t12: u8 + var $t13: u8 + var $t14: u8 + var $t15: bool + var $t16: u64 + var $t17: u64 + var $t18: u64 + var $t19: u8 + var $t20: u8 + var $t21: u8 + var $t22: u64 + var $t23: u64 + var $t24: u64 + var $t25: u8 + var $t26: u8 + var $t27: u8 + var $t28: u64 + 0: $t5 := move($t0) + 1: $t2 := $t5 + 2: $t6 := move($t1) + 3: $t3 := $t6 + 4: $t7 := 1 + 5: $t4 := $t7 + 6: goto 7 + 7: label L5 + 8: $t8 := copy($t3) + 9: $t9 := 1 + 10: $t10 := >=($t8, $t9) + 11: if ($t10) goto 12 else goto 41 + 12: label L1 + 13: goto 14 + 14: label L2 + 15: $t11 := copy($t3) + 16: $t12 := 2 + 17: $t13 := %($t11, $t12) + 18: $t14 := 0 + 19: $t15 := ==($t13, $t14) + 20: if ($t15) goto 21 else goto 31 + 21: label L4 + 22: $t16 := copy($t2) + 23: $t17 := move($t2) + 24: $t18 := *($t16, $t17) + 25: $t2 := $t18 + 26: $t19 := move($t3) + 27: $t20 := 2 + 28: $t21 := /($t19, $t20) + 29: $t3 := $t21 + 30: goto 7 + 31: label L3 + 32: $t22 := move($t4) + 33: $t23 := copy($t2) + 34: $t24 := *($t22, $t23) + 35: $t4 := $t24 + 36: $t25 := move($t3) + 37: $t26 := 1 + 38: $t27 := -($t25, $t26) + 39: $t3 := $t27 + 40: goto 7 + 41: label L0 + 42: $t28 := move($t4) + 43: return $t28 +} + + +[variant baseline] +public fun u64::sqrt($t0|x: u64): u64 { + var $t1|bit#1#1: u128 + var $t2|res#1#1: u128 + var $t3|x#1#1: u64 + var $t4|x#2#1: u128 + var $t5: u64 + var $t6: u128 + var $t7: u128 + var $t8: u64 + var $t9: u128 + var $t10: u128 + var $t11: u128 + var $t12: bool + var $t13: u128 + var $t14: u128 + var $t15: u128 + var $t16: u128 + var $t17: bool + var $t18: u128 + var $t19: u128 + var $t20: u128 + var $t21: u128 + var $t22: u128 + var $t23: u128 + var $t24: u8 + var $t25: u128 + var $t26: u128 + var $t27: u128 + var $t28: u128 + var $t29: u8 + var $t30: u128 + var $t31: u128 + var $t32: u8 + var $t33: u128 + var $t34: u128 + var $t35: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := 18446744073709551616 + 3: $t1 := $t6 + 4: $t7 := 0 + 5: $t2 := $t7 + 6: $t8 := move($t3) + 7: $t9 := (u128)($t8) + 8: $t4 := $t9 + 9: goto 10 + 10: label L6 + 11: $t10 := copy($t1) + 12: $t11 := 0 + 13: $t12 := !=($t10, $t11) + 14: if ($t12) goto 15 else goto 50 + 15: label L1 + 16: goto 17 + 17: label L2 + 18: $t13 := copy($t4) + 19: $t14 := copy($t2) + 20: $t15 := copy($t1) + 21: $t16 := +($t14, $t15) + 22: $t17 := >=($t13, $t16) + 23: if ($t17) goto 24 else goto 38 + 24: label L4 + 25: $t18 := move($t4) + 26: $t19 := copy($t2) + 27: $t20 := copy($t1) + 28: $t21 := +($t19, $t20) + 29: $t22 := -($t18, $t21) + 30: $t4 := $t22 + 31: $t23 := move($t2) + 32: $t24 := 1 + 33: $t25 := >>($t23, $t24) + 34: $t26 := copy($t1) + 35: $t27 := +($t25, $t26) + 36: $t2 := $t27 + 37: goto 44 + 38: label L3 + 39: $t28 := move($t2) + 40: $t29 := 1 + 41: $t30 := >>($t28, $t29) + 42: $t2 := $t30 + 43: goto 44 + 44: label L5 + 45: $t31 := move($t1) + 46: $t32 := 2 + 47: $t33 := >>($t31, $t32) + 48: $t1 := $t33 + 49: goto 10 + 50: label L0 + 51: $t34 := move($t2) + 52: $t35 := (u64)($t34) + 53: return $t35 +} + + [variant baseline] public fun vector::append<#0>($t0|lhs: &mut vector<#0>, $t1|other: vector<#0>) { var $t2: &mut vector<#0> @@ -231,27 +565,25 @@ public fun vector::insert<#0>($t0|v: &mut vector<#0>, $t1|e: #0, $t2|i: u64) { 15: $t13 := move($t1) 16: vector::push_back<#0>($t12, $t13) 17: goto 18 - 18: label L5 + 18: label L4 19: $t14 := copy($t2) 20: $t15 := copy($t3) 21: $t16 := <($t14, $t15) - 22: if ($t16) goto 23 else goto 35 + 22: if ($t16) goto 23 else goto 33 23: label L3 - 24: goto 25 - 25: label L4 - 26: $t17 := copy($t0) - 27: $t18 := copy($t2) - 28: $t19 := copy($t3) - 29: vector::swap<#0>($t17, $t18, $t19) - 30: $t20 := move($t2) - 31: $t21 := 1 - 32: $t22 := +($t20, $t21) - 33: $t2 := $t22 - 34: goto 18 - 35: label L2 - 36: $t23 := move($t0) - 37: destroy($t23) - 38: return () + 24: $t17 := copy($t0) + 25: $t18 := copy($t2) + 26: $t19 := copy($t3) + 27: vector::swap<#0>($t17, $t18, $t19) + 28: $t20 := move($t2) + 29: $t21 := 1 + 30: $t22 := +($t20, $t21) + 31: $t2 := $t22 + 32: goto 18 + 33: label L2 + 34: $t23 := move($t0) + 35: destroy($t23) + 36: return () } @@ -329,31 +661,29 @@ public fun vector::remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { 16: $t15 := -($t13, $t14) 17: $t4 := $t15 18: goto 19 - 19: label L5 + 19: label L4 20: $t16 := copy($t1) 21: $t17 := copy($t4) 22: $t18 := <($t16, $t17) - 23: if ($t18) goto 24 else goto 40 + 23: if ($t18) goto 24 else goto 38 24: label L3 - 25: goto 26 - 26: label L4 - 27: $t19 := copy($t0) - 28: $t3 := $t19 - 29: $t20 := copy($t1) - 30: $t2 := $t20 - 31: $t21 := move($t1) - 32: $t22 := 1 - 33: $t23 := +($t21, $t22) - 34: $t1 := $t23 - 35: $t24 := move($t3) - 36: $t25 := move($t2) - 37: $t26 := copy($t1) - 38: vector::swap<#0>($t24, $t25, $t26) - 39: goto 19 - 40: label L2 - 41: $t27 := move($t0) - 42: $t28 := vector::pop_back<#0>($t27) - 43: return $t28 + 25: $t19 := copy($t0) + 26: $t3 := $t19 + 27: $t20 := copy($t1) + 28: $t2 := $t20 + 29: $t21 := move($t1) + 30: $t22 := 1 + 31: $t23 := +($t21, $t22) + 32: $t1 := $t23 + 33: $t24 := move($t3) + 34: $t25 := move($t2) + 35: $t26 := copy($t1) + 36: vector::swap<#0>($t24, $t25, $t26) + 37: goto 19 + 38: label L2 + 39: $t27 := move($t0) + 40: $t28 := vector::pop_back<#0>($t27) + 41: return $t28 } @@ -406,31 +736,29 @@ public fun vector::reverse<#0>($t0|v: &mut vector<#0>) { 17: $t14 := -($t12, $t13) 18: $t1 := $t14 19: goto 20 - 20: label L5 + 20: label L4 21: $t15 := copy($t2) 22: $t16 := copy($t1) 23: $t17 := <($t15, $t16) - 24: if ($t17) goto 25 else goto 41 + 24: if ($t17) goto 25 else goto 39 25: label L3 - 26: goto 27 - 27: label L4 - 28: $t18 := copy($t0) - 29: $t19 := copy($t2) - 30: $t20 := copy($t1) - 31: vector::swap<#0>($t18, $t19, $t20) - 32: $t21 := move($t2) - 33: $t22 := 1 - 34: $t23 := +($t21, $t22) - 35: $t2 := $t23 - 36: $t24 := move($t1) - 37: $t25 := 1 - 38: $t26 := -($t24, $t25) - 39: $t1 := $t26 - 40: goto 20 - 41: label L2 - 42: $t27 := move($t0) - 43: destroy($t27) - 44: return () + 26: $t18 := copy($t0) + 27: $t19 := copy($t2) + 28: $t20 := copy($t1) + 29: vector::swap<#0>($t18, $t19, $t20) + 30: $t21 := move($t2) + 31: $t22 := 1 + 32: $t23 := +($t21, $t22) + 33: $t2 := $t23 + 34: $t24 := move($t1) + 35: $t25 := 1 + 36: $t26 := -($t24, $t25) + 37: $t1 := $t26 + 38: goto 20 + 39: label L2 + 40: $t27 := move($t0) + 41: destroy($t27) + 42: return () } @@ -504,376 +832,3562 @@ public fun vector::swap_remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { [variant baseline] -public fun Collection::borrow_mut<#0>($t0|c: &mut Collection::Collection<#0>, $t1|i: u64): &mut #0 { - var $t2: &mut Collection::Collection<#0> - var $t3: &mut vector<#0> +public fun option::borrow<#0>($t0|t: &option::Option<#0>): � { + var $t1: &option::Option<#0> + var $t2: bool + var $t3: &option::Option<#0> var $t4: u64 - var $t5: &mut #0 + var $t5: &option::Option<#0> + var $t6: &vector<#0> + var $t7: u64 + var $t8: � + 0: $t1 := copy($t0) + 1: $t2 := option::is_some<#0>($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 10 + 5: label L0 + 6: $t3 := move($t0) + 7: destroy($t3) + 8: $t4 := 262145 + 9: abort($t4) + 10: label L2 + 11: $t5 := move($t0) + 12: $t6 := borrow_field>.vec($t5) + 13: $t7 := 0 + 14: $t8 := vector::borrow<#0>($t6, $t7) + 15: return $t8 +} + + +[variant baseline] +public fun option::borrow_mut<#0>($t0|t: &mut option::Option<#0>): &mut #0 { + var $t1: &mut option::Option<#0> + var $t2: &option::Option<#0> + var $t3: bool + var $t4: &mut option::Option<#0> + var $t5: u64 + var $t6: &mut option::Option<#0> + var $t7: &mut vector<#0> + var $t8: u64 + var $t9: &mut #0 + 0: $t1 := copy($t0) + 1: $t2 := freeze_ref($t1) + 2: $t3 := option::is_some<#0>($t2) + 3: if ($t3) goto 4 else goto 6 + 4: label L1 + 5: goto 11 + 6: label L0 + 7: $t4 := move($t0) + 8: destroy($t4) + 9: $t5 := 262145 + 10: abort($t5) + 11: label L2 + 12: $t6 := move($t0) + 13: $t7 := borrow_field>.vec($t6) + 14: $t8 := 0 + 15: $t9 := vector::borrow_mut<#0>($t7, $t8) + 16: return $t9 +} + + +[variant baseline] +public fun option::contains<#0>($t0|t: &option::Option<#0>, $t1|e_ref: �): bool { + var $t2: &option::Option<#0> + var $t3: &vector<#0> + var $t4: � + var $t5: bool 0: $t2 := move($t0) - 1: $t3 := borrow_field>.items($t2) + 1: $t3 := borrow_field>.vec($t2) 2: $t4 := move($t1) - 3: $t5 := vector::borrow_mut<#0>($t3, $t4) + 3: $t5 := vector::contains<#0>($t3, $t4) 4: return $t5 } [variant baseline] -public fun Collection::make_collection<#0>(): Collection::Collection<#0> { - var $t0: vector<#0> - var $t1: address - var $t2: Collection::Collection<#0> - 0: $t0 := vector::empty<#0>() - 1: $t1 := 0x2 - 2: $t2 := pack Collection::Collection<#0>($t0, $t1) - 3: return $t2 +public fun option::swap<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0): #0 { + var $t2|old_value#1#0: #0 + var $t3|vec_ref#1#0: &mut vector<#0> + var $t4: &mut option::Option<#0> + var $t5: &option::Option<#0> + var $t6: bool + var $t7: &mut option::Option<#0> + var $t8: u64 + var $t9: &mut option::Option<#0> + var $t10: &mut vector<#0> + var $t11: &mut vector<#0> + var $t12: #0 + var $t13: &mut vector<#0> + var $t14: #0 + var $t15: #0 + 0: $t4 := copy($t0) + 1: $t5 := freeze_ref($t4) + 2: $t6 := option::is_some<#0>($t5) + 3: if ($t6) goto 4 else goto 6 + 4: label L1 + 5: goto 11 + 6: label L0 + 7: $t7 := move($t0) + 8: destroy($t7) + 9: $t8 := 262145 + 10: abort($t8) + 11: label L2 + 12: $t9 := move($t0) + 13: $t10 := borrow_field>.vec($t9) + 14: $t3 := $t10 + 15: $t11 := copy($t3) + 16: $t12 := vector::pop_back<#0>($t11) + 17: $t2 := $t12 + 18: $t13 := move($t3) + 19: $t14 := move($t1) + 20: vector::push_back<#0>($t13, $t14) + 21: $t15 := move($t2) + 22: return $t15 } [variant baseline] -public fun Test::foo<#0>($t0|i: u64) { - var $t1|c#1#0: Collection::Collection> - var $t2|t#1#0: &mut Test::Token<#0> - var $t3: Collection::Collection> - var $t4: &mut Collection::Collection> - var $t5: u64 - var $t6: &mut Test::Token<#0> - var $t7: u64 - var $t8: &mut Test::Token<#0> - var $t9: &mut u64 - 0: $t3 := Collection::make_collection>() - 1: $t1 := $t3 - 2: $t4 := borrow_local($t1) - 3: $t5 := move($t0) - 4: $t6 := Collection::borrow_mut>($t4, $t5) - 5: $t2 := $t6 - 6: $t7 := 0 - 7: $t8 := move($t2) - 8: $t9 := borrow_field>.value($t8) - 9: write_ref($t9, $t7) - 10: return () +public fun option::borrow_with_default<#0>($t0|t: &option::Option<#0>, $t1|default_ref: �): � { + var $t2|tmp#$2: � + var $t3|vec_ref#1#0: &vector<#0> + var $t4: &option::Option<#0> + var $t5: &vector<#0> + var $t6: &vector<#0> + var $t7: bool + var $t8: &vector<#0> + var $t9: � + var $t10: � + var $t11: &vector<#0> + var $t12: u64 + var $t13: � + var $t14: � + 0: $t4 := move($t0) + 1: $t5 := borrow_field>.vec($t4) + 2: $t3 := $t5 + 3: $t6 := copy($t3) + 4: $t7 := vector::is_empty<#0>($t6) + 5: if ($t7) goto 6 else goto 12 + 6: label L1 + 7: $t8 := move($t3) + 8: destroy($t8) + 9: $t9 := move($t1) + 10: $t2 := $t9 + 11: goto 20 + 12: label L0 + 13: $t10 := move($t1) + 14: destroy($t10) + 15: $t11 := move($t3) + 16: $t12 := 0 + 17: $t13 := vector::borrow<#0>($t11, $t12) + 18: $t2 := $t13 + 19: goto 20 + 20: label L2 + 21: $t14 := move($t2) + 22: return $t14 } -============ after pipeline `borrow` ================ [variant baseline] -public fun vector::append<#0>($t0|lhs: &mut vector<#0>, $t1|other: vector<#0>) { - var $t2: &mut vector<#0> - var $t3: vector<#0> - var $t4: bool - var $t5: bool - var $t6: &mut vector<#0> - var $t7: #0 - var $t8: vector<#0> - 0: $t2 := borrow_local($t1) - 1: vector::reverse<#0>($t2) - 2: label L3 - 3: $t3 := copy($t1) - 4: $t4 := vector::is_empty<#0>($t3) - 5: $t5 := !($t4) - 6: if ($t5) goto 7 else goto 13 - 7: label L1 +public fun option::destroy_none<#0>($t0|t: option::Option<#0>) { + var $t1: &option::Option<#0> + var $t2: bool + var $t3: u64 + var $t4: option::Option<#0> + var $t5: vector<#0> + 0: $t1 := borrow_local($t0) + 1: $t2 := option::is_none<#0>($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t3 := 262144 + 7: abort($t3) 8: label L2 - 9: $t6 := borrow_local($t1) - 10: $t7 := vector::pop_back<#0>($t6) - 11: vector::push_back<#0>($t0, $t7) - 12: goto 2 - 13: label L0 - 14: destroy($t0) - 15: $t8 := move($t1) - 16: vector::destroy_empty<#0>($t8) - 17: trace_local[lhs]($t0) - 18: return () + 9: $t4 := move($t0) + 10: $t5 := unpack option::Option<#0>($t4) + 11: vector::destroy_empty<#0>($t5) + 12: return () } [variant baseline] -public native fun vector::borrow<#0>($t0|v: vector<#0>, $t1|i: u64): #0; +public fun option::destroy_some<#0>($t0|t: option::Option<#0>): #0 { + var $t1|elem#1#0: #0 + var $t2|vec#1#0: vector<#0> + var $t3: &option::Option<#0> + var $t4: bool + var $t5: u64 + var $t6: option::Option<#0> + var $t7: vector<#0> + var $t8: &mut vector<#0> + var $t9: #0 + var $t10: vector<#0> + var $t11: #0 + 0: $t3 := borrow_local($t0) + 1: $t4 := option::is_some<#0>($t3) + 2: if ($t4) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t5 := 262145 + 7: abort($t5) + 8: label L2 + 9: $t6 := move($t0) + 10: $t7 := unpack option::Option<#0>($t6) + 11: $t2 := $t7 + 12: $t8 := borrow_local($t2) + 13: $t9 := vector::pop_back<#0>($t8) + 14: $t1 := $t9 + 15: $t10 := move($t2) + 16: vector::destroy_empty<#0>($t10) + 17: $t11 := move($t1) + 18: return $t11 +} [variant baseline] -public native fun vector::borrow_mut<#0>($t0|v: &mut vector<#0>, $t1|i: u64): &mut #0; +public fun option::destroy_with_default<#0>($t0|t: option::Option<#0>, $t1|default: #0): #0 { + var $t2|tmp#$2: #0 + var $t3|vec#1#0: vector<#0> + var $t4: option::Option<#0> + var $t5: vector<#0> + var $t6: &vector<#0> + var $t7: bool + var $t8: #0 + var $t9: &mut vector<#0> + var $t10: #0 + var $t11: #0 + 0: $t4 := move($t0) + 1: $t5 := unpack option::Option<#0>($t4) + 2: $t3 := $t5 + 3: $t6 := borrow_local($t3) + 4: $t7 := vector::is_empty<#0>($t6) + 5: if ($t7) goto 6 else goto 10 + 6: label L1 + 7: $t8 := move($t1) + 8: $t2 := $t8 + 9: goto 15 + 10: label L0 + 11: $t9 := borrow_local($t3) + 12: $t10 := vector::pop_back<#0>($t9) + 13: $t2 := $t10 + 14: goto 15 + 15: label L2 + 16: $t11 := move($t2) + 17: return $t11 +} [variant baseline] -public fun vector::contains<#0>($t0|v: vector<#0>, $t1|e: #0): bool { - var $t2|i#1#0: u64 - var $t3|len#1#0: u64 - var $t4: u64 +public fun option::extract<#0>($t0|t: &mut option::Option<#0>): #0 { + var $t1: &mut option::Option<#0> + var $t2: &option::Option<#0> + var $t3: bool + var $t4: &mut option::Option<#0> var $t5: u64 - var $t6: bool - var $t7: #0 - var $t8: bool - var $t9: bool - var $t10: u64 - var $t11: bool - 0: $t4 := 0 - 1: $t2 := $t4 - 2: $t5 := vector::length<#0>($t0) - 3: label L5 - 4: $t6 := <($t2, $t5) - 5: if ($t6) goto 6 else goto 18 - 6: label L1 - 7: label L2 - 8: $t7 := vector::borrow<#0>($t0, $t2) - 9: $t8 := ==($t7, $t1) + var $t6: &mut option::Option<#0> + var $t7: &mut vector<#0> + var $t8: #0 + 0: $t1 := copy($t0) + 1: $t2 := freeze_ref($t1) + 2: $t3 := option::is_some<#0>($t2) + 3: if ($t3) goto 4 else goto 6 + 4: label L1 + 5: goto 11 + 6: label L0 + 7: $t4 := move($t0) + 8: destroy($t4) + 9: $t5 := 262145 + 10: abort($t5) + 11: label L2 + 12: $t6 := move($t0) + 13: $t7 := borrow_field>.vec($t6) + 14: $t8 := vector::pop_back<#0>($t7) + 15: return $t8 +} + + +[variant baseline] +public fun option::fill<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0) { + var $t2|vec_ref#1#0: &mut vector<#0> + var $t3: &mut option::Option<#0> + var $t4: &mut vector<#0> + var $t5: &mut vector<#0> + var $t6: &vector<#0> + var $t7: bool + var $t8: &mut vector<#0> + var $t9: u64 + var $t10: &mut vector<#0> + var $t11: #0 + 0: $t3 := move($t0) + 1: $t4 := borrow_field>.vec($t3) + 2: $t2 := $t4 + 3: $t5 := copy($t2) + 4: $t6 := freeze_ref($t5) + 5: $t7 := vector::is_empty<#0>($t6) + 6: if ($t7) goto 7 else goto 9 + 7: label L1 + 8: goto 14 + 9: label L0 + 10: $t8 := move($t2) + 11: destroy($t8) + 12: $t9 := 262144 + 13: abort($t9) + 14: label L2 + 15: $t10 := move($t2) + 16: $t11 := move($t1) + 17: vector::push_back<#0>($t10, $t11) + 18: return () +} + + +[variant baseline] +public fun option::get_with_default<#0>($t0|t: &option::Option<#0>, $t1|default: #0): #0 { + var $t2|tmp#$2: #0 + var $t3|vec_ref#1#0: &vector<#0> + var $t4: &option::Option<#0> + var $t5: &vector<#0> + var $t6: &vector<#0> + var $t7: bool + var $t8: &vector<#0> + var $t9: #0 + var $t10: &vector<#0> + var $t11: u64 + var $t12: � + var $t13: #0 + var $t14: #0 + 0: $t4 := move($t0) + 1: $t5 := borrow_field>.vec($t4) + 2: $t3 := $t5 + 3: $t6 := copy($t3) + 4: $t7 := vector::is_empty<#0>($t6) + 5: if ($t7) goto 6 else goto 12 + 6: label L1 + 7: $t8 := move($t3) + 8: destroy($t8) + 9: $t9 := move($t1) + 10: $t2 := $t9 + 11: goto 19 + 12: label L0 + 13: $t10 := move($t3) + 14: $t11 := 0 + 15: $t12 := vector::borrow<#0>($t10, $t11) + 16: $t13 := read_ref($t12) + 17: $t2 := $t13 + 18: goto 19 + 19: label L2 + 20: $t14 := move($t2) + 21: return $t14 +} + + +[variant baseline] +public fun option::is_none<#0>($t0|t: &option::Option<#0>): bool { + var $t1: &option::Option<#0> + var $t2: &vector<#0> + var $t3: bool + 0: $t1 := move($t0) + 1: $t2 := borrow_field>.vec($t1) + 2: $t3 := vector::is_empty<#0>($t2) + 3: return $t3 +} + + +[variant baseline] +public fun option::is_some<#0>($t0|t: &option::Option<#0>): bool { + var $t1: &option::Option<#0> + var $t2: &vector<#0> + var $t3: bool + var $t4: bool + 0: $t1 := move($t0) + 1: $t2 := borrow_field>.vec($t1) + 2: $t3 := vector::is_empty<#0>($t2) + 3: $t4 := !($t3) + 4: return $t4 +} + + +[variant baseline] +public fun option::none<#0>(): option::Option<#0> { + var $t0: vector<#0> + var $t1: option::Option<#0> + 0: $t0 := vector::empty<#0>() + 1: $t1 := pack option::Option<#0>($t0) + 2: return $t1 +} + + +[variant baseline] +public fun option::some<#0>($t0|e: #0): option::Option<#0> { + var $t1: #0 + var $t2: vector<#0> + var $t3: option::Option<#0> + 0: $t1 := move($t0) + 1: $t2 := vector::singleton<#0>($t1) + 2: $t3 := pack option::Option<#0>($t2) + 3: return $t3 +} + + +[variant baseline] +public fun option::swap_or_fill<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0): option::Option<#0> { + var $t2|tmp#$2: option::Option<#0> + var $t3|old_value#1#0: option::Option<#0> + var $t4|vec_ref#1#0: &mut vector<#0> + var $t5: &mut option::Option<#0> + var $t6: &mut vector<#0> + var $t7: &mut vector<#0> + var $t8: &vector<#0> + var $t9: bool + var $t10: option::Option<#0> + var $t11: &mut vector<#0> + var $t12: #0 + var $t13: option::Option<#0> + var $t14: option::Option<#0> + var $t15: &mut vector<#0> + var $t16: #0 + var $t17: option::Option<#0> + 0: $t5 := move($t0) + 1: $t6 := borrow_field>.vec($t5) + 2: $t4 := $t6 + 3: $t7 := copy($t4) + 4: $t8 := freeze_ref($t7) + 5: $t9 := vector::is_empty<#0>($t8) + 6: if ($t9) goto 7 else goto 11 + 7: label L1 + 8: $t10 := option::none<#0>() + 9: $t2 := $t10 + 10: goto 17 + 11: label L0 + 12: $t11 := copy($t4) + 13: $t12 := vector::pop_back<#0>($t11) + 14: $t13 := option::some<#0>($t12) + 15: $t2 := $t13 + 16: goto 17 + 17: label L2 + 18: $t14 := move($t2) + 19: $t3 := $t14 + 20: $t15 := move($t4) + 21: $t16 := move($t1) + 22: vector::push_back<#0>($t15, $t16) + 23: $t17 := move($t3) + 24: return $t17 +} + + +[variant baseline] +public fun option::to_vec<#0>($t0|t: option::Option<#0>): vector<#0> { + var $t1: option::Option<#0> + var $t2: vector<#0> + 0: $t1 := move($t0) + 1: $t2 := unpack option::Option<#0>($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::append($t0|string: &mut ascii::String, $t1|other: ascii::String) { + var $t2: &mut ascii::String + var $t3: &mut vector + var $t4: ascii::String + var $t5: vector + 0: $t2 := move($t0) + 1: $t3 := borrow_field.bytes($t2) + 2: $t4 := move($t1) + 3: $t5 := ascii::into_bytes($t4) + 4: vector::append($t3, $t5) + 5: return () +} + + +[variant baseline] +public fun ascii::index_of($t0|string: &ascii::String, $t1|substr: &ascii::String): u64 { + var $t2|tmp#$2: bool + var $t3|i#1#0: u64 + var $t4|j#1#0: u64 + var $t5|m#1#0: u64 + var $t6|n#1#0: u64 + var $t7: u64 + var $t8: &ascii::String + var $t9: u64 + var $t10: &ascii::String + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: bool + var $t15: &ascii::String + var $t16: &ascii::String + var $t17: u64 + var $t18: u64 + var $t19: u64 + var $t20: u64 + var $t21: u64 + var $t22: bool + var $t23: u64 + var $t24: u64 + var $t25: u64 + var $t26: bool + var $t27: &ascii::String + var $t28: &vector + var $t29: u64 + var $t30: u64 + var $t31: u64 + var $t32: &u8 + var $t33: u8 + var $t34: &ascii::String + var $t35: &vector + var $t36: u64 + var $t37: &u8 + var $t38: u8 + var $t39: bool + var $t40: bool + var $t41: bool + var $t42: u64 + var $t43: u64 + var $t44: u64 + var $t45: u64 + var $t46: u64 + var $t47: bool + var $t48: &ascii::String + var $t49: &ascii::String + var $t50: u64 + var $t51: u64 + var $t52: u64 + var $t53: u64 + var $t54: &ascii::String + var $t55: &ascii::String + var $t56: u64 + 0: $t7 := 0 + 1: $t3 := $t7 + 2: $t8 := copy($t0) + 3: $t9 := ascii::length($t8) + 4: $t10 := copy($t1) + 5: $t11 := ascii::length($t10) + 6: $t5 := $t11 + 7: $t6 := $t9 + 8: $t12 := copy($t6) + 9: $t13 := copy($t5) + 10: $t14 := <($t12, $t13) + 11: if ($t14) goto 12 else goto 19 + 12: label L1 + 13: $t15 := move($t1) + 14: destroy($t15) + 15: $t16 := move($t0) + 16: destroy($t16) + 17: $t17 := move($t6) + 18: return $t17 + 19: label L0 + 20: $t18 := copy($t3) + 21: $t19 := copy($t6) + 22: $t20 := copy($t5) + 23: $t21 := -($t19, $t20) + 24: $t22 := <=($t18, $t21) + 25: if ($t22) goto 26 else goto 84 + 26: label L3 + 27: $t23 := 0 + 28: $t4 := $t23 + 29: goto 30 + 30: label L10 + 31: $t24 := copy($t4) + 32: $t25 := copy($t5) + 33: $t26 := <($t24, $t25) + 34: if ($t26) goto 35 else goto 53 + 35: label L5 + 36: goto 37 + 37: label L6 + 38: $t27 := copy($t0) + 39: $t28 := borrow_field.bytes($t27) + 40: $t29 := copy($t3) + 41: $t30 := copy($t4) + 42: $t31 := +($t29, $t30) + 43: $t32 := vector::borrow($t28, $t31) + 44: $t33 := read_ref($t32) + 45: $t34 := copy($t1) + 46: $t35 := borrow_field.bytes($t34) + 47: $t36 := copy($t4) + 48: $t37 := vector::borrow($t35, $t36) + 49: $t38 := read_ref($t37) + 50: $t39 := ==($t33, $t38) + 51: $t2 := $t39 + 52: goto 57 + 53: label L4 + 54: $t40 := false + 55: $t2 := $t40 + 56: goto 57 + 57: label L7 + 58: $t41 := move($t2) + 59: if ($t41) goto 60 else goto 66 + 60: label L9 + 61: $t42 := move($t4) + 62: $t43 := 1 + 63: $t44 := +($t42, $t43) + 64: $t4 := $t44 + 65: goto 30 + 66: label L8 + 67: $t45 := move($t4) + 68: $t46 := copy($t5) + 69: $t47 := ==($t45, $t46) + 70: if ($t47) goto 71 else goto 78 + 71: label L12 + 72: $t48 := move($t1) + 73: destroy($t48) + 74: $t49 := move($t0) + 75: destroy($t49) + 76: $t50 := move($t3) + 77: return $t50 + 78: label L11 + 79: $t51 := move($t3) + 80: $t52 := 1 + 81: $t53 := +($t51, $t52) + 82: $t3 := $t53 + 83: goto 19 + 84: label L2 + 85: $t54 := move($t1) + 86: destroy($t54) + 87: $t55 := move($t0) + 88: destroy($t55) + 89: $t56 := move($t6) + 90: return $t56 +} + + +[variant baseline] +public fun ascii::insert($t0|s: &mut ascii::String, $t1|at: u64, $t2|o: ascii::String) { + var $t3|e#1#2: u8 + var $t4|v#1#1: vector + var $t5: u64 + var $t6: &mut ascii::String + var $t7: &ascii::String + var $t8: u64 + var $t9: bool + var $t10: &mut ascii::String + var $t11: u64 + var $t12: ascii::String + var $t13: vector + var $t14: &vector + var $t15: bool + var $t16: bool + var $t17: &mut vector + var $t18: u8 + var $t19: &mut ascii::String + var $t20: &mut vector + var $t21: u8 + var $t22: u64 + var $t23: &mut ascii::String + var $t24: vector + 0: $t5 := copy($t1) + 1: $t6 := copy($t0) + 2: $t7 := freeze_ref($t6) + 3: $t8 := ascii::length($t7) + 4: $t9 := <=($t5, $t8) + 5: if ($t9) goto 6 else goto 8 + 6: label L1 + 7: goto 13 + 8: label L0 + 9: $t10 := move($t0) + 10: destroy($t10) + 11: $t11 := 65537 + 12: abort($t11) + 13: label L2 + 14: $t12 := move($t2) + 15: $t13 := ascii::into_bytes($t12) + 16: $t4 := $t13 + 17: goto 18 + 18: label L5 + 19: $t14 := borrow_local($t4) + 20: $t15 := vector::is_empty($t14) + 21: $t16 := !($t15) + 22: if ($t16) goto 23 else goto 33 + 23: label L4 + 24: $t17 := borrow_local($t4) + 25: $t18 := vector::pop_back($t17) + 26: $t3 := $t18 + 27: $t19 := copy($t0) + 28: $t20 := borrow_field.bytes($t19) + 29: $t21 := move($t3) + 30: $t22 := copy($t1) + 31: vector::insert($t20, $t21, $t22) + 32: goto 18 + 33: label L3 + 34: $t23 := move($t0) + 35: destroy($t23) + 36: $t24 := move($t4) + 37: vector::destroy_empty($t24) + 38: return () +} + + +[variant baseline] +public fun ascii::is_empty($t0|string: &ascii::String): bool { + var $t1: &ascii::String + var $t2: &vector + var $t3: bool + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: $t3 := vector::is_empty($t2) + 3: return $t3 +} + + +[variant baseline] +public fun ascii::length($t0|string: &ascii::String): u64 { + var $t1: &ascii::String + var $t2: &vector + var $t3: u64 + 0: $t1 := move($t0) + 1: $t2 := ascii::as_bytes($t1) + 2: $t3 := vector::length($t2) + 3: return $t3 +} + + +[variant baseline] +public fun ascii::all_characters_printable($t0|string: &ascii::String): bool { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: bool + var $t3|i#1#12: u64 + var $t4|i#1#9: u64 + var $t5|stop#1#9: u64 + var $t6|v#1#3: &vector + var $t7: &ascii::String + var $t8: &vector + var $t9: &vector + var $t10: u64 + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: bool + var $t16: u64 + var $t17: &vector + var $t18: u64 + var $t19: &u8 + var $t20: u8 + var $t21: bool + var $t22: bool + var $t23: &vector + var $t24: bool + var $t25: u64 + var $t26: u64 + var $t27: u64 + var $t28: &vector + var $t29: bool + var $t30: bool + 0: $t7 := move($t0) + 1: $t8 := borrow_field.bytes($t7) + 2: $t6 := $t8 + 3: $t9 := copy($t6) + 4: $t10 := vector::length($t9) + 5: $t1 := $t10 + 6: $t11 := 0 + 7: $t4 := $t11 + 8: $t12 := move($t1) + 9: $t5 := $t12 + 10: goto 11 + 11: label L5 + 12: $t13 := copy($t4) + 13: $t14 := copy($t5) + 14: $t15 := <($t13, $t14) + 15: if ($t15) goto 16 else goto 38 + 16: label L1 + 17: $t16 := copy($t4) + 18: $t3 := $t16 + 19: $t17 := copy($t6) + 20: $t18 := move($t3) + 21: $t19 := vector::borrow($t17, $t18) + 22: $t20 := read_ref($t19) + 23: $t21 := ascii::is_printable_char($t20) + 24: $t22 := !($t21) + 25: if ($t22) goto 26 else goto 32 + 26: label L3 + 27: $t23 := move($t6) + 28: destroy($t23) + 29: $t24 := false + 30: $t2 := $t24 + 31: goto 44 + 32: label L2 + 33: $t25 := move($t4) + 34: $t26 := 1 + 35: $t27 := +($t25, $t26) + 36: $t4 := $t27 + 37: goto 11 + 38: label L0 + 39: $t28 := move($t6) + 40: destroy($t28) + 41: $t29 := true + 42: $t2 := $t29 + 43: goto 44 + 44: label L4 + 45: $t30 := move($t2) + 46: return $t30 +} + + +[variant baseline] +public fun ascii::string($t0|bytes: vector): ascii::String { + var $t1|x#1#0: option::Option + var $t2: vector + var $t3: option::Option + var $t4: &option::Option + var $t5: bool + var $t6: u64 + var $t7: option::Option + var $t8: ascii::String + 0: $t2 := move($t0) + 1: $t3 := ascii::try_string($t2) + 2: $t1 := $t3 + 3: $t4 := borrow_local($t1) + 4: $t5 := option::is_some($t4) + 5: if ($t5) goto 6 else goto 8 + 6: label L1 + 7: goto 11 + 8: label L0 + 9: $t6 := 65536 + 10: abort($t6) + 11: label L2 + 12: $t7 := move($t1) + 13: $t8 := option::destroy_some($t7) + 14: return $t8 +} + + +[variant baseline] +public fun ascii::as_bytes($t0|string: &ascii::String): &vector { + var $t1: &ascii::String + var $t2: &vector + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::byte($t0|char: ascii::Char): u8 { + var $t1: ascii::Char + var $t2: u8 + 0: $t1 := move($t0) + 1: $t2 := unpack ascii::Char($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::char($t0|byte: u8): ascii::Char { + var $t1: u8 + var $t2: bool + var $t3: u64 + var $t4: u8 + var $t5: ascii::Char + 0: $t1 := copy($t0) + 1: $t2 := ascii::is_valid_char($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t3 := 65536 + 7: abort($t3) + 8: label L2 + 9: $t4 := move($t0) + 10: $t5 := pack ascii::Char($t4) + 11: return $t5 +} + + +[variant baseline] +fun ascii::char_to_lowercase($t0|byte: u8): u8 { + var $t1|tmp#$1: bool + var $t2|tmp#$2: u8 + var $t3: u8 + var $t4: u8 + var $t5: bool + var $t6: u8 + var $t7: u8 + var $t8: bool + var $t9: bool + var $t10: bool + var $t11: u8 + var $t12: u8 + var $t13: u8 + var $t14: u8 + var $t15: u8 + 0: $t3 := copy($t0) + 1: $t4 := 65 + 2: $t5 := >=($t3, $t4) + 3: if ($t5) goto 4 else goto 10 + 4: label L1 + 5: $t6 := copy($t0) + 6: $t7 := 90 + 7: $t8 := <=($t6, $t7) + 8: $t1 := $t8 + 9: goto 14 + 10: label L0 + 11: $t9 := false + 12: $t1 := $t9 + 13: goto 14 + 14: label L2 + 15: $t10 := move($t1) + 16: if ($t10) goto 17 else goto 23 + 17: label L4 + 18: $t11 := move($t0) + 19: $t12 := 32 + 20: $t13 := +($t11, $t12) + 21: $t2 := $t13 + 22: goto 27 + 23: label L3 + 24: $t14 := move($t0) + 25: $t2 := $t14 + 26: goto 27 + 27: label L5 + 28: $t15 := move($t2) + 29: return $t15 +} + + +[variant baseline] +fun ascii::char_to_uppercase($t0|byte: u8): u8 { + var $t1|tmp#$1: bool + var $t2|tmp#$2: u8 + var $t3: u8 + var $t4: u8 + var $t5: bool + var $t6: u8 + var $t7: u8 + var $t8: bool + var $t9: bool + var $t10: bool + var $t11: u8 + var $t12: u8 + var $t13: u8 + var $t14: u8 + var $t15: u8 + 0: $t3 := copy($t0) + 1: $t4 := 97 + 2: $t5 := >=($t3, $t4) + 3: if ($t5) goto 4 else goto 10 + 4: label L1 + 5: $t6 := copy($t0) + 6: $t7 := 122 + 7: $t8 := <=($t6, $t7) + 8: $t1 := $t8 + 9: goto 14 + 10: label L0 + 11: $t9 := false + 12: $t1 := $t9 + 13: goto 14 + 14: label L2 + 15: $t10 := move($t1) + 16: if ($t10) goto 17 else goto 23 + 17: label L4 + 18: $t11 := move($t0) + 19: $t12 := 32 + 20: $t13 := -($t11, $t12) + 21: $t2 := $t13 + 22: goto 27 + 23: label L3 + 24: $t14 := move($t0) + 25: $t2 := $t14 + 26: goto 27 + 27: label L5 + 28: $t15 := move($t2) + 29: return $t15 +} + + +[variant baseline] +public fun ascii::into_bytes($t0|string: ascii::String): vector { + var $t1: ascii::String + var $t2: vector + 0: $t1 := move($t0) + 1: $t2 := unpack ascii::String($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::is_printable_char($t0|byte: u8): bool { + var $t1|tmp#$1: bool + var $t2: u8 + var $t3: u8 + var $t4: bool + var $t5: u8 + var $t6: u8 + var $t7: bool + var $t8: bool + var $t9: bool + 0: $t2 := copy($t0) + 1: $t3 := 32 + 2: $t4 := >=($t2, $t3) + 3: if ($t4) goto 4 else goto 10 + 4: label L1 + 5: $t5 := move($t0) + 6: $t6 := 126 + 7: $t7 := <=($t5, $t6) + 8: $t1 := $t7 + 9: goto 14 + 10: label L0 + 11: $t8 := false + 12: $t1 := $t8 + 13: goto 14 + 14: label L2 + 15: $t9 := move($t1) + 16: return $t9 +} + + +[variant baseline] +public fun ascii::is_valid_char($t0|b: u8): bool { + var $t1: u8 + var $t2: u8 + var $t3: bool + 0: $t1 := move($t0) + 1: $t2 := 127 + 2: $t3 := <=($t1, $t2) + 3: return $t3 +} + + +[variant baseline] +public fun ascii::pop_char($t0|string: &mut ascii::String): ascii::Char { + var $t1: &mut ascii::String + var $t2: &mut vector + var $t3: u8 + var $t4: ascii::Char + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: $t3 := vector::pop_back($t2) + 3: $t4 := pack ascii::Char($t3) + 4: return $t4 +} + + +[variant baseline] +public fun ascii::push_char($t0|string: &mut ascii::String, $t1|char: ascii::Char) { + var $t2: &mut ascii::String + var $t3: &mut vector + var $t4: &ascii::Char + var $t5: &u8 + var $t6: u8 + 0: $t2 := move($t0) + 1: $t3 := borrow_field.bytes($t2) + 2: $t4 := borrow_local($t1) + 3: $t5 := borrow_field.byte($t4) + 4: $t6 := read_ref($t5) + 5: vector::push_back($t3, $t6) + 6: return () +} + + +[variant baseline] +public fun ascii::substring($t0|string: &ascii::String, $t1|i: u64, $t2|j: u64): ascii::String { + var $t3|tmp#$3: bool + var $t4|bytes#1#0: vector + var $t5|i#1#3: u64 + var $t6|i#1#6: u64 + var $t7|stop#1#3: u64 + var $t8: u64 + var $t9: u64 + var $t10: bool + var $t11: u64 + var $t12: &ascii::String + var $t13: u64 + var $t14: bool + var $t15: bool + var $t16: bool + var $t17: &ascii::String + var $t18: u64 + var $t19: vector + var $t20: u64 + var $t21: u64 + var $t22: u64 + var $t23: u64 + var $t24: bool + var $t25: u64 + var $t26: &mut vector + var $t27: &ascii::String + var $t28: &vector + var $t29: u64 + var $t30: &u8 + var $t31: u8 + var $t32: u64 + var $t33: u64 + var $t34: u64 + var $t35: &ascii::String + var $t36: vector + var $t37: ascii::String + 0: $t8 := copy($t1) + 1: $t9 := copy($t2) + 2: $t10 := <=($t8, $t9) + 3: if ($t10) goto 4 else goto 11 + 4: label L1 + 5: $t11 := copy($t2) + 6: $t12 := copy($t0) + 7: $t13 := ascii::length($t12) + 8: $t14 := <=($t11, $t13) + 9: $t3 := $t14 + 10: goto 15 + 11: label L0 + 12: $t15 := false + 13: $t3 := $t15 + 14: goto 15 + 15: label L2 + 16: $t16 := move($t3) + 17: if ($t16) goto 18 else goto 20 + 18: label L4 + 19: goto 25 + 20: label L3 + 21: $t17 := move($t0) + 22: destroy($t17) + 23: $t18 := 65537 + 24: abort($t18) + 25: label L5 + 26: $t19 := [] + 27: $t4 := $t19 + 28: $t20 := move($t1) + 29: $t5 := $t20 + 30: $t21 := move($t2) + 31: $t7 := $t21 + 32: goto 33 + 33: label L8 + 34: $t22 := copy($t5) + 35: $t23 := copy($t7) + 36: $t24 := <($t22, $t23) + 37: if ($t24) goto 38 else goto 53 + 38: label L7 + 39: $t25 := copy($t5) + 40: $t6 := $t25 + 41: $t26 := borrow_local($t4) + 42: $t27 := copy($t0) + 43: $t28 := borrow_field.bytes($t27) + 44: $t29 := move($t6) + 45: $t30 := vector::borrow($t28, $t29) + 46: $t31 := read_ref($t30) + 47: vector::push_back($t26, $t31) + 48: $t32 := move($t5) + 49: $t33 := 1 + 50: $t34 := +($t32, $t33) + 51: $t5 := $t34 + 52: goto 33 + 53: label L6 + 54: $t35 := move($t0) + 55: destroy($t35) + 56: $t36 := move($t4) + 57: $t37 := pack ascii::String($t36) + 58: return $t37 +} + + +[variant baseline] +public fun ascii::to_lowercase($t0|string: &ascii::String): ascii::String { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: u8 + var $t3|tmp#$3: &mut vector + var $t4|e#1#13: &u8 + var $t5|i#1#12: u64 + var $t6|i#1#9: u64 + var $t7|r#1#1: vector + var $t8|stop#1#9: u64 + var $t9|v#1#1: &vector + var $t10|v#1#3: &vector + var $t11: &ascii::String + var $t12: &vector + var $t13: vector + var $t14: &vector + var $t15: &vector + var $t16: u64 + var $t17: u64 + var $t18: u64 + var $t19: u64 + var $t20: u64 + var $t21: bool + var $t22: u64 + var $t23: &vector + var $t24: u64 + var $t25: &u8 + var $t26: &mut vector + var $t27: &u8 + var $t28: u8 + var $t29: u8 + var $t30: &mut vector + var $t31: u8 + var $t32: u64 + var $t33: u64 + var $t34: u64 + var $t35: &vector + var $t36: vector + var $t37: ascii::String + 0: $t11 := move($t0) + 1: $t12 := ascii::as_bytes($t11) + 2: $t9 := $t12 + 3: $t13 := [] + 4: $t7 := $t13 + 5: $t14 := move($t9) + 6: $t10 := $t14 + 7: $t15 := copy($t10) + 8: $t16 := vector::length($t15) + 9: $t1 := $t16 + 10: $t17 := 0 + 11: $t6 := $t17 + 12: $t18 := move($t1) + 13: $t8 := $t18 + 14: goto 15 + 15: label L2 + 16: $t19 := copy($t6) + 17: $t20 := copy($t8) + 18: $t21 := <($t19, $t20) + 19: if ($t21) goto 20 else goto 41 + 20: label L1 + 21: $t22 := copy($t6) + 22: $t5 := $t22 + 23: $t23 := copy($t10) + 24: $t24 := move($t5) + 25: $t25 := vector::borrow($t23, $t24) + 26: $t4 := $t25 + 27: $t26 := borrow_local($t7) + 28: $t3 := $t26 + 29: $t27 := move($t4) + 30: $t28 := read_ref($t27) + 31: $t29 := ascii::char_to_lowercase($t28) + 32: $t2 := $t29 + 33: $t30 := move($t3) + 34: $t31 := move($t2) + 35: vector::push_back($t30, $t31) + 36: $t32 := move($t6) + 37: $t33 := 1 + 38: $t34 := +($t32, $t33) + 39: $t6 := $t34 + 40: goto 15 + 41: label L0 + 42: $t35 := move($t10) + 43: destroy($t35) + 44: $t36 := move($t7) + 45: $t37 := pack ascii::String($t36) + 46: return $t37 +} + + +[variant baseline] +public fun ascii::to_uppercase($t0|string: &ascii::String): ascii::String { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: u8 + var $t3|tmp#$3: &mut vector + var $t4|e#1#13: &u8 + var $t5|i#1#12: u64 + var $t6|i#1#9: u64 + var $t7|r#1#1: vector + var $t8|stop#1#9: u64 + var $t9|v#1#1: &vector + var $t10|v#1#3: &vector + var $t11: &ascii::String + var $t12: &vector + var $t13: vector + var $t14: &vector + var $t15: &vector + var $t16: u64 + var $t17: u64 + var $t18: u64 + var $t19: u64 + var $t20: u64 + var $t21: bool + var $t22: u64 + var $t23: &vector + var $t24: u64 + var $t25: &u8 + var $t26: &mut vector + var $t27: &u8 + var $t28: u8 + var $t29: u8 + var $t30: &mut vector + var $t31: u8 + var $t32: u64 + var $t33: u64 + var $t34: u64 + var $t35: &vector + var $t36: vector + var $t37: ascii::String + 0: $t11 := move($t0) + 1: $t12 := ascii::as_bytes($t11) + 2: $t9 := $t12 + 3: $t13 := [] + 4: $t7 := $t13 + 5: $t14 := move($t9) + 6: $t10 := $t14 + 7: $t15 := copy($t10) + 8: $t16 := vector::length($t15) + 9: $t1 := $t16 + 10: $t17 := 0 + 11: $t6 := $t17 + 12: $t18 := move($t1) + 13: $t8 := $t18 + 14: goto 15 + 15: label L2 + 16: $t19 := copy($t6) + 17: $t20 := copy($t8) + 18: $t21 := <($t19, $t20) + 19: if ($t21) goto 20 else goto 41 + 20: label L1 + 21: $t22 := copy($t6) + 22: $t5 := $t22 + 23: $t23 := copy($t10) + 24: $t24 := move($t5) + 25: $t25 := vector::borrow($t23, $t24) + 26: $t4 := $t25 + 27: $t26 := borrow_local($t7) + 28: $t3 := $t26 + 29: $t27 := move($t4) + 30: $t28 := read_ref($t27) + 31: $t29 := ascii::char_to_uppercase($t28) + 32: $t2 := $t29 + 33: $t30 := move($t3) + 34: $t31 := move($t2) + 35: vector::push_back($t30, $t31) + 36: $t32 := move($t6) + 37: $t33 := 1 + 38: $t34 := +($t32, $t33) + 39: $t6 := $t34 + 40: goto 15 + 41: label L0 + 42: $t35 := move($t10) + 43: destroy($t35) + 44: $t36 := move($t7) + 45: $t37 := pack ascii::String($t36) + 46: return $t37 +} + + +[variant baseline] +public fun ascii::try_string($t0|bytes: vector): option::Option { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: bool + var $t3|tmp#$3: option::Option + var $t4|i#1#12: u64 + var $t5|i#1#9: u64 + var $t6|stop#1#9: u64 + var $t7|v#1#3: &vector + var $t8: &vector + var $t9: &vector + var $t10: u64 + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: bool + var $t16: u64 + var $t17: &vector + var $t18: u64 + var $t19: &u8 + var $t20: u8 + var $t21: bool + var $t22: bool + var $t23: &vector + var $t24: bool + var $t25: u64 + var $t26: u64 + var $t27: u64 + var $t28: &vector + var $t29: bool + var $t30: bool + var $t31: vector + var $t32: ascii::String + var $t33: option::Option + var $t34: option::Option + var $t35: option::Option + 0: $t8 := borrow_local($t0) + 1: $t7 := $t8 + 2: $t9 := copy($t7) + 3: $t10 := vector::length($t9) + 4: $t1 := $t10 + 5: $t11 := 0 + 6: $t5 := $t11 + 7: $t12 := move($t1) + 8: $t6 := $t12 + 9: goto 10 + 10: label L5 + 11: $t13 := copy($t5) + 12: $t14 := copy($t6) + 13: $t15 := <($t13, $t14) + 14: if ($t15) goto 15 else goto 37 + 15: label L1 + 16: $t16 := copy($t5) + 17: $t4 := $t16 + 18: $t17 := copy($t7) + 19: $t18 := move($t4) + 20: $t19 := vector::borrow($t17, $t18) + 21: $t20 := read_ref($t19) + 22: $t21 := ascii::is_valid_char($t20) + 23: $t22 := !($t21) + 24: if ($t22) goto 25 else goto 31 + 25: label L3 + 26: $t23 := move($t7) + 27: destroy($t23) + 28: $t24 := false + 29: $t2 := $t24 + 30: goto 43 + 31: label L2 + 32: $t25 := move($t5) + 33: $t26 := 1 + 34: $t27 := +($t25, $t26) + 35: $t5 := $t27 + 36: goto 10 + 37: label L0 + 38: $t28 := move($t7) + 39: destroy($t28) + 40: $t29 := true + 41: $t2 := $t29 + 42: goto 43 + 43: label L4 + 44: $t30 := move($t2) + 45: if ($t30) goto 46 else goto 52 + 46: label L7 + 47: $t31 := move($t0) + 48: $t32 := pack ascii::String($t31) + 49: $t33 := option::some($t32) + 50: $t3 := $t33 + 51: goto 56 + 52: label L6 + 53: $t34 := option::none() + 54: $t3 := $t34 + 55: goto 56 + 56: label L8 + 57: $t35 := move($t3) + 58: return $t35 +} + + +[variant baseline] +public fun string::append($t0|s: &mut string::String, $t1|r: string::String) { + var $t2: &mut string::String + var $t3: &mut vector + var $t4: &string::String + var $t5: &vector + var $t6: vector + 0: $t2 := move($t0) + 1: $t3 := borrow_field.bytes($t2) + 2: $t4 := borrow_local($t1) + 3: $t5 := borrow_field.bytes($t4) + 4: $t6 := read_ref($t5) + 5: vector::append($t3, $t6) + 6: return () +} + + +[variant baseline] +public fun string::index_of($t0|s: &string::String, $t1|r: &string::String): u64 { + var $t2: &string::String + var $t3: &vector + var $t4: &string::String + var $t5: &vector + var $t6: u64 + 0: $t2 := move($t0) + 1: $t3 := borrow_field.bytes($t2) + 2: $t4 := move($t1) + 3: $t5 := borrow_field.bytes($t4) + 4: $t6 := string::internal_index_of($t3, $t5) + 5: return $t6 +} + + +[variant baseline] +public fun string::insert($t0|s: &mut string::String, $t1|at: u64, $t2|o: string::String) { + var $t3|tmp#$3: bool + var $t4|bytes#1#0: &vector + var $t5|end#1#0: string::String + var $t6|front#1#0: string::String + var $t7|l#1#0: u64 + var $t8: &mut string::String + var $t9: &vector + var $t10: u64 + var $t11: &vector + var $t12: u64 + var $t13: bool + var $t14: &vector + var $t15: u64 + var $t16: bool + var $t17: &vector + var $t18: bool + var $t19: bool + var $t20: &mut string::String + var $t21: u64 + var $t22: &mut string::String + var $t23: &string::String + var $t24: u64 + var $t25: &mut string::String + var $t26: &string::String + var $t27: u64 + var $t28: u64 + var $t29: string::String + var $t30: &mut string::String + var $t31: &string::String + var $t32: u64 + var $t33: u64 + var $t34: string::String + var $t35: &mut string::String + var $t36: string::String + var $t37: &mut string::String + var $t38: string::String + var $t39: string::String + var $t40: &mut string::String + 0: $t8 := copy($t0) + 1: $t9 := borrow_field.bytes($t8) + 2: $t4 := $t9 + 3: $t10 := copy($t1) + 4: $t11 := copy($t4) + 5: $t12 := vector::length($t11) + 6: $t13 := <=($t10, $t12) + 7: if ($t13) goto 8 else goto 14 + 8: label L1 + 9: $t14 := move($t4) + 10: $t15 := copy($t1) + 11: $t16 := string::internal_is_char_boundary($t14, $t15) + 12: $t3 := $t16 + 13: goto 20 + 14: label L0 + 15: $t17 := move($t4) + 16: destroy($t17) + 17: $t18 := false + 18: $t3 := $t18 + 19: goto 20 + 20: label L2 + 21: $t19 := move($t3) + 22: if ($t19) goto 23 else goto 25 + 23: label L4 + 24: goto 30 + 25: label L3 + 26: $t20 := move($t0) + 27: destroy($t20) + 28: $t21 := 2 + 29: abort($t21) + 30: label L5 + 31: $t22 := copy($t0) + 32: $t23 := freeze_ref($t22) + 33: $t24 := string::length($t23) + 34: $t7 := $t24 + 35: $t25 := copy($t0) + 36: $t26 := freeze_ref($t25) + 37: $t27 := 0 + 38: $t28 := copy($t1) + 39: $t29 := string::substring($t26, $t27, $t28) + 40: $t6 := $t29 + 41: $t30 := copy($t0) + 42: $t31 := freeze_ref($t30) + 43: $t32 := move($t1) + 44: $t33 := move($t7) + 45: $t34 := string::substring($t31, $t32, $t33) + 46: $t5 := $t34 + 47: $t35 := borrow_local($t6) + 48: $t36 := move($t2) + 49: string::append($t35, $t36) + 50: $t37 := borrow_local($t6) + 51: $t38 := move($t5) + 52: string::append($t37, $t38) + 53: $t39 := move($t6) + 54: $t40 := move($t0) + 55: write_ref($t40, $t39) + 56: return () +} + + +[variant baseline] +public fun string::is_empty($t0|s: &string::String): bool { + var $t1: &string::String + var $t2: &vector + var $t3: bool + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: $t3 := vector::is_empty($t2) + 3: return $t3 +} + + +[variant baseline] +public fun string::length($t0|s: &string::String): u64 { + var $t1: &string::String + var $t2: &vector + var $t3: u64 + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: $t3 := vector::length($t2) + 3: return $t3 +} + + +[variant baseline] +public fun string::as_bytes($t0|s: &string::String): &vector { + var $t1: &string::String + var $t2: &vector + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: return $t2 +} + + +[variant baseline] +public fun string::into_bytes($t0|s: string::String): vector { + var $t1: string::String + var $t2: vector + 0: $t1 := move($t0) + 1: $t2 := unpack string::String($t1) + 2: return $t2 +} + + +[variant baseline] +public fun string::bytes($t0|s: &string::String): &vector { + var $t1: &string::String + var $t2: &vector + 0: $t1 := move($t0) + 1: $t2 := string::as_bytes($t1) + 2: return $t2 +} + + +[variant baseline] +public fun string::substring($t0|s: &string::String, $t1|i: u64, $t2|j: u64): string::String { + var $t3|tmp#$3: bool + var $t4|tmp#$4: bool + var $t5|tmp#$5: bool + var $t6|bytes#1#0: &vector + var $t7|l#1#0: u64 + var $t8: &string::String + var $t9: &vector + var $t10: &vector + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: bool + var $t15: u64 + var $t16: u64 + var $t17: bool + var $t18: bool + var $t19: bool + var $t20: &vector + var $t21: u64 + var $t22: bool + var $t23: bool + var $t24: bool + var $t25: &vector + var $t26: u64 + var $t27: bool + var $t28: bool + var $t29: bool + var $t30: &vector + var $t31: u64 + var $t32: &vector + var $t33: u64 + var $t34: u64 + var $t35: vector + var $t36: string::String + 0: $t8 := move($t0) + 1: $t9 := borrow_field.bytes($t8) + 2: $t6 := $t9 + 3: $t10 := copy($t6) + 4: $t11 := vector::length($t10) + 5: $t7 := $t11 + 6: $t12 := copy($t2) + 7: $t13 := move($t7) + 8: $t14 := <=($t12, $t13) + 9: if ($t14) goto 10 else goto 16 + 10: label L1 + 11: $t15 := copy($t1) + 12: $t16 := copy($t2) + 13: $t17 := <=($t15, $t16) + 14: $t3 := $t17 + 15: goto 20 + 16: label L0 + 17: $t18 := false + 18: $t3 := $t18 + 19: goto 20 + 20: label L2 + 21: $t19 := move($t3) + 22: if ($t19) goto 23 else goto 29 + 23: label L4 + 24: $t20 := copy($t6) + 25: $t21 := copy($t1) + 26: $t22 := string::internal_is_char_boundary($t20, $t21) + 27: $t4 := $t22 + 28: goto 33 + 29: label L3 + 30: $t23 := false + 31: $t4 := $t23 + 32: goto 33 + 33: label L5 + 34: $t24 := move($t4) + 35: if ($t24) goto 36 else goto 42 + 36: label L7 + 37: $t25 := copy($t6) + 38: $t26 := copy($t2) + 39: $t27 := string::internal_is_char_boundary($t25, $t26) + 40: $t5 := $t27 + 41: goto 46 + 42: label L6 + 43: $t28 := false + 44: $t5 := $t28 + 45: goto 46 + 46: label L8 + 47: $t29 := move($t5) + 48: if ($t29) goto 49 else goto 51 + 49: label L10 + 50: goto 56 + 51: label L9 + 52: $t30 := move($t6) + 53: destroy($t30) + 54: $t31 := 2 + 55: abort($t31) + 56: label L11 + 57: $t32 := move($t6) + 58: $t33 := move($t1) + 59: $t34 := move($t2) + 60: $t35 := string::internal_sub_string($t32, $t33, $t34) + 61: $t36 := pack string::String($t35) + 62: return $t36 +} + + +[variant baseline] +public fun string::append_utf8($t0|s: &mut string::String, $t1|bytes: vector) { + var $t2: &mut string::String + var $t3: vector + var $t4: string::String + 0: $t2 := move($t0) + 1: $t3 := move($t1) + 2: $t4 := string::utf8($t3) + 3: string::append($t2, $t4) + 4: return () +} + + +[variant baseline] +public fun string::from_ascii($t0|s: ascii::String): string::String { + var $t1: ascii::String + var $t2: vector + var $t3: string::String + 0: $t1 := move($t0) + 1: $t2 := ascii::into_bytes($t1) + 2: $t3 := pack string::String($t2) + 3: return $t3 +} + + +[variant baseline] +native fun string::internal_check_utf8($t0|v: &vector): bool; + + +[variant baseline] +native fun string::internal_index_of($t0|v: &vector, $t1|r: &vector): u64; + + +[variant baseline] +native fun string::internal_is_char_boundary($t0|v: &vector, $t1|i: u64): bool; + + +[variant baseline] +native fun string::internal_sub_string($t0|v: &vector, $t1|i: u64, $t2|j: u64): vector; + + +[variant baseline] +public fun string::sub_string($t0|s: &string::String, $t1|i: u64, $t2|j: u64): string::String { + var $t3: &string::String + var $t4: u64 + var $t5: u64 + var $t6: string::String + 0: $t3 := move($t0) + 1: $t4 := move($t1) + 2: $t5 := move($t2) + 3: $t6 := string::substring($t3, $t4, $t5) + 4: return $t6 +} + + +[variant baseline] +public fun string::to_ascii($t0|s: string::String): ascii::String { + var $t1: string::String + var $t2: vector + var $t3: ascii::String + 0: $t1 := move($t0) + 1: $t2 := unpack string::String($t1) + 2: $t3 := ascii::string($t2) + 3: return $t3 +} + + +[variant baseline] +public fun string::try_utf8($t0|bytes: vector): option::Option { + var $t1|tmp#$1: option::Option + var $t2: &vector + var $t3: bool + var $t4: vector + var $t5: string::String + var $t6: option::Option + var $t7: option::Option + var $t8: option::Option + 0: $t2 := borrow_local($t0) + 1: $t3 := string::internal_check_utf8($t2) + 2: if ($t3) goto 3 else goto 9 + 3: label L1 + 4: $t4 := move($t0) + 5: $t5 := pack string::String($t4) + 6: $t6 := option::some($t5) + 7: $t1 := $t6 + 8: goto 13 + 9: label L0 + 10: $t7 := option::none() + 11: $t1 := $t7 + 12: goto 13 + 13: label L2 + 14: $t8 := move($t1) + 15: return $t8 +} + + +[variant baseline] +public fun string::utf8($t0|bytes: vector): string::String { + var $t1: &vector + var $t2: bool + var $t3: u64 + var $t4: vector + var $t5: string::String + 0: $t1 := borrow_local($t0) + 1: $t2 := string::internal_check_utf8($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t3 := 1 + 7: abort($t3) + 8: label L2 + 9: $t4 := move($t0) + 10: $t5 := pack string::String($t4) + 11: return $t5 +} + + +[variant baseline] +public fun Collection::borrow_mut<#0>($t0|c: &mut Collection::Collection<#0>, $t1|i: u64): &mut #0 { + var $t2: &mut Collection::Collection<#0> + var $t3: &mut vector<#0> + var $t4: u64 + var $t5: &mut #0 + 0: $t2 := move($t0) + 1: $t3 := borrow_field>.items($t2) + 2: $t4 := move($t1) + 3: $t5 := vector::borrow_mut<#0>($t3, $t4) + 4: return $t5 +} + + +[variant baseline] +public fun Collection::make_collection<#0>(): Collection::Collection<#0> { + var $t0: vector<#0> + var $t1: address + var $t2: Collection::Collection<#0> + 0: $t0 := vector::empty<#0>() + 1: $t1 := 0x2 + 2: $t2 := pack Collection::Collection<#0>($t0, $t1) + 3: return $t2 +} + + +[variant baseline] +public fun Test::foo<#0>($t0|i: u64) { + var $t1|c#1#0: Collection::Collection> + var $t2|t#1#0: &mut Test::Token<#0> + var $t3: Collection::Collection> + var $t4: &mut Collection::Collection> + var $t5: u64 + var $t6: &mut Test::Token<#0> + var $t7: u64 + var $t8: &mut Test::Token<#0> + var $t9: &mut u64 + 0: $t3 := Collection::make_collection>() + 1: $t1 := $t3 + 2: $t4 := borrow_local($t1) + 3: $t5 := move($t0) + 4: $t6 := Collection::borrow_mut>($t4, $t5) + 5: $t2 := $t6 + 6: $t7 := 0 + 7: $t8 := move($t2) + 8: $t9 := borrow_field>.value($t8) + 9: write_ref($t9, $t7) + 10: return () +} + +============ after pipeline `borrow` ================ + +[variant baseline] +public fun u64::diff($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: bool + 0: $t5 := >($t0, $t1) + 1: if ($t5) goto 2 else goto 5 + 2: label L1 + 3: $t2 := -($t0, $t1) + 4: goto 7 + 5: label L0 + 6: $t2 := -($t1, $t0) + 7: label L2 + 8: return $t2 +} + + +[variant baseline] +public fun u64::divide_and_round_up($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: bool + var $t8: u64 + var $t9: u64 + 0: $t5 := %($t0, $t1) + 1: $t6 := 0 + 2: $t7 := ==($t5, $t6) + 3: if ($t7) goto 4 else goto 7 + 4: label L1 + 5: $t2 := /($t0, $t1) + 6: goto 11 + 7: label L0 + 8: $t8 := /($t0, $t1) + 9: $t9 := 1 + 10: $t2 := +($t8, $t9) + 11: label L2 + 12: return $t2 +} + + +[variant baseline] +public fun u64::max($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: bool + 0: $t5 := >($t0, $t1) + 1: if ($t5) goto 2 else goto 5 + 2: label L1 + 3: $t2 := $t0 + 4: goto 7 + 5: label L0 + 6: $t2 := $t1 + 7: label L2 + 8: return $t2 +} + + +[variant baseline] +public fun u64::min($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: bool + 0: $t5 := <($t0, $t1) + 1: if ($t5) goto 2 else goto 5 + 2: label L1 + 3: $t2 := $t0 + 4: goto 7 + 5: label L0 + 6: $t2 := $t1 + 7: label L2 + 8: return $t2 +} + + +[variant baseline] +public fun u64::pow($t0|base: u64, $t1|exponent: u8): u64 { + var $t2|base#1#1: u64 + var $t3|exponent#1#1: u8 + var $t4|res#1#1: u64 + var $t5: u64 + var $t6: u8 + var $t7: bool + var $t8: u8 + var $t9: u8 + var $t10: u8 + var $t11: bool + var $t12: u8 + var $t13: u8 + 0: $t2 := $t0 + 1: $t3 := $t1 + 2: $t5 := 1 + 3: $t4 := $t5 + 4: label L5 + 5: $t6 := 1 + 6: $t7 := >=($t3, $t6) + 7: if ($t7) goto 8 else goto 25 + 8: label L1 + 9: label L2 + 10: $t8 := 2 + 11: $t9 := %($t3, $t8) + 12: $t10 := 0 + 13: $t11 := ==($t9, $t10) + 14: if ($t11) goto 15 else goto 20 + 15: label L4 + 16: $t2 := *($t2, $t2) + 17: $t12 := 2 + 18: $t3 := /($t3, $t12) + 19: goto 4 + 20: label L3 + 21: $t4 := *($t4, $t2) + 22: $t13 := 1 + 23: $t3 := -($t3, $t13) + 24: goto 4 + 25: label L0 + 26: return $t4 +} + + +[variant baseline] +public fun u64::sqrt($t0|x: u64): u64 { + var $t1|bit#1#1: u128 + var $t2|res#1#1: u128 + var $t3|x#1#1: u64 + var $t4|x#2#1: u128 + var $t5: u128 + var $t6: u128 + var $t7: u128 + var $t8: bool + var $t9: u128 + var $t10: bool + var $t11: u128 + var $t12: u8 + var $t13: u128 + var $t14: u8 + var $t15: u8 + var $t16: u64 + 0: $t5 := 18446744073709551616 + 1: $t1 := $t5 + 2: $t6 := 0 + 3: $t2 := $t6 + 4: $t4 := (u128)($t0) + 5: label L6 + 6: $t7 := 0 + 7: $t8 := !=($t1, $t7) + 8: if ($t8) goto 9 else goto 28 + 9: label L1 + 10: label L2 + 11: $t9 := +($t2, $t1) + 12: $t10 := >=($t4, $t9) + 13: if ($t10) goto 14 else goto 21 + 14: label L4 + 15: $t11 := +($t2, $t1) + 16: $t4 := -($t4, $t11) + 17: $t12 := 1 + 18: $t13 := >>($t2, $t12) + 19: $t2 := +($t13, $t1) + 20: goto 24 + 21: label L3 + 22: $t14 := 1 + 23: $t2 := >>($t2, $t14) + 24: label L5 + 25: $t15 := 2 + 26: $t1 := >>($t1, $t15) + 27: goto 5 + 28: label L0 + 29: $t16 := (u64)($t2) + 30: return $t16 +} + + +[variant baseline] +public fun vector::append<#0>($t0|lhs: &mut vector<#0>, $t1|other: vector<#0>) { + var $t2: &mut vector<#0> + var $t3: vector<#0> + var $t4: bool + var $t5: bool + var $t6: &mut vector<#0> + var $t7: #0 + var $t8: vector<#0> + 0: $t2 := borrow_local($t1) + 1: vector::reverse<#0>($t2) + 2: label L3 + 3: $t3 := copy($t1) + 4: $t4 := vector::is_empty<#0>($t3) + 5: $t5 := !($t4) + 6: if ($t5) goto 7 else goto 13 + 7: label L1 + 8: label L2 + 9: $t6 := borrow_local($t1) + 10: $t7 := vector::pop_back<#0>($t6) + 11: vector::push_back<#0>($t0, $t7) + 12: goto 2 + 13: label L0 + 14: destroy($t0) + 15: $t8 := move($t1) + 16: vector::destroy_empty<#0>($t8) + 17: trace_local[lhs]($t0) + 18: return () +} + + +[variant baseline] +public native fun vector::borrow<#0>($t0|v: vector<#0>, $t1|i: u64): #0; + + +[variant baseline] +public native fun vector::borrow_mut<#0>($t0|v: &mut vector<#0>, $t1|i: u64): &mut #0; + + +[variant baseline] +public fun vector::contains<#0>($t0|v: vector<#0>, $t1|e: #0): bool { + var $t2|i#1#0: u64 + var $t3|len#1#0: u64 + var $t4: u64 + var $t5: u64 + var $t6: bool + var $t7: #0 + var $t8: bool + var $t9: bool + var $t10: u64 + var $t11: bool + 0: $t4 := 0 + 1: $t2 := $t4 + 2: $t5 := vector::length<#0>($t0) + 3: label L5 + 4: $t6 := <($t2, $t5) + 5: if ($t6) goto 6 else goto 18 + 6: label L1 + 7: label L2 + 8: $t7 := vector::borrow<#0>($t0, $t2) + 9: $t8 := ==($t7, $t1) 10: if ($t8) goto 11 else goto 14 11: label L4 12: $t9 := true 13: return $t9 14: label L3 - 15: $t10 := 1 - 16: $t2 := +($t2, $t10) - 17: goto 3 - 18: label L0 - 19: $t11 := false - 20: return $t11 + 15: $t10 := 1 + 16: $t2 := +($t2, $t10) + 17: goto 3 + 18: label L0 + 19: $t11 := false + 20: return $t11 +} + + +[variant baseline] +public native fun vector::destroy_empty<#0>($t0|v: vector<#0>); + + +[variant baseline] +public native fun vector::empty<#0>(): vector<#0>; + + +[variant baseline] +public fun vector::index_of<#0>($t0|v: vector<#0>, $t1|e: #0): (bool, u64) { + var $t2|i#1#0: u64 + var $t3|len#1#0: u64 + var $t4: u64 + var $t5: u64 + var $t6: bool + var $t7: #0 + var $t8: bool + var $t9: bool + var $t10: u64 + var $t11: bool + var $t12: u64 + 0: $t4 := 0 + 1: $t2 := $t4 + 2: $t5 := vector::length<#0>($t0) + 3: label L5 + 4: $t6 := <($t2, $t5) + 5: if ($t6) goto 6 else goto 18 + 6: label L1 + 7: label L2 + 8: $t7 := vector::borrow<#0>($t0, $t2) + 9: $t8 := ==($t7, $t1) + 10: if ($t8) goto 11 else goto 14 + 11: label L4 + 12: $t9 := true + 13: return ($t9, $t2) + 14: label L3 + 15: $t10 := 1 + 16: $t2 := +($t2, $t10) + 17: goto 3 + 18: label L0 + 19: $t11 := false + 20: $t12 := 0 + 21: return ($t11, $t12) +} + + +[variant baseline] +public fun vector::insert<#0>($t0|v: &mut vector<#0>, $t1|e: #0, $t2|i: u64) { + var $t3|len#1#0: u64 + var $t4: vector<#0> + var $t5: u64 + var $t6: bool + var $t7: u64 + var $t8: bool + var $t9: u64 + 0: $t4 := read_ref($t0) + 1: $t5 := vector::length<#0>($t4) + 2: $t6 := >($t2, $t5) + 3: if ($t6) goto 4 else goto 8 + 4: label L1 + 5: destroy($t0) + 6: $t7 := 131072 + 7: abort($t7) + 8: label L0 + 9: vector::push_back<#0>($t0, $t1) + 10: label L4 + 11: $t8 := <($t2, $t5) + 12: if ($t8) goto 13 else goto 18 + 13: label L3 + 14: vector::swap<#0>($t0, $t2, $t5) + 15: $t9 := 1 + 16: $t2 := +($t2, $t9) + 17: goto 10 + 18: label L2 + 19: destroy($t0) + 20: trace_local[v]($t0) + 21: return () +} + + +[variant baseline] +public fun vector::is_empty<#0>($t0|v: vector<#0>): bool { + var $t1: u64 + var $t2: u64 + var $t3: bool + 0: $t1 := vector::length<#0>($t0) + 1: $t2 := 0 + 2: $t3 := ==($t1, $t2) + 3: return $t3 +} + + +[variant baseline] +public native fun vector::length<#0>($t0|v: vector<#0>): u64; + + +[variant baseline] +public native fun vector::pop_back<#0>($t0|v: &mut vector<#0>): #0; + + +[variant baseline] +public native fun vector::push_back<#0>($t0|v: &mut vector<#0>, $t1|e: #0); + + +[variant baseline] +public fun vector::remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { + var $t2|tmp#$2: u64 + var $t3|tmp#$3: &mut vector<#0> + var $t4|len#1#0: u64 + var $t5: vector<#0> + var $t6: u64 + var $t7: bool + var $t8: u64 + var $t9: u64 + var $t10: u64 + var $t11: bool + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: #0 + 0: $t5 := read_ref($t0) + 1: $t6 := vector::length<#0>($t5) + 2: $t7 := >=($t1, $t6) + 3: if ($t7) goto 4 else goto 8 + 4: label L1 + 5: destroy($t0) + 6: $t8 := 131072 + 7: abort($t8) + 8: label L0 + 9: $t9 := 1 + 10: $t10 := -($t6, $t9) + 11: label L4 + 12: $t11 := <($t1, $t10) + 13: if ($t11) goto 14 else goto 21 + 14: label L3 + 15: $t12 := copy($t1) + 16: $t13 := 1 + 17: $t14 := +($t1, $t13) + 18: $t1 := $t14 + 19: vector::swap<#0>($t0, $t12, $t14) + 20: goto 11 + 21: label L2 + 22: $t15 := vector::pop_back<#0>($t0) + 23: trace_local[v]($t0) + 24: return $t15 +} + + +[variant baseline] +public fun vector::reverse<#0>($t0|v: &mut vector<#0>) { + var $t1|back_index#1#0: u64 + var $t2|front_index#1#0: u64 + var $t3|len#1#0: u64 + var $t4: vector<#0> + var $t5: u64 + var $t6: u64 + var $t7: bool + var $t8: u64 + var $t9: u64 + var $t10: bool + var $t11: u64 + var $t12: u64 + 0: $t4 := read_ref($t0) + 1: $t5 := vector::length<#0>($t4) + 2: $t6 := 0 + 3: $t7 := ==($t5, $t6) + 4: if ($t7) goto 5 else goto 9 + 5: label L1 + 6: destroy($t0) + 7: trace_local[v]($t0) + 8: return () + 9: label L0 + 10: $t8 := 0 + 11: $t2 := $t8 + 12: $t9 := 1 + 13: $t1 := -($t5, $t9) + 14: label L4 + 15: $t10 := <($t2, $t1) + 16: if ($t10) goto 17 else goto 24 + 17: label L3 + 18: vector::swap<#0>($t0, $t2, $t1) + 19: $t11 := 1 + 20: $t2 := +($t2, $t11) + 21: $t12 := 1 + 22: $t1 := -($t1, $t12) + 23: goto 14 + 24: label L2 + 25: destroy($t0) + 26: trace_local[v]($t0) + 27: return () +} + + +[variant baseline] +public fun vector::singleton<#0>($t0|e: #0): vector<#0> { + var $t1|v#1#0: vector<#0> + var $t2: &mut vector<#0> + var $t3: vector<#0> + 0: $t1 := vector::empty<#0>() + 1: $t2 := borrow_local($t1) + 2: vector::push_back<#0>($t2, $t0) + 3: $t3 := move($t1) + 4: return $t3 +} + + +[variant baseline] +public native fun vector::swap<#0>($t0|v: &mut vector<#0>, $t1|i: u64, $t2|j: u64); + + +[variant baseline] +public fun vector::swap_remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { + var $t2|last_idx#1#0: u64 + var $t3: vector<#0> + var $t4: bool + var $t5: bool + var $t6: u64 + var $t7: vector<#0> + var $t8: u64 + var $t9: u64 + var $t10: u64 + var $t11: #0 + 0: $t3 := read_ref($t0) + 1: $t4 := vector::is_empty<#0>($t3) + 2: $t5 := !($t4) + 3: if ($t5) goto 4 else goto 6 + 4: label L1 + 5: goto 10 + 6: label L0 + 7: destroy($t0) + 8: $t6 := 131072 + 9: abort($t6) + 10: label L2 + 11: $t7 := read_ref($t0) + 12: $t8 := vector::length<#0>($t7) + 13: $t9 := 1 + 14: $t10 := -($t8, $t9) + 15: vector::swap<#0>($t0, $t1, $t10) + 16: $t11 := vector::pop_back<#0>($t0) + 17: trace_local[v]($t0) + 18: return $t11 +} + + +[variant baseline] +public fun option::borrow<#0>($t0|t: option::Option<#0>): #0 { + var $t1: bool + var $t2: u64 + var $t3: vector<#0> + var $t4: u64 + var $t5: #0 + 0: $t1 := option::is_some<#0>($t0) + 1: if ($t1) goto 2 else goto 4 + 2: label L1 + 3: goto 7 + 4: label L0 + 5: $t2 := 262145 + 6: abort($t2) + 7: label L2 + 8: $t3 := get_field>.vec($t0) + 9: $t4 := 0 + 10: $t5 := vector::borrow<#0>($t3, $t4) + 11: return $t5 +} + + +[variant baseline] +public fun option::borrow_mut<#0>($t0|t: &mut option::Option<#0>): &mut #0 { + var $t1: option::Option<#0> + var $t2: bool + var $t3: u64 + var $t4: &mut vector<#0> + var $t5: u64 + var $t6: &mut #0 + 0: $t1 := read_ref($t0) + 1: $t2 := option::is_some<#0>($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 9 + 5: label L0 + 6: destroy($t0) + 7: $t3 := 262145 + 8: abort($t3) + 9: label L2 + 10: $t4 := borrow_field>.vec($t0) + 11: $t5 := 0 + 12: $t6 := vector::borrow_mut<#0>($t4, $t5) + 13: trace_local[t]($t0) + 14: return $t6 +} + + +[variant baseline] +public fun option::contains<#0>($t0|t: option::Option<#0>, $t1|e_ref: #0): bool { + var $t2: vector<#0> + var $t3: bool + 0: $t2 := get_field>.vec($t0) + 1: $t3 := vector::contains<#0>($t2, $t1) + 2: return $t3 +} + + +[variant baseline] +public fun option::swap<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0): #0 { + var $t2|old_value#1#0: #0 + var $t3|vec_ref#1#0: &mut vector<#0> + var $t4: option::Option<#0> + var $t5: bool + var $t6: u64 + var $t7: &mut vector<#0> + var $t8: #0 + 0: $t4 := read_ref($t0) + 1: $t5 := option::is_some<#0>($t4) + 2: if ($t5) goto 3 else goto 5 + 3: label L1 + 4: goto 9 + 5: label L0 + 6: destroy($t0) + 7: $t6 := 262145 + 8: abort($t6) + 9: label L2 + 10: $t7 := borrow_field>.vec($t0) + 11: $t8 := vector::pop_back<#0>($t7) + 12: vector::push_back<#0>($t7, $t1) + 13: trace_local[t]($t0) + 14: return $t8 +} + + +[variant baseline] +public fun option::borrow_with_default<#0>($t0|t: option::Option<#0>, $t1|default_ref: #0): #0 { + var $t2|tmp#$2: #0 + var $t3|vec_ref#1#0: vector<#0> + var $t4: vector<#0> + var $t5: bool + var $t6: u64 + 0: $t4 := get_field>.vec($t0) + 1: $t5 := vector::is_empty<#0>($t4) + 2: if ($t5) goto 3 else goto 6 + 3: label L1 + 4: $t2 := $t1 + 5: goto 9 + 6: label L0 + 7: $t6 := 0 + 8: $t2 := vector::borrow<#0>($t4, $t6) + 9: label L2 + 10: return $t2 +} + + +[variant baseline] +public fun option::destroy_none<#0>($t0|t: option::Option<#0>) { + var $t1: bool + var $t2: u64 + var $t3: vector<#0> + 0: $t1 := option::is_none<#0>($t0) + 1: if ($t1) goto 2 else goto 4 + 2: label L1 + 3: goto 7 + 4: label L0 + 5: $t2 := 262144 + 6: abort($t2) + 7: label L2 + 8: $t3 := unpack option::Option<#0>($t0) + 9: vector::destroy_empty<#0>($t3) + 10: return () +} + + +[variant baseline] +public fun option::destroy_some<#0>($t0|t: option::Option<#0>): #0 { + var $t1|elem#1#0: #0 + var $t2|vec#1#0: vector<#0> + var $t3: bool + var $t4: u64 + var $t5: &mut vector<#0> + var $t6: #0 + var $t7: vector<#0> + 0: $t3 := option::is_some<#0>($t0) + 1: if ($t3) goto 2 else goto 4 + 2: label L1 + 3: goto 7 + 4: label L0 + 5: $t4 := 262145 + 6: abort($t4) + 7: label L2 + 8: $t2 := unpack option::Option<#0>($t0) + 9: $t5 := borrow_local($t2) + 10: $t6 := vector::pop_back<#0>($t5) + 11: $t7 := move($t2) + 12: vector::destroy_empty<#0>($t7) + 13: return $t6 +} + + +[variant baseline] +public fun option::destroy_with_default<#0>($t0|t: option::Option<#0>, $t1|default: #0): #0 { + var $t2|tmp#$2: #0 + var $t3|vec#1#0: vector<#0> + var $t4: vector<#0> + var $t5: bool + var $t6: &mut vector<#0> + 0: $t3 := unpack option::Option<#0>($t0) + 1: $t4 := copy($t3) + 2: $t5 := vector::is_empty<#0>($t4) + 3: if ($t5) goto 4 else goto 7 + 4: label L1 + 5: $t2 := $t1 + 6: goto 10 + 7: label L0 + 8: $t6 := borrow_local($t3) + 9: $t2 := vector::pop_back<#0>($t6) + 10: label L2 + 11: return $t2 +} + + +[variant baseline] +public fun option::extract<#0>($t0|t: &mut option::Option<#0>): #0 { + var $t1: option::Option<#0> + var $t2: bool + var $t3: u64 + var $t4: &mut vector<#0> + var $t5: #0 + 0: $t1 := read_ref($t0) + 1: $t2 := option::is_some<#0>($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 9 + 5: label L0 + 6: destroy($t0) + 7: $t3 := 262145 + 8: abort($t3) + 9: label L2 + 10: $t4 := borrow_field>.vec($t0) + 11: $t5 := vector::pop_back<#0>($t4) + 12: trace_local[t]($t0) + 13: return $t5 +} + + +[variant baseline] +public fun option::fill<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0) { + var $t2|vec_ref#1#0: &mut vector<#0> + var $t3: &mut vector<#0> + var $t4: vector<#0> + var $t5: bool + var $t6: u64 + 0: $t3 := borrow_field>.vec($t0) + 1: $t4 := read_ref($t3) + 2: $t5 := vector::is_empty<#0>($t4) + 3: if ($t5) goto 4 else goto 14 + 4: label L1 + 5: goto 10 + 6: label L0 + 7: destroy($t3) + 8: $t6 := 262144 + 9: abort($t6) + 10: label L2 + 11: vector::push_back<#0>($t3, $t1) + 12: trace_local[t]($t0) + 13: return () + 14: label L3 + 15: destroy($t0) + 16: goto 6 +} + + +[variant baseline] +public fun option::get_with_default<#0>($t0|t: option::Option<#0>, $t1|default: #0): #0 { + var $t2|tmp#$2: #0 + var $t3|vec_ref#1#0: vector<#0> + var $t4: vector<#0> + var $t5: bool + var $t6: u64 + var $t7: #0 + 0: $t4 := get_field>.vec($t0) + 1: $t5 := vector::is_empty<#0>($t4) + 2: if ($t5) goto 3 else goto 6 + 3: label L1 + 4: $t2 := $t1 + 5: goto 10 + 6: label L0 + 7: $t6 := 0 + 8: $t7 := vector::borrow<#0>($t4, $t6) + 9: $t2 := $t7 + 10: label L2 + 11: return $t2 +} + + +[variant baseline] +public fun option::is_none<#0>($t0|t: option::Option<#0>): bool { + var $t1: vector<#0> + var $t2: bool + 0: $t1 := get_field>.vec($t0) + 1: $t2 := vector::is_empty<#0>($t1) + 2: return $t2 +} + + +[variant baseline] +public fun option::is_some<#0>($t0|t: option::Option<#0>): bool { + var $t1: vector<#0> + var $t2: bool + var $t3: bool + 0: $t1 := get_field>.vec($t0) + 1: $t2 := vector::is_empty<#0>($t1) + 2: $t3 := !($t2) + 3: return $t3 +} + + +[variant baseline] +public fun option::none<#0>(): option::Option<#0> { + var $t0: vector<#0> + var $t1: option::Option<#0> + 0: $t0 := vector::empty<#0>() + 1: $t1 := pack option::Option<#0>($t0) + 2: return $t1 +} + + +[variant baseline] +public fun option::some<#0>($t0|e: #0): option::Option<#0> { + var $t1: vector<#0> + var $t2: option::Option<#0> + 0: $t1 := vector::singleton<#0>($t0) + 1: $t2 := pack option::Option<#0>($t1) + 2: return $t2 +} + + +[variant baseline] +public fun option::swap_or_fill<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0): option::Option<#0> { + var $t2|tmp#$2: option::Option<#0> + var $t3|old_value#1#0: option::Option<#0> + var $t4|vec_ref#1#0: &mut vector<#0> + var $t5: &mut vector<#0> + var $t6: vector<#0> + var $t7: bool + var $t8: #0 + 0: $t5 := borrow_field>.vec($t0) + 1: $t6 := read_ref($t5) + 2: $t7 := vector::is_empty<#0>($t6) + 3: if ($t7) goto 4 else goto 7 + 4: label L1 + 5: $t2 := option::none<#0>() + 6: goto 10 + 7: label L0 + 8: $t8 := vector::pop_back<#0>($t5) + 9: $t2 := option::some<#0>($t8) + 10: label L2 + 11: vector::push_back<#0>($t5, $t1) + 12: trace_local[t]($t0) + 13: return $t2 +} + + +[variant baseline] +public fun option::to_vec<#0>($t0|t: option::Option<#0>): vector<#0> { + var $t1: vector<#0> + 0: $t1 := unpack option::Option<#0>($t0) + 1: return $t1 +} + + +[variant baseline] +public fun ascii::append($t0|string: &mut ascii::String, $t1|other: ascii::String) { + var $t2: &mut vector + var $t3: vector + 0: $t2 := borrow_field.bytes($t0) + 1: $t3 := ascii::into_bytes($t1) + 2: vector::append($t2, $t3) + 3: trace_local[string]($t0) + 4: return () +} + + +[variant baseline] +public fun ascii::index_of($t0|string: ascii::String, $t1|substr: ascii::String): u64 { + var $t2|tmp#$2: bool + var $t3|i#1#0: u64 + var $t4|j#1#0: u64 + var $t5|m#1#0: u64 + var $t6|n#1#0: u64 + var $t7: u64 + var $t8: u64 + var $t9: u64 + var $t10: bool + var $t11: u64 + var $t12: bool + var $t13: u64 + var $t14: bool + var $t15: vector + var $t16: u64 + var $t17: u8 + var $t18: vector + var $t19: u8 + var $t20: bool + var $t21: u64 + var $t22: bool + var $t23: u64 + 0: $t7 := 0 + 1: $t3 := $t7 + 2: $t8 := ascii::length($t0) + 3: $t9 := ascii::length($t1) + 4: $t10 := <($t8, $t9) + 5: if ($t10) goto 6 else goto 8 + 6: label L1 + 7: return $t8 + 8: label L0 + 9: $t11 := -($t8, $t9) + 10: $t12 := <=($t3, $t11) + 11: if ($t12) goto 12 else goto 45 + 12: label L3 + 13: $t13 := 0 + 14: $t4 := $t13 + 15: label L10 + 16: $t14 := <($t4, $t9) + 17: if ($t14) goto 18 else goto 27 + 18: label L5 + 19: label L6 + 20: $t15 := get_field.bytes($t0) + 21: $t16 := +($t3, $t4) + 22: $t17 := vector::borrow($t15, $t16) + 23: $t18 := get_field.bytes($t1) + 24: $t19 := vector::borrow($t18, $t4) + 25: $t2 := ==($t17, $t19) + 26: goto 30 + 27: label L4 + 28: $t20 := false + 29: $t2 := $t20 + 30: label L7 + 31: if ($t2) goto 32 else goto 36 + 32: label L9 + 33: $t21 := 1 + 34: $t4 := +($t4, $t21) + 35: goto 15 + 36: label L8 + 37: $t22 := ==($t4, $t9) + 38: if ($t22) goto 39 else goto 41 + 39: label L12 + 40: return $t3 + 41: label L11 + 42: $t23 := 1 + 43: $t3 := +($t3, $t23) + 44: goto 8 + 45: label L2 + 46: return $t8 +} + + +[variant baseline] +public fun ascii::insert($t0|s: &mut ascii::String, $t1|at: u64, $t2|o: ascii::String) { + var $t3|e#1#2: u8 + var $t4|v#1#1: vector + var $t5: ascii::String + var $t6: u64 + var $t7: bool + var $t8: u64 + var $t9: vector + var $t10: bool + var $t11: bool + var $t12: &mut vector + var $t13: u8 + var $t14: &mut vector + var $t15: vector + 0: $t5 := read_ref($t0) + 1: $t6 := ascii::length($t5) + 2: $t7 := <=($t1, $t6) + 3: if ($t7) goto 4 else goto 6 + 4: label L1 + 5: goto 10 + 6: label L0 + 7: destroy($t0) + 8: $t8 := 65537 + 9: abort($t8) + 10: label L2 + 11: $t4 := ascii::into_bytes($t2) + 12: label L5 + 13: $t9 := copy($t4) + 14: $t10 := vector::is_empty($t9) + 15: $t11 := !($t10) + 16: if ($t11) goto 17 else goto 23 + 17: label L4 + 18: $t12 := borrow_local($t4) + 19: $t13 := vector::pop_back($t12) + 20: $t14 := borrow_field.bytes($t0) + 21: vector::insert($t14, $t13, $t1) + 22: goto 12 + 23: label L3 + 24: destroy($t0) + 25: $t15 := move($t4) + 26: vector::destroy_empty($t15) + 27: trace_local[s]($t0) + 28: return () +} + + +[variant baseline] +public fun ascii::is_empty($t0|string: ascii::String): bool { + var $t1: vector + var $t2: bool + 0: $t1 := get_field.bytes($t0) + 1: $t2 := vector::is_empty($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::length($t0|string: ascii::String): u64 { + var $t1: vector + var $t2: u64 + 0: $t1 := ascii::as_bytes($t0) + 1: $t2 := vector::length($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::all_characters_printable($t0|string: ascii::String): bool { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: bool + var $t3|i#1#12: u64 + var $t4|i#1#9: u64 + var $t5|stop#1#9: u64 + var $t6|v#1#3: vector + var $t7: vector + var $t8: u64 + var $t9: u64 + var $t10: bool + var $t11: u8 + var $t12: bool + var $t13: bool + var $t14: bool + var $t15: u64 + var $t16: bool + 0: $t7 := get_field.bytes($t0) + 1: $t8 := vector::length($t7) + 2: $t9 := 0 + 3: $t4 := $t9 + 4: label L5 + 5: $t10 := <($t4, $t8) + 6: if ($t10) goto 7 else goto 20 + 7: label L1 + 8: $t11 := vector::borrow($t7, $t4) + 9: $t12 := ascii::is_printable_char($t11) + 10: $t13 := !($t12) + 11: if ($t13) goto 12 else goto 16 + 12: label L3 + 13: $t14 := false + 14: $t2 := $t14 + 15: goto 23 + 16: label L2 + 17: $t15 := 1 + 18: $t4 := +($t4, $t15) + 19: goto 4 + 20: label L0 + 21: $t16 := true + 22: $t2 := $t16 + 23: label L4 + 24: return $t2 +} + + +[variant baseline] +public fun ascii::string($t0|bytes: vector): ascii::String { + var $t1|x#1#0: option::Option + var $t2: option::Option + var $t3: bool + var $t4: u64 + var $t5: ascii::String + 0: $t2 := ascii::try_string($t0) + 1: $t3 := option::is_some($t2) + 2: if ($t3) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t4 := 65536 + 7: abort($t4) + 8: label L2 + 9: $t5 := option::destroy_some($t2) + 10: return $t5 +} + + +[variant baseline] +public fun ascii::as_bytes($t0|string: ascii::String): vector { + var $t1: vector + 0: $t1 := get_field.bytes($t0) + 1: return $t1 +} + + +[variant baseline] +public fun ascii::byte($t0|char: ascii::Char): u8 { + var $t1: u8 + 0: $t1 := unpack ascii::Char($t0) + 1: return $t1 +} + + +[variant baseline] +public fun ascii::char($t0|byte: u8): ascii::Char { + var $t1: bool + var $t2: u64 + var $t3: ascii::Char + 0: $t1 := ascii::is_valid_char($t0) + 1: if ($t1) goto 2 else goto 4 + 2: label L1 + 3: goto 7 + 4: label L0 + 5: $t2 := 65536 + 6: abort($t2) + 7: label L2 + 8: $t3 := pack ascii::Char($t0) + 9: return $t3 +} + + +[variant baseline] +fun ascii::char_to_lowercase($t0|byte: u8): u8 { + var $t1|tmp#$1: bool + var $t2|tmp#$2: u8 + var $t3: u8 + var $t4: bool + var $t5: u8 + var $t6: bool + var $t7: u8 + 0: $t3 := 65 + 1: $t4 := >=($t0, $t3) + 2: if ($t4) goto 3 else goto 7 + 3: label L1 + 4: $t5 := 90 + 5: $t1 := <=($t0, $t5) + 6: goto 10 + 7: label L0 + 8: $t6 := false + 9: $t1 := $t6 + 10: label L2 + 11: if ($t1) goto 12 else goto 16 + 12: label L4 + 13: $t7 := 32 + 14: $t2 := +($t0, $t7) + 15: goto 18 + 16: label L3 + 17: $t2 := $t0 + 18: label L5 + 19: return $t2 +} + + +[variant baseline] +fun ascii::char_to_uppercase($t0|byte: u8): u8 { + var $t1|tmp#$1: bool + var $t2|tmp#$2: u8 + var $t3: u8 + var $t4: bool + var $t5: u8 + var $t6: bool + var $t7: u8 + 0: $t3 := 97 + 1: $t4 := >=($t0, $t3) + 2: if ($t4) goto 3 else goto 7 + 3: label L1 + 4: $t5 := 122 + 5: $t1 := <=($t0, $t5) + 6: goto 10 + 7: label L0 + 8: $t6 := false + 9: $t1 := $t6 + 10: label L2 + 11: if ($t1) goto 12 else goto 16 + 12: label L4 + 13: $t7 := 32 + 14: $t2 := -($t0, $t7) + 15: goto 18 + 16: label L3 + 17: $t2 := $t0 + 18: label L5 + 19: return $t2 +} + + +[variant baseline] +public fun ascii::into_bytes($t0|string: ascii::String): vector { + var $t1: vector + 0: $t1 := unpack ascii::String($t0) + 1: return $t1 +} + + +[variant baseline] +public fun ascii::is_printable_char($t0|byte: u8): bool { + var $t1|tmp#$1: bool + var $t2: u8 + var $t3: bool + var $t4: u8 + var $t5: bool + 0: $t2 := 32 + 1: $t3 := >=($t0, $t2) + 2: if ($t3) goto 3 else goto 7 + 3: label L1 + 4: $t4 := 126 + 5: $t1 := <=($t0, $t4) + 6: goto 10 + 7: label L0 + 8: $t5 := false + 9: $t1 := $t5 + 10: label L2 + 11: return $t1 } [variant baseline] -public native fun vector::destroy_empty<#0>($t0|v: vector<#0>); +public fun ascii::is_valid_char($t0|b: u8): bool { + var $t1: u8 + var $t2: bool + 0: $t1 := 127 + 1: $t2 := <=($t0, $t1) + 2: return $t2 +} [variant baseline] -public native fun vector::empty<#0>(): vector<#0>; +public fun ascii::pop_char($t0|string: &mut ascii::String): ascii::Char { + var $t1: &mut vector + var $t2: u8 + var $t3: ascii::Char + 0: $t1 := borrow_field.bytes($t0) + 1: $t2 := vector::pop_back($t1) + 2: $t3 := pack ascii::Char($t2) + 3: trace_local[string]($t0) + 4: return $t3 +} [variant baseline] -public fun vector::index_of<#0>($t0|v: vector<#0>, $t1|e: #0): (bool, u64) { - var $t2|i#1#0: u64 - var $t3|len#1#0: u64 - var $t4: u64 - var $t5: u64 - var $t6: bool - var $t7: #0 - var $t8: bool - var $t9: bool - var $t10: u64 - var $t11: bool - var $t12: u64 - 0: $t4 := 0 - 1: $t2 := $t4 - 2: $t5 := vector::length<#0>($t0) - 3: label L5 - 4: $t6 := <($t2, $t5) - 5: if ($t6) goto 6 else goto 18 - 6: label L1 - 7: label L2 - 8: $t7 := vector::borrow<#0>($t0, $t2) - 9: $t8 := ==($t7, $t1) - 10: if ($t8) goto 11 else goto 14 - 11: label L4 - 12: $t9 := true - 13: return ($t9, $t2) - 14: label L3 - 15: $t10 := 1 - 16: $t2 := +($t2, $t10) - 17: goto 3 - 18: label L0 - 19: $t11 := false - 20: $t12 := 0 - 21: return ($t11, $t12) +public fun ascii::push_char($t0|string: &mut ascii::String, $t1|char: ascii::Char) { + var $t2: &mut vector + var $t3: u8 + 0: $t2 := borrow_field.bytes($t0) + 1: $t3 := get_field.byte($t1) + 2: vector::push_back($t2, $t3) + 3: trace_local[string]($t0) + 4: return () } [variant baseline] -public fun vector::insert<#0>($t0|v: &mut vector<#0>, $t1|e: #0, $t2|i: u64) { - var $t3|len#1#0: u64 - var $t4: vector<#0> - var $t5: u64 - var $t6: bool - var $t7: u64 +public fun ascii::substring($t0|string: ascii::String, $t1|i: u64, $t2|j: u64): ascii::String { + var $t3|tmp#$3: bool + var $t4|bytes#1#0: vector + var $t5|i#1#3: u64 + var $t6|i#1#6: u64 + var $t7|stop#1#3: u64 var $t8: bool var $t9: u64 - 0: $t4 := read_ref($t0) - 1: $t5 := vector::length<#0>($t4) - 2: $t6 := >($t2, $t5) - 3: if ($t6) goto 4 else goto 8 - 4: label L1 - 5: destroy($t0) - 6: $t7 := 131072 - 7: abort($t7) - 8: label L0 - 9: vector::push_back<#0>($t0, $t1) - 10: label L5 - 11: $t8 := <($t2, $t5) - 12: if ($t8) goto 13 else goto 19 + var $t10: bool + var $t11: u64 + var $t12: vector + var $t13: bool + var $t14: &mut vector + var $t15: vector + var $t16: u8 + var $t17: u64 + var $t18: vector + var $t19: ascii::String + 0: $t8 := <=($t1, $t2) + 1: if ($t8) goto 2 else goto 6 + 2: label L1 + 3: $t9 := ascii::length($t0) + 4: $t3 := <=($t2, $t9) + 5: goto 9 + 6: label L0 + 7: $t10 := false + 8: $t3 := $t10 + 9: label L2 + 10: if ($t3) goto 11 else goto 13 + 11: label L4 + 12: goto 16 13: label L3 - 14: label L4 - 15: vector::swap<#0>($t0, $t2, $t5) - 16: $t9 := 1 - 17: $t2 := +($t2, $t9) - 18: goto 10 - 19: label L2 - 20: destroy($t0) - 21: trace_local[v]($t0) - 22: return () + 14: $t11 := 65537 + 15: abort($t11) + 16: label L5 + 17: $t12 := [] + 18: $t4 := $t12 + 19: $t5 := $t1 + 20: label L8 + 21: $t13 := <($t5, $t2) + 22: if ($t13) goto 23 else goto 31 + 23: label L7 + 24: $t14 := borrow_local($t4) + 25: $t15 := get_field.bytes($t0) + 26: $t16 := vector::borrow($t15, $t5) + 27: vector::push_back($t14, $t16) + 28: $t17 := 1 + 29: $t5 := +($t5, $t17) + 30: goto 20 + 31: label L6 + 32: $t18 := move($t4) + 33: $t19 := pack ascii::String($t18) + 34: return $t19 } [variant baseline] -public fun vector::is_empty<#0>($t0|v: vector<#0>): bool { - var $t1: u64 - var $t2: u64 - var $t3: bool - 0: $t1 := vector::length<#0>($t0) - 1: $t2 := 0 - 2: $t3 := ==($t1, $t2) - 3: return $t3 +public fun ascii::to_lowercase($t0|string: ascii::String): ascii::String { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: u8 + var $t3|tmp#$3: &mut vector + var $t4|e#1#13: u8 + var $t5|i#1#12: u64 + var $t6|i#1#9: u64 + var $t7|r#1#1: vector + var $t8|stop#1#9: u64 + var $t9|v#1#1: vector + var $t10|v#1#3: vector + var $t11: vector + var $t12: vector + var $t13: u64 + var $t14: u64 + var $t15: bool + var $t16: u8 + var $t17: &mut vector + var $t18: u8 + var $t19: u64 + var $t20: vector + var $t21: ascii::String + 0: $t11 := ascii::as_bytes($t0) + 1: $t12 := [] + 2: $t7 := $t12 + 3: $t13 := vector::length($t11) + 4: $t14 := 0 + 5: $t6 := $t14 + 6: label L2 + 7: $t15 := <($t6, $t13) + 8: if ($t15) goto 9 else goto 17 + 9: label L1 + 10: $t16 := vector::borrow($t11, $t6) + 11: $t17 := borrow_local($t7) + 12: $t18 := ascii::char_to_lowercase($t16) + 13: vector::push_back($t17, $t18) + 14: $t19 := 1 + 15: $t6 := +($t6, $t19) + 16: goto 6 + 17: label L0 + 18: $t20 := move($t7) + 19: $t21 := pack ascii::String($t20) + 20: return $t21 } [variant baseline] -public native fun vector::length<#0>($t0|v: vector<#0>): u64; +public fun ascii::to_uppercase($t0|string: ascii::String): ascii::String { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: u8 + var $t3|tmp#$3: &mut vector + var $t4|e#1#13: u8 + var $t5|i#1#12: u64 + var $t6|i#1#9: u64 + var $t7|r#1#1: vector + var $t8|stop#1#9: u64 + var $t9|v#1#1: vector + var $t10|v#1#3: vector + var $t11: vector + var $t12: vector + var $t13: u64 + var $t14: u64 + var $t15: bool + var $t16: u8 + var $t17: &mut vector + var $t18: u8 + var $t19: u64 + var $t20: vector + var $t21: ascii::String + 0: $t11 := ascii::as_bytes($t0) + 1: $t12 := [] + 2: $t7 := $t12 + 3: $t13 := vector::length($t11) + 4: $t14 := 0 + 5: $t6 := $t14 + 6: label L2 + 7: $t15 := <($t6, $t13) + 8: if ($t15) goto 9 else goto 17 + 9: label L1 + 10: $t16 := vector::borrow($t11, $t6) + 11: $t17 := borrow_local($t7) + 12: $t18 := ascii::char_to_uppercase($t16) + 13: vector::push_back($t17, $t18) + 14: $t19 := 1 + 15: $t6 := +($t6, $t19) + 16: goto 6 + 17: label L0 + 18: $t20 := move($t7) + 19: $t21 := pack ascii::String($t20) + 20: return $t21 +} [variant baseline] -public native fun vector::pop_back<#0>($t0|v: &mut vector<#0>): #0; +public fun ascii::try_string($t0|bytes: vector): option::Option { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: bool + var $t3|tmp#$3: option::Option + var $t4|i#1#12: u64 + var $t5|i#1#9: u64 + var $t6|stop#1#9: u64 + var $t7|v#1#3: vector + var $t8: u64 + var $t9: u64 + var $t10: bool + var $t11: u8 + var $t12: bool + var $t13: bool + var $t14: bool + var $t15: u64 + var $t16: bool + var $t17: ascii::String + 0: $t8 := vector::length($t0) + 1: $t9 := 0 + 2: $t5 := $t9 + 3: label L5 + 4: $t10 := <($t5, $t8) + 5: if ($t10) goto 6 else goto 19 + 6: label L1 + 7: $t11 := vector::borrow($t0, $t5) + 8: $t12 := ascii::is_valid_char($t11) + 9: $t13 := !($t12) + 10: if ($t13) goto 11 else goto 15 + 11: label L3 + 12: $t14 := false + 13: $t2 := $t14 + 14: goto 22 + 15: label L2 + 16: $t15 := 1 + 17: $t5 := +($t5, $t15) + 18: goto 3 + 19: label L0 + 20: $t16 := true + 21: $t2 := $t16 + 22: label L4 + 23: if ($t2) goto 24 else goto 28 + 24: label L7 + 25: $t17 := pack ascii::String($t0) + 26: $t3 := option::some($t17) + 27: goto 30 + 28: label L6 + 29: $t3 := option::none() + 30: label L8 + 31: return $t3 +} [variant baseline] -public native fun vector::push_back<#0>($t0|v: &mut vector<#0>, $t1|e: #0); +public fun string::append($t0|s: &mut string::String, $t1|r: string::String) { + var $t2: &mut vector + var $t3: vector + 0: $t2 := borrow_field.bytes($t0) + 1: $t3 := get_field.bytes($t1) + 2: vector::append($t2, $t3) + 3: trace_local[s]($t0) + 4: return () +} [variant baseline] -public fun vector::remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { - var $t2|tmp#$2: u64 - var $t3|tmp#$3: &mut vector<#0> - var $t4|len#1#0: u64 - var $t5: vector<#0> - var $t6: u64 - var $t7: bool - var $t8: u64 +public fun string::index_of($t0|s: string::String, $t1|r: string::String): u64 { + var $t2: vector + var $t3: vector + var $t4: u64 + 0: $t2 := get_field.bytes($t0) + 1: $t3 := get_field.bytes($t1) + 2: $t4 := string::internal_index_of($t2, $t3) + 3: return $t4 +} + + +[variant baseline] +public fun string::insert($t0|s: &mut string::String, $t1|at: u64, $t2|o: string::String) { + var $t3|tmp#$3: bool + var $t4|bytes#1#0: vector + var $t5|end#1#0: string::String + var $t6|front#1#0: string::String + var $t7|l#1#0: u64 + var $t8: vector var $t9: u64 - var $t10: u64 + var $t10: bool var $t11: bool var $t12: u64 - var $t13: u64 + var $t13: string::String var $t14: u64 - var $t15: #0 - 0: $t5 := read_ref($t0) - 1: $t6 := vector::length<#0>($t5) - 2: $t7 := >=($t1, $t6) - 3: if ($t7) goto 4 else goto 8 + var $t15: string::String + var $t16: u64 + var $t17: string::String + var $t18: string::String + var $t19: &mut string::String + var $t20: &mut string::String + var $t21: string::String + 0: $t8 := get_field.bytes($t0) + 1: $t9 := vector::length($t8) + 2: $t10 := <=($t1, $t9) + 3: if ($t10) goto 4 else goto 7 4: label L1 - 5: destroy($t0) - 6: $t8 := 131072 - 7: abort($t8) - 8: label L0 - 9: $t9 := 1 - 10: $t10 := -($t6, $t9) - 11: label L5 - 12: $t11 := <($t1, $t10) - 13: if ($t11) goto 14 else goto 22 + 5: $t3 := string::internal_is_char_boundary($t8, $t1) + 6: goto 10 + 7: label L0 + 8: $t11 := false + 9: $t3 := $t11 + 10: label L2 + 11: if ($t3) goto 12 else goto 14 + 12: label L4 + 13: goto 18 14: label L3 - 15: label L4 - 16: $t12 := copy($t1) - 17: $t13 := 1 - 18: $t14 := +($t1, $t13) - 19: $t1 := $t14 - 20: vector::swap<#0>($t0, $t12, $t14) - 21: goto 11 - 22: label L2 - 23: $t15 := vector::pop_back<#0>($t0) - 24: trace_local[v]($t0) - 25: return $t15 + 15: destroy($t0) + 16: $t12 := 2 + 17: abort($t12) + 18: label L5 + 19: $t13 := read_ref($t0) + 20: $t14 := string::length($t13) + 21: $t15 := read_ref($t0) + 22: $t16 := 0 + 23: $t6 := string::substring($t15, $t16, $t1) + 24: $t17 := read_ref($t0) + 25: $t18 := string::substring($t17, $t1, $t14) + 26: $t19 := borrow_local($t6) + 27: string::append($t19, $t2) + 28: $t20 := borrow_local($t6) + 29: string::append($t20, $t18) + 30: $t21 := move($t6) + 31: write_ref($t0, $t21) + 32: trace_local[s]($t0) + 33: return () } [variant baseline] -public fun vector::reverse<#0>($t0|v: &mut vector<#0>) { - var $t1|back_index#1#0: u64 - var $t2|front_index#1#0: u64 - var $t3|len#1#0: u64 - var $t4: vector<#0> - var $t5: u64 - var $t6: u64 - var $t7: bool - var $t8: u64 - var $t9: u64 - var $t10: bool - var $t11: u64 - var $t12: u64 - 0: $t4 := read_ref($t0) - 1: $t5 := vector::length<#0>($t4) - 2: $t6 := 0 - 3: $t7 := ==($t5, $t6) - 4: if ($t7) goto 5 else goto 9 - 5: label L1 - 6: destroy($t0) - 7: trace_local[v]($t0) - 8: return () - 9: label L0 - 10: $t8 := 0 - 11: $t2 := $t8 - 12: $t9 := 1 - 13: $t1 := -($t5, $t9) - 14: label L5 - 15: $t10 := <($t2, $t1) - 16: if ($t10) goto 17 else goto 25 - 17: label L3 - 18: label L4 - 19: vector::swap<#0>($t0, $t2, $t1) - 20: $t11 := 1 - 21: $t2 := +($t2, $t11) - 22: $t12 := 1 - 23: $t1 := -($t1, $t12) - 24: goto 14 - 25: label L2 - 26: destroy($t0) - 27: trace_local[v]($t0) - 28: return () +public fun string::is_empty($t0|s: string::String): bool { + var $t1: vector + var $t2: bool + 0: $t1 := get_field.bytes($t0) + 1: $t2 := vector::is_empty($t1) + 2: return $t2 } [variant baseline] -public fun vector::singleton<#0>($t0|e: #0): vector<#0> { - var $t1|v#1#0: vector<#0> - var $t2: &mut vector<#0> - var $t3: vector<#0> - 0: $t1 := vector::empty<#0>() - 1: $t2 := borrow_local($t1) - 2: vector::push_back<#0>($t2, $t0) - 3: $t3 := move($t1) - 4: return $t3 +public fun string::length($t0|s: string::String): u64 { + var $t1: vector + var $t2: u64 + 0: $t1 := get_field.bytes($t0) + 1: $t2 := vector::length($t1) + 2: return $t2 } [variant baseline] -public native fun vector::swap<#0>($t0|v: &mut vector<#0>, $t1|i: u64, $t2|j: u64); +public fun string::as_bytes($t0|s: string::String): vector { + var $t1: vector + 0: $t1 := get_field.bytes($t0) + 1: return $t1 +} [variant baseline] -public fun vector::swap_remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { - var $t2|last_idx#1#0: u64 - var $t3: vector<#0> - var $t4: bool - var $t5: bool - var $t6: u64 - var $t7: vector<#0> - var $t8: u64 +public fun string::into_bytes($t0|s: string::String): vector { + var $t1: vector + 0: $t1 := unpack string::String($t0) + 1: return $t1 +} + + +[variant baseline] +public fun string::bytes($t0|s: string::String): vector { + var $t1: vector + 0: $t1 := string::as_bytes($t0) + 1: return $t1 +} + + +[variant baseline] +public fun string::substring($t0|s: string::String, $t1|i: u64, $t2|j: u64): string::String { + var $t3|tmp#$3: bool + var $t4|tmp#$4: bool + var $t5|tmp#$5: bool + var $t6|bytes#1#0: vector + var $t7|l#1#0: u64 + var $t8: vector var $t9: u64 - var $t10: u64 - var $t11: #0 - 0: $t3 := read_ref($t0) - 1: $t4 := vector::is_empty<#0>($t3) - 2: $t5 := !($t4) - 3: if ($t5) goto 4 else goto 6 + var $t10: bool + var $t11: bool + var $t12: bool + var $t13: bool + var $t14: u64 + var $t15: vector + var $t16: string::String + 0: $t8 := get_field.bytes($t0) + 1: $t9 := vector::length($t8) + 2: $t10 := <=($t2, $t9) + 3: if ($t10) goto 4 else goto 7 4: label L1 - 5: goto 10 - 6: label L0 - 7: destroy($t0) - 8: $t6 := 131072 - 9: abort($t6) + 5: $t3 := <=($t1, $t2) + 6: goto 10 + 7: label L0 + 8: $t11 := false + 9: $t3 := $t11 10: label L2 - 11: $t7 := read_ref($t0) - 12: $t8 := vector::length<#0>($t7) - 13: $t9 := 1 - 14: $t10 := -($t8, $t9) - 15: vector::swap<#0>($t0, $t1, $t10) - 16: $t11 := vector::pop_back<#0>($t0) - 17: trace_local[v]($t0) - 18: return $t11 + 11: if ($t3) goto 12 else goto 15 + 12: label L4 + 13: $t4 := string::internal_is_char_boundary($t8, $t1) + 14: goto 18 + 15: label L3 + 16: $t12 := false + 17: $t4 := $t12 + 18: label L5 + 19: if ($t4) goto 20 else goto 23 + 20: label L7 + 21: $t5 := string::internal_is_char_boundary($t8, $t2) + 22: goto 26 + 23: label L6 + 24: $t13 := false + 25: $t5 := $t13 + 26: label L8 + 27: if ($t5) goto 28 else goto 30 + 28: label L10 + 29: goto 33 + 30: label L9 + 31: $t14 := 2 + 32: abort($t14) + 33: label L11 + 34: $t15 := string::internal_sub_string($t8, $t1, $t2) + 35: $t16 := pack string::String($t15) + 36: return $t16 +} + + +[variant baseline] +public fun string::append_utf8($t0|s: &mut string::String, $t1|bytes: vector) { + var $t2: string::String + 0: $t2 := string::utf8($t1) + 1: string::append($t0, $t2) + 2: trace_local[s]($t0) + 3: return () +} + + +[variant baseline] +public fun string::from_ascii($t0|s: ascii::String): string::String { + var $t1: vector + var $t2: string::String + 0: $t1 := ascii::into_bytes($t0) + 1: $t2 := pack string::String($t1) + 2: return $t2 +} + + +[variant baseline] +native fun string::internal_check_utf8($t0|v: vector): bool; + + +[variant baseline] +native fun string::internal_index_of($t0|v: vector, $t1|r: vector): u64; + + +[variant baseline] +native fun string::internal_is_char_boundary($t0|v: vector, $t1|i: u64): bool; + + +[variant baseline] +native fun string::internal_sub_string($t0|v: vector, $t1|i: u64, $t2|j: u64): vector; + + +[variant baseline] +public fun string::sub_string($t0|s: string::String, $t1|i: u64, $t2|j: u64): string::String { + var $t3: string::String + 0: $t3 := string::substring($t0, $t1, $t2) + 1: return $t3 +} + + +[variant baseline] +public fun string::to_ascii($t0|s: string::String): ascii::String { + var $t1: vector + var $t2: ascii::String + 0: $t1 := unpack string::String($t0) + 1: $t2 := ascii::string($t1) + 2: return $t2 +} + + +[variant baseline] +public fun string::try_utf8($t0|bytes: vector): option::Option { + var $t1|tmp#$1: option::Option + var $t2: bool + var $t3: string::String + 0: $t2 := string::internal_check_utf8($t0) + 1: if ($t2) goto 2 else goto 6 + 2: label L1 + 3: $t3 := pack string::String($t0) + 4: $t1 := option::some($t3) + 5: goto 8 + 6: label L0 + 7: $t1 := option::none() + 8: label L2 + 9: return $t1 +} + + +[variant baseline] +public fun string::utf8($t0|bytes: vector): string::String { + var $t1: bool + var $t2: u64 + var $t3: string::String + 0: $t1 := string::internal_check_utf8($t0) + 1: if ($t1) goto 2 else goto 4 + 2: label L1 + 3: goto 7 + 4: label L0 + 5: $t2 := 1 + 6: abort($t2) + 7: label L2 + 8: $t3 := pack string::String($t0) + 9: return $t3 } @@ -925,6 +4439,10 @@ fun vector::borrow_mut[baseline] borrowed_by: Reference($t0) -> {([], Return(0))} borrows_from: Return(0) -> {([], Reference($t0))} +fun option::borrow_mut[baseline] +borrowed_by: Reference($t0) -> {(.vec (vector<#0>)/[], Return(0))} +borrows_from: Return(0) -> {(.vec (vector<#0>)/[], Reference($t0))} + fun Collection::borrow_mut[baseline] borrowed_by: Reference($t0) -> {(.items (vector<#0>)/[], Return(0))} borrows_from: Return(0) -> {(.items (vector<#0>)/[], Reference($t0))} diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/borrow/hyper_edge.move b/external-crates/move/crates/move-stackless-bytecode/tests/borrow/hyper_edge.move index 9aee1d251b47c..a0f97ccdc06d9 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/borrow/hyper_edge.move +++ b/external-crates/move/crates/move-stackless-bytecode/tests/borrow/hyper_edge.move @@ -1,9 +1,12 @@ +// dep: ../move-stdlib/sources/macros.move +// dep: ../move-stdlib/sources/u64.move +// dep: ../move-stdlib/sources/option.move +// dep: ../move-stdlib/sources/ascii.move +// dep: ../move-stdlib/sources/string.move // dep: ../move-stdlib/sources/vector.move module 0x2::Collection { - use std::vector; - - struct Collection has drop { + public struct Collection has drop { items: vector, owner: address, } @@ -23,10 +26,10 @@ module 0x2::Collection { module 0x2::Test { use 0x2::Collection; - struct Token has drop { value: u64 } + public struct Token has drop { value: u64 } public fun foo(i: u64) { - let c = Collection::make_collection>(); + let mut c = Collection::make_collection>(); let t = Collection::borrow_mut(&mut c, i); t.value = 0; } diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/borrow_strong/basic_test.move b/external-crates/move/crates/move-stackless-bytecode/tests/borrow_strong/basic_test.move index 9b530c7ebec2f..0582dcc24b1c3 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/borrow_strong/basic_test.move +++ b/external-crates/move/crates/move-stackless-bytecode/tests/borrow_strong/basic_test.move @@ -1,11 +1,11 @@ module 0x42::TestBorrow { - struct R has copy, drop { + public struct R has copy, drop { x: u64, y: u64 } fun test1() : R { - let r = R {x: 3, y: 4}; + let mut r = R {x: 3, y: 4}; let r_ref = &mut r; let x_ref = &mut r_ref.x; *x_ref = 0; @@ -22,7 +22,7 @@ module 0x42::TestBorrow { } fun test4() : R { - let r = R {x: 3, y: 4}; + let mut r = R {x: 3, y: 4}; let r_ref = &mut r; test3(r_ref, 0); r @@ -33,7 +33,7 @@ module 0x42::TestBorrow { } fun test6() : R { - let r = R {x: 3, y: 4}; + let mut r = R {x: 3, y: 4}; let r_ref = &mut r; let x_ref = test5(r_ref); test2(x_ref, 0); @@ -41,19 +41,19 @@ module 0x42::TestBorrow { } fun test7(b: bool) { - let r1 = R {x: 3, y: 4}; - let r2 = R {x: 4, y: 5}; - let r_ref = &mut r1; + let mut r1 = R {x: 3, y: 4}; + let mut r2 = R {x: 4, y: 5}; + let mut r_ref = &mut r1; if (b) { r_ref = &mut r2; }; test3(r_ref, 0) } - fun test8(b: bool, n: u64, r_ref: &mut R) { - let r1 = R {x: 3, y: 4}; - let r2 = R {x: 4, y: 5}; - let t_ref = &mut r2; + fun test8(b: bool, mut n: u64, r_ref: &mut R) { + let mut r1 = R {x: 3, y: 4}; + let mut r2 = R {x: 4, y: 5}; + let mut t_ref = &mut r2; while (0 < n) { if (n/2 == 0) { t_ref = &mut r1 @@ -70,7 +70,7 @@ module 0x42::TestBorrow { } fun test9(b : bool, r_ref: &mut R) : &mut u64 { - let r_field = &mut r_ref.x; + let mut r_field = &mut r_ref.x; if(b) { r_field = &mut r_ref.y; }; @@ -79,9 +79,9 @@ module 0x42::TestBorrow { } fun test10(b : bool) : R { - let r = R {x: 3, y: 4}; + let mut r = R {x: 3, y: 4}; let r_ref = &mut r; - let r_field = &mut r_ref.x; + let mut r_field = &mut r_ref.x; if(b) { r_field = test9(b, r_ref); }; diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/eliminate_imm_refs/basic_test.move b/external-crates/move/crates/move-stackless-bytecode/tests/eliminate_imm_refs/basic_test.move index 8dada99bc3612..82065de503a20 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/eliminate_imm_refs/basic_test.move +++ b/external-crates/move/crates/move-stackless-bytecode/tests/eliminate_imm_refs/basic_test.move @@ -1,11 +1,11 @@ module 0x42::TestEliminateImmRefs { - struct R has copy, drop { + public struct R has copy, drop { x: u64 } fun test1() : R { - let r = R {x: 3}; + let mut r = R {x: 3}; let r_ref = &mut r; let x_ref = &mut r_ref.x; *x_ref = 0; diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/return_internal_refs.move b/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/return_internal_refs.move index 9d5d2663d9850..15df8e2cbee95 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/return_internal_refs.move +++ b/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/return_internal_refs.move @@ -1,6 +1,6 @@ module 0x1::LeakInternalRefs { - struct S { f: u64, g: u64 } + public struct S { f: u64, g: u64 } fun leak_mut_ref(s: &mut S): &mut u64 { &mut s.f @@ -23,7 +23,7 @@ module 0x1::LeakInternalRefs { } fun leak_in_loop(x: &mut u64, s: &mut S): &mut u64 { - let i = 0; + let mut i = 0; while (i < 10) { if (i == 7) { return &mut s.f diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/return_refs_into_vec.exp b/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/return_refs_into_vec.exp index a5e235909a205..30333879c28e8 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/return_refs_into_vec.exp +++ b/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/return_refs_into_vec.exp @@ -1,5 +1,339 @@ ============ initial translation from Move ================ +[variant baseline] +public fun u64::diff($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: bool + var $t10: u64 + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: u64 + var $t16: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := move($t1) + 3: $t4 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := copy($t4) + 6: $t9 := >($t7, $t8) + 7: if ($t9) goto 8 else goto 14 + 8: label L1 + 9: $t10 := move($t3) + 10: $t11 := move($t4) + 11: $t12 := -($t10, $t11) + 12: $t2 := $t12 + 13: goto 20 + 14: label L0 + 15: $t13 := move($t4) + 16: $t14 := move($t3) + 17: $t15 := -($t13, $t14) + 18: $t2 := $t15 + 19: goto 20 + 20: label L2 + 21: $t16 := move($t2) + 22: return $t16 +} + + +[variant baseline] +public fun u64::divide_and_round_up($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: u64 + var $t10: u64 + var $t11: bool + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: u64 + var $t16: u64 + var $t17: u64 + var $t18: u64 + var $t19: u64 + var $t20: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := move($t1) + 3: $t4 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := copy($t4) + 6: $t9 := %($t7, $t8) + 7: $t10 := 0 + 8: $t11 := ==($t9, $t10) + 9: if ($t11) goto 10 else goto 16 + 10: label L1 + 11: $t12 := move($t3) + 12: $t13 := move($t4) + 13: $t14 := /($t12, $t13) + 14: $t2 := $t14 + 15: goto 24 + 16: label L0 + 17: $t15 := move($t3) + 18: $t16 := move($t4) + 19: $t17 := /($t15, $t16) + 20: $t18 := 1 + 21: $t19 := +($t17, $t18) + 22: $t2 := $t19 + 23: goto 24 + 24: label L2 + 25: $t20 := move($t2) + 26: return $t20 +} + + +[variant baseline] +public fun u64::max($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: bool + var $t10: u64 + var $t11: u64 + var $t12: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := move($t1) + 3: $t4 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := copy($t4) + 6: $t9 := >($t7, $t8) + 7: if ($t9) goto 8 else goto 12 + 8: label L1 + 9: $t10 := move($t3) + 10: $t2 := $t10 + 11: goto 16 + 12: label L0 + 13: $t11 := move($t4) + 14: $t2 := $t11 + 15: goto 16 + 16: label L2 + 17: $t12 := move($t2) + 18: return $t12 +} + + +[variant baseline] +public fun u64::min($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: bool + var $t10: u64 + var $t11: u64 + var $t12: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := move($t1) + 3: $t4 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := copy($t4) + 6: $t9 := <($t7, $t8) + 7: if ($t9) goto 8 else goto 12 + 8: label L1 + 9: $t10 := move($t3) + 10: $t2 := $t10 + 11: goto 16 + 12: label L0 + 13: $t11 := move($t4) + 14: $t2 := $t11 + 15: goto 16 + 16: label L2 + 17: $t12 := move($t2) + 18: return $t12 +} + + +[variant baseline] +public fun u64::pow($t0|base: u64, $t1|exponent: u8): u64 { + var $t2|base#1#1: u64 + var $t3|exponent#1#1: u8 + var $t4|res#1#1: u64 + var $t5: u64 + var $t6: u8 + var $t7: u64 + var $t8: u8 + var $t9: u8 + var $t10: bool + var $t11: u8 + var $t12: u8 + var $t13: u8 + var $t14: u8 + var $t15: bool + var $t16: u64 + var $t17: u64 + var $t18: u64 + var $t19: u8 + var $t20: u8 + var $t21: u8 + var $t22: u64 + var $t23: u64 + var $t24: u64 + var $t25: u8 + var $t26: u8 + var $t27: u8 + var $t28: u64 + 0: $t5 := move($t0) + 1: $t2 := $t5 + 2: $t6 := move($t1) + 3: $t3 := $t6 + 4: $t7 := 1 + 5: $t4 := $t7 + 6: goto 7 + 7: label L5 + 8: $t8 := copy($t3) + 9: $t9 := 1 + 10: $t10 := >=($t8, $t9) + 11: if ($t10) goto 12 else goto 41 + 12: label L1 + 13: goto 14 + 14: label L2 + 15: $t11 := copy($t3) + 16: $t12 := 2 + 17: $t13 := %($t11, $t12) + 18: $t14 := 0 + 19: $t15 := ==($t13, $t14) + 20: if ($t15) goto 21 else goto 31 + 21: label L4 + 22: $t16 := copy($t2) + 23: $t17 := move($t2) + 24: $t18 := *($t16, $t17) + 25: $t2 := $t18 + 26: $t19 := move($t3) + 27: $t20 := 2 + 28: $t21 := /($t19, $t20) + 29: $t3 := $t21 + 30: goto 7 + 31: label L3 + 32: $t22 := move($t4) + 33: $t23 := copy($t2) + 34: $t24 := *($t22, $t23) + 35: $t4 := $t24 + 36: $t25 := move($t3) + 37: $t26 := 1 + 38: $t27 := -($t25, $t26) + 39: $t3 := $t27 + 40: goto 7 + 41: label L0 + 42: $t28 := move($t4) + 43: return $t28 +} + + +[variant baseline] +public fun u64::sqrt($t0|x: u64): u64 { + var $t1|bit#1#1: u128 + var $t2|res#1#1: u128 + var $t3|x#1#1: u64 + var $t4|x#2#1: u128 + var $t5: u64 + var $t6: u128 + var $t7: u128 + var $t8: u64 + var $t9: u128 + var $t10: u128 + var $t11: u128 + var $t12: bool + var $t13: u128 + var $t14: u128 + var $t15: u128 + var $t16: u128 + var $t17: bool + var $t18: u128 + var $t19: u128 + var $t20: u128 + var $t21: u128 + var $t22: u128 + var $t23: u128 + var $t24: u8 + var $t25: u128 + var $t26: u128 + var $t27: u128 + var $t28: u128 + var $t29: u8 + var $t30: u128 + var $t31: u128 + var $t32: u8 + var $t33: u128 + var $t34: u128 + var $t35: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := 18446744073709551616 + 3: $t1 := $t6 + 4: $t7 := 0 + 5: $t2 := $t7 + 6: $t8 := move($t3) + 7: $t9 := (u128)($t8) + 8: $t4 := $t9 + 9: goto 10 + 10: label L6 + 11: $t10 := copy($t1) + 12: $t11 := 0 + 13: $t12 := !=($t10, $t11) + 14: if ($t12) goto 15 else goto 50 + 15: label L1 + 16: goto 17 + 17: label L2 + 18: $t13 := copy($t4) + 19: $t14 := copy($t2) + 20: $t15 := copy($t1) + 21: $t16 := +($t14, $t15) + 22: $t17 := >=($t13, $t16) + 23: if ($t17) goto 24 else goto 38 + 24: label L4 + 25: $t18 := move($t4) + 26: $t19 := copy($t2) + 27: $t20 := copy($t1) + 28: $t21 := +($t19, $t20) + 29: $t22 := -($t18, $t21) + 30: $t4 := $t22 + 31: $t23 := move($t2) + 32: $t24 := 1 + 33: $t25 := >>($t23, $t24) + 34: $t26 := copy($t1) + 35: $t27 := +($t25, $t26) + 36: $t2 := $t27 + 37: goto 44 + 38: label L3 + 39: $t28 := move($t2) + 40: $t29 := 1 + 41: $t30 := >>($t28, $t29) + 42: $t2 := $t30 + 43: goto 44 + 44: label L5 + 45: $t31 := move($t1) + 46: $t32 := 2 + 47: $t33 := >>($t31, $t32) + 48: $t1 := $t33 + 49: goto 10 + 50: label L0 + 51: $t34 := move($t2) + 52: $t35 := (u64)($t34) + 53: return $t35 +} + + [variant baseline] public fun vector::append<#0>($t0|lhs: &mut vector<#0>, $t1|other: vector<#0>) { var $t2: &mut vector<#0> @@ -231,27 +565,25 @@ public fun vector::insert<#0>($t0|v: &mut vector<#0>, $t1|e: #0, $t2|i: u64) { 15: $t13 := move($t1) 16: vector::push_back<#0>($t12, $t13) 17: goto 18 - 18: label L5 + 18: label L4 19: $t14 := copy($t2) 20: $t15 := copy($t3) 21: $t16 := <($t14, $t15) - 22: if ($t16) goto 23 else goto 35 + 22: if ($t16) goto 23 else goto 33 23: label L3 - 24: goto 25 - 25: label L4 - 26: $t17 := copy($t0) - 27: $t18 := copy($t2) - 28: $t19 := copy($t3) - 29: vector::swap<#0>($t17, $t18, $t19) - 30: $t20 := move($t2) - 31: $t21 := 1 - 32: $t22 := +($t20, $t21) - 33: $t2 := $t22 - 34: goto 18 - 35: label L2 - 36: $t23 := move($t0) - 37: destroy($t23) - 38: return () + 24: $t17 := copy($t0) + 25: $t18 := copy($t2) + 26: $t19 := copy($t3) + 27: vector::swap<#0>($t17, $t18, $t19) + 28: $t20 := move($t2) + 29: $t21 := 1 + 30: $t22 := +($t20, $t21) + 31: $t2 := $t22 + 32: goto 18 + 33: label L2 + 34: $t23 := move($t0) + 35: destroy($t23) + 36: return () } @@ -329,31 +661,29 @@ public fun vector::remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { 16: $t15 := -($t13, $t14) 17: $t4 := $t15 18: goto 19 - 19: label L5 + 19: label L4 20: $t16 := copy($t1) 21: $t17 := copy($t4) 22: $t18 := <($t16, $t17) - 23: if ($t18) goto 24 else goto 40 + 23: if ($t18) goto 24 else goto 38 24: label L3 - 25: goto 26 - 26: label L4 - 27: $t19 := copy($t0) - 28: $t3 := $t19 - 29: $t20 := copy($t1) - 30: $t2 := $t20 - 31: $t21 := move($t1) - 32: $t22 := 1 - 33: $t23 := +($t21, $t22) - 34: $t1 := $t23 - 35: $t24 := move($t3) - 36: $t25 := move($t2) - 37: $t26 := copy($t1) - 38: vector::swap<#0>($t24, $t25, $t26) - 39: goto 19 - 40: label L2 - 41: $t27 := move($t0) - 42: $t28 := vector::pop_back<#0>($t27) - 43: return $t28 + 25: $t19 := copy($t0) + 26: $t3 := $t19 + 27: $t20 := copy($t1) + 28: $t2 := $t20 + 29: $t21 := move($t1) + 30: $t22 := 1 + 31: $t23 := +($t21, $t22) + 32: $t1 := $t23 + 33: $t24 := move($t3) + 34: $t25 := move($t2) + 35: $t26 := copy($t1) + 36: vector::swap<#0>($t24, $t25, $t26) + 37: goto 19 + 38: label L2 + 39: $t27 := move($t0) + 40: $t28 := vector::pop_back<#0>($t27) + 41: return $t28 } @@ -406,31 +736,29 @@ public fun vector::reverse<#0>($t0|v: &mut vector<#0>) { 17: $t14 := -($t12, $t13) 18: $t1 := $t14 19: goto 20 - 20: label L5 + 20: label L4 21: $t15 := copy($t2) 22: $t16 := copy($t1) 23: $t17 := <($t15, $t16) - 24: if ($t17) goto 25 else goto 41 + 24: if ($t17) goto 25 else goto 39 25: label L3 - 26: goto 27 - 27: label L4 - 28: $t18 := copy($t0) - 29: $t19 := copy($t2) - 30: $t20 := copy($t1) - 31: vector::swap<#0>($t18, $t19, $t20) - 32: $t21 := move($t2) - 33: $t22 := 1 - 34: $t23 := +($t21, $t22) - 35: $t2 := $t23 - 36: $t24 := move($t1) - 37: $t25 := 1 - 38: $t26 := -($t24, $t25) - 39: $t1 := $t26 - 40: goto 20 - 41: label L2 - 42: $t27 := move($t0) - 43: destroy($t27) - 44: return () + 26: $t18 := copy($t0) + 27: $t19 := copy($t2) + 28: $t20 := copy($t1) + 29: vector::swap<#0>($t18, $t19, $t20) + 30: $t21 := move($t2) + 31: $t22 := 1 + 32: $t23 := +($t21, $t22) + 33: $t2 := $t23 + 34: $t24 := move($t1) + 35: $t25 := 1 + 36: $t26 := -($t24, $t25) + 37: $t1 := $t26 + 38: goto 20 + 39: label L2 + 40: $t27 := move($t0) + 41: destroy($t27) + 42: return () } @@ -526,530 +854,4518 @@ fun ReturnRefsIntoVec::return_vec_index_mut($t0|v: &mut vector): &mut u64 { 3: return $t3 } -============ after pipeline `escape_analysis` ================ [variant baseline] -public fun vector::append<#0>($t0|lhs: &mut vector<#0>, $t1|other: vector<#0>) { - var $t2: &mut vector<#0> - var $t3: &vector<#0> - var $t4: bool - var $t5: bool - var $t6: &mut vector<#0> - var $t7: &mut vector<#0> - var $t8: #0 - var $t9: &mut vector<#0> - var $t10: vector<#0> - 0: $t2 := borrow_local($t1) - 1: vector::reverse<#0>($t2) - 2: goto 3 - 3: label L3 - 4: $t3 := borrow_local($t1) - 5: $t4 := vector::is_empty<#0>($t3) - 6: $t5 := !($t4) - 7: if ($t5) goto 8 else goto 16 - 8: label L1 - 9: goto 10 +public fun option::borrow<#0>($t0|t: &option::Option<#0>): � { + var $t1: &option::Option<#0> + var $t2: bool + var $t3: &option::Option<#0> + var $t4: u64 + var $t5: &option::Option<#0> + var $t6: &vector<#0> + var $t7: u64 + var $t8: � + 0: $t1 := copy($t0) + 1: $t2 := option::is_some<#0>($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 10 + 5: label L0 + 6: $t3 := move($t0) + 7: destroy($t3) + 8: $t4 := 262145 + 9: abort($t4) 10: label L2 - 11: $t6 := copy($t0) - 12: $t7 := borrow_local($t1) - 13: $t8 := vector::pop_back<#0>($t7) - 14: vector::push_back<#0>($t6, $t8) - 15: goto 3 - 16: label L0 - 17: $t9 := move($t0) - 18: destroy($t9) - 19: $t10 := move($t1) - 20: vector::destroy_empty<#0>($t10) - 21: return () + 11: $t5 := move($t0) + 12: $t6 := borrow_field>.vec($t5) + 13: $t7 := 0 + 14: $t8 := vector::borrow<#0>($t6, $t7) + 15: return $t8 } [variant baseline] -public native fun vector::borrow<#0>($t0|v: &vector<#0>, $t1|i: u64): � +public fun option::borrow_mut<#0>($t0|t: &mut option::Option<#0>): &mut #0 { + var $t1: &mut option::Option<#0> + var $t2: &option::Option<#0> + var $t3: bool + var $t4: &mut option::Option<#0> + var $t5: u64 + var $t6: &mut option::Option<#0> + var $t7: &mut vector<#0> + var $t8: u64 + var $t9: &mut #0 + 0: $t1 := copy($t0) + 1: $t2 := freeze_ref($t1) + 2: $t3 := option::is_some<#0>($t2) + 3: if ($t3) goto 4 else goto 6 + 4: label L1 + 5: goto 11 + 6: label L0 + 7: $t4 := move($t0) + 8: destroy($t4) + 9: $t5 := 262145 + 10: abort($t5) + 11: label L2 + 12: $t6 := move($t0) + 13: $t7 := borrow_field>.vec($t6) + 14: $t8 := 0 + 15: $t9 := vector::borrow_mut<#0>($t7, $t8) + 16: return $t9 +} [variant baseline] -public native fun vector::borrow_mut<#0>($t0|v: &mut vector<#0>, $t1|i: u64): &mut #0; +public fun option::contains<#0>($t0|t: &option::Option<#0>, $t1|e_ref: �): bool { + var $t2: &option::Option<#0> + var $t3: &vector<#0> + var $t4: � + var $t5: bool + 0: $t2 := move($t0) + 1: $t3 := borrow_field>.vec($t2) + 2: $t4 := move($t1) + 3: $t5 := vector::contains<#0>($t3, $t4) + 4: return $t5 +} [variant baseline] -public fun vector::contains<#0>($t0|v: &vector<#0>, $t1|e: �): bool { - var $t2|i#1#0: u64 - var $t3|len#1#0: u64 - var $t4: u64 - var $t5: &vector<#0> - var $t6: u64 - var $t7: u64 +public fun option::swap<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0): #0 { + var $t2|old_value#1#0: #0 + var $t3|vec_ref#1#0: &mut vector<#0> + var $t4: &mut option::Option<#0> + var $t5: &option::Option<#0> + var $t6: bool + var $t7: &mut option::Option<#0> var $t8: u64 - var $t9: bool - var $t10: &vector<#0> - var $t11: u64 - var $t12: � + var $t9: &mut option::Option<#0> + var $t10: &mut vector<#0> + var $t11: &mut vector<#0> + var $t12: #0 + var $t13: &mut vector<#0> + var $t14: #0 + var $t15: #0 + 0: $t4 := copy($t0) + 1: $t5 := freeze_ref($t4) + 2: $t6 := option::is_some<#0>($t5) + 3: if ($t6) goto 4 else goto 6 + 4: label L1 + 5: goto 11 + 6: label L0 + 7: $t7 := move($t0) + 8: destroy($t7) + 9: $t8 := 262145 + 10: abort($t8) + 11: label L2 + 12: $t9 := move($t0) + 13: $t10 := borrow_field>.vec($t9) + 14: $t3 := $t10 + 15: $t11 := copy($t3) + 16: $t12 := vector::pop_back<#0>($t11) + 17: $t2 := $t12 + 18: $t13 := move($t3) + 19: $t14 := move($t1) + 20: vector::push_back<#0>($t13, $t14) + 21: $t15 := move($t2) + 22: return $t15 +} + + +[variant baseline] +public fun option::borrow_with_default<#0>($t0|t: &option::Option<#0>, $t1|default_ref: �): � { + var $t2|tmp#$2: � + var $t3|vec_ref#1#0: &vector<#0> + var $t4: &option::Option<#0> + var $t5: &vector<#0> + var $t6: &vector<#0> + var $t7: bool + var $t8: &vector<#0> + var $t9: � + var $t10: � + var $t11: &vector<#0> + var $t12: u64 var $t13: � - var $t14: bool - var $t15: &vector<#0> - var $t16: � - var $t17: bool - var $t18: u64 - var $t19: u64 - var $t20: u64 - var $t21: &vector<#0> - var $t22: � - var $t23: bool - 0: $t4 := 0 - 1: $t2 := $t4 - 2: $t5 := copy($t0) - 3: $t6 := vector::length<#0>($t5) - 4: $t3 := $t6 - 5: goto 6 - 6: label L5 - 7: $t7 := copy($t2) - 8: $t8 := copy($t3) - 9: $t9 := <($t7, $t8) - 10: if ($t9) goto 11 else goto 33 - 11: label L1 - 12: goto 13 - 13: label L2 - 14: $t10 := copy($t0) - 15: $t11 := copy($t2) - 16: $t12 := vector::borrow<#0>($t10, $t11) - 17: $t13 := copy($t1) - 18: $t14 := ==($t12, $t13) - 19: if ($t14) goto 20 else goto 27 - 20: label L4 - 21: $t15 := move($t0) - 22: destroy($t15) - 23: $t16 := move($t1) - 24: destroy($t16) - 25: $t17 := true - 26: return $t17 - 27: label L3 - 28: $t18 := move($t2) - 29: $t19 := 1 - 30: $t20 := +($t18, $t19) - 31: $t2 := $t20 - 32: goto 6 - 33: label L0 - 34: $t21 := move($t0) - 35: destroy($t21) - 36: $t22 := move($t1) - 37: destroy($t22) - 38: $t23 := false - 39: return $t23 + var $t14: � + 0: $t4 := move($t0) + 1: $t5 := borrow_field>.vec($t4) + 2: $t3 := $t5 + 3: $t6 := copy($t3) + 4: $t7 := vector::is_empty<#0>($t6) + 5: if ($t7) goto 6 else goto 12 + 6: label L1 + 7: $t8 := move($t3) + 8: destroy($t8) + 9: $t9 := move($t1) + 10: $t2 := $t9 + 11: goto 20 + 12: label L0 + 13: $t10 := move($t1) + 14: destroy($t10) + 15: $t11 := move($t3) + 16: $t12 := 0 + 17: $t13 := vector::borrow<#0>($t11, $t12) + 18: $t2 := $t13 + 19: goto 20 + 20: label L2 + 21: $t14 := move($t2) + 22: return $t14 } [variant baseline] -public native fun vector::destroy_empty<#0>($t0|v: vector<#0>); +public fun option::destroy_none<#0>($t0|t: option::Option<#0>) { + var $t1: &option::Option<#0> + var $t2: bool + var $t3: u64 + var $t4: option::Option<#0> + var $t5: vector<#0> + 0: $t1 := borrow_local($t0) + 1: $t2 := option::is_none<#0>($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t3 := 262144 + 7: abort($t3) + 8: label L2 + 9: $t4 := move($t0) + 10: $t5 := unpack option::Option<#0>($t4) + 11: vector::destroy_empty<#0>($t5) + 12: return () +} [variant baseline] -public native fun vector::empty<#0>(): vector<#0>; +public fun option::destroy_some<#0>($t0|t: option::Option<#0>): #0 { + var $t1|elem#1#0: #0 + var $t2|vec#1#0: vector<#0> + var $t3: &option::Option<#0> + var $t4: bool + var $t5: u64 + var $t6: option::Option<#0> + var $t7: vector<#0> + var $t8: &mut vector<#0> + var $t9: #0 + var $t10: vector<#0> + var $t11: #0 + 0: $t3 := borrow_local($t0) + 1: $t4 := option::is_some<#0>($t3) + 2: if ($t4) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t5 := 262145 + 7: abort($t5) + 8: label L2 + 9: $t6 := move($t0) + 10: $t7 := unpack option::Option<#0>($t6) + 11: $t2 := $t7 + 12: $t8 := borrow_local($t2) + 13: $t9 := vector::pop_back<#0>($t8) + 14: $t1 := $t9 + 15: $t10 := move($t2) + 16: vector::destroy_empty<#0>($t10) + 17: $t11 := move($t1) + 18: return $t11 +} [variant baseline] -public fun vector::index_of<#0>($t0|v: &vector<#0>, $t1|e: �): (bool, u64) { - var $t2|i#1#0: u64 - var $t3|len#1#0: u64 - var $t4: u64 +public fun option::destroy_with_default<#0>($t0|t: option::Option<#0>, $t1|default: #0): #0 { + var $t2|tmp#$2: #0 + var $t3|vec#1#0: vector<#0> + var $t4: option::Option<#0> + var $t5: vector<#0> + var $t6: &vector<#0> + var $t7: bool + var $t8: #0 + var $t9: &mut vector<#0> + var $t10: #0 + var $t11: #0 + 0: $t4 := move($t0) + 1: $t5 := unpack option::Option<#0>($t4) + 2: $t3 := $t5 + 3: $t6 := borrow_local($t3) + 4: $t7 := vector::is_empty<#0>($t6) + 5: if ($t7) goto 6 else goto 10 + 6: label L1 + 7: $t8 := move($t1) + 8: $t2 := $t8 + 9: goto 15 + 10: label L0 + 11: $t9 := borrow_local($t3) + 12: $t10 := vector::pop_back<#0>($t9) + 13: $t2 := $t10 + 14: goto 15 + 15: label L2 + 16: $t11 := move($t2) + 17: return $t11 +} + + +[variant baseline] +public fun option::extract<#0>($t0|t: &mut option::Option<#0>): #0 { + var $t1: &mut option::Option<#0> + var $t2: &option::Option<#0> + var $t3: bool + var $t4: &mut option::Option<#0> + var $t5: u64 + var $t6: &mut option::Option<#0> + var $t7: &mut vector<#0> + var $t8: #0 + 0: $t1 := copy($t0) + 1: $t2 := freeze_ref($t1) + 2: $t3 := option::is_some<#0>($t2) + 3: if ($t3) goto 4 else goto 6 + 4: label L1 + 5: goto 11 + 6: label L0 + 7: $t4 := move($t0) + 8: destroy($t4) + 9: $t5 := 262145 + 10: abort($t5) + 11: label L2 + 12: $t6 := move($t0) + 13: $t7 := borrow_field>.vec($t6) + 14: $t8 := vector::pop_back<#0>($t7) + 15: return $t8 +} + + +[variant baseline] +public fun option::fill<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0) { + var $t2|vec_ref#1#0: &mut vector<#0> + var $t3: &mut option::Option<#0> + var $t4: &mut vector<#0> + var $t5: &mut vector<#0> + var $t6: &vector<#0> + var $t7: bool + var $t8: &mut vector<#0> + var $t9: u64 + var $t10: &mut vector<#0> + var $t11: #0 + 0: $t3 := move($t0) + 1: $t4 := borrow_field>.vec($t3) + 2: $t2 := $t4 + 3: $t5 := copy($t2) + 4: $t6 := freeze_ref($t5) + 5: $t7 := vector::is_empty<#0>($t6) + 6: if ($t7) goto 7 else goto 9 + 7: label L1 + 8: goto 14 + 9: label L0 + 10: $t8 := move($t2) + 11: destroy($t8) + 12: $t9 := 262144 + 13: abort($t9) + 14: label L2 + 15: $t10 := move($t2) + 16: $t11 := move($t1) + 17: vector::push_back<#0>($t10, $t11) + 18: return () +} + + +[variant baseline] +public fun option::get_with_default<#0>($t0|t: &option::Option<#0>, $t1|default: #0): #0 { + var $t2|tmp#$2: #0 + var $t3|vec_ref#1#0: &vector<#0> + var $t4: &option::Option<#0> + var $t5: &vector<#0> + var $t6: &vector<#0> + var $t7: bool + var $t8: &vector<#0> + var $t9: #0 + var $t10: &vector<#0> + var $t11: u64 + var $t12: � + var $t13: #0 + var $t14: #0 + 0: $t4 := move($t0) + 1: $t5 := borrow_field>.vec($t4) + 2: $t3 := $t5 + 3: $t6 := copy($t3) + 4: $t7 := vector::is_empty<#0>($t6) + 5: if ($t7) goto 6 else goto 12 + 6: label L1 + 7: $t8 := move($t3) + 8: destroy($t8) + 9: $t9 := move($t1) + 10: $t2 := $t9 + 11: goto 19 + 12: label L0 + 13: $t10 := move($t3) + 14: $t11 := 0 + 15: $t12 := vector::borrow<#0>($t10, $t11) + 16: $t13 := read_ref($t12) + 17: $t2 := $t13 + 18: goto 19 + 19: label L2 + 20: $t14 := move($t2) + 21: return $t14 +} + + +[variant baseline] +public fun option::is_none<#0>($t0|t: &option::Option<#0>): bool { + var $t1: &option::Option<#0> + var $t2: &vector<#0> + var $t3: bool + 0: $t1 := move($t0) + 1: $t2 := borrow_field>.vec($t1) + 2: $t3 := vector::is_empty<#0>($t2) + 3: return $t3 +} + + +[variant baseline] +public fun option::is_some<#0>($t0|t: &option::Option<#0>): bool { + var $t1: &option::Option<#0> + var $t2: &vector<#0> + var $t3: bool + var $t4: bool + 0: $t1 := move($t0) + 1: $t2 := borrow_field>.vec($t1) + 2: $t3 := vector::is_empty<#0>($t2) + 3: $t4 := !($t3) + 4: return $t4 +} + + +[variant baseline] +public fun option::none<#0>(): option::Option<#0> { + var $t0: vector<#0> + var $t1: option::Option<#0> + 0: $t0 := vector::empty<#0>() + 1: $t1 := pack option::Option<#0>($t0) + 2: return $t1 +} + + +[variant baseline] +public fun option::some<#0>($t0|e: #0): option::Option<#0> { + var $t1: #0 + var $t2: vector<#0> + var $t3: option::Option<#0> + 0: $t1 := move($t0) + 1: $t2 := vector::singleton<#0>($t1) + 2: $t3 := pack option::Option<#0>($t2) + 3: return $t3 +} + + +[variant baseline] +public fun option::swap_or_fill<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0): option::Option<#0> { + var $t2|tmp#$2: option::Option<#0> + var $t3|old_value#1#0: option::Option<#0> + var $t4|vec_ref#1#0: &mut vector<#0> + var $t5: &mut option::Option<#0> + var $t6: &mut vector<#0> + var $t7: &mut vector<#0> + var $t8: &vector<#0> + var $t9: bool + var $t10: option::Option<#0> + var $t11: &mut vector<#0> + var $t12: #0 + var $t13: option::Option<#0> + var $t14: option::Option<#0> + var $t15: &mut vector<#0> + var $t16: #0 + var $t17: option::Option<#0> + 0: $t5 := move($t0) + 1: $t6 := borrow_field>.vec($t5) + 2: $t4 := $t6 + 3: $t7 := copy($t4) + 4: $t8 := freeze_ref($t7) + 5: $t9 := vector::is_empty<#0>($t8) + 6: if ($t9) goto 7 else goto 11 + 7: label L1 + 8: $t10 := option::none<#0>() + 9: $t2 := $t10 + 10: goto 17 + 11: label L0 + 12: $t11 := copy($t4) + 13: $t12 := vector::pop_back<#0>($t11) + 14: $t13 := option::some<#0>($t12) + 15: $t2 := $t13 + 16: goto 17 + 17: label L2 + 18: $t14 := move($t2) + 19: $t3 := $t14 + 20: $t15 := move($t4) + 21: $t16 := move($t1) + 22: vector::push_back<#0>($t15, $t16) + 23: $t17 := move($t3) + 24: return $t17 +} + + +[variant baseline] +public fun option::to_vec<#0>($t0|t: option::Option<#0>): vector<#0> { + var $t1: option::Option<#0> + var $t2: vector<#0> + 0: $t1 := move($t0) + 1: $t2 := unpack option::Option<#0>($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::append($t0|string: &mut ascii::String, $t1|other: ascii::String) { + var $t2: &mut ascii::String + var $t3: &mut vector + var $t4: ascii::String + var $t5: vector + 0: $t2 := move($t0) + 1: $t3 := borrow_field.bytes($t2) + 2: $t4 := move($t1) + 3: $t5 := ascii::into_bytes($t4) + 4: vector::append($t3, $t5) + 5: return () +} + + +[variant baseline] +public fun ascii::index_of($t0|string: &ascii::String, $t1|substr: &ascii::String): u64 { + var $t2|tmp#$2: bool + var $t3|i#1#0: u64 + var $t4|j#1#0: u64 + var $t5|m#1#0: u64 + var $t6|n#1#0: u64 + var $t7: u64 + var $t8: &ascii::String + var $t9: u64 + var $t10: &ascii::String + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: bool + var $t15: &ascii::String + var $t16: &ascii::String + var $t17: u64 + var $t18: u64 + var $t19: u64 + var $t20: u64 + var $t21: u64 + var $t22: bool + var $t23: u64 + var $t24: u64 + var $t25: u64 + var $t26: bool + var $t27: &ascii::String + var $t28: &vector + var $t29: u64 + var $t30: u64 + var $t31: u64 + var $t32: &u8 + var $t33: u8 + var $t34: &ascii::String + var $t35: &vector + var $t36: u64 + var $t37: &u8 + var $t38: u8 + var $t39: bool + var $t40: bool + var $t41: bool + var $t42: u64 + var $t43: u64 + var $t44: u64 + var $t45: u64 + var $t46: u64 + var $t47: bool + var $t48: &ascii::String + var $t49: &ascii::String + var $t50: u64 + var $t51: u64 + var $t52: u64 + var $t53: u64 + var $t54: &ascii::String + var $t55: &ascii::String + var $t56: u64 + 0: $t7 := 0 + 1: $t3 := $t7 + 2: $t8 := copy($t0) + 3: $t9 := ascii::length($t8) + 4: $t10 := copy($t1) + 5: $t11 := ascii::length($t10) + 6: $t5 := $t11 + 7: $t6 := $t9 + 8: $t12 := copy($t6) + 9: $t13 := copy($t5) + 10: $t14 := <($t12, $t13) + 11: if ($t14) goto 12 else goto 19 + 12: label L1 + 13: $t15 := move($t1) + 14: destroy($t15) + 15: $t16 := move($t0) + 16: destroy($t16) + 17: $t17 := move($t6) + 18: return $t17 + 19: label L0 + 20: $t18 := copy($t3) + 21: $t19 := copy($t6) + 22: $t20 := copy($t5) + 23: $t21 := -($t19, $t20) + 24: $t22 := <=($t18, $t21) + 25: if ($t22) goto 26 else goto 84 + 26: label L3 + 27: $t23 := 0 + 28: $t4 := $t23 + 29: goto 30 + 30: label L10 + 31: $t24 := copy($t4) + 32: $t25 := copy($t5) + 33: $t26 := <($t24, $t25) + 34: if ($t26) goto 35 else goto 53 + 35: label L5 + 36: goto 37 + 37: label L6 + 38: $t27 := copy($t0) + 39: $t28 := borrow_field.bytes($t27) + 40: $t29 := copy($t3) + 41: $t30 := copy($t4) + 42: $t31 := +($t29, $t30) + 43: $t32 := vector::borrow($t28, $t31) + 44: $t33 := read_ref($t32) + 45: $t34 := copy($t1) + 46: $t35 := borrow_field.bytes($t34) + 47: $t36 := copy($t4) + 48: $t37 := vector::borrow($t35, $t36) + 49: $t38 := read_ref($t37) + 50: $t39 := ==($t33, $t38) + 51: $t2 := $t39 + 52: goto 57 + 53: label L4 + 54: $t40 := false + 55: $t2 := $t40 + 56: goto 57 + 57: label L7 + 58: $t41 := move($t2) + 59: if ($t41) goto 60 else goto 66 + 60: label L9 + 61: $t42 := move($t4) + 62: $t43 := 1 + 63: $t44 := +($t42, $t43) + 64: $t4 := $t44 + 65: goto 30 + 66: label L8 + 67: $t45 := move($t4) + 68: $t46 := copy($t5) + 69: $t47 := ==($t45, $t46) + 70: if ($t47) goto 71 else goto 78 + 71: label L12 + 72: $t48 := move($t1) + 73: destroy($t48) + 74: $t49 := move($t0) + 75: destroy($t49) + 76: $t50 := move($t3) + 77: return $t50 + 78: label L11 + 79: $t51 := move($t3) + 80: $t52 := 1 + 81: $t53 := +($t51, $t52) + 82: $t3 := $t53 + 83: goto 19 + 84: label L2 + 85: $t54 := move($t1) + 86: destroy($t54) + 87: $t55 := move($t0) + 88: destroy($t55) + 89: $t56 := move($t6) + 90: return $t56 +} + + +[variant baseline] +public fun ascii::insert($t0|s: &mut ascii::String, $t1|at: u64, $t2|o: ascii::String) { + var $t3|e#1#2: u8 + var $t4|v#1#1: vector + var $t5: u64 + var $t6: &mut ascii::String + var $t7: &ascii::String + var $t8: u64 + var $t9: bool + var $t10: &mut ascii::String + var $t11: u64 + var $t12: ascii::String + var $t13: vector + var $t14: &vector + var $t15: bool + var $t16: bool + var $t17: &mut vector + var $t18: u8 + var $t19: &mut ascii::String + var $t20: &mut vector + var $t21: u8 + var $t22: u64 + var $t23: &mut ascii::String + var $t24: vector + 0: $t5 := copy($t1) + 1: $t6 := copy($t0) + 2: $t7 := freeze_ref($t6) + 3: $t8 := ascii::length($t7) + 4: $t9 := <=($t5, $t8) + 5: if ($t9) goto 6 else goto 8 + 6: label L1 + 7: goto 13 + 8: label L0 + 9: $t10 := move($t0) + 10: destroy($t10) + 11: $t11 := 65537 + 12: abort($t11) + 13: label L2 + 14: $t12 := move($t2) + 15: $t13 := ascii::into_bytes($t12) + 16: $t4 := $t13 + 17: goto 18 + 18: label L5 + 19: $t14 := borrow_local($t4) + 20: $t15 := vector::is_empty($t14) + 21: $t16 := !($t15) + 22: if ($t16) goto 23 else goto 33 + 23: label L4 + 24: $t17 := borrow_local($t4) + 25: $t18 := vector::pop_back($t17) + 26: $t3 := $t18 + 27: $t19 := copy($t0) + 28: $t20 := borrow_field.bytes($t19) + 29: $t21 := move($t3) + 30: $t22 := copy($t1) + 31: vector::insert($t20, $t21, $t22) + 32: goto 18 + 33: label L3 + 34: $t23 := move($t0) + 35: destroy($t23) + 36: $t24 := move($t4) + 37: vector::destroy_empty($t24) + 38: return () +} + + +[variant baseline] +public fun ascii::is_empty($t0|string: &ascii::String): bool { + var $t1: &ascii::String + var $t2: &vector + var $t3: bool + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: $t3 := vector::is_empty($t2) + 3: return $t3 +} + + +[variant baseline] +public fun ascii::length($t0|string: &ascii::String): u64 { + var $t1: &ascii::String + var $t2: &vector + var $t3: u64 + 0: $t1 := move($t0) + 1: $t2 := ascii::as_bytes($t1) + 2: $t3 := vector::length($t2) + 3: return $t3 +} + + +[variant baseline] +public fun ascii::all_characters_printable($t0|string: &ascii::String): bool { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: bool + var $t3|i#1#12: u64 + var $t4|i#1#9: u64 + var $t5|stop#1#9: u64 + var $t6|v#1#3: &vector + var $t7: &ascii::String + var $t8: &vector + var $t9: &vector + var $t10: u64 + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: bool + var $t16: u64 + var $t17: &vector + var $t18: u64 + var $t19: &u8 + var $t20: u8 + var $t21: bool + var $t22: bool + var $t23: &vector + var $t24: bool + var $t25: u64 + var $t26: u64 + var $t27: u64 + var $t28: &vector + var $t29: bool + var $t30: bool + 0: $t7 := move($t0) + 1: $t8 := borrow_field.bytes($t7) + 2: $t6 := $t8 + 3: $t9 := copy($t6) + 4: $t10 := vector::length($t9) + 5: $t1 := $t10 + 6: $t11 := 0 + 7: $t4 := $t11 + 8: $t12 := move($t1) + 9: $t5 := $t12 + 10: goto 11 + 11: label L5 + 12: $t13 := copy($t4) + 13: $t14 := copy($t5) + 14: $t15 := <($t13, $t14) + 15: if ($t15) goto 16 else goto 38 + 16: label L1 + 17: $t16 := copy($t4) + 18: $t3 := $t16 + 19: $t17 := copy($t6) + 20: $t18 := move($t3) + 21: $t19 := vector::borrow($t17, $t18) + 22: $t20 := read_ref($t19) + 23: $t21 := ascii::is_printable_char($t20) + 24: $t22 := !($t21) + 25: if ($t22) goto 26 else goto 32 + 26: label L3 + 27: $t23 := move($t6) + 28: destroy($t23) + 29: $t24 := false + 30: $t2 := $t24 + 31: goto 44 + 32: label L2 + 33: $t25 := move($t4) + 34: $t26 := 1 + 35: $t27 := +($t25, $t26) + 36: $t4 := $t27 + 37: goto 11 + 38: label L0 + 39: $t28 := move($t6) + 40: destroy($t28) + 41: $t29 := true + 42: $t2 := $t29 + 43: goto 44 + 44: label L4 + 45: $t30 := move($t2) + 46: return $t30 +} + + +[variant baseline] +public fun ascii::string($t0|bytes: vector): ascii::String { + var $t1|x#1#0: option::Option + var $t2: vector + var $t3: option::Option + var $t4: &option::Option + var $t5: bool + var $t6: u64 + var $t7: option::Option + var $t8: ascii::String + 0: $t2 := move($t0) + 1: $t3 := ascii::try_string($t2) + 2: $t1 := $t3 + 3: $t4 := borrow_local($t1) + 4: $t5 := option::is_some($t4) + 5: if ($t5) goto 6 else goto 8 + 6: label L1 + 7: goto 11 + 8: label L0 + 9: $t6 := 65536 + 10: abort($t6) + 11: label L2 + 12: $t7 := move($t1) + 13: $t8 := option::destroy_some($t7) + 14: return $t8 +} + + +[variant baseline] +public fun ascii::as_bytes($t0|string: &ascii::String): &vector { + var $t1: &ascii::String + var $t2: &vector + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::byte($t0|char: ascii::Char): u8 { + var $t1: ascii::Char + var $t2: u8 + 0: $t1 := move($t0) + 1: $t2 := unpack ascii::Char($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::char($t0|byte: u8): ascii::Char { + var $t1: u8 + var $t2: bool + var $t3: u64 + var $t4: u8 + var $t5: ascii::Char + 0: $t1 := copy($t0) + 1: $t2 := ascii::is_valid_char($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t3 := 65536 + 7: abort($t3) + 8: label L2 + 9: $t4 := move($t0) + 10: $t5 := pack ascii::Char($t4) + 11: return $t5 +} + + +[variant baseline] +fun ascii::char_to_lowercase($t0|byte: u8): u8 { + var $t1|tmp#$1: bool + var $t2|tmp#$2: u8 + var $t3: u8 + var $t4: u8 + var $t5: bool + var $t6: u8 + var $t7: u8 + var $t8: bool + var $t9: bool + var $t10: bool + var $t11: u8 + var $t12: u8 + var $t13: u8 + var $t14: u8 + var $t15: u8 + 0: $t3 := copy($t0) + 1: $t4 := 65 + 2: $t5 := >=($t3, $t4) + 3: if ($t5) goto 4 else goto 10 + 4: label L1 + 5: $t6 := copy($t0) + 6: $t7 := 90 + 7: $t8 := <=($t6, $t7) + 8: $t1 := $t8 + 9: goto 14 + 10: label L0 + 11: $t9 := false + 12: $t1 := $t9 + 13: goto 14 + 14: label L2 + 15: $t10 := move($t1) + 16: if ($t10) goto 17 else goto 23 + 17: label L4 + 18: $t11 := move($t0) + 19: $t12 := 32 + 20: $t13 := +($t11, $t12) + 21: $t2 := $t13 + 22: goto 27 + 23: label L3 + 24: $t14 := move($t0) + 25: $t2 := $t14 + 26: goto 27 + 27: label L5 + 28: $t15 := move($t2) + 29: return $t15 +} + + +[variant baseline] +fun ascii::char_to_uppercase($t0|byte: u8): u8 { + var $t1|tmp#$1: bool + var $t2|tmp#$2: u8 + var $t3: u8 + var $t4: u8 + var $t5: bool + var $t6: u8 + var $t7: u8 + var $t8: bool + var $t9: bool + var $t10: bool + var $t11: u8 + var $t12: u8 + var $t13: u8 + var $t14: u8 + var $t15: u8 + 0: $t3 := copy($t0) + 1: $t4 := 97 + 2: $t5 := >=($t3, $t4) + 3: if ($t5) goto 4 else goto 10 + 4: label L1 + 5: $t6 := copy($t0) + 6: $t7 := 122 + 7: $t8 := <=($t6, $t7) + 8: $t1 := $t8 + 9: goto 14 + 10: label L0 + 11: $t9 := false + 12: $t1 := $t9 + 13: goto 14 + 14: label L2 + 15: $t10 := move($t1) + 16: if ($t10) goto 17 else goto 23 + 17: label L4 + 18: $t11 := move($t0) + 19: $t12 := 32 + 20: $t13 := -($t11, $t12) + 21: $t2 := $t13 + 22: goto 27 + 23: label L3 + 24: $t14 := move($t0) + 25: $t2 := $t14 + 26: goto 27 + 27: label L5 + 28: $t15 := move($t2) + 29: return $t15 +} + + +[variant baseline] +public fun ascii::into_bytes($t0|string: ascii::String): vector { + var $t1: ascii::String + var $t2: vector + 0: $t1 := move($t0) + 1: $t2 := unpack ascii::String($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::is_printable_char($t0|byte: u8): bool { + var $t1|tmp#$1: bool + var $t2: u8 + var $t3: u8 + var $t4: bool + var $t5: u8 + var $t6: u8 + var $t7: bool + var $t8: bool + var $t9: bool + 0: $t2 := copy($t0) + 1: $t3 := 32 + 2: $t4 := >=($t2, $t3) + 3: if ($t4) goto 4 else goto 10 + 4: label L1 + 5: $t5 := move($t0) + 6: $t6 := 126 + 7: $t7 := <=($t5, $t6) + 8: $t1 := $t7 + 9: goto 14 + 10: label L0 + 11: $t8 := false + 12: $t1 := $t8 + 13: goto 14 + 14: label L2 + 15: $t9 := move($t1) + 16: return $t9 +} + + +[variant baseline] +public fun ascii::is_valid_char($t0|b: u8): bool { + var $t1: u8 + var $t2: u8 + var $t3: bool + 0: $t1 := move($t0) + 1: $t2 := 127 + 2: $t3 := <=($t1, $t2) + 3: return $t3 +} + + +[variant baseline] +public fun ascii::pop_char($t0|string: &mut ascii::String): ascii::Char { + var $t1: &mut ascii::String + var $t2: &mut vector + var $t3: u8 + var $t4: ascii::Char + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: $t3 := vector::pop_back($t2) + 3: $t4 := pack ascii::Char($t3) + 4: return $t4 +} + + +[variant baseline] +public fun ascii::push_char($t0|string: &mut ascii::String, $t1|char: ascii::Char) { + var $t2: &mut ascii::String + var $t3: &mut vector + var $t4: &ascii::Char + var $t5: &u8 + var $t6: u8 + 0: $t2 := move($t0) + 1: $t3 := borrow_field.bytes($t2) + 2: $t4 := borrow_local($t1) + 3: $t5 := borrow_field.byte($t4) + 4: $t6 := read_ref($t5) + 5: vector::push_back($t3, $t6) + 6: return () +} + + +[variant baseline] +public fun ascii::substring($t0|string: &ascii::String, $t1|i: u64, $t2|j: u64): ascii::String { + var $t3|tmp#$3: bool + var $t4|bytes#1#0: vector + var $t5|i#1#3: u64 + var $t6|i#1#6: u64 + var $t7|stop#1#3: u64 + var $t8: u64 + var $t9: u64 + var $t10: bool + var $t11: u64 + var $t12: &ascii::String + var $t13: u64 + var $t14: bool + var $t15: bool + var $t16: bool + var $t17: &ascii::String + var $t18: u64 + var $t19: vector + var $t20: u64 + var $t21: u64 + var $t22: u64 + var $t23: u64 + var $t24: bool + var $t25: u64 + var $t26: &mut vector + var $t27: &ascii::String + var $t28: &vector + var $t29: u64 + var $t30: &u8 + var $t31: u8 + var $t32: u64 + var $t33: u64 + var $t34: u64 + var $t35: &ascii::String + var $t36: vector + var $t37: ascii::String + 0: $t8 := copy($t1) + 1: $t9 := copy($t2) + 2: $t10 := <=($t8, $t9) + 3: if ($t10) goto 4 else goto 11 + 4: label L1 + 5: $t11 := copy($t2) + 6: $t12 := copy($t0) + 7: $t13 := ascii::length($t12) + 8: $t14 := <=($t11, $t13) + 9: $t3 := $t14 + 10: goto 15 + 11: label L0 + 12: $t15 := false + 13: $t3 := $t15 + 14: goto 15 + 15: label L2 + 16: $t16 := move($t3) + 17: if ($t16) goto 18 else goto 20 + 18: label L4 + 19: goto 25 + 20: label L3 + 21: $t17 := move($t0) + 22: destroy($t17) + 23: $t18 := 65537 + 24: abort($t18) + 25: label L5 + 26: $t19 := [] + 27: $t4 := $t19 + 28: $t20 := move($t1) + 29: $t5 := $t20 + 30: $t21 := move($t2) + 31: $t7 := $t21 + 32: goto 33 + 33: label L8 + 34: $t22 := copy($t5) + 35: $t23 := copy($t7) + 36: $t24 := <($t22, $t23) + 37: if ($t24) goto 38 else goto 53 + 38: label L7 + 39: $t25 := copy($t5) + 40: $t6 := $t25 + 41: $t26 := borrow_local($t4) + 42: $t27 := copy($t0) + 43: $t28 := borrow_field.bytes($t27) + 44: $t29 := move($t6) + 45: $t30 := vector::borrow($t28, $t29) + 46: $t31 := read_ref($t30) + 47: vector::push_back($t26, $t31) + 48: $t32 := move($t5) + 49: $t33 := 1 + 50: $t34 := +($t32, $t33) + 51: $t5 := $t34 + 52: goto 33 + 53: label L6 + 54: $t35 := move($t0) + 55: destroy($t35) + 56: $t36 := move($t4) + 57: $t37 := pack ascii::String($t36) + 58: return $t37 +} + + +[variant baseline] +public fun ascii::to_lowercase($t0|string: &ascii::String): ascii::String { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: u8 + var $t3|tmp#$3: &mut vector + var $t4|e#1#13: &u8 + var $t5|i#1#12: u64 + var $t6|i#1#9: u64 + var $t7|r#1#1: vector + var $t8|stop#1#9: u64 + var $t9|v#1#1: &vector + var $t10|v#1#3: &vector + var $t11: &ascii::String + var $t12: &vector + var $t13: vector + var $t14: &vector + var $t15: &vector + var $t16: u64 + var $t17: u64 + var $t18: u64 + var $t19: u64 + var $t20: u64 + var $t21: bool + var $t22: u64 + var $t23: &vector + var $t24: u64 + var $t25: &u8 + var $t26: &mut vector + var $t27: &u8 + var $t28: u8 + var $t29: u8 + var $t30: &mut vector + var $t31: u8 + var $t32: u64 + var $t33: u64 + var $t34: u64 + var $t35: &vector + var $t36: vector + var $t37: ascii::String + 0: $t11 := move($t0) + 1: $t12 := ascii::as_bytes($t11) + 2: $t9 := $t12 + 3: $t13 := [] + 4: $t7 := $t13 + 5: $t14 := move($t9) + 6: $t10 := $t14 + 7: $t15 := copy($t10) + 8: $t16 := vector::length($t15) + 9: $t1 := $t16 + 10: $t17 := 0 + 11: $t6 := $t17 + 12: $t18 := move($t1) + 13: $t8 := $t18 + 14: goto 15 + 15: label L2 + 16: $t19 := copy($t6) + 17: $t20 := copy($t8) + 18: $t21 := <($t19, $t20) + 19: if ($t21) goto 20 else goto 41 + 20: label L1 + 21: $t22 := copy($t6) + 22: $t5 := $t22 + 23: $t23 := copy($t10) + 24: $t24 := move($t5) + 25: $t25 := vector::borrow($t23, $t24) + 26: $t4 := $t25 + 27: $t26 := borrow_local($t7) + 28: $t3 := $t26 + 29: $t27 := move($t4) + 30: $t28 := read_ref($t27) + 31: $t29 := ascii::char_to_lowercase($t28) + 32: $t2 := $t29 + 33: $t30 := move($t3) + 34: $t31 := move($t2) + 35: vector::push_back($t30, $t31) + 36: $t32 := move($t6) + 37: $t33 := 1 + 38: $t34 := +($t32, $t33) + 39: $t6 := $t34 + 40: goto 15 + 41: label L0 + 42: $t35 := move($t10) + 43: destroy($t35) + 44: $t36 := move($t7) + 45: $t37 := pack ascii::String($t36) + 46: return $t37 +} + + +[variant baseline] +public fun ascii::to_uppercase($t0|string: &ascii::String): ascii::String { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: u8 + var $t3|tmp#$3: &mut vector + var $t4|e#1#13: &u8 + var $t5|i#1#12: u64 + var $t6|i#1#9: u64 + var $t7|r#1#1: vector + var $t8|stop#1#9: u64 + var $t9|v#1#1: &vector + var $t10|v#1#3: &vector + var $t11: &ascii::String + var $t12: &vector + var $t13: vector + var $t14: &vector + var $t15: &vector + var $t16: u64 + var $t17: u64 + var $t18: u64 + var $t19: u64 + var $t20: u64 + var $t21: bool + var $t22: u64 + var $t23: &vector + var $t24: u64 + var $t25: &u8 + var $t26: &mut vector + var $t27: &u8 + var $t28: u8 + var $t29: u8 + var $t30: &mut vector + var $t31: u8 + var $t32: u64 + var $t33: u64 + var $t34: u64 + var $t35: &vector + var $t36: vector + var $t37: ascii::String + 0: $t11 := move($t0) + 1: $t12 := ascii::as_bytes($t11) + 2: $t9 := $t12 + 3: $t13 := [] + 4: $t7 := $t13 + 5: $t14 := move($t9) + 6: $t10 := $t14 + 7: $t15 := copy($t10) + 8: $t16 := vector::length($t15) + 9: $t1 := $t16 + 10: $t17 := 0 + 11: $t6 := $t17 + 12: $t18 := move($t1) + 13: $t8 := $t18 + 14: goto 15 + 15: label L2 + 16: $t19 := copy($t6) + 17: $t20 := copy($t8) + 18: $t21 := <($t19, $t20) + 19: if ($t21) goto 20 else goto 41 + 20: label L1 + 21: $t22 := copy($t6) + 22: $t5 := $t22 + 23: $t23 := copy($t10) + 24: $t24 := move($t5) + 25: $t25 := vector::borrow($t23, $t24) + 26: $t4 := $t25 + 27: $t26 := borrow_local($t7) + 28: $t3 := $t26 + 29: $t27 := move($t4) + 30: $t28 := read_ref($t27) + 31: $t29 := ascii::char_to_uppercase($t28) + 32: $t2 := $t29 + 33: $t30 := move($t3) + 34: $t31 := move($t2) + 35: vector::push_back($t30, $t31) + 36: $t32 := move($t6) + 37: $t33 := 1 + 38: $t34 := +($t32, $t33) + 39: $t6 := $t34 + 40: goto 15 + 41: label L0 + 42: $t35 := move($t10) + 43: destroy($t35) + 44: $t36 := move($t7) + 45: $t37 := pack ascii::String($t36) + 46: return $t37 +} + + +[variant baseline] +public fun ascii::try_string($t0|bytes: vector): option::Option { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: bool + var $t3|tmp#$3: option::Option + var $t4|i#1#12: u64 + var $t5|i#1#9: u64 + var $t6|stop#1#9: u64 + var $t7|v#1#3: &vector + var $t8: &vector + var $t9: &vector + var $t10: u64 + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: bool + var $t16: u64 + var $t17: &vector + var $t18: u64 + var $t19: &u8 + var $t20: u8 + var $t21: bool + var $t22: bool + var $t23: &vector + var $t24: bool + var $t25: u64 + var $t26: u64 + var $t27: u64 + var $t28: &vector + var $t29: bool + var $t30: bool + var $t31: vector + var $t32: ascii::String + var $t33: option::Option + var $t34: option::Option + var $t35: option::Option + 0: $t8 := borrow_local($t0) + 1: $t7 := $t8 + 2: $t9 := copy($t7) + 3: $t10 := vector::length($t9) + 4: $t1 := $t10 + 5: $t11 := 0 + 6: $t5 := $t11 + 7: $t12 := move($t1) + 8: $t6 := $t12 + 9: goto 10 + 10: label L5 + 11: $t13 := copy($t5) + 12: $t14 := copy($t6) + 13: $t15 := <($t13, $t14) + 14: if ($t15) goto 15 else goto 37 + 15: label L1 + 16: $t16 := copy($t5) + 17: $t4 := $t16 + 18: $t17 := copy($t7) + 19: $t18 := move($t4) + 20: $t19 := vector::borrow($t17, $t18) + 21: $t20 := read_ref($t19) + 22: $t21 := ascii::is_valid_char($t20) + 23: $t22 := !($t21) + 24: if ($t22) goto 25 else goto 31 + 25: label L3 + 26: $t23 := move($t7) + 27: destroy($t23) + 28: $t24 := false + 29: $t2 := $t24 + 30: goto 43 + 31: label L2 + 32: $t25 := move($t5) + 33: $t26 := 1 + 34: $t27 := +($t25, $t26) + 35: $t5 := $t27 + 36: goto 10 + 37: label L0 + 38: $t28 := move($t7) + 39: destroy($t28) + 40: $t29 := true + 41: $t2 := $t29 + 42: goto 43 + 43: label L4 + 44: $t30 := move($t2) + 45: if ($t30) goto 46 else goto 52 + 46: label L7 + 47: $t31 := move($t0) + 48: $t32 := pack ascii::String($t31) + 49: $t33 := option::some($t32) + 50: $t3 := $t33 + 51: goto 56 + 52: label L6 + 53: $t34 := option::none() + 54: $t3 := $t34 + 55: goto 56 + 56: label L8 + 57: $t35 := move($t3) + 58: return $t35 +} + + +[variant baseline] +public fun string::append($t0|s: &mut string::String, $t1|r: string::String) { + var $t2: &mut string::String + var $t3: &mut vector + var $t4: &string::String + var $t5: &vector + var $t6: vector + 0: $t2 := move($t0) + 1: $t3 := borrow_field.bytes($t2) + 2: $t4 := borrow_local($t1) + 3: $t5 := borrow_field.bytes($t4) + 4: $t6 := read_ref($t5) + 5: vector::append($t3, $t6) + 6: return () +} + + +[variant baseline] +public fun string::index_of($t0|s: &string::String, $t1|r: &string::String): u64 { + var $t2: &string::String + var $t3: &vector + var $t4: &string::String + var $t5: &vector + var $t6: u64 + 0: $t2 := move($t0) + 1: $t3 := borrow_field.bytes($t2) + 2: $t4 := move($t1) + 3: $t5 := borrow_field.bytes($t4) + 4: $t6 := string::internal_index_of($t3, $t5) + 5: return $t6 +} + + +[variant baseline] +public fun string::insert($t0|s: &mut string::String, $t1|at: u64, $t2|o: string::String) { + var $t3|tmp#$3: bool + var $t4|bytes#1#0: &vector + var $t5|end#1#0: string::String + var $t6|front#1#0: string::String + var $t7|l#1#0: u64 + var $t8: &mut string::String + var $t9: &vector + var $t10: u64 + var $t11: &vector + var $t12: u64 + var $t13: bool + var $t14: &vector + var $t15: u64 + var $t16: bool + var $t17: &vector + var $t18: bool + var $t19: bool + var $t20: &mut string::String + var $t21: u64 + var $t22: &mut string::String + var $t23: &string::String + var $t24: u64 + var $t25: &mut string::String + var $t26: &string::String + var $t27: u64 + var $t28: u64 + var $t29: string::String + var $t30: &mut string::String + var $t31: &string::String + var $t32: u64 + var $t33: u64 + var $t34: string::String + var $t35: &mut string::String + var $t36: string::String + var $t37: &mut string::String + var $t38: string::String + var $t39: string::String + var $t40: &mut string::String + 0: $t8 := copy($t0) + 1: $t9 := borrow_field.bytes($t8) + 2: $t4 := $t9 + 3: $t10 := copy($t1) + 4: $t11 := copy($t4) + 5: $t12 := vector::length($t11) + 6: $t13 := <=($t10, $t12) + 7: if ($t13) goto 8 else goto 14 + 8: label L1 + 9: $t14 := move($t4) + 10: $t15 := copy($t1) + 11: $t16 := string::internal_is_char_boundary($t14, $t15) + 12: $t3 := $t16 + 13: goto 20 + 14: label L0 + 15: $t17 := move($t4) + 16: destroy($t17) + 17: $t18 := false + 18: $t3 := $t18 + 19: goto 20 + 20: label L2 + 21: $t19 := move($t3) + 22: if ($t19) goto 23 else goto 25 + 23: label L4 + 24: goto 30 + 25: label L3 + 26: $t20 := move($t0) + 27: destroy($t20) + 28: $t21 := 2 + 29: abort($t21) + 30: label L5 + 31: $t22 := copy($t0) + 32: $t23 := freeze_ref($t22) + 33: $t24 := string::length($t23) + 34: $t7 := $t24 + 35: $t25 := copy($t0) + 36: $t26 := freeze_ref($t25) + 37: $t27 := 0 + 38: $t28 := copy($t1) + 39: $t29 := string::substring($t26, $t27, $t28) + 40: $t6 := $t29 + 41: $t30 := copy($t0) + 42: $t31 := freeze_ref($t30) + 43: $t32 := move($t1) + 44: $t33 := move($t7) + 45: $t34 := string::substring($t31, $t32, $t33) + 46: $t5 := $t34 + 47: $t35 := borrow_local($t6) + 48: $t36 := move($t2) + 49: string::append($t35, $t36) + 50: $t37 := borrow_local($t6) + 51: $t38 := move($t5) + 52: string::append($t37, $t38) + 53: $t39 := move($t6) + 54: $t40 := move($t0) + 55: write_ref($t40, $t39) + 56: return () +} + + +[variant baseline] +public fun string::is_empty($t0|s: &string::String): bool { + var $t1: &string::String + var $t2: &vector + var $t3: bool + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: $t3 := vector::is_empty($t2) + 3: return $t3 +} + + +[variant baseline] +public fun string::length($t0|s: &string::String): u64 { + var $t1: &string::String + var $t2: &vector + var $t3: u64 + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: $t3 := vector::length($t2) + 3: return $t3 +} + + +[variant baseline] +public fun string::as_bytes($t0|s: &string::String): &vector { + var $t1: &string::String + var $t2: &vector + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: return $t2 +} + + +[variant baseline] +public fun string::into_bytes($t0|s: string::String): vector { + var $t1: string::String + var $t2: vector + 0: $t1 := move($t0) + 1: $t2 := unpack string::String($t1) + 2: return $t2 +} + + +[variant baseline] +public fun string::bytes($t0|s: &string::String): &vector { + var $t1: &string::String + var $t2: &vector + 0: $t1 := move($t0) + 1: $t2 := string::as_bytes($t1) + 2: return $t2 +} + + +[variant baseline] +public fun string::substring($t0|s: &string::String, $t1|i: u64, $t2|j: u64): string::String { + var $t3|tmp#$3: bool + var $t4|tmp#$4: bool + var $t5|tmp#$5: bool + var $t6|bytes#1#0: &vector + var $t7|l#1#0: u64 + var $t8: &string::String + var $t9: &vector + var $t10: &vector + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: bool + var $t15: u64 + var $t16: u64 + var $t17: bool + var $t18: bool + var $t19: bool + var $t20: &vector + var $t21: u64 + var $t22: bool + var $t23: bool + var $t24: bool + var $t25: &vector + var $t26: u64 + var $t27: bool + var $t28: bool + var $t29: bool + var $t30: &vector + var $t31: u64 + var $t32: &vector + var $t33: u64 + var $t34: u64 + var $t35: vector + var $t36: string::String + 0: $t8 := move($t0) + 1: $t9 := borrow_field.bytes($t8) + 2: $t6 := $t9 + 3: $t10 := copy($t6) + 4: $t11 := vector::length($t10) + 5: $t7 := $t11 + 6: $t12 := copy($t2) + 7: $t13 := move($t7) + 8: $t14 := <=($t12, $t13) + 9: if ($t14) goto 10 else goto 16 + 10: label L1 + 11: $t15 := copy($t1) + 12: $t16 := copy($t2) + 13: $t17 := <=($t15, $t16) + 14: $t3 := $t17 + 15: goto 20 + 16: label L0 + 17: $t18 := false + 18: $t3 := $t18 + 19: goto 20 + 20: label L2 + 21: $t19 := move($t3) + 22: if ($t19) goto 23 else goto 29 + 23: label L4 + 24: $t20 := copy($t6) + 25: $t21 := copy($t1) + 26: $t22 := string::internal_is_char_boundary($t20, $t21) + 27: $t4 := $t22 + 28: goto 33 + 29: label L3 + 30: $t23 := false + 31: $t4 := $t23 + 32: goto 33 + 33: label L5 + 34: $t24 := move($t4) + 35: if ($t24) goto 36 else goto 42 + 36: label L7 + 37: $t25 := copy($t6) + 38: $t26 := copy($t2) + 39: $t27 := string::internal_is_char_boundary($t25, $t26) + 40: $t5 := $t27 + 41: goto 46 + 42: label L6 + 43: $t28 := false + 44: $t5 := $t28 + 45: goto 46 + 46: label L8 + 47: $t29 := move($t5) + 48: if ($t29) goto 49 else goto 51 + 49: label L10 + 50: goto 56 + 51: label L9 + 52: $t30 := move($t6) + 53: destroy($t30) + 54: $t31 := 2 + 55: abort($t31) + 56: label L11 + 57: $t32 := move($t6) + 58: $t33 := move($t1) + 59: $t34 := move($t2) + 60: $t35 := string::internal_sub_string($t32, $t33, $t34) + 61: $t36 := pack string::String($t35) + 62: return $t36 +} + + +[variant baseline] +public fun string::append_utf8($t0|s: &mut string::String, $t1|bytes: vector) { + var $t2: &mut string::String + var $t3: vector + var $t4: string::String + 0: $t2 := move($t0) + 1: $t3 := move($t1) + 2: $t4 := string::utf8($t3) + 3: string::append($t2, $t4) + 4: return () +} + + +[variant baseline] +public fun string::from_ascii($t0|s: ascii::String): string::String { + var $t1: ascii::String + var $t2: vector + var $t3: string::String + 0: $t1 := move($t0) + 1: $t2 := ascii::into_bytes($t1) + 2: $t3 := pack string::String($t2) + 3: return $t3 +} + + +[variant baseline] +native fun string::internal_check_utf8($t0|v: &vector): bool; + + +[variant baseline] +native fun string::internal_index_of($t0|v: &vector, $t1|r: &vector): u64; + + +[variant baseline] +native fun string::internal_is_char_boundary($t0|v: &vector, $t1|i: u64): bool; + + +[variant baseline] +native fun string::internal_sub_string($t0|v: &vector, $t1|i: u64, $t2|j: u64): vector; + + +[variant baseline] +public fun string::sub_string($t0|s: &string::String, $t1|i: u64, $t2|j: u64): string::String { + var $t3: &string::String + var $t4: u64 + var $t5: u64 + var $t6: string::String + 0: $t3 := move($t0) + 1: $t4 := move($t1) + 2: $t5 := move($t2) + 3: $t6 := string::substring($t3, $t4, $t5) + 4: return $t6 +} + + +[variant baseline] +public fun string::to_ascii($t0|s: string::String): ascii::String { + var $t1: string::String + var $t2: vector + var $t3: ascii::String + 0: $t1 := move($t0) + 1: $t2 := unpack string::String($t1) + 2: $t3 := ascii::string($t2) + 3: return $t3 +} + + +[variant baseline] +public fun string::try_utf8($t0|bytes: vector): option::Option { + var $t1|tmp#$1: option::Option + var $t2: &vector + var $t3: bool + var $t4: vector + var $t5: string::String + var $t6: option::Option + var $t7: option::Option + var $t8: option::Option + 0: $t2 := borrow_local($t0) + 1: $t3 := string::internal_check_utf8($t2) + 2: if ($t3) goto 3 else goto 9 + 3: label L1 + 4: $t4 := move($t0) + 5: $t5 := pack string::String($t4) + 6: $t6 := option::some($t5) + 7: $t1 := $t6 + 8: goto 13 + 9: label L0 + 10: $t7 := option::none() + 11: $t1 := $t7 + 12: goto 13 + 13: label L2 + 14: $t8 := move($t1) + 15: return $t8 +} + + +[variant baseline] +public fun string::utf8($t0|bytes: vector): string::String { + var $t1: &vector + var $t2: bool + var $t3: u64 + var $t4: vector + var $t5: string::String + 0: $t1 := borrow_local($t0) + 1: $t2 := string::internal_check_utf8($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t3 := 1 + 7: abort($t3) + 8: label L2 + 9: $t4 := move($t0) + 10: $t5 := pack string::String($t4) + 11: return $t5 +} + +============ after pipeline `escape_analysis` ================ + +[variant baseline] +public fun u64::diff($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: bool + var $t10: u64 + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: u64 + var $t16: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := move($t1) + 3: $t4 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := copy($t4) + 6: $t9 := >($t7, $t8) + 7: if ($t9) goto 8 else goto 14 + 8: label L1 + 9: $t10 := move($t3) + 10: $t11 := move($t4) + 11: $t12 := -($t10, $t11) + 12: $t2 := $t12 + 13: goto 20 + 14: label L0 + 15: $t13 := move($t4) + 16: $t14 := move($t3) + 17: $t15 := -($t13, $t14) + 18: $t2 := $t15 + 19: goto 20 + 20: label L2 + 21: $t16 := move($t2) + 22: return $t16 +} + + +[variant baseline] +public fun u64::divide_and_round_up($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: u64 + var $t10: u64 + var $t11: bool + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: u64 + var $t16: u64 + var $t17: u64 + var $t18: u64 + var $t19: u64 + var $t20: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := move($t1) + 3: $t4 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := copy($t4) + 6: $t9 := %($t7, $t8) + 7: $t10 := 0 + 8: $t11 := ==($t9, $t10) + 9: if ($t11) goto 10 else goto 16 + 10: label L1 + 11: $t12 := move($t3) + 12: $t13 := move($t4) + 13: $t14 := /($t12, $t13) + 14: $t2 := $t14 + 15: goto 24 + 16: label L0 + 17: $t15 := move($t3) + 18: $t16 := move($t4) + 19: $t17 := /($t15, $t16) + 20: $t18 := 1 + 21: $t19 := +($t17, $t18) + 22: $t2 := $t19 + 23: goto 24 + 24: label L2 + 25: $t20 := move($t2) + 26: return $t20 +} + + +[variant baseline] +public fun u64::max($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: bool + var $t10: u64 + var $t11: u64 + var $t12: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := move($t1) + 3: $t4 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := copy($t4) + 6: $t9 := >($t7, $t8) + 7: if ($t9) goto 8 else goto 12 + 8: label L1 + 9: $t10 := move($t3) + 10: $t2 := $t10 + 11: goto 16 + 12: label L0 + 13: $t11 := move($t4) + 14: $t2 := $t11 + 15: goto 16 + 16: label L2 + 17: $t12 := move($t2) + 18: return $t12 +} + + +[variant baseline] +public fun u64::min($t0|x: u64, $t1|y: u64): u64 { + var $t2|tmp#$2: u64 + var $t3|x#1#1: u64 + var $t4|y#1#1: u64 + var $t5: u64 + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: bool + var $t10: u64 + var $t11: u64 + var $t12: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := move($t1) + 3: $t4 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := copy($t4) + 6: $t9 := <($t7, $t8) + 7: if ($t9) goto 8 else goto 12 + 8: label L1 + 9: $t10 := move($t3) + 10: $t2 := $t10 + 11: goto 16 + 12: label L0 + 13: $t11 := move($t4) + 14: $t2 := $t11 + 15: goto 16 + 16: label L2 + 17: $t12 := move($t2) + 18: return $t12 +} + + +[variant baseline] +public fun u64::pow($t0|base: u64, $t1|exponent: u8): u64 { + var $t2|base#1#1: u64 + var $t3|exponent#1#1: u8 + var $t4|res#1#1: u64 + var $t5: u64 + var $t6: u8 + var $t7: u64 + var $t8: u8 + var $t9: u8 + var $t10: bool + var $t11: u8 + var $t12: u8 + var $t13: u8 + var $t14: u8 + var $t15: bool + var $t16: u64 + var $t17: u64 + var $t18: u64 + var $t19: u8 + var $t20: u8 + var $t21: u8 + var $t22: u64 + var $t23: u64 + var $t24: u64 + var $t25: u8 + var $t26: u8 + var $t27: u8 + var $t28: u64 + 0: $t5 := move($t0) + 1: $t2 := $t5 + 2: $t6 := move($t1) + 3: $t3 := $t6 + 4: $t7 := 1 + 5: $t4 := $t7 + 6: goto 7 + 7: label L5 + 8: $t8 := copy($t3) + 9: $t9 := 1 + 10: $t10 := >=($t8, $t9) + 11: if ($t10) goto 12 else goto 41 + 12: label L1 + 13: goto 14 + 14: label L2 + 15: $t11 := copy($t3) + 16: $t12 := 2 + 17: $t13 := %($t11, $t12) + 18: $t14 := 0 + 19: $t15 := ==($t13, $t14) + 20: if ($t15) goto 21 else goto 31 + 21: label L4 + 22: $t16 := copy($t2) + 23: $t17 := move($t2) + 24: $t18 := *($t16, $t17) + 25: $t2 := $t18 + 26: $t19 := move($t3) + 27: $t20 := 2 + 28: $t21 := /($t19, $t20) + 29: $t3 := $t21 + 30: goto 7 + 31: label L3 + 32: $t22 := move($t4) + 33: $t23 := copy($t2) + 34: $t24 := *($t22, $t23) + 35: $t4 := $t24 + 36: $t25 := move($t3) + 37: $t26 := 1 + 38: $t27 := -($t25, $t26) + 39: $t3 := $t27 + 40: goto 7 + 41: label L0 + 42: $t28 := move($t4) + 43: return $t28 +} + + +[variant baseline] +public fun u64::sqrt($t0|x: u64): u64 { + var $t1|bit#1#1: u128 + var $t2|res#1#1: u128 + var $t3|x#1#1: u64 + var $t4|x#2#1: u128 + var $t5: u64 + var $t6: u128 + var $t7: u128 + var $t8: u64 + var $t9: u128 + var $t10: u128 + var $t11: u128 + var $t12: bool + var $t13: u128 + var $t14: u128 + var $t15: u128 + var $t16: u128 + var $t17: bool + var $t18: u128 + var $t19: u128 + var $t20: u128 + var $t21: u128 + var $t22: u128 + var $t23: u128 + var $t24: u8 + var $t25: u128 + var $t26: u128 + var $t27: u128 + var $t28: u128 + var $t29: u8 + var $t30: u128 + var $t31: u128 + var $t32: u8 + var $t33: u128 + var $t34: u128 + var $t35: u64 + 0: $t5 := move($t0) + 1: $t3 := $t5 + 2: $t6 := 18446744073709551616 + 3: $t1 := $t6 + 4: $t7 := 0 + 5: $t2 := $t7 + 6: $t8 := move($t3) + 7: $t9 := (u128)($t8) + 8: $t4 := $t9 + 9: goto 10 + 10: label L6 + 11: $t10 := copy($t1) + 12: $t11 := 0 + 13: $t12 := !=($t10, $t11) + 14: if ($t12) goto 15 else goto 50 + 15: label L1 + 16: goto 17 + 17: label L2 + 18: $t13 := copy($t4) + 19: $t14 := copy($t2) + 20: $t15 := copy($t1) + 21: $t16 := +($t14, $t15) + 22: $t17 := >=($t13, $t16) + 23: if ($t17) goto 24 else goto 38 + 24: label L4 + 25: $t18 := move($t4) + 26: $t19 := copy($t2) + 27: $t20 := copy($t1) + 28: $t21 := +($t19, $t20) + 29: $t22 := -($t18, $t21) + 30: $t4 := $t22 + 31: $t23 := move($t2) + 32: $t24 := 1 + 33: $t25 := >>($t23, $t24) + 34: $t26 := copy($t1) + 35: $t27 := +($t25, $t26) + 36: $t2 := $t27 + 37: goto 44 + 38: label L3 + 39: $t28 := move($t2) + 40: $t29 := 1 + 41: $t30 := >>($t28, $t29) + 42: $t2 := $t30 + 43: goto 44 + 44: label L5 + 45: $t31 := move($t1) + 46: $t32 := 2 + 47: $t33 := >>($t31, $t32) + 48: $t1 := $t33 + 49: goto 10 + 50: label L0 + 51: $t34 := move($t2) + 52: $t35 := (u64)($t34) + 53: return $t35 +} + + +[variant baseline] +public fun vector::append<#0>($t0|lhs: &mut vector<#0>, $t1|other: vector<#0>) { + var $t2: &mut vector<#0> + var $t3: &vector<#0> + var $t4: bool + var $t5: bool + var $t6: &mut vector<#0> + var $t7: &mut vector<#0> + var $t8: #0 + var $t9: &mut vector<#0> + var $t10: vector<#0> + 0: $t2 := borrow_local($t1) + 1: vector::reverse<#0>($t2) + 2: goto 3 + 3: label L3 + 4: $t3 := borrow_local($t1) + 5: $t4 := vector::is_empty<#0>($t3) + 6: $t5 := !($t4) + 7: if ($t5) goto 8 else goto 16 + 8: label L1 + 9: goto 10 + 10: label L2 + 11: $t6 := copy($t0) + 12: $t7 := borrow_local($t1) + 13: $t8 := vector::pop_back<#0>($t7) + 14: vector::push_back<#0>($t6, $t8) + 15: goto 3 + 16: label L0 + 17: $t9 := move($t0) + 18: destroy($t9) + 19: $t10 := move($t1) + 20: vector::destroy_empty<#0>($t10) + 21: return () +} + + +[variant baseline] +public native fun vector::borrow<#0>($t0|v: &vector<#0>, $t1|i: u64): � + + +[variant baseline] +public native fun vector::borrow_mut<#0>($t0|v: &mut vector<#0>, $t1|i: u64): &mut #0; + + +[variant baseline] +public fun vector::contains<#0>($t0|v: &vector<#0>, $t1|e: �): bool { + var $t2|i#1#0: u64 + var $t3|len#1#0: u64 + var $t4: u64 + var $t5: &vector<#0> + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: bool + var $t10: &vector<#0> + var $t11: u64 + var $t12: � + var $t13: � + var $t14: bool + var $t15: &vector<#0> + var $t16: � + var $t17: bool + var $t18: u64 + var $t19: u64 + var $t20: u64 + var $t21: &vector<#0> + var $t22: � + var $t23: bool + 0: $t4 := 0 + 1: $t2 := $t4 + 2: $t5 := copy($t0) + 3: $t6 := vector::length<#0>($t5) + 4: $t3 := $t6 + 5: goto 6 + 6: label L5 + 7: $t7 := copy($t2) + 8: $t8 := copy($t3) + 9: $t9 := <($t7, $t8) + 10: if ($t9) goto 11 else goto 33 + 11: label L1 + 12: goto 13 + 13: label L2 + 14: $t10 := copy($t0) + 15: $t11 := copy($t2) + 16: $t12 := vector::borrow<#0>($t10, $t11) + 17: $t13 := copy($t1) + 18: $t14 := ==($t12, $t13) + 19: if ($t14) goto 20 else goto 27 + 20: label L4 + 21: $t15 := move($t0) + 22: destroy($t15) + 23: $t16 := move($t1) + 24: destroy($t16) + 25: $t17 := true + 26: return $t17 + 27: label L3 + 28: $t18 := move($t2) + 29: $t19 := 1 + 30: $t20 := +($t18, $t19) + 31: $t2 := $t20 + 32: goto 6 + 33: label L0 + 34: $t21 := move($t0) + 35: destroy($t21) + 36: $t22 := move($t1) + 37: destroy($t22) + 38: $t23 := false + 39: return $t23 +} + + +[variant baseline] +public native fun vector::destroy_empty<#0>($t0|v: vector<#0>); + + +[variant baseline] +public native fun vector::empty<#0>(): vector<#0>; + + +[variant baseline] +public fun vector::index_of<#0>($t0|v: &vector<#0>, $t1|e: �): (bool, u64) { + var $t2|i#1#0: u64 + var $t3|len#1#0: u64 + var $t4: u64 + var $t5: &vector<#0> + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: bool + var $t10: &vector<#0> + var $t11: u64 + var $t12: � + var $t13: � + var $t14: bool + var $t15: &vector<#0> + var $t16: � + var $t17: bool + var $t18: u64 + var $t19: u64 + var $t20: u64 + var $t21: u64 + var $t22: &vector<#0> + var $t23: � + var $t24: bool + var $t25: u64 + 0: $t4 := 0 + 1: $t2 := $t4 + 2: $t5 := copy($t0) + 3: $t6 := vector::length<#0>($t5) + 4: $t3 := $t6 + 5: goto 6 + 6: label L5 + 7: $t7 := copy($t2) + 8: $t8 := copy($t3) + 9: $t9 := <($t7, $t8) + 10: if ($t9) goto 11 else goto 34 + 11: label L1 + 12: goto 13 + 13: label L2 + 14: $t10 := copy($t0) + 15: $t11 := copy($t2) + 16: $t12 := vector::borrow<#0>($t10, $t11) + 17: $t13 := copy($t1) + 18: $t14 := ==($t12, $t13) + 19: if ($t14) goto 20 else goto 28 + 20: label L4 + 21: $t15 := move($t0) + 22: destroy($t15) + 23: $t16 := move($t1) + 24: destroy($t16) + 25: $t17 := true + 26: $t18 := move($t2) + 27: return ($t17, $t18) + 28: label L3 + 29: $t19 := move($t2) + 30: $t20 := 1 + 31: $t21 := +($t19, $t20) + 32: $t2 := $t21 + 33: goto 6 + 34: label L0 + 35: $t22 := move($t0) + 36: destroy($t22) + 37: $t23 := move($t1) + 38: destroy($t23) + 39: $t24 := false + 40: $t25 := 0 + 41: return ($t24, $t25) +} + + +[variant baseline] +public fun vector::insert<#0>($t0|v: &mut vector<#0>, $t1|e: #0, $t2|i: u64) { + var $t3|len#1#0: u64 + var $t4: &mut vector<#0> + var $t5: &vector<#0> + var $t6: u64 + var $t7: u64 + var $t8: u64 + var $t9: bool + var $t10: &mut vector<#0> + var $t11: u64 + var $t12: &mut vector<#0> + var $t13: #0 + var $t14: u64 + var $t15: u64 + var $t16: bool + var $t17: &mut vector<#0> + var $t18: u64 + var $t19: u64 + var $t20: u64 + var $t21: u64 + var $t22: u64 + var $t23: &mut vector<#0> + 0: $t4 := copy($t0) + 1: $t5 := freeze_ref($t4) + 2: $t6 := vector::length<#0>($t5) + 3: $t3 := $t6 + 4: $t7 := copy($t2) + 5: $t8 := copy($t3) + 6: $t9 := >($t7, $t8) + 7: if ($t9) goto 8 else goto 13 + 8: label L1 + 9: $t10 := move($t0) + 10: destroy($t10) + 11: $t11 := 131072 + 12: abort($t11) + 13: label L0 + 14: $t12 := copy($t0) + 15: $t13 := move($t1) + 16: vector::push_back<#0>($t12, $t13) + 17: goto 18 + 18: label L4 + 19: $t14 := copy($t2) + 20: $t15 := copy($t3) + 21: $t16 := <($t14, $t15) + 22: if ($t16) goto 23 else goto 33 + 23: label L3 + 24: $t17 := copy($t0) + 25: $t18 := copy($t2) + 26: $t19 := copy($t3) + 27: vector::swap<#0>($t17, $t18, $t19) + 28: $t20 := move($t2) + 29: $t21 := 1 + 30: $t22 := +($t20, $t21) + 31: $t2 := $t22 + 32: goto 18 + 33: label L2 + 34: $t23 := move($t0) + 35: destroy($t23) + 36: return () +} + + +[variant baseline] +public fun vector::is_empty<#0>($t0|v: &vector<#0>): bool { + var $t1: &vector<#0> + var $t2: u64 + var $t3: u64 + var $t4: bool + 0: $t1 := move($t0) + 1: $t2 := vector::length<#0>($t1) + 2: $t3 := 0 + 3: $t4 := ==($t2, $t3) + 4: return $t4 +} + + +[variant baseline] +public native fun vector::length<#0>($t0|v: &vector<#0>): u64; + + +[variant baseline] +public native fun vector::pop_back<#0>($t0|v: &mut vector<#0>): #0; + + +[variant baseline] +public native fun vector::push_back<#0>($t0|v: &mut vector<#0>, $t1|e: #0); + + +[variant baseline] +public fun vector::remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { + var $t2|tmp#$2: u64 + var $t3|tmp#$3: &mut vector<#0> + var $t4|len#1#0: u64 + var $t5: &mut vector<#0> + var $t6: &vector<#0> + var $t7: u64 + var $t8: u64 + var $t9: u64 + var $t10: bool + var $t11: &mut vector<#0> + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: u64 + var $t16: u64 + var $t17: u64 + var $t18: bool + var $t19: &mut vector<#0> + var $t20: u64 + var $t21: u64 + var $t22: u64 + var $t23: u64 + var $t24: &mut vector<#0> + var $t25: u64 + var $t26: u64 + var $t27: &mut vector<#0> + var $t28: #0 + 0: $t5 := copy($t0) + 1: $t6 := freeze_ref($t5) + 2: $t7 := vector::length<#0>($t6) + 3: $t4 := $t7 + 4: $t8 := copy($t1) + 5: $t9 := copy($t4) + 6: $t10 := >=($t8, $t9) + 7: if ($t10) goto 8 else goto 13 + 8: label L1 + 9: $t11 := move($t0) + 10: destroy($t11) + 11: $t12 := 131072 + 12: abort($t12) + 13: label L0 + 14: $t13 := move($t4) + 15: $t14 := 1 + 16: $t15 := -($t13, $t14) + 17: $t4 := $t15 + 18: goto 19 + 19: label L4 + 20: $t16 := copy($t1) + 21: $t17 := copy($t4) + 22: $t18 := <($t16, $t17) + 23: if ($t18) goto 24 else goto 38 + 24: label L3 + 25: $t19 := copy($t0) + 26: $t3 := $t19 + 27: $t20 := copy($t1) + 28: $t2 := $t20 + 29: $t21 := move($t1) + 30: $t22 := 1 + 31: $t23 := +($t21, $t22) + 32: $t1 := $t23 + 33: $t24 := move($t3) + 34: $t25 := move($t2) + 35: $t26 := copy($t1) + 36: vector::swap<#0>($t24, $t25, $t26) + 37: goto 19 + 38: label L2 + 39: $t27 := move($t0) + 40: $t28 := vector::pop_back<#0>($t27) + 41: return $t28 +} + + +[variant baseline] +public fun vector::reverse<#0>($t0|v: &mut vector<#0>) { + var $t1|back_index#1#0: u64 + var $t2|front_index#1#0: u64 + var $t3|len#1#0: u64 + var $t4: &mut vector<#0> var $t5: &vector<#0> var $t6: u64 var $t7: u64 var $t8: u64 var $t9: bool + var $t10: &mut vector<#0> + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: u64 + var $t16: u64 + var $t17: bool + var $t18: &mut vector<#0> + var $t19: u64 + var $t20: u64 + var $t21: u64 + var $t22: u64 + var $t23: u64 + var $t24: u64 + var $t25: u64 + var $t26: u64 + var $t27: &mut vector<#0> + 0: $t4 := copy($t0) + 1: $t5 := freeze_ref($t4) + 2: $t6 := vector::length<#0>($t5) + 3: $t3 := $t6 + 4: $t7 := copy($t3) + 5: $t8 := 0 + 6: $t9 := ==($t7, $t8) + 7: if ($t9) goto 8 else goto 12 + 8: label L1 + 9: $t10 := move($t0) + 10: destroy($t10) + 11: return () + 12: label L0 + 13: $t11 := 0 + 14: $t2 := $t11 + 15: $t12 := move($t3) + 16: $t13 := 1 + 17: $t14 := -($t12, $t13) + 18: $t1 := $t14 + 19: goto 20 + 20: label L4 + 21: $t15 := copy($t2) + 22: $t16 := copy($t1) + 23: $t17 := <($t15, $t16) + 24: if ($t17) goto 25 else goto 39 + 25: label L3 + 26: $t18 := copy($t0) + 27: $t19 := copy($t2) + 28: $t20 := copy($t1) + 29: vector::swap<#0>($t18, $t19, $t20) + 30: $t21 := move($t2) + 31: $t22 := 1 + 32: $t23 := +($t21, $t22) + 33: $t2 := $t23 + 34: $t24 := move($t1) + 35: $t25 := 1 + 36: $t26 := -($t24, $t25) + 37: $t1 := $t26 + 38: goto 20 + 39: label L2 + 40: $t27 := move($t0) + 41: destroy($t27) + 42: return () +} + + +[variant baseline] +public fun vector::singleton<#0>($t0|e: #0): vector<#0> { + var $t1|v#1#0: vector<#0> + var $t2: vector<#0> + var $t3: &mut vector<#0> + var $t4: #0 + var $t5: vector<#0> + 0: $t2 := vector::empty<#0>() + 1: $t1 := $t2 + 2: $t3 := borrow_local($t1) + 3: $t4 := move($t0) + 4: vector::push_back<#0>($t3, $t4) + 5: $t5 := move($t1) + 6: return $t5 +} + + +[variant baseline] +public native fun vector::swap<#0>($t0|v: &mut vector<#0>, $t1|i: u64, $t2|j: u64); + + +[variant baseline] +public fun vector::swap_remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { + var $t2|last_idx#1#0: u64 + var $t3: &mut vector<#0> + var $t4: &vector<#0> + var $t5: bool + var $t6: bool + var $t7: &mut vector<#0> + var $t8: u64 + var $t9: &mut vector<#0> + var $t10: &vector<#0> + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: &mut vector<#0> + var $t15: u64 + var $t16: u64 + var $t17: &mut vector<#0> + var $t18: #0 + 0: $t3 := copy($t0) + 1: $t4 := freeze_ref($t3) + 2: $t5 := vector::is_empty<#0>($t4) + 3: $t6 := !($t5) + 4: if ($t6) goto 5 else goto 7 + 5: label L1 + 6: goto 12 + 7: label L0 + 8: $t7 := move($t0) + 9: destroy($t7) + 10: $t8 := 131072 + 11: abort($t8) + 12: label L2 + 13: $t9 := copy($t0) + 14: $t10 := freeze_ref($t9) + 15: $t11 := vector::length<#0>($t10) + 16: $t12 := 1 + 17: $t13 := -($t11, $t12) + 18: $t2 := $t13 + 19: $t14 := copy($t0) + 20: $t15 := move($t1) + 21: $t16 := move($t2) + 22: vector::swap<#0>($t14, $t15, $t16) + 23: $t17 := move($t0) + 24: $t18 := vector::pop_back<#0>($t17) + 25: return $t18 +} + + +[variant baseline] +fun ReturnRefsIntoVec::return_vec_index_immut($t0|v: &vector): &u64 { + var $t1: &vector + var $t2: u64 + var $t3: &u64 + 0: $t1 := move($t0) + 1: $t2 := 0 + 2: $t3 := vector::borrow($t1, $t2) + 3: return $t3 +} + + +[variant baseline] +fun ReturnRefsIntoVec::return_vec_index_mut($t0|v: &mut vector): &mut u64 { + var $t1: &mut vector + var $t2: u64 + var $t3: &mut u64 + 0: $t1 := move($t0) + 1: $t2 := 0 + 2: $t3 := vector::borrow_mut($t1, $t2) + 3: return $t3 +} + + +[variant baseline] +public fun option::borrow<#0>($t0|t: &option::Option<#0>): � { + var $t1: &option::Option<#0> + var $t2: bool + var $t3: &option::Option<#0> + var $t4: u64 + var $t5: &option::Option<#0> + var $t6: &vector<#0> + var $t7: u64 + var $t8: � + 0: $t1 := copy($t0) + 1: $t2 := option::is_some<#0>($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 10 + 5: label L0 + 6: $t3 := move($t0) + 7: destroy($t3) + 8: $t4 := 262145 + 9: abort($t4) + 10: label L2 + 11: $t5 := move($t0) + 12: $t6 := borrow_field>.vec($t5) + 13: $t7 := 0 + 14: $t8 := vector::borrow<#0>($t6, $t7) + 15: return $t8 +} + + +[variant baseline] +public fun option::borrow_mut<#0>($t0|t: &mut option::Option<#0>): &mut #0 { + var $t1: &mut option::Option<#0> + var $t2: &option::Option<#0> + var $t3: bool + var $t4: &mut option::Option<#0> + var $t5: u64 + var $t6: &mut option::Option<#0> + var $t7: &mut vector<#0> + var $t8: u64 + var $t9: &mut #0 + 0: $t1 := copy($t0) + 1: $t2 := freeze_ref($t1) + 2: $t3 := option::is_some<#0>($t2) + 3: if ($t3) goto 4 else goto 6 + 4: label L1 + 5: goto 11 + 6: label L0 + 7: $t4 := move($t0) + 8: destroy($t4) + 9: $t5 := 262145 + 10: abort($t5) + 11: label L2 + 12: $t6 := move($t0) + 13: $t7 := borrow_field>.vec($t6) + 14: $t8 := 0 + 15: $t9 := vector::borrow_mut<#0>($t7, $t8) + 16: return $t9 +} + + +[variant baseline] +public fun option::contains<#0>($t0|t: &option::Option<#0>, $t1|e_ref: �): bool { + var $t2: &option::Option<#0> + var $t3: &vector<#0> + var $t4: � + var $t5: bool + 0: $t2 := move($t0) + 1: $t3 := borrow_field>.vec($t2) + 2: $t4 := move($t1) + 3: $t5 := vector::contains<#0>($t3, $t4) + 4: return $t5 +} + + +[variant baseline] +public fun option::swap<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0): #0 { + var $t2|old_value#1#0: #0 + var $t3|vec_ref#1#0: &mut vector<#0> + var $t4: &mut option::Option<#0> + var $t5: &option::Option<#0> + var $t6: bool + var $t7: &mut option::Option<#0> + var $t8: u64 + var $t9: &mut option::Option<#0> + var $t10: &mut vector<#0> + var $t11: &mut vector<#0> + var $t12: #0 + var $t13: &mut vector<#0> + var $t14: #0 + var $t15: #0 + 0: $t4 := copy($t0) + 1: $t5 := freeze_ref($t4) + 2: $t6 := option::is_some<#0>($t5) + 3: if ($t6) goto 4 else goto 6 + 4: label L1 + 5: goto 11 + 6: label L0 + 7: $t7 := move($t0) + 8: destroy($t7) + 9: $t8 := 262145 + 10: abort($t8) + 11: label L2 + 12: $t9 := move($t0) + 13: $t10 := borrow_field>.vec($t9) + 14: $t3 := $t10 + 15: $t11 := copy($t3) + 16: $t12 := vector::pop_back<#0>($t11) + 17: $t2 := $t12 + 18: $t13 := move($t3) + 19: $t14 := move($t1) + 20: vector::push_back<#0>($t13, $t14) + 21: $t15 := move($t2) + 22: return $t15 +} + + +[variant baseline] +public fun option::borrow_with_default<#0>($t0|t: &option::Option<#0>, $t1|default_ref: �): � { + var $t2|tmp#$2: � + var $t3|vec_ref#1#0: &vector<#0> + var $t4: &option::Option<#0> + var $t5: &vector<#0> + var $t6: &vector<#0> + var $t7: bool + var $t8: &vector<#0> + var $t9: � + var $t10: � + var $t11: &vector<#0> + var $t12: u64 + var $t13: � + var $t14: � + 0: $t4 := move($t0) + 1: $t5 := borrow_field>.vec($t4) + 2: $t3 := $t5 + 3: $t6 := copy($t3) + 4: $t7 := vector::is_empty<#0>($t6) + 5: if ($t7) goto 6 else goto 12 + 6: label L1 + 7: $t8 := move($t3) + 8: destroy($t8) + 9: $t9 := move($t1) + 10: $t2 := $t9 + 11: goto 20 + 12: label L0 + 13: $t10 := move($t1) + 14: destroy($t10) + 15: $t11 := move($t3) + 16: $t12 := 0 + 17: $t13 := vector::borrow<#0>($t11, $t12) + 18: $t2 := $t13 + 19: goto 20 + 20: label L2 + 21: $t14 := move($t2) + 22: return $t14 +} + + +[variant baseline] +public fun option::destroy_none<#0>($t0|t: option::Option<#0>) { + var $t1: &option::Option<#0> + var $t2: bool + var $t3: u64 + var $t4: option::Option<#0> + var $t5: vector<#0> + 0: $t1 := borrow_local($t0) + 1: $t2 := option::is_none<#0>($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t3 := 262144 + 7: abort($t3) + 8: label L2 + 9: $t4 := move($t0) + 10: $t5 := unpack option::Option<#0>($t4) + 11: vector::destroy_empty<#0>($t5) + 12: return () +} + + +[variant baseline] +public fun option::destroy_some<#0>($t0|t: option::Option<#0>): #0 { + var $t1|elem#1#0: #0 + var $t2|vec#1#0: vector<#0> + var $t3: &option::Option<#0> + var $t4: bool + var $t5: u64 + var $t6: option::Option<#0> + var $t7: vector<#0> + var $t8: &mut vector<#0> + var $t9: #0 + var $t10: vector<#0> + var $t11: #0 + 0: $t3 := borrow_local($t0) + 1: $t4 := option::is_some<#0>($t3) + 2: if ($t4) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t5 := 262145 + 7: abort($t5) + 8: label L2 + 9: $t6 := move($t0) + 10: $t7 := unpack option::Option<#0>($t6) + 11: $t2 := $t7 + 12: $t8 := borrow_local($t2) + 13: $t9 := vector::pop_back<#0>($t8) + 14: $t1 := $t9 + 15: $t10 := move($t2) + 16: vector::destroy_empty<#0>($t10) + 17: $t11 := move($t1) + 18: return $t11 +} + + +[variant baseline] +public fun option::destroy_with_default<#0>($t0|t: option::Option<#0>, $t1|default: #0): #0 { + var $t2|tmp#$2: #0 + var $t3|vec#1#0: vector<#0> + var $t4: option::Option<#0> + var $t5: vector<#0> + var $t6: &vector<#0> + var $t7: bool + var $t8: #0 + var $t9: &mut vector<#0> + var $t10: #0 + var $t11: #0 + 0: $t4 := move($t0) + 1: $t5 := unpack option::Option<#0>($t4) + 2: $t3 := $t5 + 3: $t6 := borrow_local($t3) + 4: $t7 := vector::is_empty<#0>($t6) + 5: if ($t7) goto 6 else goto 10 + 6: label L1 + 7: $t8 := move($t1) + 8: $t2 := $t8 + 9: goto 15 + 10: label L0 + 11: $t9 := borrow_local($t3) + 12: $t10 := vector::pop_back<#0>($t9) + 13: $t2 := $t10 + 14: goto 15 + 15: label L2 + 16: $t11 := move($t2) + 17: return $t11 +} + + +[variant baseline] +public fun option::extract<#0>($t0|t: &mut option::Option<#0>): #0 { + var $t1: &mut option::Option<#0> + var $t2: &option::Option<#0> + var $t3: bool + var $t4: &mut option::Option<#0> + var $t5: u64 + var $t6: &mut option::Option<#0> + var $t7: &mut vector<#0> + var $t8: #0 + 0: $t1 := copy($t0) + 1: $t2 := freeze_ref($t1) + 2: $t3 := option::is_some<#0>($t2) + 3: if ($t3) goto 4 else goto 6 + 4: label L1 + 5: goto 11 + 6: label L0 + 7: $t4 := move($t0) + 8: destroy($t4) + 9: $t5 := 262145 + 10: abort($t5) + 11: label L2 + 12: $t6 := move($t0) + 13: $t7 := borrow_field>.vec($t6) + 14: $t8 := vector::pop_back<#0>($t7) + 15: return $t8 +} + + +[variant baseline] +public fun option::fill<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0) { + var $t2|vec_ref#1#0: &mut vector<#0> + var $t3: &mut option::Option<#0> + var $t4: &mut vector<#0> + var $t5: &mut vector<#0> + var $t6: &vector<#0> + var $t7: bool + var $t8: &mut vector<#0> + var $t9: u64 + var $t10: &mut vector<#0> + var $t11: #0 + 0: $t3 := move($t0) + 1: $t4 := borrow_field>.vec($t3) + 2: $t2 := $t4 + 3: $t5 := copy($t2) + 4: $t6 := freeze_ref($t5) + 5: $t7 := vector::is_empty<#0>($t6) + 6: if ($t7) goto 7 else goto 9 + 7: label L1 + 8: goto 14 + 9: label L0 + 10: $t8 := move($t2) + 11: destroy($t8) + 12: $t9 := 262144 + 13: abort($t9) + 14: label L2 + 15: $t10 := move($t2) + 16: $t11 := move($t1) + 17: vector::push_back<#0>($t10, $t11) + 18: return () +} + + +[variant baseline] +public fun option::get_with_default<#0>($t0|t: &option::Option<#0>, $t1|default: #0): #0 { + var $t2|tmp#$2: #0 + var $t3|vec_ref#1#0: &vector<#0> + var $t4: &option::Option<#0> + var $t5: &vector<#0> + var $t6: &vector<#0> + var $t7: bool + var $t8: &vector<#0> + var $t9: #0 var $t10: &vector<#0> var $t11: u64 var $t12: � - var $t13: � + var $t13: #0 + var $t14: #0 + 0: $t4 := move($t0) + 1: $t5 := borrow_field>.vec($t4) + 2: $t3 := $t5 + 3: $t6 := copy($t3) + 4: $t7 := vector::is_empty<#0>($t6) + 5: if ($t7) goto 6 else goto 12 + 6: label L1 + 7: $t8 := move($t3) + 8: destroy($t8) + 9: $t9 := move($t1) + 10: $t2 := $t9 + 11: goto 19 + 12: label L0 + 13: $t10 := move($t3) + 14: $t11 := 0 + 15: $t12 := vector::borrow<#0>($t10, $t11) + 16: $t13 := read_ref($t12) + 17: $t2 := $t13 + 18: goto 19 + 19: label L2 + 20: $t14 := move($t2) + 21: return $t14 +} + + +[variant baseline] +public fun option::is_none<#0>($t0|t: &option::Option<#0>): bool { + var $t1: &option::Option<#0> + var $t2: &vector<#0> + var $t3: bool + 0: $t1 := move($t0) + 1: $t2 := borrow_field>.vec($t1) + 2: $t3 := vector::is_empty<#0>($t2) + 3: return $t3 +} + + +[variant baseline] +public fun option::is_some<#0>($t0|t: &option::Option<#0>): bool { + var $t1: &option::Option<#0> + var $t2: &vector<#0> + var $t3: bool + var $t4: bool + 0: $t1 := move($t0) + 1: $t2 := borrow_field>.vec($t1) + 2: $t3 := vector::is_empty<#0>($t2) + 3: $t4 := !($t3) + 4: return $t4 +} + + +[variant baseline] +public fun option::none<#0>(): option::Option<#0> { + var $t0: vector<#0> + var $t1: option::Option<#0> + 0: $t0 := vector::empty<#0>() + 1: $t1 := pack option::Option<#0>($t0) + 2: return $t1 +} + + +[variant baseline] +public fun option::some<#0>($t0|e: #0): option::Option<#0> { + var $t1: #0 + var $t2: vector<#0> + var $t3: option::Option<#0> + 0: $t1 := move($t0) + 1: $t2 := vector::singleton<#0>($t1) + 2: $t3 := pack option::Option<#0>($t2) + 3: return $t3 +} + + +[variant baseline] +public fun option::swap_or_fill<#0>($t0|t: &mut option::Option<#0>, $t1|e: #0): option::Option<#0> { + var $t2|tmp#$2: option::Option<#0> + var $t3|old_value#1#0: option::Option<#0> + var $t4|vec_ref#1#0: &mut vector<#0> + var $t5: &mut option::Option<#0> + var $t6: &mut vector<#0> + var $t7: &mut vector<#0> + var $t8: &vector<#0> + var $t9: bool + var $t10: option::Option<#0> + var $t11: &mut vector<#0> + var $t12: #0 + var $t13: option::Option<#0> + var $t14: option::Option<#0> + var $t15: &mut vector<#0> + var $t16: #0 + var $t17: option::Option<#0> + 0: $t5 := move($t0) + 1: $t6 := borrow_field>.vec($t5) + 2: $t4 := $t6 + 3: $t7 := copy($t4) + 4: $t8 := freeze_ref($t7) + 5: $t9 := vector::is_empty<#0>($t8) + 6: if ($t9) goto 7 else goto 11 + 7: label L1 + 8: $t10 := option::none<#0>() + 9: $t2 := $t10 + 10: goto 17 + 11: label L0 + 12: $t11 := copy($t4) + 13: $t12 := vector::pop_back<#0>($t11) + 14: $t13 := option::some<#0>($t12) + 15: $t2 := $t13 + 16: goto 17 + 17: label L2 + 18: $t14 := move($t2) + 19: $t3 := $t14 + 20: $t15 := move($t4) + 21: $t16 := move($t1) + 22: vector::push_back<#0>($t15, $t16) + 23: $t17 := move($t3) + 24: return $t17 +} + + +[variant baseline] +public fun option::to_vec<#0>($t0|t: option::Option<#0>): vector<#0> { + var $t1: option::Option<#0> + var $t2: vector<#0> + 0: $t1 := move($t0) + 1: $t2 := unpack option::Option<#0>($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::append($t0|string: &mut ascii::String, $t1|other: ascii::String) { + var $t2: &mut ascii::String + var $t3: &mut vector + var $t4: ascii::String + var $t5: vector + 0: $t2 := move($t0) + 1: $t3 := borrow_field.bytes($t2) + 2: $t4 := move($t1) + 3: $t5 := ascii::into_bytes($t4) + 4: vector::append($t3, $t5) + 5: return () +} + + +[variant baseline] +public fun ascii::index_of($t0|string: &ascii::String, $t1|substr: &ascii::String): u64 { + var $t2|tmp#$2: bool + var $t3|i#1#0: u64 + var $t4|j#1#0: u64 + var $t5|m#1#0: u64 + var $t6|n#1#0: u64 + var $t7: u64 + var $t8: &ascii::String + var $t9: u64 + var $t10: &ascii::String + var $t11: u64 + var $t12: u64 + var $t13: u64 var $t14: bool - var $t15: &vector<#0> - var $t16: � - var $t17: bool + var $t15: &ascii::String + var $t16: &ascii::String + var $t17: u64 var $t18: u64 var $t19: u64 var $t20: u64 var $t21: u64 - var $t22: &vector<#0> - var $t23: � - var $t24: bool + var $t22: bool + var $t23: u64 + var $t24: u64 var $t25: u64 - 0: $t4 := 0 - 1: $t2 := $t4 - 2: $t5 := copy($t0) - 3: $t6 := vector::length<#0>($t5) - 4: $t3 := $t6 - 5: goto 6 - 6: label L5 - 7: $t7 := copy($t2) - 8: $t8 := copy($t3) - 9: $t9 := <($t7, $t8) - 10: if ($t9) goto 11 else goto 34 - 11: label L1 - 12: goto 13 - 13: label L2 - 14: $t10 := copy($t0) - 15: $t11 := copy($t2) - 16: $t12 := vector::borrow<#0>($t10, $t11) - 17: $t13 := copy($t1) - 18: $t14 := ==($t12, $t13) - 19: if ($t14) goto 20 else goto 28 - 20: label L4 - 21: $t15 := move($t0) - 22: destroy($t15) - 23: $t16 := move($t1) - 24: destroy($t16) - 25: $t17 := true - 26: $t18 := move($t2) - 27: return ($t17, $t18) - 28: label L3 - 29: $t19 := move($t2) - 30: $t20 := 1 - 31: $t21 := +($t19, $t20) - 32: $t2 := $t21 - 33: goto 6 - 34: label L0 - 35: $t22 := move($t0) - 36: destroy($t22) - 37: $t23 := move($t1) - 38: destroy($t23) - 39: $t24 := false - 40: $t25 := 0 - 41: return ($t24, $t25) + var $t26: bool + var $t27: &ascii::String + var $t28: &vector + var $t29: u64 + var $t30: u64 + var $t31: u64 + var $t32: &u8 + var $t33: u8 + var $t34: &ascii::String + var $t35: &vector + var $t36: u64 + var $t37: &u8 + var $t38: u8 + var $t39: bool + var $t40: bool + var $t41: bool + var $t42: u64 + var $t43: u64 + var $t44: u64 + var $t45: u64 + var $t46: u64 + var $t47: bool + var $t48: &ascii::String + var $t49: &ascii::String + var $t50: u64 + var $t51: u64 + var $t52: u64 + var $t53: u64 + var $t54: &ascii::String + var $t55: &ascii::String + var $t56: u64 + 0: $t7 := 0 + 1: $t3 := $t7 + 2: $t8 := copy($t0) + 3: $t9 := ascii::length($t8) + 4: $t10 := copy($t1) + 5: $t11 := ascii::length($t10) + 6: $t5 := $t11 + 7: $t6 := $t9 + 8: $t12 := copy($t6) + 9: $t13 := copy($t5) + 10: $t14 := <($t12, $t13) + 11: if ($t14) goto 12 else goto 19 + 12: label L1 + 13: $t15 := move($t1) + 14: destroy($t15) + 15: $t16 := move($t0) + 16: destroy($t16) + 17: $t17 := move($t6) + 18: return $t17 + 19: label L0 + 20: $t18 := copy($t3) + 21: $t19 := copy($t6) + 22: $t20 := copy($t5) + 23: $t21 := -($t19, $t20) + 24: $t22 := <=($t18, $t21) + 25: if ($t22) goto 26 else goto 84 + 26: label L3 + 27: $t23 := 0 + 28: $t4 := $t23 + 29: goto 30 + 30: label L10 + 31: $t24 := copy($t4) + 32: $t25 := copy($t5) + 33: $t26 := <($t24, $t25) + 34: if ($t26) goto 35 else goto 53 + 35: label L5 + 36: goto 37 + 37: label L6 + 38: $t27 := copy($t0) + 39: $t28 := borrow_field.bytes($t27) + 40: $t29 := copy($t3) + 41: $t30 := copy($t4) + 42: $t31 := +($t29, $t30) + 43: $t32 := vector::borrow($t28, $t31) + 44: $t33 := read_ref($t32) + 45: $t34 := copy($t1) + 46: $t35 := borrow_field.bytes($t34) + 47: $t36 := copy($t4) + 48: $t37 := vector::borrow($t35, $t36) + 49: $t38 := read_ref($t37) + 50: $t39 := ==($t33, $t38) + 51: $t2 := $t39 + 52: goto 57 + 53: label L4 + 54: $t40 := false + 55: $t2 := $t40 + 56: goto 57 + 57: label L7 + 58: $t41 := move($t2) + 59: if ($t41) goto 60 else goto 66 + 60: label L9 + 61: $t42 := move($t4) + 62: $t43 := 1 + 63: $t44 := +($t42, $t43) + 64: $t4 := $t44 + 65: goto 30 + 66: label L8 + 67: $t45 := move($t4) + 68: $t46 := copy($t5) + 69: $t47 := ==($t45, $t46) + 70: if ($t47) goto 71 else goto 78 + 71: label L12 + 72: $t48 := move($t1) + 73: destroy($t48) + 74: $t49 := move($t0) + 75: destroy($t49) + 76: $t50 := move($t3) + 77: return $t50 + 78: label L11 + 79: $t51 := move($t3) + 80: $t52 := 1 + 81: $t53 := +($t51, $t52) + 82: $t3 := $t53 + 83: goto 19 + 84: label L2 + 85: $t54 := move($t1) + 86: destroy($t54) + 87: $t55 := move($t0) + 88: destroy($t55) + 89: $t56 := move($t6) + 90: return $t56 } [variant baseline] -public fun vector::insert<#0>($t0|v: &mut vector<#0>, $t1|e: #0, $t2|i: u64) { - var $t3|len#1#0: u64 - var $t4: &mut vector<#0> - var $t5: &vector<#0> - var $t6: u64 - var $t7: u64 +public fun ascii::insert($t0|s: &mut ascii::String, $t1|at: u64, $t2|o: ascii::String) { + var $t3|e#1#2: u8 + var $t4|v#1#1: vector + var $t5: u64 + var $t6: &mut ascii::String + var $t7: &ascii::String var $t8: u64 var $t9: bool - var $t10: &mut vector<#0> + var $t10: &mut ascii::String var $t11: u64 - var $t12: &mut vector<#0> - var $t13: #0 - var $t14: u64 - var $t15: u64 + var $t12: ascii::String + var $t13: vector + var $t14: &vector + var $t15: bool var $t16: bool - var $t17: &mut vector<#0> - var $t18: u64 - var $t19: u64 - var $t20: u64 - var $t21: u64 + var $t17: &mut vector + var $t18: u8 + var $t19: &mut ascii::String + var $t20: &mut vector + var $t21: u8 var $t22: u64 - var $t23: &mut vector<#0> - 0: $t4 := copy($t0) - 1: $t5 := freeze_ref($t4) - 2: $t6 := vector::length<#0>($t5) - 3: $t3 := $t6 - 4: $t7 := copy($t2) - 5: $t8 := copy($t3) - 6: $t9 := >($t7, $t8) - 7: if ($t9) goto 8 else goto 13 - 8: label L1 + var $t23: &mut ascii::String + var $t24: vector + 0: $t5 := copy($t1) + 1: $t6 := copy($t0) + 2: $t7 := freeze_ref($t6) + 3: $t8 := ascii::length($t7) + 4: $t9 := <=($t5, $t8) + 5: if ($t9) goto 6 else goto 8 + 6: label L1 + 7: goto 13 + 8: label L0 9: $t10 := move($t0) 10: destroy($t10) - 11: $t11 := 131072 + 11: $t11 := 65537 12: abort($t11) - 13: label L0 - 14: $t12 := copy($t0) - 15: $t13 := move($t1) - 16: vector::push_back<#0>($t12, $t13) + 13: label L2 + 14: $t12 := move($t2) + 15: $t13 := ascii::into_bytes($t12) + 16: $t4 := $t13 17: goto 18 18: label L5 - 19: $t14 := copy($t2) - 20: $t15 := copy($t3) - 21: $t16 := <($t14, $t15) - 22: if ($t16) goto 23 else goto 35 - 23: label L3 - 24: goto 25 - 25: label L4 - 26: $t17 := copy($t0) - 27: $t18 := copy($t2) - 28: $t19 := copy($t3) - 29: vector::swap<#0>($t17, $t18, $t19) - 30: $t20 := move($t2) - 31: $t21 := 1 - 32: $t22 := +($t20, $t21) - 33: $t2 := $t22 - 34: goto 18 - 35: label L2 - 36: $t23 := move($t0) - 37: destroy($t23) + 19: $t14 := borrow_local($t4) + 20: $t15 := vector::is_empty($t14) + 21: $t16 := !($t15) + 22: if ($t16) goto 23 else goto 33 + 23: label L4 + 24: $t17 := borrow_local($t4) + 25: $t18 := vector::pop_back($t17) + 26: $t3 := $t18 + 27: $t19 := copy($t0) + 28: $t20 := borrow_field.bytes($t19) + 29: $t21 := move($t3) + 30: $t22 := copy($t1) + 31: vector::insert($t20, $t21, $t22) + 32: goto 18 + 33: label L3 + 34: $t23 := move($t0) + 35: destroy($t23) + 36: $t24 := move($t4) + 37: vector::destroy_empty($t24) 38: return () } [variant baseline] -public fun vector::is_empty<#0>($t0|v: &vector<#0>): bool { - var $t1: &vector<#0> - var $t2: u64 +public fun ascii::is_empty($t0|string: &ascii::String): bool { + var $t1: &ascii::String + var $t2: &vector + var $t3: bool + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: $t3 := vector::is_empty($t2) + 3: return $t3 +} + + +[variant baseline] +public fun ascii::length($t0|string: &ascii::String): u64 { + var $t1: &ascii::String + var $t2: &vector + var $t3: u64 + 0: $t1 := move($t0) + 1: $t2 := ascii::as_bytes($t1) + 2: $t3 := vector::length($t2) + 3: return $t3 +} + + +[variant baseline] +public fun ascii::all_characters_printable($t0|string: &ascii::String): bool { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: bool + var $t3|i#1#12: u64 + var $t4|i#1#9: u64 + var $t5|stop#1#9: u64 + var $t6|v#1#3: &vector + var $t7: &ascii::String + var $t8: &vector + var $t9: &vector + var $t10: u64 + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: bool + var $t16: u64 + var $t17: &vector + var $t18: u64 + var $t19: &u8 + var $t20: u8 + var $t21: bool + var $t22: bool + var $t23: &vector + var $t24: bool + var $t25: u64 + var $t26: u64 + var $t27: u64 + var $t28: &vector + var $t29: bool + var $t30: bool + 0: $t7 := move($t0) + 1: $t8 := borrow_field.bytes($t7) + 2: $t6 := $t8 + 3: $t9 := copy($t6) + 4: $t10 := vector::length($t9) + 5: $t1 := $t10 + 6: $t11 := 0 + 7: $t4 := $t11 + 8: $t12 := move($t1) + 9: $t5 := $t12 + 10: goto 11 + 11: label L5 + 12: $t13 := copy($t4) + 13: $t14 := copy($t5) + 14: $t15 := <($t13, $t14) + 15: if ($t15) goto 16 else goto 38 + 16: label L1 + 17: $t16 := copy($t4) + 18: $t3 := $t16 + 19: $t17 := copy($t6) + 20: $t18 := move($t3) + 21: $t19 := vector::borrow($t17, $t18) + 22: $t20 := read_ref($t19) + 23: $t21 := ascii::is_printable_char($t20) + 24: $t22 := !($t21) + 25: if ($t22) goto 26 else goto 32 + 26: label L3 + 27: $t23 := move($t6) + 28: destroy($t23) + 29: $t24 := false + 30: $t2 := $t24 + 31: goto 44 + 32: label L2 + 33: $t25 := move($t4) + 34: $t26 := 1 + 35: $t27 := +($t25, $t26) + 36: $t4 := $t27 + 37: goto 11 + 38: label L0 + 39: $t28 := move($t6) + 40: destroy($t28) + 41: $t29 := true + 42: $t2 := $t29 + 43: goto 44 + 44: label L4 + 45: $t30 := move($t2) + 46: return $t30 +} + + +[variant baseline] +public fun ascii::string($t0|bytes: vector): ascii::String { + var $t1|x#1#0: option::Option + var $t2: vector + var $t3: option::Option + var $t4: &option::Option + var $t5: bool + var $t6: u64 + var $t7: option::Option + var $t8: ascii::String + 0: $t2 := move($t0) + 1: $t3 := ascii::try_string($t2) + 2: $t1 := $t3 + 3: $t4 := borrow_local($t1) + 4: $t5 := option::is_some($t4) + 5: if ($t5) goto 6 else goto 8 + 6: label L1 + 7: goto 11 + 8: label L0 + 9: $t6 := 65536 + 10: abort($t6) + 11: label L2 + 12: $t7 := move($t1) + 13: $t8 := option::destroy_some($t7) + 14: return $t8 +} + + +[variant baseline] +public fun ascii::as_bytes($t0|string: &ascii::String): &vector { + var $t1: &ascii::String + var $t2: &vector + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::byte($t0|char: ascii::Char): u8 { + var $t1: ascii::Char + var $t2: u8 + 0: $t1 := move($t0) + 1: $t2 := unpack ascii::Char($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::char($t0|byte: u8): ascii::Char { + var $t1: u8 + var $t2: bool var $t3: u64 + var $t4: u8 + var $t5: ascii::Char + 0: $t1 := copy($t0) + 1: $t2 := ascii::is_valid_char($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t3 := 65536 + 7: abort($t3) + 8: label L2 + 9: $t4 := move($t0) + 10: $t5 := pack ascii::Char($t4) + 11: return $t5 +} + + +[variant baseline] +fun ascii::char_to_lowercase($t0|byte: u8): u8 { + var $t1|tmp#$1: bool + var $t2|tmp#$2: u8 + var $t3: u8 + var $t4: u8 + var $t5: bool + var $t6: u8 + var $t7: u8 + var $t8: bool + var $t9: bool + var $t10: bool + var $t11: u8 + var $t12: u8 + var $t13: u8 + var $t14: u8 + var $t15: u8 + 0: $t3 := copy($t0) + 1: $t4 := 65 + 2: $t5 := >=($t3, $t4) + 3: if ($t5) goto 4 else goto 10 + 4: label L1 + 5: $t6 := copy($t0) + 6: $t7 := 90 + 7: $t8 := <=($t6, $t7) + 8: $t1 := $t8 + 9: goto 14 + 10: label L0 + 11: $t9 := false + 12: $t1 := $t9 + 13: goto 14 + 14: label L2 + 15: $t10 := move($t1) + 16: if ($t10) goto 17 else goto 23 + 17: label L4 + 18: $t11 := move($t0) + 19: $t12 := 32 + 20: $t13 := +($t11, $t12) + 21: $t2 := $t13 + 22: goto 27 + 23: label L3 + 24: $t14 := move($t0) + 25: $t2 := $t14 + 26: goto 27 + 27: label L5 + 28: $t15 := move($t2) + 29: return $t15 +} + + +[variant baseline] +fun ascii::char_to_uppercase($t0|byte: u8): u8 { + var $t1|tmp#$1: bool + var $t2|tmp#$2: u8 + var $t3: u8 + var $t4: u8 + var $t5: bool + var $t6: u8 + var $t7: u8 + var $t8: bool + var $t9: bool + var $t10: bool + var $t11: u8 + var $t12: u8 + var $t13: u8 + var $t14: u8 + var $t15: u8 + 0: $t3 := copy($t0) + 1: $t4 := 97 + 2: $t5 := >=($t3, $t4) + 3: if ($t5) goto 4 else goto 10 + 4: label L1 + 5: $t6 := copy($t0) + 6: $t7 := 122 + 7: $t8 := <=($t6, $t7) + 8: $t1 := $t8 + 9: goto 14 + 10: label L0 + 11: $t9 := false + 12: $t1 := $t9 + 13: goto 14 + 14: label L2 + 15: $t10 := move($t1) + 16: if ($t10) goto 17 else goto 23 + 17: label L4 + 18: $t11 := move($t0) + 19: $t12 := 32 + 20: $t13 := -($t11, $t12) + 21: $t2 := $t13 + 22: goto 27 + 23: label L3 + 24: $t14 := move($t0) + 25: $t2 := $t14 + 26: goto 27 + 27: label L5 + 28: $t15 := move($t2) + 29: return $t15 +} + + +[variant baseline] +public fun ascii::into_bytes($t0|string: ascii::String): vector { + var $t1: ascii::String + var $t2: vector + 0: $t1 := move($t0) + 1: $t2 := unpack ascii::String($t1) + 2: return $t2 +} + + +[variant baseline] +public fun ascii::is_printable_char($t0|byte: u8): bool { + var $t1|tmp#$1: bool + var $t2: u8 + var $t3: u8 var $t4: bool + var $t5: u8 + var $t6: u8 + var $t7: bool + var $t8: bool + var $t9: bool + 0: $t2 := copy($t0) + 1: $t3 := 32 + 2: $t4 := >=($t2, $t3) + 3: if ($t4) goto 4 else goto 10 + 4: label L1 + 5: $t5 := move($t0) + 6: $t6 := 126 + 7: $t7 := <=($t5, $t6) + 8: $t1 := $t7 + 9: goto 14 + 10: label L0 + 11: $t8 := false + 12: $t1 := $t8 + 13: goto 14 + 14: label L2 + 15: $t9 := move($t1) + 16: return $t9 +} + + +[variant baseline] +public fun ascii::is_valid_char($t0|b: u8): bool { + var $t1: u8 + var $t2: u8 + var $t3: bool 0: $t1 := move($t0) - 1: $t2 := vector::length<#0>($t1) - 2: $t3 := 0 - 3: $t4 := ==($t2, $t3) + 1: $t2 := 127 + 2: $t3 := <=($t1, $t2) + 3: return $t3 +} + + +[variant baseline] +public fun ascii::pop_char($t0|string: &mut ascii::String): ascii::Char { + var $t1: &mut ascii::String + var $t2: &mut vector + var $t3: u8 + var $t4: ascii::Char + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: $t3 := vector::pop_back($t2) + 3: $t4 := pack ascii::Char($t3) 4: return $t4 } [variant baseline] -public native fun vector::length<#0>($t0|v: &vector<#0>): u64; - - -[variant baseline] -public native fun vector::pop_back<#0>($t0|v: &mut vector<#0>): #0; +public fun ascii::push_char($t0|string: &mut ascii::String, $t1|char: ascii::Char) { + var $t2: &mut ascii::String + var $t3: &mut vector + var $t4: &ascii::Char + var $t5: &u8 + var $t6: u8 + 0: $t2 := move($t0) + 1: $t3 := borrow_field.bytes($t2) + 2: $t4 := borrow_local($t1) + 3: $t5 := borrow_field.byte($t4) + 4: $t6 := read_ref($t5) + 5: vector::push_back($t3, $t6) + 6: return () +} + + +[variant baseline] +public fun ascii::substring($t0|string: &ascii::String, $t1|i: u64, $t2|j: u64): ascii::String { + var $t3|tmp#$3: bool + var $t4|bytes#1#0: vector + var $t5|i#1#3: u64 + var $t6|i#1#6: u64 + var $t7|stop#1#3: u64 + var $t8: u64 + var $t9: u64 + var $t10: bool + var $t11: u64 + var $t12: &ascii::String + var $t13: u64 + var $t14: bool + var $t15: bool + var $t16: bool + var $t17: &ascii::String + var $t18: u64 + var $t19: vector + var $t20: u64 + var $t21: u64 + var $t22: u64 + var $t23: u64 + var $t24: bool + var $t25: u64 + var $t26: &mut vector + var $t27: &ascii::String + var $t28: &vector + var $t29: u64 + var $t30: &u8 + var $t31: u8 + var $t32: u64 + var $t33: u64 + var $t34: u64 + var $t35: &ascii::String + var $t36: vector + var $t37: ascii::String + 0: $t8 := copy($t1) + 1: $t9 := copy($t2) + 2: $t10 := <=($t8, $t9) + 3: if ($t10) goto 4 else goto 11 + 4: label L1 + 5: $t11 := copy($t2) + 6: $t12 := copy($t0) + 7: $t13 := ascii::length($t12) + 8: $t14 := <=($t11, $t13) + 9: $t3 := $t14 + 10: goto 15 + 11: label L0 + 12: $t15 := false + 13: $t3 := $t15 + 14: goto 15 + 15: label L2 + 16: $t16 := move($t3) + 17: if ($t16) goto 18 else goto 20 + 18: label L4 + 19: goto 25 + 20: label L3 + 21: $t17 := move($t0) + 22: destroy($t17) + 23: $t18 := 65537 + 24: abort($t18) + 25: label L5 + 26: $t19 := [] + 27: $t4 := $t19 + 28: $t20 := move($t1) + 29: $t5 := $t20 + 30: $t21 := move($t2) + 31: $t7 := $t21 + 32: goto 33 + 33: label L8 + 34: $t22 := copy($t5) + 35: $t23 := copy($t7) + 36: $t24 := <($t22, $t23) + 37: if ($t24) goto 38 else goto 53 + 38: label L7 + 39: $t25 := copy($t5) + 40: $t6 := $t25 + 41: $t26 := borrow_local($t4) + 42: $t27 := copy($t0) + 43: $t28 := borrow_field.bytes($t27) + 44: $t29 := move($t6) + 45: $t30 := vector::borrow($t28, $t29) + 46: $t31 := read_ref($t30) + 47: vector::push_back($t26, $t31) + 48: $t32 := move($t5) + 49: $t33 := 1 + 50: $t34 := +($t32, $t33) + 51: $t5 := $t34 + 52: goto 33 + 53: label L6 + 54: $t35 := move($t0) + 55: destroy($t35) + 56: $t36 := move($t4) + 57: $t37 := pack ascii::String($t36) + 58: return $t37 +} + + +[variant baseline] +public fun ascii::to_lowercase($t0|string: &ascii::String): ascii::String { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: u8 + var $t3|tmp#$3: &mut vector + var $t4|e#1#13: &u8 + var $t5|i#1#12: u64 + var $t6|i#1#9: u64 + var $t7|r#1#1: vector + var $t8|stop#1#9: u64 + var $t9|v#1#1: &vector + var $t10|v#1#3: &vector + var $t11: &ascii::String + var $t12: &vector + var $t13: vector + var $t14: &vector + var $t15: &vector + var $t16: u64 + var $t17: u64 + var $t18: u64 + var $t19: u64 + var $t20: u64 + var $t21: bool + var $t22: u64 + var $t23: &vector + var $t24: u64 + var $t25: &u8 + var $t26: &mut vector + var $t27: &u8 + var $t28: u8 + var $t29: u8 + var $t30: &mut vector + var $t31: u8 + var $t32: u64 + var $t33: u64 + var $t34: u64 + var $t35: &vector + var $t36: vector + var $t37: ascii::String + 0: $t11 := move($t0) + 1: $t12 := ascii::as_bytes($t11) + 2: $t9 := $t12 + 3: $t13 := [] + 4: $t7 := $t13 + 5: $t14 := move($t9) + 6: $t10 := $t14 + 7: $t15 := copy($t10) + 8: $t16 := vector::length($t15) + 9: $t1 := $t16 + 10: $t17 := 0 + 11: $t6 := $t17 + 12: $t18 := move($t1) + 13: $t8 := $t18 + 14: goto 15 + 15: label L2 + 16: $t19 := copy($t6) + 17: $t20 := copy($t8) + 18: $t21 := <($t19, $t20) + 19: if ($t21) goto 20 else goto 41 + 20: label L1 + 21: $t22 := copy($t6) + 22: $t5 := $t22 + 23: $t23 := copy($t10) + 24: $t24 := move($t5) + 25: $t25 := vector::borrow($t23, $t24) + 26: $t4 := $t25 + 27: $t26 := borrow_local($t7) + 28: $t3 := $t26 + 29: $t27 := move($t4) + 30: $t28 := read_ref($t27) + 31: $t29 := ascii::char_to_lowercase($t28) + 32: $t2 := $t29 + 33: $t30 := move($t3) + 34: $t31 := move($t2) + 35: vector::push_back($t30, $t31) + 36: $t32 := move($t6) + 37: $t33 := 1 + 38: $t34 := +($t32, $t33) + 39: $t6 := $t34 + 40: goto 15 + 41: label L0 + 42: $t35 := move($t10) + 43: destroy($t35) + 44: $t36 := move($t7) + 45: $t37 := pack ascii::String($t36) + 46: return $t37 +} [variant baseline] -public native fun vector::push_back<#0>($t0|v: &mut vector<#0>, $t1|e: #0); +public fun ascii::to_uppercase($t0|string: &ascii::String): ascii::String { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: u8 + var $t3|tmp#$3: &mut vector + var $t4|e#1#13: &u8 + var $t5|i#1#12: u64 + var $t6|i#1#9: u64 + var $t7|r#1#1: vector + var $t8|stop#1#9: u64 + var $t9|v#1#1: &vector + var $t10|v#1#3: &vector + var $t11: &ascii::String + var $t12: &vector + var $t13: vector + var $t14: &vector + var $t15: &vector + var $t16: u64 + var $t17: u64 + var $t18: u64 + var $t19: u64 + var $t20: u64 + var $t21: bool + var $t22: u64 + var $t23: &vector + var $t24: u64 + var $t25: &u8 + var $t26: &mut vector + var $t27: &u8 + var $t28: u8 + var $t29: u8 + var $t30: &mut vector + var $t31: u8 + var $t32: u64 + var $t33: u64 + var $t34: u64 + var $t35: &vector + var $t36: vector + var $t37: ascii::String + 0: $t11 := move($t0) + 1: $t12 := ascii::as_bytes($t11) + 2: $t9 := $t12 + 3: $t13 := [] + 4: $t7 := $t13 + 5: $t14 := move($t9) + 6: $t10 := $t14 + 7: $t15 := copy($t10) + 8: $t16 := vector::length($t15) + 9: $t1 := $t16 + 10: $t17 := 0 + 11: $t6 := $t17 + 12: $t18 := move($t1) + 13: $t8 := $t18 + 14: goto 15 + 15: label L2 + 16: $t19 := copy($t6) + 17: $t20 := copy($t8) + 18: $t21 := <($t19, $t20) + 19: if ($t21) goto 20 else goto 41 + 20: label L1 + 21: $t22 := copy($t6) + 22: $t5 := $t22 + 23: $t23 := copy($t10) + 24: $t24 := move($t5) + 25: $t25 := vector::borrow($t23, $t24) + 26: $t4 := $t25 + 27: $t26 := borrow_local($t7) + 28: $t3 := $t26 + 29: $t27 := move($t4) + 30: $t28 := read_ref($t27) + 31: $t29 := ascii::char_to_uppercase($t28) + 32: $t2 := $t29 + 33: $t30 := move($t3) + 34: $t31 := move($t2) + 35: vector::push_back($t30, $t31) + 36: $t32 := move($t6) + 37: $t33 := 1 + 38: $t34 := +($t32, $t33) + 39: $t6 := $t34 + 40: goto 15 + 41: label L0 + 42: $t35 := move($t10) + 43: destroy($t35) + 44: $t36 := move($t7) + 45: $t37 := pack ascii::String($t36) + 46: return $t37 +} [variant baseline] -public fun vector::remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { - var $t2|tmp#$2: u64 - var $t3|tmp#$3: &mut vector<#0> - var $t4|len#1#0: u64 - var $t5: &mut vector<#0> - var $t6: &vector<#0> - var $t7: u64 - var $t8: u64 - var $t9: u64 - var $t10: bool - var $t11: &mut vector<#0> +public fun ascii::try_string($t0|bytes: vector): option::Option { + var $t1|$stop#0#6: u64 + var $t2|tmp#$2: bool + var $t3|tmp#$3: option::Option + var $t4|i#1#12: u64 + var $t5|i#1#9: u64 + var $t6|stop#1#9: u64 + var $t7|v#1#3: &vector + var $t8: &vector + var $t9: &vector + var $t10: u64 + var $t11: u64 var $t12: u64 var $t13: u64 var $t14: u64 - var $t15: u64 + var $t15: bool var $t16: u64 - var $t17: u64 - var $t18: bool - var $t19: &mut vector<#0> - var $t20: u64 - var $t21: u64 - var $t22: u64 - var $t23: u64 - var $t24: &mut vector<#0> + var $t17: &vector + var $t18: u64 + var $t19: &u8 + var $t20: u8 + var $t21: bool + var $t22: bool + var $t23: &vector + var $t24: bool var $t25: u64 var $t26: u64 - var $t27: &mut vector<#0> - var $t28: #0 - 0: $t5 := copy($t0) - 1: $t6 := freeze_ref($t5) - 2: $t7 := vector::length<#0>($t6) - 3: $t4 := $t7 - 4: $t8 := copy($t1) - 5: $t9 := copy($t4) - 6: $t10 := >=($t8, $t9) - 7: if ($t10) goto 8 else goto 13 - 8: label L1 - 9: $t11 := move($t0) - 10: destroy($t11) - 11: $t12 := 131072 - 12: abort($t12) - 13: label L0 - 14: $t13 := move($t4) - 15: $t14 := 1 - 16: $t15 := -($t13, $t14) - 17: $t4 := $t15 - 18: goto 19 - 19: label L5 - 20: $t16 := copy($t1) - 21: $t17 := copy($t4) - 22: $t18 := <($t16, $t17) - 23: if ($t18) goto 24 else goto 40 - 24: label L3 - 25: goto 26 - 26: label L4 - 27: $t19 := copy($t0) - 28: $t3 := $t19 - 29: $t20 := copy($t1) - 30: $t2 := $t20 - 31: $t21 := move($t1) - 32: $t22 := 1 - 33: $t23 := +($t21, $t22) - 34: $t1 := $t23 - 35: $t24 := move($t3) - 36: $t25 := move($t2) - 37: $t26 := copy($t1) - 38: vector::swap<#0>($t24, $t25, $t26) - 39: goto 19 - 40: label L2 - 41: $t27 := move($t0) - 42: $t28 := vector::pop_back<#0>($t27) - 43: return $t28 + var $t27: u64 + var $t28: &vector + var $t29: bool + var $t30: bool + var $t31: vector + var $t32: ascii::String + var $t33: option::Option + var $t34: option::Option + var $t35: option::Option + 0: $t8 := borrow_local($t0) + 1: $t7 := $t8 + 2: $t9 := copy($t7) + 3: $t10 := vector::length($t9) + 4: $t1 := $t10 + 5: $t11 := 0 + 6: $t5 := $t11 + 7: $t12 := move($t1) + 8: $t6 := $t12 + 9: goto 10 + 10: label L5 + 11: $t13 := copy($t5) + 12: $t14 := copy($t6) + 13: $t15 := <($t13, $t14) + 14: if ($t15) goto 15 else goto 37 + 15: label L1 + 16: $t16 := copy($t5) + 17: $t4 := $t16 + 18: $t17 := copy($t7) + 19: $t18 := move($t4) + 20: $t19 := vector::borrow($t17, $t18) + 21: $t20 := read_ref($t19) + 22: $t21 := ascii::is_valid_char($t20) + 23: $t22 := !($t21) + 24: if ($t22) goto 25 else goto 31 + 25: label L3 + 26: $t23 := move($t7) + 27: destroy($t23) + 28: $t24 := false + 29: $t2 := $t24 + 30: goto 43 + 31: label L2 + 32: $t25 := move($t5) + 33: $t26 := 1 + 34: $t27 := +($t25, $t26) + 35: $t5 := $t27 + 36: goto 10 + 37: label L0 + 38: $t28 := move($t7) + 39: destroy($t28) + 40: $t29 := true + 41: $t2 := $t29 + 42: goto 43 + 43: label L4 + 44: $t30 := move($t2) + 45: if ($t30) goto 46 else goto 52 + 46: label L7 + 47: $t31 := move($t0) + 48: $t32 := pack ascii::String($t31) + 49: $t33 := option::some($t32) + 50: $t3 := $t33 + 51: goto 56 + 52: label L6 + 53: $t34 := option::none() + 54: $t3 := $t34 + 55: goto 56 + 56: label L8 + 57: $t35 := move($t3) + 58: return $t35 } [variant baseline] -public fun vector::reverse<#0>($t0|v: &mut vector<#0>) { - var $t1|back_index#1#0: u64 - var $t2|front_index#1#0: u64 - var $t3|len#1#0: u64 - var $t4: &mut vector<#0> - var $t5: &vector<#0> +public fun string::append($t0|s: &mut string::String, $t1|r: string::String) { + var $t2: &mut string::String + var $t3: &mut vector + var $t4: &string::String + var $t5: &vector + var $t6: vector + 0: $t2 := move($t0) + 1: $t3 := borrow_field.bytes($t2) + 2: $t4 := borrow_local($t1) + 3: $t5 := borrow_field.bytes($t4) + 4: $t6 := read_ref($t5) + 5: vector::append($t3, $t6) + 6: return () +} + + +[variant baseline] +public fun string::index_of($t0|s: &string::String, $t1|r: &string::String): u64 { + var $t2: &string::String + var $t3: &vector + var $t4: &string::String + var $t5: &vector var $t6: u64 - var $t7: u64 - var $t8: u64 - var $t9: bool - var $t10: &mut vector<#0> - var $t11: u64 + 0: $t2 := move($t0) + 1: $t3 := borrow_field.bytes($t2) + 2: $t4 := move($t1) + 3: $t5 := borrow_field.bytes($t4) + 4: $t6 := string::internal_index_of($t3, $t5) + 5: return $t6 +} + + +[variant baseline] +public fun string::insert($t0|s: &mut string::String, $t1|at: u64, $t2|o: string::String) { + var $t3|tmp#$3: bool + var $t4|bytes#1#0: &vector + var $t5|end#1#0: string::String + var $t6|front#1#0: string::String + var $t7|l#1#0: u64 + var $t8: &mut string::String + var $t9: &vector + var $t10: u64 + var $t11: &vector var $t12: u64 - var $t13: u64 - var $t14: u64 + var $t13: bool + var $t14: &vector var $t15: u64 - var $t16: u64 - var $t17: bool - var $t18: &mut vector<#0> - var $t19: u64 - var $t20: u64 + var $t16: bool + var $t17: &vector + var $t18: bool + var $t19: bool + var $t20: &mut string::String var $t21: u64 - var $t22: u64 - var $t23: u64 + var $t22: &mut string::String + var $t23: &string::String var $t24: u64 - var $t25: u64 - var $t26: u64 - var $t27: &mut vector<#0> - 0: $t4 := copy($t0) - 1: $t5 := freeze_ref($t4) - 2: $t6 := vector::length<#0>($t5) - 3: $t3 := $t6 - 4: $t7 := copy($t3) - 5: $t8 := 0 - 6: $t9 := ==($t7, $t8) - 7: if ($t9) goto 8 else goto 12 + var $t25: &mut string::String + var $t26: &string::String + var $t27: u64 + var $t28: u64 + var $t29: string::String + var $t30: &mut string::String + var $t31: &string::String + var $t32: u64 + var $t33: u64 + var $t34: string::String + var $t35: &mut string::String + var $t36: string::String + var $t37: &mut string::String + var $t38: string::String + var $t39: string::String + var $t40: &mut string::String + 0: $t8 := copy($t0) + 1: $t9 := borrow_field.bytes($t8) + 2: $t4 := $t9 + 3: $t10 := copy($t1) + 4: $t11 := copy($t4) + 5: $t12 := vector::length($t11) + 6: $t13 := <=($t10, $t12) + 7: if ($t13) goto 8 else goto 14 8: label L1 - 9: $t10 := move($t0) - 10: destroy($t10) - 11: return () - 12: label L0 - 13: $t11 := 0 - 14: $t2 := $t11 - 15: $t12 := move($t3) - 16: $t13 := 1 - 17: $t14 := -($t12, $t13) - 18: $t1 := $t14 + 9: $t14 := move($t4) + 10: $t15 := copy($t1) + 11: $t16 := string::internal_is_char_boundary($t14, $t15) + 12: $t3 := $t16 + 13: goto 20 + 14: label L0 + 15: $t17 := move($t4) + 16: destroy($t17) + 17: $t18 := false + 18: $t3 := $t18 19: goto 20 - 20: label L5 - 21: $t15 := copy($t2) - 22: $t16 := copy($t1) - 23: $t17 := <($t15, $t16) - 24: if ($t17) goto 25 else goto 41 + 20: label L2 + 21: $t19 := move($t3) + 22: if ($t19) goto 23 else goto 25 + 23: label L4 + 24: goto 30 25: label L3 - 26: goto 27 - 27: label L4 - 28: $t18 := copy($t0) - 29: $t19 := copy($t2) - 30: $t20 := copy($t1) - 31: vector::swap<#0>($t18, $t19, $t20) - 32: $t21 := move($t2) - 33: $t22 := 1 - 34: $t23 := +($t21, $t22) - 35: $t2 := $t23 - 36: $t24 := move($t1) - 37: $t25 := 1 - 38: $t26 := -($t24, $t25) - 39: $t1 := $t26 - 40: goto 20 - 41: label L2 - 42: $t27 := move($t0) - 43: destroy($t27) - 44: return () + 26: $t20 := move($t0) + 27: destroy($t20) + 28: $t21 := 2 + 29: abort($t21) + 30: label L5 + 31: $t22 := copy($t0) + 32: $t23 := freeze_ref($t22) + 33: $t24 := string::length($t23) + 34: $t7 := $t24 + 35: $t25 := copy($t0) + 36: $t26 := freeze_ref($t25) + 37: $t27 := 0 + 38: $t28 := copy($t1) + 39: $t29 := string::substring($t26, $t27, $t28) + 40: $t6 := $t29 + 41: $t30 := copy($t0) + 42: $t31 := freeze_ref($t30) + 43: $t32 := move($t1) + 44: $t33 := move($t7) + 45: $t34 := string::substring($t31, $t32, $t33) + 46: $t5 := $t34 + 47: $t35 := borrow_local($t6) + 48: $t36 := move($t2) + 49: string::append($t35, $t36) + 50: $t37 := borrow_local($t6) + 51: $t38 := move($t5) + 52: string::append($t37, $t38) + 53: $t39 := move($t6) + 54: $t40 := move($t0) + 55: write_ref($t40, $t39) + 56: return () } [variant baseline] -public fun vector::singleton<#0>($t0|e: #0): vector<#0> { - var $t1|v#1#0: vector<#0> - var $t2: vector<#0> - var $t3: &mut vector<#0> - var $t4: #0 - var $t5: vector<#0> - 0: $t2 := vector::empty<#0>() - 1: $t1 := $t2 - 2: $t3 := borrow_local($t1) - 3: $t4 := move($t0) - 4: vector::push_back<#0>($t3, $t4) - 5: $t5 := move($t1) - 6: return $t5 +public fun string::is_empty($t0|s: &string::String): bool { + var $t1: &string::String + var $t2: &vector + var $t3: bool + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: $t3 := vector::is_empty($t2) + 3: return $t3 } [variant baseline] -public native fun vector::swap<#0>($t0|v: &mut vector<#0>, $t1|i: u64, $t2|j: u64); +public fun string::length($t0|s: &string::String): u64 { + var $t1: &string::String + var $t2: &vector + var $t3: u64 + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: $t3 := vector::length($t2) + 3: return $t3 +} [variant baseline] -public fun vector::swap_remove<#0>($t0|v: &mut vector<#0>, $t1|i: u64): #0 { - var $t2|last_idx#1#0: u64 - var $t3: &mut vector<#0> - var $t4: &vector<#0> - var $t5: bool - var $t6: bool - var $t7: &mut vector<#0> - var $t8: u64 - var $t9: &mut vector<#0> - var $t10: &vector<#0> +public fun string::as_bytes($t0|s: &string::String): &vector { + var $t1: &string::String + var $t2: &vector + 0: $t1 := move($t0) + 1: $t2 := borrow_field.bytes($t1) + 2: return $t2 +} + + +[variant baseline] +public fun string::into_bytes($t0|s: string::String): vector { + var $t1: string::String + var $t2: vector + 0: $t1 := move($t0) + 1: $t2 := unpack string::String($t1) + 2: return $t2 +} + + +[variant baseline] +public fun string::bytes($t0|s: &string::String): &vector { + var $t1: &string::String + var $t2: &vector + 0: $t1 := move($t0) + 1: $t2 := string::as_bytes($t1) + 2: return $t2 +} + + +[variant baseline] +public fun string::substring($t0|s: &string::String, $t1|i: u64, $t2|j: u64): string::String { + var $t3|tmp#$3: bool + var $t4|tmp#$4: bool + var $t5|tmp#$5: bool + var $t6|bytes#1#0: &vector + var $t7|l#1#0: u64 + var $t8: &string::String + var $t9: &vector + var $t10: &vector var $t11: u64 var $t12: u64 var $t13: u64 - var $t14: &mut vector<#0> + var $t14: bool var $t15: u64 var $t16: u64 - var $t17: &mut vector<#0> - var $t18: #0 - 0: $t3 := copy($t0) - 1: $t4 := freeze_ref($t3) - 2: $t5 := vector::is_empty<#0>($t4) - 3: $t6 := !($t5) - 4: if ($t6) goto 5 else goto 7 - 5: label L1 - 6: goto 12 - 7: label L0 - 8: $t7 := move($t0) - 9: destroy($t7) - 10: $t8 := 131072 - 11: abort($t8) - 12: label L2 - 13: $t9 := copy($t0) - 14: $t10 := freeze_ref($t9) - 15: $t11 := vector::length<#0>($t10) - 16: $t12 := 1 - 17: $t13 := -($t11, $t12) - 18: $t2 := $t13 - 19: $t14 := copy($t0) - 20: $t15 := move($t1) - 21: $t16 := move($t2) - 22: vector::swap<#0>($t14, $t15, $t16) - 23: $t17 := move($t0) - 24: $t18 := vector::pop_back<#0>($t17) - 25: return $t18 + var $t17: bool + var $t18: bool + var $t19: bool + var $t20: &vector + var $t21: u64 + var $t22: bool + var $t23: bool + var $t24: bool + var $t25: &vector + var $t26: u64 + var $t27: bool + var $t28: bool + var $t29: bool + var $t30: &vector + var $t31: u64 + var $t32: &vector + var $t33: u64 + var $t34: u64 + var $t35: vector + var $t36: string::String + 0: $t8 := move($t0) + 1: $t9 := borrow_field.bytes($t8) + 2: $t6 := $t9 + 3: $t10 := copy($t6) + 4: $t11 := vector::length($t10) + 5: $t7 := $t11 + 6: $t12 := copy($t2) + 7: $t13 := move($t7) + 8: $t14 := <=($t12, $t13) + 9: if ($t14) goto 10 else goto 16 + 10: label L1 + 11: $t15 := copy($t1) + 12: $t16 := copy($t2) + 13: $t17 := <=($t15, $t16) + 14: $t3 := $t17 + 15: goto 20 + 16: label L0 + 17: $t18 := false + 18: $t3 := $t18 + 19: goto 20 + 20: label L2 + 21: $t19 := move($t3) + 22: if ($t19) goto 23 else goto 29 + 23: label L4 + 24: $t20 := copy($t6) + 25: $t21 := copy($t1) + 26: $t22 := string::internal_is_char_boundary($t20, $t21) + 27: $t4 := $t22 + 28: goto 33 + 29: label L3 + 30: $t23 := false + 31: $t4 := $t23 + 32: goto 33 + 33: label L5 + 34: $t24 := move($t4) + 35: if ($t24) goto 36 else goto 42 + 36: label L7 + 37: $t25 := copy($t6) + 38: $t26 := copy($t2) + 39: $t27 := string::internal_is_char_boundary($t25, $t26) + 40: $t5 := $t27 + 41: goto 46 + 42: label L6 + 43: $t28 := false + 44: $t5 := $t28 + 45: goto 46 + 46: label L8 + 47: $t29 := move($t5) + 48: if ($t29) goto 49 else goto 51 + 49: label L10 + 50: goto 56 + 51: label L9 + 52: $t30 := move($t6) + 53: destroy($t30) + 54: $t31 := 2 + 55: abort($t31) + 56: label L11 + 57: $t32 := move($t6) + 58: $t33 := move($t1) + 59: $t34 := move($t2) + 60: $t35 := string::internal_sub_string($t32, $t33, $t34) + 61: $t36 := pack string::String($t35) + 62: return $t36 } [variant baseline] -fun ReturnRefsIntoVec::return_vec_index_immut($t0|v: &vector): &u64 { - var $t1: &vector - var $t2: u64 - var $t3: &u64 +public fun string::append_utf8($t0|s: &mut string::String, $t1|bytes: vector) { + var $t2: &mut string::String + var $t3: vector + var $t4: string::String + 0: $t2 := move($t0) + 1: $t3 := move($t1) + 2: $t4 := string::utf8($t3) + 3: string::append($t2, $t4) + 4: return () +} + + +[variant baseline] +public fun string::from_ascii($t0|s: ascii::String): string::String { + var $t1: ascii::String + var $t2: vector + var $t3: string::String 0: $t1 := move($t0) - 1: $t2 := 0 - 2: $t3 := vector::borrow($t1, $t2) + 1: $t2 := ascii::into_bytes($t1) + 2: $t3 := pack string::String($t2) 3: return $t3 } [variant baseline] -fun ReturnRefsIntoVec::return_vec_index_mut($t0|v: &mut vector): &mut u64 { - var $t1: &mut vector - var $t2: u64 - var $t3: &mut u64 +native fun string::internal_check_utf8($t0|v: &vector): bool; + + +[variant baseline] +native fun string::internal_index_of($t0|v: &vector, $t1|r: &vector): u64; + + +[variant baseline] +native fun string::internal_is_char_boundary($t0|v: &vector, $t1|i: u64): bool; + + +[variant baseline] +native fun string::internal_sub_string($t0|v: &vector, $t1|i: u64, $t2|j: u64): vector; + + +[variant baseline] +public fun string::sub_string($t0|s: &string::String, $t1|i: u64, $t2|j: u64): string::String { + var $t3: &string::String + var $t4: u64 + var $t5: u64 + var $t6: string::String + 0: $t3 := move($t0) + 1: $t4 := move($t1) + 2: $t5 := move($t2) + 3: $t6 := string::substring($t3, $t4, $t5) + 4: return $t6 +} + + +[variant baseline] +public fun string::to_ascii($t0|s: string::String): ascii::String { + var $t1: string::String + var $t2: vector + var $t3: ascii::String 0: $t1 := move($t0) - 1: $t2 := 0 - 2: $t3 := vector::borrow_mut($t1, $t2) + 1: $t2 := unpack string::String($t1) + 2: $t3 := ascii::string($t2) 3: return $t3 } + + +[variant baseline] +public fun string::try_utf8($t0|bytes: vector): option::Option { + var $t1|tmp#$1: option::Option + var $t2: &vector + var $t3: bool + var $t4: vector + var $t5: string::String + var $t6: option::Option + var $t7: option::Option + var $t8: option::Option + 0: $t2 := borrow_local($t0) + 1: $t3 := string::internal_check_utf8($t2) + 2: if ($t3) goto 3 else goto 9 + 3: label L1 + 4: $t4 := move($t0) + 5: $t5 := pack string::String($t4) + 6: $t6 := option::some($t5) + 7: $t1 := $t6 + 8: goto 13 + 9: label L0 + 10: $t7 := option::none() + 11: $t1 := $t7 + 12: goto 13 + 13: label L2 + 14: $t8 := move($t1) + 15: return $t8 +} + + +[variant baseline] +public fun string::utf8($t0|bytes: vector): string::String { + var $t1: &vector + var $t2: bool + var $t3: u64 + var $t4: vector + var $t5: string::String + 0: $t1 := borrow_local($t0) + 1: $t2 := string::internal_check_utf8($t1) + 2: if ($t2) goto 3 else goto 5 + 3: label L1 + 4: goto 8 + 5: label L0 + 6: $t3 := 1 + 7: abort($t3) + 8: label L2 + 9: $t4 := move($t0) + 10: $t5 := pack string::String($t4) + 11: return $t5 +} diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/return_refs_into_vec.move b/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/return_refs_into_vec.move index f6518eabc4402..7c6ac51c5377a 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/return_refs_into_vec.move +++ b/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/return_refs_into_vec.move @@ -1,7 +1,11 @@ +// dep: ../move-stdlib/sources/macros.move +// dep: ../move-stdlib/sources/u64.move +// dep: ../move-stdlib/sources/option.move +// dep: ../move-stdlib/sources/ascii.move +// dep: ../move-stdlib/sources/string.move // dep: ../move-stdlib/sources/vector.move module 0x1::ReturnRefsIntoVec { - use std::vector; // should not complain fun return_vec_index_immut(v: &vector): &u64 { diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/struct_eq.exp b/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/struct_eq.exp index 5b40517253203..a54b79a43f823 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/struct_eq.exp +++ b/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/struct_eq.exp @@ -39,10 +39,3 @@ public fun StructEq::new(): StructEq::S { 1: $t1 := pack StructEq::S($t0) 2: return $t1 } - -============ Diagnostics ================ -warning: DEPRECATED. will be removed - ┌─ tests/escape_analysis/struct_eq.move:5:5 - │ -5 │ invariant forall s: S: s == S { f: 10 }; - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Specification blocks are deprecated and are no longer used diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/struct_eq.move b/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/struct_eq.move index 33b9ff48059ee..216c7b3074f1a 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/struct_eq.move +++ b/external-crates/move/crates/move-stackless-bytecode/tests/escape_analysis/struct_eq.move @@ -1,8 +1,6 @@ module 0x1::StructEq { - struct S { f: u64 } - - invariant forall s: S: s == S { f: 10 }; + public struct S { f: u64 } public fun new(): S { S { f: 10 } diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/from_move/smoke_test.exp b/external-crates/move/crates/move-stackless-bytecode/tests/from_move/smoke_test.exp index 7c4c9b025d777..5f67678cfb725 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/from_move/smoke_test.exp +++ b/external-crates/move/crates/move-stackless-bytecode/tests/from_move/smoke_test.exp @@ -1,21 +1,13 @@ ============ initial translation from Move ================ [variant baseline] -public fun signer::address_of($t0|s: &signer): address { - var $t1: &signer - var $t2: &address - var $t3: address - 0: $t1 := move($t0) - 1: $t2 := signer::borrow_address($t1) - 2: $t3 := read_ref($t2) - 3: return $t3 +public fun address::length(): u64 { + var $t0: u64 + 0: $t0 := 32 + 1: return $t0 } -[variant baseline] -public native fun signer::borrow_address($t0|s: &signer): &address; - - [variant baseline] fun SmokeTest::arithmetic_ops($t0|a: u64): (u64, u64) { var $t1|c#1#0: u64 diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/from_move/smoke_test.move b/external-crates/move/crates/move-stackless-bytecode/tests/from_move/smoke_test.move index 92acb6be19f0a..a4a211a59f280 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/from_move/smoke_test.move +++ b/external-crates/move/crates/move-stackless-bytecode/tests/from_move/smoke_test.move @@ -1,7 +1,7 @@ // This module contains just some arbitrary code to smoke test the basic functionality of translation from Move // to stackless bytecode. Coverage for byte code translation is achieved by many more tests in the prover. -// dep: ../move-stdlib/sources/signer.move +// dep: ../move-stdlib/sources/address.move module 0x42::SmokeTest { // ----------------- @@ -24,17 +24,17 @@ module 0x42::SmokeTest { (c, a) } - struct A { + public struct A { addr: address, val: u64, } - struct B { + public struct B { val: u64, a: A, } - struct C { + public struct C { val: u64, b: B, } diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/livevar/basic_test.move b/external-crates/move/crates/move-stackless-bytecode/tests/livevar/basic_test.move index 9f0c3f241ef16..5d3373fa01e66 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/livevar/basic_test.move +++ b/external-crates/move/crates/move-stackless-bytecode/tests/livevar/basic_test.move @@ -1,5 +1,5 @@ module 0x42::TestLiveVars { - struct R has copy, drop { + public struct R has copy, drop { x: u64 } @@ -11,14 +11,14 @@ module 0x42::TestLiveVars { fun test2(b: bool) : u64 { let r1 = R {x: 3}; let r2 = R {x: 4}; - let r_ref = &r1; + let mut r_ref = &r1; if (b) { r_ref = &r2; }; test1(r_ref) } - fun test3(n: u64, r_ref: &R) : u64 { + fun test3(mut n: u64, mut r_ref: &R) : u64 { let r1 = R {x: 3}; let r2 = R {x: 4}; while (0 < n) { diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/memory_instr/basic_test.move b/external-crates/move/crates/move-stackless-bytecode/tests/memory_instr/basic_test.move index bacf2b4f7fd61..2f85281358bc4 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/memory_instr/basic_test.move +++ b/external-crates/move/crates/move-stackless-bytecode/tests/memory_instr/basic_test.move @@ -1,10 +1,10 @@ module 0x42::TestPackref { - struct R has copy, drop { + public struct R has copy, drop { x: u64 } fun test1() : R { - let r = R {x: 3}; + let mut r = R {x: 3}; let r_ref = &mut r; let x_ref = &mut r_ref.x; *x_ref = 0; @@ -21,7 +21,7 @@ module 0x42::TestPackref { } fun test4() : R { - let r = R {x: 3}; + let mut r = R {x: 3}; let r_ref = &mut r; test3(r_ref, 0); r @@ -32,7 +32,7 @@ module 0x42::TestPackref { } fun test6() : R { - let r = R {x: 3}; + let mut r = R {x: 3}; let r_ref = &mut r; let x_ref = test5(r_ref); test2(x_ref, 0); @@ -40,19 +40,19 @@ module 0x42::TestPackref { } fun test7(b: bool) { - let r1 = R {x: 3}; - let r2 = R {x: 4}; - let r_ref = &mut r1; + let mut r1 = R {x: 3}; + let mut r2 = R {x: 4}; + let mut r_ref = &mut r1; if (b) { r_ref = &mut r2; }; test3(r_ref, 0) } - fun test8(b: bool, n: u64, r_ref: &mut R) { - let r1 = R {x: 3}; - let r2 = R {x: 4}; - let t_ref = &mut r2; + fun test8(b: bool, mut n: u64, r_ref: &mut R) { + let mut r1 = R {x: 3}; + let mut r2 = R {x: 4}; + let mut t_ref = &mut r2; while (0 < n) { if (n/2 == 0) { t_ref = &mut r1 diff --git a/external-crates/move/crates/move-stackless-bytecode/tests/mut_ref_instrumentation/basic_test.move b/external-crates/move/crates/move-stackless-bytecode/tests/mut_ref_instrumentation/basic_test.move index 5133d72e27845..62ce5ab86038f 100644 --- a/external-crates/move/crates/move-stackless-bytecode/tests/mut_ref_instrumentation/basic_test.move +++ b/external-crates/move/crates/move-stackless-bytecode/tests/mut_ref_instrumentation/basic_test.move @@ -1,11 +1,11 @@ module 0x42::TestEliminateMutRefs { - struct R has copy, drop { + public struct R has copy, drop { x: u64 } fun test1() : R { - let r = R {x: 3}; + let mut r = R {x: 3}; let r_ref = &mut r; let x_ref = &mut r_ref.x; *x_ref = 0; @@ -22,7 +22,7 @@ module 0x42::TestEliminateMutRefs { } fun test4() : R { - let r = R {x: 3}; + let mut r = R {x: 3}; let r_ref = &mut r; test3(r_ref, 0); r @@ -33,7 +33,7 @@ module 0x42::TestEliminateMutRefs { } fun test6() : R { - let r = R {x: 3}; + let mut r = R {x: 3}; let r_ref = &mut r; let x_ref = test5(r_ref); test2(x_ref, 0); @@ -41,19 +41,19 @@ module 0x42::TestEliminateMutRefs { } fun test7(b: bool) { - let r1 = R {x: 3}; - let r2 = R {x: 4}; - let r_ref = &mut r1; + let mut r1 = R {x: 3}; + let mut r2 = R {x: 4}; + let mut r_ref = &mut r1; if (b) { r_ref = &mut r2; }; test3(r_ref, 0) } - fun test8(b: bool, n: u64, r_ref: &mut R) { - let r1 = R {x: 3}; - let r2 = R {x: 4}; - let t_ref = &mut r2; + fun test8(b: bool, mut n: u64, r_ref: &mut R) { + let mut r1 = R {x: 3}; + let mut r2 = R {x: 4}; + let mut t_ref = &mut r2; while (0 < n) { if (n/2 == 0) { t_ref = &mut r1 diff --git a/external-crates/move/crates/move-stdlib-natives/src/lib.rs b/external-crates/move/crates/move-stdlib-natives/src/lib.rs index 0a49187cb9623..4859724018619 100644 --- a/external-crates/move/crates/move-stdlib-natives/src/lib.rs +++ b/external-crates/move/crates/move-stdlib-natives/src/lib.rs @@ -20,6 +20,7 @@ use move_vm_runtime::native_functions::{make_table_from_iter, NativeFunctionTabl #[derive(Debug, Clone)] pub struct GasParameters { pub bcs: bcs::GasParameters, + pub debug: debug::GasParameters, pub hash: hash::GasParameters, pub signer: signer::GasParameters, pub string: string::GasParameters, @@ -40,7 +41,14 @@ impl GasParameters { failure: 0.into(), }, }, - + debug: debug::GasParameters { + print: debug::PrintGasParameters { + base_cost: 0.into(), + }, + print_stack_trace: debug::PrintStackTraceGasParameters { + base_cost: 0.into(), + }, + }, hash: hash::GasParameters { sha2_256: hash::Sha2_256GasParameters { base: 0.into(), @@ -102,49 +110,32 @@ impl GasParameters { }, } } -} - -pub fn all_natives( - move_std_addr: AccountAddress, - gas_params: GasParameters, -) -> NativeFunctionTable { - let mut natives = vec![]; - macro_rules! add_natives { - ($module_name: expr, $natives: expr) => { - natives.extend( - $natives.map(|(func_name, func)| ($module_name.to_string(), func_name, func)), - ); - }; - } - - add_natives!("bcs", bcs::make_all(gas_params.bcs)); - add_natives!("hash", hash::make_all(gas_params.hash)); - add_natives!("signer", signer::make_all(gas_params.signer)); - add_natives!("string", string::make_all(gas_params.string)); - add_natives!("type_name", type_name::make_all(gas_params.type_name)); - add_natives!("vector", vector::make_all(gas_params.vector)); - #[cfg(feature = "testing")] - { - add_natives!("unit_test", unit_test::make_all(gas_params.unit_test)); - } - - make_table_from_iter(move_std_addr, natives) -} - -#[derive(Debug, Clone)] -pub struct NurseryGasParameters { - debug: debug::GasParameters, -} - -impl NurseryGasParameters { - pub fn zeros() -> Self { + pub fn new( + bcs: bcs::GasParameters, + debug: debug::GasParameters, + hash: hash::GasParameters, + string: string::GasParameters, + type_name: type_name::GasParameters, + vector: vector::GasParameters, + ) -> Self { Self { - debug: debug::GasParameters { - print: debug::PrintGasParameters { + bcs, + debug, + hash, + string, + type_name, + vector, + signer: signer::GasParameters { + borrow_address: signer::BorrowAddressGasParameters { base: 0.into() }, + }, + #[cfg(feature = "testing")] + unit_test: unit_test::GasParameters { + create_signers_for_testing: unit_test::CreateSignersForTestingGasParameters { base_cost: 0.into(), + unit_cost: 0.into(), }, - print_stack_trace: debug::PrintStackTraceGasParameters { + poison: unit_test::PoisonGasParameters { base_cost: 0.into(), }, }, @@ -152,10 +143,10 @@ impl NurseryGasParameters { } } -pub fn nursery_natives( - silent: bool, +pub fn all_natives( move_std_addr: AccountAddress, - gas_params: NurseryGasParameters, + gas_params: GasParameters, + debug_is_silent: bool, ) -> NativeFunctionTable { let mut natives = vec![]; @@ -167,10 +158,20 @@ pub fn nursery_natives( }; } + add_natives!("bcs", bcs::make_all(gas_params.bcs)); + add_natives!("hash", hash::make_all(gas_params.hash)); + add_natives!("signer", signer::make_all(gas_params.signer)); + add_natives!("string", string::make_all(gas_params.string)); + add_natives!("type_name", type_name::make_all(gas_params.type_name)); + add_natives!("vector", vector::make_all(gas_params.vector)); add_natives!( "debug", - debug::make_all(silent, gas_params.debug, move_std_addr) + debug::make_all(debug_is_silent, gas_params.debug, move_std_addr) ); + #[cfg(feature = "testing")] + { + add_natives!("unit_test", unit_test::make_all(gas_params.unit_test)); + } make_table_from_iter(move_std_addr, natives) } diff --git a/external-crates/move/crates/move-stdlib/Move.toml b/external-crates/move/crates/move-stdlib/Move.toml index 8ea2f565bf829..43c2eb345a90b 100644 --- a/external-crates/move/crates/move-stdlib/Move.toml +++ b/external-crates/move/crates/move-stdlib/Move.toml @@ -1,5 +1,6 @@ [package] name = "MoveStdlib" +edition = "2024.beta" [addresses] std = "_" diff --git a/external-crates/move/crates/move-stdlib/docs/address.md b/external-crates/move/crates/move-stdlib/docs/address.md new file mode 100644 index 0000000000000..830fada18e886 --- /dev/null +++ b/external-crates/move/crates/move-stdlib/docs/address.md @@ -0,0 +1,44 @@ + + + +# Module `0x1::address` + +Provides a way to get address length since it's a +platform-specific parameter. + + +- [Function `length`](#0x1_address_length) + + +

+ + + + + +## Function `length` + +Should be converted to a native function. +Current implementation only works for Sui. + + +
public fun length(): u64
+
+ + + +
+Implementation + + +
public fun length(): u64 {
+    32
+}
+
+ + + +
+ + +[//]: # ("File containing references which can be used from documentation") diff --git a/external-crates/move/crates/move-stdlib/docs/ascii.md b/external-crates/move/crates/move-stdlib/docs/ascii.md index 5430a4e361c65..92cb66877a162 100644 --- a/external-crates/move/crates/move-stdlib/docs/ascii.md +++ b/external-crates/move/crates/move-stdlib/docs/ascii.md @@ -17,14 +17,24 @@ that characters are valid ASCII, and that strings consist of only valid ASCII ch - [Function `push_char`](#0x1_ascii_push_char) - [Function `pop_char`](#0x1_ascii_pop_char) - [Function `length`](#0x1_ascii_length) +- [Function `append`](#0x1_ascii_append) +- [Function `insert`](#0x1_ascii_insert) +- [Function `substring`](#0x1_ascii_substring) - [Function `as_bytes`](#0x1_ascii_as_bytes) - [Function `into_bytes`](#0x1_ascii_into_bytes) - [Function `byte`](#0x1_ascii_byte) - [Function `is_valid_char`](#0x1_ascii_is_valid_char) - [Function `is_printable_char`](#0x1_ascii_is_printable_char) +- [Function `is_empty`](#0x1_ascii_is_empty) +- [Function `to_uppercase`](#0x1_ascii_to_uppercase) +- [Function `to_lowercase`](#0x1_ascii_to_lowercase) +- [Function `index_of`](#0x1_ascii_index_of) +- [Function `char_to_uppercase`](#0x1_ascii_char_to_uppercase) +- [Function `char_to_lowercase`](#0x1_ascii_char_to_lowercase)
use 0x1::option;
+use 0x1::vector;
 
@@ -51,7 +61,7 @@ defined in this module.
-bytes: vector<u8> +bytes: vector<u8>
@@ -79,7 +89,7 @@ An ASCII character.
-byte: u8 +byte: u8
@@ -94,12 +104,22 @@ An ASCII character. ## Constants - + An invalid ASCII character was encountered when creating an ASCII string. -
const EINVALID_ASCII_CHARACTER: u64 = 65536;
+
const EInvalidASCIICharacter: u64 = 65536;
+
+ + + + + +An invalid index was encountered when creating a substring. + + +
const EInvalidIndex: u64 = 65537;
 
@@ -111,7 +131,7 @@ An invalid ASCII character was encountered when creating an ASCII string. Convert a byte into a Char that is checked to make sure it is valid ASCII. -
public fun char(byte: u8): ascii::Char
+
public fun char(byte: u8): ascii::Char
 
@@ -120,8 +140,8 @@ Convert a byte into a Char< Implementation -
public fun char(byte: u8): Char {
-    assert!(is_valid_char(byte), EINVALID_ASCII_CHARACTER);
+
public fun char(byte: u8): Char {
+    assert!(is_valid_char(byte), EInvalidASCIICharacter);
     Char { byte }
 }
 
@@ -138,7 +158,7 @@ Convert a vector of bytes bytes into an string(bytes: vector<u8>): ascii::String +
public fun string(bytes: vector<u8>): ascii::String
 
@@ -147,13 +167,10 @@ Convert a vector of bytes bytes into an string(bytes: vector<u8>): String { - let x = try_string(bytes); - assert!( - option::is_some(&x), - EINVALID_ASCII_CHARACTER - ); - option::destroy_some(x) +
public fun string(bytes: vector<u8>): String {
+    let x = try_string(bytes);
+    assert!(x.is_some(), EInvalidASCIICharacter);
+    x.destroy_some()
 }
 
@@ -170,7 +187,7 @@ Convert a vector of bytes bytes into an try_string(bytes: vector<u8>): option::Option<ascii::String> +
public fun try_string(bytes: vector<u8>): option::Option<ascii::String>
 
@@ -179,15 +196,10 @@ characters. Otherwise returns None. Implementation -
public fun try_string(bytes: vector<u8>): Option<String> {
-    let len = vector::length(&bytes);
-    let i = 0;
-    while (i < len) {
-        let possible_byte = *vector::borrow(&bytes, i);
-        if (!is_valid_char(possible_byte)) return option::none();
-        i = i + 1;
-    };
-    option::some(String { bytes })
+
public fun try_string(bytes: vector<u8>): Option<String> {
+    let is_valid = bytes.all!(|byte| is_valid_char(*byte));
+    if (is_valid) option::some(String { bytes })
+    else option::none()
 }
 
@@ -213,14 +225,7 @@ Returns false otherwise. Not all all_characters_printable(string: &String): bool { - let len = vector::length(&string.bytes); - let i = 0; - while (i < len) { - let byte = *vector::borrow(&string.bytes, i); - if (!is_printable_char(byte)) return false; - i = i + 1; - }; - true + string.bytes.all!(|byte| is_printable_char(*byte)) }
@@ -232,6 +237,7 @@ Returns false otherwise. Not all Char to the end of the string.
public fun push_char(string: &mut ascii::String, char: ascii::Char)
@@ -244,7 +250,7 @@ Returns false otherwise. Not all push_char(string: &mut String, char: Char) {
-    vector::push_back(&mut string.bytes, char.byte);
+    string.bytes.push_back(char.byte);
 }
 
@@ -256,6 +262,7 @@ Returns false otherwise. Not all Char from the end of the string.
public fun pop_char(string: &mut ascii::String): ascii::Char
@@ -268,7 +275,7 @@ Returns false otherwise. Not all pop_char(string: &mut String): Char {
-    Char { byte: vector::pop_back(&mut string.bytes) }
+    Char { byte: string.bytes.pop_back() }
 }
 
@@ -280,9 +287,60 @@ Returns false otherwise. Not all string in bytes. + + +
public fun length(string: &ascii::String): u64
+
+ + + +
+Implementation + + +
public fun length(string: &String): u64 {
+    string.as_bytes().length()
+}
+
+ + + +
+ + + +## Function `append` + +Append the other string to the end of string. + + +
public fun append(string: &mut ascii::String, other: ascii::String)
+
+ + + +
+Implementation + + +
public fun append(string: &mut String, other: String) {
+    string.bytes.append(other.into_bytes())
+}
+
+ + + +
+ + + +## Function `insert` + +Insert the other string at the at index of string. -
public fun length(string: &ascii::String): u64
+
public fun insert(s: &mut ascii::String, at: u64, o: ascii::String)
 
@@ -291,8 +349,37 @@ Returns false otherwise. Not all length(string: &String): u64 { - vector::length(as_bytes(string)) +
public fun insert(s: &mut String, at: u64, o: String) {
+    assert!(at <= s.length(), EInvalidIndex);
+    o.into_bytes().destroy!(|e| s.bytes.insert(e, at));
+}
+
+ + + + + + + +## Function `substring` + +Copy the slice of the string from i to j into a new String. + + +
public fun substring(string: &ascii::String, i: u64, j: u64): ascii::String
+
+ + + +
+Implementation + + +
public fun substring(string: &String, i: u64, j: u64): String {
+    assert!(i <= j && j <= string.length(), EInvalidIndex);
+    let mut bytes = vector[];
+    i.range_do!(j, |i| bytes.push_back(string.bytes[i]));
+    String { bytes }
 }
 
@@ -307,7 +394,7 @@ Returns false otherwise. Not all string as a reference -
public fun as_bytes(string: &ascii::String): &vector<u8>
+
public fun as_bytes(string: &ascii::String): &vector<u8>
 
@@ -316,8 +403,8 @@ Get the inner bytes of the stringImplementation -
public fun as_bytes(string: &String): &vector<u8> {
-   &string.bytes
+
public fun as_bytes(string: &String): &vector<u8> {
+    &string.bytes
 }
 
@@ -332,7 +419,7 @@ Get the inner bytes of the stringstring
to get its backing bytes -
public fun into_bytes(string: ascii::String): vector<u8>
+
public fun into_bytes(string: ascii::String): vector<u8>
 
@@ -341,9 +428,9 @@ Unpack the string to get its bac Implementation -
public fun into_bytes(string: String): vector<u8> {
-   let String { bytes } = string;
-   bytes
+
public fun into_bytes(string: String): vector<u8> {
+    let String { bytes } = string;
+    bytes
 }
 
@@ -355,10 +442,10 @@ Unpack the string to get its bac ## Function `byte` -Unpack the char into its underlying byte. +Unpack the char into its underlying bytes. -
public fun byte(char: ascii::Char): u8
+
public fun byte(char: ascii::Char): u8
 
@@ -367,9 +454,9 @@ Unpack the char into its underlying byte. Implementation -
public fun byte(char: Char): u8 {
-   let Char { byte } = char;
-   byte
+
public fun byte(char: Char): u8 {
+    let Char { byte } = char;
+    byte
 }
 
@@ -381,10 +468,11 @@ Unpack the char into its underlying byte. ## Function `is_valid_char` -Returns true if b is a valid ASCII character. Returns false otherwise. +Returns true if b is a valid ASCII character. +Returns false otherwise. -
public fun is_valid_char(b: u8): bool
+
public fun is_valid_char(b: u8): bool
 
@@ -393,8 +481,8 @@ Returns true if b is a valid ASCII character. R Implementation -
public fun is_valid_char(b: u8): bool {
-   b <= 0x7F
+
public fun is_valid_char(b: u8): bool {
+    b <= 0x7F
 }
 
@@ -406,10 +494,176 @@ Returns true if b is a valid ASCII character. R ## Function `is_printable_char` -Returns true if byte is an printable ASCII character. Returns false otherwise. +Returns true if byte is an printable ASCII character. +Returns false otherwise. + + +
public fun is_printable_char(byte: u8): bool
+
+ + + +
+Implementation + + +
public fun is_printable_char(byte: u8): bool {
+    byte >= 0x20 && // Disallow metacharacters
+    byte <= 0x7E // Don't allow DEL metacharacter
+}
+
+ + + +
+ + + +## Function `is_empty` + +Returns true if string is empty. + + +
public fun is_empty(string: &ascii::String): bool
+
+ + + +
+Implementation + + +
public fun is_empty(string: &String): bool {
+    string.bytes.is_empty()
+}
+
+ + + +
+ + + +## Function `to_uppercase` + +Convert a string to its uppercase equivalent. + + +
public fun to_uppercase(string: &ascii::String): ascii::String
+
+ + + +
+Implementation + + +
public fun to_uppercase(string: &String): String {
+    let bytes = string.as_bytes().map_ref!(|byte| char_to_uppercase(*byte));
+    String { bytes }
+}
+
+ + + +
+ + + +## Function `to_lowercase` + +Convert a string to its lowercase equivalent. + + +
public fun to_lowercase(string: &ascii::String): ascii::String
+
+ + + +
+Implementation + + +
public fun to_lowercase(string: &String): String {
+    let bytes = string.as_bytes().map_ref!(|byte| char_to_lowercase(*byte));
+    String { bytes }
+}
+
+ + + +
+ + + +## Function `index_of` + +Computes the index of the first occurrence of the substr in the string. +Returns the length of the string if the substr is not found. +Returns 0 if the substr is empty. + + +
public fun index_of(string: &ascii::String, substr: &ascii::String): u64
+
+ + + +
+Implementation + + +
public fun index_of(string: &String, substr: &String): u64 {
+    let mut i = 0;
+    let (n, m) = (string.length(), substr.length());
+    if (n < m) return n;
+    while (i <= n - m) {
+        let mut j = 0;
+        while (j < m && string.bytes[i + j] == substr.bytes[j]) j = j + 1;
+        if (j == m) return i;
+        i = i + 1;
+    };
+    n
+}
+
+ + + +
+ + + +## Function `char_to_uppercase` + +Convert a char to its lowercase equivalent. + + +
fun char_to_uppercase(byte: u8): u8
+
+ + + +
+Implementation + + +
fun char_to_uppercase(byte: u8): u8 {
+    if (byte >= 0x61 && byte <= 0x7A) byte - 0x20
+    else byte
+}
+
+ + + +
+ + + +## Function `char_to_lowercase` + +Convert a char to its lowercase equivalent. -
public fun is_printable_char(byte: u8): bool
+
fun char_to_lowercase(byte: u8): u8
 
@@ -418,9 +672,9 @@ Returns true if byte is an printable ASCII char Implementation -
public fun is_printable_char(byte: u8): bool {
-   byte >= 0x20 && // Disallow metacharacters
-   byte <= 0x7E // Don't allow DEL metacharacter
+
fun char_to_lowercase(byte: u8): u8 {
+    if (byte >= 0x41 && byte <= 0x5A) byte + 0x20
+    else byte
 }
 
diff --git a/external-crates/move/crates/move-stdlib/docs/bcs.md b/external-crates/move/crates/move-stdlib/docs/bcs.md index 1b89050bb7799..3497986f00b38 100644 --- a/external-crates/move/crates/move-stdlib/docs/bcs.md +++ b/external-crates/move/crates/move-stdlib/docs/bcs.md @@ -23,7 +23,7 @@ details on BCS. Return the binary representation of v in BCS (Binary Canonical Serialization) format -
public fun to_bytes<MoveValue>(v: &MoveValue): vector<u8>
+
public fun to_bytes<MoveValue>(v: &MoveValue): vector<u8>
 
@@ -32,7 +32,7 @@ Return the binary representation of v in BCS (Binary Canonical Seri Implementation -
native public fun to_bytes<MoveValue>(v: &MoveValue): vector<u8>;
+
native public fun to_bytes<MoveValue>(v: &MoveValue): vector<u8>;
 
diff --git a/external-crates/move/crates/move-stdlib/docs/bit_vector.md b/external-crates/move/crates/move-stdlib/docs/bit_vector.md index 30e0a7496901b..465aa95a26ff6 100644 --- a/external-crates/move/crates/move-stdlib/docs/bit_vector.md +++ b/external-crates/move/crates/move-stdlib/docs/bit_vector.md @@ -37,7 +37,7 @@
-length: u64 +length: u64
@@ -63,7 +63,7 @@ The provided index is out of bounds -
const EINDEX: u64 = 131072;
+
const EINDEX: u64 = 131072;
 
@@ -73,7 +73,7 @@ The provided index is out of bounds An invalid length of bitvector was given -
const ELENGTH: u64 = 131073;
+
const ELENGTH: u64 = 131073;
 
@@ -83,7 +83,16 @@ An invalid length of bitvector was given The maximum allowed bitvector size -
const MAX_SIZE: u64 = 1024;
+
const MAX_SIZE: u64 = 1024;
+
+ + + + + + + +
const WORD_SIZE: u64 = 1;
 
@@ -94,7 +103,7 @@ The maximum allowed bitvector size -
public fun new(length: u64): bit_vector::BitVector
+
public fun new(length: u64): bit_vector::BitVector
 
@@ -103,13 +112,13 @@ The maximum allowed bitvector size Implementation -
public fun new(length: u64): BitVector {
+
public fun new(length: u64): BitVector {
     assert!(length > 0, ELENGTH);
     assert!(length < MAX_SIZE, ELENGTH);
-    let counter = 0;
-    let bit_field = vector::empty();
+    let mut counter = 0;
+    let mut bit_field = vector::empty();
     while (counter < length) {
-        vector::push_back(&mut bit_field, false);
+        bit_field.push_back(false);
         counter = counter + 1;
     };
 
@@ -131,7 +140,7 @@ The maximum allowed bitvector size
 Set the bit at bit_index in the bitvector regardless of its previous state.
 
 
-
public fun set(bitvector: &mut bit_vector::BitVector, bit_index: u64)
+
public fun set(bitvector: &mut bit_vector::BitVector, bit_index: u64)
 
@@ -140,9 +149,9 @@ Set the bit at bit_index in the bitvector regardless o Implementation -
public fun set(bitvector: &mut BitVector, bit_index: u64) {
-    assert!(bit_index < vector::length(&bitvector.bit_field), EINDEX);
-    let x = vector::borrow_mut(&mut bitvector.bit_field, bit_index);
+
public fun set(bitvector: &mut BitVector, bit_index: u64) {
+    assert!(bit_index < bitvector.bit_field.length(), EINDEX);
+    let x = &mut bitvector.bit_field[bit_index];
     *x = true;
 }
 
@@ -158,7 +167,7 @@ Set the bit at bit_index in the bitvector regardless o Unset the bit at bit_index in the bitvector regardless of its previous state. -
public fun unset(bitvector: &mut bit_vector::BitVector, bit_index: u64)
+
public fun unset(bitvector: &mut bit_vector::BitVector, bit_index: u64)
 
@@ -167,9 +176,9 @@ Unset the bit at bit_index in the bitvector regardless Implementation -
public fun unset(bitvector: &mut BitVector, bit_index: u64) {
-    assert!(bit_index < vector::length(&bitvector.bit_field), EINDEX);
-    let x = vector::borrow_mut(&mut bitvector.bit_field, bit_index);
+
public fun unset(bitvector: &mut BitVector, bit_index: u64) {
+    assert!(bit_index < bitvector.bit_field.length(), EINDEX);
+    let x = &mut bitvector.bit_field[bit_index];
     *x = false;
 }
 
@@ -186,7 +195,7 @@ Shift the bitvector left by amount. If amountpublic fun shift_left(bitvector: &mut bit_vector::BitVector, amount: u64) +
public fun shift_left(bitvector: &mut bit_vector::BitVector, amount: u64)
 
@@ -195,21 +204,21 @@ bitvector's length the bitvector will be zeroed out. Implementation -
public fun shift_left(bitvector: &mut BitVector, amount: u64) {
+
public fun shift_left(bitvector: &mut BitVector, amount: u64) {
     if (amount >= bitvector.length) {
-       let len = vector::length(&bitvector.bit_field);
-       let i = 0;
+       let len = bitvector.bit_field.length();
+       let mut i = 0;
        while (i < len) {
-           let elem = vector::borrow_mut(&mut bitvector.bit_field, i);
+           let elem = &mut bitvector.bit_field[i];
            *elem = false;
            i = i + 1;
        };
     } else {
-        let i = amount;
+        let mut i = amount;
 
         while (i < bitvector.length) {
-            if (is_index_set(bitvector, i)) set(bitvector, i - amount)
-            else unset(bitvector, i - amount);
+            if (bitvector.is_index_set(i)) bitvector.set(i - amount)
+            else bitvector.unset(i - amount);
             i = i + 1;
         };
 
@@ -235,7 +244,7 @@ Return the value of the bit at bit_index in the bitvectorfalse represents a 0
 
 
-
public fun is_index_set(bitvector: &bit_vector::BitVector, bit_index: u64): bool
+
public fun is_index_set(bitvector: &bit_vector::BitVector, bit_index: u64): bool
 
@@ -244,9 +253,9 @@ represents "1" and false represents a 0 Implementation -
public fun is_index_set(bitvector: &BitVector, bit_index: u64): bool {
-    assert!(bit_index < vector::length(&bitvector.bit_field), EINDEX);
-    *vector::borrow(&bitvector.bit_field, bit_index)
+
public fun is_index_set(bitvector: &BitVector, bit_index: u64): bool {
+    assert!(bit_index < bitvector.bit_field.length(), EINDEX);
+    bitvector.bit_field[bit_index]
 }
 
@@ -261,7 +270,7 @@ represents "1" and false represents a 0 Return the length (number of usable bits) of this bitvector -
public fun length(bitvector: &bit_vector::BitVector): u64
+
public fun length(bitvector: &bit_vector::BitVector): u64
 
@@ -270,8 +279,8 @@ Return the length (number of usable bits) of this bitvector Implementation -
public fun length(bitvector: &BitVector): u64 {
-    vector::length(&bitvector.bit_field)
+
public fun length(bitvector: &BitVector): u64 {
+    bitvector.bit_field.length()
 }
 
@@ -288,7 +297,7 @@ including) start_index in the bitvector. If there is n sequence, then 0 is returned. -
public fun longest_set_sequence_starting_at(bitvector: &bit_vector::BitVector, start_index: u64): u64
+
public fun longest_set_sequence_starting_at(bitvector: &bit_vector::BitVector, start_index: u64): u64
 
@@ -297,13 +306,13 @@ sequence, then 0 is returned. Implementation -
public fun longest_set_sequence_starting_at(bitvector: &BitVector, start_index: u64): u64 {
+
public fun longest_set_sequence_starting_at(bitvector: &BitVector, start_index: u64): u64 {
     assert!(start_index < bitvector.length, EINDEX);
-    let index = start_index;
+    let mut index = start_index;
 
     // Find the greatest index in the vector such that all indices less than it are set.
     while (index < bitvector.length) {
-        if (!is_index_set(bitvector, index)) break;
+        if (!bitvector.is_index_set(index)) break;
         index = index + 1;
     };
 
diff --git a/external-crates/move/crates/move-stdlib/nursery/docs/debug.md b/external-crates/move/crates/move-stdlib/docs/debug.md
similarity index 86%
rename from external-crates/move/crates/move-stdlib/nursery/docs/debug.md
rename to external-crates/move/crates/move-stdlib/docs/debug.md
index 33bd18df12f6f..8cd525b3ea6ef 100644
--- a/external-crates/move/crates/move-stdlib/nursery/docs/debug.md
+++ b/external-crates/move/crates/move-stdlib/docs/debug.md
@@ -18,7 +18,6 @@ Module providing debug functionality.
 
 ## Function `print`
 
-Pretty-prints any Move value. For a Move struct, includes its field names, their types and their values.
 
 
 
public fun print<T>(x: &T)
@@ -41,7 +40,6 @@ Pretty-prints any Move value. For a Move struct, includes its field names, their
 
 ## Function `print_stack_trace`
 
-Prints the calling function's stack trace.
 
 
 
public fun print_stack_trace()
@@ -59,3 +57,6 @@ Prints the calling function's stack trace.
 
 
 
+ + +[//]: # ("File containing references which can be used from documentation") diff --git a/external-crates/move/crates/move-stdlib/docs/error.md b/external-crates/move/crates/move-stdlib/docs/error.md deleted file mode 100644 index 0cc2487da9611..0000000000000 --- a/external-crates/move/crates/move-stdlib/docs/error.md +++ /dev/null @@ -1,475 +0,0 @@ - - - -# Module `0x1::error` - -This module defines a set of canonical error codes which are optional to use by applications for the -abort and assert! features. - -Canonical error codes use the 3 lowest bytes of the u64 abort code range (the upper 5 bytes are free for other use). -Of those, the highest byte represents the *error category* and the lower two bytes the *error reason*. -Given an error category 0x1 and a reason 0x3, a canonical abort code looks as 0x10003. - -A module can use a canonical code with a constant declaration of the following form: - -``` -/// An invalid ASCII character was encountered when creating a string. -const EINVALID_CHARACTER: u64 = 0x010003; -``` - -This code is both valid in the worlds with and without canonical errors. It can be used as a plain module local -error reason understand by the existing error map tooling, or as a canonical code. - -The actual canonical categories have been adopted from Google's canonical error codes, which in turn are derived -from Unix error codes [see here](https://cloud.google.com/apis/design/errors#handling_errors). Each code has an -associated HTTP error code which can be used in REST apis. The mapping from error code to http code is not 1:1; -error codes here are a bit richer than HTTP codes. - - -- [Constants](#@Constants_0) -- [Function `canonical`](#0x1_error_canonical) -- [Function `invalid_argument`](#0x1_error_invalid_argument) -- [Function `out_of_range`](#0x1_error_out_of_range) -- [Function `invalid_state`](#0x1_error_invalid_state) -- [Function `unauthenticated`](#0x1_error_unauthenticated) -- [Function `permission_denied`](#0x1_error_permission_denied) -- [Function `not_found`](#0x1_error_not_found) -- [Function `aborted`](#0x1_error_aborted) -- [Function `already_exists`](#0x1_error_already_exists) -- [Function `resource_exhausted`](#0x1_error_resource_exhausted) -- [Function `internal`](#0x1_error_internal) -- [Function `not_implemented`](#0x1_error_not_implemented) -- [Function `unavailable`](#0x1_error_unavailable) - - -
- - - - - -## Constants - - - - -Concurrency conflict, such as read-modify-write conflict (http: 409) - - -
const ABORTED: u64 = 7;
-
- - - - - -The resource that a client tried to create already exists (http: 409) - - -
const ALREADY_EXISTS: u64 = 8;
-
- - - - - -Request cancelled by the client (http: 499) - - -
const CANCELLED: u64 = 10;
-
- - - - - -Internal error (http: 500) - - -
const INTERNAL: u64 = 11;
-
- - - - - -Caller specified an invalid argument (http: 400) - - -
const INVALID_ARGUMENT: u64 = 1;
-
- - - - - -The system is not in a state where the operation can be performed (http: 400) - - -
const INVALID_STATE: u64 = 3;
-
- - - - - -A specified resource is not found (http: 404) - - -
const NOT_FOUND: u64 = 6;
-
- - - - - -Feature not implemented (http: 501) - - -
const NOT_IMPLEMENTED: u64 = 12;
-
- - - - - -An input or result of a computation is out of range (http: 400) - - -
const OUT_OF_RANGE: u64 = 2;
-
- - - - - -client does not have sufficient permission (http: 403) - - -
const PERMISSION_DENIED: u64 = 5;
-
- - - - - -Out of gas or other forms of quota (http: 429) - - -
const RESOURCE_EXHAUSTED: u64 = 9;
-
- - - - - -Request not authenticated due to missing, invalid, or expired auth token (http: 401) - - -
const UNAUTHENTICATED: u64 = 4;
-
- - - - - -The service is currently unavailable. Indicates that a retry could solve the issue (http: 503) - - -
const UNAVAILABLE: u64 = 13;
-
- - - - - -## Function `canonical` - -Construct a canonical error code from a category and a reason. - - -
public fun canonical(category: u64, reason: u64): u64
-
- - - -
-Implementation - - -
public fun canonical(category: u64, reason: u64): u64 {
-  (category << 16) + reason
-}
-
- - - -
- - - -## Function `invalid_argument` - -Functions to construct a canonical error code of the given category. - - -
public fun invalid_argument(r: u64): u64
-
- - - -
-Implementation - - -
public fun invalid_argument(r: u64): u64 {  canonical(INVALID_ARGUMENT, r) }
-
- - - -
- - - -## Function `out_of_range` - - - -
public fun out_of_range(r: u64): u64
-
- - - -
-Implementation - - -
public fun out_of_range(r: u64): u64 {  canonical(OUT_OF_RANGE, r) }
-
- - - -
- - - -## Function `invalid_state` - - - -
public fun invalid_state(r: u64): u64
-
- - - -
-Implementation - - -
public fun invalid_state(r: u64): u64 {  canonical(INVALID_STATE, r) }
-
- - - -
- - - -## Function `unauthenticated` - - - -
public fun unauthenticated(r: u64): u64
-
- - - -
-Implementation - - -
public fun unauthenticated(r: u64): u64 { canonical(UNAUTHENTICATED, r) }
-
- - - -
- - - -## Function `permission_denied` - - - -
public fun permission_denied(r: u64): u64
-
- - - -
-Implementation - - -
public fun permission_denied(r: u64): u64 { canonical(PERMISSION_DENIED, r) }
-
- - - -
- - - -## Function `not_found` - - - -
public fun not_found(r: u64): u64
-
- - - -
-Implementation - - -
public fun not_found(r: u64): u64 { canonical(NOT_FOUND, r) }
-
- - - -
- - - -## Function `aborted` - - - -
public fun aborted(r: u64): u64
-
- - - -
-Implementation - - -
public fun aborted(r: u64): u64 { canonical(ABORTED, r) }
-
- - - -
- - - -## Function `already_exists` - - - -
public fun already_exists(r: u64): u64
-
- - - -
-Implementation - - -
public fun already_exists(r: u64): u64 { canonical(ALREADY_EXISTS, r) }
-
- - - -
- - - -## Function `resource_exhausted` - - - -
public fun resource_exhausted(r: u64): u64
-
- - - -
-Implementation - - -
public fun resource_exhausted(r: u64): u64 {  canonical(RESOURCE_EXHAUSTED, r) }
-
- - - -
- - - -## Function `internal` - - - -
public fun internal(r: u64): u64
-
- - - -
-Implementation - - -
public fun internal(r: u64): u64 {  canonical(INTERNAL, r) }
-
- - - -
- - - -## Function `not_implemented` - - - -
public fun not_implemented(r: u64): u64
-
- - - -
-Implementation - - -
public fun not_implemented(r: u64): u64 {  canonical(NOT_IMPLEMENTED, r) }
-
- - - -
- - - -## Function `unavailable` - - - -
public fun unavailable(r: u64): u64
-
- - - -
-Implementation - - -
public fun unavailable(r: u64): u64 { canonical(UNAVAILABLE, r) }
-
- - - -
- - -[//]: # ("File containing references which can be used from documentation") diff --git a/external-crates/move/crates/move-stdlib/docs/fixed_point32.md b/external-crates/move/crates/move-stdlib/docs/fixed_point32.md index 8644f861dd01d..95abd66963732 100644 --- a/external-crates/move/crates/move-stdlib/docs/fixed_point32.md +++ b/external-crates/move/crates/move-stdlib/docs/fixed_point32.md @@ -15,12 +15,6 @@ a 32-bit fractional part. - [Function `create_from_raw_value`](#0x1_fixed_point32_create_from_raw_value) - [Function `get_raw_value`](#0x1_fixed_point32_get_raw_value) - [Function `is_zero`](#0x1_fixed_point32_is_zero) -- [Function `min`](#0x1_fixed_point32_min) -- [Function `max`](#0x1_fixed_point32_max) -- [Function `create_from_u64`](#0x1_fixed_point32_create_from_u64) -- [Function `floor`](#0x1_fixed_point32_floor) -- [Function `ceil`](#0x1_fixed_point32_ceil) -- [Function `round`](#0x1_fixed_point32_round)
@@ -53,7 +47,7 @@ decimal.
-value: u64 +value: u64
@@ -73,17 +67,17 @@ decimal. The denominator provided was zero -
const EDENOMINATOR: u64 = 65537;
+
const EDENOMINATOR: u64 = 65537;
 
-The quotient value would be too large to be held in a u64 +The quotient value would be too large to be held in a u64 -
const EDIVISION: u64 = 131074;
+
const EDIVISION: u64 = 131074;
 
@@ -93,17 +87,17 @@ The quotient value would be too large to be held in a u64 A division by zero was encountered -
const EDIVISION_BY_ZERO: u64 = 65540;
+
const EDIVISION_BY_ZERO: u64 = 65540;
 
-The multiplied value would be too large to be held in a u64 +The multiplied value would be too large to be held in a u64 -
const EMULTIPLICATION: u64 = 131075;
+
const EMULTIPLICATION: u64 = 131075;
 
@@ -113,7 +107,7 @@ The multiplied value would be too large to be held in a u64 The computed ratio when converting to a FixedPoint32 would be unrepresentable -
const ERATIO_OUT_OF_RANGE: u64 = 131077;
+
const ERATIO_OUT_OF_RANGE: u64 = 131077;
 
@@ -123,7 +117,7 @@ The computed ratio when converting to a MAX_U64: u128 = 18446744073709551615; +
const MAX_U64: u128 = 18446744073709551615;
 
@@ -137,7 +131,7 @@ fractional part of the product. This will abort if the product overflows. -
public fun multiply_u64(val: u64, multiplier: fixed_point32::FixedPoint32): u64
+
public fun multiply_u64(val: u64, multiplier: fixed_point32::FixedPoint32): u64
 
@@ -146,17 +140,17 @@ overflows. Implementation -
public fun multiply_u64(val: u64, multiplier: FixedPoint32): u64 {
+
public fun multiply_u64(val: u64, multiplier: FixedPoint32): u64 {
     // The product of two 64 bit values has 128 bits, so perform the
-    // multiplication with u128 types and keep the full 128 bit product
+    // multiplication with u128 types and keep the full 128 bit product
     // to avoid losing accuracy.
-    let unscaled_product = (val as u128) * (multiplier.value as u128);
+    let unscaled_product = val as u128 * (multiplier.value as u128);
     // The unscaled product has 32 fractional bits (from the multiplier)
     // so rescale it by shifting away the low bits.
     let product = unscaled_product >> 32;
     // Check whether the value is too large.
     assert!(product <= MAX_U64, EMULTIPLICATION);
-    (product as u64)
+    product as u64
 }
 
@@ -173,7 +167,7 @@ fractional part of the quotient. This will abort if the divisor is zero or if the quotient overflows. -
public fun divide_u64(val: u64, divisor: fixed_point32::FixedPoint32): u64
+
public fun divide_u64(val: u64, divisor: fixed_point32::FixedPoint32): u64
 
@@ -182,18 +176,18 @@ is zero or if the quotient overflows. Implementation -
public fun divide_u64(val: u64, divisor: FixedPoint32): u64 {
+
public fun divide_u64(val: u64, divisor: FixedPoint32): u64 {
     // Check for division by zero.
     assert!(divisor.value != 0, EDIVISION_BY_ZERO);
     // First convert to 128 bits and then shift left to
     // add 32 fractional zero bits to the dividend.
-    let scaled_value = (val as u128) << 32;
-    let quotient = scaled_value / (divisor.value as u128);
+    let scaled_value = val as u128 << 32;
+    let quotient = scaled_value / (divisor.value as u128);
     // Check whether the value is too large.
     assert!(quotient <= MAX_U64, EDIVISION);
     // the value may be too large, which will cause the cast to fail
-    // with an arithmetic error.
-    (quotient as u64)
+    // with an arithmetic error.
+    quotient as u64
 }
 
@@ -217,7 +211,7 @@ very small imprecision in the binary representation could change the rounding, e.g., 0.0125 will round down to 0.012 instead of up to 0.013. -
public fun create_from_rational(numerator: u64, denominator: u64): fixed_point32::FixedPoint32
+
public fun create_from_rational(numerator: u64, denominator: u64): fixed_point32::FixedPoint32
 
@@ -226,20 +220,20 @@ rounding, e.g., 0.0125 will round down to 0.012 instead of up to 0.013. Implementation -
public fun create_from_rational(numerator: u64, denominator: u64): FixedPoint32 {
+
public fun create_from_rational(numerator: u64, denominator: u64): FixedPoint32 {
     // If the denominator is zero, this will abort.
     // Scale the numerator to have 64 fractional bits and the denominator
     // to have 32 fractional bits, so that the quotient will have 32
     // fractional bits.
-    let scaled_numerator = (numerator as u128) << 64;
-    let scaled_denominator = (denominator as u128) << 32;
+    let scaled_numerator = numerator as u128 << 64;
+    let scaled_denominator = denominator as u128 << 32;
     assert!(scaled_denominator != 0, EDENOMINATOR);
     let quotient = scaled_numerator / scaled_denominator;
     assert!(quotient != 0 || numerator == 0, ERATIO_OUT_OF_RANGE);
     // Return the quotient as a fixed-point number. We first need to check whether the cast
     // can succeed.
     assert!(quotient <= MAX_U64, ERATIO_OUT_OF_RANGE);
-    FixedPoint32 { value: (quotient as u64) }
+    FixedPoint32 { value: quotient as u64 }
 }
 
@@ -254,7 +248,7 @@ rounding, e.g., 0.0125 will round down to 0.012 instead of up to 0.013. Create a fixedpoint value from a raw value. -
public fun create_from_raw_value(value: u64): fixed_point32::FixedPoint32
+
public fun create_from_raw_value(value: u64): fixed_point32::FixedPoint32
 
@@ -263,7 +257,7 @@ Create a fixedpoint value from a raw value. Implementation -
public fun create_from_raw_value(value: u64): FixedPoint32 {
+
public fun create_from_raw_value(value: u64): FixedPoint32 {
     FixedPoint32 { value }
 }
 
@@ -281,7 +275,7 @@ adding or subtracting FixedPoint32 values, can be done using the raw values directly. -
public fun get_raw_value(num: fixed_point32::FixedPoint32): u64
+
public fun get_raw_value(num: fixed_point32::FixedPoint32): u64
 
@@ -290,7 +284,7 @@ values directly. Implementation -
public fun get_raw_value(num: FixedPoint32): u64 {
+
public fun get_raw_value(num: FixedPoint32): u64 {
     num.value
 }
 
@@ -322,177 +316,6 @@ Returns true if the ratio is zero. - - - - -## Function `min` - -Returns the smaller of the two FixedPoint32 numbers. - - -
public fun min(num1: fixed_point32::FixedPoint32, num2: fixed_point32::FixedPoint32): fixed_point32::FixedPoint32
-
- - - -
-Implementation - - -
public fun min(num1: FixedPoint32, num2: FixedPoint32): FixedPoint32 {
-    if (num1.value < num2.value) {
-        num1
-    } else {
-        num2
-    }
-}
-
- - - -
- - - -## Function `max` - -Returns the larger of the two FixedPoint32 numbers. - - -
public fun max(num1: fixed_point32::FixedPoint32, num2: fixed_point32::FixedPoint32): fixed_point32::FixedPoint32
-
- - - -
-Implementation - - -
public fun max(num1: FixedPoint32, num2: FixedPoint32): FixedPoint32 {
-    if (num1.value > num2.value) {
-        num1
-    } else {
-        num2
-    }
-}
-
- - - -
- - - -## Function `create_from_u64` - -Create a fixedpoint value from a u64 value. - - -
public fun create_from_u64(val: u64): fixed_point32::FixedPoint32
-
- - - -
-Implementation - - -
public fun create_from_u64(val: u64): FixedPoint32 {
-    let value = (val as u128) << 32;
-    assert!(value <= MAX_U64, ERATIO_OUT_OF_RANGE);
-    FixedPoint32{value: (value as u64)}
-}
-
- - - -
- - - -## Function `floor` - -Returns the largest integer less than or equal to a given number. - - -
public fun floor(num: fixed_point32::FixedPoint32): u64
-
- - - -
-Implementation - - -
public fun floor(num: FixedPoint32): u64 {
-    num.value >> 32
-}
-
- - - -
- - - -## Function `ceil` - -Rounds up the given FixedPoint32 to the next largest integer. - - -
public fun ceil(num: fixed_point32::FixedPoint32): u64
-
- - - -
-Implementation - - -
public fun ceil(num: FixedPoint32): u64 {
-    let floored_num = floor(num) << 32;
-    if (num.value == floored_num) {
-        return floored_num >> 32
-    };
-    let val = ((floored_num as u128) + (1 << 32));
-    (val >> 32 as u64)
-}
-
- - - -
- - - -## Function `round` - -Returns the value of a FixedPoint32 to the nearest integer. - - -
public fun round(num: fixed_point32::FixedPoint32): u64
-
- - - -
-Implementation - - -
public fun round(num: FixedPoint32): u64 {
-    let floored_num = floor(num) << 32;
-    let boundary = floored_num + ((1 << 32) / 2);
-    if (num.value < boundary) {
-        floored_num >> 32
-    } else {
-        ceil(num)
-    }
-}
-
- - -
diff --git a/external-crates/move/crates/move-stdlib/docs/hash.md b/external-crates/move/crates/move-stdlib/docs/hash.md index c054f33cb569a..e3b110a627729 100644 --- a/external-crates/move/crates/move-stdlib/docs/hash.md +++ b/external-crates/move/crates/move-stdlib/docs/hash.md @@ -23,7 +23,7 @@ as in the Move prover's prelude. -
public fun sha2_256(data: vector<u8>): vector<u8>
+
public fun sha2_256(data: vector<u8>): vector<u8>
 
@@ -32,7 +32,7 @@ as in the Move prover's prelude. Implementation -
native public fun sha2_256(data: vector<u8>): vector<u8>;
+
native public fun sha2_256(data: vector<u8>): vector<u8>;
 
@@ -45,7 +45,7 @@ as in the Move prover's prelude. -
public fun sha3_256(data: vector<u8>): vector<u8>
+
public fun sha3_256(data: vector<u8>): vector<u8>
 
@@ -54,7 +54,7 @@ as in the Move prover's prelude. Implementation -
native public fun sha3_256(data: vector<u8>): vector<u8>;
+
native public fun sha3_256(data: vector<u8>): vector<u8>;
 
diff --git a/external-crates/move/crates/move-stdlib/docs/macros.md b/external-crates/move/crates/move-stdlib/docs/macros.md new file mode 100644 index 0000000000000..4fd3e7cd33d95 --- /dev/null +++ b/external-crates/move/crates/move-stdlib/docs/macros.md @@ -0,0 +1,14 @@ + + + +# Module `0x1::macros` + +This module holds shared implementation of macros used in std + + + + +
+ + +[//]: # ("File containing references which can be used from documentation") diff --git a/external-crates/move/crates/move-stdlib/docs/option.md b/external-crates/move/crates/move-stdlib/docs/option.md index 04baf52a56f91..070a8f9672a07 100644 --- a/external-crates/move/crates/move-stdlib/docs/option.md +++ b/external-crates/move/crates/move-stdlib/docs/option.md @@ -72,7 +72,7 @@ The Option is in an inval The Option is Some while it should be None. -
const EOPTION_IS_SET: u64 = 262144;
+
const EOPTION_IS_SET: u64 = 262144;
 
@@ -83,7 +83,7 @@ The Option is in an inval The Option is None while it should be Some. -
const EOPTION_NOT_SET: u64 = 262145;
+
const EOPTION_NOT_SET: u64 = 262145;
 
@@ -155,7 +155,7 @@ Return true if t does not hold a value
public fun is_none<Element>(t: &Option<Element>): bool {
-    vector::is_empty(&t.vec)
+    t.vec.is_empty()
 }
 
@@ -180,7 +180,7 @@ Return true if t holds a value
public fun is_some<Element>(t: &Option<Element>): bool {
-    !vector::is_empty(&t.vec)
+    !t.vec.is_empty()
 }
 
@@ -206,7 +206,7 @@ Always returns false if t does not hold a value
public fun contains<Element>(t: &Option<Element>, e_ref: &Element): bool {
-    vector::contains(&t.vec, e_ref)
+    t.vec.contains(e_ref)
 }
 
@@ -232,8 +232,8 @@ Aborts if t does not hold a value
public fun borrow<Element>(t: &Option<Element>): &Element {
-    assert!(is_some(t), EOPTION_NOT_SET);
-    vector::borrow(&t.vec, 0)
+    assert!(t.is_some(), EOPTION_NOT_SET);
+    &t.vec[0]
 }
 
@@ -260,8 +260,8 @@ Return default_ref if t does not hold a value
public fun borrow_with_default<Element>(t: &Option<Element>, default_ref: &Element): &Element {
     let vec_ref = &t.vec;
-    if (vector::is_empty(vec_ref)) default_ref
-    else vector::borrow(vec_ref, 0)
+    if (vec_ref.is_empty()) default_ref
+    else &vec_ref[0]
 }
 
@@ -291,8 +291,8 @@ Return default if t does not hold a value default: Element, ): Element { let vec_ref = &t.vec; - if (vector::is_empty(vec_ref)) default - else *vector::borrow(vec_ref, 0) + if (vec_ref.is_empty()) default + else vec_ref[0] }
@@ -319,7 +319,7 @@ Aborts if t already holds a value
public fun fill<Element>(t: &mut Option<Element>, e: Element) {
     let vec_ref = &mut t.vec;
-    if (vector::is_empty(vec_ref)) vector::push_back(vec_ref, e)
+    if (vec_ref.is_empty()) vec_ref.push_back(e)
     else abort EOPTION_IS_SET
 }
 
@@ -346,8 +346,8 @@ Aborts if t does not hold a value
public fun extract<Element>(t: &mut Option<Element>): Element {
-    assert!(is_some(t), EOPTION_NOT_SET);
-    vector::pop_back(&mut t.vec)
+    assert!(t.is_some(), EOPTION_NOT_SET);
+    t.vec.pop_back()
 }
 
@@ -373,8 +373,8 @@ Aborts if t does not hold a value
public fun borrow_mut<Element>(t: &mut Option<Element>): &mut Element {
-    assert!(is_some(t), EOPTION_NOT_SET);
-    vector::borrow_mut(&mut t.vec, 0)
+    assert!(t.is_some(), EOPTION_NOT_SET);
+    &mut t.vec[0]
 }
 
@@ -400,10 +400,10 @@ Aborts if t does not hold a value
public fun swap<Element>(t: &mut Option<Element>, e: Element): Element {
-    assert!(is_some(t), EOPTION_NOT_SET);
+    assert!(t.is_some(), EOPTION_NOT_SET);
     let vec_ref = &mut t.vec;
-    let old_value = vector::pop_back(vec_ref);
-    vector::push_back(vec_ref, e);
+    let old_value = vec_ref.pop_back();
+    vec_ref.push_back(e);
     old_value
 }
 
@@ -432,9 +432,9 @@ Different from swap(), swap_or_fill() allows for t not holding a va
public fun swap_or_fill<Element>(t: &mut Option<Element>, e: Element): Option<Element> {
     let vec_ref = &mut t.vec;
-    let old_value = if (vector::is_empty(vec_ref)) none()
-        else some(vector::pop_back(vec_ref));
-    vector::push_back(vec_ref, e);
+    let old_value = if (vec_ref.is_empty()) none()
+        else some(vec_ref.pop_back());
+    vec_ref.push_back(e);
     old_value
 }
 
@@ -460,9 +460,9 @@ Destroys t. If t holds a value, return it. Returns public fun destroy_with_default<Element: drop>(t: Option<Element>, default: Element): Element { - let Option { vec } = t; - if (vector::is_empty(&vec)) default - else vector::pop_back(&mut vec) + let Option { mut vec } = t; + if (vec.is_empty()) default + else vec.pop_back() }
@@ -488,10 +488,10 @@ Aborts if t does not hold a value
public fun destroy_some<Element>(t: Option<Element>): Element {
-    assert!(is_some(&t), EOPTION_NOT_SET);
-    let Option { vec } = t;
-    let elem = vector::pop_back(&mut vec);
-    vector::destroy_empty(vec);
+    assert!(t.is_some(), EOPTION_NOT_SET);
+    let Option { mut vec } = t;
+    let elem = vec.pop_back();
+    vec.destroy_empty();
     elem
 }
 
@@ -518,9 +518,9 @@ Aborts if t holds a value
public fun destroy_none<Element>(t: Option<Element>) {
-    assert!(is_none(&t), EOPTION_IS_SET);
+    assert!(t.is_none(), EOPTION_IS_SET);
     let Option { vec } = t;
-    vector::destroy_empty(vec)
+    vec.destroy_empty()
 }
 
diff --git a/external-crates/move/crates/move-stdlib/docs/overview.md b/external-crates/move/crates/move-stdlib/docs/overview.md index e8aff3f0414fa..e3e118394a99c 100644 --- a/external-crates/move/crates/move-stdlib/docs/overview.md +++ b/external-crates/move/crates/move-stdlib/docs/overview.md @@ -12,16 +12,23 @@ This is the root document for the Move stdlib module documentation. The Move std ## Index +- [`0x1::address`](address.md#0x1_address) - [`0x1::ascii`](ascii.md#0x1_ascii) - [`0x1::bcs`](bcs.md#0x1_bcs) - [`0x1::bit_vector`](bit_vector.md#0x1_bit_vector) -- [`0x1::error`](error.md#0x1_error) +- [`0x1::debug`](debug.md#0x1_debug) - [`0x1::fixed_point32`](fixed_point32.md#0x1_fixed_point32) - [`0x1::hash`](hash.md#0x1_hash) +- [`0x1::macros`](macros.md#0x1_macros) - [`0x1::option`](option.md#0x1_option) -- [`0x1::signer`](signer.md#0x1_signer) - [`0x1::string`](string.md#0x1_string) - [`0x1::type_name`](type_name.md#0x1_type_name) +- [`0x1::u128`](u128.md#0x1_u128) +- [`0x1::u16`](u16.md#0x1_u16) +- [`0x1::u256`](u256.md#0x1_u256) +- [`0x1::u32`](u32.md#0x1_u32) +- [`0x1::u64`](u64.md#0x1_u64) +- [`0x1::u8`](u8.md#0x1_u8) - [`0x1::vector`](vector.md#0x1_vector) diff --git a/external-crates/move/crates/move-stdlib/docs/signer.md b/external-crates/move/crates/move-stdlib/docs/signer.md deleted file mode 100644 index 99b418f9e1abc..0000000000000 --- a/external-crates/move/crates/move-stdlib/docs/signer.md +++ /dev/null @@ -1,63 +0,0 @@ - - - -# Module `0x1::signer` - - - -- [Function `borrow_address`](#0x1_signer_borrow_address) -- [Function `address_of`](#0x1_signer_address_of) - - -
- - - - - -## Function `borrow_address` - - - -
public fun borrow_address(s: &signer): &address
-
- - - -
-Implementation - - -
native public fun borrow_address(s: &signer): &address;
-
- - - -
- - - -## Function `address_of` - - - -
public fun address_of(s: &signer): address
-
- - - -
-Implementation - - -
public fun address_of(s: &signer): address {
-    *borrow_address(s)
-}
-
- - - -
- - -[//]: # ("File containing references which can be used from documentation") diff --git a/external-crates/move/crates/move-stdlib/docs/string.md b/external-crates/move/crates/move-stdlib/docs/string.md index e962b56d86758..c08d3514d8861 100644 --- a/external-crates/move/crates/move-stdlib/docs/string.md +++ b/external-crates/move/crates/move-stdlib/docs/string.md @@ -3,28 +3,35 @@ # Module `0x1::string` -The string module defines the String type which represents UTF8 encoded strings. +The string module defines the String type which represents UTF8 encoded +strings. - [Struct `String`](#0x1_string_String) - [Constants](#@Constants_0) - [Function `utf8`](#0x1_string_utf8) +- [Function `from_ascii`](#0x1_string_from_ascii) +- [Function `to_ascii`](#0x1_string_to_ascii) - [Function `try_utf8`](#0x1_string_try_utf8) -- [Function `bytes`](#0x1_string_bytes) +- [Function `as_bytes`](#0x1_string_as_bytes) +- [Function `into_bytes`](#0x1_string_into_bytes) - [Function `is_empty`](#0x1_string_is_empty) - [Function `length`](#0x1_string_length) - [Function `append`](#0x1_string_append) - [Function `append_utf8`](#0x1_string_append_utf8) - [Function `insert`](#0x1_string_insert) -- [Function `sub_string`](#0x1_string_sub_string) +- [Function `substring`](#0x1_string_substring) - [Function `index_of`](#0x1_string_index_of) - [Function `internal_check_utf8`](#0x1_string_internal_check_utf8) - [Function `internal_is_char_boundary`](#0x1_string_internal_is_char_boundary) - [Function `internal_sub_string`](#0x1_string_internal_sub_string) - [Function `internal_index_of`](#0x1_string_internal_index_of) +- [Function `bytes`](#0x1_string_bytes) +- [Function `sub_string`](#0x1_string_sub_string) -
use 0x1::option;
+
use 0x1::ascii;
+use 0x1::option;
 use 0x1::vector;
 
@@ -34,7 +41,8 @@ The string module defines the String
holds a sequence of bytes which is guaranteed to be in utf8 format. +A String holds a sequence of bytes which is guaranteed to be in utf8 +format.
struct String has copy, drop, store
@@ -48,7 +56,7 @@ A String holds a sequence
 
 
-bytes: vector<u8> +bytes: vector<u8>
@@ -63,22 +71,22 @@ A String holds a sequence ## Constants - + Index out of range. -
const EINVALID_INDEX: u64 = 2;
+
const EInvalidIndex: u64 = 2;
 
- + An invalid UTF8 encoding. -
const EINVALID_UTF8: u64 = 1;
+
const EInvalidUTF8: u64 = 1;
 
@@ -87,10 +95,63 @@ An invalid UTF8 encoding. ## Function `utf8` -Creates a new string from a sequence of bytes. Aborts if the bytes do not represent valid utf8. +Creates a new string from a sequence of bytes. Aborts if the bytes do +not represent valid utf8. + + +
public fun utf8(bytes: vector<u8>): string::String
+
+ + + +
+Implementation + + +
public fun utf8(bytes: vector<u8>): String {
+    assert!(internal_check_utf8(&bytes), EInvalidUTF8);
+    String { bytes }
+}
+
+ + + +
+ + + +## Function `from_ascii` + +Convert an ASCII string to a UTF8 string + + +
public fun from_ascii(s: ascii::String): string::String
+
+ -
public fun utf8(bytes: vector<u8>): string::String
+
+Implementation + + +
public fun from_ascii(s: ascii::String): String {
+    String { bytes: s.into_bytes() }
+}
+
+ + + +
+ + + +## Function `to_ascii` + +Convert an UTF8 string to an ASCII string. +Aborts if s is not valid ASCII + + +
public fun to_ascii(s: string::String): ascii::String
 
@@ -99,9 +160,9 @@ Creates a new string from a sequence of bytes. Aborts if the bytes do not repres Implementation -
public fun utf8(bytes: vector<u8>): String {
-    assert!(internal_check_utf8(&bytes), EINVALID_UTF8);
-    String{bytes}
+
public fun to_ascii(s: String): ascii::String {
+    let String { bytes } = s;
+    bytes.to_ascii_string()
 }
 
@@ -116,7 +177,7 @@ Creates a new string from a sequence of bytes. Aborts if the bytes do not repres Tries to create a new string from a sequence of bytes. -
public fun try_utf8(bytes: vector<u8>): option::Option<string::String>
+
public fun try_utf8(bytes: vector<u8>): option::Option<string::String>
 
@@ -125,12 +186,9 @@ Tries to create a new string from a sequence of bytes. Implementation -
public fun try_utf8(bytes: vector<u8>): Option<String> {
-    if (internal_check_utf8(&bytes)) {
-        option::some(String{bytes})
-    } else {
-        option::none()
-    }
+
public fun try_utf8(bytes: vector<u8>): Option<String> {
+    if (internal_check_utf8(&bytes)) option::some(String { bytes })
+    else option::none()
 }
 
@@ -138,14 +196,14 @@ Tries to create a new string from a sequence of bytes. - + -## Function `bytes` +## Function `as_bytes` Returns a reference to the underlying byte vector. -
public fun bytes(s: &string::String): &vector<u8>
+
public fun as_bytes(s: &string::String): &vector<u8>
 
@@ -154,13 +212,39 @@ Returns a reference to the underlying byte vector. Implementation -
public fun bytes(s: &String): &vector<u8> {
+
public fun as_bytes(s: &String): &vector<u8> {
     &s.bytes
 }
 
+ + + + +## Function `into_bytes` + +Unpack the string to get its underlying bytes. + + +
public fun into_bytes(s: string::String): vector<u8>
+
+ + + +
+Implementation + + +
public fun into_bytes(s: String): vector<u8> {
+    let String { bytes } = s;
+    bytes
+}
+
+ + +
@@ -180,7 +264,7 @@ Checks whether this string is empty.
public fun is_empty(s: &String): bool {
-    vector::is_empty(&s.bytes)
+    s.bytes.is_empty()
 }
 
@@ -195,7 +279,7 @@ Checks whether this string is empty. Returns the length of this string, in bytes. -
public fun length(s: &string::String): u64
+
public fun length(s: &string::String): u64
 
@@ -204,8 +288,8 @@ Returns the length of this string, in bytes. Implementation -
public fun length(s: &String): u64 {
-    vector::length(&s.bytes)
+
public fun length(s: &String): u64 {
+    s.bytes.length()
 }
 
@@ -230,7 +314,7 @@ Appends a string.
public fun append(s: &mut String, r: String) {
-    vector::append(&mut s.bytes, r.bytes)
+    s.bytes.append(r.bytes)
 }
 
@@ -245,7 +329,7 @@ Appends a string. Appends bytes which must be in valid utf8 format. -
public fun append_utf8(s: &mut string::String, bytes: vector<u8>)
+
public fun append_utf8(s: &mut string::String, bytes: vector<u8>)
 
@@ -254,8 +338,8 @@ Appends bytes which must be in valid utf8 format. Implementation -
public fun append_utf8(s: &mut String, bytes: vector<u8>) {
-    append(s, utf8(bytes))
+
public fun append_utf8(s: &mut String, bytes: vector<u8>) {
+    s.append(utf8(bytes))
 }
 
@@ -267,11 +351,11 @@ Appends bytes which must be in valid utf8 format. ## Function `insert` -Insert the other string at the byte index in given string. The index must be at a valid utf8 char -boundary. +Insert the other string at the byte index in given string. The index +must be at a valid utf8 char boundary. -
public fun insert(s: &mut string::String, at: u64, o: string::String)
+
public fun insert(s: &mut string::String, at: u64, o: string::String)
 
@@ -280,14 +364,17 @@ boundary. Implementation -
public fun insert(s: &mut String, at: u64, o: String) {
+
public fun insert(s: &mut String, at: u64, o: String) {
     let bytes = &s.bytes;
-    assert!(at <= vector::length(bytes) && internal_is_char_boundary(bytes, at), EINVALID_INDEX);
-    let l = length(s);
-    let front = sub_string(s, 0, at);
-    let end = sub_string(s, at, l);
-    append(&mut front, o);
-    append(&mut front, end);
+    assert!(
+        at <= bytes.length() && internal_is_char_boundary(bytes, at),
+        EInvalidIndex,
+    );
+    let l = s.length();
+    let mut front = s.substring(0, at);
+    let end = s.substring(at, l);
+    front.append(o);
+    front.append(end);
     *s = front;
 }
 
@@ -296,16 +383,17 @@ boundary. - + -## Function `sub_string` +## Function `substring` -Returns a sub-string using the given byte indices, where i is the first byte position and j is the start -of the first byte not included (or the length of the string). The indices must be at valid utf8 char boundaries, +Returns a sub-string using the given byte indices, where i is the first +byte position and j is the start of the first byte not included (or the +length of the string). The indices must be at valid utf8 char boundaries, guaranteeing that the result is valid utf8. -
public fun sub_string(s: &string::String, i: u64, j: u64): string::String
+
public fun substring(s: &string::String, i: u64, j: u64): string::String
 
@@ -314,14 +402,17 @@ guaranteeing that the result is valid utf8. Implementation -
public fun sub_string(s: &String, i: u64, j: u64): String {
+
public fun substring(s: &String, i: u64, j: u64): String {
     let bytes = &s.bytes;
-    let l = vector::length(bytes);
+    let l = bytes.length();
     assert!(
-        j <= l && i <= j && internal_is_char_boundary(bytes, i) && internal_is_char_boundary(bytes, j),
-        EINVALID_INDEX
+        j <= l &&
+        i <= j &&
+        internal_is_char_boundary(bytes, i) &&
+        internal_is_char_boundary(bytes, j),
+        EInvalidIndex,
     );
-    String{bytes: internal_sub_string(bytes, i, j)}
+    String { bytes: internal_sub_string(bytes, i, j) }
 }
 
@@ -333,10 +424,11 @@ guaranteeing that the result is valid utf8. ## Function `index_of` -Computes the index of the first occurrence of a string. Returns length(s) if no occurrence found. +Computes the index of the first occurrence of a string. Returns s.length() +if no occurrence found. -
public fun index_of(s: &string::String, r: &string::String): u64
+
public fun index_of(s: &string::String, r: &string::String): u64
 
@@ -345,7 +437,7 @@ Computes the index of the first occurrence of a string. Returns index_of(s: &String, r: &String): u64 { +
public fun index_of(s: &String, r: &String): u64 {
     internal_index_of(&s.bytes, &r.bytes)
 }
 
@@ -360,7 +452,7 @@ Computes the index of the first occurrence of a string. Returns internal_check_utf8(v: &vector<u8>): bool +
fun internal_check_utf8(v: &vector<u8>): bool
 
@@ -369,7 +461,7 @@ Computes the index of the first occurrence of a string. Returns internal_check_utf8(v: &vector<u8>): bool; +
native fun internal_check_utf8(v: &vector<u8>): bool;
 
@@ -382,7 +474,7 @@ Computes the index of the first occurrence of a string. Returns internal_is_char_boundary(v: &vector<u8>, i: u64): bool +
fun internal_is_char_boundary(v: &vector<u8>, i: u64): bool
 
@@ -391,7 +483,7 @@ Computes the index of the first occurrence of a string. Returns internal_is_char_boundary(v: &vector<u8>, i: u64): bool; +
native fun internal_is_char_boundary(v: &vector<u8>, i: u64): bool;
 
@@ -404,7 +496,7 @@ Computes the index of the first occurrence of a string. Returns internal_sub_string(v: &vector<u8>, i: u64, j: u64): vector<u8> +
fun internal_sub_string(v: &vector<u8>, i: u64, j: u64): vector<u8>
 
@@ -413,7 +505,7 @@ Computes the index of the first occurrence of a string. Returns internal_sub_string(v: &vector<u8>, i: u64, j: u64): vector<u8>; +
native fun internal_sub_string(v: &vector<u8>, i: u64, j: u64): vector<u8>;
 
@@ -426,7 +518,29 @@ Computes the index of the first occurrence of a string. Returns internal_index_of(v: &vector<u8>, r: &vector<u8>): u64 +
fun internal_index_of(v: &vector<u8>, r: &vector<u8>): u64
+
+ + + +
+Implementation + + +
native fun internal_index_of(v: &vector<u8>, r: &vector<u8>): u64;
+
+ + + +
+ + + +## Function `bytes` + + + +
public fun bytes(s: &string::String): &vector<u8>
 
@@ -435,7 +549,31 @@ Computes the index of the first occurrence of a string. Returns internal_index_of(v: &vector<u8>, r: &vector<u8>): u64; +
public fun bytes(s: &String): &vector<u8> { s.as_bytes() }
+
+ + + + + + + +## Function `sub_string` + + + +
public fun sub_string(s: &string::String, i: u64, j: u64): string::String
+
+ + + +
+Implementation + + +
public fun sub_string(s: &String, i: u64, j: u64): String {
+    s.substring(i, j)
+}
 
diff --git a/external-crates/move/crates/move-stdlib/docs/type_name.md b/external-crates/move/crates/move-stdlib/docs/type_name.md index 070e0ffd56e45..2c79606a1aefe 100644 --- a/external-crates/move/crates/move-stdlib/docs/type_name.md +++ b/external-crates/move/crates/move-stdlib/docs/type_name.md @@ -7,13 +7,18 @@ Functionality for converting Move types into values. Use with care! - [Struct `TypeName`](#0x1_type_name_TypeName) +- [Constants](#@Constants_0) - [Function `get`](#0x1_type_name_get) - [Function `get_with_original_ids`](#0x1_type_name_get_with_original_ids) +- [Function `is_primitive`](#0x1_type_name_is_primitive) - [Function `borrow_string`](#0x1_type_name_borrow_string) +- [Function `get_address`](#0x1_type_name_get_address) +- [Function `get_module`](#0x1_type_name_get_module) - [Function `into_string`](#0x1_type_name_into_string) -
use 0x1::ascii;
+
use 0x1::address;
+use 0x1::ascii;
 
@@ -40,10 +45,10 @@ Functionality for converting Move types into values. Use with care!
String representation of the type. All types are represented using their source syntax: - "u8", "u64", "u128", "bool", "address", "vector", "signer" for ground types. + "u8", "u64", "bool", "address", "vector", and so on for primitive types. Struct types are represented as fully qualified type names; e.g. 00000000000000000000000000000001::string::String or - 0000000000000000000000000000000a::module_name1::type_name1<0000000000000000000000000000000a::module_name2::type_name2<u64>> + 0000000000000000000000000000000a::module_name1::type_name1<0000000000000000000000000000000a::module_name2::type_name2<u64>> Addresses are hex-encoded lowercase values of length ADDRESS_LENGTH (16, 20, or 32 depending on the Move platform)
@@ -51,6 +56,91 @@ Functionality for converting Move types into values. Use with care! + + +## Constants + + + + +ASCII Character code for the c (lowercase c) symbol. + + +
const ASCII_C: u8 = 99;
+
+ + + + + +ASCII Character code for the : (colon) symbol. + + +
const ASCII_COLON: u8 = 58;
+
+ + + + + +ASCII Character code for the e (lowercase e) symbol. + + +
const ASCII_E: u8 = 101;
+
+ + + + + +ASCII Character code for the o (lowercase o) symbol. + + +
const ASCII_O: u8 = 111;
+
+ + + + + +ASCII Character code for the r (lowercase r) symbol. + + +
const ASCII_R: u8 = 114;
+
+ + + + + +ASCII Character code for the t (lowercase t) symbol. + + +
const ASCII_T: u8 = 116;
+
+ + + + + +ASCII Character code for the v (lowercase v) symbol. + + +
const ASCII_V: u8 = 118;
+
+ + + + + +The type is not from a package/module. It is a primitive type. + + +
const ENonModuleType: u64 = 0;
+
+ + + ## Function `get` @@ -102,6 +192,49 @@ later upgrade). + + + + +## Function `is_primitive` + +Returns true iff the TypeName represents a primitive type, i.e. one of +u8, u16, u32, u64, u128, u256, bool, address, vector. + + +
public fun is_primitive(self: &type_name::TypeName): bool
+
+ + + +
+Implementation + + +
public fun is_primitive(self: &TypeName): bool {
+    let bytes = self.name.as_bytes();
+    bytes == &b"bool" ||
+    bytes == &b"u8" ||
+    bytes == &b"u16" ||
+    bytes == &b"u32" ||
+    bytes == &b"u64" ||
+    bytes == &b"u128" ||
+    bytes == &b"u256" ||
+    bytes == &b"address" ||
+    (
+        bytes.length() >= 6 &&
+        bytes[0] == ASCII_V &&
+        bytes[1] == ASCII_E &&
+        bytes[2] == ASCII_C &&
+        bytes[3] == ASCII_T &&
+        bytes[4] == ASCII_O &&
+        bytes[5] == ASCII_R,
+    )
+}
+
+ + +
@@ -127,6 +260,89 @@ Get the String representation of self + + + + +## Function `get_address` + +Get Address string (Base16 encoded), first part of the TypeName. +Aborts if given a primitive type. + + +
public fun get_address(self: &type_name::TypeName): ascii::String
+
+ + + +
+Implementation + + +
public fun get_address(self: &TypeName): String {
+    assert!(!self.is_primitive(), ENonModuleType);
+
+    // Base16 (string) representation of an address has 2 symbols per byte.
+    let len = address::length() * 2;
+    let str_bytes = self.name.as_bytes();
+    let mut addr_bytes = vector[];
+    let mut i = 0;
+
+    // Read `len` bytes from the type name and push them to addr_bytes.
+    while (i < len) {
+        addr_bytes.push_back(str_bytes[i]);
+        i = i + 1;
+    };
+
+    ascii::string(addr_bytes)
+}
+
+ + + +
+ + + +## Function `get_module` + +Get name of the module. +Aborts if given a primitive type. + + +
public fun get_module(self: &type_name::TypeName): ascii::String
+
+ + + +
+Implementation + + +
public fun get_module(self: &TypeName): String {
+    assert!(!self.is_primitive(), ENonModuleType);
+
+    // Starts after address and a double colon: `<addr as HEX>::`
+    let mut i = address::length() * 2 + 2;
+    let str_bytes = self.name.as_bytes();
+    let mut module_name = vector[];
+    let colon = ASCII_COLON;
+    loop {
+        let char = &str_bytes[i];
+        if (char != &colon) {
+            module_name.push_back(*char);
+            i = i + 1;
+        } else {
+            break
+        }
+    };
+
+    ascii::string(module_name)
+}
+
+ + +
diff --git a/external-crates/move/crates/move-stdlib/docs/u128.md b/external-crates/move/crates/move-stdlib/docs/u128.md new file mode 100644 index 0000000000000..682372e184dc2 --- /dev/null +++ b/external-crates/move/crates/move-stdlib/docs/u128.md @@ -0,0 +1,195 @@ + + + +# Module `0x1::u128` + + + +- [Function `max`](#0x1_u128_max) +- [Function `min`](#0x1_u128_min) +- [Function `diff`](#0x1_u128_diff) +- [Function `divide_and_round_up`](#0x1_u128_divide_and_round_up) +- [Function `pow`](#0x1_u128_pow) +- [Function `sqrt`](#0x1_u128_sqrt) + + +
+ + + + + +## Function `max` + +Return the larger of x and y + + +
public fun max(x: u128, y: u128): u128
+
+ + + +
+Implementation + + +
public fun max(x: u128, y: u128): u128 {
+    std::macros::num_max!(x, y)
+}
+
+ + + +
+ + + +## Function `min` + +Return the smaller of x and y + + +
public fun min(x: u128, y: u128): u128
+
+ + + +
+Implementation + + +
public fun min(x: u128, y: u128): u128 {
+    std::macros::num_min!(x, y)
+}
+
+ + + +
+ + + +## Function `diff` + +Return the absolute value of x - y + + +
public fun diff(x: u128, y: u128): u128
+
+ + + +
+Implementation + + +
public fun diff(x: u128, y: u128): u128 {
+    std::macros::num_diff!(x, y)
+}
+
+ + + +
+ + + +## Function `divide_and_round_up` + +Calculate x / y, but round up the result. + + +
public fun divide_and_round_up(x: u128, y: u128): u128
+
+ + + +
+Implementation + + +
public fun divide_and_round_up(x: u128, y: u128): u128 {
+    std::macros::num_divide_and_round_up!(x, y)
+}
+
+ + + +
+ + + +## Function `pow` + +Return the value of a base raised to a power + + +
public fun pow(base: u128, exponent: u8): u128
+
+ + + +
+Implementation + + +
public fun pow(base: u128, exponent: u8): u128 {
+    std::macros::num_pow!(base, exponent)
+}
+
+ + + +
+ + + +## Function `sqrt` + +Get a nearest lower integer Square Root for x. Given that this +function can only operate with integers, it is impossible +to get perfect (or precise) integer square root for some numbers. + +Example: +``` +math::sqrt(9) => 3 +math::sqrt(8) => 2 // the nearest lower square root is 4; +``` + +In integer math, one of the possible ways to get results with more +precision is to use higher values or temporarily multiply the +value by some bigger number. Ideally if this is a square of 10 or 100. + +Example: +``` +math::sqrt(8) => 2; +math::sqrt(8 * 10000) => 282; +// now we can use this value as if it was 2.82; +// but to get the actual result, this value needs +// to be divided by 100 (because sqrt(10000)). + + +math::sqrt(8 * 1000000) => 2828; // same as above, 2828 / 1000 (2.828) +``` + + +
public fun sqrt(x: u128): u128
+
+ + + +
+Implementation + + +
public fun sqrt(x: u128): u128 {
+    std::macros::num_sqrt!<u128, u256>(x, 128)
+}
+
+ + + +
+ + +[//]: # ("File containing references which can be used from documentation") diff --git a/external-crates/move/crates/move-stdlib/docs/u16.md b/external-crates/move/crates/move-stdlib/docs/u16.md new file mode 100644 index 0000000000000..08f849ea354fa --- /dev/null +++ b/external-crates/move/crates/move-stdlib/docs/u16.md @@ -0,0 +1,195 @@ + + + +# Module `0x1::u16` + + + +- [Function `max`](#0x1_u16_max) +- [Function `min`](#0x1_u16_min) +- [Function `diff`](#0x1_u16_diff) +- [Function `divide_and_round_up`](#0x1_u16_divide_and_round_up) +- [Function `pow`](#0x1_u16_pow) +- [Function `sqrt`](#0x1_u16_sqrt) + + +
+ + + + + +## Function `max` + +Return the larger of x and y + + +
public fun max(x: u16, y: u16): u16
+
+ + + +
+Implementation + + +
public fun max(x: u16, y: u16): u16 {
+    std::macros::num_max!(x, y)
+}
+
+ + + +
+ + + +## Function `min` + +Return the smaller of x and y + + +
public fun min(x: u16, y: u16): u16
+
+ + + +
+Implementation + + +
public fun min(x: u16, y: u16): u16 {
+    std::macros::num_min!(x, y)
+}
+
+ + + +
+ + + +## Function `diff` + +Return the absolute value of x - y + + +
public fun diff(x: u16, y: u16): u16
+
+ + + +
+Implementation + + +
public fun diff(x: u16, y: u16): u16 {
+    std::macros::num_diff!(x, y)
+}
+
+ + + +
+ + + +## Function `divide_and_round_up` + +Calculate x / y, but round up the result. + + +
public fun divide_and_round_up(x: u16, y: u16): u16
+
+ + + +
+Implementation + + +
public fun divide_and_round_up(x: u16, y: u16): u16 {
+    std::macros::num_divide_and_round_up!(x, y)
+}
+
+ + + +
+ + + +## Function `pow` + +Return the value of a base raised to a power + + +
public fun pow(base: u16, exponent: u8): u16
+
+ + + +
+Implementation + + +
public fun pow(base: u16, exponent: u8): u16 {
+    std::macros::num_pow!(base, exponent)
+}
+
+ + + +
+ + + +## Function `sqrt` + +Get a nearest lower integer Square Root for x. Given that this +function can only operate with integers, it is impossible +to get perfect (or precise) integer square root for some numbers. + +Example: +``` +math::sqrt(9) => 3 +math::sqrt(8) => 2 // the nearest lower square root is 4; +``` + +In integer math, one of the possible ways to get results with more +precision is to use higher values or temporarily multiply the +value by some bigger number. Ideally if this is a square of 10 or 100. + +Example: +``` +math::sqrt(8) => 2; +math::sqrt(8 * 10000) => 282; +// now we can use this value as if it was 2.82; +// but to get the actual result, this value needs +// to be divided by 100 (because sqrt(10000)). + + +math::sqrt(8 * 1000000) => 2828; // same as above, 2828 / 1000 (2.828) +``` + + +
public fun sqrt(x: u16): u16
+
+ + + +
+Implementation + + +
public fun sqrt(x: u16): u16 {
+    std::macros::num_sqrt!<u16, u32>(x, 16)
+}
+
+ + + +
+ + +[//]: # ("File containing references which can be used from documentation") diff --git a/external-crates/move/crates/move-stdlib/docs/u256.md b/external-crates/move/crates/move-stdlib/docs/u256.md new file mode 100644 index 0000000000000..84ed79a1229c6 --- /dev/null +++ b/external-crates/move/crates/move-stdlib/docs/u256.md @@ -0,0 +1,145 @@ + + + +# Module `0x1::u256` + + + +- [Function `max`](#0x1_u256_max) +- [Function `min`](#0x1_u256_min) +- [Function `diff`](#0x1_u256_diff) +- [Function `divide_and_round_up`](#0x1_u256_divide_and_round_up) +- [Function `pow`](#0x1_u256_pow) + + +
+ + + + + +## Function `max` + +Return the larger of x and y + + +
public fun max(x: u256, y: u256): u256
+
+ + + +
+Implementation + + +
public fun max(x: u256, y: u256): u256 {
+    std::macros::num_max!(x, y)
+}
+
+ + + +
+ + + +## Function `min` + +Return the smaller of x and y + + +
public fun min(x: u256, y: u256): u256
+
+ + + +
+Implementation + + +
public fun min(x: u256, y: u256): u256 {
+    std::macros::num_min!(x, y)
+}
+
+ + + +
+ + + +## Function `diff` + +Return the absolute value of x - y + + +
public fun diff(x: u256, y: u256): u256
+
+ + + +
+Implementation + + +
public fun diff(x: u256, y: u256): u256 {
+    std::macros::num_diff!(x, y)
+}
+
+ + + +
+ + + +## Function `divide_and_round_up` + +Calculate x / y, but round up the result. + + +
public fun divide_and_round_up(x: u256, y: u256): u256
+
+ + + +
+Implementation + + +
public fun divide_and_round_up(x: u256, y: u256): u256 {
+    std::macros::num_divide_and_round_up!(x, y)
+}
+
+ + + +
+ + + +## Function `pow` + +Return the value of a base raised to a power + + +
public fun pow(base: u256, exponent: u8): u256
+
+ + + +
+Implementation + + +
public fun pow(base: u256, exponent: u8): u256 {
+    std::macros::num_pow!(base, exponent)
+}
+
+ + + +
+ + +[//]: # ("File containing references which can be used from documentation") diff --git a/external-crates/move/crates/move-stdlib/docs/u32.md b/external-crates/move/crates/move-stdlib/docs/u32.md new file mode 100644 index 0000000000000..084335a41875f --- /dev/null +++ b/external-crates/move/crates/move-stdlib/docs/u32.md @@ -0,0 +1,195 @@ + + + +# Module `0x1::u32` + + + +- [Function `max`](#0x1_u32_max) +- [Function `min`](#0x1_u32_min) +- [Function `diff`](#0x1_u32_diff) +- [Function `divide_and_round_up`](#0x1_u32_divide_and_round_up) +- [Function `pow`](#0x1_u32_pow) +- [Function `sqrt`](#0x1_u32_sqrt) + + +
+ + + + + +## Function `max` + +Return the larger of x and y + + +
public fun max(x: u32, y: u32): u32
+
+ + + +
+Implementation + + +
public fun max(x: u32, y: u32): u32 {
+    std::macros::num_max!(x, y)
+}
+
+ + + +
+ + + +## Function `min` + +Return the smaller of x and y + + +
public fun min(x: u32, y: u32): u32
+
+ + + +
+Implementation + + +
public fun min(x: u32, y: u32): u32 {
+    std::macros::num_min!(x, y)
+}
+
+ + + +
+ + + +## Function `diff` + +Return the absolute value of x - y + + +
public fun diff(x: u32, y: u32): u32
+
+ + + +
+Implementation + + +
public fun diff(x: u32, y: u32): u32 {
+    std::macros::num_diff!(x, y)
+}
+
+ + + +
+ + + +## Function `divide_and_round_up` + +Calculate x / y, but round up the result. + + +
public fun divide_and_round_up(x: u32, y: u32): u32
+
+ + + +
+Implementation + + +
public fun divide_and_round_up(x: u32, y: u32): u32 {
+    std::macros::num_divide_and_round_up!(x, y)
+}
+
+ + + +
+ + + +## Function `pow` + +Return the value of a base raised to a power + + +
public fun pow(base: u32, exponent: u8): u32
+
+ + + +
+Implementation + + +
public fun pow(base: u32, exponent: u8): u32 {
+    std::macros::num_pow!(base, exponent)
+}
+
+ + + +
+ + + +## Function `sqrt` + +Get a nearest lower integer Square Root for x. Given that this +function can only operate with integers, it is impossible +to get perfect (or precise) integer square root for some numbers. + +Example: +``` +math::sqrt(9) => 3 +math::sqrt(8) => 2 // the nearest lower square root is 4; +``` + +In integer math, one of the possible ways to get results with more +precision is to use higher values or temporarily multiply the +value by some bigger number. Ideally if this is a square of 10 or 100. + +Example: +``` +math::sqrt(8) => 2; +math::sqrt(8 * 10000) => 282; +// now we can use this value as if it was 2.82; +// but to get the actual result, this value needs +// to be divided by 100 (because sqrt(10000)). + + +math::sqrt(8 * 1000000) => 2828; // same as above, 2828 / 1000 (2.828) +``` + + +
public fun sqrt(x: u32): u32
+
+ + + +
+Implementation + + +
public fun sqrt(x: u32): u32 {
+    std::macros::num_sqrt!<u32, u64>(x, 32)
+}
+
+ + + +
+ + +[//]: # ("File containing references which can be used from documentation") diff --git a/external-crates/move/crates/move-stdlib/docs/u64.md b/external-crates/move/crates/move-stdlib/docs/u64.md new file mode 100644 index 0000000000000..9d27a4311f1c6 --- /dev/null +++ b/external-crates/move/crates/move-stdlib/docs/u64.md @@ -0,0 +1,195 @@ + + + +# Module `0x1::u64` + + + +- [Function `max`](#0x1_u64_max) +- [Function `min`](#0x1_u64_min) +- [Function `diff`](#0x1_u64_diff) +- [Function `divide_and_round_up`](#0x1_u64_divide_and_round_up) +- [Function `pow`](#0x1_u64_pow) +- [Function `sqrt`](#0x1_u64_sqrt) + + +
+ + + + + +## Function `max` + +Return the larger of x and y + + +
public fun max(x: u64, y: u64): u64
+
+ + + +
+Implementation + + +
public fun max(x: u64, y: u64): u64 {
+    std::macros::num_max!(x, y)
+}
+
+ + + +
+ + + +## Function `min` + +Return the smaller of x and y + + +
public fun min(x: u64, y: u64): u64
+
+ + + +
+Implementation + + +
public fun min(x: u64, y: u64): u64 {
+    std::macros::num_min!(x, y)
+}
+
+ + + +
+ + + +## Function `diff` + +Return the absolute value of x - y + + +
public fun diff(x: u64, y: u64): u64
+
+ + + +
+Implementation + + +
public fun diff(x: u64, y: u64): u64 {
+    std::macros::num_diff!(x, y)
+}
+
+ + + +
+ + + +## Function `divide_and_round_up` + +Calculate x / y, but round up the result. + + +
public fun divide_and_round_up(x: u64, y: u64): u64
+
+ + + +
+Implementation + + +
public fun divide_and_round_up(x: u64, y: u64): u64 {
+    std::macros::num_divide_and_round_up!(x, y)
+}
+
+ + + +
+ + + +## Function `pow` + +Return the value of a base raised to a power + + +
public fun pow(base: u64, exponent: u8): u64
+
+ + + +
+Implementation + + +
public fun pow(base: u64, exponent: u8): u64 {
+    std::macros::num_pow!(base, exponent)
+}
+
+ + + +
+ + + +## Function `sqrt` + +Get a nearest lower integer Square Root for x. Given that this +function can only operate with integers, it is impossible +to get perfect (or precise) integer square root for some numbers. + +Example: +``` +math::sqrt(9) => 3 +math::sqrt(8) => 2 // the nearest lower square root is 4; +``` + +In integer math, one of the possible ways to get results with more +precision is to use higher values or temporarily multiply the +value by some bigger number. Ideally if this is a square of 10 or 100. + +Example: +``` +math::sqrt(8) => 2; +math::sqrt(8 * 10000) => 282; +// now we can use this value as if it was 2.82; +// but to get the actual result, this value needs +// to be divided by 100 (because sqrt(10000)). + + +math::sqrt(8 * 1000000) => 2828; // same as above, 2828 / 1000 (2.828) +``` + + +
public fun sqrt(x: u64): u64
+
+ + + +
+Implementation + + +
public fun sqrt(x: u64): u64 {
+    std::macros::num_sqrt!<u64, u128>(x, 64)
+}
+
+ + + +
+ + +[//]: # ("File containing references which can be used from documentation") diff --git a/external-crates/move/crates/move-stdlib/docs/u8.md b/external-crates/move/crates/move-stdlib/docs/u8.md new file mode 100644 index 0000000000000..0dc6d3b4fc02e --- /dev/null +++ b/external-crates/move/crates/move-stdlib/docs/u8.md @@ -0,0 +1,195 @@ + + + +# Module `0x1::u8` + + + +- [Function `max`](#0x1_u8_max) +- [Function `min`](#0x1_u8_min) +- [Function `diff`](#0x1_u8_diff) +- [Function `divide_and_round_up`](#0x1_u8_divide_and_round_up) +- [Function `pow`](#0x1_u8_pow) +- [Function `sqrt`](#0x1_u8_sqrt) + + +
+ + + + + +## Function `max` + +Return the larger of x and y + + +
public fun max(x: u8, y: u8): u8
+
+ + + +
+Implementation + + +
public fun max(x: u8, y: u8): u8 {
+    std::macros::num_max!(x, y)
+}
+
+ + + +
+ + + +## Function `min` + +Return the smaller of x and y + + +
public fun min(x: u8, y: u8): u8
+
+ + + +
+Implementation + + +
public fun min(x: u8, y: u8): u8 {
+    std::macros::num_min!(x, y)
+}
+
+ + + +
+ + + +## Function `diff` + +Return the absolute value of x - y + + +
public fun diff(x: u8, y: u8): u8
+
+ + + +
+Implementation + + +
public fun diff(x: u8, y: u8): u8 {
+    std::macros::num_diff!(x, y)
+}
+
+ + + +
+ + + +## Function `divide_and_round_up` + +Calculate x / y, but round up the result. + + +
public fun divide_and_round_up(x: u8, y: u8): u8
+
+ + + +
+Implementation + + +
public fun divide_and_round_up(x: u8, y: u8): u8 {
+    std::macros::num_divide_and_round_up!(x, y)
+}
+
+ + + +
+ + + +## Function `pow` + +Return the value of a base raised to a power + + +
public fun pow(base: u8, exponent: u8): u8
+
+ + + +
+Implementation + + +
public fun pow(base: u8, exponent: u8): u8 {
+    std::macros::num_pow!(base, exponent)
+}
+
+ + + +
+ + + +## Function `sqrt` + +Get a nearest lower integer Square Root for x. Given that this +function can only operate with integers, it is impossible +to get perfect (or precise) integer square root for some numbers. + +Example: +``` +math::sqrt(9) => 3 +math::sqrt(8) => 2 // the nearest lower square root is 4; +``` + +In integer math, one of the possible ways to get results with more +precision is to use higher values or temporarily multiply the +value by some bigger number. Ideally if this is a square of 10 or 100. + +Example: +``` +math::sqrt(8) => 2; +math::sqrt(8 * 10000) => 282; +// now we can use this value as if it was 2.82; +// but to get the actual result, this value needs +// to be divided by 100 (because sqrt(10000)). + + +math::sqrt(8 * 1000000) => 2828; // same as above, 2828 / 1000 (2.828) +``` + + +
public fun sqrt(x: u8): u8
+
+ + + +
+Implementation + + +
public fun sqrt(x: u8): u8 {
+    std::macros::num_sqrt!<u8, u16>(x, 8)
+}
+
+ + + +
+ + +[//]: # ("File containing references which can be used from documentation") diff --git a/external-crates/move/crates/move-stdlib/docs/vector.md b/external-crates/move/crates/move-stdlib/docs/vector.md index be96fad8c4044..1e13e7dc46eaf 100644 --- a/external-crates/move/crates/move-stdlib/docs/vector.md +++ b/external-crates/move/crates/move-stdlib/docs/vector.md @@ -41,7 +41,7 @@ vectors are growable. This module has many native functions. The index into the vector is out of bounds -
const EINDEX_OUT_OF_BOUNDS: u64 = 131072;
+
const EINDEX_OUT_OF_BOUNDS: u64 = 131072;
 
@@ -62,7 +62,7 @@ Create an empty vector. Implementation -
native public fun empty<Element>(): vector<Element>;
+
public native fun empty<Element>(): vector<Element>;
 
@@ -76,7 +76,7 @@ Create an empty vector. Return the length of the vector. -
public fun length<Element>(v: &vector<Element>): u64
+
public fun length<Element>(v: &vector<Element>): u64
 
@@ -85,7 +85,7 @@ Return the length of the vector. Implementation -
native public fun length<Element>(v: &vector<Element>): u64;
+
public native fun length<Element>(v: &vector<Element>): u64;
 
@@ -100,7 +100,7 @@ Acquire an immutable reference to the ith element of the vector i
is out of bounds. -
public fun borrow<Element>(v: &vector<Element>, i: u64): &Element
+
public fun borrow<Element>(v: &vector<Element>, i: u64): &Element
 
@@ -109,7 +109,7 @@ Aborts if i is out of bounds. Implementation -
native public fun borrow<Element>(v: &vector<Element>, i: u64): ∈
+
public native fun borrow<Element>(v: &vector<Element>, i: u64): ∈
 
@@ -132,7 +132,7 @@ Add element e to the end of the vector v. Implementation -
native public fun push_back<Element>(v: &mut vector<Element>, e: Element);
+
public native fun push_back<Element>(v: &mut vector<Element>, e: Element);
 
@@ -147,7 +147,7 @@ Return a mutable reference to the ith element in the vector v Aborts if i is out of bounds. -
public fun borrow_mut<Element>(v: &mut vector<Element>, i: u64): &mut Element
+
public fun borrow_mut<Element>(v: &mut vector<Element>, i: u64): &mut Element
 
@@ -156,7 +156,7 @@ Aborts if i is out of bounds. Implementation -
native public fun borrow_mut<Element>(v: &mut vector<Element>, i: u64): &mut Element;
+
public native fun borrow_mut<Element>(v: &mut vector<Element>, i: u64): &mut Element;
 
@@ -180,7 +180,7 @@ Aborts if v is empty. Implementation -
native public fun pop_back<Element>(v: &mut vector<Element>): Element;
+
public native fun pop_back<Element>(v: &mut vector<Element>): Element;
 
@@ -204,7 +204,7 @@ Aborts if v is not empty. Implementation -
native public fun destroy_empty<Element>(v: vector<Element>);
+
public native fun destroy_empty<Element>(v: vector<Element>);
 
@@ -219,7 +219,7 @@ Swaps the elements at the ith and jth indices in the v Aborts if i or j is out of bounds. -
public fun swap<Element>(v: &mut vector<Element>, i: u64, j: u64)
+
public fun swap<Element>(v: &mut vector<Element>, i: u64, j: u64)
 
@@ -228,7 +228,7 @@ Aborts if i or j is out of bounds. Implementation -
native public fun swap<Element>(v: &mut vector<Element>, i: u64, j: u64);
+
public native fun swap<Element>(v: &mut vector<Element>, i: u64, j: u64);
 
@@ -252,8 +252,8 @@ Return an vector of size one containing element e.
public fun singleton<Element>(e: Element): vector<Element> {
-    let v = empty();
-    push_back(&mut v, e);
+    let mut v = empty();
+    v.push_back(e);
     v
 }
 
@@ -279,13 +279,13 @@ Reverses the order of the elements in the vector v in place.
public fun reverse<Element>(v: &mut vector<Element>) {
-    let len = length(v);
+    let len = v.length();
     if (len == 0) return ();
 
-    let front_index = 0;
-    let back_index = len -1;
+    let mut front_index = 0;
+    let mut back_index = len - 1;
     while (front_index < back_index) {
-        swap(v, front_index, back_index);
+        v.swap(front_index, back_index);
         front_index = front_index + 1;
         back_index = back_index - 1;
     }
@@ -312,10 +312,10 @@ Pushes all of the elements of the other vector into the lhsImplementation
 
 
-
public fun append<Element>(lhs: &mut vector<Element>, other: vector<Element>) {
-    reverse(&mut other);
-    while (!is_empty(&other)) push_back(lhs, pop_back(&mut other));
-    destroy_empty(other);
+
public fun append<Element>(lhs: &mut vector<Element>, mut other: vector<Element>) {
+    other.reverse();
+    while (!other.is_empty()) lhs.push_back(other.pop_back());
+    other.destroy_empty();
 }
 
@@ -340,7 +340,7 @@ Return true if the vector v has no elements and
public fun is_empty<Element>(v: &vector<Element>): bool {
-    length(v) == 0
+    v.length() == 0
 }
 
@@ -366,10 +366,10 @@ Otherwise, returns false.
public fun contains<Element>(v: &vector<Element>, e: &Element): bool {
-    let i = 0;
-    let len = length(v);
+    let mut i = 0;
+    let len = v.length();
     while (i < len) {
-        if (borrow(v, i) == e) return true;
+        if (&v[i] == e) return true;
         i = i + 1;
     };
     false
@@ -388,7 +388,7 @@ Return (true, i) if e is in the vector v<
 Otherwise, returns (false, 0).
 
 
-
public fun index_of<Element>(v: &vector<Element>, e: &Element): (bool, u64)
+
public fun index_of<Element>(v: &vector<Element>, e: &Element): (bool, u64)
 
@@ -397,11 +397,11 @@ Otherwise, returns (false, 0). Implementation -
public fun index_of<Element>(v: &vector<Element>, e: &Element): (bool, u64) {
-    let i = 0;
-    let len = length(v);
+
public fun index_of<Element>(v: &vector<Element>, e: &Element): (bool, u64) {
+    let mut i = 0;
+    let len = v.length();
     while (i < len) {
-        if (borrow(v, i) == e) return (true, i);
+        if (&v[i] == e) return (true, i);
         i = i + 1;
     };
     (false, 0)
@@ -421,7 +421,7 @@ This is O(n) and preserves ordering of elements in the vector.
 Aborts if i is out of bounds.
 
 
-
public fun remove<Element>(v: &mut vector<Element>, i: u64): Element
+
public fun remove<Element>(v: &mut vector<Element>, i: u64): Element
 
@@ -430,14 +430,14 @@ Aborts if i is out of bounds. Implementation -
public fun remove<Element>(v: &mut vector<Element>, i: u64): Element {
-    let len = length(v);
+
public fun remove<Element>(v: &mut vector<Element>, mut i: u64): Element {
+    let mut len = v.length();
     // i out of bounds; abort
     if (i >= len) abort EINDEX_OUT_OF_BOUNDS;
 
     len = len - 1;
-    while (i < len) swap(v, i, { i = i + 1; i });
-    pop_back(v)
+    while (i < len) v.swap(i, { i = i + 1; i });
+    v.pop_back()
 }
 
@@ -451,12 +451,12 @@ Aborts if i is out of bounds. Insert e at position i in the vector v. If i is in bounds, this shifts the old v[i] and all subsequent elements to the right. -If i == length(v), this adds e to the end of the vector. +If i == v.length(), this adds e to the end of the vector. This is O(n) and preserves ordering of elements in the vector. -Aborts if i > length(v) +Aborts if i > v.length() -
public fun insert<Element>(v: &mut vector<Element>, e: Element, i: u64)
+
public fun insert<Element>(v: &mut vector<Element>, e: Element, i: u64)
 
@@ -465,14 +465,14 @@ Aborts if i > length(v)Implementation -
public fun insert<Element>(v: &mut vector<Element>, e: Element, i: u64) {
-    let len = length(v);
+
public fun insert<Element>(v: &mut vector<Element>, e: Element, mut i: u64) {
+    let len = v.length();
     // i too big abort
     if (i > len) abort EINDEX_OUT_OF_BOUNDS;
 
-    push_back(v, e);
+    v.push_back(e);
     while (i < len) {
-        swap(v, i, len);
+        v.swap(i, len);
         i = i + 1
     }
 }
@@ -491,7 +491,7 @@ This is O(1), but does not preserve ordering of elements in the vector.
 Aborts if i is out of bounds.
 
 
-
public fun swap_remove<Element>(v: &mut vector<Element>, i: u64): Element
+
public fun swap_remove<Element>(v: &mut vector<Element>, i: u64): Element
 
@@ -500,11 +500,11 @@ Aborts if i is out of bounds. Implementation -
public fun swap_remove<Element>(v: &mut vector<Element>, i: u64): Element {
-    assert!(!is_empty(v), EINDEX_OUT_OF_BOUNDS);
-    let last_idx = length(v) - 1;
-    swap(v, i, last_idx);
-    pop_back(v)
+
public fun swap_remove<Element>(v: &mut vector<Element>, i: u64): Element {
+    assert!(!v.is_empty(), EINDEX_OUT_OF_BOUNDS);
+    let last_idx = v.length() - 1;
+    v.swap(i, last_idx);
+    v.pop_back()
 }
 
diff --git a/external-crates/move/crates/move-stdlib/error_description.errmap b/external-crates/move/crates/move-stdlib/error_description.errmap deleted file mode 100644 index e278809587b0938d92310bc82416685344e3b14e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1310 zcmcJP(Qnf*5XO_S4KXn$#2ye&C*TDk?Ew(tVM5AUZ!_wWs!b>0B}tQOYbmvZ<8*_+ zo{Qs7(_ou;Ac_# z87vNV{)mu{)e=+U7A1l(P%l(d7BD59P)SS=e8;l4W(lMCzrk3^zrt9|`wi@j-sno| zdAnCdoy5nVUR$^pKN@8O zN&_^U4Z_8`b&XO`W_pf5#!@RUkXpK(6-b+pAJPXd+#Wld3}QYU@Ltf4*=HMc*_1|9 zr69ZhE$pVt?&%d}a`Y|v&PLHI2%r!vQMc1L{b_T$d30 - -# Module `0x1::compare` - -Utilities for comparing Move values based on their representation in BCS. - - -- [Constants](#@Constants_0) -- [Function `cmp_bcs_bytes`](#0x1_compare_cmp_bcs_bytes) -- [Function `cmp_u8`](#0x1_compare_cmp_u8) -- [Function `cmp_u64`](#0x1_compare_cmp_u64) - - -
- - - - - -## Constants - - - - - - -
const EQUAL: u8 = 0;
-
- - - - - - - -
const GREATER_THAN: u8 = 2;
-
- - - - - - - -
const LESS_THAN: u8 = 1;
-
- - - - - -## Function `cmp_bcs_bytes` - -compare vectors v1 and v2 using (1) vector contents from right to left and then -(2) vector length to break ties. -Returns either EQUAL (0u8), LESS_THAN (1u8), or GREATER_THAN (2u8). - -This function is designed to compare BCS (Binary Canonical Serialization)-encoded values -(i.e., vectors produced by bcs::to_bytes). A typical client will call -compare::cmp_bcs_bytes(bcs::to_bytes(&t1), bcs::to_bytes(&t2)). The comparison provides the -following guarantees w.r.t the original values t1 and t2: -- cmp_bcs_bytes(bcs(t1), bcs(t2)) == LESS_THAN iff cmp_bcs_bytes(t2, t1) == GREATER_THAN -- compare::cmp<T>(t1, t2) == EQUAL iff t1 == t2 and (similarly) -compare::cmp<T>(t1, t2) != EQUAL iff t1 != t2, where == and != denote the Move -bytecode operations for polymorphic equality. -- for all primitive types T with < and > comparison operators exposed in Move bytecode -(u8, u16, u32, u64, u128, u256), we have -compare_bcs_bytes(bcs(t1), bcs(t2)) == LESS_THAN iff t1 < t2 and (similarly) -compare_bcs_bytes(bcs(t1), bcs(t2)) == LESS_THAN iff t1 > t2. - -For all other types, the order is whatever the BCS encoding of the type and the comparison -strategy above gives you. One case where the order might be surprising is the address -type. -CoreAddresses are 16 byte hex values that BCS encodes with the identity function. The right -to left, byte-by-byte comparison means that (for example) -compare_bcs_bytes(bcs(0x01), bcs(0x10)) == LESS_THAN (as you'd expect), but -compare_bcs_bytes(bcs(0x100), bcs(0x001)) == LESS_THAN (as you probably wouldn't expect). -Keep this in mind when using this function to compare addresses. - - -
public fun cmp_bcs_bytes(v1: &vector<u8>, v2: &vector<u8>): u8
-
- - - -
-Implementation - - -
public fun cmp_bcs_bytes(v1: &vector<u8>, v2: &vector<u8>): u8 {
-    let i1 = vector::length(v1);
-    let i2 = vector::length(v2);
-    let len_cmp = cmp_u64(i1, i2);
-
-    // BCS uses little endian encoding for all integer types, so we choose to compare from left
-    // to right. Going right to left would make the behavior of compare::cmp diverge from the
-    // bytecode operators < and > on integer values (which would be confusing).
-    while (i1 > 0 && i2 > 0) {
-        i1 = i1 - 1;
-        i2 = i2 - 1;
-        let elem_cmp = cmp_u8(*vector::borrow(v1, i1), *vector::borrow(v2, i2));
-        if (elem_cmp != 0) return elem_cmp
-        // else, compare next element
-    };
-    // all compared elements equal; use length comparion to break the tie
-    len_cmp
-}
-
- - - -
- - - -## Function `cmp_u8` - -Compare two u8's - - -
fun cmp_u8(i1: u8, i2: u8): u8
-
- - - -
-Implementation - - -
fun cmp_u8(i1: u8, i2: u8): u8 {
-    if (i1 == i2) EQUAL
-    else if (i1 < i2) LESS_THAN
-    else GREATER_THAN
-}
-
- - - -
- - - -## Function `cmp_u64` - -Compare two u64's - - -
fun cmp_u64(i1: u64, i2: u64): u8
-
- - - -
-Implementation - - -
fun cmp_u64(i1: u64, i2: u64): u8 {
-    if (i1 == i2) EQUAL
-    else if (i1 < i2) LESS_THAN
-    else GREATER_THAN
-}
-
- - - -
diff --git a/external-crates/move/crates/move-stdlib/nursery/sources/compare.move b/external-crates/move/crates/move-stdlib/nursery/sources/compare.move deleted file mode 100644 index 04238e4c2afe7..0000000000000 --- a/external-crates/move/crates/move-stdlib/nursery/sources/compare.move +++ /dev/null @@ -1,70 +0,0 @@ -/// Utilities for comparing Move values based on their representation in BCS. -module std::compare { - use std::vector; - - // Move does not have signed integers, so we cannot use the usual 0, -1, 1 convention to - // represent EQUAL, LESS_THAN, and GREATER_THAN. Instead, we fun a new convention using u8 - // constants: - const EQUAL: u8 = 0; - const LESS_THAN: u8 = 1; - const GREATER_THAN: u8 = 2; - - /// compare vectors `v1` and `v2` using (1) vector contents from right to left and then - /// (2) vector length to break ties. - /// Returns either `EQUAL` (0u8), `LESS_THAN` (1u8), or `GREATER_THAN` (2u8). - /// - /// This function is designed to compare BCS (Binary Canonical Serialization)-encoded values - /// (i.e., vectors produced by `bcs::to_bytes`). A typical client will call - /// `compare::cmp_bcs_bytes(bcs::to_bytes(&t1), bcs::to_bytes(&t2))`. The comparison provides the - /// following guarantees w.r.t the original values t1 and t2: - /// - `cmp_bcs_bytes(bcs(t1), bcs(t2)) == LESS_THAN` iff `cmp_bcs_bytes(t2, t1) == GREATER_THAN` - /// - `compare::cmp(t1, t2) == EQUAL` iff `t1 == t2` and (similarly) - /// `compare::cmp(t1, t2) != EQUAL` iff `t1 != t2`, where `==` and `!=` denote the Move - /// bytecode operations for polymorphic equality. - /// - for all primitive types `T` with `<` and `>` comparison operators exposed in Move bytecode - /// (`u8`, `u16`, `u32`, `u64`, `u128`, `u256`), we have - /// `compare_bcs_bytes(bcs(t1), bcs(t2)) == LESS_THAN` iff `t1 < t2` and (similarly) - /// `compare_bcs_bytes(bcs(t1), bcs(t2)) == LESS_THAN` iff `t1 > t2`. - /// - /// For all other types, the order is whatever the BCS encoding of the type and the comparison - /// strategy above gives you. One case where the order might be surprising is the `address` - /// type. - /// CoreAddresses are 16 byte hex values that BCS encodes with the identity function. The right - /// to left, byte-by-byte comparison means that (for example) - /// `compare_bcs_bytes(bcs(0x01), bcs(0x10)) == LESS_THAN` (as you'd expect), but - /// `compare_bcs_bytes(bcs(0x100), bcs(0x001)) == LESS_THAN` (as you probably wouldn't expect). - /// Keep this in mind when using this function to compare addresses. - public fun cmp_bcs_bytes(v1: &vector, v2: &vector): u8 { - let i1 = vector::length(v1); - let i2 = vector::length(v2); - let len_cmp = cmp_u64(i1, i2); - - // BCS uses little endian encoding for all integer types, so we choose to compare from left - // to right. Going right to left would make the behavior of compare::cmp diverge from the - // bytecode operators < and > on integer values (which would be confusing). - while (i1 > 0 && i2 > 0) { - i1 = i1 - 1; - i2 = i2 - 1; - let elem_cmp = cmp_u8(*vector::borrow(v1, i1), *vector::borrow(v2, i2)); - if (elem_cmp != 0) return elem_cmp - // else, compare next element - }; - // all compared elements equal; use length comparion to break the tie - len_cmp - } - - /// Compare two `u8`'s - fun cmp_u8(i1: u8, i2: u8): u8 { - if (i1 == i2) EQUAL - else if (i1 < i2) LESS_THAN - else GREATER_THAN - } - - /// Compare two `u64`'s - fun cmp_u64(i1: u64, i2: u64): u8 { - if (i1 == i2) EQUAL - else if (i1 < i2) LESS_THAN - else GREATER_THAN - } - -} diff --git a/external-crates/move/crates/move-stdlib/nursery/sources/debug.move b/external-crates/move/crates/move-stdlib/nursery/sources/debug.move deleted file mode 100644 index d4a506d10f5dd..0000000000000 --- a/external-crates/move/crates/move-stdlib/nursery/sources/debug.move +++ /dev/null @@ -1,18 +0,0 @@ -/// Module providing debug functionality. -module std::debug { - - /// Pretty-prints any Move value. For a Move struct, includes its field names, their types and their values. - native public fun print(x: &T); - - /// Prints the calling function's stack trace. - native public fun print_stack_trace(); - - #[test_only] - use std::string; - - #[test_only] - /// Utility function for printing a sequence of UTF8 bytes as a string (e.g., `b"Hello"`). - public fun print_string(utf8_bytes: vector) { - print(&string::utf8(utf8_bytes)); - } -} diff --git a/external-crates/move/crates/move-stdlib/nursery/tests/compare_tests.move b/external-crates/move/crates/move-stdlib/nursery/tests/compare_tests.move deleted file mode 100644 index 4af198639ffd0..0000000000000 --- a/external-crates/move/crates/move-stdlib/nursery/tests/compare_tests.move +++ /dev/null @@ -1,86 +0,0 @@ -// Tests for polymorphic comparison in Move -#[test_only] -module std::compareTests { - use std::compare; - use std::bcs; - - const EQUAL: u8 = 0; - const LESS_THAN: u8 = 1; - const GREATER_THAN: u8 = 2; - - - #[test] - fun equality_of_simple_types() { - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&true), &bcs::to_bytes(&true)) == EQUAL, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1u8), &bcs::to_bytes(&1u8)) == EQUAL, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1u16), &bcs::to_bytes(&1u16)) == EQUAL, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1u32), &bcs::to_bytes(&1u32)) == EQUAL, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1), &bcs::to_bytes(&1)) == EQUAL, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1u128), &bcs::to_bytes(&1u128)) == EQUAL, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1u256), &bcs::to_bytes(&1u256)) == EQUAL, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&@0x1), &bcs::to_bytes(&@0x1)) == EQUAL, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&x"01"), &bcs::to_bytes(&x"01")) == EQUAL, 0); - } - - #[test] - fun inequality_of_simple_types() { - // inequality of simple types - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&true), &bcs::to_bytes(&false)) != EQUAL, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1u8), &bcs::to_bytes(&0u8)) != EQUAL, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1u16), &bcs::to_bytes(&0u16)) != EQUAL, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1u32), &bcs::to_bytes(&0u32)) != EQUAL, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1), &bcs::to_bytes(&0)) != EQUAL, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1u128), &bcs::to_bytes(&0u128)) != EQUAL, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1u256), &bcs::to_bytes(&0u256)) != EQUAL, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&@0x1), &bcs::to_bytes(&@0x0)) != EQUAL, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&x"01"), &bcs::to_bytes(&x"00")) != EQUAL, 0); - } - - #[test] - fun less_than_with_natural_ordering() { - // less than for types with a natural ordering exposed via bytecode operations - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&false), &bcs::to_bytes(&true)) == LESS_THAN, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&0u8), &bcs::to_bytes(&1u8)) == LESS_THAN, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&0u16), &bcs::to_bytes(&1u16)) == LESS_THAN, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&0u32), &bcs::to_bytes(&1u32)) == LESS_THAN, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&0), &bcs::to_bytes(&1)) == LESS_THAN, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&0u128), &bcs::to_bytes(&1u128)) == LESS_THAN, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&0u256), &bcs::to_bytes(&1u256)) == LESS_THAN, 0); - } - - #[test] - fun less_than_without_natural_ordering() { - // less then for types without a natural ordering exposed by bytecode operations - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&@0x0), &bcs::to_bytes(&@0x1)) == LESS_THAN, 0); // sensible - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&@0x01), &bcs::to_bytes(&@0x10)) == LESS_THAN, 0); // sensible - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&@0x100), &bcs::to_bytes(&@0x001)) == LESS_THAN, 0); // potentially confusing - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&x"00"), &bcs::to_bytes(&x"01")) == LESS_THAN, 0); // sensible - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&x"01"), &bcs::to_bytes(&x"10")) == LESS_THAN, 0); // sensible - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&x"0000"), &bcs::to_bytes(&x"01")) == LESS_THAN, 0); // - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&x"0100"), &bcs::to_bytes(&x"0001")) == LESS_THAN, 0); // potentially confusing - } - - #[test] - fun greater_than_with_natural_ordering() { - // greater than for types with a natural ordering exposed by bytecode operations - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&true), &bcs::to_bytes(&false)) == GREATER_THAN, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1u8), &bcs::to_bytes(&0u8)) == GREATER_THAN, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1u16), &bcs::to_bytes(&0u16)) == GREATER_THAN, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1u32), &bcs::to_bytes(&0u32)) == GREATER_THAN, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1), &bcs::to_bytes(&0)) == GREATER_THAN, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1u128), &bcs::to_bytes(&0u128)) == GREATER_THAN, 0); - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&1u256), &bcs::to_bytes(&0u256)) == GREATER_THAN, 0); - } - - #[test] - fun greater_than_without_natural_ordering() { - // greater than for types without a natural ordering exposed by by bytecode operations - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&@0x1), &bcs::to_bytes(&@0x0)) == GREATER_THAN, 0); // sensible - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&@0x10), &bcs::to_bytes(&@0x01)) == GREATER_THAN, 0); // sensible - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&@0x001), &bcs::to_bytes(&@0x100)) == GREATER_THAN, 0); // potentially confusing - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&x"01"), &bcs::to_bytes(&x"00")) == GREATER_THAN, 0); // sensible - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&x"10"), &bcs::to_bytes(&x"01")) == GREATER_THAN, 0); // sensible - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&x"01"), &bcs::to_bytes(&x"0000")) == GREATER_THAN, 0); // sensible - assert!(compare::cmp_bcs_bytes(&bcs::to_bytes(&x"0001"), &bcs::to_bytes(&x"0100")) == GREATER_THAN, 0); // potentially confusing - } -} diff --git a/external-crates/move/crates/move-stdlib/sources/address.move b/external-crates/move/crates/move-stdlib/sources/address.move new file mode 100644 index 0000000000000..ec1416b4473bf --- /dev/null +++ b/external-crates/move/crates/move-stdlib/sources/address.move @@ -0,0 +1,12 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +/// Provides a way to get address length since it's a +/// platform-specific parameter. +module std::address { + /// Should be converted to a native function. + /// Current implementation only works for Sui. + public fun length(): u64 { + 32 + } +} diff --git a/external-crates/move/crates/move-stdlib/sources/ascii.move b/external-crates/move/crates/move-stdlib/sources/ascii.move index 95e05588b4be4..60564b49893a1 100644 --- a/external-crates/move/crates/move-stdlib/sources/ascii.move +++ b/external-crates/move/crates/move-stdlib/sources/ascii.move @@ -1,107 +1,166 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + /// The `ASCII` module defines basic string and char newtypes in Move that verify /// that characters are valid ASCII, and that strings consist of only valid ASCII characters. module std::ascii { - use std::vector; - use std::option::{Self, Option}; + // Allows calling `.to_string()` to convert an `ascii::String` into as `string::String` + public use fun std::string::from_ascii as String.to_string; /// An invalid ASCII character was encountered when creating an ASCII string. - const EINVALID_ASCII_CHARACTER: u64 = 0x10000; - - /// The `String` struct holds a vector of bytes that all represent - /// valid ASCII characters. Note that these ASCII characters may not all - /// be printable. To determine if a `String` contains only "printable" - /// characters you should use the `all_characters_printable` predicate - /// defined in this module. - struct String has copy, drop, store { - bytes: vector, - } - - /// An ASCII character. - struct Char has copy, drop, store { - byte: u8, - } + const EInvalidASCIICharacter: u64 = 0x10000; + /// An invalid index was encountered when creating a substring. + const EInvalidIndex: u64 = 0x10001; + + /// The `String` struct holds a vector of bytes that all represent + /// valid ASCII characters. Note that these ASCII characters may not all + /// be printable. To determine if a `String` contains only "printable" + /// characters you should use the `all_characters_printable` predicate + /// defined in this module. + public struct String has copy, drop, store { + bytes: vector, + } + + /// An ASCII character. + public struct Char has copy, drop, store { + byte: u8, + } /// Convert a `byte` into a `Char` that is checked to make sure it is valid ASCII. public fun char(byte: u8): Char { - assert!(is_valid_char(byte), EINVALID_ASCII_CHARACTER); + assert!(is_valid_char(byte), EInvalidASCIICharacter); Char { byte } } /// Convert a vector of bytes `bytes` into an `String`. Aborts if /// `bytes` contains non-ASCII characters. public fun string(bytes: vector): String { - let x = try_string(bytes); - assert!( - option::is_some(&x), - EINVALID_ASCII_CHARACTER - ); - option::destroy_some(x) + let x = try_string(bytes); + assert!(x.is_some(), EInvalidASCIICharacter); + x.destroy_some() } /// Convert a vector of bytes `bytes` into an `String`. Returns /// `Some()` if the `bytes` contains all valid ASCII /// characters. Otherwise returns `None`. public fun try_string(bytes: vector): Option { - let len = vector::length(&bytes); - let i = 0; - while (i < len) { - let possible_byte = *vector::borrow(&bytes, i); - if (!is_valid_char(possible_byte)) return option::none(); - i = i + 1; - }; - option::some(String { bytes }) + let is_valid = bytes.all!(|byte| is_valid_char(*byte)); + if (is_valid) option::some(String { bytes }) + else option::none() } /// Returns `true` if all characters in `string` are printable characters /// Returns `false` otherwise. Not all `String`s are printable strings. public fun all_characters_printable(string: &String): bool { - let len = vector::length(&string.bytes); - let i = 0; - while (i < len) { - let byte = *vector::borrow(&string.bytes, i); - if (!is_printable_char(byte)) return false; - i = i + 1; - }; - true + string.bytes.all!(|byte| is_printable_char(*byte)) } + /// Push a `Char` to the end of the `string`. public fun push_char(string: &mut String, char: Char) { - vector::push_back(&mut string.bytes, char.byte); + string.bytes.push_back(char.byte); } + /// Pop a `Char` from the end of the `string`. public fun pop_char(string: &mut String): Char { - Char { byte: vector::pop_back(&mut string.bytes) } + Char { byte: string.bytes.pop_back() } } + /// Returns the length of the `string` in bytes. public fun length(string: &String): u64 { - vector::length(as_bytes(string)) + string.as_bytes().length() + } + + /// Append the `other` string to the end of `string`. + public fun append(string: &mut String, other: String) { + string.bytes.append(other.into_bytes()) + } + + /// Insert the `other` string at the `at` index of `string`. + public fun insert(s: &mut String, at: u64, o: String) { + assert!(at <= s.length(), EInvalidIndex); + o.into_bytes().destroy!(|e| s.bytes.insert(e, at)); + } + + /// Copy the slice of the `string` from `i` to `j` into a new `String`. + public fun substring(string: &String, i: u64, j: u64): String { + assert!(i <= j && j <= string.length(), EInvalidIndex); + let mut bytes = vector[]; + i.range_do!(j, |i| bytes.push_back(string.bytes[i])); + String { bytes } } /// Get the inner bytes of the `string` as a reference public fun as_bytes(string: &String): &vector { - &string.bytes + &string.bytes } /// Unpack the `string` to get its backing bytes public fun into_bytes(string: String): vector { - let String { bytes } = string; - bytes + let String { bytes } = string; + bytes } - /// Unpack the `char` into its underlying byte. + /// Unpack the `char` into its underlying bytes. public fun byte(char: Char): u8 { - let Char { byte } = char; - byte + let Char { byte } = char; + byte } - /// Returns `true` if `b` is a valid ASCII character. Returns `false` otherwise. + /// Returns `true` if `b` is a valid ASCII character. + /// Returns `false` otherwise. public fun is_valid_char(b: u8): bool { - b <= 0x7F + b <= 0x7F } - /// Returns `true` if `byte` is an printable ASCII character. Returns `false` otherwise. + /// Returns `true` if `byte` is an printable ASCII character. + /// Returns `false` otherwise. public fun is_printable_char(byte: u8): bool { - byte >= 0x20 && // Disallow metacharacters - byte <= 0x7E // Don't allow DEL metacharacter + byte >= 0x20 && // Disallow metacharacters + byte <= 0x7E // Don't allow DEL metacharacter + } + + /// Returns `true` if `string` is empty. + public fun is_empty(string: &String): bool { + string.bytes.is_empty() + } + + /// Convert a `string` to its uppercase equivalent. + public fun to_uppercase(string: &String): String { + let bytes = string.as_bytes().map_ref!(|byte| char_to_uppercase(*byte)); + String { bytes } + } + + /// Convert a `string` to its lowercase equivalent. + public fun to_lowercase(string: &String): String { + let bytes = string.as_bytes().map_ref!(|byte| char_to_lowercase(*byte)); + String { bytes } + } + + /// Computes the index of the first occurrence of the `substr` in the `string`. + /// Returns the length of the `string` if the `substr` is not found. + /// Returns 0 if the `substr` is empty. + public fun index_of(string: &String, substr: &String): u64 { + let mut i = 0; + let (n, m) = (string.length(), substr.length()); + if (n < m) return n; + while (i <= n - m) { + let mut j = 0; + while (j < m && string.bytes[i + j] == substr.bytes[j]) j = j + 1; + if (j == m) return i; + i = i + 1; + }; + n + } + + /// Convert a `char` to its lowercase equivalent. + fun char_to_uppercase(byte: u8): u8 { + if (byte >= 0x61 && byte <= 0x7A) byte - 0x20 + else byte + } + + /// Convert a `char` to its lowercase equivalent. + fun char_to_lowercase(byte: u8): u8 { + if (byte >= 0x41 && byte <= 0x5A) byte + 0x20 + else byte } } diff --git a/external-crates/move/crates/move-stdlib/sources/bcs.move b/external-crates/move/crates/move-stdlib/sources/bcs.move index edac0cff2f19e..8e07273cf1ee5 100644 --- a/external-crates/move/crates/move-stdlib/sources/bcs.move +++ b/external-crates/move/crates/move-stdlib/sources/bcs.move @@ -1,3 +1,6 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + /// Utility for converting a Move value to its binary representation in BCS (Binary Canonical /// Serialization). BCS is the binary encoding for Move resources and other non-module values /// published on-chain. See https://github.com/diem/bcs#binary-canonical-serialization-bcs for more diff --git a/external-crates/move/crates/move-stdlib/sources/bit_vector.move b/external-crates/move/crates/move-stdlib/sources/bit_vector.move index ddf3b99b3fda1..354b72c492872 100644 --- a/external-crates/move/crates/move-stdlib/sources/bit_vector.move +++ b/external-crates/move/crates/move-stdlib/sources/bit_vector.move @@ -1,17 +1,18 @@ -module std::bit_vector { - use std::vector; +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 +module std::bit_vector { /// The provided index is out of bounds const EINDEX: u64 = 0x20000; /// An invalid length of bitvector was given const ELENGTH: u64 = 0x20001; - #[test_only] + #[allow(unused_const)] const WORD_SIZE: u64 = 1; /// The maximum allowed bitvector size const MAX_SIZE: u64 = 1024; - struct BitVector has copy, drop, store { + public struct BitVector has copy, drop, store { length: u64, bit_field: vector, } @@ -19,10 +20,10 @@ module std::bit_vector { public fun new(length: u64): BitVector { assert!(length > 0, ELENGTH); assert!(length < MAX_SIZE, ELENGTH); - let counter = 0; - let bit_field = vector::empty(); + let mut counter = 0; + let mut bit_field = vector::empty(); while (counter < length) { - vector::push_back(&mut bit_field, false); + bit_field.push_back(false); counter = counter + 1; }; @@ -34,15 +35,15 @@ module std::bit_vector { /// Set the bit at `bit_index` in the `bitvector` regardless of its previous state. public fun set(bitvector: &mut BitVector, bit_index: u64) { - assert!(bit_index < vector::length(&bitvector.bit_field), EINDEX); - let x = vector::borrow_mut(&mut bitvector.bit_field, bit_index); + assert!(bit_index < bitvector.bit_field.length(), EINDEX); + let x = &mut bitvector.bit_field[bit_index]; *x = true; } /// Unset the bit at `bit_index` in the `bitvector` regardless of its previous state. public fun unset(bitvector: &mut BitVector, bit_index: u64) { - assert!(bit_index < vector::length(&bitvector.bit_field), EINDEX); - let x = vector::borrow_mut(&mut bitvector.bit_field, bit_index); + assert!(bit_index < bitvector.bit_field.length(), EINDEX); + let x = &mut bitvector.bit_field[bit_index]; *x = false; } @@ -50,19 +51,19 @@ module std::bit_vector { /// bitvector's length the bitvector will be zeroed out. public fun shift_left(bitvector: &mut BitVector, amount: u64) { if (amount >= bitvector.length) { - let len = vector::length(&bitvector.bit_field); - let i = 0; + let len = bitvector.bit_field.length(); + let mut i = 0; while (i < len) { - let elem = vector::borrow_mut(&mut bitvector.bit_field, i); + let elem = &mut bitvector.bit_field[i]; *elem = false; i = i + 1; }; } else { - let i = amount; + let mut i = amount; while (i < bitvector.length) { - if (is_index_set(bitvector, i)) set(bitvector, i - amount) - else unset(bitvector, i - amount); + if (bitvector.is_index_set(i)) bitvector.set(i - amount) + else bitvector.unset(i - amount); i = i + 1; }; @@ -78,13 +79,13 @@ module std::bit_vector { /// Return the value of the bit at `bit_index` in the `bitvector`. `true` /// represents "1" and `false` represents a 0 public fun is_index_set(bitvector: &BitVector, bit_index: u64): bool { - assert!(bit_index < vector::length(&bitvector.bit_field), EINDEX); - *vector::borrow(&bitvector.bit_field, bit_index) + assert!(bit_index < bitvector.bit_field.length(), EINDEX); + bitvector.bit_field[bit_index] } /// Return the length (number of usable bits) of this bitvector public fun length(bitvector: &BitVector): u64 { - vector::length(&bitvector.bit_field) + bitvector.bit_field.length() } /// Returns the length of the longest sequence of set bits starting at (and @@ -92,11 +93,11 @@ module std::bit_vector { /// sequence, then `0` is returned. public fun longest_set_sequence_starting_at(bitvector: &BitVector, start_index: u64): u64 { assert!(start_index < bitvector.length, EINDEX); - let index = start_index; + let mut index = start_index; // Find the greatest index in the vector such that all indices less than it are set. while (index < bitvector.length) { - if (!is_index_set(bitvector, index)) break; + if (!bitvector.is_index_set(index)) break; index = index + 1; }; diff --git a/external-crates/move/crates/move-stdlib/sources/debug.move b/external-crates/move/crates/move-stdlib/sources/debug.move new file mode 100644 index 0000000000000..dc9d236a8d07d --- /dev/null +++ b/external-crates/move/crates/move-stdlib/sources/debug.move @@ -0,0 +1,9 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +/// Module providing debug functionality. +module std::debug { + native public fun print(x: &T); + + native public fun print_stack_trace(); +} diff --git a/external-crates/move/crates/move-stdlib/sources/error.move b/external-crates/move/crates/move-stdlib/sources/error.move deleted file mode 100644 index 81958a6914eb4..0000000000000 --- a/external-crates/move/crates/move-stdlib/sources/error.move +++ /dev/null @@ -1,82 +0,0 @@ -/// This module defines a set of canonical error codes which are optional to use by applications for the -/// `abort` and `assert!` features. -/// -/// Canonical error codes use the 3 lowest bytes of the u64 abort code range (the upper 5 bytes are free for other use). -/// Of those, the highest byte represents the *error category* and the lower two bytes the *error reason*. -/// Given an error category `0x1` and a reason `0x3`, a canonical abort code looks as `0x10003`. -/// -/// A module can use a canonical code with a constant declaration of the following form: -/// -/// ``` -/// /// An invalid ASCII character was encountered when creating a string. -/// const EINVALID_CHARACTER: u64 = 0x010003; -/// ``` -/// -/// This code is both valid in the worlds with and without canonical errors. It can be used as a plain module local -/// error reason understand by the existing error map tooling, or as a canonical code. -/// -/// The actual canonical categories have been adopted from Google's canonical error codes, which in turn are derived -/// from Unix error codes [see here](https://cloud.google.com/apis/design/errors#handling_errors). Each code has an -/// associated HTTP error code which can be used in REST apis. The mapping from error code to http code is not 1:1; -/// error codes here are a bit richer than HTTP codes. -module std::error { - - /// Caller specified an invalid argument (http: 400) - const INVALID_ARGUMENT: u64 = 0x1; - - /// An input or result of a computation is out of range (http: 400) - const OUT_OF_RANGE: u64 = 0x2; - - /// The system is not in a state where the operation can be performed (http: 400) - const INVALID_STATE: u64 = 0x3; - - /// Request not authenticated due to missing, invalid, or expired auth token (http: 401) - const UNAUTHENTICATED: u64 = 0x4; - - /// client does not have sufficient permission (http: 403) - const PERMISSION_DENIED: u64 = 0x5; - - /// A specified resource is not found (http: 404) - const NOT_FOUND: u64 = 0x6; - - /// Concurrency conflict, such as read-modify-write conflict (http: 409) - const ABORTED: u64 = 0x7; - - /// The resource that a client tried to create already exists (http: 409) - const ALREADY_EXISTS: u64 = 0x8; - - /// Out of gas or other forms of quota (http: 429) - const RESOURCE_EXHAUSTED: u64 = 0x9; - - #[allow(unused_const)] - /// Request cancelled by the client (http: 499) - const CANCELLED: u64 = 0xA; - - /// Internal error (http: 500) - const INTERNAL: u64 = 0xB; - - /// Feature not implemented (http: 501) - const NOT_IMPLEMENTED: u64 = 0xC; - - /// The service is currently unavailable. Indicates that a retry could solve the issue (http: 503) - const UNAVAILABLE: u64 = 0xD; - - /// Construct a canonical error code from a category and a reason. - public fun canonical(category: u64, reason: u64): u64 { - (category << 16) + reason - } - - /// Functions to construct a canonical error code of the given category. - public fun invalid_argument(r: u64): u64 { canonical(INVALID_ARGUMENT, r) } - public fun out_of_range(r: u64): u64 { canonical(OUT_OF_RANGE, r) } - public fun invalid_state(r: u64): u64 { canonical(INVALID_STATE, r) } - public fun unauthenticated(r: u64): u64 { canonical(UNAUTHENTICATED, r) } - public fun permission_denied(r: u64): u64 { canonical(PERMISSION_DENIED, r) } - public fun not_found(r: u64): u64 { canonical(NOT_FOUND, r) } - public fun aborted(r: u64): u64 { canonical(ABORTED, r) } - public fun already_exists(r: u64): u64 { canonical(ALREADY_EXISTS, r) } - public fun resource_exhausted(r: u64): u64 { canonical(RESOURCE_EXHAUSTED, r) } - public fun internal(r: u64): u64 { canonical(INTERNAL, r) } - public fun not_implemented(r: u64): u64 { canonical(NOT_IMPLEMENTED, r) } - public fun unavailable(r: u64): u64 { canonical(UNAVAILABLE, r) } -} diff --git a/external-crates/move/crates/move-stdlib/sources/fixed_point32.move b/external-crates/move/crates/move-stdlib/sources/fixed_point32.move index 40cee62aeaefd..d25eb58ed3b1c 100644 --- a/external-crates/move/crates/move-stdlib/sources/fixed_point32.move +++ b/external-crates/move/crates/move-stdlib/sources/fixed_point32.move @@ -1,3 +1,6 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + /// Defines a fixed-point numeric type with a 32-bit integer part and /// a 32-bit fractional part. @@ -12,7 +15,7 @@ module std::fixed_point32 { /// floating-point has less than 16 decimal digits of precision, so /// be careful about using floating-point to convert these values to /// decimal. - struct FixedPoint32 has copy, drop, store { value: u64 } + public struct FixedPoint32 has copy, drop, store { value: u64 } ///> TODO: This is a basic constant and should be provided somewhere centrally in the framework. const MAX_U64: u128 = 18446744073709551615; @@ -35,13 +38,13 @@ module std::fixed_point32 { // The product of two 64 bit values has 128 bits, so perform the // multiplication with u128 types and keep the full 128 bit product // to avoid losing accuracy. - let unscaled_product = (val as u128) * (multiplier.value as u128); + let unscaled_product = val as u128 * (multiplier.value as u128); // The unscaled product has 32 fractional bits (from the multiplier) // so rescale it by shifting away the low bits. let product = unscaled_product >> 32; // Check whether the value is too large. assert!(product <= MAX_U64, EMULTIPLICATION); - (product as u64) + product as u64 } /// Divide a u64 integer by a fixed-point number, truncating any @@ -52,13 +55,13 @@ module std::fixed_point32 { assert!(divisor.value != 0, EDIVISION_BY_ZERO); // First convert to 128 bits and then shift left to // add 32 fractional zero bits to the dividend. - let scaled_value = (val as u128) << 32; + let scaled_value = val as u128 << 32; let quotient = scaled_value / (divisor.value as u128); // Check whether the value is too large. assert!(quotient <= MAX_U64, EDIVISION); // the value may be too large, which will cause the cast to fail // with an arithmetic error. - (quotient as u64) + quotient as u64 } /// Create a fixed-point value from a rational number specified by its @@ -76,15 +79,15 @@ module std::fixed_point32 { // Scale the numerator to have 64 fractional bits and the denominator // to have 32 fractional bits, so that the quotient will have 32 // fractional bits. - let scaled_numerator = (numerator as u128) << 64; - let scaled_denominator = (denominator as u128) << 32; + let scaled_numerator = numerator as u128 << 64; + let scaled_denominator = denominator as u128 << 32; assert!(scaled_denominator != 0, EDENOMINATOR); let quotient = scaled_numerator / scaled_denominator; assert!(quotient != 0 || numerator == 0, ERATIO_OUT_OF_RANGE); // Return the quotient as a fixed-point number. We first need to check whether the cast // can succeed. assert!(quotient <= MAX_U64, ERATIO_OUT_OF_RANGE); - FixedPoint32 { value: (quotient as u64) } + FixedPoint32 { value: quotient as u64 } } /// Create a fixedpoint value from a raw value. @@ -103,55 +106,4 @@ module std::fixed_point32 { public fun is_zero(num: FixedPoint32): bool { num.value == 0 } - - /// Returns the smaller of the two FixedPoint32 numbers. - public fun min(num1: FixedPoint32, num2: FixedPoint32): FixedPoint32 { - if (num1.value < num2.value) { - num1 - } else { - num2 - } - } - - /// Returns the larger of the two FixedPoint32 numbers. - public fun max(num1: FixedPoint32, num2: FixedPoint32): FixedPoint32 { - if (num1.value > num2.value) { - num1 - } else { - num2 - } - } - - /// Create a fixedpoint value from a u64 value. - public fun create_from_u64(val: u64): FixedPoint32 { - let value = (val as u128) << 32; - assert!(value <= MAX_U64, ERATIO_OUT_OF_RANGE); - FixedPoint32{value: (value as u64)} - } - - /// Returns the largest integer less than or equal to a given number. - public fun floor(num: FixedPoint32): u64 { - num.value >> 32 - } - - /// Rounds up the given FixedPoint32 to the next largest integer. - public fun ceil(num: FixedPoint32): u64 { - let floored_num = floor(num) << 32; - if (num.value == floored_num) { - return floored_num >> 32 - }; - let val = ((floored_num as u128) + (1 << 32)); - (val >> 32 as u64) - } - - /// Returns the value of a FixedPoint32 to the nearest integer. - public fun round(num: FixedPoint32): u64 { - let floored_num = floor(num) << 32; - let boundary = floored_num + ((1 << 32) / 2); - if (num.value < boundary) { - floored_num >> 32 - } else { - ceil(num) - } - } } diff --git a/external-crates/move/crates/move-stdlib/sources/hash.move b/external-crates/move/crates/move-stdlib/sources/hash.move index daadc4e815770..ed84f18a9a7fc 100644 --- a/external-crates/move/crates/move-stdlib/sources/hash.move +++ b/external-crates/move/crates/move-stdlib/sources/hash.move @@ -1,3 +1,6 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + /// Module which defines SHA hashes for byte vectors. /// /// The functions in this module are natively declared both in the Move runtime diff --git a/external-crates/move/crates/move-stdlib/sources/macros.move b/external-crates/move/crates/move-stdlib/sources/macros.move new file mode 100644 index 0000000000000..53416703f5c9c --- /dev/null +++ b/external-crates/move/crates/move-stdlib/sources/macros.move @@ -0,0 +1,102 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +/// This module holds shared implementation of macros used in `std` +module std::macros { + public macro fun num_max($x: _, $y: _): _ { + let x = $x; + let y = $y; + if (x > y) x + else y + } + + public macro fun num_min($x: _, $y: _): _ { + let x = $x; + let y = $y; + if (x < y) x + else y + } + + public macro fun num_diff($x: _, $y: _): _ { + let x = $x; + let y = $y; + if (x > y) x - y + else y - x + } + + public macro fun num_divide_and_round_up($x: _, $y: _): _ { + let x = $x; + let y = $y; + if (x % y == 0) x / y + else x / y + 1 + } + + + public macro fun num_pow($base: _, $exponent: u8): _ { + let mut base = $base; + let mut exponent = $exponent; + let mut res = 1; + while (exponent >= 1) { + if (exponent % 2 == 0) { + base = base * base; + exponent = exponent / 2; + } else { + res = res * base; + exponent = exponent - 1; + } + }; + + res + } + + public macro fun num_sqrt<$T, $U>($x: $T, $bitsize: u8): $T { + let x = $x; + let mut bit = (1: $U) << $bitsize; + let mut res = (0: $U); + let mut x = x as $U; + + while (bit != 0) { + if (x >= res + bit) { + x = x - (res + bit); + res = (res >> 1) + bit; + } else { + res = res >> 1; + }; + bit = bit >> 2; + }; + + res as $T + } + + public macro fun range_do($start: _, $stop: _, $f: |_|) { + let mut i = $start; + let stop = $stop; + while (i < stop) { + $f(i); + i = i + 1; + } + } + + public macro fun range_do_eq($start: _, $stop: _, $f: |_|) { + let mut i = $start; + let stop = $stop; + // we check `i >= stop` inside the loop instead of `i <= stop` as `while` condition to avoid + // incrementing `i` past the MAX integer value. + // Because of this, we need to check if `i > stop` and return early--instead of letting the + // loop bound handle it, like in the `range_do` macro. + if (i > stop) return; + loop { + $f(i); + if (i >= stop) break; + i = i + 1; + } + } + + public macro fun do($stop: _, $f: |_|) { + range_do!(0, $stop, $f) + } + + public macro fun do_eq($stop: _, $f: |_|) { + range_do_eq!(0, $stop, $f) + } +} diff --git a/external-crates/move/crates/move-stdlib/sources/option.move b/external-crates/move/crates/move-stdlib/sources/option.move index 1de18126613bc..b0f1862c91ba5 100644 --- a/external-crates/move/crates/move-stdlib/sources/option.move +++ b/external-crates/move/crates/move-stdlib/sources/option.move @@ -1,10 +1,11 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + /// This module defines the Option type and its methods to represent and handle an optional value. module std::option { - use std::vector; - /// Abstraction of a value that may or may not be present. Implemented with a vector of size /// zero or one because Move bytecode does not have ADTs. - struct Option has copy, drop, store { + public struct Option has copy, drop, store { vec: vector } @@ -27,33 +28,33 @@ module std::option { /// Return true if `t` does not hold a value public fun is_none(t: &Option): bool { - vector::is_empty(&t.vec) + t.vec.is_empty() } /// Return true if `t` holds a value public fun is_some(t: &Option): bool { - !vector::is_empty(&t.vec) + !t.vec.is_empty() } /// Return true if the value in `t` is equal to `e_ref` /// Always returns `false` if `t` does not hold a value public fun contains(t: &Option, e_ref: &Element): bool { - vector::contains(&t.vec, e_ref) + t.vec.contains(e_ref) } /// Return an immutable reference to the value inside `t` /// Aborts if `t` does not hold a value public fun borrow(t: &Option): &Element { - assert!(is_some(t), EOPTION_NOT_SET); - vector::borrow(&t.vec, 0) + assert!(t.is_some(), EOPTION_NOT_SET); + &t.vec[0] } /// Return a reference to the value inside `t` if it holds one /// Return `default_ref` if `t` does not hold a value public fun borrow_with_default(t: &Option, default_ref: &Element): &Element { let vec_ref = &t.vec; - if (vector::is_empty(vec_ref)) default_ref - else vector::borrow(vec_ref, 0) + if (vec_ref.is_empty()) default_ref + else &vec_ref[0] } /// Return the value inside `t` if it holds one @@ -63,39 +64,39 @@ module std::option { default: Element, ): Element { let vec_ref = &t.vec; - if (vector::is_empty(vec_ref)) default - else *vector::borrow(vec_ref, 0) + if (vec_ref.is_empty()) default + else vec_ref[0] } /// Convert the none option `t` to a some option by adding `e`. /// Aborts if `t` already holds a value public fun fill(t: &mut Option, e: Element) { let vec_ref = &mut t.vec; - if (vector::is_empty(vec_ref)) vector::push_back(vec_ref, e) + if (vec_ref.is_empty()) vec_ref.push_back(e) else abort EOPTION_IS_SET } /// Convert a `some` option to a `none` by removing and returning the value stored inside `t` /// Aborts if `t` does not hold a value public fun extract(t: &mut Option): Element { - assert!(is_some(t), EOPTION_NOT_SET); - vector::pop_back(&mut t.vec) + assert!(t.is_some(), EOPTION_NOT_SET); + t.vec.pop_back() } /// Return a mutable reference to the value inside `t` /// Aborts if `t` does not hold a value public fun borrow_mut(t: &mut Option): &mut Element { - assert!(is_some(t), EOPTION_NOT_SET); - vector::borrow_mut(&mut t.vec, 0) + assert!(t.is_some(), EOPTION_NOT_SET); + &mut t.vec[0] } /// Swap the old value inside `t` with `e` and return the old value /// Aborts if `t` does not hold a value public fun swap(t: &mut Option, e: Element): Element { - assert!(is_some(t), EOPTION_NOT_SET); + assert!(t.is_some(), EOPTION_NOT_SET); let vec_ref = &mut t.vec; - let old_value = vector::pop_back(vec_ref); - vector::push_back(vec_ref, e); + let old_value = vec_ref.pop_back(); + vec_ref.push_back(e); old_value } @@ -104,41 +105,136 @@ module std::option { /// Different from swap(), swap_or_fill() allows for `t` not holding a value. public fun swap_or_fill(t: &mut Option, e: Element): Option { let vec_ref = &mut t.vec; - let old_value = if (vector::is_empty(vec_ref)) none() - else some(vector::pop_back(vec_ref)); - vector::push_back(vec_ref, e); + let old_value = if (vec_ref.is_empty()) none() + else some(vec_ref.pop_back()); + vec_ref.push_back(e); old_value } /// Destroys `t.` If `t` holds a value, return it. Returns `default` otherwise public fun destroy_with_default(t: Option, default: Element): Element { - let Option { vec } = t; - if (vector::is_empty(&vec)) default - else vector::pop_back(&mut vec) + let Option { mut vec } = t; + if (vec.is_empty()) default + else vec.pop_back() } /// Unpack `t` and return its contents /// Aborts if `t` does not hold a value public fun destroy_some(t: Option): Element { - assert!(is_some(&t), EOPTION_NOT_SET); - let Option { vec } = t; - let elem = vector::pop_back(&mut vec); - vector::destroy_empty(vec); + assert!(t.is_some(), EOPTION_NOT_SET); + let Option { mut vec } = t; + let elem = vec.pop_back(); + vec.destroy_empty(); elem } /// Unpack `t` /// Aborts if `t` holds a value public fun destroy_none(t: Option) { - assert!(is_none(&t), EOPTION_IS_SET); + assert!(t.is_none(), EOPTION_IS_SET); let Option { vec } = t; - vector::destroy_empty(vec) + vec.destroy_empty() } - /// Convert `t` into a vector of length 1 if it is `Some`, /// and an empty vector otherwise public fun to_vec(t: Option): vector { let Option { vec } = t; vec } + + // === Macro Functions === + + /// Destroy `Option` and call the closure `f` on the value inside if it holds one. + public macro fun destroy<$T>($o: Option<$T>, $f: |$T|) { + let o = $o; + o.do!($f); + } + + /// Destroy `Option` and call the closure `f` on the value inside if it holds one. + public macro fun do<$T>($o: Option<$T>, $f: |$T|) { + let o = $o; + if (o.is_some()) { + $f(o.destroy_some()); + } + } + + /// Execute a closure on the value inside `t` if it holds one. + public macro fun do_ref<$T>($o: &Option<$T>, $f: |&$T|) { + let o = $o; + if (o.is_some()) { + $f(o.borrow()); + } + } + + /// Execute a closure on the mutable reference to the value inside `t` if it holds one. + public macro fun do_mut<$T>($o: &mut Option<$T>, $f: |&mut $T|) { + let o = $o; + if (o.is_some()) $f(o.borrow_mut()); + } + + /// Select the first `Some` value from the two options, or `None` if both are `None`. + /// Equivalent to Rust's `a.or(b)`. + public macro fun or<$T>($o: Option<$T>, $default: Option<$T>): Option<$T> { + let o = $o; + if (o.is_some()) o + else $default + } + + /// If the value is `Some`, call the closure `f` on it. Otherwise, return `None`. + /// Equivalent to Rust's `t.and_then(f)`. + public macro fun and<$T, $U>($o: Option<$T>, $f: |$T| -> Option<$U>): Option<$U> { + let o = $o; + if (o.is_some()) $f(o.extract()) + else none() + } + + /// If the value is `Some`, call the closure `f` on it. Otherwise, return `None`. + /// Equivalent to Rust's `t.and_then(f)`. + public macro fun and_ref<$T, $U>($o: &Option<$T>, $f: |&$T| -> Option<$U>): Option<$U> { + let o = $o; + if (o.is_some()) $f(o.borrow()) + else none() + } + + /// Map an `Option` to `Option` by applying a function to a contained value. + /// Equivalent to Rust's `t.map(f)`. + public macro fun map<$T, $U>($o: Option<$T>, $f: |$T| -> $U): Option<$U> { + let mut o = $o; + if (o.is_some()) some($f(o.extract())) + else none() + } + + /// Map an `Option` value to `Option` by applying a function to a contained value by reference. + /// Original `Option` is preserved. + /// Equivalent to Rust's `t.map(f)`. + public macro fun map_ref<$T, $U>($o: &Option<$T>, $f: |&$T| -> $U): Option<$U> { + let o = $o; + if (o.is_some()) some($f(o.borrow())) + else none() + } + + /// Return `None` if the value is `None`, otherwise return `Option` if the predicate `f` returns true. + public macro fun filter<$T: drop>($o: Option<$T>, $f: |&$T| -> bool): Option<$T> { + let o = $o; + if (o.is_some() && $f(o.borrow())) o + else none() + } + + /// Return `false` if the value is `None`, otherwise return the result of the predicate `f`. + public macro fun is_some_and<$T>($o: &Option<$T>, $f: |&$T| -> bool): bool { + let o = $o; + o.is_some() && $f(o.borrow()) + } + + /// Destroy `Option` and return the value inside if it holds one, or `default` otherwise. + /// Equivalent to Rust's `t.unwrap_or(default)`. + /// + /// Note: this function is a more efficient version of `destroy_with_default`, as it does not + /// evaluate the default value unless necessary. The `destroy_with_default` function should be + /// deprecated in favor of this function. + public macro fun destroy_or<$T>($o: Option<$T>, $default: $T): $T { + let o = $o; + if (o.is_some()) o.destroy_some() + else $default + } } diff --git a/external-crates/move/crates/move-stdlib/sources/signer.move b/external-crates/move/crates/move-stdlib/sources/signer.move deleted file mode 100644 index 55376dd3fff7e..0000000000000 --- a/external-crates/move/crates/move-stdlib/sources/signer.move +++ /dev/null @@ -1,15 +0,0 @@ -module std::signer { - // Borrows the address of the signer - // Conceptually, you can think of the `signer` as being a struct wrapper arround an - // address - // ``` - // struct signer has drop { addr: address } - // ``` - // `borrow_address` borrows this inner field - native public fun borrow_address(s: &signer): &address; - - // Copies the address of the signer - public fun address_of(s: &signer): address { - *borrow_address(s) - } -} diff --git a/external-crates/move/crates/move-stdlib/sources/string.move b/external-crates/move/crates/move-stdlib/sources/string.move index fa9f6b61b7967..0939b2cbe45f3 100644 --- a/external-crates/move/crates/move-stdlib/sources/string.move +++ b/external-crates/move/crates/move-stdlib/sources/string.move @@ -1,101 +1,137 @@ -/// The `string` module defines the `String` type which represents UTF8 encoded strings. +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +/// The `string` module defines the `String` type which represents UTF8 encoded +/// strings. module std::string { - use std::vector; - use std::option::{Self, Option}; + use std::ascii; /// An invalid UTF8 encoding. - const EINVALID_UTF8: u64 = 1; + const EInvalidUTF8: u64 = 1; /// Index out of range. - const EINVALID_INDEX: u64 = 2; + const EInvalidIndex: u64 = 2; - /// A `String` holds a sequence of bytes which is guaranteed to be in utf8 format. - struct String has copy, drop, store { + /// A `String` holds a sequence of bytes which is guaranteed to be in utf8 + /// format. + public struct String has copy, drop, store { bytes: vector, } - /// Creates a new string from a sequence of bytes. Aborts if the bytes do not represent valid utf8. + /// Creates a new string from a sequence of bytes. Aborts if the bytes do + /// not represent valid utf8. public fun utf8(bytes: vector): String { - assert!(internal_check_utf8(&bytes), EINVALID_UTF8); - String{bytes} + assert!(internal_check_utf8(&bytes), EInvalidUTF8); + String { bytes } + } + + /// Convert an ASCII string to a UTF8 string + public fun from_ascii(s: ascii::String): String { + String { bytes: s.into_bytes() } + } + + /// Convert an UTF8 string to an ASCII string. + /// Aborts if `s` is not valid ASCII + public fun to_ascii(s: String): ascii::String { + let String { bytes } = s; + bytes.to_ascii_string() } /// Tries to create a new string from a sequence of bytes. public fun try_utf8(bytes: vector): Option { - if (internal_check_utf8(&bytes)) { - option::some(String{bytes}) - } else { - option::none() - } + if (internal_check_utf8(&bytes)) option::some(String { bytes }) + else option::none() } /// Returns a reference to the underlying byte vector. - public fun bytes(s: &String): &vector { + public fun as_bytes(s: &String): &vector { &s.bytes } + /// Unpack the `string` to get its underlying bytes. + public fun into_bytes(s: String): vector { + let String { bytes } = s; + bytes + } + /// Checks whether this string is empty. public fun is_empty(s: &String): bool { - vector::is_empty(&s.bytes) + s.bytes.is_empty() } /// Returns the length of this string, in bytes. public fun length(s: &String): u64 { - vector::length(&s.bytes) + s.bytes.length() } /// Appends a string. public fun append(s: &mut String, r: String) { - vector::append(&mut s.bytes, r.bytes) + s.bytes.append(r.bytes) } /// Appends bytes which must be in valid utf8 format. public fun append_utf8(s: &mut String, bytes: vector) { - append(s, utf8(bytes)) + s.append(utf8(bytes)) } - /// Insert the other string at the byte index in given string. The index must be at a valid utf8 char - /// boundary. + /// Insert the other string at the byte index in given string. The index + /// must be at a valid utf8 char boundary. public fun insert(s: &mut String, at: u64, o: String) { let bytes = &s.bytes; - assert!(at <= vector::length(bytes) && internal_is_char_boundary(bytes, at), EINVALID_INDEX); - let l = length(s); - let front = sub_string(s, 0, at); - let end = sub_string(s, at, l); - append(&mut front, o); - append(&mut front, end); + assert!( + at <= bytes.length() && internal_is_char_boundary(bytes, at), + EInvalidIndex, + ); + let l = s.length(); + let mut front = s.substring(0, at); + let end = s.substring(at, l); + front.append(o); + front.append(end); *s = front; } - /// Returns a sub-string using the given byte indices, where `i` is the first byte position and `j` is the start - /// of the first byte not included (or the length of the string). The indices must be at valid utf8 char boundaries, + /// Returns a sub-string using the given byte indices, where `i` is the first + /// byte position and `j` is the start of the first byte not included (or the + /// length of the string). The indices must be at valid utf8 char boundaries, /// guaranteeing that the result is valid utf8. - public fun sub_string(s: &String, i: u64, j: u64): String { + public fun substring(s: &String, i: u64, j: u64): String { let bytes = &s.bytes; - let l = vector::length(bytes); + let l = bytes.length(); assert!( - j <= l && i <= j && internal_is_char_boundary(bytes, i) && internal_is_char_boundary(bytes, j), - EINVALID_INDEX + j <= l && + i <= j && + internal_is_char_boundary(bytes, i) && + internal_is_char_boundary(bytes, j), + EInvalidIndex, ); - String{bytes: internal_sub_string(bytes, i, j)} + String { bytes: internal_sub_string(bytes, i, j) } } - /// Computes the index of the first occurrence of a string. Returns `length(s)` if no occurrence found. + /// Computes the index of the first occurrence of a string. Returns `s.length()` + /// if no occurrence found. public fun index_of(s: &String, r: &String): u64 { internal_index_of(&s.bytes, &r.bytes) } - // Native API + native fun internal_check_utf8(v: &vector): bool; native fun internal_is_char_boundary(v: &vector, i: u64): bool; native fun internal_sub_string(v: &vector, i: u64, j: u64): vector; native fun internal_index_of(v: &vector, r: &vector): u64; - // Test only API for the native function. Don't return a value so other - // tests aren't tempted to use this function. #[test_only] - public fun internal_sub_string_for_testing(v: &vector, i: u64, j: u64) { - internal_sub_string(v, i, j); + public fun internal_sub_string_for_testing(v: &vector, i: u64, j: u64): vector { + internal_sub_string(v, i, j) + } + + // === Deprecated === + + #[deprecated(note = b"Use `std::string::as_bytes` instead.")] + public fun bytes(s: &String): &vector { s.as_bytes() } + + #[deprecated(note = b"Use `std::string::substring` instead.")] + public fun sub_string(s: &String, i: u64, j: u64): String { + s.substring(i, j) } } diff --git a/external-crates/move/crates/move-stdlib/sources/type_name.move b/external-crates/move/crates/move-stdlib/sources/type_name.move index f439a64182b01..70cc4407a8c5c 100644 --- a/external-crates/move/crates/move-stdlib/sources/type_name.move +++ b/external-crates/move/crates/move-stdlib/sources/type_name.move @@ -1,16 +1,39 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + /// Functionality for converting Move types into values. Use with care! module std::type_name { - use std::ascii::String; + use std::ascii::{Self, String}; + use std::address; + + /// ASCII Character code for the `:` (colon) symbol. + const ASCII_COLON: u8 = 58; + + /// ASCII Character code for the `v` (lowercase v) symbol. + const ASCII_V: u8 = 118; + /// ASCII Character code for the `e` (lowercase e) symbol. + const ASCII_E: u8 = 101; + /// ASCII Character code for the `c` (lowercase c) symbol. + const ASCII_C: u8 = 99; + /// ASCII Character code for the `t` (lowercase t) symbol. + const ASCII_T: u8 = 116; + /// ASCII Character code for the `o` (lowercase o) symbol. + const ASCII_O: u8 = 111; + /// ASCII Character code for the `r` (lowercase r) symbol. + const ASCII_R: u8 = 114; + + /// The type is not from a package/module. It is a primitive type. + const ENonModuleType: u64 = 0; - struct TypeName has copy, drop, store { + public struct TypeName has copy, drop, store { /// String representation of the type. All types are represented /// using their source syntax: - /// "u8", "u64", "u128", "bool", "address", "vector", "signer" for ground types. + /// "u8", "u64", "bool", "address", "vector", and so on for primitive types. /// Struct types are represented as fully qualified type names; e.g. /// `00000000000000000000000000000001::string::String` or /// `0000000000000000000000000000000a::module_name1::type_name1<0000000000000000000000000000000a::module_name2::type_name2>` /// Addresses are hex-encoded lowercase values of length ADDRESS_LENGTH (16, 20, or 32 depending on the Move platform) - name: String + name: String, } /// Return a value representation of the type `T`. Package IDs @@ -26,11 +49,77 @@ module std::type_name { /// later upgrade). public native fun get_with_original_ids(): TypeName; + /// Returns true iff the TypeName represents a primitive type, i.e. one of + /// u8, u16, u32, u64, u128, u256, bool, address, vector. + public fun is_primitive(self: &TypeName): bool { + let bytes = self.name.as_bytes(); + bytes == &b"bool" || + bytes == &b"u8" || + bytes == &b"u16" || + bytes == &b"u32" || + bytes == &b"u64" || + bytes == &b"u128" || + bytes == &b"u256" || + bytes == &b"address" || + ( + bytes.length() >= 6 && + bytes[0] == ASCII_V && + bytes[1] == ASCII_E && + bytes[2] == ASCII_C && + bytes[3] == ASCII_T && + bytes[4] == ASCII_O && + bytes[5] == ASCII_R, + ) + } + /// Get the String representation of `self` public fun borrow_string(self: &TypeName): &String { &self.name } + /// Get Address string (Base16 encoded), first part of the TypeName. + /// Aborts if given a primitive type. + public fun get_address(self: &TypeName): String { + assert!(!self.is_primitive(), ENonModuleType); + + // Base16 (string) representation of an address has 2 symbols per byte. + let len = address::length() * 2; + let str_bytes = self.name.as_bytes(); + let mut addr_bytes = vector[]; + let mut i = 0; + + // Read `len` bytes from the type name and push them to addr_bytes. + while (i < len) { + addr_bytes.push_back(str_bytes[i]); + i = i + 1; + }; + + ascii::string(addr_bytes) + } + + /// Get name of the module. + /// Aborts if given a primitive type. + public fun get_module(self: &TypeName): String { + assert!(!self.is_primitive(), ENonModuleType); + + // Starts after address and a double colon: `::` + let mut i = address::length() * 2 + 2; + let str_bytes = self.name.as_bytes(); + let mut module_name = vector[]; + let colon = ASCII_COLON; + loop { + let char = &str_bytes[i]; + if (char != &colon) { + module_name.push_back(*char); + i = i + 1; + } else { + break + } + }; + + ascii::string(module_name) + } + /// Convert `self` into its inner String public fun into_string(self: TypeName): String { self.name diff --git a/external-crates/move/crates/move-stdlib/sources/u128.move b/external-crates/move/crates/move-stdlib/sources/u128.move new file mode 100644 index 0000000000000..947c330085ca1 --- /dev/null +++ b/external-crates/move/crates/move-stdlib/sources/u128.move @@ -0,0 +1,79 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[defines_primitive(u128)] +module std::u128 { + /// Return the larger of `x` and `y` + public fun max(x: u128, y: u128): u128 { + std::macros::num_max!(x, y) + } + + /// Return the smaller of `x` and `y` + public fun min(x: u128, y: u128): u128 { + std::macros::num_min!(x, y) + } + + /// Return the absolute value of x - y + public fun diff(x: u128, y: u128): u128 { + std::macros::num_diff!(x, y) + } + + /// Calculate x / y, but round up the result. + public fun divide_and_round_up(x: u128, y: u128): u128 { + std::macros::num_divide_and_round_up!(x, y) + } + + /// Return the value of a base raised to a power + public fun pow(base: u128, exponent: u8): u128 { + std::macros::num_pow!(base, exponent) + } + + /// Get a nearest lower integer Square Root for `x`. Given that this + /// function can only operate with integers, it is impossible + /// to get perfect (or precise) integer square root for some numbers. + /// + /// Example: + /// ``` + /// math::sqrt(9) => 3 + /// math::sqrt(8) => 2 // the nearest lower square root is 4; + /// ``` + /// + /// In integer math, one of the possible ways to get results with more + /// precision is to use higher values or temporarily multiply the + /// value by some bigger number. Ideally if this is a square of 10 or 100. + /// + /// Example: + /// ``` + /// math::sqrt(8) => 2; + /// math::sqrt(8 * 10000) => 282; + /// // now we can use this value as if it was 2.82; + /// // but to get the actual result, this value needs + /// // to be divided by 100 (because sqrt(10000)). + /// + /// + /// math::sqrt(8 * 1000000) => 2828; // same as above, 2828 / 1000 (2.828) + /// ``` + public fun sqrt(x: u128): u128 { + std::macros::num_sqrt!(x, 128) + } + + /// Loops applying `$f` to each number from `$start` to `$stop` (exclusive) + public macro fun range_do($start: u128, $stop: u128, $f: |u128|) { + std::macros::range_do!($start, $stop, $f) + } + + /// Loops applying `$f` to each number from `$start` to `$stop` (inclusive) + public macro fun range_do_eq($start: u128, $stop: u128, $f: |u128|) { + std::macros::range_do_eq!($start, $stop, $f) + } + + /// Loops applying `$f` to each number from `0` to `$stop` (exclusive) + public macro fun do($stop: u128, $f: |u128|) { + std::macros::do!($stop, $f) + } + + /// Loops applying `$f` to each number from `0` to `$stop` (inclusive) + public macro fun do_eq($stop: u128, $f: |u128|) { + std::macros::do_eq!($stop, $f) + } +} diff --git a/external-crates/move/crates/move-stdlib/sources/u16.move b/external-crates/move/crates/move-stdlib/sources/u16.move new file mode 100644 index 0000000000000..9d051c11721dc --- /dev/null +++ b/external-crates/move/crates/move-stdlib/sources/u16.move @@ -0,0 +1,79 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[defines_primitive(u16)] +module std::u16 { + /// Return the larger of `x` and `y` + public fun max(x: u16, y: u16): u16 { + std::macros::num_max!(x, y) + } + + /// Return the smaller of `x` and `y` + public fun min(x: u16, y: u16): u16 { + std::macros::num_min!(x, y) + } + + /// Return the absolute value of x - y + public fun diff(x: u16, y: u16): u16 { + std::macros::num_diff!(x, y) + } + + /// Calculate x / y, but round up the result. + public fun divide_and_round_up(x: u16, y: u16): u16 { + std::macros::num_divide_and_round_up!(x, y) + } + + /// Return the value of a base raised to a power + public fun pow(base: u16, exponent: u8): u16 { + std::macros::num_pow!(base, exponent) + } + + /// Get a nearest lower integer Square Root for `x`. Given that this + /// function can only operate with integers, it is impossible + /// to get perfect (or precise) integer square root for some numbers. + /// + /// Example: + /// ``` + /// math::sqrt(9) => 3 + /// math::sqrt(8) => 2 // the nearest lower square root is 4; + /// ``` + /// + /// In integer math, one of the possible ways to get results with more + /// precision is to use higher values or temporarily multiply the + /// value by some bigger number. Ideally if this is a square of 10 or 100. + /// + /// Example: + /// ``` + /// math::sqrt(8) => 2; + /// math::sqrt(8 * 10000) => 282; + /// // now we can use this value as if it was 2.82; + /// // but to get the actual result, this value needs + /// // to be divided by 100 (because sqrt(10000)). + /// + /// + /// math::sqrt(8 * 1000000) => 2828; // same as above, 2828 / 1000 (2.828) + /// ``` + public fun sqrt(x: u16): u16 { + std::macros::num_sqrt!(x, 16) + } + + /// Loops applying `$f` to each number from `$start` to `$stop` (exclusive) + public macro fun range_do($start: u16, $stop: u16, $f: |u16|) { + std::macros::range_do!($start, $stop, $f) + } + + /// Loops applying `$f` to each number from `$start` to `$stop` (inclusive) + public macro fun range_do_eq($start: u16, $stop: u16, $f: |u16|) { + std::macros::range_do_eq!($start, $stop, $f) + } + + /// Loops applying `$f` to each number from `0` to `$stop` (exclusive) + public macro fun do($stop: u16, $f: |u16|) { + std::macros::do!($stop, $f) + } + + /// Loops applying `$f` to each number from `0` to `$stop` (inclusive) + public macro fun do_eq($stop: u16, $f: |u16|) { + std::macros::do_eq!($stop, $f) + } +} diff --git a/external-crates/move/crates/move-stdlib/sources/u256.move b/external-crates/move/crates/move-stdlib/sources/u256.move new file mode 100644 index 0000000000000..1c1846db661d7 --- /dev/null +++ b/external-crates/move/crates/move-stdlib/sources/u256.move @@ -0,0 +1,50 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[defines_primitive(u256)] +module std::u256 { + /// Return the larger of `x` and `y` + public fun max(x: u256, y: u256): u256 { + std::macros::num_max!(x, y) + } + + /// Return the smaller of `x` and `y` + public fun min(x: u256, y: u256): u256 { + std::macros::num_min!(x, y) + } + + /// Return the absolute value of x - y + public fun diff(x: u256, y: u256): u256 { + std::macros::num_diff!(x, y) + } + + /// Calculate x / y, but round up the result. + public fun divide_and_round_up(x: u256, y: u256): u256 { + std::macros::num_divide_and_round_up!(x, y) + } + + /// Return the value of a base raised to a power + public fun pow(base: u256, exponent: u8): u256 { + std::macros::num_pow!(base, exponent) + } + + /// Loops applying `$f` to each number from `$start` to `$stop` (exclusive) + public macro fun range_do($start: u256, $stop: u256, $f: |u256|) { + std::macros::range_do!($start, $stop, $f) + } + + /// Loops applying `$f` to each number from `$start` to `$stop` (inclusive) + public macro fun range_do_eq($start: u256, $stop: u256, $f: |u256|) { + std::macros::range_do_eq!($start, $stop, $f) + } + + /// Loops applying `$f` to each number from `0` to `$stop` (exclusive) + public macro fun do($stop: u256, $f: |u256|) { + std::macros::do!($stop, $f) + } + + /// Loops applying `$f` to each number from `0` to `$stop` (inclusive) + public macro fun do_eq($stop: u256, $f: |u256|) { + std::macros::do_eq!($stop, $f) + } +} diff --git a/external-crates/move/crates/move-stdlib/sources/u32.move b/external-crates/move/crates/move-stdlib/sources/u32.move new file mode 100644 index 0000000000000..8ad44d722c178 --- /dev/null +++ b/external-crates/move/crates/move-stdlib/sources/u32.move @@ -0,0 +1,79 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[defines_primitive(u32)] +module std::u32 { + /// Return the larger of `x` and `y` + public fun max(x: u32, y: u32): u32 { + std::macros::num_max!(x, y) + } + + /// Return the smaller of `x` and `y` + public fun min(x: u32, y: u32): u32 { + std::macros::num_min!(x, y) + } + + /// Return the absolute value of x - y + public fun diff(x: u32, y: u32): u32 { + std::macros::num_diff!(x, y) + } + + /// Calculate x / y, but round up the result. + public fun divide_and_round_up(x: u32, y: u32): u32 { + std::macros::num_divide_and_round_up!(x, y) + } + + /// Return the value of a base raised to a power + public fun pow(base: u32, exponent: u8): u32 { + std::macros::num_pow!(base, exponent) + } + + /// Get a nearest lower integer Square Root for `x`. Given that this + /// function can only operate with integers, it is impossible + /// to get perfect (or precise) integer square root for some numbers. + /// + /// Example: + /// ``` + /// math::sqrt(9) => 3 + /// math::sqrt(8) => 2 // the nearest lower square root is 4; + /// ``` + /// + /// In integer math, one of the possible ways to get results with more + /// precision is to use higher values or temporarily multiply the + /// value by some bigger number. Ideally if this is a square of 10 or 100. + /// + /// Example: + /// ``` + /// math::sqrt(8) => 2; + /// math::sqrt(8 * 10000) => 282; + /// // now we can use this value as if it was 2.82; + /// // but to get the actual result, this value needs + /// // to be divided by 100 (because sqrt(10000)). + /// + /// + /// math::sqrt(8 * 1000000) => 2828; // same as above, 2828 / 1000 (2.828) + /// ``` + public fun sqrt(x: u32): u32 { + std::macros::num_sqrt!(x, 32) + } + + /// Loops applying `$f` to each number from `$start` to `$stop` (exclusive) + public macro fun range_do($start: u32, $stop: u32, $f: |u32|) { + std::macros::range_do!($start, $stop, $f) + } + + /// Loops applying `$f` to each number from `$start` to `$stop` (inclusive) + public macro fun range_do_eq($start: u32, $stop: u32, $f: |u32|) { + std::macros::range_do_eq!($start, $stop, $f) + } + + /// Loops applying `$f` to each number from `0` to `$stop` (exclusive) + public macro fun do($stop: u32, $f: |u32|) { + std::macros::do!($stop, $f) + } + + /// Loops applying `$f` to each number from `0` to `$stop` (inclusive) + public macro fun do_eq($stop: u32, $f: |u32|) { + std::macros::do_eq!($stop, $f) + } +} diff --git a/external-crates/move/crates/move-stdlib/sources/u64.move b/external-crates/move/crates/move-stdlib/sources/u64.move new file mode 100644 index 0000000000000..9963dcc1b8206 --- /dev/null +++ b/external-crates/move/crates/move-stdlib/sources/u64.move @@ -0,0 +1,79 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[defines_primitive(u64)] +module std::u64 { + /// Return the larger of `x` and `y` + public fun max(x: u64, y: u64): u64 { + std::macros::num_max!(x, y) + } + + /// Return the smaller of `x` and `y` + public fun min(x: u64, y: u64): u64 { + std::macros::num_min!(x, y) + } + + /// Return the absolute value of x - y + public fun diff(x: u64, y: u64): u64 { + std::macros::num_diff!(x, y) + } + + /// Calculate x / y, but round up the result. + public fun divide_and_round_up(x: u64, y: u64): u64 { + std::macros::num_divide_and_round_up!(x, y) + } + + /// Return the value of a base raised to a power + public fun pow(base: u64, exponent: u8): u64 { + std::macros::num_pow!(base, exponent) + } + + /// Get a nearest lower integer Square Root for `x`. Given that this + /// function can only operate with integers, it is impossible + /// to get perfect (or precise) integer square root for some numbers. + /// + /// Example: + /// ``` + /// math::sqrt(9) => 3 + /// math::sqrt(8) => 2 // the nearest lower square root is 4; + /// ``` + /// + /// In integer math, one of the possible ways to get results with more + /// precision is to use higher values or temporarily multiply the + /// value by some bigger number. Ideally if this is a square of 10 or 100. + /// + /// Example: + /// ``` + /// math::sqrt(8) => 2; + /// math::sqrt(8 * 10000) => 282; + /// // now we can use this value as if it was 2.82; + /// // but to get the actual result, this value needs + /// // to be divided by 100 (because sqrt(10000)). + /// + /// + /// math::sqrt(8 * 1000000) => 2828; // same as above, 2828 / 1000 (2.828) + /// ``` + public fun sqrt(x: u64): u64 { + std::macros::num_sqrt!(x, 64) + } + + /// Loops applying `$f` to each number from `$start` to `$stop` (exclusive) + public macro fun range_do($start: u64, $stop: u64, $f: |u64|) { + std::macros::range_do!($start, $stop, $f) + } + + /// Loops applying `$f` to each number from `$start` to `$stop` (inclusive) + public macro fun range_do_eq($start: u64, $stop: u64, $f: |u64|) { + std::macros::range_do_eq!($start, $stop, $f) + } + + /// Loops applying `$f` to each number from `0` to `$stop` (exclusive) + public macro fun do($stop: u64, $f: |u64|) { + std::macros::do!($stop, $f) + } + + /// Loops applying `$f` to each number from `0` to `$stop` (inclusive) + public macro fun do_eq($stop: u64, $f: |u64|) { + std::macros::do_eq!($stop, $f) + } +} diff --git a/external-crates/move/crates/move-stdlib/sources/u8.move b/external-crates/move/crates/move-stdlib/sources/u8.move new file mode 100644 index 0000000000000..4eaca05a94345 --- /dev/null +++ b/external-crates/move/crates/move-stdlib/sources/u8.move @@ -0,0 +1,79 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[defines_primitive(u8)] +module std::u8 { + /// Return the larger of `x` and `y` + public fun max(x: u8, y: u8): u8 { + std::macros::num_max!(x, y) + } + + /// Return the smaller of `x` and `y` + public fun min(x: u8, y: u8): u8 { + std::macros::num_min!(x, y) + } + + /// Return the absolute value of x - y + public fun diff(x: u8, y: u8): u8 { + std::macros::num_diff!(x, y) + } + + /// Calculate x / y, but round up the result. + public fun divide_and_round_up(x: u8, y: u8): u8 { + std::macros::num_divide_and_round_up!(x, y) + } + + /// Return the value of a base raised to a power + public fun pow(base: u8, exponent: u8): u8 { + std::macros::num_pow!(base, exponent) + } + + /// Get a nearest lower integer Square Root for `x`. Given that this + /// function can only operate with integers, it is impossible + /// to get perfect (or precise) integer square root for some numbers. + /// + /// Example: + /// ``` + /// math::sqrt(9) => 3 + /// math::sqrt(8) => 2 // the nearest lower square root is 4; + /// ``` + /// + /// In integer math, one of the possible ways to get results with more + /// precision is to use higher values or temporarily multiply the + /// value by some bigger number. Ideally if this is a square of 10 or 100. + /// + /// Example: + /// ``` + /// math::sqrt(8) => 2; + /// math::sqrt(8 * 10000) => 282; + /// // now we can use this value as if it was 2.82; + /// // but to get the actual result, this value needs + /// // to be divided by 100 (because sqrt(10000)). + /// + /// + /// math::sqrt(8 * 1000000) => 2828; // same as above, 2828 / 1000 (2.828) + /// ``` + public fun sqrt(x: u8): u8 { + std::macros::num_sqrt!(x, 8) + } + + /// Loops applying `$f` to each number from `$start` to `$stop` (exclusive) + public macro fun range_do($start: u8, $stop: u8, $f: |u8|) { + std::macros::range_do!($start, $stop, $f) + } + + /// Loops applying `$f` to each number from `$start` to `$stop` (inclusive) + public macro fun range_do_eq($start: u8, $stop: u8, $f: |u8|) { + std::macros::range_do_eq!($start, $stop, $f) + } + + /// Loops applying `$f` to each number from `0` to `$stop` (exclusive) + public macro fun do($stop: u8, $f: |u8|) { + std::macros::do!($stop, $f) + } + + /// Loops applying `$f` to each number from `0` to `$stop` (inclusive) + public macro fun do_eq($stop: u8, $f: |u8|) { + std::macros::do_eq!($stop, $f) + } +} diff --git a/external-crates/move/crates/move-stdlib/sources/unit_test.move b/external-crates/move/crates/move-stdlib/sources/unit_test.move index 28526857c8521..1deb6a70c0529 100644 --- a/external-crates/move/crates/move-stdlib/sources/unit_test.move +++ b/external-crates/move/crates/move-stdlib/sources/unit_test.move @@ -1,3 +1,6 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + #[test_only] /// Module providing testing functionality. Only included for tests. module std::unit_test { @@ -9,4 +12,23 @@ module std::unit_test { /// This will cause a linking failure if an attempt is made to publish a /// test module in a VM that isn't in unit test mode. native public fun poison(); + + public macro fun assert_eq<$T: drop>($t1: $T, $t2: $T) { + let t1 = $t1; + let t2 = $t2; + assert_ref_eq!(&t1, &t2) + } + + public macro fun assert_ref_eq<$T>($t1: &$T, $t2: &$T) { + let t1 = $t1; + let t2 = $t2; + let res = t1 == t2; + if (!res) { + std::debug::print(&b"Assertion failed:"); + std::debug::print(t1); + std::debug::print(&b"!="); + std::debug::print(t2); + assert!(false); + } + } } diff --git a/external-crates/move/crates/move-stdlib/sources/vector.move b/external-crates/move/crates/move-stdlib/sources/vector.move index 1eb179ba683c1..cd98ed0ae7955 100644 --- a/external-crates/move/crates/move-stdlib/sources/vector.move +++ b/external-crates/move/crates/move-stdlib/sources/vector.move @@ -1,88 +1,106 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + #[defines_primitive(vector)] /// A variable-sized container that can hold any type. Indexing is 0-based, and /// vectors are growable. This module has many native functions. module std::vector { + /// Allows calling `.to_string()` on a vector of `u8` to get a utf8 `String`. + public use fun std::string::utf8 as vector.to_string; + + /// Allows calling `.try_to_string()` on a vector of `u8` to get a utf8 `String`. + /// This will return `None` if the vector is not valid utf8. + public use fun std::string::try_utf8 as vector.try_to_string; + + /// Allows calling `.to_ascii_string()` on a vector of `u8` to get an `ascii::String`. + public use fun std::ascii::string as vector.to_ascii_string; + + /// Allows calling `.try_to_ascii_string()` on a vector of `u8` to get an + /// `ascii::String`. This will return `None` if the vector is not valid ascii. + public use fun std::ascii::try_string as vector.try_to_ascii_string; /// The index into the vector is out of bounds const EINDEX_OUT_OF_BOUNDS: u64 = 0x20000; #[bytecode_instruction] /// Create an empty vector. - native public fun empty(): vector; + public native fun empty(): vector; #[bytecode_instruction] /// Return the length of the vector. - native public fun length(v: &vector): u64; + public native fun length(v: &vector): u64; + #[syntax(index)] #[bytecode_instruction] /// Acquire an immutable reference to the `i`th element of the vector `v`. /// Aborts if `i` is out of bounds. - native public fun borrow(v: &vector, i: u64): ∈ + public native fun borrow(v: &vector, i: u64): ∈ #[bytecode_instruction] /// Add element `e` to the end of the vector `v`. - native public fun push_back(v: &mut vector, e: Element); + public native fun push_back(v: &mut vector, e: Element); + #[syntax(index)] #[bytecode_instruction] /// Return a mutable reference to the `i`th element in the vector `v`. /// Aborts if `i` is out of bounds. - native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; + public native fun borrow_mut(v: &mut vector, i: u64): &mut Element; #[bytecode_instruction] /// Pop an element from the end of vector `v`. /// Aborts if `v` is empty. - native public fun pop_back(v: &mut vector): Element; + public native fun pop_back(v: &mut vector): Element; #[bytecode_instruction] /// Destroy the vector `v`. /// Aborts if `v` is not empty. - native public fun destroy_empty(v: vector); + public native fun destroy_empty(v: vector); #[bytecode_instruction] /// Swaps the elements at the `i`th and `j`th indices in the vector `v`. /// Aborts if `i` or `j` is out of bounds. - native public fun swap(v: &mut vector, i: u64, j: u64); + public native fun swap(v: &mut vector, i: u64, j: u64); /// Return an vector of size one containing element `e`. public fun singleton(e: Element): vector { - let v = empty(); - push_back(&mut v, e); + let mut v = empty(); + v.push_back(e); v } /// Reverses the order of the elements in the vector `v` in place. public fun reverse(v: &mut vector) { - let len = length(v); + let len = v.length(); if (len == 0) return (); - let front_index = 0; - let back_index = len -1; + let mut front_index = 0; + let mut back_index = len - 1; while (front_index < back_index) { - swap(v, front_index, back_index); + v.swap(front_index, back_index); front_index = front_index + 1; back_index = back_index - 1; } } /// Pushes all of the elements of the `other` vector into the `lhs` vector. - public fun append(lhs: &mut vector, other: vector) { - reverse(&mut other); - while (!is_empty(&other)) push_back(lhs, pop_back(&mut other)); - destroy_empty(other); + public fun append(lhs: &mut vector, mut other: vector) { + other.reverse(); + while (!other.is_empty()) lhs.push_back(other.pop_back()); + other.destroy_empty(); } /// Return `true` if the vector `v` has no elements and `false` otherwise. public fun is_empty(v: &vector): bool { - length(v) == 0 + v.length() == 0 } /// Return true if `e` is in the vector `v`. /// Otherwise, returns false. public fun contains(v: &vector, e: &Element): bool { - let i = 0; - let len = length(v); + let mut i = 0; + let len = v.length(); while (i < len) { - if (borrow(v, i) == e) return true; + if (&v[i] == e) return true; i = i + 1; }; false @@ -91,10 +109,10 @@ module std::vector { /// Return `(true, i)` if `e` is in the vector `v` at index `i`. /// Otherwise, returns `(false, 0)`. public fun index_of(v: &vector, e: &Element): (bool, u64) { - let i = 0; - let len = length(v); + let mut i = 0; + let len = v.length(); while (i < len) { - if (borrow(v, i) == e) return (true, i); + if (&v[i] == e) return (true, i); i = i + 1; }; (false, 0) @@ -103,29 +121,29 @@ module std::vector { /// Remove the `i`th element of the vector `v`, shifting all subsequent elements. /// This is O(n) and preserves ordering of elements in the vector. /// Aborts if `i` is out of bounds. - public fun remove(v: &mut vector, i: u64): Element { - let len = length(v); + public fun remove(v: &mut vector, mut i: u64): Element { + let mut len = v.length(); // i out of bounds; abort if (i >= len) abort EINDEX_OUT_OF_BOUNDS; len = len - 1; - while (i < len) swap(v, i, { i = i + 1; i }); - pop_back(v) + while (i < len) v.swap(i, { i = i + 1; i }); + v.pop_back() } /// Insert `e` at position `i` in the vector `v`. /// If `i` is in bounds, this shifts the old `v[i]` and all subsequent elements to the right. - /// If `i == length(v)`, this adds `e` to the end of the vector. + /// If `i == v.length()`, this adds `e` to the end of the vector. /// This is O(n) and preserves ordering of elements in the vector. - /// Aborts if `i > length(v)` - public fun insert(v: &mut vector, e: Element, i: u64) { - let len = length(v); + /// Aborts if `i > v.length()` + public fun insert(v: &mut vector, e: Element, mut i: u64) { + let len = v.length(); // i too big abort if (i > len) abort EINDEX_OUT_OF_BOUNDS; - push_back(v, e); + v.push_back(e); while (i < len) { - swap(v, i, len); + v.swap(i, len); i = i + 1 } } @@ -134,9 +152,213 @@ module std::vector { /// This is O(1), but does not preserve ordering of elements in the vector. /// Aborts if `i` is out of bounds. public fun swap_remove(v: &mut vector, i: u64): Element { - assert!(!is_empty(v), EINDEX_OUT_OF_BOUNDS); - let last_idx = length(v) - 1; - swap(v, i, last_idx); - pop_back(v) + assert!(!v.is_empty(), EINDEX_OUT_OF_BOUNDS); + let last_idx = v.length() - 1; + v.swap(i, last_idx); + v.pop_back() + } + + // === Macros === + + /// Create a vector of length `n` by calling the function `f` on each index. + public macro fun tabulate<$T>($n: u64, $f: |u64| -> $T): vector<$T> { + let mut v = vector[]; + let n = $n; + n.do!(|i| v.push_back($f(i))); + v + } + + /// Destroy the vector `v` by calling `f` on each element and then destroying the vector. + /// Does not preserve the order of elements in the vector (starts from the end of the vector). + public macro fun destroy<$T>($v: vector<$T>, $f: |$T|) { + let mut v = $v; + while (!v.is_empty()) $f(v.pop_back()); + v.destroy_empty(); + } + + /// Destroy the vector `v` by calling `f` on each element and then destroying the vector. + /// Preserves the order of elements in the vector. + public macro fun do<$T>($v: vector<$T>, $f: |$T|) { + let mut v = $v; + v.reverse(); + while (!v.is_empty()) $f(v.pop_back()); + v.destroy_empty(); + } + + /// Perform an action `f` on each element of the vector `v`. The vector is not modified. + public macro fun do_ref<$T>($v: &vector<$T>, $f: |&$T|) { + let v = $v; + v.length().do!(|i| $f(&v[i])) + } + + /// Perform an action `f` on each element of the vector `v`. + /// The function `f` takes a mutable reference to the element. + public macro fun do_mut<$T>($v: &mut vector<$T>, $f: |&mut $T|) { + let v = $v; + v.length().do!(|i| $f(&mut v[i])) + } + + /// Map the vector `v` to a new vector by applying the function `f` to each element. + /// Preserves the order of elements in the vector, first is called first. + public macro fun map<$T, $U>($v: vector<$T>, $f: |$T| -> $U): vector<$U> { + let v = $v; + let mut r = vector[]; + v.do!(|e| r.push_back($f(e))); + r + } + + /// Map the vector `v` to a new vector by applying the function `f` to each element. + /// Preserves the order of elements in the vector, first is called first. + public macro fun map_ref<$T, $U>($v: &vector<$T>, $f: |&$T| -> $U): vector<$U> { + let v = $v; + let mut r = vector[]; + v.do_ref!(|e| r.push_back($f(e))); + r + } + + /// Filter the vector `v` by applying the function `f` to each element. + /// Return a new vector containing only the elements for which `f` returns `true`. + public macro fun filter<$T: drop>($v: vector<$T>, $f: |&$T| -> bool): vector<$T> { + let v = $v; + let mut r = vector[]; + v.do!(|e| if ($f(&e)) r.push_back(e)); + r + } + + /// Split the vector `v` into two vectors by applying the function `f` to each element. + /// Return a tuple containing two vectors: the first containing the elements for which `f` returns `true`, + /// and the second containing the elements for which `f` returns `false`. + public macro fun partition<$T>($v: vector<$T>, $f: |&$T| -> bool): (vector<$T>, vector<$T>) { + let v = $v; + let mut r1 = vector[]; + let mut r2 = vector[]; + v.do!(|e| if ($f(&e)) r1.push_back(e) else r2.push_back(e)); + (r1, r2) + } + + /// Finds the index of first element in the vector `v` that satisfies the predicate `f`. + /// Returns `some(index)` if such an element is found, otherwise `none()`. + public macro fun find_index<$T>($v: vector<$T>, $f: |&$T| -> bool): Option { + let v = $v; + 'find_index: { + v.length().do!(|i| if ($f(&v[i])) return 'find_index option::some(i)); + option::none() + } + } + + /// Count how many elements in the vector `v` satisfy the predicate `f`. + public macro fun count<$T>($v: &vector<$T>, $f: |&$T| -> bool): u64 { + let v = $v; + let mut count = 0; + v.do_ref!(|e| if ($f(e)) count = count + 1); + count + } + + /// Reduce the vector `v` to a single value by applying the function `f` to each element. + /// Similar to `fold_left` in Rust and `reduce` in Python and JavaScript. + public macro fun fold<$T, $Acc>($v: vector<$T>, $init: $Acc, $f: |$Acc, $T| -> $Acc): $Acc { + let v = $v; + let mut acc = $init; + v.do!(|e| acc = $f(acc, e)); + acc + } + + /// Whether any element in the vector `v` satisfies the predicate `f`. + /// If the vector is empty, returns `false`. + public macro fun any<$T>($v: &vector<$T>, $f: |&$T| -> bool): bool { + let v = $v; + 'any: { + v.do_ref!(|e| if ($f(e)) return 'any true); + false + } + } + + /// Whether all elements in the vector `v` satisfy the predicate `f`. + /// If the vector is empty, returns `true`. + public macro fun all<$T>($v: &vector<$T>, $f: |&$T| -> bool): bool { + let v = $v; + 'all: { + v.do_ref!(|e| if (!$f(e)) return 'all false); + true + } + } + + /// Destroys two vectors `v1` and `v2` by calling `f` to each pair of elements. + /// Aborts if the vectors are not of the same length. + /// The order of elements in the vectors is preserved. + public macro fun zip_do<$T1, $T2>($v1: vector<$T1>, $v2: vector<$T2>, $f: |$T1, $T2|) { + let v1 = $v1; + let mut v2 = $v2; + v2.reverse(); + let len = v1.length(); + assert!(len == v2.length()); + v1.do!(|el1| $f(el1, v2.pop_back())); + } + + /// Destroys two vectors `v1` and `v2` by calling `f` to each pair of elements. + /// Aborts if the vectors are not of the same length. + /// Starts from the end of the vectors. + public macro fun zip_do_reverse<$T1, $T2>($v1: vector<$T1>, $v2: vector<$T2>, $f: |$T1, $T2|) { + let v1 = $v1; + let mut v2 = $v2; + let len = v1.length(); + assert!(len == v2.length()); + v1.destroy!(|el1| $f(el1, v2.pop_back())); + } + + /// Iterate through `v1` and `v2` and apply the function `f` to references of each pair of + /// elements. The vectors are not modified. + /// Aborts if the vectors are not of the same length. + /// The order of elements in the vectors is preserved. + public macro fun zip_do_ref<$T1, $T2>($v1: &vector<$T1>, $v2: &vector<$T2>, $f: |&$T1, &$T2|) { + let v1 = $v1; + let v2 = $v2; + let len = v1.length(); + assert!(len == v2.length()); + len.do!(|i| $f(&v1[i], &v2[i])); + } + + /// Iterate through `v1` and `v2` and apply the function `f` to mutable references of each pair + /// of elements. The vectors may be modified. + /// Aborts if the vectors are not of the same length. + /// The order of elements in the vectors is preserved. + public macro fun zip_do_mut<$T1, $T2>( + $v1: &mut vector<$T1>, + $v2: &mut vector<$T2>, + $f: |&mut $T1, &mut $T2|, + ) { + let v1 = $v1; + let v2 = $v2; + let len = v1.length(); + assert!(len == v2.length()); + len.do!(|i| $f(&mut v1[i], &mut v2[i])); + } + + /// Destroys two vectors `v1` and `v2` by applying the function `f` to each pair of elements. + /// The returned values are collected into a new vector. + /// Aborts if the vectors are not of the same length. + /// The order of elements in the vectors is preserved. + public macro fun zip_map<$T1, $T2, $U>( + $v1: vector<$T1>, + $v2: vector<$T2>, + $f: |$T1, $T2| -> $U, + ): vector<$U> { + let mut r = vector[]; + zip_do!($v1, $v2, |el1, el2| r.push_back($f(el1, el2))); + r + } + + /// Iterate through `v1` and `v2` and apply the function `f` to references of each pair of + /// elements. The returned values are collected into a new vector. + /// Aborts if the vectors are not of the same length. + /// The order of elements in the vectors is preserved. + public macro fun zip_map_ref<$T1, $T2, $U>( + $v1: &vector<$T1>, + $v2: &vector<$T2>, + $f: |&$T1, &$T2| -> $U, + ): vector<$U> { + let mut r = vector[]; + zip_do_ref!($v1, $v2, |el1, el2| r.push_back($f(el1, el2))); + r } } diff --git a/external-crates/move/crates/move-stdlib/src/lib.rs b/external-crates/move/crates/move-stdlib/src/lib.rs index 45c5c87b8b7d2..5d714e3d3acd1 100644 --- a/external-crates/move/crates/move-stdlib/src/lib.rs +++ b/external-crates/move/crates/move-stdlib/src/lib.rs @@ -14,9 +14,7 @@ mod tests; pub mod utils; const MODULES_DIR: &str = "sources"; -const NURSERY_DIR: &str = "nursery"; const DOCS_DIR: &str = "docs"; -const NURSERY_DOCS_DIR: &str = "nursery/docs"; const REFERENCES_TEMPLATE: &str = "doc_templates/references.md"; const OVERVIEW_TEMPLATE: &str = "doc_templates/overview.md"; @@ -45,20 +43,11 @@ pub fn move_stdlib_docs_full_path() -> String { format!("{}/{}", env!("CARGO_MANIFEST_DIR"), DOCS_DIR) } -pub fn move_nursery_docs_full_path() -> String { - format!("{}/{}", env!("CARGO_MANIFEST_DIR"), NURSERY_DOCS_DIR) -} - pub fn move_stdlib_files() -> Vec { let path = path_in_crate(MODULES_DIR); find_filenames(&[path], |p| extension_equals(p, MOVE_EXTENSION)).unwrap() } -pub fn move_nursery_files() -> Vec { - let path = path_in_crate(NURSERY_DIR); - find_filenames(&[path], |p| extension_equals(p, MOVE_EXTENSION)).unwrap() -} - pub fn move_stdlib_named_addresses() -> BTreeMap { let mapping = [("std", "0x1")]; mapping @@ -116,16 +105,3 @@ pub fn build_stdlib_doc(output_path: &str) { move_stdlib_named_addresses(), ) } - -pub fn build_nursery_doc(output_path: &str) { - build_doc( - output_path, - "", - vec![], - None, - move_nursery_files().as_slice(), - vec![move_stdlib_modules_full_path()], - false, - move_stdlib_named_addresses(), - ) -} diff --git a/external-crates/move/crates/move-stdlib/src/main.rs b/external-crates/move/crates/move-stdlib/src/main.rs index 03652b782db60..500c40977c5d5 100644 --- a/external-crates/move/crates/move-stdlib/src/main.rs +++ b/external-crates/move/crates/move-stdlib/src/main.rs @@ -12,10 +12,5 @@ fn main() { //std::fs::create_dir_all(&move_stdlib::move_stdlib_docs_full_path()).unwrap(); move_stdlib::build_stdlib_doc(&move_stdlib::move_stdlib_docs_full_path()); }); - - time_it("Generating nursery documentation", || { - std::fs::remove_dir_all(move_stdlib::move_nursery_docs_full_path()).unwrap_or(()); - move_stdlib::build_nursery_doc(&move_stdlib::move_nursery_docs_full_path()); - }); } } diff --git a/external-crates/move/crates/move-stdlib/tests/ascii_tests.move b/external-crates/move/crates/move-stdlib/tests/ascii_tests.move index c455b362b62fa..ec6d4c7fdb817 100644 --- a/external-crates/move/crates/move-stdlib/tests/ascii_tests.move +++ b/external-crates/move/crates/move-stdlib/tests/ascii_tests.move @@ -1,120 +1,222 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + #[test_only] module std::ascii_tests { use std::ascii; - use std::vector; - use std::option; #[test] fun test_ascii_chars() { - let i = 0; + let mut i = 0; let end = 128; - let vec = vector::empty(); + let mut vec = vector[]; while (i < end) { - assert!(ascii::is_valid_char(i), 0); - vector::push_back(&mut vec, i); + assert!(ascii::is_valid_char(i)); + vec.push_back(i); i = i + 1; }; - let str = ascii::string(vec); - assert!(vector::length(ascii::as_bytes(&str)) == 128, 0); - assert!(!ascii::all_characters_printable(&str), 1); - assert!(vector::length(&ascii::into_bytes(str)) == 128, 2); + let str = vec.to_ascii_string(); + assert!(str.as_bytes().length() == 128); + assert!(!str.all_characters_printable()); + assert!(str.into_bytes().length() == 128); } #[test] fun test_ascii_push_chars() { - let i = 0; + let mut i = 0; let end = 128; - let str = ascii::string(vector::empty()); + let mut str = vector[].to_ascii_string(); while (i < end) { - ascii::push_char(&mut str, ascii::char(i)); + str.push_char(ascii::char(i)); i = i + 1; }; - assert!(vector::length(ascii::as_bytes(&str)) == 128, 0); - assert!(ascii::length(&str) == 128, 0); - assert!(!ascii::all_characters_printable(&str), 1); + assert!(str.as_bytes().length() == 128); + assert!(str.length() == 128); + assert!(!str.all_characters_printable()); } #[test] fun test_ascii_push_char_pop_char() { - let i = 0; + let mut i = 0; let end = 128; - let str = ascii::string(vector::empty()); + let mut str = vector[].to_ascii_string(); while (i < end) { - ascii::push_char(&mut str, ascii::char(i)); + str.push_char(ascii::char(i)); i = i + 1; }; while (i > 0) { - let char = ascii::pop_char(&mut str); - assert!(ascii::byte(char) == i - 1, 0); + let char = str.pop_char(); + assert!(ascii::byte(char) == i - 1); i = i - 1; }; - assert!(vector::length(ascii::as_bytes(&str)) == 0, 0); - assert!(ascii::length(&str) == 0, 0); - assert!(ascii::all_characters_printable(&str), 1); + assert!(str.as_bytes().length() == 0); + assert!(str.length() == 0); + assert!(str.all_characters_printable()); } #[test] fun test_printable_chars() { - let i = 0x20; + let mut i = 0x20; let end = 0x7E; - let vec = vector::empty(); + let mut vec = vector[]; while (i <= end) { - assert!(ascii::is_printable_char(i), 0); - vector::push_back(&mut vec, i); + assert!(ascii::is_printable_char(i)); + vec.push_back(i); i = i + 1; }; - let str = ascii::string(vec); - assert!(ascii::all_characters_printable(&str), 0); + let str = vec.to_ascii_string(); + assert!(str.all_characters_printable()); } #[test] fun printable_chars_dont_allow_tab() { - let str = ascii::string(vector::singleton(0x09)); - assert!(!ascii::all_characters_printable(&str), 0); + let str = vector[0x09].to_ascii_string(); + assert!(!str.all_characters_printable()); } #[test] fun printable_chars_dont_allow_newline() { - let str = ascii::string(vector::singleton(0x0A)); - assert!(!ascii::all_characters_printable(&str), 0); + let str = vector[0x0A].to_ascii_string(); + assert!(!str.all_characters_printable()); } #[test] fun test_invalid_ascii_characters() { - let i = 128u8; + let mut i = 128u8; let end = 255u8; while (i < end) { - let try_str = ascii::try_string(vector::singleton(i)); - assert!(option::is_none(&try_str), 0); + let try_str = vector[i].try_to_ascii_string(); + assert!(try_str.is_none()); i = i + 1; }; } #[test] fun test_nonvisible_chars() { - let i = 0; + let mut i = 0; let end = 0x09; while (i < end) { - let str = ascii::string(vector::singleton(i)); - assert!(!ascii::all_characters_printable(&str), 0); + let str = vector[i].to_ascii_string(); + assert!(!str.all_characters_printable()); i = i + 1; }; - let i = 0x0B; + let mut i = 0x0B; let end = 0x0F; while (i <= end) { - let str = ascii::string(vector::singleton(i)); - assert!(!ascii::all_characters_printable(&str), 0); + let str = vector[i].to_ascii_string(); + assert!(!str.all_characters_printable()); i = i + 1; }; } + + #[test] + fun test_append() { + let mut str = b"hello".to_ascii_string(); + str.append(b" world".to_ascii_string()); + + assert!(str == b"hello world".to_ascii_string()); + } + + #[test] + fun test_to_uppercase() { + let str = b"azhello_world_!".to_ascii_string(); + assert!(str.to_uppercase() == b"AZHELLO_WORLD_!".to_ascii_string()); + } + + #[test] + fun test_to_lowercase() { + let str = b"AZHELLO_WORLD_!".to_ascii_string(); + assert!(str.to_lowercase() == b"azhello_world_!".to_ascii_string()); + } + + #[test] + fun test_substring() { + let str = b"hello world".to_ascii_string(); + assert!(str.substring(0, 5) == b"hello".to_ascii_string()); + assert!(str.substring(6, 11) == b"world".to_ascii_string()); + } + + #[test] + fun test_substring_len_one() { + let str = b"hello world".to_ascii_string(); + assert!(str.substring(0, 1) == b"h".to_ascii_string()); + assert!(str.substring(6, 7) == b"w".to_ascii_string()); + } + + #[test] + fun test_substring_len_zero() { + let str = b"hello world".to_ascii_string(); + assert!(str.substring(0, 0).is_empty()); + } + + #[test] + fun test_index_of() { + let str = b"hello world orwell".to_ascii_string(); + assert!(str.index_of(&b"hello".to_ascii_string()) == 0); + assert!(str.index_of(&b"world".to_ascii_string()) == 6); + assert!(str.index_of(&b"o".to_ascii_string()) == 4); + assert!(str.index_of(&b"z".to_ascii_string()) == str.length()); + assert!(str.index_of(&b"o ".to_ascii_string()) == 4); + assert!(str.index_of(&b"or".to_ascii_string()) == 7); + assert!(str.index_of(&b"".to_ascii_string()) == 0); + assert!(str.index_of(&b"orwell".to_ascii_string()) == 12); + assert!( + b"ororwell" + .to_ascii_string() + .index_of(&b"orwell".to_ascii_string()) == 2, + ); + } + + #[test, expected_failure(abort_code = ascii::EInvalidIndex)] + fun test_substring_i_out_of_bounds_fail() { + let str = b"hello world".to_ascii_string(); + str.substring(12, 13); + } + + #[test, expected_failure(abort_code = ascii::EInvalidIndex)] + fun test_substring_j_lt_i_fail() { + let str = b"hello world".to_ascii_string(); + str.substring(9, 8); + } + + #[test, expected_failure(abort_code = ascii::EInvalidIndex)] + fun test_substring_j_out_of_bounds_fail() { + let str = b"hello world".to_ascii_string(); + str.substring(9, 13); + } + + #[test] + fun test_insert() { + let mut str = b"hello".to_ascii_string(); + str.insert(5, b" world".to_ascii_string()); + assert!(str == b"hello world".to_ascii_string()); + + str.insert(5, b" cruel".to_ascii_string()); + assert!(str == b"hello cruel world".to_ascii_string()); + } + + #[test] + fun test_insert_empty() { + let mut str = b"hello".to_ascii_string(); + str.insert(5, b"".to_ascii_string()); + assert!(str == b"hello".to_ascii_string()); + } + + #[test, expected_failure(abort_code = ascii::EInvalidIndex)] + fun test_insert_out_of_bounds_fail() { + let mut str = b"hello".to_ascii_string(); + str.insert(6, b" world".to_ascii_string()); + } } diff --git a/external-crates/move/crates/move-stdlib/tests/bcs_tests.move b/external-crates/move/crates/move-stdlib/tests/bcs_tests.move index c6ca978b397c1..308dd99920289 100644 --- a/external-crates/move/crates/move-stdlib/tests/bcs_tests.move +++ b/external-crates/move/crates/move-stdlib/tests/bcs_tests.move @@ -1,69 +1,74 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + #[test_only] module std::bcs_tests { use std::bcs; - struct Box has copy, drop, store { x: T } - struct Box3 has copy, drop, store { x: Box> } - struct Box7 has copy, drop, store { x: Box3> } - struct Box15 has copy, drop, store { x: Box7> } - struct Box31 has copy, drop, store { x: Box15> } - struct Box63 has copy, drop, store { x: Box31> } - struct Box127 has copy, drop, store { x: Box63> } + public struct Box has copy, drop, store { x: T } + public struct Box3 has copy, drop, store { x: Box> } + public struct Box7 has copy, drop, store { x: Box3> } + public struct Box15 has copy, drop, store { x: Box7> } + public struct Box31 has copy, drop, store { x: Box15> } + public struct Box63 has copy, drop, store { x: Box31> } + public struct Box127 has copy, drop, store { x: Box63> } #[test] fun bcs_address() { - let addr = @0x1234567890abcdef1234567890abcdef89b9f9d1fadc027cf9532d6f99041522; - let expected_output = x"1234567890abcdef1234567890abcdef89b9f9d1fadc027cf9532d6f99041522"; - assert!(bcs::to_bytes(&addr) == expected_output, 0); + let addr = @0x0000000000000000000000000000000089b9f9d1fadc027cf9532d6f99041522; + let expected_output = x"0000000000000000000000000000000089b9f9d1fadc027cf9532d6f99041522"; + assert!(bcs::to_bytes(&addr) == expected_output); } #[test] fun bcs_bool() { let expected_output = x"01"; - assert!(bcs::to_bytes(&true) == expected_output, 0); + assert!(bcs::to_bytes(&true) == expected_output); } #[test] fun bcs_u8() { let expected_output = x"01"; - assert!(bcs::to_bytes(&1u8) == expected_output, 0); + assert!(bcs::to_bytes(&1u8) == expected_output); } #[test] fun bcs_u16() { let expected_output = x"0100"; - assert!(bcs::to_bytes(&1u16) == expected_output, 0); + assert!(bcs::to_bytes(&1u16) == expected_output); } #[test] fun bcs_u32() { let expected_output = x"01000000"; - assert!(bcs::to_bytes(&1u32) == expected_output, 0); + assert!(bcs::to_bytes(&1u32) == expected_output); } #[test] fun bcs_u64() { let expected_output = x"0100000000000000"; - assert!(bcs::to_bytes(&1) == expected_output, 0); + assert!(bcs::to_bytes(&1) == expected_output); } #[test] fun bcs_u128() { let expected_output = x"01000000000000000000000000000000"; - assert!(bcs::to_bytes(&1u128) == expected_output, 0); + assert!(bcs::to_bytes(&1u128) == expected_output); } #[test] fun bcs_u256() { let expected_output = x"0100000000000000000000000000000000000000000000000000000000000000"; - assert!(bcs::to_bytes(&1u256) == expected_output, 0); + assert!(bcs::to_bytes(&1u256) == expected_output); } #[test] fun bcs_vec_u8() { let v = x"0f"; let expected_output = x"010f"; - assert!(bcs::to_bytes(&v) == expected_output, 0); + assert!(bcs::to_bytes(&v) == expected_output); } fun box3(x: T): Box3 { @@ -96,7 +101,8 @@ module std::bcs_tests { } #[test] - #[expected_failure] // VM_MAX_VALUE_DEPTH_REACHED + #[expected_failure] + // failes due to VM max value depth fun encode_129() { bcs::to_bytes(&Box { x: box127(true) }); } diff --git a/external-crates/move/crates/move-stdlib/tests/bit_vector_tests.move b/external-crates/move/crates/move-stdlib/tests/bit_vector_tests.move index 2dfa2e047471a..10a35c0cfa84c 100644 --- a/external-crates/move/crates/move-stdlib/tests/bit_vector_tests.move +++ b/external-crates/move/crates/move-stdlib/tests/bit_vector_tests.move @@ -1,18 +1,23 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + #[test_only] module std::bit_vector_tests { use std::bit_vector; #[test_only] fun test_bitvector_set_unset_of_size(k: u64) { - let bitvector = bit_vector::new(k); - let index = 0; + let mut bitvector = bit_vector::new(k); + let mut index = 0; while (index < k) { - bit_vector::set(&mut bitvector, index); - assert!(bit_vector::is_index_set(&bitvector, index), 0); + bitvector.set(index); + assert!(bitvector.is_index_set(index)); index = index + 1; - let index_to_right = index; + let mut index_to_right = index; while (index_to_right < k) { - assert!(!bit_vector::is_index_set(&bitvector, index_to_right), 1); + assert!(!bitvector.is_index_set(index_to_right)); index_to_right = index_to_right + 1; }; }; @@ -20,12 +25,12 @@ module std::bit_vector_tests { index = 0; while (index < k) { - bit_vector::unset(&mut bitvector, index); - assert!(!bit_vector::is_index_set(&bitvector, index), 0); + bitvector.unset(index); + assert!(!bitvector.is_index_set(index)); index = index + 1; - let index_to_right = index; + let mut index_to_right = index; while (index_to_right < k) { - assert!(bit_vector::is_index_set(&bitvector, index_to_right), 1); + assert!(bitvector.is_index_set(index_to_right)); index_to_right = index_to_right + 1; }; }; @@ -34,22 +39,22 @@ module std::bit_vector_tests { #[test] #[expected_failure(abort_code = bit_vector::EINDEX)] fun set_bit_out_of_bounds() { - let bitvector = bit_vector::new(bit_vector::word_size()); - bit_vector::set(&mut bitvector, bit_vector::word_size()); + let mut bitvector = bit_vector::new(bit_vector::word_size()); + bitvector.set(bit_vector::word_size()); } #[test] #[expected_failure(abort_code = bit_vector::EINDEX)] fun unset_bit_out_of_bounds() { - let bitvector = bit_vector::new(bit_vector::word_size()); - bit_vector::unset(&mut bitvector, bit_vector::word_size()); + let mut bitvector = bit_vector::new(bit_vector::word_size()); + bitvector.unset(bit_vector::word_size()); } #[test] #[expected_failure(abort_code = bit_vector::EINDEX)] fun index_bit_out_of_bounds() { let bitvector = bit_vector::new(bit_vector::word_size()); - bit_vector::is_index_set(&bitvector, bit_vector::word_size()); + bitvector.is_index_set(bit_vector::word_size()); } #[test] @@ -59,71 +64,71 @@ module std::bit_vector_tests { #[test] fun test_set_bit_and_index_odd_size() { - test_bitvector_set_unset_of_size(300) + test_bitvector_set_unset_of_size(140) } #[test] fun longest_sequence_no_set_zero_index() { let bitvector = bit_vector::new(100); - assert!(bit_vector::longest_set_sequence_starting_at(&bitvector, 0) == 0, 0); + assert!(bitvector.longest_set_sequence_starting_at(0) == 0); } #[test] fun longest_sequence_one_set_zero_index() { - let bitvector = bit_vector::new(100); - bit_vector::set(&mut bitvector, 1); - assert!(bit_vector::longest_set_sequence_starting_at(&bitvector, 0) == 0, 0); + let mut bitvector = bit_vector::new(100); + bitvector.set(1); + assert!(bitvector.longest_set_sequence_starting_at(0) == 0); } #[test] fun longest_sequence_no_set_nonzero_index() { let bitvector = bit_vector::new(100); - assert!(bit_vector::longest_set_sequence_starting_at(&bitvector, 51) == 0, 0); + assert!(bitvector.longest_set_sequence_starting_at(51) == 0); } #[test] fun longest_sequence_two_set_nonzero_index() { - let bitvector = bit_vector::new(100); - bit_vector::set(&mut bitvector, 50); - bit_vector::set(&mut bitvector, 52); - assert!(bit_vector::longest_set_sequence_starting_at(&bitvector, 51) == 0, 0); + let mut bitvector = bit_vector::new(100); + bitvector.set(50); + bitvector.set(52); + assert!(bitvector.longest_set_sequence_starting_at(51) == 0); } #[test] fun longest_sequence_with_break() { - let bitvector = bit_vector::new(100); - let i = 0; + let mut bitvector = bit_vector::new(100); + let mut i = 0; while (i < 20) { - bit_vector::set(&mut bitvector, i); + bitvector.set(i); i = i + 1; }; // create a break in the run i = i + 1; while (i < 100) { - bit_vector::set(&mut bitvector, i); + bitvector.set(i); i = i + 1; }; - assert!(bit_vector::longest_set_sequence_starting_at(&bitvector, 0) == 20, 0); - assert!(bit_vector::longest_set_sequence_starting_at(&bitvector, 20) == 0, 0); - assert!(bit_vector::longest_set_sequence_starting_at(&bitvector, 21) == 100 - 21, 0); + assert!(bitvector.longest_set_sequence_starting_at(0) == 20); + assert!(bitvector.longest_set_sequence_starting_at(20) == 0); + assert!(bitvector.longest_set_sequence_starting_at(21) == 100 - 21); } #[test] fun test_shift_left() { - let bitlen = 133; - let bitvector = bit_vector::new(bitlen); + let bitlen = 97; + let mut bitvector = bit_vector::new(bitlen); - let i = 0; + let mut i = 0; while (i < bitlen) { - bit_vector::set(&mut bitvector, i); + bitvector.set(i); i = i + 1; }; i = bitlen - 1; while (i > 0) { - assert!(bit_vector::is_index_set(&bitvector, i), 0); - bit_vector::shift_left(&mut bitvector, 1); - assert!(!bit_vector::is_index_set(&bitvector, i), 1); + assert!(bitvector.is_index_set(i)); + bitvector.shift_left(1); + assert!(!bitvector.is_index_set( i)); i = i - 1; }; } @@ -132,21 +137,21 @@ module std::bit_vector_tests { fun test_shift_left_specific_amount() { let bitlen = 300; let shift_amount = 133; - let bitvector = bit_vector::new(bitlen); + let mut bitvector = bit_vector::new(bitlen); - bit_vector::set(&mut bitvector, 201); - assert!(bit_vector::is_index_set(&bitvector, 201), 0); + bitvector.set(201); + assert!(bitvector.is_index_set(201)); - bit_vector::shift_left(&mut bitvector, shift_amount); - assert!(bit_vector::is_index_set(&bitvector, 201 - shift_amount), 1); - assert!(!bit_vector::is_index_set(&bitvector, 201), 2); + bitvector.shift_left(shift_amount); + assert!(bitvector.is_index_set(201 - shift_amount)); + assert!(!bitvector.is_index_set(201)); // Make sure this shift clears all the bits - bit_vector::shift_left(&mut bitvector, bitlen - 1); + bitvector.shift_left(bitlen - 1); - let i = 0; + let mut i = 0; while (i < bitlen) { - assert!(!bit_vector::is_index_set(&bitvector, i), 3); + assert!(!bitvector.is_index_set(i)); i = i + 1; } } @@ -156,28 +161,28 @@ module std::bit_vector_tests { let bitlen = 50; let chosen_index = 24; let shift_amount = 3; - let bitvector = bit_vector::new(bitlen); + let mut bitvector = bit_vector::new(bitlen); - let i = 0; + let mut i = 0; while (i < bitlen) { - bit_vector::set(&mut bitvector, i); + bitvector.set(i); i = i + 1; }; - bit_vector::unset(&mut bitvector, chosen_index); - assert!(!bit_vector::is_index_set(&bitvector, chosen_index), 0); + bitvector.unset(chosen_index); + assert!(!bitvector.is_index_set(chosen_index)); - bit_vector::shift_left(&mut bitvector, shift_amount); + bitvector.shift_left(shift_amount); i = 0; while (i < bitlen) { // only chosen_index - shift_amount and the remaining bits should be BitVector::unset if ((i == chosen_index - shift_amount) || (i >= bitlen - shift_amount)) { - assert!(!bit_vector::is_index_set(&bitvector, i), 1); + assert!(!bitvector.is_index_set(i)); } else { - assert!(bit_vector::is_index_set(&bitvector, i), 2); + assert!(bitvector.is_index_set(i)); }; i = i + 1; } @@ -186,18 +191,18 @@ module std::bit_vector_tests { #[test] fun shift_left_at_size() { let bitlen = 133; - let bitvector = bit_vector::new(bitlen); + let mut bitvector = bit_vector::new(bitlen); - let i = 0; + let mut i = 0; while (i < bitlen) { - bit_vector::set(&mut bitvector, i); + bitvector.set(i); i = i + 1; }; - bit_vector::shift_left(&mut bitvector, bitlen - 1); + bitvector.shift_left(bitlen - 1); i = bitlen - 1; while (i > 0) { - assert!(!bit_vector::is_index_set(&bitvector, i), 1); + assert!(!bitvector.is_index_set( i)); i = i - 1; }; } @@ -205,8 +210,8 @@ module std::bit_vector_tests { #[test] fun shift_left_more_than_size() { let bitlen = 133; - let bitvector = bit_vector::new(bitlen); - bit_vector::shift_left(&mut bitvector, bitlen); + let mut bitvector = bit_vector::new(bitlen); + bitvector.shift_left(bitlen); } #[test] @@ -218,6 +223,6 @@ module std::bit_vector_tests { #[test] fun single_bit_bitvector() { let bitvector = bit_vector::new(1); - assert!(bit_vector::length(&bitvector) == 1, 0); + assert!(bitvector.length() == 1); } } diff --git a/external-crates/move/crates/move-stdlib/tests/fixedpoint32_tests.move b/external-crates/move/crates/move-stdlib/tests/fixedpoint32_tests.move index 83513dbe0cfda..4fd5f847b4ab9 100644 --- a/external-crates/move/crates/move-stdlib/tests/fixedpoint32_tests.move +++ b/external-crates/move/crates/move-stdlib/tests/fixedpoint32_tests.move @@ -1,3 +1,8 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + #[test_only] module std::fixed_point32_tests { use std::fixed_point32; @@ -28,7 +33,7 @@ module std::fixed_point32_tests { #[test] fun create_zero() { let x = fixed_point32::create_from_rational(0, 1); - assert!(fixed_point32::is_zero(x), 0); + assert!(x.is_zero()); } #[test] @@ -75,14 +80,14 @@ module std::fixed_point32_tests { fun exact_multiply() { let f = fixed_point32::create_from_rational(3, 4); // 0.75 let nine = fixed_point32::multiply_u64(12, f); // 12 * 0.75 - assert!(nine == 9, 0); + assert!(nine == 9); } #[test] fun exact_divide() { let f = fixed_point32::create_from_rational(3, 4); // 0.75 let twelve = fixed_point32::divide_u64(9, f); // 9 / 0.75 - assert!(twelve == 12, 0); + assert!(twelve == 12); } #[test] @@ -91,98 +96,19 @@ module std::fixed_point32_tests { let not_three = fixed_point32::multiply_u64(9, copy f); // 9 * 0.333... // multiply_u64 does NOT round -- it truncates -- so values that // are not perfectly representable in binary may be off by one. - assert!(not_three == 2, 0); + assert!(not_three == 2); // Try again with a fraction slightly larger than 1/3. - let f = fixed_point32::create_from_raw_value(fixed_point32::get_raw_value(f) + 1); + let f = fixed_point32::create_from_raw_value(f.get_raw_value() + 1); let three = fixed_point32::multiply_u64(9, f); - assert!(three == 3, 1); + assert!(three == 3); } #[test] fun create_from_rational_max_numerator_denominator() { // Test creating a 1.0 fraction from the maximum u64 value. let f = fixed_point32::create_from_rational(18446744073709551615, 18446744073709551615); - let one = fixed_point32::get_raw_value(f); - assert!(one == 4294967296, 0); // 0x1.00000000 - } - - #[test] - fun min_can_return_smaller_fixed_point_number() { - let one = fixed_point32::create_from_rational(1, 1); - let two = fixed_point32::create_from_rational(2, 1); - let smaller_number1 = fixed_point32::min(one, two); - let val1 = fixed_point32::get_raw_value(smaller_number1); - assert!(val1 == 4294967296, 0); // 0x1.00000000 - let smaller_number2 = fixed_point32::min(two, one); - let val2 = fixed_point32::get_raw_value(smaller_number2); - assert!(val2 == 4294967296, 0); // 0x1.00000000 - } - - #[test] - fun max_can_return_larger_fixed_point_number() { - let one = fixed_point32::create_from_rational(1, 1); - let two = fixed_point32::create_from_rational(2, 1); - let larger_number1 = fixed_point32::max(one, two); - let larger_number2 = fixed_point32::max(two, one); - let val1 = fixed_point32::get_raw_value(larger_number1); - assert!(val1 == 8589934592, 0); // 0x2.00000000 - let val2 = fixed_point32::get_raw_value(larger_number2); - assert!(val2 == 8589934592, 0); // 0x2.00000000 - } - - #[test] - fun floor_can_return_the_correct_number_zero() { - let point_five = fixed_point32::create_from_rational(1, 2); - let val = fixed_point32::floor(point_five); - assert!(val == 0, 0); - } - - #[test] - fun create_from_u64_create_correct_fixed_point_number() { - let one = fixed_point32::create_from_u64(1); - let val = fixed_point32::get_raw_value(one); - assert!(val == 4294967296, 0); - } - - #[test] - #[expected_failure(abort_code = fixed_point32::ERATIO_OUT_OF_RANGE)] - fun create_from_u64_throw_error_when_number_too_large() { - fixed_point32::create_from_u64(4294967296); // (u64 >> 32) + 1 - } - - #[test] - fun floor_can_return_the_correct_number_one() { - let three_point_five = fixed_point32::create_from_rational(7, 2); // 3.5 - let val = fixed_point32::floor(three_point_five); - assert!(val == 3, 0); - } - - #[test] - fun ceil_can_round_up_correctly() { - let point_five = fixed_point32::create_from_rational(1, 2); // 0.5 - let val = fixed_point32::ceil(point_five); - assert!(val == 1, 0); - } - - #[test] - fun ceil_will_not_change_if_number_already_integer() { - let one = fixed_point32::create_from_rational(1, 1); // 0.5 - let val = fixed_point32::ceil(one); - assert!(val == 1, 0); - } - - #[test] - fun round_can_round_up_correctly() { - let point_five = fixed_point32::create_from_rational(1, 2); // 0.5 - let val = fixed_point32::round(point_five); - assert!(val == 1, 0); - } - - #[test] - fun round_can_round_down_correctly() { - let num = fixed_point32::create_from_rational(499, 1000); // 0.499 - let val = fixed_point32::round(num); - assert!(val == 0, 0); + let one = f.get_raw_value(); + assert!(one == 4294967296); // 0x1.00000000 } } diff --git a/external-crates/move/crates/move-stdlib/tests/hash_tests.move b/external-crates/move/crates/move-stdlib/tests/hash_tests.move index 449f7b914bae7..8b309c30e1f75 100644 --- a/external-crates/move/crates/move-stdlib/tests/hash_tests.move +++ b/external-crates/move/crates/move-stdlib/tests/hash_tests.move @@ -1,3 +1,8 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + #[test_only] module std::hash_tests { use std::hash; @@ -6,13 +11,13 @@ module std::hash_tests { fun sha2_256_expected_hash() { let input = x"616263"; let expected_output = x"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"; - assert!(hash::sha2_256(input) == expected_output, 0); + assert!(hash::sha2_256(input) == expected_output); } #[test] fun sha3_256_expected_hash() { let input = x"616263"; let expected_output = x"3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"; - assert!(hash::sha3_256(input) == expected_output, 0); + assert!(hash::sha3_256(input) == expected_output); } } diff --git a/external-crates/move/crates/move-stdlib/tests/integer_tests.move b/external-crates/move/crates/move-stdlib/tests/integer_tests.move new file mode 100644 index 0000000000000..07d940b7a86d4 --- /dev/null +++ b/external-crates/move/crates/move-stdlib/tests/integer_tests.move @@ -0,0 +1,220 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// helpers for integer tests +#[test_only] +module std::integer_tests { + use std::unit_test::assert_eq; + + public(package) macro fun cases($max: _, $cases: vector<_>, $f: |_, _, _|) { + let mut cases = $cases; + let max_pred = $max - 1; + while (!cases.is_empty()) { + let case = cases.pop_back(); + let case_pred = case.max(1) - 1; + let case_succ = case.min(max_pred) + 1; + $f(case_pred, case, case_succ); + } + } + + public(package) macro fun test_max($max: _, $cases: vector<_>) { + let max = $max; + let cases = $cases; + assert_eq!(max.max(max), max); + cases!(max, cases, |case_pred, case, case_succ| { + assert_eq!(max.max(case), max); + assert_eq!(case.max(max), max); + assert_eq!(case.max(case), case); + assert_eq!(case_pred.max(case), case); + assert_eq!(case_succ.max(case), case_succ); + }) + } + + public(package) macro fun test_min($max: _, $cases: vector<_>) { + let max = $max; + let cases = $cases; + assert_eq!(max.min(max), max); + cases!(max, cases, |case_pred, case, case_succ| { + assert_eq!(max.min(case), case); + assert_eq!(case.min(max), case); + assert_eq!(case.min(case), case); + assert_eq!(case_pred.min(case), case_pred); + assert_eq!(case_succ.min(case), case); + }) + } + + public(package) macro fun test_diff($max: _, $cases: vector<_>) { + let max = $max; + let cases = $cases; + assert_eq!(max.diff(max), 0); + cases!(max, cases, |case_pred, case, case_succ| { + assert_eq!(max.diff(case), max - case); + assert_eq!(case.diff(max), max - case); + assert_eq!(case.diff(case), 0); + assert_eq!(case_pred.diff(case), case - case_pred); + assert_eq!(case.diff(case_pred), case - case_pred); + assert_eq!(case_succ.diff(case), case_succ - case); + assert_eq!(case.diff(case_succ), case_succ - case); + }) + } + + public(package) macro fun check_div_round($x: _, $y: _) { + let x = $x; + let y = $y; + if (y == 0) return; + assert_eq!(x.divide_and_round_up(y), (x / y) + (x % y).min(1)); + } + + public(package) macro fun test_divide_and_round_up($max: _, $cases: vector<_>) { + let max = $max; + let cases = $cases; + assert_eq!(max.divide_and_round_up(max), 1); + check_div_round!(max, max); + cases!(max, cases, |case_pred, case, case_succ| { + check_div_round!(max, case); + check_div_round!(case, max); + check_div_round!(case, case); + check_div_round!(case_pred, case); + check_div_round!(case, case_pred); + check_div_round!(case_succ, case); + check_div_round!(case, case_succ); + }) + } + + public(package) macro fun slow_pow($base: _, $exp: u8): _ { + let base = $base; + let mut exp = $exp; + let mut result = 1; + while (exp > 0) { + result = result * base; + exp = exp - 1; + }; + result + } + + public(package) macro fun test_pow<$T>($max: $T, $cases: vector<$T>) { + let max = $max; + let cases = $cases; + cases!(max, cases, |case_pred, case, case_succ| { + assert_eq!(case_pred.pow(0), 1); + assert_eq!(case_pred.pow(1), case_pred); + assert_eq!(case.pow(0), 1); + assert_eq!(case.pow(1), case); + assert_eq!(case_succ.pow(0), 1); + assert_eq!(case_succ.pow(1), case_succ); + }); + assert_eq!((0: $T).pow(2), 0); + assert_eq!((1: $T).pow(255), 1); + assert_eq!((2: $T).pow(7), slow_pow!((2: $T), 7)); + assert_eq!((3: $T).pow(5), slow_pow!((3: $T), 5)); + } + + public(package) macro fun test_sqrt<$T>( + $max: $T, + $bound_cases: vector<$T>, + $reflexive_cases: vector<$T>, + ) { + let max = $max; + let cases = $bound_cases; + // logical bounds cases + let max_sqrt = max.sqrt(); + cases!(max, cases, |case_pred, case, case_succ| { + let sqrt_pred = case_pred.sqrt(); + assert!(sqrt_pred * sqrt_pred <= case_pred); + let sqrt = case.sqrt(); + assert!(sqrt * sqrt <= case); + let sqrt_succ = case_succ.sqrt(); + assert!(sqrt_succ * sqrt_succ <= case_succ); + + if (sqrt_pred >= max_sqrt) return; + assert!((sqrt_pred + 1) * (sqrt_pred + 1) > case_pred); + + if (sqrt >= max_sqrt) return; + assert!((sqrt + 1) * (sqrt + 1) > case); + + if (sqrt_succ >= max_sqrt) return; + assert!((sqrt_succ + 1) * (sqrt_succ + 1) > case_succ); + }); + + // simple reflexive cases + let cases: vector<$T> = $reflexive_cases; + cases!(max, cases, |case_pred, case, case_succ| { + assert_eq!((case_pred * case_pred).sqrt(), case_pred); + assert_eq!((case * case).sqrt(), case); + assert_eq!((case_succ * case_succ).sqrt(), case_succ); + }); + + // test that the square of a non perfect square is the most recent square root perfect + // square, rounding down + let mut cases: vector<$T> = vector[2, 3, 4, 5, 6]; + while (!cases.is_empty()) { + let case = cases.pop_back(); + let prev = case - 1; + let square = case * case; + let prev_suare = prev * prev; + let mut i = prev_suare; + while (i < square) { + assert_eq!(i.sqrt(), prev); + i = i + 1; + } + } + } + + public(package) macro fun sum_range<$T>($n: $T): $T { + let n = $n; + (n * (n + 1)) / 2 + } + + public(package) macro fun test_dos_case<$T>($case: $T) { + let case = $case; + let mut sum: $T = 0; + case.do!(|i| sum = sum + i); + assert_eq!(sum, sum_range!(case - 1)); + + sum = 0; + case.do_eq!(|i| sum = sum + i); + assert_eq!(sum, sum_range!(case)); + + let half = case / 2; + + sum = 0; + half.range_do!(case, |i| sum = sum + i); + assert_eq!(sum, sum_range!(case - 1) - sum_range!(half - 1)); + + sum = 0; + half.range_do_eq!(case, |i| sum = sum + i); + assert_eq!(sum, sum_range!(case) - sum_range!(half - 1)); + } + + public(package) macro fun test_dos<$T>($max: $T, $cases: vector<$T>) { + let max = $max; + let cases = $cases; + // test bounds/invalid ranges + (0: $T).do!(|_| assert!(false)); + cases!(max, cases, |case_pred, case, case_succ| { + if (case == 0) return; + case.range_do!(0, |_| assert!(false)); + case.range_do_eq!(0, |_| assert!(false)); + + if (case == max) return; + case.range_do!(case_pred, |_| assert!(false)); + case_succ.range_do!(case, |_| assert!(false)); + case.range_do_eq!(case_pred, |_| assert!(false)); + case_succ.range_do_eq!(case, |_| assert!(false)); + }); + + // test upper bound being max + let max_pred = max - 1; + max_pred.range_do_eq!(max, |_| ()); + + // test iteration numbers + let cases: vector<$T> = vector[3, 5, 8, 11, 14]; + cases!(max, cases, |case_pred, case, case_succ| { + test_dos_case!(case_pred); + test_dos_case!(case); + test_dos_case!(case_succ); + }); + } + + +} diff --git a/external-crates/move/crates/move-stdlib/tests/move_unit_test.rs b/external-crates/move/crates/move-stdlib/tests/move_unit_test.rs deleted file mode 100644 index 538b08c3e8718..0000000000000 --- a/external-crates/move/crates/move-stdlib/tests/move_unit_test.rs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -use move_cli::base::test::{run_move_unit_tests, UnitTestResult}; -use move_core_types::account_address::AccountAddress; -use move_stdlib::path_in_crate; -use move_stdlib_natives::{all_natives, nursery_natives, GasParameters, NurseryGasParameters}; -use move_unit_test::UnitTestingConfig; -use tempfile::tempdir; - -fn run_tests_for_pkg(path_to_pkg: impl Into, include_nursery_natives: bool) { - let pkg_path = path_in_crate(path_to_pkg); - - let mut natives = all_natives( - AccountAddress::from_hex_literal("0x1").unwrap(), - GasParameters::zeros(), - ); - if include_nursery_natives { - natives.extend(nursery_natives( - /* silent */ false, - AccountAddress::from_hex_literal("0x1").unwrap(), - NurseryGasParameters::zeros(), - )) - } - - let result = run_move_unit_tests( - &pkg_path, - move_package::BuildConfig { - test_mode: true, - install_dir: Some(tempdir().unwrap().path().to_path_buf()), - ..Default::default() - }, - UnitTestingConfig::default_with_bound(Some(1_000_000_000)), - natives, - None, - /* compute_coverage */ false, - &mut std::io::stdout(), - ) - .unwrap(); - if result.0 != UnitTestResult::Success { - panic!("aborting because of Move unit test failures"); - } -} - -#[test] -fn move_unit_tests() { - run_tests_for_pkg(".", false); - run_tests_for_pkg("nursery", true); -} diff --git a/external-crates/move/crates/move-stdlib/tests/move_verification_test.rs b/external-crates/move/crates/move-stdlib/tests/move_verification_test.rs deleted file mode 100644 index 2a5dbffb18fa8..0000000000000 --- a/external-crates/move/crates/move-stdlib/tests/move_verification_test.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// SPDX-License-Identifier: Apache-2.0 - -// TODO: split this into individual tests once the package system supports this. - -#[test] -fn prove() { - // TODO re-enable when the prover works again - // use move_cli::base::prove::ProverTest; - // ProverTest::create(".").run(); - // ProverTest::create("nursery").run() -} diff --git a/external-crates/move/crates/move-stdlib/tests/option_tests.move b/external-crates/move/crates/move-stdlib/tests/option_tests.move index 950a940dc4207..a8cb8875d5495 100644 --- a/external-crates/move/crates/move-stdlib/tests/option_tests.move +++ b/external-crates/move/crates/move-stdlib/tests/option_tests.move @@ -1,20 +1,22 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + #[test_only] module std::option_tests { - use std::option; - use std::vector; - #[test] fun option_none_is_none() { let none = option::none(); - assert!(option::is_none(&none), 0); - assert!(!option::is_some(&none), 1); + assert!(none.is_none()); + assert!(!none.is_some()); } #[test] fun option_some_is_some() { let some = option::some(5); - assert!(!option::is_none(&some), 0); - assert!(option::is_some(&some), 1); + assert!(!some.is_none()); + assert!(some.is_some()); } #[test] @@ -22,149 +24,197 @@ module std::option_tests { let none = option::none(); let some = option::some(5); let some_other = option::some(6); - assert!(option::contains(&some, &5), 0); - assert!(option::contains(&some_other, &6), 1); - assert!(!option::contains(&none, &5), 2); - assert!(!option::contains(&some_other, &5), 3); + assert!(some.contains(&5)); + assert!(some_other.contains(&6)); + assert!(!none.contains(&5)); + assert!(!some_other.contains(&5)); } #[test] fun option_borrow_some() { let some = option::some(5); let some_other = option::some(6); - assert!(*option::borrow(&some) == 5, 3); - assert!(*option::borrow(&some_other) == 6, 4); + assert!(*some.borrow() == 5); + assert!(*some_other.borrow() == 6); } #[test] #[expected_failure(abort_code = option::EOPTION_NOT_SET)] fun option_borrow_none() { - option::borrow(&option::none()); + option::none().borrow(); } #[test] fun borrow_mut_some() { - let some = option::some(1); - let ref = option::borrow_mut(&mut some); + let mut some = option::some(1); + let ref = some.borrow_mut(); *ref = 10; - assert!(*option::borrow(&some) == 10, 0); + assert!(*some.borrow() == 10); } #[test] #[expected_failure(abort_code = option::EOPTION_NOT_SET)] fun borrow_mut_none() { - option::borrow_mut(&mut option::none()); + option::none().borrow_mut(); } #[test] fun borrow_with_default() { let none = option::none(); let some = option::some(5); - assert!(*option::borrow_with_default(&some, &7) == 5, 0); - assert!(*option::borrow_with_default(&none, &7) == 7, 1); + assert!(*some.borrow_with_default(&7) == 5); + assert!(*none.borrow_with_default(&7) == 7); } #[test] fun get_with_default() { let none = option::none(); let some = option::some(5); - assert!(option::get_with_default(&some, 7) == 5, 0); - assert!(option::get_with_default(&none, 7) == 7, 1); + assert!(option::get_with_default(&some, 7) == 5); + assert!(option::get_with_default(&none, 7) == 7); } #[test] fun extract_some() { - let opt = option::some(1); - assert!(option::extract(&mut opt) == 1, 0); - assert!(option::is_none(&opt), 1); + let mut opt = option::some(1); + assert!(opt.extract() == 1); + assert!(opt.is_none()); } #[test] #[expected_failure(abort_code = option::EOPTION_NOT_SET)] fun extract_none() { - option::extract(&mut option::none()); + option::none().extract(); } #[test] fun swap_some() { - let some = option::some(5); - assert!(option::swap(&mut some, 1) == 5, 0); - assert!(*option::borrow(&some) == 1, 1); + let mut some = option::some(5); + assert!(some.swap(1) == 5); + assert!(*some.borrow() == 1); } #[test] fun swap_or_fill_some() { - let some = option::some(5); - assert!(option::swap_or_fill(&mut some, 1) == option::some(5), 0); - assert!(*option::borrow(&some) == 1, 1); + let mut some = option::some(5); + assert!(some.swap_or_fill(1) == option::some(5)); + assert!(*some.borrow() == 1); } #[test] fun swap_or_fill_none() { - let none = option::none(); - assert!(option::swap_or_fill(&mut none, 1) == option::none(), 0); - assert!(*option::borrow(&none) == 1, 1); + let mut none = option::none(); + assert!(none.swap_or_fill(1) == option::none()); + assert!(*none.borrow() == 1); } #[test] #[expected_failure(abort_code = option::EOPTION_NOT_SET)] fun swap_none() { - option::swap(&mut option::none(), 1); + option::none().swap(1); } #[test] fun fill_none() { - let none = option::none(); - option::fill(&mut none, 3); - assert!(option::is_some(&none), 0); - assert!(*option::borrow(&none) == 3, 1); + let mut none = option::none(); + none.fill(3); + assert!(none.is_some()); + assert!(*none.borrow() == 3); } #[test] #[expected_failure(abort_code = option::EOPTION_IS_SET)] fun fill_some() { - option::fill(&mut option::some(3), 0); + option::some(3).fill(0); } #[test] fun destroy_with_default() { - assert!(option::destroy_with_default(option::none(), 4) == 4, 0); - assert!(option::destroy_with_default(option::some(4), 5) == 4, 1); + assert!(option::none().destroy_with_default(4) == 4); + assert!(option::some(4).destroy_with_default(5) == 4); } #[test] fun destroy_some() { - assert!(option::destroy_some(option::some(4)) == 4, 0); + assert!(option::some(4).destroy_some() == 4); } #[test] #[expected_failure(abort_code = option::EOPTION_NOT_SET)] fun destroy_some_none() { - option::destroy_some(option::none()); + option::none().destroy_some(); } #[test] fun destroy_none() { - option::destroy_none(option::none()); + option::none().destroy_none(); } #[test] #[expected_failure(abort_code = option::EOPTION_IS_SET)] fun destroy_none_some() { - option::destroy_none(option::some(0)); + option::some(0).destroy_none(); } #[test] fun into_vec_some() { - let v = option::to_vec(option::some(0)); - assert!(vector::length(&v) == 1, 0); - let x = vector::pop_back(&mut v); - assert!(x == 0, 1); + let mut v = option::some(0).to_vec(); + assert!(v.length() == 1); + let x = v.pop_back(); + assert!(x == 0); } #[test] fun into_vec_none() { - let v: vector = option::to_vec(option::none()); - assert!(vector::is_empty(&v), 0); + let v: vector = option::none().to_vec(); + assert!(v.is_empty()); + } + + // === Macros === + + #[test] + fun do_destroy() { + let mut counter = 0; + option::some(5).destroy!(|x| counter = x); + option::some(10).do!(|x| counter = counter + x); + + assert!(counter == 15); + } + + #[test] + fun do_ref_mut() { + let mut counter = 0; + let mut opt = option::some(5); + opt.do_mut!(|x| *x = 100); + opt.do_ref!(|x| counter = *x); + + assert!(counter == 100); + } + + #[test] + fun map_map_ref() { + assert!(option::some(5).map!(|x| vector[x]) == option::some(vector[5])); + assert!(option::some(5).map_ref!(|x| vector[*x]) == option::some(vector[5])); + assert!(option::none().map!(|x| vector[x]) == option::none()); + assert!(option::none().map_ref!(|x| vector[*x]) == option::none()); + } + + #[test] + fun filter() { + assert!(option::some(5).filter!(|x| *x == 5) == option::some(5)); + assert!(option::some(5).filter!(|x| *x == 6) == option::none()); + } + + #[test] + fun is_some_and() { + assert!(option::some(5).is_some_and!(|x| *x == 5)); + assert!(!option::some(5).is_some_and!(|x| *x == 6)); + assert!(!option::none().is_some_and!(|x| *x == 5)); + } + + #[test] + fun destroy_or() { + assert!(option::none().destroy_or!(10) == 10); + assert!(option::some(5).destroy_or!(10) == 5); } } diff --git a/external-crates/move/crates/move-stdlib/tests/string_tests.move b/external-crates/move/crates/move-stdlib/tests/string_tests.move index e7810a3422262..bf99e678860d0 100644 --- a/external-crates/move/crates/move-stdlib/tests/string_tests.move +++ b/external-crates/move/crates/move-stdlib/tests/string_tests.move @@ -1,3 +1,8 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + #[test_only] module std::string_tests { use std::string; @@ -5,74 +10,79 @@ module std::string_tests { #[test] fun test_valid_utf8() { let sparkle_heart = vector[240, 159, 146, 150]; - let s = string::utf8(sparkle_heart); - assert!(string::length(&s) == 4, 22); + let s = sparkle_heart.to_string(); + assert!(s.length() == 4); } #[test] - #[expected_failure(abort_code = string::EINVALID_UTF8)] + #[expected_failure(abort_code = string::EInvalidUTF8)] fun test_invalid_utf8() { let no_sparkle_heart = vector[0, 159, 146, 150]; - let s = string::utf8(no_sparkle_heart); - assert!(string::length(&s) == 1, 22); + let s = no_sparkle_heart.to_string(); + assert!(s.length() == 1); } #[test] - fun test_sub_string() { - let s = string::utf8(b"abcd"); - let sub = string::sub_string(&s, 2, 4); - assert!(sub == string::utf8(b"cd"), 22) + fun test_substring() { + let s = b"abcd".to_string(); + let sub = s.substring(2, 4); + assert!(sub == b"cd".to_string()) } #[test] - #[expected_failure(abort_code = string::EINVALID_INDEX)] - fun test_sub_string_invalid_boundary() { + #[expected_failure(abort_code = string::EInvalidIndex)] + fun test_substring_invalid_boundary() { let sparkle_heart = vector[240, 159, 146, 150]; - let s = string::utf8(sparkle_heart); - let _sub = string::sub_string(&s, 1, 4); + let s = sparkle_heart.to_string(); + let _sub = s.substring(1, 4); } #[test] - #[expected_failure(abort_code = string::EINVALID_INDEX)] - fun test_sub_string_invalid_index() { - let s = string::utf8(b"abcd"); - let _sub = string::sub_string(&s, 4, 5); + #[expected_failure(abort_code = string::EInvalidIndex)] + fun test_substring_invalid_index() { + let s = b"abcd".to_string(); + let _sub = s.substring(4, 5); } #[test] - fun test_sub_string_empty() { - let s = string::utf8(b"abcd"); - let sub = string::sub_string(&s, 4, 4); - assert!(string::is_empty(&sub), 22) + fun test_substring_empty() { + let s = b"abcd".to_string(); + let sub = s.substring(4, 4); + assert!(sub.is_empty()) } #[test] fun test_index_of() { - let s = string::utf8(b"abcd"); - let r = string::utf8(b"bc"); - let p = string::index_of(&s, &r); - assert!(p == 1, 22) + let s = b"abcd".to_string(); + let r = b"bc".to_string(); + let p = s.index_of(&r); + assert!(p == 1) } #[test] fun test_index_of_fail() { - let s = string::utf8(b"abcd"); - let r = string::utf8(b"bce"); - let p = string::index_of(&s, &r); - assert!(p == 4, 22) + let s = b"abcd".to_string(); + let r = b"bce".to_string(); + let p = s.index_of(&r); + assert!(p == 4) } #[test] fun test_append() { - let s = string::utf8(b"abcd"); - string::append(&mut s, string::utf8(b"ef")); - assert!(s == string::utf8(b"abcdef"), 22) + let mut s = b"abcd".to_string(); + s.append(b"ef".to_string()); + assert!(s == b"abcdef".to_string()) } #[test] fun test_insert() { - let s = string::utf8(b"abcd"); - string::insert(&mut s, 1, string::utf8(b"xy")); - assert!(s == string::utf8(b"axybcd"), 22) + let mut s = b"abcd".to_string(); + s.insert(1, b"xy".to_string()); + assert!(s == b"axybcd".to_string()) + } + + #[test] + fun test_into_bytes() { + assert!(b"abcd" == b"abcd".to_string().into_bytes()) } } diff --git a/external-crates/move/crates/move-stdlib/tests/type_name_tests.move b/external-crates/move/crates/move-stdlib/tests/type_name_tests.move index 48c1ba2edc1a5..256c31c901fa8 100644 --- a/external-crates/move/crates/move-stdlib/tests/type_name_tests.move +++ b/external-crates/move/crates/move-stdlib/tests/type_name_tests.move @@ -1,47 +1,104 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + // note: intentionally using 0xa here to test non-0x1 module addresses module 0xA::type_name_tests { #[test_only] - use std::type_name::{get, into_string}; + use std::type_name::{get, into_string, is_primitive, get_address, get_module}; #[test_only] use std::ascii::string; - struct TestStruct {} + public struct TestStruct {} - struct TestGenerics { } + public struct TestGenerics { } - struct TestMultiGenerics { } + public struct TestMultiGenerics { } #[test] - fun test_ground_types() { - assert!(into_string(get()) == string(b"u8"), 0); - assert!(into_string(get()) == string(b"u64"), 0); - assert!(into_string(get()) == string(b"u128"), 0); - assert!(into_string(get
()) == string(b"address"), 0); - assert!(into_string(get()) == string(b"signer"), 0); - assert!(into_string(get>()) == string(b"vector"), 0) + fun test_primitive_types() { + assert!(into_string(get()) == string(b"u8")); + assert!(into_string(get()) == string(b"u16")); + assert!(into_string(get()) == string(b"u32")); + assert!(into_string(get()) == string(b"u64")); + assert!(into_string(get()) == string(b"u128")); + assert!(into_string(get()) == string(b"u256")); + assert!(into_string(get
()) == string(b"address")); + assert!(into_string(get>()) == string(b"vector")); + assert!(into_string(get>>()) == string(b"vector>")); + assert!(into_string(get>>()) == string(b"vector>")); } - // Note: these tests assume a 16 byte address length, and will fail on platforms where addresses are 20 or 32 bytes + #[test] + fun test_is_primitive() { + assert!(is_primitive(&get())); + assert!(is_primitive(&get())); + assert!(is_primitive(&get())); + assert!(is_primitive(&get())); + assert!(is_primitive(&get())); + assert!(is_primitive(&get())); + assert!(is_primitive(&get
())); + assert!(is_primitive(&get>())); + assert!(is_primitive(&get>>())); + assert!(is_primitive(&get>>())); + } + + // Note: these tests assume a 32 byte address length #[test] fun test_structs() { - assert!(into_string(get()) == string(b"000000000000000000000000000000000000000000000000000000000000000a::type_name_tests::TestStruct"), 0); - assert!(into_string(get()) == string(b"0000000000000000000000000000000000000000000000000000000000000001::ascii::String"), 0); - assert!(into_string(get>()) == string(b"0000000000000000000000000000000000000000000000000000000000000001::option::Option"), 0); - assert!(into_string(get()) == string(b"0000000000000000000000000000000000000000000000000000000000000001::string::String"), 0); + assert!(into_string(get()) == string(b"000000000000000000000000000000000000000000000000000000000000000a::type_name_tests::TestStruct")); + assert!(into_string(get()) == string(b"0000000000000000000000000000000000000000000000000000000000000001::ascii::String")); + assert!(into_string(get>()) == string(b"0000000000000000000000000000000000000000000000000000000000000001::option::Option")); + assert!(into_string(get()) == string(b"0000000000000000000000000000000000000000000000000000000000000001::string::String")); } - // Note: these tests assume a 16 byte address length, and will fail on platforms where addresses are 20 or 32 bytes + // Note: these tests assume a 32 byte address length #[test] fun test_generics() { - assert!(into_string(get>()) == string(b"000000000000000000000000000000000000000000000000000000000000000a::type_name_tests::TestGenerics<0000000000000000000000000000000000000000000000000000000000000001::string::String>"), 0); - assert!(into_string(get>>()) == string(b"vector<000000000000000000000000000000000000000000000000000000000000000a::type_name_tests::TestGenerics>"), 0); - assert!(into_string(get>>()) == string(b"0000000000000000000000000000000000000000000000000000000000000001::option::Option<000000000000000000000000000000000000000000000000000000000000000a::type_name_tests::TestGenerics>"), 0); + assert!(into_string(get>()) == string(b"000000000000000000000000000000000000000000000000000000000000000a::type_name_tests::TestGenerics<0000000000000000000000000000000000000000000000000000000000000001::string::String>")); + assert!(into_string(get>>()) == string(b"vector<000000000000000000000000000000000000000000000000000000000000000a::type_name_tests::TestGenerics>")); + assert!(into_string(get>>()) == string(b"0000000000000000000000000000000000000000000000000000000000000001::option::Option<000000000000000000000000000000000000000000000000000000000000000a::type_name_tests::TestGenerics>")); } - // Note: these tests assume a 16 byte address length, and will fail on platforms where addresses are 20 or 32 bytes + // Note: these tests assume a 32 byte address length #[test] fun test_multi_generics() { - assert!(into_string(get>()) == string(b"000000000000000000000000000000000000000000000000000000000000000a::type_name_tests::TestMultiGenerics"), 0); - assert!(into_string(get, TestGenerics>>()) == string(b"000000000000000000000000000000000000000000000000000000000000000a::type_name_tests::TestMultiGenerics,000000000000000000000000000000000000000000000000000000000000000a::type_name_tests::TestGenerics>"), 0); + assert!(into_string(get>()) == string(b"000000000000000000000000000000000000000000000000000000000000000a::type_name_tests::TestMultiGenerics")); + assert!(into_string(get, TestGenerics>>()) == string(b"000000000000000000000000000000000000000000000000000000000000000a::type_name_tests::TestMultiGenerics,000000000000000000000000000000000000000000000000000000000000000a::type_name_tests::TestGenerics>")); + } + + #[test] + fun test_get_address() { + assert!(get_address(&get()) == string(b"0000000000000000000000000000000000000000000000000000000000000001")); + assert!(get_address(&get()) == string(b"000000000000000000000000000000000000000000000000000000000000000a")); + assert!(get_address(&get>()) == string(b"000000000000000000000000000000000000000000000000000000000000000a")); + } + + #[test] + fun test_get_module() { + assert!(get_module(&get()) == string(b"ascii")); + assert!(get_module(&get()) == string(b"type_name_tests")); + assert!(get_module(&get>()) == string(b"type_name_tests")); + } + + #[test, expected_failure(abort_code = std::type_name::ENonModuleType)] + fun test_get_address_aborts_with_primitive() { + get_address(&get()); + } + + #[test, expected_failure(abort_code = std::type_name::ENonModuleType)] + fun test_get_module_aborts_with_primitive() { + get_module(&get()); + } + + #[test, expected_failure(abort_code = std::type_name::ENonModuleType)] + fun test_get_address_aborts_with_primitive_generic() { + get_address(&get>()); + } + + #[test, expected_failure(abort_code = std::type_name::ENonModuleType)] + fun test_get_module_aborts_with_primitive_generic() { + get_module(&get>>()); } } diff --git a/external-crates/move/crates/move-stdlib/tests/u128_tests.move b/external-crates/move/crates/move-stdlib/tests/u128_tests.move new file mode 100644 index 0000000000000..315190be0589b --- /dev/null +++ b/external-crates/move/crates/move-stdlib/tests/u128_tests.move @@ -0,0 +1,78 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[test_only] +module std::u128_tests { + use std::integer_tests; + use std::unit_test::assert_eq; + + const BIT_SIZE: u8 = 128; + const MAX: u128 = 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF; + const MAX_PRED: u128 = MAX - 1; + + const CASES: vector = vector[ + 0, + 1, + 10, + 11, + 100, + 111, + 1 << (BIT_SIZE / 2 - 1), + (1 << (BIT_SIZE / 2 - 1)) + 1, + 1 << (BIT_SIZE - 1), + (1 << (BIT_SIZE - 1)) + 1, + MAX / 2, + (MAX / 2) + 1, + MAX_PRED, + MAX, + ]; + + #[test] + fun test_max() { + integer_tests::test_max!(MAX, CASES); + } + + #[test] + fun test_min() { + integer_tests::test_min!(MAX, CASES); + } + + #[test] + fun test_diff() { + integer_tests::test_diff!(MAX, CASES); + } + + #[test] + fun test_divide_and_round_up() { + integer_tests::test_divide_and_round_up!(MAX, CASES); + } + + #[test, expected_failure(arithmetic_error, location = std::u8)] + fun test_divide_and_round_up_error() { + 1u8.divide_and_round_up(0); + } + + #[test] + fun test_pow() { + integer_tests::test_pow!(MAX, CASES); + assert_eq!(2u128.pow(12), integer_tests::slow_pow!(2u128, 12)); + assert_eq!(3u128.pow(27), integer_tests::slow_pow!(3u128, 27)); + } + + #[test, expected_failure(arithmetic_error, location = std::u16)] + fun test_pow_overflow() { + 255u16.pow(255); + } + + #[test] + fun test_sqrt() { + let reflexive_cases = + vector[0, 2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50, 53, 56, 59]; + integer_tests::test_sqrt!(MAX, CASES, reflexive_cases) + } + + #[test] + fun test_dos() { + integer_tests::test_dos!(MAX, CASES); + } +} diff --git a/external-crates/move/crates/move-stdlib/tests/u16_tests.move b/external-crates/move/crates/move-stdlib/tests/u16_tests.move new file mode 100644 index 0000000000000..f872acf70f988 --- /dev/null +++ b/external-crates/move/crates/move-stdlib/tests/u16_tests.move @@ -0,0 +1,78 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[test_only] +module std::u16_tests { + use std::integer_tests; + use std::unit_test::assert_eq; + + const BIT_SIZE: u8 = 16; + const MAX: u16 = 0xFFFF; + const MAX_PRED: u16 = MAX - 1; + + const CASES: vector = vector[ + 0, + 1, + 10, + 11, + 100, + 111, + 1 << (BIT_SIZE / 2 - 1), + (1 << (BIT_SIZE / 2 - 1)) + 1, + 1 << (BIT_SIZE - 1), + (1 << (BIT_SIZE - 1)) + 1, + MAX / 2, + (MAX / 2) + 1, + MAX_PRED, + MAX, + ]; + + #[test] + fun test_max() { + integer_tests::test_max!(MAX, CASES); + } + + #[test] + fun test_min() { + integer_tests::test_min!(MAX, CASES); + } + + #[test] + fun test_diff() { + integer_tests::test_diff!(MAX, CASES); + } + + #[test] + fun test_divide_and_round_up() { + integer_tests::test_divide_and_round_up!(MAX, CASES); + } + + #[test, expected_failure(arithmetic_error, location = std::u8)] + fun test_divide_and_round_up_error() { + 1u8.divide_and_round_up(0); + } + + #[test] + fun test_pow() { + integer_tests::test_pow!(MAX, CASES); + assert_eq!(2u16.pow(12), integer_tests::slow_pow!(2u16, 12)); + assert_eq!(3u16.pow(10), integer_tests::slow_pow!(3u16, 10)); + } + + #[test, expected_failure(arithmetic_error, location = std::u16)] + fun test_pow_overflow() { + 255u16.pow(255); + } + + #[test] + fun test_sqrt() { + let reflexive_cases = + vector[0, 2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50, 53, 56, 59]; + integer_tests::test_sqrt!(MAX, CASES, reflexive_cases) + } + + #[test] + fun test_dos() { + integer_tests::test_dos!(MAX, CASES); + } +} diff --git a/external-crates/move/crates/move-stdlib/tests/u256_tests.move b/external-crates/move/crates/move-stdlib/tests/u256_tests.move new file mode 100644 index 0000000000000..eff171bf43fa0 --- /dev/null +++ b/external-crates/move/crates/move-stdlib/tests/u256_tests.move @@ -0,0 +1,72 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[test_only] +module std::u256_tests { + use std::integer_tests; + use std::unit_test::assert_eq; + + const BIT_SIZE: u8 = 255; + const MAX: u256 = + 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF; + const MAX_PRED: u256 = MAX - 1; + + const CASES: vector = vector[ + 0, + 1, + 10, + 11, + 100, + 111, + 1 << (BIT_SIZE / 2), + (1 << (BIT_SIZE / 2)) + 1, + 1 << BIT_SIZE, + (1 << BIT_SIZE) + 1, + MAX / 2, + (MAX / 2) + 1, + MAX_PRED, + MAX, + ]; + + #[test] + fun test_max() { + integer_tests::test_max!(MAX, CASES); + } + + #[test] + fun test_min() { + integer_tests::test_min!(MAX, CASES); + } + + #[test] + fun test_diff() { + integer_tests::test_diff!(MAX, CASES); + } + + #[test] + fun test_divide_and_round_up() { + integer_tests::test_divide_and_round_up!(MAX, CASES); + } + + #[test, expected_failure(arithmetic_error, location = std::u8)] + fun test_divide_and_round_up_error() { + 1u8.divide_and_round_up(0); + } + + #[test] + fun test_pow() { + integer_tests::test_pow!(MAX, CASES); + assert_eq!(2u256.pow(12), integer_tests::slow_pow!(2u256, 12)); + assert_eq!(3u256.pow(27), integer_tests::slow_pow!(3u256, 27)); + } + + #[test, expected_failure(arithmetic_error, location = std::u256)] + fun test_pow_overflow() { + 255u256.pow(255); + } + + #[test] + fun test_dos() { + integer_tests::test_dos!(MAX, CASES); + } +} diff --git a/external-crates/move/crates/move-stdlib/tests/u32_tests.move b/external-crates/move/crates/move-stdlib/tests/u32_tests.move new file mode 100644 index 0000000000000..c2890c1d5cff9 --- /dev/null +++ b/external-crates/move/crates/move-stdlib/tests/u32_tests.move @@ -0,0 +1,78 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[test_only] +module std::u32_tests { + use std::integer_tests; + use std::unit_test::assert_eq; + + const BIT_SIZE: u8 = 32; + const MAX: u32 = 0xFFFF_FFFF; + const MAX_PRED: u32 = MAX - 1; + + const CASES: vector = vector[ + 0, + 1, + 10, + 11, + 100, + 111, + 1 << (BIT_SIZE / 2 - 1), + (1 << (BIT_SIZE / 2 - 1)) + 1, + 1 << (BIT_SIZE - 1), + (1 << (BIT_SIZE - 1)) + 1, + MAX / 2, + (MAX / 2) + 1, + MAX_PRED, + MAX, + ]; + + #[test] + fun test_max() { + integer_tests::test_max!(MAX, CASES); + } + + #[test] + fun test_min() { + integer_tests::test_min!(MAX, CASES); + } + + #[test] + fun test_diff() { + integer_tests::test_diff!(MAX, CASES); + } + + #[test] + fun test_divide_and_round_up() { + integer_tests::test_divide_and_round_up!(MAX, CASES); + } + + #[test, expected_failure(arithmetic_error, location = std::u8)] + fun test_divide_and_round_up_error() { + 1u8.divide_and_round_up(0); + } + + #[test] + fun test_pow() { + integer_tests::test_pow!(MAX, CASES); + assert_eq!(2u32.pow(12), integer_tests::slow_pow!(2u32, 12)); + assert_eq!(3u32.pow(20), integer_tests::slow_pow!(3u32, 20)); + } + + #[test, expected_failure(arithmetic_error, location = std::u32)] + fun test_pow_overflow() { + 255u32.pow(255); + } + + #[test] + fun test_sqrt() { + let reflexive_cases = + vector[0, 2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50, 53, 56, 59]; + integer_tests::test_sqrt!(MAX, CASES, reflexive_cases) + } + + #[test] + fun test_dos() { + integer_tests::test_dos!(MAX, CASES); + } +} diff --git a/external-crates/move/crates/move-stdlib/tests/u64_tests.move b/external-crates/move/crates/move-stdlib/tests/u64_tests.move new file mode 100644 index 0000000000000..aee0790856f0f --- /dev/null +++ b/external-crates/move/crates/move-stdlib/tests/u64_tests.move @@ -0,0 +1,79 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[test_only] +module std::u64_tests { + use std::integer_tests; + use std::unit_test::assert_eq; + + const BIT_SIZE: u8 = 64; + const MAX: u64 = 0xFFFF_FFFF_FFFF_FFFF; + const MAX_PRED: u64 = MAX - 1; + + const CASES: vector = vector[ + 0, + 1, + 10, + 11, + 100, + 111, + 1 << (BIT_SIZE / 2 - 1), + (1 << (BIT_SIZE / 2 - 1)) + 1, + 1 << (BIT_SIZE - 1), + (1 << (BIT_SIZE - 1)) + 1, + MAX / 2, + (MAX / 2) + 1, + MAX_PRED, + MAX, + ]; + + #[test] + fun test_max() { + integer_tests::test_max!(MAX, CASES); + } + + #[test] + fun test_min() { + integer_tests::test_min!(MAX, CASES); + } + + #[test] + fun test_diff() { + integer_tests::test_diff!(MAX, CASES); + } + + #[test] + fun test_divide_and_round_up() { + integer_tests::test_divide_and_round_up!(MAX, CASES); + } + + #[test, expected_failure(arithmetic_error, location = std::u8)] + fun test_divide_and_round_up_error() { + 1u8.divide_and_round_up(0); + } + + #[test] + fun test_pow() { + integer_tests::test_pow!(MAX, CASES); + assert_eq!(2u64.pow(12), integer_tests::slow_pow!(2u64, 12)); + assert_eq!(3u64.pow(27), integer_tests::slow_pow!(3u64, 27)); + } + + #[test, expected_failure(arithmetic_error, location = std::u64)] + fun test_pow_overflow() { + 255u64.pow(255); + } + + #[test] + fun test_sqrt() { + let reflexive_cases = + vector[0, 2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50, 53, 56, 59]; + integer_tests::test_sqrt!(MAX, CASES, reflexive_cases) + } + + #[test] + fun test_dos() { + integer_tests::test_dos!(MAX, CASES); + } + +} diff --git a/external-crates/move/crates/move-stdlib/tests/u8_tests.move b/external-crates/move/crates/move-stdlib/tests/u8_tests.move new file mode 100644 index 0000000000000..7aa1d6f263a00 --- /dev/null +++ b/external-crates/move/crates/move-stdlib/tests/u8_tests.move @@ -0,0 +1,77 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[test_only] +module std::u8_tests { + use std::integer_tests; + use std::unit_test::assert_eq; + + const BIT_SIZE: u8 = 8; + const MAX: u8 = 0xFF; + const MAX_PRED: u8 = MAX - 1; + + const CASES: vector = vector[ + 0, + 1, + 10, + 11, + 100, + 111, + 1 << (BIT_SIZE / 2 - 1), + (1 << (BIT_SIZE / 2 - 1)) + 1, + 1 << (BIT_SIZE - 1), + (1 << (BIT_SIZE - 1)) + 1, + MAX / 2, + (MAX / 2) + 1, + MAX_PRED, + MAX, + ]; + + #[test] + fun test_max() { + integer_tests::test_max!(MAX, CASES); + } + + #[test] + fun test_min() { + integer_tests::test_min!(MAX, CASES); + } + + #[test] + fun test_diff() { + integer_tests::test_diff!(MAX, CASES); + } + + #[test] + fun test_divide_and_round_up() { + integer_tests::test_divide_and_round_up!(MAX, CASES); + } + + #[test, expected_failure(arithmetic_error, location = std::u8)] + fun test_divide_and_round_up_error() { + 1u8.divide_and_round_up(0); + } + + #[test] + fun test_pow() { + integer_tests::test_pow!(MAX, CASES); + } + + #[test, expected_failure(arithmetic_error, location = std::u8)] + fun test_pow_overflow() { + 255u8.pow(255); + } + + #[test] + fun test_sqrt() { + integer_tests::test_sqrt!(MAX, CASES, vector[0, 2, 5, 8, 11, 14]); + } + + #[test] + fun test_dos() { + let mut sum = 0u16; + 255u8.do_eq!(|i| sum = sum + (i as u16)); + assert_eq!(sum, 32640); + integer_tests::test_dos!(MAX, CASES); + } +} diff --git a/external-crates/move/crates/move-stdlib/tests/vector_tests.move b/external-crates/move/crates/move-stdlib/tests/vector_tests.move index 5480f6b685d96..2bb76d6310017 100644 --- a/external-crates/move/crates/move-stdlib/tests/vector_tests.move +++ b/external-crates/move/crates/move-stdlib/tests/vector_tests.move @@ -1,90 +1,93 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + #[test_only] module std::vector_tests { - use std::vector as V; - - struct R has store { } - struct Droppable has drop {} - struct NotDroppable {} + public struct R has store { } + public struct Droppable has drop {} + public struct NotDroppable {} #[test] fun test_singleton_contains() { - assert!(*V::borrow(&V::singleton(0), 0) == 0, 0); - assert!(*V::borrow(&V::singleton(true), 0) == true, 0); - assert!(*V::borrow(&V::singleton(@0x1), 0) == @0x1, 0); + assert!(vector[0][0] == 0); + assert!(vector[true][0] == true); + assert!(vector[@0x1][0] == @0x1); } #[test] fun test_singleton_len() { - assert!(V::length(&V::singleton(0)) == 1, 0); - assert!(V::length(&V::singleton(true)) == 1, 0); - assert!(V::length(&V::singleton(@0x1)) == 1, 0); + assert!(&vector[0].length() == 1); + assert!(&vector[true].length() == 1); + assert!(&vector[@0x1].length() == 1); } #[test] fun test_empty_is_empty() { - assert!(V::is_empty(&V::empty()), 0); + assert!(vector[].is_empty()); } #[test] fun append_empties_is_empty() { - let v1 = V::empty(); - let v2 = V::empty(); - V::append(&mut v1, v2); - assert!(V::is_empty(&v1), 0); + let mut v1 = vector[]; + let v2 = vector[]; + v1.append(v2); + assert!(v1.is_empty()); } #[test] fun append_respects_order_empty_lhs() { - let v1 = V::empty(); - let v2 = V::empty(); - V::push_back(&mut v2, 0); - V::push_back(&mut v2, 1); - V::push_back(&mut v2, 2); - V::push_back(&mut v2, 3); - V::append(&mut v1, v2); - assert!(!V::is_empty(&v1), 0); - assert!(V::length(&v1) == 4, 1); - assert!(*V::borrow(&v1, 0) == 0, 2); - assert!(*V::borrow(&v1, 1) == 1, 3); - assert!(*V::borrow(&v1, 2) == 2, 4); - assert!(*V::borrow(&v1, 3) == 3, 5); + let mut v1 = vector[]; + let mut v2 = vector[]; + v2.push_back(0); + v2.push_back(1); + v2.push_back(2); + v2.push_back(3); + v1.append(v2); + assert!(!v1.is_empty()); + assert!(v1.length() == 4); + assert!(v1[0] == 0); + assert!(v1[1] == 1); + assert!(v1[2] == 2); + assert!(v1[3] == 3); } #[test] fun append_respects_order_empty_rhs() { - let v1 = V::empty(); - let v2 = V::empty(); - V::push_back(&mut v1, 0); - V::push_back(&mut v1, 1); - V::push_back(&mut v1, 2); - V::push_back(&mut v1, 3); - V::append(&mut v1, v2); - assert!(!V::is_empty(&v1), 0); - assert!(V::length(&v1) == 4, 1); - assert!(*V::borrow(&v1, 0) == 0, 2); - assert!(*V::borrow(&v1, 1) == 1, 3); - assert!(*V::borrow(&v1, 2) == 2, 4); - assert!(*V::borrow(&v1, 3) == 3, 5); + let mut v1 = vector[]; + let v2 = vector[]; + v1.push_back(0); + v1.push_back(1); + v1.push_back(2); + v1.push_back(3); + v1.append(v2); + assert!(!v1.is_empty()); + assert!(v1.length() == 4); + assert!(v1[0] == 0); + assert!(v1[1] == 1); + assert!(v1[2] == 2); + assert!(v1[3] == 3); } #[test] fun append_respects_order_nonempty_rhs_lhs() { - let v1 = V::empty(); - let v2 = V::empty(); - V::push_back(&mut v1, 0); - V::push_back(&mut v1, 1); - V::push_back(&mut v1, 2); - V::push_back(&mut v1, 3); - V::push_back(&mut v2, 4); - V::push_back(&mut v2, 5); - V::push_back(&mut v2, 6); - V::push_back(&mut v2, 7); - V::append(&mut v1, v2); - assert!(!V::is_empty(&v1), 0); - assert!(V::length(&v1) == 8, 1); - let i = 0; + let mut v1 = vector[]; + let mut v2 = vector[]; + v1.push_back(0); + v1.push_back(1); + v1.push_back(2); + v1.push_back(3); + v2.push_back(4); + v2.push_back(5); + v2.push_back(6); + v2.push_back(7); + v1.append(v2); + assert!(!v1.is_empty()); + assert!(v1.length() == 8); + let mut i = 0; while (i < 8) { - assert!(*V::borrow(&v1, i) == i, i); + assert!(v1[i] == i, i); i = i + 1; } } @@ -92,395 +95,397 @@ module std::vector_tests { #[test] #[expected_failure(vector_error, minor_status = 1, location = Self)] fun borrow_out_of_range() { - let v = V::empty(); - V::push_back(&mut v, 7); - V::borrow(&v, 1); + let mut v = vector[]; + v.push_back(7); + &v[1]; } #[test] fun vector_contains() { - let vec = V::empty(); - assert!(!V::contains(&vec, &0), 1); + let mut vec = vector[]; + assert!(!vec.contains(&0)); - V::push_back(&mut vec, 0); - assert!(V::contains(&vec, &0), 2); - assert!(!V::contains(&vec, &1), 3); + vec.push_back(0); + assert!(vec.contains(&0)); + assert!(!vec.contains(&1)); - V::push_back(&mut vec, 1); - assert!(V::contains(&vec, &0), 4); - assert!(V::contains(&vec, &1), 5); - assert!(!V::contains(&vec, &2), 6); + vec.push_back(1); + assert!(vec.contains(&0)); + assert!(vec.contains(&1)); + assert!(!vec.contains(&2)); - V::push_back(&mut vec, 2); - assert!(V::contains(&vec, &0), 7); - assert!(V::contains(&vec, &1), 8); - assert!(V::contains(&vec, &2), 9); - assert!(!V::contains(&vec, &3), 10); + vec.push_back(2); + assert!(vec.contains(&0)); + assert!(vec.contains(&1)); + assert!(vec.contains(&2)); + assert!(!vec.contains(&3)); } #[test] fun destroy_empty() { - V::destroy_empty(V::empty()); - V::destroy_empty(V::empty()); + vector[].destroy_empty(); + vector[].destroy_empty(); + vector::empty().destroy_empty(); + vector::empty().destroy_empty(); } #[test] fun destroy_empty_with_pops() { - let v = V::empty(); - V::push_back(&mut v, 42); - V::pop_back(&mut v); - V::destroy_empty(v); + let mut v = vector[]; + v.push_back(42); + v.pop_back(); + v.destroy_empty(); } #[test] #[expected_failure(vector_error, minor_status = 3, location = Self)] fun destroy_non_empty() { - let v = V::empty(); - V::push_back(&mut v, 42); - V::destroy_empty(v); + let mut v = vector[]; + v.push_back(42); + v.destroy_empty(); } #[test] fun get_set_work() { - let vec = V::empty(); - V::push_back(&mut vec, 0); - V::push_back(&mut vec, 1); - assert!(*V::borrow(&vec, 1) == 1, 0); - assert!(*V::borrow(&vec, 0) == 0, 1); + let mut vec = vector[]; + vec.push_back(0); + vec.push_back(1); + assert!(vec[1] == 1); + assert!(vec[0] == 0); - *V::borrow_mut(&mut vec, 0) = 17; - assert!(*V::borrow(&vec, 1) == 1, 0); - assert!(*V::borrow(&vec, 0) == 17, 0); + *&mut vec[0] = 17; + assert!(vec[1] == 1); + assert!(vec[0] == 17); } #[test] #[expected_failure(vector_error, minor_status = 2, location = Self)] fun pop_out_of_range() { - let v = V::empty(); - V::pop_back(&mut v); + let mut v = vector[]; + v.pop_back(); } #[test] fun swap_different_indices() { - let vec = V::empty(); - V::push_back(&mut vec, 0); - V::push_back(&mut vec, 1); - V::push_back(&mut vec, 2); - V::push_back(&mut vec, 3); - V::swap(&mut vec, 0, 3); - V::swap(&mut vec, 1, 2); - assert!(*V::borrow(&vec, 0) == 3, 0); - assert!(*V::borrow(&vec, 1) == 2, 0); - assert!(*V::borrow(&vec, 2) == 1, 0); - assert!(*V::borrow(&vec, 3) == 0, 0); + let mut vec = vector[]; + vec.push_back(0); + vec.push_back(1); + vec.push_back(2); + vec.push_back(3); + vec.swap(0, 3); + vec.swap(1, 2); + assert!(vec[0] == 3); + assert!(vec[1] == 2); + assert!(vec[2] == 1); + assert!(vec[3] == 0); } #[test] fun swap_same_index() { - let vec = V::empty(); - V::push_back(&mut vec, 0); - V::push_back(&mut vec, 1); - V::push_back(&mut vec, 2); - V::push_back(&mut vec, 3); - V::swap(&mut vec, 1, 1); - assert!(*V::borrow(&vec, 0) == 0, 0); - assert!(*V::borrow(&vec, 1) == 1, 0); - assert!(*V::borrow(&vec, 2) == 2, 0); - assert!(*V::borrow(&vec, 3) == 3, 0); + let mut vec = vector[]; + vec.push_back(0); + vec.push_back(1); + vec.push_back(2); + vec.push_back(3); + vec.swap(1, 1); + assert!(vec[0] == 0); + assert!(vec[1] == 1); + assert!(vec[2] == 2); + assert!(vec[3] == 3); } #[test] fun remove_singleton_vector() { - let v = V::empty(); - V::push_back(&mut v, 0); - assert!(V::remove(&mut v, 0) == 0, 0); - assert!(V::length(&v) == 0, 0); + let mut v = vector[]; + v.push_back(0); + assert!(v.remove(0) == 0); + assert!(v.length() == 0); } #[test] fun remove_nonsingleton_vector() { - let v = V::empty(); - V::push_back(&mut v, 0); - V::push_back(&mut v, 1); - V::push_back(&mut v, 2); - V::push_back(&mut v, 3); + let mut v = vector[]; + v.push_back(0); + v.push_back(1); + v.push_back(2); + v.push_back(3); - assert!(V::remove(&mut v, 1) == 1, 0); - assert!(V::length(&v) == 3, 0); - assert!(*V::borrow(&v, 0) == 0, 0); - assert!(*V::borrow(&v, 1) == 2, 0); - assert!(*V::borrow(&v, 2) == 3, 0); + assert!(v.remove(1) == 1); + assert!(v.length() == 3); + assert!(v[0] == 0); + assert!(v[1] == 2); + assert!(v[2] == 3); } #[test] fun remove_nonsingleton_vector_last_elem() { - let v = V::empty(); - V::push_back(&mut v, 0); - V::push_back(&mut v, 1); - V::push_back(&mut v, 2); - V::push_back(&mut v, 3); + let mut v = vector[]; + v.push_back(0); + v.push_back(1); + v.push_back(2); + v.push_back(3); - assert!(V::remove(&mut v, 3) == 3, 0); - assert!(V::length(&v) == 3, 0); - assert!(*V::borrow(&v, 0) == 0, 0); - assert!(*V::borrow(&v, 1) == 1, 0); - assert!(*V::borrow(&v, 2) == 2, 0); + assert!(v.remove(3) == 3); + assert!(v.length() == 3); + assert!(v[0] == 0); + assert!(v[1] == 1); + assert!(v[2] == 2); } #[test] - #[expected_failure(abort_code = V::EINDEX_OUT_OF_BOUNDS)] + #[expected_failure(abort_code = vector::EINDEX_OUT_OF_BOUNDS)] fun remove_empty_vector() { - let v = V::empty(); - V::remove(&mut v, 0); + let mut v = vector[]; + v.remove(0); } #[test] - #[expected_failure(abort_code = V::EINDEX_OUT_OF_BOUNDS)] + #[expected_failure(abort_code = vector::EINDEX_OUT_OF_BOUNDS)] fun remove_out_of_bound_index() { - let v = V::empty(); - V::push_back(&mut v, 0); - V::remove(&mut v, 1); + let mut v = vector[]; + v.push_back(0); + v.remove(1); } #[test] fun reverse_vector_empty() { - let v = V::empty(); - let is_empty = V::is_empty(&v); - V::reverse(&mut v); - assert!(is_empty == V::is_empty(&v), 0); + let mut v = vector[]; + let is_empty = v.is_empty(); + v.reverse(); + assert!(is_empty == v.is_empty()); } #[test] fun reverse_singleton_vector() { - let v = V::empty(); - V::push_back(&mut v, 0); - assert!(*V::borrow(&v, 0) == 0, 1); - V::reverse(&mut v); - assert!(*V::borrow(&v, 0) == 0, 2); + let mut v = vector[]; + v.push_back(0); + assert!(v[0] == 0); + v.reverse(); + assert!(v[0] == 0); } #[test] fun reverse_vector_nonempty_even_length() { - let v = V::empty(); - V::push_back(&mut v, 0); - V::push_back(&mut v, 1); - V::push_back(&mut v, 2); - V::push_back(&mut v, 3); + let mut v = vector[]; + v.push_back(0); + v.push_back(1); + v.push_back(2); + v.push_back(3); - assert!(*V::borrow(&v, 0) == 0, 1); - assert!(*V::borrow(&v, 1) == 1, 2); - assert!(*V::borrow(&v, 2) == 2, 3); - assert!(*V::borrow(&v, 3) == 3, 4); + assert!(v[0] == 0); + assert!(v[1] == 1); + assert!(v[2] == 2); + assert!(v[3] == 3); - V::reverse(&mut v); + v.reverse(); - assert!(*V::borrow(&v, 3) == 0, 5); - assert!(*V::borrow(&v, 2) == 1, 6); - assert!(*V::borrow(&v, 1) == 2, 7); - assert!(*V::borrow(&v, 0) == 3, 8); + assert!(v[3] == 0); + assert!(v[2] == 1); + assert!(v[1] == 2); + assert!(v[0] == 3); } #[test] fun reverse_vector_nonempty_odd_length_non_singleton() { - let v = V::empty(); - V::push_back(&mut v, 0); - V::push_back(&mut v, 1); - V::push_back(&mut v, 2); + let mut v = vector[]; + v.push_back(0); + v.push_back(1); + v.push_back(2); - assert!(*V::borrow(&v, 0) == 0, 1); - assert!(*V::borrow(&v, 1) == 1, 2); - assert!(*V::borrow(&v, 2) == 2, 3); + assert!(v[0] == 0); + assert!(v[1] == 1); + assert!(v[2] == 2); - V::reverse(&mut v); + v.reverse(); - assert!(*V::borrow(&v, 2) == 0, 4); - assert!(*V::borrow(&v, 1) == 1, 5); - assert!(*V::borrow(&v, 0) == 2, 6); + assert!(v[2] == 0); + assert!(v[1] == 1); + assert!(v[0] == 2); } #[test] #[expected_failure(vector_error, minor_status = 1, location = Self)] fun swap_empty() { - let v = V::empty(); - V::swap(&mut v, 0, 0); + let mut v = vector[]; + v.swap(0, 0); } #[test] #[expected_failure(vector_error, minor_status = 1, location = Self)] fun swap_out_of_range() { - let v = V::empty(); + let mut v = vector[]; - V::push_back(&mut v, 0); - V::push_back(&mut v, 1); - V::push_back(&mut v, 2); - V::push_back(&mut v, 3); + v.push_back(0); + v.push_back(1); + v.push_back(2); + v.push_back(3); - V::swap(&mut v, 1, 10); + v.swap(1, 10); } #[test] - #[expected_failure(abort_code = V::EINDEX_OUT_OF_BOUNDS)] + #[expected_failure(abort_code = std::vector::EINDEX_OUT_OF_BOUNDS)] fun swap_remove_empty() { - let v = V::empty(); - V::swap_remove(&mut v, 0); + let mut v = vector[]; + v.swap_remove(0); } #[test] fun swap_remove_singleton() { - let v = V::empty(); - V::push_back(&mut v, 0); - assert!(V::swap_remove(&mut v, 0) == 0, 0); - assert!(V::is_empty(&v), 1); + let mut v = vector[]; + v.push_back(0); + assert!(v.swap_remove(0) == 0); + assert!(v.is_empty()); } #[test] fun swap_remove_inside_vector() { - let v = V::empty(); - V::push_back(&mut v, 0); - V::push_back(&mut v, 1); - V::push_back(&mut v, 2); - V::push_back(&mut v, 3); + let mut v = vector[]; + v.push_back(0); + v.push_back(1); + v.push_back(2); + v.push_back(3); - assert!(*V::borrow(&v, 0) == 0, 1); - assert!(*V::borrow(&v, 1) == 1, 2); - assert!(*V::borrow(&v, 2) == 2, 3); - assert!(*V::borrow(&v, 3) == 3, 4); + assert!(v[0] == 0); + assert!(v[1] == 1); + assert!(v[2] == 2); + assert!(v[3] == 3); - assert!(V::swap_remove(&mut v, 1) == 1, 5); - assert!(V::length(&v) == 3, 6); + assert!(v.swap_remove(1) == 1); + assert!(v.length() == 3); - assert!(*V::borrow(&v, 0) == 0, 7); - assert!(*V::borrow(&v, 1) == 3, 8); - assert!(*V::borrow(&v, 2) == 2, 9); + assert!(v[0] == 0); + assert!(v[1] == 3); + assert!(v[2] == 2); } #[test] fun swap_remove_end_of_vector() { - let v = V::empty(); - V::push_back(&mut v, 0); - V::push_back(&mut v, 1); - V::push_back(&mut v, 2); - V::push_back(&mut v, 3); + let mut v = vector[]; + v.push_back(0); + v.push_back(1); + v.push_back(2); + v.push_back(3); - assert!(*V::borrow(&v, 0) == 0, 1); - assert!(*V::borrow(&v, 1) == 1, 2); - assert!(*V::borrow(&v, 2) == 2, 3); - assert!(*V::borrow(&v, 3) == 3, 4); + assert!(v[0] == 0); + assert!(v[1] == 1); + assert!(v[2] == 2); + assert!(v[3] == 3); - assert!(V::swap_remove(&mut v, 3) == 3, 5); - assert!(V::length(&v) == 3, 6); + assert!(v.swap_remove(3) == 3); + assert!(v.length() == 3); - assert!(*V::borrow(&v, 0) == 0, 7); - assert!(*V::borrow(&v, 1) == 1, 8); - assert!(*V::borrow(&v, 2) == 2, 9); + assert!(v[0] == 0); + assert!(v[1] == 1); + assert!(v[2] == 2); } #[test] #[expected_failure(vector_error, minor_status = 1, location = std::vector)] fun swap_remove_out_of_range() { - let v = V::empty(); - V::push_back(&mut v, 0); - V::swap_remove(&mut v, 1); + let mut v = vector[]; + v.push_back(0); + v.swap_remove(1); } #[test] fun push_back_and_borrow() { - let v = V::empty(); - V::push_back(&mut v, 7); - assert!(!V::is_empty(&v), 0); - assert!(V::length(&v) == 1, 1); - assert!(*V::borrow(&v, 0) == 7, 2); + let mut v = vector[]; + v.push_back(7); + assert!(!v.is_empty()); + assert!(v.length() == 1); + assert!(v[0] == 7); - V::push_back(&mut v, 8); - assert!(V::length(&v) == 2, 3); - assert!(*V::borrow(&v, 0) == 7, 4); - assert!(*V::borrow(&v, 1) == 8, 5); + v.push_back(8); + assert!(v.length() == 2); + assert!(v[0] == 7); + assert!(v[1] == 8); } #[test] fun index_of_empty_not_has() { - let v = V::empty(); - let (has, index) = V::index_of(&v, &true); - assert!(!has, 0); - assert!(index == 0, 1); + let v = vector[]; + let (has, index) = v.index_of(&true); + assert!(!has); + assert!(index == 0); } #[test] fun index_of_nonempty_not_has() { - let v = V::empty(); - V::push_back(&mut v, false); - let (has, index) = V::index_of(&v, &true); - assert!(!has, 0); - assert!(index == 0, 1); + let mut v = vector[]; + v.push_back(false); + let (has, index) = v.index_of(&true); + assert!(!has); + assert!(index == 0); } #[test] fun index_of_nonempty_has() { - let v = V::empty(); - V::push_back(&mut v, false); - V::push_back(&mut v, true); - let (has, index) = V::index_of(&v, &true); - assert!(has, 0); - assert!(index == 1, 1); + let mut v = vector[]; + v.push_back(false); + v.push_back(true); + let (has, index) = v.index_of(&true); + assert!(has); + assert!(index == 1); } // index_of will return the index first occurence that is equal #[test] fun index_of_nonempty_has_multiple_occurences() { - let v = V::empty(); - V::push_back(&mut v, false); - V::push_back(&mut v, true); - V::push_back(&mut v, true); - let (has, index) = V::index_of(&v, &true); - assert!(has, 0); - assert!(index == 1, 1); + let mut v = vector[]; + v.push_back(false); + v.push_back(true); + v.push_back(true); + let (has, index) = v.index_of(&true); + assert!(has); + assert!(index == 1); } #[test] fun length() { - let empty = V::empty(); - assert!(V::length(&empty) == 0, 0); - let i = 0; + let mut empty = vector[]; + assert!(empty.length() == 0); + let mut i = 0; let max_len = 42; while (i < max_len) { - V::push_back(&mut empty, i); - assert!(V::length(&empty) == i + 1, i); + empty.push_back(i); + assert!(empty.length() == i + 1, i); i = i + 1; } } #[test] fun pop_push_back() { - let v = V::empty(); - let i = 0; + let mut v = vector[]; + let mut i = 0; let max_len = 42; while (i < max_len) { - V::push_back(&mut v, i); + v.push_back(i); i = i + 1; }; while (i > 0) { - assert!(V::pop_back(&mut v) == i - 1, i); + assert!(v.pop_back() == i - 1, i); i = i - 1; }; } #[test_only] - fun test_natives_with_type(x1: T, x2: T): (T, T) { - let v = V::empty(); - assert!(V::length(&v) == 0, 0); - V::push_back(&mut v, x1); - assert!(V::length(&v) == 1, 1); - V::push_back(&mut v, x2); - assert!(V::length(&v) == 2, 2); - V::swap(&mut v, 0, 1); - x1 = V::pop_back(&mut v); - assert!(V::length(&v) == 1, 3); - x2 = V::pop_back(&mut v); - assert!(V::length(&v) == 0, 4); - V::destroy_empty(v); + fun test_natives_with_type(mut x1: T, mut x2: T): (T, T) { + let mut v = vector[]; + assert!(v.length() == 0); + v.push_back(x1); + assert!(v.length() == 1); + v.push_back(x2); + assert!(v.length() == 2); + v.swap(0, 1); + x1 = v.pop_back(); + assert!(v.length() == 1); + x2 = v.pop_back(); + assert!(v.length() == 0); + v.destroy_empty(); (x1, x2) } @@ -495,7 +500,7 @@ module std::vector_tests { test_natives_with_type(true, false); test_natives_with_type
(@0x1, @0x2); - test_natives_with_type>(V::empty(), V::empty()); + test_natives_with_type>(vector[], vector[]); test_natives_with_type(Droppable{}, Droppable{}); (NotDroppable {}, NotDroppable {}) = test_natives_with_type( @@ -506,64 +511,279 @@ module std::vector_tests { #[test] fun test_insert() { - let v = vector[7]; - V::insert(&mut v, 6, 0); - assert!(v == vector[6, 7], 0); + let mut v = vector[7]; + v.insert(6, 0); + assert!(v == vector[6, 7]); - let v = vector[7, 9]; - V::insert(&mut v, 8, 1); - assert!(v == vector[7, 8, 9], 0); + let mut v = vector[7, 9]; + v.insert(8, 1); + assert!(v == vector[7, 8, 9]); - let v = vector[6, 7]; - V::insert(&mut v, 5, 0); - assert!(v == vector[5, 6, 7], 0); + let mut v = vector[6, 7]; + v.insert(5, 0); + assert!(v == vector[5, 6, 7]); - let v = vector[5, 6, 8]; - V::insert(&mut v, 7, 2); - assert!(v == vector[5, 6, 7, 8], 0); + let mut v = vector[5, 6, 8]; + v.insert(7, 2); + assert!(v == vector[5, 6, 7, 8]); } #[test] fun insert_at_end() { - let v = vector[]; - V::insert(&mut v, 6, 0); - assert!(v == vector[6], 0); + let mut v = vector[]; + v.insert(6, 0); + assert!(v == vector[6]); - V::insert(&mut v, 7, 1); - assert!(v == vector[6, 7], 0); + v.insert(7, 1); + assert!(v == vector[6, 7]); } #[test] - #[expected_failure(abort_code = V::EINDEX_OUT_OF_BOUNDS)] + #[expected_failure(abort_code = std::vector::EINDEX_OUT_OF_BOUNDS)] fun insert_out_of_range() { - let v = vector[7]; - V::insert(&mut v, 6, 2); + let mut v = vector[7]; + v.insert(6, 2); } #[test] fun size_limit_ok() { - let v = V::empty(); - let i = 0; - // Limit is currently 1024 * 1024 - let max_len = 1024 * 1024; + let mut v = vector[]; + let mut i = 0; + // Limit is currently 1024 * 54 + let max_len = 1024 * 53; while (i < max_len) { - V::push_back(&mut v, i); + v.push_back(i); i = i + 1; }; } #[test] - #[expected_failure(vector_error, minor_status = 4, location = Self)] + #[expected_failure(out_of_gas, location = Self)] fun size_limit_fail() { - let v = V::empty(); - let i = 0; - // Limit is currently 1024 * 1024 - let max_len = 1024 * 1024 + 1; + let mut v = vector[]; + let mut i = 0; + // Choose value beyond limit + let max_len = 1024 * 1024; while (i < max_len) { - V::push_back(&mut v, i); + v.push_back(i); i = i + 1; }; } + + #[test] + fun test_string_aliases() { + assert!(b"hello_world".to_string().length() == 11); + assert!(b"hello_world".try_to_string().is_some()); + + assert!(b"hello_world".to_ascii_string().length() == 11); + assert!(b"hello_world".try_to_ascii_string().is_some()); + } + + // === Macros === + + #[test] + fun test_destroy_macro() { + vector[].destroy!(|_| assert!(false)); // very funky + + let mut acc = 0; + vector[10, 20, 30, 40].destroy!(|e| acc = acc + e); + assert!(acc == 100); + } + + #[test] + fun test_count_macro() { + assert!(vector[].count!(|e| *e == 2) == 0); + assert!(vector[0, 1, 2, 3].count!(|e| *e == 2) == 1); + assert!(vector[0, 1, 2, 3].count!(|e| *e % 2 == 0) == vector[0, 2].length()); + } + + #[test] + fun test_tabulate_macro() { + let v = vector::tabulate!(10, |i| i); + assert!(v == vector[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + + let v = vector::tabulate!(5, |i| 10 - i); + assert!(v == vector[10, 9, 8, 7, 6]); + + let v = vector::tabulate!(0, |i| i); + assert!(v == vector[]); + } + + #[test] + fun test_do_macro() { + vector[].do!(|_| assert!(false)); // should never run + vector[].do_ref!(|_| assert!(false)); + vector[].do_mut!(|_| assert!(false)); + + let mut acc = 0; + vector[10, 20, 30, 40].do!(|e| acc = acc + e); + assert!(acc == 100); + + let vec = vector[10, 20]; + vec.do!(|e| acc = acc + e); + assert!(vector[10, 20] == vec); + + let mut acc = 0; + vector[10, 20, 30, 40].do_ref!(|e| acc = acc + *e); + assert!(acc == 100); + + let mut vec = vector[10, 20, 30, 40]; + vec.do_mut!(|e| *e = *e + 1); + assert!(vec == vector[11, 21, 31, 41]); + } + + #[test] + fun test_map_macro() { + let e = vector[]; + assert!(e.map!(|e| e + 1) == vector[]); + + let r = vector[0, 1, 2, 3]; + assert!(r.map!(|e| e + 1) == vector[1, 2, 3, 4]); + + let r = vector[0, 1, 2, 3]; + assert!(r.map_ref!(|e| *e * 2) == vector[0, 2, 4, 6]); + } + + #[test] + fun filter_macro() { + let e = vector[]; + assert!(e.filter!(|e| *e % 2 == 0) == vector[]); + + let r = vector[0, 1, 2, 3]; + assert!(r.filter!(|e| *e % 2 == 0) == vector[0, 2]); + } + + #[test] + fun partition_macro() { + let e = vector[]; + let (even, odd) = e.partition!(|e| (*e % 2) == 0); + assert!(even == vector[]); + assert!(odd == vector[]); + + let r = vector[0, 1, 2, 3]; + let (even, odd) = r.partition!(|e| (*e % 2) == 0); + assert!(even == vector[0, 2]); + assert!(odd == vector[1, 3]); + } + + #[test] + fun find_index_macro() { + let e = vector[]; + assert!(e.find_index!(|e| *e == 0).is_none()); + assert!(e.find_index!(|_| true).is_none()); + + let r = vector[0, 10, 100, 1_000]; + assert!(r.find_index!(|e| *e == 100).destroy_some() == 2); + assert!(r.find_index!(|e| *e == 10_000).is_none()); + } + + #[test] + fun fold_macro() { + let e = vector[]; + assert!(e.fold!(0, |acc, e| acc + e) == 0); + + let r = vector[0, 1, 2, 3]; + assert!(r.fold!(10, |acc, e| acc + e) == 16); + } + + #[test] + fun any_all_macro() { + assert!(vector[].any!(|e| *e == 2) == false); + assert!(vector[].all!(|e| *e == 2) == true); + assert!(vector[0, 1, 2, 3].any!(|e| *e == 2)); + assert!(!vector[0, 1, 2, 3].any!(|e| *e == 4)); + assert!(vector[0, 1, 2, 3].all!(|e| *e < 4)); + assert!(!vector[0, 1, 2, 3].all!(|e| *e < 3)); + } + + #[test, expected_failure] + fun zip_do_macro_fail() { + let v1 = vector[1u64]; + let v2 = vector[4u64, 5]; + let mut res = vector[]; + v1.zip_do!(v2, |a, b| res.push_back(a + b)); + } + + #[test] + fun zip_do_macro() { + let v1 = vector[1u64, 2, 3]; + let v2 = vector[4u64, 5, 6]; + let mut res = vector[]; + v1.zip_do!(v2, |a, b| res.push_back(a + b)); + assert!(res == vector[5, 7, 9]); + } + + #[test, expected_failure] + fun zip_do_reverse_macro_fail() { + let v1 = vector[1u64]; + let v2 = vector[4u64, 5]; + let mut res = vector[]; + v2.zip_do_reverse!(v1, |a, b| res.push_back(a + b)); + } + + #[test] + fun zip_do_reverse_macro() { + let v1 = vector[1u64, 2, 3]; + let v2 = vector[4u64, 5, 6]; + let mut res = vector[]; + v2.zip_do_reverse!(v1, |a, b| res.push_back(a + b)); + assert!(res == vector[9, 7, 5]); + } + + #[test, expected_failure] + fun zip_do_ref_macro_fail() { + let v1 = vector[1u64]; + let v2 = vector[4u64, 5]; + let mut res = vector[]; + v2.zip_do_ref!(&v1, |a, b| res.push_back(*a + *b)); + } + + #[test] + fun zip_do_ref_macro() { + let v1 = vector[1u64, 2, 3]; + let v2 = vector[4u64, 5, 6]; + let mut res = vector[]; + v1.zip_do_ref!(&v2, |a, b| res.push_back(*a + *b)); + assert!(res == vector[5, 7, 9]); + } + + #[test, expected_failure] + fun zip_do_mut_macro_fail() { + let mut v1 = vector[1u64]; + let mut v2 = vector[4u64, 5]; + v1.zip_do_mut!(&mut v2, |a, b| { + let c = *a; + *a = *b; + *b = c; + }); + } + + #[test] + fun zip_do_mut_macro() { + let mut v1 = vector[1u64, 2, 3]; + let mut v2 = vector[4u64, 5, 6]; + v1.zip_do_mut!(&mut v2, |a, b| { + let c = *a; + *a = *b; + *b = c; + }); + assert!(v1 == vector[4, 5, 6]); + assert!(v2 == vector[1, 2, 3]); + } + + #[test] + fun zip_map_macro() { + let v1 = vector[1u64, 2, 3]; + let v2 = vector[4u64, 5, 6]; + assert!(v1.zip_map!(v2, |a, b| a + b) == vector[5, 7, 9]); + } + + #[test] + fun zip_map_ref_macro() { + let v1 = vector[1u64, 2, 3]; + let v2 = vector[4u64, 5, 6]; + assert!(v2.zip_map_ref!(&v1, |a, b| *a + *b) == vector[5, 7, 9]); + } } diff --git a/external-crates/move/crates/move-transactional-test-runner/src/vm_test_harness.rs b/external-crates/move/crates/move-transactional-test-runner/src/vm_test_harness.rs index 9ae5ca713a235..bfa9734d737e8 100644 --- a/external-crates/move/crates/move-transactional-test-runner/src/vm_test_harness.rs +++ b/external-crates/move/crates/move-transactional-test-runner/src/vm_test_harness.rs @@ -274,6 +274,7 @@ impl SimpleVMTestAdapter { STD_ADDR, // TODO: come up with a suitable gas schedule move_stdlib_natives::GasParameters::zeros(), + /* silent */ false, ), vm_config, ) diff --git a/external-crates/move/crates/move-unit-test/src/test_runner.rs b/external-crates/move/crates/move-unit-test/src/test_runner.rs index 186d45575fa12..9cc5b87c9ca0f 100644 --- a/external-crates/move/crates/move-unit-test/src/test_runner.rs +++ b/external-crates/move/crates/move-unit-test/src/test_runner.rs @@ -126,6 +126,7 @@ impl TestRunner { move_stdlib_natives::all_natives( AccountAddress::from_hex_literal("0x1").unwrap(), move_stdlib_natives::GasParameters::zeros(), + /* silent */ false, ) }); Ok(Self { diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/address_args.move b/external-crates/move/crates/move-unit-test/tests/test_sources/address_args.move index 4faabd02d0ba4..bb56acb2f988e 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/address_args.move +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/address_args.move @@ -1,5 +1,4 @@ -address 0x1 { -module M { +module 0x1::M { const ErrorCode: u64 = 100; #[test(a = @0x42)] @@ -19,4 +18,3 @@ module M { assert!(a == @0x43, 100); } } -} diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/arithmetic_errors.move b/external-crates/move/crates/move-unit-test/tests/test_sources/arithmetic_errors.move index 74185c75d5e20..d4a9cb199d735 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/arithmetic_errors.move +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/arithmetic_errors.move @@ -1,5 +1,4 @@ -address 0x1 { -module M { +module 0x1::M { #[test] #[expected_failure] fun u64_sub_underflow() { @@ -24,4 +23,3 @@ module M { 4294967296 * 4294967296; } } -} diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/construct_data.move b/external-crates/move/crates/move-unit-test/tests/test_sources/construct_data.move index 7a6da03ba315f..8876a204089e9 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/construct_data.move +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/construct_data.move @@ -1,7 +1,6 @@ -address 0x1 { -module B { +module 0x1::B { #[test_only] - struct TestingStruct has drop { x: u64 } + public struct TestingStruct has drop { x: u64 } #[test_only] public fun construct_with_number(x: u64): TestingStruct { @@ -14,7 +13,7 @@ module B { } } -module M { +module 0x1::M { #[test_only] use 0x1::B; @@ -30,4 +29,3 @@ module M { assert!(B::get_struct_x_field(&s) != 0, 0); } } -} diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/cross_module_aborts.exp b/external-crates/move/crates/move-unit-test/tests/test_sources/cross_module_aborts.exp index d2d0ae63e60c7..6e7e37be0ed86 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/cross_module_aborts.exp +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/cross_module_aborts.exp @@ -8,16 +8,16 @@ Failures in 0x1::B: ┌── failing_test ────── │ error[E11001]: test failure -│ ┌─ cross_module_aborts.move:5:9 +│ ┌─ cross_module_aborts.move:4:9 │ │ -│ 4 │ public fun this_aborts() { +│ 3 │ public fun this_aborts() { │ │ ----------- In this function in 0x1::M -│ 5 │ abort 0 +│ 4 │ abort 0 │ │ ^^^^^^^ Test was not expected to error, but it aborted with code 0 originating in the module 0x1::M rooted here │ │ │ stack trace -│ B::failing_test(tests/test_sources/cross_module_aborts.move:19) +│ B::failing_test(tests/test_sources/cross_module_aborts.move:18) │ └────────────────── diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/cross_module_aborts.move b/external-crates/move/crates/move-unit-test/tests/test_sources/cross_module_aborts.move index 519690185ebb0..2ab099fc48c06 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/cross_module_aborts.move +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/cross_module_aborts.move @@ -1,5 +1,4 @@ -address 0x1 { -module M { +module 0x1::M { #[test_only] public fun this_aborts() { abort 0 @@ -9,7 +8,7 @@ module M { fun dummy_test() { } } -module B { +module 0x1::B { #[test_only] use 0x1::M; @@ -19,4 +18,3 @@ module B { M::this_aborts() } } -} diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/do_nothing.move b/external-crates/move/crates/move-unit-test/tests/test_sources/do_nothing.move index 8aa9bb708bd51..15fe4a72923ed 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/do_nothing.move +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/do_nothing.move @@ -1,6 +1,4 @@ -address 0x1 { -module M { +module 0x1::M { #[test] fun do_nothing() {} } -} diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/expected_abort_no_abort.move b/external-crates/move/crates/move-unit-test/tests/test_sources/expected_abort_no_abort.move index e7c022e8366b3..fc94d10c56240 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/expected_abort_no_abort.move +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/expected_abort_no_abort.move @@ -1,9 +1,7 @@ -address 0x1 { -module M { +module 0x1::M { #[test, expected_failure] fun fail() { } #[test, expected_failure(abort_code=0, location=0x1::M)] fun fail_with_code() { } } -} diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/native_abort.exp b/external-crates/move/crates/move-unit-test/tests/test_sources/native_abort.exp index ad65b7f3924c6..e62e47b3f0368 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/native_abort.exp +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/native_abort.exp @@ -9,11 +9,11 @@ Failures in 0x1::A: ┌── native_abort_good_wrong_code ────── │ error[E11001]: test failure -│ ┌─ native_abort.move:12:9 +│ ┌─ native_abort.move:11:9 │ │ -│ 11 │ fun native_abort_good_wrong_code() { +│ 10 │ fun native_abort_good_wrong_code() { │ │ ---------------------------- In this function in 0x1::A -│ 12 │ vector::borrow(&vector::empty(), 1); +│ 11 │ vector::borrow(&vector::empty(), 1); │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Test did not error as expected. Expected test to give a vector operation error with sub-status 0 originating in the module 0x1::A but instead it gave a vector operation error with sub-status 1 originating in the module 0x1::A rooted here │ │ @@ -22,11 +22,11 @@ Failures in 0x1::A: ┌── native_abort_unexpected_abort ────── │ error[E11001]: test failure -│ ┌─ native_abort.move:6:9 +│ ┌─ native_abort.move:5:9 │ │ -│ 5 │ fun native_abort_unexpected_abort() { +│ 4 │ fun native_abort_unexpected_abort() { │ │ ----------------------------- In this function in 0x1::A -│ 6 │ vector::borrow(&vector::empty(), 1); +│ 5 │ vector::borrow(&vector::empty(), 1); │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Test was not expected to error, but it gave a vector operation error with sub-status 1 originating in the module 0x1::A rooted here │ │ diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/native_abort.move b/external-crates/move/crates/move-unit-test/tests/test_sources/native_abort.move index ca5ec24c188de..53203d68f1a84 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/native_abort.move +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/native_abort.move @@ -1,5 +1,4 @@ module 0x1::A { - use std::vector; #[test] fun native_abort_unexpected_abort() { diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/non_exsistent_native.move b/external-crates/move/crates/move-unit-test/tests/test_sources/non_exsistent_native.move index 2dd99823a43b4..b623fa3170c51 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/non_exsistent_native.move +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/non_exsistent_native.move @@ -1,5 +1,4 @@ -address 0x1 { -module M { +module 0x1::M { native fun foo(); #[test] @@ -7,4 +6,3 @@ module M { foo() } } -} diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/proposal_test.move b/external-crates/move/crates/move-unit-test/tests/test_sources/proposal_test.move index 4c5b0c42c7444..40d74305afa16 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/proposal_test.move +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/proposal_test.move @@ -1,13 +1,12 @@ // This is a test based on the example in the unit testing proposal -address 0x1 { -module TestonlyModule { +module 0x1::TestonlyModule { #[test_only] public fun aborts() { abort 42 } } -module Module { +module 0x1::Module { fun a(a: u64): bool { a == 10 } @@ -26,7 +25,7 @@ module Module { // A test only struct. This will only be included in test mode. #[test_only, allow(unused_field)] - struct C has drop, key, store { x: T } + public struct C has drop, key, store { x: T } #[test] // test entry point. fun tests_a() { // an actual test that will be run @@ -47,4 +46,3 @@ module Module { TestonlyModule::aborts() } } -} diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/signer_args.exp b/external-crates/move/crates/move-unit-test/tests/test_sources/signer_args.exp index 08c535f42f67e..b4f7f0f84b3f6 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/signer_args.exp +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/signer_args.exp @@ -17,11 +17,11 @@ Failures in 0x1::M: ┌── single_signer_fail ────── │ error[E11001]: test failure -│ ┌─ signer_args.move:9:9 +│ ┌─ signer_args.move:8:9 │ │ -│ 8 │ fun single_signer_fail(_a: signer) { +│ 7 │ fun single_signer_fail(_a: signer) { │ │ ------------------ In this function in 0x1::M -│ 9 │ abort 0 +│ 8 │ abort 0 │ │ ^^^^^^^ Test was not expected to error, but it aborted with code 0 originating in the module 0x1::M rooted here │ │ diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/signer_args.move b/external-crates/move/crates/move-unit-test/tests/test_sources/signer_args.move index 7b8edcb5dd3e0..9da46ca0a99e5 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/signer_args.move +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/signer_args.move @@ -1,5 +1,4 @@ -address 0x1 { -module M { +module 0x1::M { #[test(_a=@0x1)] fun single_signer_pass(_a: signer) { } @@ -20,13 +19,8 @@ module M { abort 0 } - #[test_only] - use std::signer; - #[test(a=@0x1, b=@0x2)] fun test_correct_signer_arg_addrs(a: signer, b: signer) { - assert!(signer::address_of(&a) == @0x1, 0); - assert!(signer::address_of(&b) == @0x2, 1); + assert!(a != b) } } -} diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/timeout.exp b/external-crates/move/crates/move-unit-test/tests/test_sources/timeout.exp index 9fe952826d7eb..05c32d5cdd88e 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/timeout.exp +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/timeout.exp @@ -11,9 +11,9 @@ Failures in 0x1::M: ┌── no_timeout_fail ────── │ error[E11001]: test failure -│ ┌─ timeout.move:18:29 +│ ┌─ timeout.move:17:29 │ │ -│ 18 │ fun no_timeout_fail() { abort 0 } +│ 17 │ fun no_timeout_fail() { abort 0 } │ │ --------------- ^^^^^^^ Test was not expected to error, but it aborted with code 0 originating in the module 0x1::M rooted here │ │ │ │ │ In this function in 0x1::M diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/timeout.move b/external-crates/move/crates/move-unit-test/tests/test_sources/timeout.move index 1c045830269af..b772e68a35d88 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/timeout.move +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/timeout.move @@ -1,5 +1,4 @@ -address 0x1 { -module M { +module 0x1::M { #[test] fun timeout_fail() { while (true) {} @@ -19,10 +18,9 @@ module M { #[test] fun no_timeout_while_loop() { - let i = 0; + let mut i = 0; while (i < 10) { i = i + 1; }; } } -} diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/unexpected_abort.exp b/external-crates/move/crates/move-unit-test/tests/test_sources/unexpected_abort.exp index ea763695bbc0e..7aeae31d736f8 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/unexpected_abort.exp +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/unexpected_abort.exp @@ -12,11 +12,11 @@ Failures in 0x1::M: ┌── unexpected_abort ────── │ error[E11001]: test failure -│ ┌─ unexpected_abort.move:5:9 +│ ┌─ unexpected_abort.move:4:9 │ │ -│ 4 │ public fun unexpected_abort() { +│ 3 │ public fun unexpected_abort() { │ │ ---------------- In this function in 0x1::M -│ 5 │ abort 0 +│ 4 │ abort 0 │ │ ^^^^^^^ Test was not expected to error, but it aborted with code 0 originating in the module 0x1::M rooted here │ │ @@ -25,45 +25,45 @@ Failures in 0x1::M: ┌── unexpected_abort_in_native_function ────── │ error[E11001]: test failure -│ ┌─ string.move:92:16 -│ │ -│ 92 │ native fun internal_sub_string(v: &vector, i: u64, j: u64): vector; -│ │ ^^^^^^^^^^^^^^^^^^^ -│ │ │ -│ │ Test was not expected to error, but it aborted with code 1 originating in the module std::string rooted here -│ │ In this function in std::string +│ ┌─ string.move:120:16 +│ │ +│ 120 │ native fun internal_sub_string(v: &vector, i: u64, j: u64): vector; +│ │ ^^^^^^^^^^^^^^^^^^^ +│ │ │ +│ │ Test was not expected to error, but it aborted with code 1 originating in the module std::string rooted here +│ │ In this function in std::string │ │ │ stack trace -│ M::abort_in_native(tests/test_sources/unexpected_abort.move:43) -│ M::unexpected_abort_in_native_function(tests/test_sources/unexpected_abort.move:39) +│ M::abort_in_native(tests/test_sources/unexpected_abort.move:42) +│ M::unexpected_abort_in_native_function(tests/test_sources/unexpected_abort.move:38) │ └────────────────── ┌── unexpected_abort_in_other_function ────── │ error[E11001]: test failure -│ ┌─ unexpected_abort.move:28:9 +│ ┌─ unexpected_abort.move:27:9 │ │ -│ 27 │ fun abort_in_other_function() { +│ 26 │ fun abort_in_other_function() { │ │ ----------------------- In this function in 0x1::M -│ 28 │ abort 1 +│ 27 │ abort 1 │ │ ^^^^^^^ Test was not expected to error, but it aborted with code 1 originating in the module 0x1::M rooted here │ │ │ stack trace -│ M::unexpected_abort_in_other_function(tests/test_sources/unexpected_abort.move:33) +│ M::unexpected_abort_in_other_function(tests/test_sources/unexpected_abort.move:32) │ └────────────────── ┌── wrong_abort_code ────── │ error[E11001]: test failure -│ ┌─ unexpected_abort.move:11:9 +│ ┌─ unexpected_abort.move:10:9 │ │ -│ 10 │ public fun wrong_abort_code() { +│ 9 │ public fun wrong_abort_code() { │ │ ---------------- In this function in 0x1::M -│ 11 │ abort 0 +│ 10 │ abort 0 │ │ ^^^^^^^ Test did not error as expected. Expected test to abort with code 1 originating in the module 0x1::M but instead it aborted with code 0 originating in the module 0x1::M rooted here │ │ diff --git a/external-crates/move/crates/move-unit-test/tests/test_sources/unexpected_abort.move b/external-crates/move/crates/move-unit-test/tests/test_sources/unexpected_abort.move index 2eaa062d07abc..1b81628abfd15 100644 --- a/external-crates/move/crates/move-unit-test/tests/test_sources/unexpected_abort.move +++ b/external-crates/move/crates/move-unit-test/tests/test_sources/unexpected_abort.move @@ -1,5 +1,4 @@ -address 0x1 { -module M { +module 0x1::M { #[test] public fun unexpected_abort() { abort 0 @@ -43,4 +42,3 @@ module M { std::string::internal_sub_string_for_testing(&vector[0], 1, 0); } } -} diff --git a/external-crates/move/crates/move-vm-integration-tests/Cargo.toml b/external-crates/move/crates/move-vm-integration-tests/Cargo.toml index 0df741d16e3dc..370d32a3e43e0 100644 --- a/external-crates/move/crates/move-vm-integration-tests/Cargo.toml +++ b/external-crates/move/crates/move-vm-integration-tests/Cargo.toml @@ -18,7 +18,7 @@ tempfile.workspace = true memory-stats = "1.0.0" move-core-types.workspace = true -move-binary-format.workspace = true +move-binary-format = { workspace = true, features = ["fuzzing"] } move-bytecode-verifier.workspace = true move-compiler.workspace = true move-vm-config.workspace = true diff --git a/external-crates/move/crates/move-vm-integration-tests/src/tests/binary_format_version.rs b/external-crates/move/crates/move-vm-integration-tests/src/tests/binary_format_version.rs index 11490fe82b292..7a39280c5997e 100644 --- a/external-crates/move/crates/move-vm-integration-tests/src/tests/binary_format_version.rs +++ b/external-crates/move/crates/move-vm-integration-tests/src/tests/binary_format_version.rs @@ -23,6 +23,7 @@ fn test_publish_module_with_custom_max_binary_format_version() { let vm = MoveVM::new(move_stdlib_natives::all_natives( AccountAddress::from_hex_literal("0x1").unwrap(), move_stdlib_natives::GasParameters::zeros(), + /* silent debug */ true, )) .unwrap(); let mut sess = vm.new_session(&storage); @@ -55,6 +56,7 @@ fn test_publish_module_with_custom_max_binary_format_version() { move_stdlib_natives::all_natives( AccountAddress::from_hex_literal("0x1").unwrap(), move_stdlib_natives::GasParameters::zeros(), + /* silent debug */ true, ), vm_config, ) diff --git a/external-crates/move/crates/move-vm-integration-tests/src/tests/depth_tests_modules.move b/external-crates/move/crates/move-vm-integration-tests/src/tests/depth_tests_modules.move index 22f0c4c9fc5b3..639d337290280 100644 --- a/external-crates/move/crates/move-vm-integration-tests/src/tests/depth_tests_modules.move +++ b/external-crates/move/crates/move-vm-integration-tests/src/tests/depth_tests_modules.move @@ -1,61 +1,60 @@ -address 0x2 { -module A { - struct S has copy, drop { +module 0x2::A { + public struct S has copy, drop { f1: 0x2::B::S, f2: 0x2::C::S, } - struct Box has copy, drop, store { x: T } - struct Box3 has copy, drop, store { x: Box> } - struct Box7 has copy, drop, store { x: Box3> } - struct Box15 has copy, drop, store { x: Box7> } - struct Box31 has copy, drop, store { x: Box15> } - struct Box63 has copy, drop, store { x: Box31> } - struct Box127 has copy, drop, store { x: Box63> } + public struct Box has copy, drop, store { x: T } + public struct Box3 has copy, drop, store { x: Box> } + public struct Box7 has copy, drop, store { x: Box3> } + public struct Box15 has copy, drop, store { x: Box7> } + public struct Box31 has copy, drop, store { x: Box15> } + public struct Box63 has copy, drop, store { x: Box31> } + public struct Box127 has copy, drop, store { x: Box63> } } -module B { - struct S has copy, drop { +module 0x2::B { + public struct S has copy, drop { f1: u64, f2: u128, } } -module C { - struct S has copy, drop { +module 0x2::C { + public struct S has copy, drop { f1: address, f2: bool, } } -module D { - struct S has copy, drop { +module 0x2::D { + public struct S has copy, drop { f1: 0x2::B::S, } } -module E { - struct S has copy, drop { +module 0x2::E { + public struct S has copy, drop { f1: 0x2::F::S, f2: u64, } } -module F { - struct S has copy, drop { +module 0x2::F { + public struct S has copy, drop { f1: T, f2: u64, } } -module G { - struct S has copy, drop { +module 0x2::G { + public struct S has copy, drop { f1: 0x2::H::S, f2: u64, } } -module H { - struct S has copy, drop { +module 0x2::H { + public struct S has copy, drop { f1: 0x2::F::S, f2: 0x2::E::S, f3: 0x2::E::S<0x2::F::S>, @@ -65,8 +64,8 @@ module H { } } -module I { - struct S { +module 0x2::I { + public struct S { f1: F, f2: E, f3: E>, @@ -77,37 +76,36 @@ module I { f8: u64, } - struct E { + public struct E { f1: F, f2: u64, } - struct F { + public struct F { f1: T, f2: u64, } - struct H { + public struct H { f1: T, f2: u64, } - struct G { + public struct G { f: H, } - struct L { + public struct L { g1: G, g2: H, } - struct LL { + public struct LL { g1: G, g2: H, } - struct N { + public struct N { f: u64 } } -} diff --git a/external-crates/move/crates/move-vm-integration-tests/src/tests/function_arg_tests.rs b/external-crates/move/crates/move-vm-integration-tests/src/tests/function_arg_tests.rs index 1f11fa7376aef..1953109d2c136 100644 --- a/external-crates/move/crates/move-vm-integration-tests/src/tests/function_arg_tests.rs +++ b/external-crates/move/crates/move-vm-integration-tests/src/tests/function_arg_tests.rs @@ -39,8 +39,8 @@ fn run( let code = format!( r#" module 0x{}::M {{ - struct Foo has copy, drop {{ x: u64 }} - struct Bar has copy, drop {{ x: T }} + public struct Foo has copy, drop {{ x: u64 }} + public struct Bar has copy, drop {{ x: T }} fun foo<{}>({}) {{ }} }} diff --git a/external-crates/move/crates/move-vm-integration-tests/src/tests/loader_tests_modules.move b/external-crates/move/crates/move-vm-integration-tests/src/tests/loader_tests_modules.move index dcdc5a4764034..74d980a6c0e28 100644 --- a/external-crates/move/crates/move-vm-integration-tests/src/tests/loader_tests_modules.move +++ b/external-crates/move/crates/move-vm-integration-tests/src/tests/loader_tests_modules.move @@ -1,173 +1,171 @@ -address 0x2 { - module A { - struct S has copy, drop { - f1: 0x2::B::S, - f2: 0x2::C::S, - } +module 0x2::A { + public struct S has copy, drop { + f1: 0x2::B::S, + f2: 0x2::C::S, + } - public fun new(f1: 0x2::B::S, f2: 0x2::C::S): S { - Self::S { f1, f2 } - } + public fun new(f1: 0x2::B::S, f2: 0x2::C::S): S { + Self::S { f1, f2 } + } - public fun destroy(v: S): (0x2::B::S, 0x2::C::S) { - let S { f1: v1, f2: v2 } = v; - (v1, v2) - } + public fun destroy(v: S): (0x2::B::S, 0x2::C::S) { + let S { f1: v1, f2: v2 } = v; + (v1, v2) + } - public fun entry_a() { - let i = 0; - while (i < 10) { - let b = 0x2::B::new(20, 100); - let c = 0x2::C::new(@0x42, true); - let another_b = 0x2::B::b_and_c(&b, c); - let (_, _) = 0x2::B::destroy(another_b); - let another_c = 0x2::C::new(@0x42, false); - 0x2::C::destroy(another_c); - i = i + 1; - } + public fun entry_a() { + let mut i = 0; + while (i < 10) { + let b = 0x2::B::new(20, 100); + let c = 0x2::C::new(@0x42, true); + let another_b = 0x2::B::b_and_c(&b, c); + let (_, _) = 0x2::B::destroy(another_b); + let another_c = 0x2::C::new(@0x42, false); + 0x2::C::destroy(another_c); + i = i + 1; } + } - public fun get_field_1(s: &S): 0x2::B::S { - *&s.f1 - } + public fun get_field_1(s: &S): 0x2::B::S { + *&s.f1 } +} - module B { - struct S has copy, drop { - f1: u64, - f2: u128, - } +module 0x2::B { + public struct S has copy, drop { + f1: u64, + f2: u128, + } - public fun new(v1: u64, v2: u128): S { S { f1: v1, f2: v2 } } + public fun new(v1: u64, v2: u128): S { S { f1: v1, f2: v2 } } - public fun destroy(v: S): (u64, u128) { - let S { f1: val1, f2: val2 } = v; - (val1, val2) - } + public fun destroy(v: S): (u64, u128) { + let S { f1: val1, f2: val2 } = v; + (val1, val2) + } - public fun b_and_c(b: &S, c: 0x2::C::S): S { - let _ = 0x2::C::destroy(c); - let another_b = S { - f1: 0, - f2: b.f2, - }; - another_b - } + public fun b_and_c(b: &S, c: 0x2::C::S): S { + let _ = 0x2::C::destroy(c); + let another_b = S { + f1: 0, + f2: b.f2, + }; + another_b } +} - module C { - struct S has copy, drop { - f1: address, - f2: bool, - } +module 0x2::C { + public struct S has copy, drop { + f1: address, + f2: bool, + } - public fun new(v1: address, v2: bool): S { - Self::S { - f1: v1, - f2: v2, - } + public fun new(v1: address, v2: bool): S { + Self::S { + f1: v1, + f2: v2, } + } - public fun destroy(v: S): address { - let S { f1: v1, f2: _ } = v; - v1 - } + public fun destroy(v: S): address { + let S { f1: v1, f2: _ } = v; + v1 + } - public fun just_c() { - let i = 0; - while (i < 10) { - let c = new(@0x0, false); - let S { f1: _, f2: _ } = c; - i = i + 1; - } + public fun just_c() { + let mut i = 0; + while (i < 10) { + let c = new(@0x0, false); + let S { f1: _, f2: _ } = c; + i = i + 1; } } +} - module D { - struct S has copy, drop { - f1: 0x2::B::S, - } +module 0x2::D { + public struct S has copy, drop { + f1: 0x2::B::S, + } - public fun new(): 0x2::D::S { - Self::S { - f1: 0x2::B::new(20, 100), - } + public fun new(): 0x2::D::S { + Self::S { + f1: 0x2::B::new(20, 100), } + } - public fun entry_d() { - let i = 0; - while (i < 10) { - let b = 0x2::B::new(20, 100); - let c = 0x2::C::new(@0x45, false); - let another_b = 0x2::B::b_and_c(&b, c); - let (_, _) = 0x2::B::destroy(another_b); - let another_c = 0x2::C::new(@0x46, true); - 0x2::C::destroy(another_c); - i = i + 1; - } + public fun entry_d() { + let mut i = 0; + while (i < 10) { + let b = 0x2::B::new(20, 100); + let c = 0x2::C::new(@0x45, false); + let another_b = 0x2::B::b_and_c(&b, c); + let (_, _) = 0x2::B::destroy(another_b); + let another_c = 0x2::C::new(@0x46, true); + 0x2::C::destroy(another_c); + i = i + 1; } } +} - module E { - struct S { - f1: u64, - } +module 0x2::E { + public struct S { + f1: u64, + } - public fun new(): 0x2::E::S { Self::S { f1: 20 } } - - public fun entry_e() { - let i = 0; - while (i < 10) { - let b = 0x2::B::new(20, 100); - let c = 0x2::C::new(@0x100, false); - let another_b = 0x2::B::b_and_c(&b, c); - let (_, _) = 0x2::B::destroy(another_b); - let another_c = 0x2::C::new(@0x101, true); - 0x2::C::destroy(another_c); - i = i + 1; - }; - } + public fun new(): 0x2::E::S { Self::S { f1: 20 } } + + public fun entry_e() { + let mut i = 0; + while (i < 10) { + let b = 0x2::B::new(20, 100); + let c = 0x2::C::new(@0x100, false); + let another_b = 0x2::B::b_and_c(&b, c); + let (_, _) = 0x2::B::destroy(another_b); + let another_c = 0x2::C::new(@0x101, true); + 0x2::C::destroy(another_c); + i = i + 1; + }; } +} - module F { - struct S { - f1: u64, - } +module 0x2::F { + public struct S { + f1: u64, + } - public fun new(): 0x2::F::S { Self::S { f1: 20 } } + public fun new(): 0x2::F::S { Self::S { f1: 20 } } - public fun entry_f() { - 0x2::A::entry_a(); - } + public fun entry_f() { + 0x2::A::entry_a(); } +} - module G { - struct S has copy, drop { - f1: u64, - f2: u128, - f3: u16, - f4: u32, - f5: u256 - } +module 0x2::G { + public struct S has copy, drop { + f1: u64, + f2: u128, + f3: u16, + f4: u32, + f5: u256 + } - public fun new(v1: u64, v2: u128, v3: u16, v4: u32, v5: u256): S { S { f1: v1, f2: v2, f3: v3, f4: v4, f5: v5 } } + public fun new(v1: u64, v2: u128, v3: u16, v4: u32, v5: u256): S { S { f1: v1, f2: v2, f3: v3, f4: v4, f5: v5 } } - public fun destroy(v: S): (u64, u128, u16, u32, u256) { - let S { f1: val1, f2: val2, f3: val3, f4: val4, f5: val5 } = v; - (val1, val2, val3, val4, val5) - } + public fun destroy(v: S): (u64, u128, u16, u32, u256) { + let S { f1: val1, f2: val2, f3: val3, f4: val4, f5: val5 } = v; + (val1, val2, val3, val4, val5) + } - public fun b_and_c(b: &S, c: 0x2::C::S): S { - let _ = 0x2::C::destroy(c); - let another_b = S { - f1: 0, - f2: b.f2, - f3: b.f3 + (b.f2 as u16), - f4: (b.f1 as u32), - f5: b.f5 - }; - another_b - } + public fun b_and_c(b: &S, c: 0x2::C::S): S { + let _ = 0x2::C::destroy(c); + let another_b = S { + f1: 0, + f2: b.f2, + f3: b.f3 + (b.f2 as u16), + f4: (b.f1 as u32), + f5: b.f5 + }; + another_b } } diff --git a/external-crates/move/crates/move-vm-integration-tests/src/tests/nested_loop_tests.rs b/external-crates/move/crates/move-vm-integration-tests/src/tests/nested_loop_tests.rs index 633f0d4f15db6..05b5110ad5bad 100644 --- a/external-crates/move/crates/move-vm-integration-tests/src/tests/nested_loop_tests.rs +++ b/external-crates/move/crates/move-vm-integration-tests/src/tests/nested_loop_tests.rs @@ -17,9 +17,9 @@ fn test_publish_module_with_nested_loops() { let code = r#" module {{ADDR}}::M { fun foo() { - let i = 0; + let mut i = 0; while (i < 10) { - let j = 0; + let mut j = 0; while (j < 10) { j = j + 1; }; @@ -42,6 +42,7 @@ fn test_publish_module_with_nested_loops() { move_stdlib_natives::all_natives( AccountAddress::from_hex_literal("0x1").unwrap(), move_stdlib_natives::GasParameters::zeros(), + /* silent debug */ true, ), VMConfig { verifier: VerifierConfig { @@ -65,6 +66,7 @@ fn test_publish_module_with_nested_loops() { move_stdlib_natives::all_natives( AccountAddress::from_hex_literal("0x1").unwrap(), move_stdlib_natives::GasParameters::zeros(), + /* silent debug */ true, ), VMConfig { verifier: VerifierConfig { diff --git a/external-crates/move/crates/move-vm-integration-tests/src/tests/relinking_tests_b_v1.move b/external-crates/move/crates/move-vm-integration-tests/src/tests/relinking_tests_b_v1.move index 2adcb5bdb6a41..bf93efa2b63d3 100644 --- a/external-crates/move/crates/move-vm-integration-tests/src/tests/relinking_tests_b_v1.move +++ b/external-crates/move/crates/move-vm-integration-tests/src/tests/relinking_tests_b_v1.move @@ -1,6 +1,6 @@ /// Dependencies: [C v0+] module 0x2::b { - struct S { x: u64 } + public struct S { x: u64 } public fun b(): u64 { 0x2::c::c() * 0x2::c::d() diff --git a/external-crates/move/crates/move-vm-integration-tests/src/tests/relinking_tests_c_v0.move b/external-crates/move/crates/move-vm-integration-tests/src/tests/relinking_tests_c_v0.move index f099f126c4b47..6efe3748c3795 100644 --- a/external-crates/move/crates/move-vm-integration-tests/src/tests/relinking_tests_c_v0.move +++ b/external-crates/move/crates/move-vm-integration-tests/src/tests/relinking_tests_c_v0.move @@ -1,6 +1,6 @@ /// Dependencies: [] module 0x2::c { - struct S { x: u64 } + public struct S { x: u64 } public fun c(): u64 { 42 diff --git a/external-crates/move/crates/move-vm-integration-tests/src/tests/relinking_tests_c_v1.move b/external-crates/move/crates/move-vm-integration-tests/src/tests/relinking_tests_c_v1.move index fecfe86940a76..b36c4e9549b78 100644 --- a/external-crates/move/crates/move-vm-integration-tests/src/tests/relinking_tests_c_v1.move +++ b/external-crates/move/crates/move-vm-integration-tests/src/tests/relinking_tests_c_v1.move @@ -1,7 +1,7 @@ /// Dependencies: [] module 0x2::c { - struct S { x: u64 } - struct R { x: u64, y: u64 } + public struct S { x: u64 } + public struct R { x: u64, y: u64 } public fun c(): u64 { 43 diff --git a/external-crates/move/crates/move-vm-integration-tests/src/tests/relinking_tests_c_v2.move b/external-crates/move/crates/move-vm-integration-tests/src/tests/relinking_tests_c_v2.move index 2660fbbe6346e..ba0318bef1cfe 100644 --- a/external-crates/move/crates/move-vm-integration-tests/src/tests/relinking_tests_c_v2.move +++ b/external-crates/move/crates/move-vm-integration-tests/src/tests/relinking_tests_c_v2.move @@ -1,8 +1,8 @@ /// Dependencies: [] module 0x2::c { - struct S { x: u64 } - struct R { x: u64, y: u64 } - struct Q { x: u64, y: u64, z: u64 } + public struct S { x: u64 } + public struct R { x: u64, y: u64 } + public struct Q { x: u64, y: u64, z: u64 } public fun c(): u64 { 45 diff --git a/external-crates/move/crates/move-vm-transactional-tests/tests/builtins/get_txn_sender.exp b/external-crates/move/crates/move-vm-transactional-tests/tests/builtins/get_txn_sender.exp index 6cd67db3f6472..c23aa2b760be4 100644 --- a/external-crates/move/crates/move-vm-transactional-tests/tests/builtins/get_txn_sender.exp +++ b/external-crates/move/crates/move-vm-transactional-tests/tests/builtins/get_txn_sender.exp @@ -1 +1,5 @@ processed 1 task + +task 0, lines 1-8: +//# run --signers 0x1 +return values: { 0000000000000000000000000000000000000000000000000000000000000001 } diff --git a/external-crates/move/crates/move-vm-transactional-tests/tests/builtins/get_txn_sender.mvir b/external-crates/move/crates/move-vm-transactional-tests/tests/builtins/get_txn_sender.mvir index 3bc4def5fbcc9..d50e8c3015153 100644 --- a/external-crates/move/crates/move-vm-transactional-tests/tests/builtins/get_txn_sender.mvir +++ b/external-crates/move/crates/move-vm-transactional-tests/tests/builtins/get_txn_sender.mvir @@ -1,14 +1,8 @@ //# run --signers 0x1 module 0x42.m { -import 0x1.signer; -entry foo(account: signer) { - let sender: address; - let addr: address; +entry foo(account: signer): signer { label b0: - sender = signer.address_of(&account); - addr = 0x1; - assert(copy(sender) == copy(addr), 42); - return; + return move(account); } } diff --git a/external-crates/move/crates/move-vm-transactional-tests/tests/entry_points/call_native.exp b/external-crates/move/crates/move-vm-transactional-tests/tests/entry_points/call_native.exp index 7dc107f50aff1..f2d3d9e9a0fdc 100644 --- a/external-crates/move/crates/move-vm-transactional-tests/tests/entry_points/call_native.exp +++ b/external-crates/move/crates/move-vm-transactional-tests/tests/entry_points/call_native.exp @@ -5,8 +5,8 @@ task 0, line 2: return values: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] task 1, line 4: -//# run std::signer::borrow_address --args @3735928559 -return values: 00000000000000000000000000000000000000000000000000000000deadbeef +//# run std::vector::borrow --type-args u64 --args vector[42] 0 +return values: 42 task 2, line 6: //# run std::type_name::get --type-args vector diff --git a/external-crates/move/crates/move-vm-transactional-tests/tests/entry_points/call_native.move b/external-crates/move/crates/move-vm-transactional-tests/tests/entry_points/call_native.move index 11de68abe0ba4..d471f44d41748 100644 --- a/external-crates/move/crates/move-vm-transactional-tests/tests/entry_points/call_native.move +++ b/external-crates/move/crates/move-vm-transactional-tests/tests/entry_points/call_native.move @@ -1,6 +1,6 @@ //# run std::bcs::to_bytes --type-args u256 --args 0u256 -//# run std::signer::borrow_address --args @3735928559 +//# run std::vector::borrow --type-args u64 --args vector[42] 0 //# run std::type_name::get --type-args vector diff --git a/external-crates/move/crates/test-generation/src/lib.rs b/external-crates/move/crates/test-generation/src/lib.rs index 0a7ee75176b71..4a4ce8449659a 100644 --- a/external-crates/move/crates/test-generation/src/lib.rs +++ b/external-crates/move/crates/test-generation/src/lib.rs @@ -135,6 +135,7 @@ fn execute_function_in_module( let vm = MoveVM::new(move_stdlib_natives::all_natives( AccountAddress::from_hex_literal("0x1").unwrap(), move_stdlib_natives::GasParameters::zeros(), + /* silent debug */ true, )) .unwrap(); diff --git a/sui-execution/latest/sui-adapter/src/execution_engine.rs b/sui-execution/latest/sui-adapter/src/execution_engine.rs index 60035e6806f60..306e2a9d25126 100644 --- a/sui-execution/latest/sui-adapter/src/execution_engine.rs +++ b/sui-execution/latest/sui-adapter/src/execution_engine.rs @@ -917,7 +917,7 @@ mod checked { if protocol_config.fresh_vm_on_framework_upgrade() { let new_vm = new_move_vm( - all_natives(/* silent */ true), + all_natives(/* silent */ true, protocol_config), protocol_config, /* enable_profiler */ None, ) diff --git a/sui-execution/latest/sui-move-natives/src/lib.rs b/sui-execution/latest/sui-move-natives/src/lib.rs index c129bf4e26539..36fb6865d69eb 100644 --- a/sui-execution/latest/sui-move-natives/src/lib.rs +++ b/sui-execution/latest/sui-move-natives/src/lib.rs @@ -51,7 +51,7 @@ use move_core_types::{ runtime_value as R, vm_status::StatusCode, }; -use move_stdlib_natives::{GasParameters, NurseryGasParameters}; +use move_stdlib_natives::{self as MSN, GasParameters}; use move_vm_runtime::native_functions::{NativeContext, NativeFunction, NativeFunctionTable}; use move_vm_types::{ loaded_data::runtime_types::Type, @@ -640,7 +640,111 @@ impl NativesCostTable { } } -pub fn all_natives(silent: bool) -> NativeFunctionTable { +pub fn make_stdlib_gas_params_for_protocol_config( + protocol_config: &ProtocolConfig, +) -> GasParameters { + macro_rules! get_gas_cost_or_default { + ($name: ident) => {{ + debug_assert!( + protocol_config.version.as_u64() < 53 || protocol_config.$name().is_some() + ); + protocol_config.$name().map(Into::into).unwrap_or(0.into()) + }}; + } + GasParameters::new( + MSN::bcs::GasParameters { + to_bytes: MSN::bcs::ToBytesGasParameters { + per_byte_serialized: get_gas_cost_or_default!( + bcs_per_byte_serialized_cost_as_option + ), + legacy_min_output_size: get_gas_cost_or_default!( + bcs_legacy_min_output_size_cost_as_option + ), + failure: get_gas_cost_or_default!(bcs_failure_cost_as_option), + }, + }, + MSN::debug::GasParameters { + print: MSN::debug::PrintGasParameters { + base_cost: get_gas_cost_or_default!(debug_print_base_cost_as_option), + }, + print_stack_trace: MSN::debug::PrintStackTraceGasParameters { + base_cost: get_gas_cost_or_default!(debug_print_stack_trace_base_cost_as_option), + }, + }, + MSN::hash::GasParameters { + sha2_256: MSN::hash::Sha2_256GasParameters { + base: get_gas_cost_or_default!(hash_sha2_256_base_cost_as_option), + per_byte: get_gas_cost_or_default!(hash_sha2_256_per_byte_cost_as_option), + legacy_min_input_len: get_gas_cost_or_default!( + hash_sha2_256_legacy_min_input_len_cost_as_option + ), + }, + sha3_256: MSN::hash::Sha3_256GasParameters { + base: get_gas_cost_or_default!(hash_sha3_256_base_cost_as_option), + per_byte: get_gas_cost_or_default!(hash_sha3_256_per_byte_cost_as_option), + legacy_min_input_len: get_gas_cost_or_default!( + hash_sha3_256_legacy_min_input_len_cost_as_option + ), + }, + }, + MSN::string::GasParameters { + check_utf8: MSN::string::CheckUtf8GasParameters { + base: get_gas_cost_or_default!(string_check_utf8_base_cost_as_option), + per_byte: get_gas_cost_or_default!(string_check_utf8_per_byte_cost_as_option), + }, + is_char_boundary: MSN::string::IsCharBoundaryGasParameters { + base: get_gas_cost_or_default!(string_is_char_boundary_base_cost_as_option), + }, + sub_string: MSN::string::SubStringGasParameters { + base: get_gas_cost_or_default!(string_sub_string_base_cost_as_option), + per_byte: get_gas_cost_or_default!(string_sub_string_per_byte_cost_as_option), + }, + index_of: MSN::string::IndexOfGasParameters { + base: get_gas_cost_or_default!(string_index_of_base_cost_as_option), + per_byte_pattern: get_gas_cost_or_default!( + string_index_of_per_byte_pattern_cost_as_option + ), + per_byte_searched: get_gas_cost_or_default!( + string_index_of_per_byte_searched_cost_as_option + ), + }, + }, + MSN::type_name::GasParameters { + get: MSN::type_name::GetGasParameters { + base: get_gas_cost_or_default!(type_name_get_base_cost_as_option), + per_byte: get_gas_cost_or_default!(type_name_get_per_byte_cost_as_option), + }, + }, + MSN::vector::GasParameters { + empty: MSN::vector::EmptyGasParameters { + base: get_gas_cost_or_default!(vector_empty_base_cost_as_option), + }, + length: MSN::vector::LengthGasParameters { + base: get_gas_cost_or_default!(vector_length_base_cost_as_option), + }, + push_back: MSN::vector::PushBackGasParameters { + base: get_gas_cost_or_default!(vector_push_back_base_cost_as_option), + legacy_per_abstract_memory_unit: get_gas_cost_or_default!( + vector_push_back_legacy_per_abstract_memory_unit_cost_as_option + ), + }, + borrow: MSN::vector::BorrowGasParameters { + base: get_gas_cost_or_default!(vector_borrow_base_cost_as_option), + }, + pop_back: MSN::vector::PopBackGasParameters { + base: get_gas_cost_or_default!(vector_pop_back_base_cost_as_option), + }, + destroy_empty: MSN::vector::DestroyEmptyGasParameters { + base: get_gas_cost_or_default!(vector_destroy_empty_base_cost_as_option), + }, + swap: MSN::vector::SwapGasParameters { + base: get_gas_cost_or_default!(vector_swap_base_cost_as_option), + }, + }, + ) +} + +pub fn all_natives(silent: bool, protocol_config: &ProtocolConfig) -> NativeFunctionTable { let sui_framework_natives: &[(&str, &str, NativeFunction)] = &[ ("address", "from_bytes", make_native!(address::from_bytes)), ("address", "to_u256", make_native!(address::to_u256)), @@ -966,14 +1070,8 @@ pub fn all_natives(silent: bool) -> NativeFunctionTable { .chain(sui_framework_natives_iter) .chain(move_stdlib_natives::all_natives( MOVE_STDLIB_ADDRESS, - // TODO: tune gas params - GasParameters::zeros(), - )) - .chain(move_stdlib_natives::nursery_natives( + make_stdlib_gas_params_for_protocol_config(protocol_config), silent, - MOVE_STDLIB_ADDRESS, - // TODO: tune gas params - NurseryGasParameters::zeros(), )) .collect() } diff --git a/sui-execution/src/latest.rs b/sui-execution/src/latest.rs index a9f165624b928..4fb142dda49b9 100644 --- a/sui-execution/src/latest.rs +++ b/sui-execution/src/latest.rs @@ -50,7 +50,7 @@ impl Executor { enable_profiler: Option, ) -> Result { Ok(Executor(Arc::new(new_move_vm( - all_natives(silent), + all_natives(silent, protocol_config), protocol_config, enable_profiler, )?))) From 9edb72257d9a54cc84a6d3fd2b26cc28fb579338 Mon Sep 17 00:00:00 2001 From: Joy Wang <108701016+joyqvq@users.noreply.github.com> Date: Mon, 29 Jul 2024 12:37:28 -0400 Subject: [PATCH 019/232] fix: handle nondeterminism in test (#18841) ## Description my guess is the modified signature byte is already 0 and does not need modification. fix here is to deterministically flip the byte. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-e2e-tests/tests/passkey_e2e_tests.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/sui-e2e-tests/tests/passkey_e2e_tests.rs b/crates/sui-e2e-tests/tests/passkey_e2e_tests.rs index 699fd96afa842..505f240d82edc 100644 --- a/crates/sui-e2e-tests/tests/passkey_e2e_tests.rs +++ b/crates/sui-e2e-tests/tests/passkey_e2e_tests.rs @@ -303,7 +303,11 @@ async fn test_passkey_fails_to_verify_sig() { let test_cluster = TestClusterBuilder::new().build().await; let response = create_credential_and_sign_test_tx(&test_cluster, None, false, false).await; let mut modified_sig = response.user_sig_bytes.clone(); - modified_sig[1] = 0x00; + if modified_sig[1] == 0x00 { + modified_sig[1] = 0x01; + } else { + modified_sig[1] = 0x00; + } let sig = GenericSignature::PasskeyAuthenticator( PasskeyAuthenticator::new_for_testing( response.authenticator_data, From 0512a87dfac161044b2833878e2ad0a7a8103583 Mon Sep 17 00:00:00 2001 From: Todd Nowacki Date: Mon, 29 Jul 2024 11:09:05 -0700 Subject: [PATCH 020/232] [move] fixed find_index (#18842) ## Description - Fixed find_index not being by-ref ## Test plan - new tests that force no `copy` possible --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .../sui-framework/packages/move-stdlib/sources/vector.move | 2 +- .../packages/move-stdlib/tests/vector_tests.move | 5 +++++ .../move/crates/move-analyzer/tests/dot_completion.exp | 2 +- external-crates/move/crates/move-stdlib/sources/vector.move | 2 +- .../move/crates/move-stdlib/tests/vector_tests.move | 5 +++++ 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/crates/sui-framework/packages/move-stdlib/sources/vector.move b/crates/sui-framework/packages/move-stdlib/sources/vector.move index cd98ed0ae7955..55c1abac34b74 100644 --- a/crates/sui-framework/packages/move-stdlib/sources/vector.move +++ b/crates/sui-framework/packages/move-stdlib/sources/vector.move @@ -238,7 +238,7 @@ module std::vector { /// Finds the index of first element in the vector `v` that satisfies the predicate `f`. /// Returns `some(index)` if such an element is found, otherwise `none()`. - public macro fun find_index<$T>($v: vector<$T>, $f: |&$T| -> bool): Option { + public macro fun find_index<$T>($v: &vector<$T>, $f: |&$T| -> bool): Option { let v = $v; 'find_index: { v.length().do!(|i| if ($f(&v[i])) return 'find_index option::some(i)); diff --git a/crates/sui-framework/packages/move-stdlib/tests/vector_tests.move b/crates/sui-framework/packages/move-stdlib/tests/vector_tests.move index 2bb76d6310017..3aac6ef5b4e5f 100644 --- a/crates/sui-framework/packages/move-stdlib/tests/vector_tests.move +++ b/crates/sui-framework/packages/move-stdlib/tests/vector_tests.move @@ -677,6 +677,11 @@ module std::vector_tests { let r = vector[0, 10, 100, 1_000]; assert!(r.find_index!(|e| *e == 100).destroy_some() == 2); assert!(r.find_index!(|e| *e == 10_000).is_none()); + + let v = vector[Droppable{}, Droppable{}]; + let idx = v.find_index!(|e| e == Droppable{}); + assert!(idx.destroy_some() == 0); + assert!(&v[idx.destroy_some()] == Droppable{}); } #[test] diff --git a/external-crates/move/crates/move-analyzer/tests/dot_completion.exp b/external-crates/move/crates/move-analyzer/tests/dot_completion.exp index ad426b6f92568..547bc99ee900a 100644 --- a/external-crates/move/crates/move-analyzer/tests/dot_completion.exp +++ b/external-crates/move/crates/move-analyzer/tests/dot_completion.exp @@ -107,7 +107,7 @@ Method 'filter!()' Method 'find_index!()' INSERT TEXT: 'find_index!(|${1}| ${2})' TARGET : '(std::vector::find_index)' - TYPE : 'fun <$T>(vector<$T>, |&$T| -> bool): Option' + TYPE : 'fun <$T>(&vector<$T>, |&$T| -> bool): Option' Method 'fold!()' INSERT TEXT: 'fold!(${1:init}, |${2}, ${3}| ${4})' TARGET : '(std::vector::fold)' diff --git a/external-crates/move/crates/move-stdlib/sources/vector.move b/external-crates/move/crates/move-stdlib/sources/vector.move index cd98ed0ae7955..55c1abac34b74 100644 --- a/external-crates/move/crates/move-stdlib/sources/vector.move +++ b/external-crates/move/crates/move-stdlib/sources/vector.move @@ -238,7 +238,7 @@ module std::vector { /// Finds the index of first element in the vector `v` that satisfies the predicate `f`. /// Returns `some(index)` if such an element is found, otherwise `none()`. - public macro fun find_index<$T>($v: vector<$T>, $f: |&$T| -> bool): Option { + public macro fun find_index<$T>($v: &vector<$T>, $f: |&$T| -> bool): Option { let v = $v; 'find_index: { v.length().do!(|i| if ($f(&v[i])) return 'find_index option::some(i)); diff --git a/external-crates/move/crates/move-stdlib/tests/vector_tests.move b/external-crates/move/crates/move-stdlib/tests/vector_tests.move index 2bb76d6310017..3aac6ef5b4e5f 100644 --- a/external-crates/move/crates/move-stdlib/tests/vector_tests.move +++ b/external-crates/move/crates/move-stdlib/tests/vector_tests.move @@ -677,6 +677,11 @@ module std::vector_tests { let r = vector[0, 10, 100, 1_000]; assert!(r.find_index!(|e| *e == 100).destroy_some() == 2); assert!(r.find_index!(|e| *e == 10_000).is_none()); + + let v = vector[Droppable{}, Droppable{}]; + let idx = v.find_index!(|e| e == Droppable{}); + assert!(idx.destroy_some() == 0); + assert!(&v[idx.destroy_some()] == Droppable{}); } #[test] From 982024ab1793c98c87330b1dc50d737141494479 Mon Sep 17 00:00:00 2001 From: Mark Logan <103447440+mystenmark@users.noreply.github.com> Date: Mon, 29 Jul 2024 15:05:18 -0700 Subject: [PATCH 021/232] Fix edge-case in assert which can be triggered from jsonrpc (#18843) See comments in code for explanation. I haven't been able to repro this in a test, but I'm pretty confident about the source of the bug. --- .../sui-core/src/execution_cache/writeback_cache.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/crates/sui-core/src/execution_cache/writeback_cache.rs b/crates/sui-core/src/execution_cache/writeback_cache.rs index 845747eeca6ba..2b770f740d50a 100644 --- a/crates/sui-core/src/execution_cache/writeback_cache.rs +++ b/crates/sui-core/src/execution_cache/writeback_cache.rs @@ -73,6 +73,7 @@ use sui_types::object::Object; use sui_types::storage::{MarkerValue, ObjectKey, ObjectOrTombstone, ObjectStore, PackageObject}; use sui_types::sui_system_state::{get_sui_system_state, SuiSystemState}; use sui_types::transaction::{VerifiedSignedTransaction, VerifiedTransaction}; +use tap::TapOptional; use tracing::{debug, info, instrument, trace, warn}; use super::ExecutionCacheAPI; @@ -1453,7 +1454,10 @@ impl ObjectCacheRead for WritebackCache { // exists only to ensure correctness in all the edge cases. let latest: Option<(SequenceNumber, ObjectEntry)> = if let Some(dirty_set) = dirty_entry { - dirty_set.get_highest().cloned() + dirty_set + .get_highest() + .cloned() + .tap_none(|| panic!("dirty set cannot be empty")) } else { self.record_db_get("object_lt_or_eq_version_latest") .get_latest_object_or_tombstone(object_id)? @@ -1485,7 +1489,12 @@ impl ObjectCacheRead for WritebackCache { } } else { // no object found in dirty set or db, object does not exist - assert!(cached_entry.is_none()); + // When this is called from a read api (i.e. not the execution path) it is + // possible that the object has been deleted and pruned. In this case, + // there would be no entry at all on disk, but we may have a tombstone in the + // cache + let highest = cached_entry.and_then(|c| c.get_highest()); + assert!(highest.is_none() || highest.unwrap().1.is_tombstone()); self.cache_object_not_found(&object_id); Ok(None) } From 14f02fb4f322a0247f13922ffa00de1c932f49c1 Mon Sep 17 00:00:00 2001 From: Todd Nowacki Date: Mon, 29 Jul 2024 15:19:03 -0700 Subject: [PATCH 022/232] [move] Mark sui::math as deprecated (#18849) ## Description - Adds the `deprecated` annotation to `sui::math` ## Test plan - Ran tests --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [X] CLI: The Move module `sui::math` has been deprecated. The individual integer modules, e.g. `std::u64`, should be used instead. - [ ] Rust SDK: - [ ] REST API: --- crates/sui-framework/packages/sui-framework/sources/math.move | 1 + .../sui-framework/packages/sui-framework/tests/math_tests.move | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/sui-framework/packages/sui-framework/sources/math.move b/crates/sui-framework/packages/sui-framework/sources/math.move index 6f079e8ad56d8..f98a7d7c5eb72 100644 --- a/crates/sui-framework/packages/sui-framework/sources/math.move +++ b/crates/sui-framework/packages/sui-framework/sources/math.move @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 /// DEPRECATED, use the each integer type's individual module instead, e.g. `std::u64` +#[deprecated(note = b"Use the each integer type's individual module instead, e.g. `std::u64`")] module sui::math { /// DEPRECATED, use `std::u64::max` instead diff --git a/crates/sui-framework/packages/sui-framework/tests/math_tests.move b/crates/sui-framework/packages/sui-framework/tests/math_tests.move index 7ab3117eae52b..e55bb0bad3006 100644 --- a/crates/sui-framework/packages/sui-framework/tests/math_tests.move +++ b/crates/sui-framework/packages/sui-framework/tests/math_tests.move @@ -1,7 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -#[test_only] +#[test_only, allow(deprecated_usage)] module sui::math_tests { use sui::math; From 90809ef227f9848df8597292f761d08e89a29c39 Mon Sep 17 00:00:00 2001 From: plam-ml <127577476+plam-ml@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:03:21 -0700 Subject: [PATCH 023/232] update wallet connect logo (#18821) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description https://linear.app/mysten-labs/issue/APPS-65/update-wallet-logo ![Screenshot 2024-07-26 at 1 09 36 PM](https://github.com/user-attachments/assets/47fc5d1d-1b20-4be4-8406-2c875fff70b3) ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- apps/wallet/src/dapp-interface/WalletStandardInterface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wallet/src/dapp-interface/WalletStandardInterface.ts b/apps/wallet/src/dapp-interface/WalletStandardInterface.ts index 3c74b905dd490..739507c8b94c7 100644 --- a/apps/wallet/src/dapp-interface/WalletStandardInterface.ts +++ b/apps/wallet/src/dapp-interface/WalletStandardInterface.ts @@ -109,7 +109,7 @@ export class SuiWallet implements Wallet { } get icon() { - return 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzIiIGhlaWdodD0iNzIiIHZpZXdCb3g9IjAgMCA3MiA3MiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjcyIiBoZWlnaHQ9IjcyIiByeD0iMTYiIGZpbGw9IiM2RkJDRjAiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yMC40MjEzIDUyLjc4MzhDMjMuNjQ5NiA1OC4zNzYgMjkuNDMyMSA2MS43MTQyIDM1Ljg4ODggNjEuNzE0MkM0Mi4zNDU1IDYxLjcxNDIgNDguMTI3IDU4LjM3NiA1MS4zNTY0IDUyLjc4MzhDNTQuNTg0OCA0Ny4xOTI2IDU0LjU4NDggNDAuNTE2MyA1MS4zNTY0IDM0LjkyNEwzNy43NTI0IDExLjM2MTVDMzYuOTI0MSA5LjkyNzAxIDM0Ljg1MzUgOS45MjcwMSAzNC4wMjUzIDExLjM2MTVMMjAuNDIxMyAzNC45MjRDMTcuMTkyOSA0MC41MTUyIDE3LjE5MjkgNDcuMTkxNSAyMC40MjEzIDUyLjc4MzhaTTMyLjA1NjYgMjIuNTcxM0wzNC45NTcxIDE3LjU0NzRDMzUuMzcxMiAxNi44MzAxIDM2LjQwNjUgMTYuODMwMSAzNi44MjA2IDE3LjU0NzRMNDcuOTc5MSAzNi44NzQ4QzUwLjAyOTEgNDAuNDI1NCA1MC40MTM5IDQ0LjUzNSA0OS4xMzM1IDQ4LjI5NTRDNDkuMDAwMiA0Ny42ODE5IDQ4LjgxMzggNDcuMDU0MiA0OC41NjI2IDQ2LjQyMDFDNDcuMDIxMyA0Mi41MzA0IDQzLjUzNjMgMzkuNTI4OSAzOC4yMDIzIDM3LjQ5ODJDMzQuNTM1MSAzNi4xMDcxIDMyLjE5NDMgMzQuMDYxMyAzMS4yNDMxIDMxLjQxNzFDMzAuMDE4IDI4LjAwODkgMzEuMjk3NiAyNC4yOTI0IDMyLjA1NjYgMjIuNTcxM1pNMjcuMTEwNyAzMS4xMzc5TDIzLjc5ODYgMzYuODc0OEMyMS4yNzQ4IDQxLjI0NTkgMjEuMjc0OCA0Ni40NjQxIDIzLjc5ODYgNTAuODM1M0MyNi4zMjIzIDU1LjIwNjQgMzAuODQxMyA1Ny44MTUgMzUuODg4OCA1Ny44MTVDMzkuMjQxMyA1Ny44MTUgNDIuMzYxNSA1Ni42NjMzIDQ0LjgxODQgNTQuNjA4OEM0NS4xMzg4IDUzLjgwMjEgNDYuMTMxIDUwLjg0OTIgNDQuOTA1MiA0Ny44MDU4QzQzLjc3MyA0NC45OTU0IDQxLjA0ODIgNDIuNzUxOSAzNi44MDYxIDQxLjEzNkMzMi4wMTEgMzkuMzE3MSAyOC44OTU4IDM2LjQ3NzQgMjcuNTQ4NiAzMi42OTg0QzI3LjM2MzEgMzIuMTc4MSAyNy4yMTg5IDMxLjY1NjggMjcuMTEwNyAzMS4xMzc5WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+' as const; + return 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjgiIGhlaWdodD0iMjgiIHZpZXdCb3g9IjAgMCAyOCAyOCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxyZWN0IHdpZHRoPSIyOCIgaGVpZ2h0PSIyOCIgZmlsbD0iIzRDQTNGRiIvPgogICAgPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xOC44MzI3IDEyLjM0MTNWMTIuMzQyMkMxOS42NDgyIDEzLjM2NTMgMjAuMTM2IDE0LjY2MTMgMjAuMTM2IDE2LjA3MDVDMjAuMTM2IDE3LjQ3OTggMTkuNjMzNyAxOC44MTQzIDE4Ljc5NTcgMTkuODQ0M0wxOC43MjM1IDE5LjkzM0wxOC43MDQ1IDE5LjgyMDNDMTguNjg4MiAxOS43MjQ1IDE4LjY2OSAxOS42Mjc1IDE4LjY0NyAxOS41M0MxOC4yMjc3IDE3LjY4NzUgMTYuODYxMiAxNi4xMDc1IDE0LjYxMjUgMTQuODI4MkMxMy4wOTQgMTMuOTY2OCAxMi4yMjQ3IDEyLjkyOTIgMTEuOTk2NSAxMS43NTA4QzExLjg0OSAxMC45ODg1IDExLjk1ODcgMTAuMjIzIDEyLjE3MDUgOS41NjcyNUMxMi4zODIyIDguOTExNzUgMTIuNjk3MiA4LjM2MjUgMTIuOTY0NyA4LjAzMkwxMy44Mzk1IDYuOTYyMjVDMTMuOTkzIDYuNzc0NzUgMTQuMjggNi43NzQ3NSAxNC40MzM1IDYuOTYyMjVMMTguODMzIDEyLjM0MTVMMTguODMyNyAxMi4zNDEzWk0yMC4yMTY1IDExLjI3MjVWMTEuMjcyTDE0LjM1MyA0LjEwMjc1QzE0LjI0MSAzLjk2NTc1IDE0LjAzMTUgMy45NjU3NSAxMy45MTk1IDQuMTAyNzVMOC4wNTYgMTEuMjcyM1YxMS4yNzI4TDguMDM3IDExLjI5NjVDNi45NTgyNSAxMi42MzUzIDYuMzEyNSAxNC4zMzY4IDYuMzEyNSAxNi4xODlDNi4zMTI1IDIwLjUwMjggOS44MTUyNSAyNCAxNC4xMzYzIDI0QzE4LjQ1NzIgMjQgMjEuOTYgMjAuNTAyOCAyMS45NiAxNi4xODlDMjEuOTYgMTQuMzM2OCAyMS4zMTQyIDEyLjYzNTMgMjAuMjM1MiAxMS4yOTYzTDIwLjIxNiAxMS4yNzI1SDIwLjIxNjVaTTkuNDU5MjUgMTIuMzE4TDkuOTgzNzUgMTEuNjc2NUw5Ljk5OTUgMTEuNzk1QzEwLjAxMiAxMS44ODg3IDEwLjAyNzIgMTEuOTgzIDEwLjA0NTIgMTIuMDc3OEMxMC4zODQ1IDEzLjg1ODIgMTEuNTk2NyAxNS4zNDI4IDEzLjYyMzUgMTYuNDkyNUMxNS4zODUyIDE3LjQ5NSAxNi40MTEgMTguNjQ4IDE2LjcwNjUgMTkuOTEyNUMxNi44Mjk4IDIwLjQ0MDMgMTYuODUxNyAyMC45NTk1IDE2Ljc5ODUgMjEuNDEzNUwxNi43OTUyIDIxLjQ0MTVMMTYuNzY5NyAyMS40NTRDMTUuOTc0NyAyMS44NDI1IDE1LjA4MDcgMjIuMDYwNSAxNC4xMzYgMjIuMDYwNUMxMC44MjI1IDIyLjA2MDUgOC4xMzYyNSAxOS4zNzg4IDguMTM2MjUgMTYuMDcwNUM4LjEzNjI1IDE0LjY1MDMgOC42MzE1IDEzLjM0NSA5LjQ1OSAxMi4zMTgzTDkuNDU5MjUgMTIuMzE4WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+Cg==' as const; } get chains() { From 17454e6449157be9abb208e35e42403b04831bed Mon Sep 17 00:00:00 2001 From: veth <162897869+VitalikButerinEth@users.noreply.github.com> Date: Tue, 30 Jul 2024 12:12:45 +0800 Subject: [PATCH 024/232] chore: fix some comments (#17992) ## Description fix some comments ## Test plan No need. --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: Signed-off-by: VitalikButerinEth --- apps/wallet/src/ui/app/pages/accounts/manage/AccountGroup.tsx | 2 +- sui-execution/latest/sui-move-natives/src/lib.rs | 2 +- sui-execution/v0/sui-move-natives/src/lib.rs | 2 +- sui-execution/v1/sui-move-natives/src/lib.rs | 2 +- sui-execution/v2/sui-move-natives/src/lib.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/wallet/src/ui/app/pages/accounts/manage/AccountGroup.tsx b/apps/wallet/src/ui/app/pages/accounts/manage/AccountGroup.tsx index f6f251f2b1fd6..c788e69745594 100644 --- a/apps/wallet/src/ui/app/pages/accounts/manage/AccountGroup.tsx +++ b/apps/wallet/src/ui/app/pages/accounts/manage/AccountGroup.tsx @@ -52,7 +52,7 @@ export function getGroupTitle(aGroupAccount: SerializedUIAccount) { : accountTypeToLabel[aGroupAccount?.type] || ''; } -// todo: we probbaly have some duplication here with the various FooterLink / ButtonOrLink +// todo: we probably have some duplication here with the various FooterLink / ButtonOrLink // components - we should look to add these to base components somewhere const FooterLink = forwardRef( ({ children, to, ...props }, ref) => { diff --git a/sui-execution/latest/sui-move-natives/src/lib.rs b/sui-execution/latest/sui-move-natives/src/lib.rs index 36fb6865d69eb..e2f50dfe1b86a 100644 --- a/sui-execution/latest/sui-move-natives/src/lib.rs +++ b/sui-execution/latest/sui-move-natives/src/lib.rs @@ -1088,7 +1088,7 @@ pub fn get_object_id(object: Value) -> Result { get_nested_struct_field(object, &[0, 0, 0]) } -// Extract a field valye that's nested inside value `v`. The offset of each nesting +// Extract a field value that's nested inside value `v`. The offset of each nesting // is determined by `offsets`. pub fn get_nested_struct_field(mut v: Value, offsets: &[usize]) -> Result { for offset in offsets { diff --git a/sui-execution/v0/sui-move-natives/src/lib.rs b/sui-execution/v0/sui-move-natives/src/lib.rs index 15d6fa6c0445d..e524becdbfe30 100644 --- a/sui-execution/v0/sui-move-natives/src/lib.rs +++ b/sui-execution/v0/sui-move-natives/src/lib.rs @@ -707,7 +707,7 @@ pub fn get_object_id(object: Value) -> Result { get_nested_struct_field(object, &[0, 0, 0]) } -// Extract a field valye that's nested inside value `v`. The offset of each nesting +// Extract a field value that's nested inside value `v`. The offset of each nesting // is determined by `offsets`. pub fn get_nested_struct_field(mut v: Value, offsets: &[usize]) -> Result { for offset in offsets { diff --git a/sui-execution/v1/sui-move-natives/src/lib.rs b/sui-execution/v1/sui-move-natives/src/lib.rs index b49ce1a218c6c..8a3621a1df8ea 100644 --- a/sui-execution/v1/sui-move-natives/src/lib.rs +++ b/sui-execution/v1/sui-move-natives/src/lib.rs @@ -762,7 +762,7 @@ pub fn get_object_id(object: Value) -> Result { get_nested_struct_field(object, &[0, 0, 0]) } -// Extract a field valye that's nested inside value `v`. The offset of each nesting +// Extract a field value that's nested inside value `v`. The offset of each nesting // is determined by `offsets`. pub fn get_nested_struct_field(mut v: Value, offsets: &[usize]) -> Result { for offset in offsets { diff --git a/sui-execution/v2/sui-move-natives/src/lib.rs b/sui-execution/v2/sui-move-natives/src/lib.rs index 34c693ecbc3e3..d4147e52d11d2 100644 --- a/sui-execution/v2/sui-move-natives/src/lib.rs +++ b/sui-execution/v2/sui-move-natives/src/lib.rs @@ -917,7 +917,7 @@ pub fn get_object_id(object: Value) -> Result { get_nested_struct_field(object, &[0, 0, 0]) } -// Extract a field valye that's nested inside value `v`. The offset of each nesting +// Extract a field value that's nested inside value `v`. The offset of each nesting // is determined by `offsets`. pub fn get_nested_struct_field(mut v: Value, offsets: &[usize]) -> Result { for offset in offsets { From 5056e4f192f6b57f2ed507a6a292a0d85c66a47b Mon Sep 17 00:00:00 2001 From: Rijnard van Tonder Date: Tue, 30 Jul 2024 09:09:47 +0200 Subject: [PATCH 025/232] docs: update outdated mention on Move.toml (#18837) ## Description Outdated doc mention as of address management: > The publish process replaces the `0x0` address with an actual on-chain address. ## Test plan N/A --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- docs/content/guides/developer/first-app/write-package.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/guides/developer/first-app/write-package.mdx b/docs/content/guides/developer/first-app/write-package.mdx index fc494923c92a2..c3754cd1fbac5 100644 --- a/docs/content/guides/developer/first-app/write-package.mdx +++ b/docs/content/guides/developer/first-app/write-package.mdx @@ -17,7 +17,7 @@ The manifest file contents include available sections of the manifest and commen - **[package]:** Contains metadata for the package. By default, the `sui move new` command populates only the `name` value of the metadata. In this case, the example passes `my_first_package` to the command, which becomes the name of the package. You can delete the first `#` of subsequent lines of the `[package]` section to provide values for the other available metadata fields. - **[dependencies]:** Lists the other packages that your package depends on to run. By default, the `sui move new` command lists the `Sui` package on GitHub (Testnet version) as the lone dependency. -- **[addresses]:** Declares named addresses that your package uses. By default, the section includes the package you create with the `sui move new` command and an address of `0x0`. The publish process replaces the `0x0` address with an actual on-chain address. +- **[addresses]:** Declares named addresses that your package uses. By default, the section includes the package you create with the `sui move new` command and an address of `0x0`. This value can be left as-is and indicates that [package addresses are automatically managed](../../../../concepts/sui-move-concepts/packages/automated-address-management) when published and upgraded. - **[dev-dependencies]:** Includes only comments that describe the section. - **[dev-addresses]:** Includes only comments that describe the section. From 33c65ab633d3dee3144bb7d2a3450835d406a348 Mon Sep 17 00:00:00 2001 From: Emma Zhong Date: Tue, 30 Jul 2024 05:48:31 -0700 Subject: [PATCH 026/232] [gql][indexer] index chain identifier into its own table (#18825) ## Description This PR puts chain identifier into its own table so that queries of chain identifier does not depend on the existence of checkpoint 0 in the db, which may be pruned away. ## Test plan Tested against devnet locally and added a gql e2e test. --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [x] GraphQL: Added a way to always have `chainIdentifier` query return the correct chain ID even when pruning is enabled. - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --------- Co-authored-by: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Co-authored-by: Ashok Menon --- .../tests/epoch/chain_identifier.exp | 16 +++++++++++ .../tests/epoch/chain_identifier.move | 12 ++++++++ .../src/types/chain_identifier.rs | 12 +++----- .../down.sql | 2 ++ .../2024-07-13-003534_chain_identifier/up.sql | 6 ++++ crates/sui-indexer/src/models/checkpoints.rs | 8 +++++- crates/sui-indexer/src/schema/mod.rs | 3 ++ crates/sui-indexer/src/schema/mysql.rs | 7 +++++ crates/sui-indexer/src/schema/pg.rs | 7 +++++ .../sui-indexer/src/store/pg_indexer_store.rs | 28 ++++++++++++++++--- 10 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 crates/sui-graphql-e2e-tests/tests/epoch/chain_identifier.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/epoch/chain_identifier.move create mode 100644 crates/sui-indexer/migrations/pg/2024-07-13-003534_chain_identifier/down.sql create mode 100644 crates/sui-indexer/migrations/pg/2024-07-13-003534_chain_identifier/up.sql diff --git a/crates/sui-graphql-e2e-tests/tests/epoch/chain_identifier.exp b/crates/sui-graphql-e2e-tests/tests/epoch/chain_identifier.exp new file mode 100644 index 0000000000000..c53d33a750392 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/epoch/chain_identifier.exp @@ -0,0 +1,16 @@ +processed 3 tasks + +init: +C: object(0,0) + +task 1, line 7: +//# create-checkpoint +Checkpoint created: 1 + +task 2, lines 9-12: +//# run-graphql +Response: { + "data": { + "chainIdentifier": "8f58ad28" + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/epoch/chain_identifier.move b/crates/sui-graphql-e2e-tests/tests/epoch/chain_identifier.move new file mode 100644 index 0000000000000..22f11bcf6277e --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/epoch/chain_identifier.move @@ -0,0 +1,12 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//# init --protocol-version 48 --simulator --accounts C + + +//# create-checkpoint + +//# run-graphql +{ + chainIdentifier +} \ No newline at end of file diff --git a/crates/sui-graphql-rpc/src/types/chain_identifier.rs b/crates/sui-graphql-rpc/src/types/chain_identifier.rs index 1072d0a580f1a..50fb3d8adaf81 100644 --- a/crates/sui-graphql-rpc/src/types/chain_identifier.rs +++ b/crates/sui-graphql-rpc/src/types/chain_identifier.rs @@ -6,8 +6,8 @@ use crate::{ error::Error, }; use async_graphql::*; -use diesel::{ExpressionMethods, QueryDsl}; -use sui_indexer::schema::checkpoints; +use diesel::QueryDsl; +use sui_indexer::schema::chain_identifier; use sui_types::{ digests::ChainIdentifier as NativeChainIdentifier, messages_checkpoint::CheckpointDigest, }; @@ -17,15 +17,11 @@ pub(crate) struct ChainIdentifier; impl ChainIdentifier { /// Query the Chain Identifier from the DB. pub(crate) async fn query(db: &Db) -> Result { - use checkpoints::dsl; + use chain_identifier::dsl; let digest_bytes = db .execute(move |conn| { - conn.first(move || { - dsl::checkpoints - .select(dsl::checkpoint_digest) - .order_by(dsl::sequence_number.asc()) - }) + conn.first(move || dsl::chain_identifier.select(dsl::checkpoint_digest)) }) .await .map_err(|e| Error::Internal(format!("Failed to fetch genesis digest: {e}")))?; diff --git a/crates/sui-indexer/migrations/pg/2024-07-13-003534_chain_identifier/down.sql b/crates/sui-indexer/migrations/pg/2024-07-13-003534_chain_identifier/down.sql new file mode 100644 index 0000000000000..57f1de973b1d2 --- /dev/null +++ b/crates/sui-indexer/migrations/pg/2024-07-13-003534_chain_identifier/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE IF EXISTS chain_identifier; diff --git a/crates/sui-indexer/migrations/pg/2024-07-13-003534_chain_identifier/up.sql b/crates/sui-indexer/migrations/pg/2024-07-13-003534_chain_identifier/up.sql new file mode 100644 index 0000000000000..205e3a89f63e5 --- /dev/null +++ b/crates/sui-indexer/migrations/pg/2024-07-13-003534_chain_identifier/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here +CREATE TABLE chain_identifier +( + checkpoint_digest BYTEA NOT NULL, + PRIMARY KEY(checkpoint_digest) +); diff --git a/crates/sui-indexer/src/models/checkpoints.rs b/crates/sui-indexer/src/models/checkpoints.rs index ea3579eb624d9..260fcfb5944f2 100644 --- a/crates/sui-indexer/src/models/checkpoints.rs +++ b/crates/sui-indexer/src/models/checkpoints.rs @@ -9,9 +9,15 @@ use sui_types::digests::CheckpointDigest; use sui_types::gas::GasCostSummary; use crate::errors::IndexerError; -use crate::schema::{checkpoints, pruner_cp_watermark}; +use crate::schema::{chain_identifier, checkpoints, pruner_cp_watermark}; use crate::types::IndexedCheckpoint; +#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] +#[diesel(table_name = chain_identifier)] +pub struct StoredChainIdentifier { + pub checkpoint_digest: Vec, +} + #[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] #[diesel(table_name = checkpoints)] pub struct StoredCheckpoint { diff --git a/crates/sui-indexer/src/schema/mod.rs b/crates/sui-indexer/src/schema/mod.rs index 2d14fe31d765f..d1d408d76a307 100644 --- a/crates/sui-indexer/src/schema/mod.rs +++ b/crates/sui-indexer/src/schema/mod.rs @@ -12,6 +12,7 @@ mod pg; #[cfg(feature = "postgres-feature")] mod inner { + pub use crate::schema::pg::chain_identifier; pub use crate::schema::pg::checkpoints; pub use crate::schema::pg::display; pub use crate::schema::pg::epochs; @@ -33,6 +34,7 @@ mod inner { #[cfg(feature = "mysql-feature")] #[cfg(not(feature = "postgres-feature"))] mod inner { + pub use crate::schema::mysql::chain_identifier; pub use crate::schema::mysql::checkpoints; pub use crate::schema::mysql::display; pub use crate::schema::mysql::epochs; @@ -51,6 +53,7 @@ mod inner { pub use crate::schema::mysql::tx_senders; } +pub use inner::chain_identifier; pub use inner::checkpoints; pub use inner::display; pub use inner::epochs; diff --git a/crates/sui-indexer/src/schema/mysql.rs b/crates/sui-indexer/src/schema/mysql.rs index fe5590929eed9..10cdc089c8884 100644 --- a/crates/sui-indexer/src/schema/mysql.rs +++ b/crates/sui-indexer/src/schema/mysql.rs @@ -2,6 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 // @generated automatically by Diesel CLI. +diesel::table! { + chain_identifier (checkpoint_digest) { + checkpoint_digest -> Blob, + } +} + diesel::table! { checkpoints (sequence_number) { sequence_number -> Bigint, @@ -227,6 +233,7 @@ diesel::table! { macro_rules! for_all_tables { ($action:path) => { $action!( + chain_identifier, checkpoints, epochs, events, diff --git a/crates/sui-indexer/src/schema/pg.rs b/crates/sui-indexer/src/schema/pg.rs index 68975e8584c12..322ad8b6a030a 100644 --- a/crates/sui-indexer/src/schema/pg.rs +++ b/crates/sui-indexer/src/schema/pg.rs @@ -2,6 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 // @generated automatically by Diesel CLI. +diesel::table! { + chain_identifier (checkpoint_digest) { + checkpoint_digest -> Bytea, + } +} + diesel::table! { checkpoints (sequence_number) { sequence_number -> Int8, @@ -284,6 +290,7 @@ diesel::table! { macro_rules! for_all_tables { ($action:path) => { $action!( + chain_identifier, checkpoints, pruner_cp_watermark, display, diff --git a/crates/sui-indexer/src/store/pg_indexer_store.rs b/crates/sui-indexer/src/store/pg_indexer_store.rs index fc4537cf73084..39e16c2e394ab 100644 --- a/crates/sui-indexer/src/store/pg_indexer_store.rs +++ b/crates/sui-indexer/src/store/pg_indexer_store.rs @@ -27,6 +27,7 @@ use crate::errors::{Context, IndexerError}; use crate::handlers::EpochToCommit; use crate::handlers::TransactionObjectChangesToCommit; use crate::metrics::IndexerMetrics; +use crate::models::checkpoints::StoredChainIdentifier; use crate::models::checkpoints::StoredCheckpoint; use crate::models::checkpoints::StoredCpTx; use crate::models::display::StoredDisplay; @@ -39,9 +40,9 @@ use crate::models::objects::{ use crate::models::packages::StoredPackage; use crate::models::transactions::StoredTransaction; use crate::schema::{ - checkpoints, display, epochs, events, objects, objects_history, objects_snapshot, packages, - pruner_cp_watermark, transactions, tx_calls, tx_changed_objects, tx_digests, tx_input_objects, - tx_recipients, tx_senders, + chain_identifier, checkpoints, display, epochs, events, objects, objects_history, + objects_snapshot, packages, pruner_cp_watermark, transactions, tx_calls, tx_changed_objects, + tx_digests, tx_input_objects, tx_recipients, tx_senders, }; use crate::types::{IndexedCheckpoint, IndexedEvent, IndexedPackage, IndexedTransaction, TxIndex}; use crate::{ @@ -598,8 +599,27 @@ impl PgIndexerStore { } fn persist_checkpoints(&self, checkpoints: Vec) -> Result<(), IndexerError> { - if checkpoints.is_empty() { + let Some(first_checkpoint) = checkpoints.first() else { return Ok(()); + }; + + // If the first checkpoint has sequence number 0, we need to persist the digest as + // chain identifier. + if first_checkpoint.sequence_number == 0 { + transactional_blocking_with_retry!( + &self.blocking_cp, + |conn| { + let checkpoint_digest = + first_checkpoint.checkpoint_digest.into_inner().to_vec(); + insert_or_ignore_into!( + chain_identifier::table, + StoredChainIdentifier { checkpoint_digest }, + conn + ); + Ok::<(), IndexerError>(()) + }, + PG_DB_COMMIT_SLEEP_DURATION + )?; } let guard = self .metrics From 75a729d5ad901b9776d34ca985eab8f6a5a435db Mon Sep 17 00:00:00 2001 From: mwtian <81660174+mwtian@users.noreply.github.com> Date: Tue, 30 Jul 2024 08:00:01 -0700 Subject: [PATCH 027/232] Remove wait timeout from LazyMysticetiClient (#18853) ## Description Getting a consensus client only fails when the validator has not finished initializing consensus. This issue can and should be detected inside consensus instead of through client. Also, clear consensus client at the end of epochs. ## Test plan CI --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .../consensus_manager/mysticeti_manager.rs | 15 +++--- crates/sui-core/src/mysticeti_adapter.rs | 52 ++++++++++--------- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/crates/sui-core/src/consensus_manager/mysticeti_manager.rs b/crates/sui-core/src/consensus_manager/mysticeti_manager.rs index 8bc6df769166e..3aa2abc343e5e 100644 --- a/crates/sui-core/src/consensus_manager/mysticeti_manager.rs +++ b/crates/sui-core/src/consensus_manager/mysticeti_manager.rs @@ -162,21 +162,15 @@ impl ConsensusManagerTrait for MysticetiManager { registry.clone(), ) .await; + let client = authority.transaction_client(); let registry_id = self.registry_service.add(registry.clone()); self.authority .swap(Some(Arc::new((authority, registry_id)))); - // create the client to send transactions to Mysticeti and update it. - self.client.set( - self.authority - .load() - .as_ref() - .expect("ConsensusAuthority should have been created by now.") - .0 - .transaction_client(), - ); + // Initialize the client to send transactions to this Mysticeti instance. + 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); @@ -190,6 +184,9 @@ impl ConsensusManagerTrait for MysticetiManager { return; }; + // Stop consensus submissions. + self.client.clear(); + // swap with empty to ensure there is no other reference to authority and we can safely do Arc unwrap let r = self.authority.swap(None).unwrap(); let Ok((authority, registry_id)) = Arc::try_unwrap(r) else { diff --git a/crates/sui-core/src/mysticeti_adapter.rs b/crates/sui-core/src/mysticeti_adapter.rs index b3e1f9f8888d9..7fe53780e0e29 100644 --- a/crates/sui-core/src/mysticeti_adapter.rs +++ b/crates/sui-core/src/mysticeti_adapter.rs @@ -10,7 +10,7 @@ use sui_types::{ messages_consensus::{ConsensusTransaction, ConsensusTransactionKind}, }; use tap::prelude::*; -use tokio::time::{sleep, timeout}; +use tokio::time::{sleep, Instant}; use tracing::warn; use crate::{ @@ -18,10 +18,9 @@ use crate::{ consensus_adapter::SubmitToConsensus, consensus_handler::SequencedConsensusTransactionKey, }; -/// Basically a wrapper struct that reads from the LOCAL_MYSTICETI_CLIENT variable where the latest -/// MysticetiClient is stored in order to communicate with Mysticeti. The LazyMysticetiClient is considered -/// "lazy" only in the sense that we can't use it directly to submit to consensus unless the underlying -/// local client is set first. +/// Gets a client to submit transactions to Mysticeti, or waits for one to be available. +/// This hides the complexities of async consensus initialization and submitting to different +/// instances of consensus across epochs. #[derive(Default, Clone)] pub struct LazyMysticetiClient { client: Arc>, @@ -40,34 +39,37 @@ impl LazyMysticetiClient { return client; } - // We expect this to get called during the SUI process start. After that at least one - // object will have initialised and won't need to call again. - const MYSTICETI_START_TIMEOUT: Duration = Duration::from_secs(60); - const LOAD_RETRY_TIMEOUT: Duration = Duration::from_millis(100); - if let Ok(client) = timeout(MYSTICETI_START_TIMEOUT, async { - loop { - let client = self.client.load(); - if client.is_some() { - return client; - } else { - sleep(LOAD_RETRY_TIMEOUT).await; + // Consensus client is initialized after validators or epoch starts, and cleared after an epoch ends. + // But calls to get() can happen during validator startup or epoch change, before consensus finished + // initializations. + // TODO: maybe listen to updates from consensus manager instead of polling. + let mut count = 0; + let start = Instant::now(); + const RETRY_INTERVAL: Duration = Duration::from_millis(100); + loop { + let client = self.client.load(); + if client.is_some() { + return client; + } else { + sleep(RETRY_INTERVAL).await; + count += 1; + if count % 100 == 0 { + warn!( + "Waiting for consensus to initialize after {:?}", + Instant::now() - start + ); } } - }) - .await - { - return client; } - - panic!( - "Timed out after {:?} waiting for Mysticeti to start!", - MYSTICETI_START_TIMEOUT, - ); } pub fn set(&self, client: Arc) { self.client.store(Some(client)); } + + pub fn clear(&self) { + self.client.store(None); + } } #[async_trait::async_trait] From 0e3903dd6176f441f39ce88c0af7db298a188383 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 30 Jul 2024 10:45:25 -0500 Subject: [PATCH 028/232] simtest: fix config-patch --- scripts/simtest/config-patch | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/simtest/config-patch b/scripts/simtest/config-patch index e4dfa3c59b31a..e30af462b02ad 100644 --- a/scripts/simtest/config-patch +++ b/scripts/simtest/config-patch @@ -12,11 +12,12 @@ diff --git a/Cargo.toml b/Cargo.toml index c0829bc1b6..4007f97d66 100644 --- a/Cargo.toml +++ b/Cargo.toml -@@ -682,3 +682,7 @@ field_names = "0.2.0" +@@ -682,6 +682,8 @@ field_names = "0.2.0" semver = "1.0.16" spinners = "4.1.0" include_dir = "0.7.3" -+ -+[patch.crates-io] + + [patch.crates-io] + quinn-proto = { git = "https://github.com/quinn-rs/quinn.git", rev = "f0fa66f871b80b9d2d7075d76967c649aecc0b77" } +tokio = { git = "https://github.com/MystenLabs/mysten-sim.git", rev = "220f52a15804a768610ac0ae3b8da7de4a5c4d2b" } +futures-timer = { git = "https://github.com/MystenLabs/mysten-sim.git", rev = "220f52a15804a768610ac0ae3b8da7de4a5c4d2b" } From 129f420e3114e132a1b4acbd47a422ddf0edd8f0 Mon Sep 17 00:00:00 2001 From: Andrew Schran Date: Tue, 30 Jul 2024 12:53:18 -0400 Subject: [PATCH 029/232] revert ci test log output back to default (#18859) --- .github/workflows/rust.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0192d2ed9e24f..43be0ff9c5430 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -43,8 +43,6 @@ env: RUSTUP_MAX_RETRIES: 10 # Don't emit giant backtraces in the CI logs. RUST_BACKTRACE: short - # Some integration tests can produce too much INFO logs that are infeasible to be printed on failure. - RUST_LOG: error # RUSTFLAGS: -D warnings RUSTDOCFLAGS: -D warnings From 6140ef850f4caad3a852d265cdc324c4cac4721a Mon Sep 17 00:00:00 2001 From: Lu Zhang <8418040+longbowlu@users.noreply.github.com> Date: Tue, 30 Jul 2024 11:58:09 -0700 Subject: [PATCH 030/232] [bridge] do not submit tx to sui if paused (#18828) ## Description in `ActionExecutor`, use `bridge_pause_rx` to decide whether to submit tx to Sui. ## Test plan new unit test --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-bridge/src/action_executor.rs | 109 ++++++++++++++++++++++- crates/sui-bridge/src/metrics.rs | 7 ++ 2 files changed, 114 insertions(+), 2 deletions(-) diff --git a/crates/sui-bridge/src/action_executor.rs b/crates/sui-bridge/src/action_executor.rs index 9fb45367b6ad5..c47c39b1d71a9 100644 --- a/crates/sui-bridge/src/action_executor.rs +++ b/crates/sui-bridge/src/action_executor.rs @@ -408,8 +408,7 @@ where >, bridge_object_arg: ObjectArg, mut token_config_rx: tokio::sync::watch::Receiver>, - // TODO: wire this up - _bridge_pause_rx: tokio::sync::watch::Receiver, + bridge_pause_rx: tokio::sync::watch::Receiver, metrics: Arc, ) { info!("Starting run_onchain_execution_loop"); @@ -429,6 +428,14 @@ where } }, certificate_wrapper = execution_queue_receiver.recv() => { + // When bridge is paused, skip execution. + // Skipped actions will be picked up upon node restarting + // if bridge is unpaused. + if *bridge_pause_rx.borrow() { + warn!("Bridge is paused, skipping execution"); + metrics.action_executor_execution_queue_skipped_actions_due_to_pausing.inc(); + continue; + } match certificate_wrapper { Some(certificate_wrapper) => { Self::handle_execution_task( @@ -663,6 +670,7 @@ pub async fn submit_to_executor( mod tests { use crate::events::init_all_struct_tags; use crate::test_utils::DUMMY_MUTALBE_BRIDGE_OBJECT_ARG; + use crate::types::BRIDGE_PAUSED; use fastcrypto::traits::KeyPair; use prometheus::Registry; use std::collections::{BTreeMap, HashMap}; @@ -1154,6 +1162,103 @@ mod tests { } } + #[tokio::test] + async fn test_skip_tx_submission_if_bridge_is_paused() { + let ( + _signing_tx, + execution_tx, + sui_client_mock, + mut tx_subscription, + store, + secrets, + dummy_sui_key, + mock0, + mock1, + mock2, + mock3, + _handles, + gas_object_ref, + sui_address, + token_tx, + bridge_pause_tx, + ) = setup().await; + let id_token_map = token_tx.borrow(); + let (action_certificate, _, _) = get_bridge_authority_approved_action( + vec![&mock0, &mock1, &mock2, &mock3], + vec![&secrets[0], &secrets[1], &secrets[2], &secrets[3]], + ); + + let action = action_certificate.data().clone(); + let arg = DUMMY_MUTALBE_BRIDGE_OBJECT_ARG; + let tx_data = build_sui_transaction( + sui_address, + &gas_object_ref, + action_certificate.clone(), + arg, + &id_token_map, + 1000, + ) + .unwrap(); + let tx_digest = get_tx_digest(tx_data, &dummy_sui_key); + mock_transaction_error( + &sui_client_mock, + tx_digest, + BridgeError::Generic("some random error".to_string()), + true, + ); + + let gas_coin = GasCoin::new_for_testing(1_000_000_000_000); // dummy gas coin + sui_client_mock.add_gas_object_info( + gas_coin.clone(), + gas_object_ref, + Owner::AddressOwner(sui_address), + ); + let action_digest = action.digest(); + sui_client_mock.set_action_onchain_status(&action, BridgeActionStatus::Pending); + + // assert bridge is unpaused now + assert!(!*bridge_pause_tx.borrow()); + + store.insert_pending_actions(&[action.clone()]).unwrap(); + assert_eq!( + store.get_all_pending_actions()[&action.digest()], + action.clone() + ); + + // Kick it (send to the execution queue, skipping the signing queue) + execution_tx + .send(CertifiedBridgeActionExecutionWrapper( + action_certificate.clone(), + 0, + )) + .await + .unwrap(); + + // Some requests come in and will fail. + tx_subscription.recv().await.unwrap(); + + // Pause the bridge + bridge_pause_tx.send(BRIDGE_PAUSED).unwrap(); + + // Kick it again + execution_tx + .send(CertifiedBridgeActionExecutionWrapper(action_certificate, 0)) + .await + .unwrap(); + + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + // Nothing is sent to execute + assert_eq!( + tx_subscription.try_recv().unwrap_err(), + tokio::sync::broadcast::error::TryRecvError::Empty + ); + // Still in WAL + assert_eq!( + store.get_all_pending_actions()[&action_digest], + action.clone() + ); + } + fn mock_bridge_authority_sigs( mocks: Vec<&BridgeRequestMockHandler>, action: &BridgeAction, diff --git a/crates/sui-bridge/src/metrics.rs b/crates/sui-bridge/src/metrics.rs index 556eb47e20278..a4999744e8d14 100644 --- a/crates/sui-bridge/src/metrics.rs +++ b/crates/sui-bridge/src/metrics.rs @@ -33,6 +33,7 @@ pub struct BridgeMetrics { pub(crate) action_executor_signing_queue_received_actions: IntCounter, pub(crate) action_executor_signing_queue_skipped_actions: IntCounter, pub(crate) action_executor_execution_queue_received_actions: IntCounter, + pub(crate) action_executor_execution_queue_skipped_actions_due_to_pausing: IntCounter, pub(crate) signer_with_cache_hit: IntCounterVec, pub(crate) signer_with_cache_miss: IntCounterVec, @@ -162,6 +163,12 @@ impl BridgeMetrics { registry, ) .unwrap(), + action_executor_execution_queue_skipped_actions_due_to_pausing: register_int_counter_with_registry!( + "bridge_action_executor_execution_queue_skipped_actions_due_to_pausing", + "Total number of skipped actions in action executor execution queue because of pausing", + registry, + ) + .unwrap(), gas_coin_balance: register_int_gauge_with_registry!( "bridge_gas_coin_balance", "Current balance of gas coin, in mist", From 8d68ee64149d2b8adf2b0b4f069b6e80c39021f4 Mon Sep 17 00:00:00 2001 From: Lu Zhang <8418040+longbowlu@users.noreply.github.com> Date: Tue, 30 Jul 2024 12:06:06 -0700 Subject: [PATCH 031/232] allow custom validator num in bridge tests (#18835) ## Description As title, this will make some e2e tests easier to write. It's worth noting that surprisingly this does not help with fullnode sync up speed in the test, which is the main source of slowness in bridge e2e tests. ## Test plan existing tests --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-bridge/src/e2e_tests/basic.rs | 10 ++++++- crates/sui-bridge/src/e2e_tests/complex.rs | 7 +++-- crates/sui-bridge/src/e2e_tests/test_utils.rs | 30 +++++++++++++++---- crates/sui-bridge/src/eth_syncer.rs | 2 +- crates/sui-bridge/src/events.rs | 1 + crates/sui-bridge/src/node.rs | 1 + 6 files changed, 40 insertions(+), 11 deletions(-) diff --git a/crates/sui-bridge/src/e2e_tests/basic.rs b/crates/sui-bridge/src/e2e_tests/basic.rs index e1d773ac81187..e875ef9efd72d 100644 --- a/crates/sui-bridge/src/e2e_tests/basic.rs +++ b/crates/sui-bridge/src/e2e_tests/basic.rs @@ -157,6 +157,7 @@ async fn test_add_new_coins_on_sui() { let mut bridge_test_cluster = BridgeTestClusterBuilder::new() .with_eth_env(true) .with_bridge_cluster(false) + .with_num_validators(3) .build() .await; @@ -180,7 +181,6 @@ async fn test_add_new_coins_on_sui() { info!("Starting bridge cluster"); bridge_test_cluster.set_approved_governance_actions_for_next_start(vec![ - vec![action.clone()], vec![action.clone()], vec![action.clone()], vec![], @@ -469,11 +469,19 @@ async fn wait_for_transfer_action_status( ) -> Result<(), anyhow::Error> { // Wait for the bridge action to be approved let now = std::time::Instant::now(); + info!( + "Waiting for onchain status {:?}. chain: {:?}, nonce: {nonce}", + status, chain_id as u8 + ); loop { let res = sui_bridge_client .get_token_transfer_action_onchain_status_until_success(chain_id as u8, nonce) .await; if res == status { + info!( + "detected on chain status {:?}. chain: {:?}, nonce: {nonce}", + status, chain_id as u8 + ); return Ok(()); } if now.elapsed().as_secs() > 60 { diff --git a/crates/sui-bridge/src/e2e_tests/complex.rs b/crates/sui-bridge/src/e2e_tests/complex.rs index 390ca71b71e27..bc20619d511af 100644 --- a/crates/sui-bridge/src/e2e_tests/complex.rs +++ b/crates/sui-bridge/src/e2e_tests/complex.rs @@ -35,13 +35,14 @@ async fn test_sui_bridge_paused() { // Setup bridge test env let bridge_test_cluster = BridgeTestClusterBuilder::new() .with_eth_env(true) + .with_bridge_cluster(true) + .with_num_validators(4) .with_approved_governance_actions(vec![ vec![pause_action.clone(), unpause_action.clone()], - vec![pause_action.clone(), unpause_action.clone()], - vec![pause_action.clone(), unpause_action.clone()], + vec![unpause_action.clone()], + vec![unpause_action.clone()], vec![], ]) - .with_bridge_cluster(true) .build() .await; diff --git a/crates/sui-bridge/src/e2e_tests/test_utils.rs b/crates/sui-bridge/src/e2e_tests/test_utils.rs index 1768f16df3d7a..f75969f6a0b34 100644 --- a/crates/sui-bridge/src/e2e_tests/test_utils.rs +++ b/crates/sui-bridge/src/e2e_tests/test_utils.rs @@ -73,6 +73,7 @@ pub const TEST_PK: &str = "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1 /// A helper struct that holds TestCluster and other Bridge related /// structs that are needed for testing. pub struct BridgeTestCluster { + pub num_validators: usize, pub test_cluster: TestCluster, bridge_client: SuiBridgeClient, eth_environment: EthBridgeEnvironment, @@ -86,6 +87,7 @@ pub struct BridgeTestCluster { pub struct BridgeTestClusterBuilder { with_eth_env: bool, with_bridge_cluster: bool, + num_validators: usize, approved_governance_actions: Option>>, eth_chain_id: BridgeChainId, sui_chain_id: BridgeChainId, @@ -102,6 +104,7 @@ impl BridgeTestClusterBuilder { BridgeTestClusterBuilder { with_eth_env: false, with_bridge_cluster: false, + num_validators: 4, approved_governance_actions: None, eth_chain_id: BridgeChainId::EthCustom, sui_chain_id: BridgeChainId::SuiCustom, @@ -118,10 +121,16 @@ impl BridgeTestClusterBuilder { self } + pub fn with_num_validators(mut self, num_validators: usize) -> Self { + self.num_validators = num_validators; + self + } + pub fn with_approved_governance_actions( mut self, approved_governance_actions: Vec>, ) -> Self { + assert_eq!(approved_governance_actions.len(), self.num_validators); self.approved_governance_actions = Some(approved_governance_actions); self } @@ -139,15 +148,15 @@ impl BridgeTestClusterBuilder { pub async fn build(self) -> BridgeTestCluster { init_all_struct_tags(); std::env::set_var("__TEST_ONLY_CONSENSUS_USE_LONG_MIN_ROUND_DELAY", "1"); - let mut bridge_keys = vec![]; let mut bridge_keys_copy = vec![]; - for _ in 0..=3 { + for _ in 0..self.num_validators { let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair(); bridge_keys.push(kp.copy()); bridge_keys_copy.push(kp); } - let start_cluster_task = tokio::task::spawn(Self::start_test_cluster(bridge_keys)); + let start_cluster_task = + tokio::task::spawn(Self::start_test_cluster(bridge_keys, self.num_validators)); let start_eth_env_task = tokio::task::spawn(Self::start_eth_env(bridge_keys_copy)); let (start_cluster_res, start_eth_env_res) = join!(start_cluster_task, start_eth_env_task); let test_cluster = start_cluster_res.unwrap(); @@ -158,7 +167,7 @@ impl BridgeTestClusterBuilder { let approved_governace_actions = self .approved_governance_actions .clone() - .unwrap_or(vec![vec![], vec![], vec![], vec![]]); + .unwrap_or(vec![vec![]; self.num_validators]); bridge_node_handles = Some( start_bridge_cluster(&test_cluster, ð_environment, approved_governace_actions) .await, @@ -168,6 +177,7 @@ impl BridgeTestClusterBuilder { .await .unwrap(); BridgeTestCluster { + num_validators: self.num_validators, test_cluster, bridge_client, eth_environment, @@ -179,8 +189,13 @@ impl BridgeTestClusterBuilder { } } - async fn start_test_cluster(bridge_keys: Vec) -> TestCluster { + async fn start_test_cluster( + bridge_keys: Vec, + num_validators: usize, + ) -> TestCluster { + assert_eq!(bridge_keys.len(), num_validators); let test_cluster: test_cluster::TestCluster = TestClusterBuilder::new() + .with_num_validators(num_validators) .with_protocol_version(BRIDGE_ENABLE_PROTOCOL_VERSION.into()) .build_with_bridge(bridge_keys, true) .await; @@ -303,6 +318,7 @@ impl BridgeTestCluster { &mut self, approved_governance_actions: Vec>, ) { + assert_eq!(approved_governance_actions.len(), self.num_validators); self.approved_governance_actions_for_next_start = Some(approved_governance_actions); } @@ -470,7 +486,9 @@ pub(crate) async fn deploy_sol_contract( ) }) .collect::>(); - let committee_member_stake = vec![stake; node_len]; + let mut committee_member_stake = vec![stake; node_len]; + // Adjust it so that the total stake is equal to TOTAL_VOTING_POWER + committee_member_stake[node_len - 1] = TOTAL_VOTING_POWER - stake * (node_len as u64 - 1); let deploy_config = SolDeployConfig { committee_member_stake: committee_member_stake.clone(), committee_members: committee_members.clone(), diff --git a/crates/sui-bridge/src/eth_syncer.rs b/crates/sui-bridge/src/eth_syncer.rs index e52461fbdbc76..840dab701060d 100644 --- a/crates/sui-bridge/src/eth_syncer.rs +++ b/crates/sui-bridge/src/eth_syncer.rs @@ -169,7 +169,7 @@ where error!("Failed to get events from eth client after retry"); continue; }; - tracing::info!( + tracing::debug!( ?contract_address, start_block, end_block, diff --git a/crates/sui-bridge/src/events.rs b/crates/sui-bridge/src/events.rs index 6072f9fd8b23e..93f04efd2570d 100644 --- a/crates/sui-bridge/src/events.rs +++ b/crates/sui-bridge/src/events.rs @@ -506,6 +506,7 @@ pub mod tests { let mut bridge_test_cluster = BridgeTestClusterBuilder::new() .with_eth_env(false) .with_bridge_cluster(false) + .with_num_validators(2) .build() .await; diff --git a/crates/sui-bridge/src/node.rs b/crates/sui-bridge/src/node.rs index ee0c9be68542f..d1331abf2cd7e 100644 --- a/crates/sui-bridge/src/node.rs +++ b/crates/sui-bridge/src/node.rs @@ -577,6 +577,7 @@ mod tests { BridgeTestClusterBuilder::new() .with_eth_env(true) .with_bridge_cluster(false) + .with_num_validators(2) .build() .await } From 447707c01284f450a97817bb6d9735bc5ad8ba28 Mon Sep 17 00:00:00 2001 From: plam-ml <127577476+plam-ml@users.noreply.github.com> Date: Tue, 30 Jul 2024 12:33:25 -0700 Subject: [PATCH 032/232] update promo setup (#18858) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description - Rounded corners in apps list - Bake in centering and padding for interstitial ![Screenshot 2024-07-30 at 8 37 13 AM](https://github.com/user-attachments/assets/b3cd8f9b-a615-4abc-a49b-db072054b059) ![Screenshot 2024-07-30 at 10 53 57 AM](https://github.com/user-attachments/assets/e7e11c05-f8f7-4455-b491-d63f89b910e6) ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .../src/ui/app/components/sui-apps/Banner.tsx | 6 ++- .../ui/app/pages/home/interstitial/index.tsx | 50 ++++++++++++------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/apps/wallet/src/ui/app/components/sui-apps/Banner.tsx b/apps/wallet/src/ui/app/components/sui-apps/Banner.tsx index 423ea49c33961..e7bd0bc026872 100644 --- a/apps/wallet/src/ui/app/components/sui-apps/Banner.tsx +++ b/apps/wallet/src/ui/app/components/sui-apps/Banner.tsx @@ -27,7 +27,11 @@ export function AppsPageBanner() { href={AppsBannerConfig.value?.bannerUrl} onClick={() => ampli.clickedBullsharkQuestsCta({ sourceFlow: 'Banner - Apps tab' })} > - Apps Banner + Apps Banner )} diff --git a/apps/wallet/src/ui/app/pages/home/interstitial/index.tsx b/apps/wallet/src/ui/app/pages/home/interstitial/index.tsx index 912cdd9ddeb9f..b60006959dc03 100644 --- a/apps/wallet/src/ui/app/pages/home/interstitial/index.tsx +++ b/apps/wallet/src/ui/app/pages/home/interstitial/index.tsx @@ -1,8 +1,10 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +import { ButtonOrLink } from '_app/shared/utils/ButtonOrLink'; import { ampli } from '_src/shared/analytics/ampli'; import ExternalLink from '_src/ui/app/components/external-link'; +import { Text } from '_src/ui/app/shared/text'; import { X32 } from '@mysten/icons'; import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; @@ -13,7 +15,7 @@ export type InterstitialConfig = { enabled: boolean; dismissKey?: string; imageUrl?: string; - + buttonText?: string; bannerUrl?: string; }; @@ -23,7 +25,14 @@ interface InterstitialProps extends InterstitialConfig { const setInterstitialDismissed = (dismissKey: string) => localStorage.setItem(dismissKey, 'true'); -function Interstitial({ enabled, dismissKey, imageUrl, bannerUrl, onClose }: InterstitialProps) { +function Interstitial({ + enabled, + dismissKey, + imageUrl, + bannerUrl, + buttonText, + onClose, +}: InterstitialProps) { const navigate = useNavigate(); useEffect(() => { @@ -39,32 +48,39 @@ function Interstitial({ enabled, dismissKey, imageUrl, bannerUrl, onClose }: Int navigate('/apps'); }; + const onClick = () => { + ampli.clickedBullsharkQuestsCta({ sourceFlow: 'Interstitial' }); + closeInterstitial(); + }; + if (!enabled) { return null; } return ( -
- {bannerUrl && ( - { - ampli.clickedBullsharkQuestsCta({ sourceFlow: 'Interstitial' }); - closeInterstitial(); - }} - className="w-full h-full" - > - interstitial-banner - - )} +
+ {bannerUrl && ( + + interstitial-banner + + )} + + + {buttonText || 'Join for a chance to win'} + +
); From b3f67dfbd1b367bdb3763add1cdf838af7b92acf Mon Sep 17 00:00:00 2001 From: Lu Zhang <8418040+longbowlu@users.noreply.github.com> Date: Tue, 30 Jul 2024 12:53:52 -0700 Subject: [PATCH 033/232] [bridge] move add new coin's handling to monitor (#18831) ## Description Today, when receiving an `AddNewToken` event, orchestrator updates the sui type tags map and send to the watch channel that action execution watches. In this PR, we consolidate the handling of this event with the newly added monitor module. In the new approach, orchestrator sends the event to monitor, instead of handling it in place. Also the type tag maps is stored in a `ArcSwap` to allow monitor to change it. Hence we get rid of the watch channel. ## Test plan Added unit tests. --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-bridge/src/action_executor.rs | 242 ++++++++++++------ crates/sui-bridge/src/monitor.rs | 117 ++++++++- crates/sui-bridge/src/node.rs | 7 +- crates/sui-bridge/src/orchestrator.rs | 112 +------- crates/sui-bridge/src/sui_client.rs | 2 +- .../sui-bridge/src/sui_transaction_builder.rs | 2 +- crates/sui-bridge/src/test_utils.rs | 3 +- crates/sui-bridge/src/types.rs | 2 +- 8 files changed, 283 insertions(+), 204 deletions(-) diff --git a/crates/sui-bridge/src/action_executor.rs b/crates/sui-bridge/src/action_executor.rs index c47c39b1d71a9..113838ab32df7 100644 --- a/crates/sui-bridge/src/action_executor.rs +++ b/crates/sui-bridge/src/action_executor.rs @@ -78,7 +78,7 @@ pub struct BridgeActionExecutor { gas_object_id: ObjectID, store: Arc, bridge_object_arg: ObjectArg, - token_config_rx: tokio::sync::watch::Receiver>, + sui_token_type_tags: Arc>>, bridge_pause_rx: tokio::sync::watch::Receiver, metrics: Arc, } @@ -109,7 +109,7 @@ where key: SuiKeyPair, sui_address: SuiAddress, gas_object_id: ObjectID, - token_config_rx: tokio::sync::watch::Receiver>, + sui_token_type_tags: Arc>>, bridge_pause_rx: tokio::sync::watch::Receiver, metrics: Arc, ) -> Self { @@ -124,7 +124,7 @@ where gas_object_id, sui_address, bridge_object_arg, - token_config_rx, + sui_token_type_tags, bridge_pause_rx, metrics, } @@ -184,7 +184,7 @@ where execution_tx_clone, execution_rx, self.bridge_object_arg, - self.token_config_rx, + self.sui_token_type_tags, self.bridge_pause_rx, metrics, ) @@ -407,58 +407,37 @@ where CertifiedBridgeActionExecutionWrapper, >, bridge_object_arg: ObjectArg, - mut token_config_rx: tokio::sync::watch::Receiver>, + sui_token_type_tags: Arc>>, bridge_pause_rx: tokio::sync::watch::Receiver, metrics: Arc, ) { info!("Starting run_onchain_execution_loop"); - let sui_token_type_tags = - ArcSwap::new(Arc::new(token_config_rx.borrow_and_update().clone())); - - loop { - tokio::select! { - result = token_config_rx.changed() => { - match result { - Ok(()) => { - sui_token_type_tags.store(Arc::new(token_config_rx.borrow_and_update().clone())); - } - Err(_) => { - panic!("Token config watch channel closed unexpectedly"); - } - } - }, - certificate_wrapper = execution_queue_receiver.recv() => { - // When bridge is paused, skip execution. - // Skipped actions will be picked up upon node restarting - // if bridge is unpaused. - if *bridge_pause_rx.borrow() { - warn!("Bridge is paused, skipping execution"); - metrics.action_executor_execution_queue_skipped_actions_due_to_pausing.inc(); - continue; - } - match certificate_wrapper { - Some(certificate_wrapper) => { - Self::handle_execution_task( - certificate_wrapper, - &sui_client, - &sui_key, - &sui_address, - gas_object_id, - &store, - &execution_queue_sender, - &bridge_object_arg, - &sui_token_type_tags, - &metrics, - ) - .await; - }, - None => { - panic!("Execution queue closed unexpectedly"); - } - } - } + while let Some(certificate_wrapper) = execution_queue_receiver.recv().await { + // When bridge is paused, skip execution. + // Skipped actions will be picked up upon node restarting + // if bridge is unpaused. + if *bridge_pause_rx.borrow() { + warn!("Bridge is paused, skipping execution"); + metrics + .action_executor_execution_queue_skipped_actions_due_to_pausing + .inc(); + continue; } + Self::handle_execution_task( + certificate_wrapper, + &sui_client, + &sui_key, + &sui_address, + gas_object_id, + &store, + &execution_queue_sender, + &bridge_object_arg, + &sui_token_type_tags, + &metrics, + ) + .await; } + panic!("Execution queue closed unexpectedly"); } #[instrument(level = "error", skip_all, fields(action_key=?certificate_wrapper.0.data().key(), attempt_times=?certificate_wrapper.1))] @@ -674,6 +653,7 @@ mod tests { use fastcrypto::traits::KeyPair; use prometheus::Registry; use std::collections::{BTreeMap, HashMap}; + use std::str::FromStr; use sui_json_rpc_types::SuiTransactionBlockEffects; use sui_json_rpc_types::SuiTransactionBlockEvents; use sui_json_rpc_types::{SuiEvent, SuiTransactionBlockResponse}; @@ -690,8 +670,8 @@ mod tests { server::mock_handler::BridgeRequestMockHandler, sui_mock_client::SuiMockClient, test_utils::{ - get_test_authorities_and_run_mock_bridge_server, get_test_sui_to_eth_bridge_action, - sign_action_with_key, + get_test_authorities_and_run_mock_bridge_server, get_test_eth_to_sui_bridge_action, + get_test_sui_to_eth_bridge_action, sign_action_with_key, }, types::{BridgeCommittee, BridgeCommitteeValiditySignInfo, CertifiedBridgeAction}, }; @@ -715,15 +695,17 @@ mod tests { _handles, gas_object_ref, sui_address, - token_tx, + sui_token_type_tags, _bridge_pause_tx, ) = setup().await; let (action_certificate, _, _) = get_bridge_authority_approved_action( vec![&mock0, &mock1, &mock2, &mock3], vec![&secrets[0], &secrets[1], &secrets[2], &secrets[3]], + None, + true, ); let action = action_certificate.data().clone(); - let id_token_map = token_tx.borrow(); + let id_token_map = (*sui_token_type_tags.load().clone()).clone(); let tx_data = build_sui_transaction( sui_address, &gas_object_ref, @@ -777,6 +759,8 @@ mod tests { let (action_certificate, _, _) = get_bridge_authority_approved_action( vec![&mock0, &mock1, &mock2, &mock3], vec![&secrets[0], &secrets[1], &secrets[2], &secrets[3]], + None, + true, ); let action = action_certificate.data().clone(); @@ -829,6 +813,8 @@ mod tests { let (action_certificate, _, _) = get_bridge_authority_approved_action( vec![&mock0, &mock1, &mock2, &mock3], vec![&secrets[0], &secrets[1], &secrets[2], &secrets[3]], + None, + true, ); let action = action_certificate.data().clone(); @@ -907,14 +893,16 @@ mod tests { _handles, gas_object_ref, sui_address, - token_tx, + sui_token_type_tags, _bridge_pause_tx, ) = setup().await; - let id_token_map = token_tx.borrow(); + let id_token_map = (*sui_token_type_tags.load().clone()).clone(); let (action_certificate, sui_tx_digest, sui_tx_event_index) = get_bridge_authority_approved_action( vec![&mock0, &mock1, &mock2, &mock3], vec![&secrets[0], &secrets[1], &secrets[2], &secrets[3]], + None, + true, ); let action = action_certificate.data().clone(); mock_bridge_authority_signing_errors( @@ -1028,7 +1016,7 @@ mod tests { _handles, _gas_object_ref, _sui_address, - _token_tx, + _sui_token_type_tags, _bridge_pause_tx, ) = setup().await; @@ -1096,13 +1084,15 @@ mod tests { _handles, gas_object_ref, sui_address, - token_tx, + sui_token_type_tags, _bridge_pause_tx, ) = setup().await; - let id_token_map = token_tx.borrow(); + let id_token_map = (*sui_token_type_tags.load().clone()).clone(); let (action_certificate, _, _) = get_bridge_authority_approved_action( vec![&mock0, &mock1, &mock2, &mock3], vec![&secrets[0], &secrets[1], &secrets[2], &secrets[3]], + None, + true, ); let action = action_certificate.data().clone(); @@ -1179,13 +1169,15 @@ mod tests { _handles, gas_object_ref, sui_address, - token_tx, + sui_token_type_tags, bridge_pause_tx, ) = setup().await; - let id_token_map = token_tx.borrow(); + let id_token_map: HashMap = (*sui_token_type_tags.load().clone()).clone(); let (action_certificate, _, _) = get_bridge_authority_approved_action( vec![&mock0, &mock1, &mock2, &mock3], vec![&secrets[0], &secrets[1], &secrets[2], &secrets[3]], + None, + true, ); let action = action_certificate.data().clone(); @@ -1234,7 +1226,7 @@ mod tests { .await .unwrap(); - // Some requests come in and will fail. + // Some requests come in tx_subscription.recv().await.unwrap(); // Pause the bridge @@ -1259,6 +1251,99 @@ mod tests { ); } + #[tokio::test] + async fn test_action_executor_handle_new_token() { + let new_token_id = 255u8; // token id that does not exist + let new_type_tag = TypeTag::from_str("0xbeef::beef::BEEF").unwrap(); + let ( + _signing_tx, + execution_tx, + sui_client_mock, + mut tx_subscription, + _store, + secrets, + dummy_sui_key, + mock0, + mock1, + mock2, + mock3, + _handles, + gas_object_ref, + sui_address, + sui_token_type_tags, + _bridge_pause_tx, + ) = setup().await; + let mut id_token_map: HashMap = (*sui_token_type_tags.load().clone()).clone(); + let (action_certificate, _, _) = get_bridge_authority_approved_action( + vec![&mock0, &mock1, &mock2, &mock3], + vec![&secrets[0], &secrets[1], &secrets[2], &secrets[3]], + Some(new_token_id), + false, // we need an eth -> sui action that entails the new token type tag in transaction building + ); + + let action = action_certificate.data().clone(); + let arg = DUMMY_MUTALBE_BRIDGE_OBJECT_ARG; + let tx_data = build_sui_transaction( + sui_address, + &gas_object_ref, + action_certificate.clone(), + arg, + &maplit::hashmap! { + new_token_id => new_type_tag.clone() + }, + 1000, + ) + .unwrap(); + let tx_digest = get_tx_digest(tx_data, &dummy_sui_key); + mock_transaction_error( + &sui_client_mock, + tx_digest, + BridgeError::Generic("some random error".to_string()), + true, + ); + + let gas_coin = GasCoin::new_for_testing(1_000_000_000_000); // dummy gas coin + sui_client_mock.add_gas_object_info( + gas_coin.clone(), + gas_object_ref, + Owner::AddressOwner(sui_address), + ); + sui_client_mock.set_action_onchain_status(&action, BridgeActionStatus::Pending); + + // Kick it (send to the execution queue, skipping the signing queue) + execution_tx + .send(CertifiedBridgeActionExecutionWrapper( + action_certificate.clone(), + 0, + )) + .await + .unwrap(); + + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + // Nothing is sent to execute, because the token id does not exist yet + assert_eq!( + tx_subscription.try_recv().unwrap_err(), + tokio::sync::broadcast::error::TryRecvError::Empty + ); + + // Now insert the new token id + id_token_map.insert(new_token_id, new_type_tag); + sui_token_type_tags.store(Arc::new(id_token_map)); + + // Kick it again + execution_tx + .send(CertifiedBridgeActionExecutionWrapper( + action_certificate.clone(), + 0, + )) + .await + .unwrap(); + + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + // The action is sent to execution + assert_eq!(tx_subscription.recv().await.unwrap(), tx_digest); + } + fn mock_bridge_authority_sigs( mocks: Vec<&BridgeRequestMockHandler>, action: &BridgeAction, @@ -1298,18 +1383,24 @@ mod tests { fn get_bridge_authority_approved_action( mocks: Vec<&BridgeRequestMockHandler>, secrets: Vec<&BridgeAuthorityKeyPair>, + token_id: Option, + sui_to_eth: bool, ) -> (VerifiedCertifiedBridgeAction, TransactionDigest, u16) { let sui_tx_digest = TransactionDigest::random(); let sui_tx_event_index = 1; - let action = get_test_sui_to_eth_bridge_action( - Some(sui_tx_digest), - Some(sui_tx_event_index), - None, - None, - None, - None, - None, - ); + let action = if sui_to_eth { + get_test_sui_to_eth_bridge_action( + Some(sui_tx_digest), + Some(sui_tx_event_index), + None, + None, + None, + None, + token_id, + ) + } else { + get_test_eth_to_sui_bridge_action(None, None, None, token_id) + }; let sigs = mock_bridge_authority_sigs(mocks, &action, secrets, sui_tx_digest, sui_tx_event_index); @@ -1385,7 +1476,7 @@ mod tests { Vec>, ObjectRef, SuiAddress, - tokio::sync::watch::Sender>, + Arc>>, tokio::sync::watch::Sender, ) { telemetry_subscribers::init_for_testing(); @@ -1424,8 +1515,7 @@ mod tests { )))); let metrics = Arc::new(BridgeMetrics::new(®istry)); let sui_token_type_tags = sui_client.get_token_id_map().await.unwrap(); - let (token_type_tags_tx, token_type_tags_rx) = - tokio::sync::watch::channel(sui_token_type_tags); + let sui_token_type_tags = Arc::new(ArcSwap::new(Arc::new(sui_token_type_tags))); let (bridge_pause_tx, bridge_pause_rx) = tokio::sync::watch::channel(false); let executor = BridgeActionExecutor::new( sui_client.clone(), @@ -1434,7 +1524,7 @@ mod tests { sui_key, sui_address, gas_object_ref.0, - token_type_tags_rx, + sui_token_type_tags.clone(), bridge_pause_rx, metrics, ) @@ -1458,7 +1548,7 @@ mod tests { handles, gas_object_ref, sui_address, - token_type_tags_tx, + sui_token_type_tags, bridge_pause_tx, ) } diff --git a/crates/sui-bridge/src/monitor.rs b/crates/sui-bridge/src/monitor.rs index 5119b115a6a2e..12affd68d0aa7 100644 --- a/crates/sui-bridge/src/monitor.rs +++ b/crates/sui-bridge/src/monitor.rs @@ -3,10 +3,6 @@ //! `BridgeMonitor` receives all `SuiBridgeEvent` and handles them accordingly. -use arc_swap::ArcSwap; -use std::sync::Arc; -use tokio::time::Duration; - use crate::client::bridge_authority_aggregator::BridgeAuthorityAggregator; use crate::crypto::BridgeAuthorityPublicKeyBytes; use crate::events::{BlocklistValidatorEvent, CommitteeMemberUrlUpdateEvent}; @@ -14,6 +10,11 @@ use crate::events::{EmergencyOpEvent, SuiBridgeEvent}; use crate::retry_with_max_elapsed_time; use crate::sui_client::{SuiClient, SuiClientInner}; use crate::types::{BridgeCommittee, IsBridgePaused}; +use arc_swap::ArcSwap; +use std::collections::HashMap; +use std::sync::Arc; +use sui_types::TypeTag; +use tokio::time::Duration; use tracing::{error, info, warn}; const REFRESH_BRIDGE_RETRY_TIMES: u64 = 3; @@ -23,6 +24,7 @@ pub struct BridgeMonitor { monitor_rx: mysten_metrics::metered_channel::Receiver, bridge_auth_agg: Arc>, bridge_paused_watch_tx: tokio::sync::watch::Sender, + sui_token_type_tags: Arc>>, } impl BridgeMonitor @@ -34,12 +36,14 @@ where monitor_rx: mysten_metrics::metered_channel::Receiver, bridge_auth_agg: Arc>, bridge_paused_watch_tx: tokio::sync::watch::Sender, + sui_token_type_tags: Arc>>, ) -> Self { Self { sui_client, monitor_rx, bridge_auth_agg, bridge_paused_watch_tx, + sui_token_type_tags, } } @@ -50,7 +54,9 @@ where mut monitor_rx, bridge_auth_agg, bridge_paused_watch_tx, + sui_token_type_tags, } = self; + let mut latest_token_config = (*sui_token_type_tags.load().clone()).clone(); while let Some(events) = monitor_rx.recv().await { match events { @@ -62,6 +68,7 @@ where SuiBridgeEvent::TokenTransferLimitExceed(_) => { // TODO } + SuiBridgeEvent::EmergencyOpEvent(event) => { info!("Received EmergencyOpEvent: {:?}", event); let is_paused = get_latest_bridge_pause_status_with_emergency_event( @@ -74,8 +81,10 @@ where .send(is_paused) .expect("Bridge pause status watch channel should not be closed"); } + SuiBridgeEvent::CommitteeMemberRegistration(_) => (), SuiBridgeEvent::CommitteeUpdateEvent(_) => (), + SuiBridgeEvent::CommitteeMemberUrlUpdateEvent(event) => { info!("Received CommitteeMemberUrlUpdateEvent: {:?}", event); let new_committee = get_latest_bridge_committee_with_url_update_event( @@ -89,6 +98,7 @@ where )))); info!("Committee updated with CommitteeMemberUrlUpdateEvent"); } + SuiBridgeEvent::BlocklistValidatorEvent(event) => { info!("Received BlocklistValidatorEvent: {:?}", event); let new_committee = get_latest_bridge_committee_with_blocklist_event( @@ -102,10 +112,22 @@ where )))); info!("Committee updated with BlocklistValidatorEvent"); } + SuiBridgeEvent::TokenRegistrationEvent(_) => (), - SuiBridgeEvent::NewTokenEvent(_) => { - // TODO + + SuiBridgeEvent::NewTokenEvent(event) => { + if let std::collections::hash_map::Entry::Vacant(entry) = + // We only add new tokens but not remove so it's ok to just insert + latest_token_config.entry(event.token_id) + { + entry.insert(event.type_name.clone()); + sui_token_type_tags.store(Arc::new(latest_token_config.clone())); + } else { + // invariant + assert_eq!(event.type_name, latest_token_config[&event.token_id]); + } } + SuiBridgeEvent::UpdateTokenPriceEvent(_) => (), } } @@ -244,8 +266,10 @@ async fn get_latest_bridge_pause_status_with_emergency_event( #[cfg(test)] mod tests { + use std::str::FromStr; + use super::*; - use crate::events::init_all_struct_tags; + use crate::events::{init_all_struct_tags, NewTokenEvent}; use crate::test_utils::{ bridge_committee_to_bridge_committee_summary, get_test_authority_and_key, }; @@ -706,8 +730,16 @@ mod tests { let agg = Arc::new(ArcSwap::new(Arc::new(BridgeAuthorityAggregator::new( Arc::new(old_committee), )))); + let sui_token_type_tags = Arc::new(ArcSwap::from(Arc::new(HashMap::new()))); let _handle = tokio::task::spawn( - BridgeMonitor::new(sui_client.clone(), monitor_rx, agg.clone(), bridge_pause_tx).run(), + BridgeMonitor::new( + sui_client.clone(), + monitor_rx, + agg.clone(), + bridge_pause_tx, + sui_token_type_tags, + ) + .run(), ); let new_url = "http://new.url".to_string(); authorities[0].base_url = new_url.clone(); @@ -752,8 +784,16 @@ mod tests { let agg = Arc::new(ArcSwap::new(Arc::new(BridgeAuthorityAggregator::new( Arc::new(old_committee), )))); + let sui_token_type_tags = Arc::new(ArcSwap::from(Arc::new(HashMap::new()))); let _handle = tokio::task::spawn( - BridgeMonitor::new(sui_client.clone(), monitor_rx, agg.clone(), bridge_pause_tx).run(), + BridgeMonitor::new( + sui_client.clone(), + monitor_rx, + agg.clone(), + bridge_pause_tx, + sui_token_type_tags, + ) + .run(), ); authorities[0].is_blocklisted = true; let to_blocklist = &authorities[0]; @@ -799,8 +839,16 @@ mod tests { let agg = Arc::new(ArcSwap::new(Arc::new(BridgeAuthorityAggregator::new( Arc::new(committee), )))); + let sui_token_type_tags = Arc::new(ArcSwap::from(Arc::new(HashMap::new()))); let _handle = tokio::task::spawn( - BridgeMonitor::new(sui_client.clone(), monitor_rx, agg.clone(), bridge_pause_tx).run(), + BridgeMonitor::new( + sui_client.clone(), + monitor_rx, + agg.clone(), + bridge_pause_tx, + sui_token_type_tags, + ) + .run(), ); sui_client_mock.set_is_bridge_paused(event.frozen); @@ -814,6 +862,55 @@ mod tests { assert!(*bridge_pause_rx.borrow() == event.frozen); } + #[tokio::test] + async fn test_update_sui_token_type_tags() { + let ( + monitor_tx, + monitor_rx, + _sui_client_mock, + sui_client, + bridge_pause_tx, + _bridge_pause_rx, + authorities, + ) = setup(); + let event = NewTokenEvent { + token_id: 255, + type_name: TypeTag::from_str("0xbeef::beef::BEEF").unwrap(), + native_token: false, + decimal_multiplier: 1000000, + notional_value: 100000000, + }; + let committee = BridgeCommittee::new(authorities.clone()).unwrap(); + let agg = Arc::new(ArcSwap::new(Arc::new(BridgeAuthorityAggregator::new( + Arc::new(committee), + )))); + let sui_token_type_tags = Arc::new(ArcSwap::from(Arc::new(HashMap::new()))); + let sui_token_type_tags_clone = sui_token_type_tags.clone(); + let _handle = tokio::task::spawn( + BridgeMonitor::new( + sui_client.clone(), + monitor_rx, + agg.clone(), + bridge_pause_tx, + sui_token_type_tags_clone, + ) + .run(), + ); + + monitor_tx + .send(SuiBridgeEvent::NewTokenEvent(event.clone())) + .await + .unwrap(); + // Wait for the monitor to process the event + tokio::time::sleep(Duration::from_secs(1)).await; + // Now expect new token type tags to appear in sui_token_type_tags + sui_token_type_tags + .load() + .clone() + .get(&event.token_id) + .unwrap(); + } + #[allow(clippy::type_complexity)] fn setup() -> ( mysten_metrics::metered_channel::Sender, diff --git a/crates/sui-bridge/src/node.rs b/crates/sui-bridge/src/node.rs index d1331abf2cd7e..031152ce2a776 100644 --- a/crates/sui-bridge/src/node.rs +++ b/crates/sui-bridge/src/node.rs @@ -114,7 +114,6 @@ async fn start_client_components( let sui_token_type_tags = sui_client.get_token_id_map().await.unwrap(); let is_bridge_paused = sui_client.is_bridge_paused().await.unwrap(); - let (token_type_tags_tx, token_type_tags_rx) = tokio::sync::watch::channel(sui_token_type_tags); let (bridge_pause_tx, bridge_pause_rx) = tokio::sync::watch::channel(is_bridge_paused); let (monitor_tx, monitor_rx) = mysten_metrics::metered_channel::channel( @@ -124,7 +123,7 @@ async fn start_client_components( .channel_inflight .with_label_values(&["monitor_queue"]), ); - + let sui_token_type_tags = Arc::new(ArcSwap::from(Arc::new(sui_token_type_tags))); let bridge_action_executor = BridgeActionExecutor::new( sui_client.clone(), bridge_auth_agg.clone(), @@ -132,7 +131,7 @@ async fn start_client_components( client_config.key, client_config.sui_address, client_config.gas_object_ref.0, - token_type_tags_rx, + sui_token_type_tags.clone(), bridge_pause_rx, metrics.clone(), ) @@ -143,6 +142,7 @@ async fn start_client_components( monitor_rx, bridge_auth_agg.clone(), bridge_pause_tx, + sui_token_type_tags, ); all_handles.push(spawn_logged_monitored_task!(monitor.run())); @@ -151,7 +151,6 @@ async fn start_client_components( sui_events_rx, eth_events_rx, store.clone(), - token_type_tags_tx, monitor_tx, metrics, ); diff --git a/crates/sui-bridge/src/orchestrator.rs b/crates/sui-bridge/src/orchestrator.rs index db437bb509017..247f5b13ea425 100644 --- a/crates/sui-bridge/src/orchestrator.rs +++ b/crates/sui-bridge/src/orchestrator.rs @@ -18,10 +18,9 @@ use crate::sui_client::{SuiClient, SuiClientInner}; use crate::types::EthLog; use ethers::types::Address as EthAddress; use mysten_metrics::spawn_logged_monitored_task; -use std::collections::HashMap; use std::sync::Arc; use sui_json_rpc_types::SuiEvent; -use sui_types::{Identifier, TypeTag}; +use sui_types::Identifier; use tokio::task::JoinHandle; use tracing::{error, info}; @@ -30,7 +29,6 @@ pub struct BridgeOrchestrator { sui_events_rx: mysten_metrics::metered_channel::Receiver<(Identifier, Vec)>, eth_events_rx: mysten_metrics::metered_channel::Receiver<(EthAddress, u64, Vec)>, store: Arc, - token_type_tags_tx: tokio::sync::watch::Sender>, monitor_tx: mysten_metrics::metered_channel::Sender, metrics: Arc, } @@ -44,7 +42,6 @@ where sui_events_rx: mysten_metrics::metered_channel::Receiver<(Identifier, Vec)>, eth_events_rx: mysten_metrics::metered_channel::Receiver<(EthAddress, u64, Vec)>, store: Arc, - token_type_tags_tx: tokio::sync::watch::Sender>, monitor_tx: mysten_metrics::metered_channel::Sender, metrics: Arc, ) -> Self { @@ -53,7 +50,6 @@ where sui_events_rx, eth_events_rx, store, - token_type_tags_tx, monitor_tx, metrics, } @@ -76,7 +72,6 @@ where store_clone, executor_sender_clone, self.sui_events_rx, - self.token_type_tags_tx, self.monitor_tx, metrics_clone, ))); @@ -101,7 +96,6 @@ where metrics_clone, ))); - // TODO: spawn bridge committee change watcher task task_handles } @@ -109,12 +103,10 @@ where store: Arc, executor_tx: mysten_metrics::metered_channel::Sender, mut sui_events_rx: mysten_metrics::metered_channel::Receiver<(Identifier, Vec)>, - token_type_tags_tx: tokio::sync::watch::Sender>, monitor_tx: mysten_metrics::metered_channel::Sender, metrics: Arc, ) { info!("Starting sui watcher task"); - let mut latest_token_config = token_type_tags_tx.borrow().clone(); while let Some((identifier, events)) = sui_events_rx.recv().await { if events.is_empty() { continue; @@ -161,30 +153,11 @@ where .await .expect("Sending event to monitor channel should not fail"); - // Handle NewTokenEvent - // TODO: broadcast this event and let the downstream services handle it - if let SuiBridgeEvent::NewTokenEvent(e) = &bridge_event { - if let std::collections::hash_map::Entry::Vacant(entry) = - latest_token_config.entry(e.token_id) - { - entry.insert(e.type_name.clone()); - token_type_tags_tx - .send(latest_token_config.clone()) - .expect("Sending token type tag should not fail"); - } else { - // invariant - assert_eq!(e.type_name, latest_token_config[&e.token_id]); - } - continue; - } - if let Some(action) = bridge_event .try_into_bridge_action(sui_event.id.tx_digest, sui_event.id.event_seq as u16) { actions.push(action); } - - // TODO: handle non Action events } if !actions.is_empty() { @@ -290,16 +263,12 @@ where #[cfg(test)] mod tests { use crate::{ - events::NewTokenEvent, test_utils::{get_test_eth_to_sui_bridge_action, get_test_log_and_action}, types::BridgeActionDigest, }; use ethers::types::{Address as EthAddress, TxHash}; - use maplit::hashmap; use prometheus::Registry; use std::str::FromStr; - use sui_types::digests::TransactionDigest; - use tokio::time::Duration; use super::*; use crate::events::init_all_struct_tags; @@ -322,7 +291,6 @@ mod tests { store, ) = setup(); let (executor, mut executor_requested_action_rx) = MockExecutor::new(); - let (token_type_tags_tx, _token_type_tags_rx) = tokio::sync::watch::channel(HashMap::new()); // start orchestrator let registry = Registry::new(); let metrics = Arc::new(BridgeMetrics::new(®istry)); @@ -331,7 +299,6 @@ mod tests { sui_events_rx, eth_events_rx, store.clone(), - token_type_tags_tx, monitor_tx, metrics, ) @@ -371,77 +338,6 @@ mod tests { } } - #[tokio::test] - async fn test_sui_watcher_task_add_new_token() { - let ( - sui_events_tx, - sui_events_rx, - _eth_events_tx, - eth_events_rx, - monitor_tx, - _monitor_rx, - sui_client, - store, - ) = setup(); - - let (executor, _executor_requested_action_rx) = MockExecutor::new(); - let (token_type_tags_tx, mut token_type_tags_rx) = - tokio::sync::watch::channel(HashMap::new()); - // start orchestrator - let registry = Registry::new(); - let metrics = Arc::new(BridgeMetrics::new(®istry)); - let _handles = BridgeOrchestrator::new( - Arc::new(sui_client), - sui_events_rx, - eth_events_rx, - store.clone(), - token_type_tags_tx, - monitor_tx, - metrics, - ) - .run(executor) - .await; - - let identifier = Identifier::from_str("test_sui_watcher_task_add_new_token").unwrap(); - let type_tag = TypeTag::from_str("0xbeef::beef::BEEF").unwrap(); - let emitted_event = crate::events::MoveNewTokenEvent { - token_id: 255, - type_name: type_tag.to_canonical_string(false), - native_token: false, - decimal_multiplier: 1000000, - notional_value: 100000000, - }; - - let sui_event = SuiEvent { - type_: NewTokenEvent.get().unwrap().clone(), - bcs: bcs::to_bytes(&emitted_event).unwrap(), - id: sui_types::event::EventID { - tx_digest: TransactionDigest::random(), - event_seq: 1, - }, - - // The following fields do not matter as of writing, - // but if tests start to fail, it's worth checking these fields. - package_id: sui_types::base_types::ObjectID::ZERO, - transaction_module: identifier.clone(), - sender: sui_types::base_types::SuiAddress::random_for_testing_only(), - parsed_json: serde_json::json!({"test": "test"}), - timestamp_ms: None, - }; - sui_events_tx - .send((identifier.clone(), vec![sui_event.clone()])) - .await - .unwrap(); - - tokio::time::timeout(Duration::from_secs(3), token_type_tags_rx.changed()) - .await - .unwrap() - .unwrap(); - - let res = token_type_tags_rx.borrow().clone(); - assert_eq!(res, hashmap! {255u8 => type_tag}); - } - #[tokio::test] async fn test_eth_watcher_task() { // Note: this test may fail beacuse of the following reasons: @@ -458,7 +354,6 @@ mod tests { sui_client, store, ) = setup(); - let (token_type_tags_tx, _token_type_tags_rx) = tokio::sync::watch::channel(HashMap::new()); let (executor, mut executor_requested_action_rx) = MockExecutor::new(); // start orchestrator let registry = Registry::new(); @@ -468,7 +363,6 @@ mod tests { sui_events_rx, eth_events_rx, store.clone(), - token_type_tags_tx, monitor_tx, metrics, ) @@ -531,7 +425,6 @@ mod tests { store, ) = setup(); let (executor, mut executor_requested_action_rx) = MockExecutor::new(); - let (token_type_tags_tx, _token_type_tags_rx) = tokio::sync::watch::channel(HashMap::new()); let action1 = get_test_sui_to_eth_bridge_action( None, @@ -543,7 +436,7 @@ mod tests { None, ); - let action2 = get_test_eth_to_sui_bridge_action(None, None, None); + let action2 = get_test_eth_to_sui_bridge_action(None, None, None, None); store .insert_pending_actions(&vec![action1.clone(), action2.clone()]) .unwrap(); @@ -556,7 +449,6 @@ mod tests { sui_events_rx, eth_events_rx, store.clone(), - token_type_tags_tx, monitor_tx, metrics, ) diff --git a/crates/sui-bridge/src/sui_client.rs b/crates/sui-bridge/src/sui_client.rs index 97586c3825c5e..56ce77050554c 100644 --- a/crates/sui-bridge/src/sui_client.rs +++ b/crates/sui-bridge/src/sui_client.rs @@ -791,7 +791,7 @@ mod tests { let id_token_map = sui_client.get_token_id_map().await.unwrap(); // 1. Create a Eth -> Sui Transfer (recipient is sender address), approve with validator secrets and assert its status to be Claimed - let action = get_test_eth_to_sui_bridge_action(None, Some(usdc_amount), Some(sender)); + let action = get_test_eth_to_sui_bridge_action(None, Some(usdc_amount), Some(sender), None); let usdc_object_ref = approve_action_with_validator_secrets( context, bridge_object_arg, diff --git a/crates/sui-bridge/src/sui_transaction_builder.rs b/crates/sui-bridge/src/sui_transaction_builder.rs index 3b1aab01b6f3d..8eb640b4f50b9 100644 --- a/crates/sui-bridge/src/sui_transaction_builder.rs +++ b/crates/sui-bridge/src/sui_transaction_builder.rs @@ -669,7 +669,7 @@ mod tests { let id_token_map = sui_client.get_token_id_map().await.unwrap(); // 1. Test Eth -> Sui Transfer approval - let action = get_test_eth_to_sui_bridge_action(None, Some(usdc_amount), Some(sender)); + let action = get_test_eth_to_sui_bridge_action(None, Some(usdc_amount), Some(sender), None); // `approve_action_with_validator_secrets` covers transaction building let usdc_object_ref = approve_action_with_validator_secrets( context, diff --git a/crates/sui-bridge/src/test_utils.rs b/crates/sui-bridge/src/test_utils.rs index 8b71664908777..1449b87547268 100644 --- a/crates/sui-bridge/src/test_utils.rs +++ b/crates/sui-bridge/src/test_utils.rs @@ -103,6 +103,7 @@ pub fn get_test_eth_to_sui_bridge_action( nonce: Option, amount: Option, sui_address: Option, + token_id: Option, ) -> BridgeAction { BridgeAction::EthToSuiBridgeAction(EthToSuiBridgeAction { eth_tx_hash: TxHash::random(), @@ -111,7 +112,7 @@ pub fn get_test_eth_to_sui_bridge_action( eth_chain_id: BridgeChainId::EthCustom, nonce: nonce.unwrap_or_default(), sui_chain_id: BridgeChainId::SuiCustom, - token_id: TOKEN_ID_USDC, + token_id: token_id.unwrap_or(TOKEN_ID_USDC), sui_adjusted_amount: amount.unwrap_or(100_000), sui_address: sui_address.unwrap_or_else(SuiAddress::random_for_testing_only), eth_address: EthAddress::random(), diff --git a/crates/sui-bridge/src/types.rs b/crates/sui-bridge/src/types.rs index c6f024be7a306..e722a79c65eea 100644 --- a/crates/sui-bridge/src/types.rs +++ b/crates/sui-bridge/src/types.rs @@ -664,7 +664,7 @@ mod tests { let action = get_test_sui_to_eth_bridge_action(None, None, None, None, None, None, None); assert_eq!(action.approval_threshold(), 3334); - let action = get_test_eth_to_sui_bridge_action(None, None, None); + let action = get_test_eth_to_sui_bridge_action(None, None, None, None); assert_eq!(action.approval_threshold(), 3334); let action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction { From de9d14fa86b1c8c2b450458e5f4aad37fd00b362 Mon Sep 17 00:00:00 2001 From: Lu Zhang <8418040+longbowlu@users.noreply.github.com> Date: Tue, 30 Jul 2024 12:54:59 -0700 Subject: [PATCH 034/232] introduce MeteredEthHttpProvider that meters rpc usage (#18833) ## Description `MeteredEthHttpProvider` counts every eth rpc query and its latency. This will be useful to track the eth rpc usage. ## Test plan unit tests --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-bridge-indexer/src/eth_worker.rs | 13 +-- .../src/latest_eth_syncer.rs | 9 +- crates/sui-bridge-indexer/src/main.rs | 3 +- crates/sui-bridge/src/config.rs | 12 ++- crates/sui-bridge/src/eth_client.rs | 20 ++-- crates/sui-bridge/src/lib.rs | 1 + crates/sui-bridge/src/metered_eth_provider.rs | 102 ++++++++++++++++++ crates/sui-bridge/src/metrics.rs | 35 ++++-- 8 files changed, 158 insertions(+), 37 deletions(-) create mode 100644 crates/sui-bridge/src/metered_eth_provider.rs diff --git a/crates/sui-bridge-indexer/src/eth_worker.rs b/crates/sui-bridge-indexer/src/eth_worker.rs index d07327ff238da..797e3ebcdd068 100644 --- a/crates/sui-bridge-indexer/src/eth_worker.rs +++ b/crates/sui-bridge-indexer/src/eth_worker.rs @@ -10,14 +10,15 @@ use crate::{ BridgeDataSource, ProcessedTxnData, TokenTransfer, TokenTransferData, TokenTransferStatus, }; use anyhow::{anyhow, Result}; +use ethers::providers::Middleware; use ethers::providers::Provider; -use ethers::providers::{Http, Middleware}; use ethers::types::Address as EthAddress; use mysten_metrics::spawn_logged_monitored_task; use std::collections::HashMap; use std::str::FromStr; use std::sync::Arc; use sui_bridge::abi::{EthBridgeEvent, EthSuiBridgeEvents}; +use sui_bridge::metered_eth_provider::{new_metered_eth_provider, MeteredEthHttpProvier}; use sui_bridge::metrics::BridgeMetrics; use sui_bridge::types::EthEvent; use sui_bridge::{eth_client::EthClient, eth_syncer::EthSyncer}; @@ -27,7 +28,7 @@ use tracing::log::error; #[derive(Clone)] pub struct EthBridgeWorker { - provider: Arc>, + provider: Arc>, pg_pool: PgPool, bridge_metrics: Arc, metrics: BridgeIndexerMetrics, @@ -45,7 +46,7 @@ impl EthBridgeWorker { let bridge_address = EthAddress::from_str(&config.eth_sui_bridge_contract_address)?; let provider = Arc::new( - Provider::::try_from(&config.eth_rpc_url)? + new_metered_eth_provider(&config.eth_rpc_url, bridge_metrics.clone())? .interval(std::time::Duration::from_millis(2000)), ); @@ -61,7 +62,7 @@ impl EthBridgeWorker { pub async fn start_indexing_finalized_events( &self, - eth_client: Arc>, + eth_client: Arc>, ) -> Result> { let newest_finalized_block = match get_latest_eth_token_transfer(&self.pg_pool, true)? { Some(transfer) => transfer.block_height as u64, @@ -97,7 +98,7 @@ impl EthBridgeWorker { pub async fn start_indexing_unfinalized_events( &self, - eth_client: Arc>, + eth_client: Arc>, ) -> Result> { let newest_unfinalized_block_recorded = match get_latest_eth_token_transfer(&self.pg_pool, false)? { @@ -146,7 +147,7 @@ impl EthBridgeWorker { } async fn process_eth_events( - provider: Arc>, + provider: Arc>, pg_pool: PgPool, metrics: BridgeIndexerMetrics, mut eth_events_rx: mysten_metrics::metered_channel::Receiver<(EthAddress, u64, Vec)>, diff --git a/crates/sui-bridge-indexer/src/latest_eth_syncer.rs b/crates/sui-bridge-indexer/src/latest_eth_syncer.rs index 150353969f736..642eed859c34a 100644 --- a/crates/sui-bridge-indexer/src/latest_eth_syncer.rs +++ b/crates/sui-bridge-indexer/src/latest_eth_syncer.rs @@ -6,7 +6,7 @@ //! only query from that block number onwards. The syncer also keeps track of the last //! block on Ethereum and will only query for events up to that block number. -use ethers::providers::{Http, JsonRpcClient, Middleware, Provider}; +use ethers::providers::{JsonRpcClient, Middleware, Provider}; use ethers::types::Address as EthAddress; use mysten_metrics::metered_channel::{channel, Receiver, Sender}; use mysten_metrics::spawn_logged_monitored_task; @@ -16,6 +16,7 @@ use std::sync::Arc; use std::time::Instant; use sui_bridge::error::BridgeResult; use sui_bridge::eth_client::EthClient; +use sui_bridge::metered_eth_provider::MeteredEthHttpProvier; use sui_bridge::retry_with_max_elapsed_time; use sui_bridge::types::RawEthLog; use tokio::task::JoinHandle; @@ -30,7 +31,7 @@ const BLOCK_QUERY_INTERVAL: Duration = Duration::from_secs(2); pub struct LatestEthSyncer

{ eth_client: Arc>, - provider: Arc>, + provider: Arc>, contract_addresses: EthTargetAddresses, } @@ -44,7 +45,7 @@ where { pub fn new( eth_client: Arc>, - provider: Arc>, + provider: Arc>, contract_addresses: EthTargetAddresses, ) -> Self { Self { @@ -92,7 +93,7 @@ where async fn run_event_listening_task( contract_address: EthAddress, mut start_block: u64, - provider: Arc>, + provider: Arc>, events_sender: Sender<(EthAddress, u64, Vec)>, eth_client: Arc>, metrics: BridgeIndexerMetrics, diff --git a/crates/sui-bridge-indexer/src/main.rs b/crates/sui-bridge-indexer/src/main.rs index ae873496f0d9e..6685fed04dca7 100644 --- a/crates/sui-bridge-indexer/src/main.rs +++ b/crates/sui-bridge-indexer/src/main.rs @@ -10,6 +10,7 @@ use std::env; use std::path::PathBuf; use std::sync::Arc; use sui_bridge::eth_client::EthClient; +use sui_bridge::metered_eth_provider::MeteredEthHttpProvier; use sui_bridge::metrics::BridgeMetrics; use sui_bridge_indexer::eth_worker::EthBridgeWorker; use sui_bridge_indexer::metrics::BridgeIndexerMetrics; @@ -82,7 +83,7 @@ async fn main() -> Result<()> { )?; let eth_client = Arc::new( - EthClient::::new( + EthClient::::new( &config.eth_rpc_url, HashSet::from_iter(vec![eth_worker.bridge_address()]), bridge_metrics.clone(), diff --git a/crates/sui-bridge/src/config.rs b/crates/sui-bridge/src/config.rs index 57d738bf56ddf..f2f982d3503ef 100644 --- a/crates/sui-bridge/src/config.rs +++ b/crates/sui-bridge/src/config.rs @@ -5,6 +5,8 @@ use crate::abi::EthBridgeConfig; use crate::crypto::BridgeAuthorityKeyPair; use crate::error::BridgeError; use crate::eth_client::EthClient; +use crate::metered_eth_provider::new_metered_eth_provider; +use crate::metered_eth_provider::MeteredEthHttpProvier; use crate::metrics::BridgeMetrics; use crate::sui_client::SuiClient; use crate::types::{is_route_valid, BridgeAction}; @@ -221,10 +223,10 @@ impl BridgeNodeConfig { async fn prepare_for_eth( &self, metrics: Arc, - ) -> anyhow::Result<(Arc>, Vec)> { + ) -> anyhow::Result<(Arc>, Vec)> { let bridge_proxy_address = EthAddress::from_str(&self.eth.eth_bridge_proxy_address)?; let provider = Arc::new( - ethers::prelude::Provider::::try_from(&self.eth.eth_rpc_url) + new_metered_eth_provider(&self.eth.eth_rpc_url, metrics.clone()) .unwrap() .interval(std::time::Duration::from_millis(2000)), ); @@ -268,7 +270,7 @@ impl BridgeNodeConfig { ); let eth_client = Arc::new( - EthClient::::new( + EthClient::::new( &self.eth.eth_rpc_url, HashSet::from_iter(vec![ bridge_proxy_address, @@ -366,7 +368,7 @@ pub struct BridgeServerConfig { pub server_listen_port: u16, pub metrics_port: u16, pub sui_client: Arc>, - pub eth_client: Arc>, + pub eth_client: Arc>, /// A list of approved governance actions. Action in this list will be signed when requested by client. pub approved_governance_actions: Vec, } @@ -378,7 +380,7 @@ pub struct BridgeClientConfig { pub gas_object_ref: ObjectRef, pub metrics_port: u16, pub sui_client: Arc>, - pub eth_client: Arc>, + pub eth_client: Arc>, pub db_path: PathBuf, pub eth_contracts: Vec, // See `BridgeNodeConfig` for the explanation of following two fields. diff --git a/crates/sui-bridge/src/eth_client.rs b/crates/sui-bridge/src/eth_client.rs index f7bed3c4d2e2e..df48b3cd88b4a 100644 --- a/crates/sui-bridge/src/eth_client.rs +++ b/crates/sui-bridge/src/eth_client.rs @@ -6,12 +6,13 @@ use std::sync::Arc; use crate::abi::EthBridgeEvent; use crate::error::{BridgeError, BridgeResult}; +use crate::metered_eth_provider::{new_metered_eth_provider, MeteredEthHttpProvier}; use crate::metrics::BridgeMetrics; use crate::types::{BridgeAction, EthLog, RawEthLog}; -use ethers::providers::{Http, JsonRpcClient, Middleware, Provider}; +use ethers::providers::{JsonRpcClient, Middleware, Provider}; use ethers::types::TxHash; use ethers::types::{Block, Filter}; -use tap::{Tap, TapFallible}; +use tap::TapFallible; #[cfg(test)] use crate::eth_mock_provider::EthMockProvider; @@ -19,20 +20,18 @@ use ethers::types::Address as EthAddress; pub struct EthClient

{ provider: Provider

, contract_addresses: HashSet, - metrics: Arc, } -impl EthClient { +impl EthClient { pub async fn new( provider_url: &str, contract_addresses: HashSet, metrics: Arc, ) -> anyhow::Result { - let provider = Provider::try_from(provider_url)?; + let provider = new_metered_eth_provider(provider_url, metrics)?; let self_ = Self { provider, contract_addresses, - metrics, }; self_.describe().await?; Ok(self_) @@ -46,7 +45,6 @@ impl EthClient { Self { provider, contract_addresses, - metrics: Arc::new(BridgeMetrics::new_for_testing()), } } } @@ -77,7 +75,6 @@ where .provider .get_transaction_receipt(tx_hash) .await - .tap(|_| self.metrics.eth_provider_queries.inc()) .map_err(BridgeError::from)? .ok_or(BridgeError::TxNotFound)?; let receipt_block_num = receipt.block_number.ok_or(BridgeError::ProviderError( @@ -115,8 +112,7 @@ where let block: Result>, ethers::prelude::ProviderError> = self.provider .request("eth_getBlockByNumber", ("finalized", false)) - .await - .tap(|_| self.metrics.eth_provider_queries.inc()); + .await; let block = block?.ok_or(BridgeError::TransientProviderError( "Provider fails to return last finalized block".into(), ))?; @@ -140,9 +136,9 @@ where .address(address); let logs = self .provider + // TODO use get_logs_paginated? .get_logs(&filter) .await - .tap(|_| self.metrics.eth_provider_queries.inc()) .map_err(BridgeError::from) .tap_err(|e| { tracing::error!( @@ -193,7 +189,6 @@ where .provider .get_logs(&filter) .await - .tap(|_| self.metrics.eth_provider_queries.inc()) .map_err(BridgeError::from) .tap_err(|e| { tracing::error!( @@ -241,7 +236,6 @@ where .provider .get_transaction_receipt(tx_hash) .await - .tap(|_| self.metrics.eth_provider_queries.inc()) .map_err(BridgeError::from)? .ok_or(BridgeError::ProviderError(format!( "Provide cannot find eth transaction for log: {:?})", diff --git a/crates/sui-bridge/src/lib.rs b/crates/sui-bridge/src/lib.rs index 71013b2c8d046..0a138372c50fe 100644 --- a/crates/sui-bridge/src/lib.rs +++ b/crates/sui-bridge/src/lib.rs @@ -12,6 +12,7 @@ pub mod eth_client; pub mod eth_syncer; pub mod eth_transaction_builder; pub mod events; +pub mod metered_eth_provider; pub mod metrics; pub mod monitor; pub mod node; diff --git a/crates/sui-bridge/src/metered_eth_provider.rs b/crates/sui-bridge/src/metered_eth_provider.rs new file mode 100644 index 0000000000000..5e4ccac1b6ece --- /dev/null +++ b/crates/sui-bridge/src/metered_eth_provider.rs @@ -0,0 +1,102 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::metrics::BridgeMetrics; +use ethers::providers::{Http, HttpClientError, JsonRpcClient, Provider}; +use serde::{de::DeserializeOwned, Serialize}; +use std::fmt::Debug; +use std::sync::Arc; +use url::{ParseError, Url}; + +#[derive(Debug, Clone)] +pub struct MeteredEthHttpProvier { + inner: Http, + metrics: Arc, +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] +impl JsonRpcClient for MeteredEthHttpProvier { + type Error = HttpClientError; + + async fn request( + &self, + method: &str, + params: T, + ) -> Result { + self.metrics + .eth_rpc_queries + .with_label_values(&[method]) + .inc(); + let _guard = self + .metrics + .eth_rpc_queries_latency + .with_label_values(&[method]) + .start_timer(); + self.inner.request(method, params).await + } +} + +impl MeteredEthHttpProvier { + pub fn new(url: impl Into, metrics: Arc) -> Self { + let inner = Http::new(url); + Self { inner, metrics } + } +} + +pub fn new_metered_eth_provider( + url: &str, + metrics: Arc, +) -> Result, ParseError> { + let http_provider = MeteredEthHttpProvier::new(Url::parse(url)?, metrics); + Ok(Provider::new(http_provider)) +} + +#[cfg(test)] +mod tests { + use super::*; + use ethers::providers::Middleware; + use prometheus::Registry; + + #[tokio::test] + async fn test_metered_eth_provider() { + let metrics = Arc::new(BridgeMetrics::new(&Registry::new())); + let provider = new_metered_eth_provider("http://localhost:9876", metrics.clone()).unwrap(); + + assert_eq!( + metrics + .eth_rpc_queries + .get_metric_with_label_values(&["eth_blockNumber"]) + .unwrap() + .get(), + 0 + ); + assert_eq!( + metrics + .eth_rpc_queries_latency + .get_metric_with_label_values(&["eth_blockNumber"]) + .unwrap() + .get_sample_count(), + 0 + ); + + provider.get_block_number().await.unwrap_err(); // the rpc cal will fail but we don't care + + assert_eq!( + metrics + .eth_rpc_queries + .get_metric_with_label_values(&["eth_blockNumber"]) + .unwrap() + .get(), + 1 + ); + assert_eq!( + metrics + .eth_rpc_queries_latency + .get_metric_with_label_values(&["eth_blockNumber"]) + .unwrap() + .get_sample_count(), + 1 + ); + } +} diff --git a/crates/sui-bridge/src/metrics.rs b/crates/sui-bridge/src/metrics.rs index a4999744e8d14..3f07ed87a9168 100644 --- a/crates/sui-bridge/src/metrics.rs +++ b/crates/sui-bridge/src/metrics.rs @@ -2,12 +2,20 @@ // SPDX-License-Identifier: Apache-2.0 use prometheus::{ - register_int_counter_vec_with_registry, register_int_counter_with_registry, - register_int_gauge_vec_with_registry, register_int_gauge_with_registry, IntCounter, - IntCounterVec, IntGauge, IntGaugeVec, Registry, + register_histogram_vec_with_registry, register_int_counter_vec_with_registry, + register_int_counter_with_registry, register_int_gauge_vec_with_registry, + register_int_gauge_with_registry, HistogramVec, IntCounter, IntCounterVec, IntGauge, + IntGaugeVec, Registry, }; -#[derive(Clone)] +const FINE_GRAINED_LATENCY_SEC_BUCKETS: &[f64] = &[ + 0.001, 0.005, 0.01, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.6, 0.7, 0.8, 0.9, + 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.5, 3.0, 3.5, 4.0, 5.0, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, + 10., 15., 20., 25., 30., 35., 40., 45., 50., 60., 70., 80., 90., 100., 120., 140., 160., 180., + 200., 250., 300., 350., 400., +]; + +#[derive(Clone, Debug)] pub struct BridgeMetrics { pub(crate) err_build_sui_transaction: IntCounter, pub(crate) err_signature_aggregation: IntCounter, @@ -38,7 +46,9 @@ pub struct BridgeMetrics { pub(crate) signer_with_cache_hit: IntCounterVec, pub(crate) signer_with_cache_miss: IntCounterVec, - pub(crate) eth_provider_queries: IntCounter, + pub(crate) eth_rpc_queries: IntCounterVec, + pub(crate) eth_rpc_queries_latency: HistogramVec, + pub(crate) gas_coin_balance: IntGauge, } @@ -175,9 +185,18 @@ impl BridgeMetrics { registry, ) .unwrap(), - eth_provider_queries: register_int_counter_with_registry!( - "bridge_eth_provider_queries", - "Total number of queries issued to eth provider", + eth_rpc_queries: register_int_counter_vec_with_registry!( + "bridge_eth_rpc_queries", + "Total number of queries issued to eth provider, by request type", + &["type"], + registry, + ) + .unwrap(), + eth_rpc_queries_latency: register_histogram_vec_with_registry!( + "bridge_eth_rpc_queries_latency", + "Latency of queries issued to eth provider, by request type", + &["type"], + FINE_GRAINED_LATENCY_SEC_BUCKETS.to_vec(), registry, ) .unwrap(), From 3053ad99496f832d5a13ff51b33be55f3e359052 Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Tue, 30 Jul 2024 21:18:12 +0100 Subject: [PATCH 035/232] [main][GraphQL] Fix pruning compatibility issues (#18862) ## Description Two further fixes to get GraphQL working with pruned databases. Cherry-pick of #18860 **into main**. ## Test plan Tested by deploying this version of the service against a pruned DB, and ensuring the following query executed successfully: ```graphql query { chainIdentifier protocolConfig(protocolVersion: 10) { featureFlags { key value } } } ``` --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [x] GraphQL: Adds support for running GraphQL against a pruned version of the service. - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-graphql-rpc/src/types/protocol_config.rs | 7 +++---- crates/sui-indexer/src/schema/pg.rs | 3 --- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/crates/sui-graphql-rpc/src/types/protocol_config.rs b/crates/sui-graphql-rpc/src/types/protocol_config.rs index 9a10021d529cf..ce60c22892d39 100644 --- a/crates/sui-graphql-rpc/src/types/protocol_config.rs +++ b/crates/sui-graphql-rpc/src/types/protocol_config.rs @@ -3,7 +3,7 @@ use async_graphql::*; use diesel::{ExpressionMethods, QueryDsl}; -use sui_indexer::schema::{checkpoints, epochs}; +use sui_indexer::schema::{chain_identifier, epochs}; use sui_protocol_config::{ProtocolConfig as NativeProtocolConfig, ProtocolVersion}; use crate::{ @@ -90,7 +90,7 @@ impl ProtocolConfigs { impl ProtocolConfigs { pub(crate) async fn query(db: &Db, protocol_version: Option) -> Result { - use checkpoints::dsl as c; + use chain_identifier::dsl as c; use epochs::dsl as e; let (latest_version, digest_bytes): (i64, Option>) = db @@ -99,9 +99,8 @@ impl ProtocolConfigs { e::epochs .select(( e::protocol_version, - c::checkpoints + c::chain_identifier .select(c::checkpoint_digest) - .filter(c::sequence_number.eq(0)) .single_value(), )) .order_by(e::epoch.desc()) diff --git a/crates/sui-indexer/src/schema/pg.rs b/crates/sui-indexer/src/schema/pg.rs index 322ad8b6a030a..564f7cd721343 100644 --- a/crates/sui-indexer/src/schema/pg.rs +++ b/crates/sui-indexer/src/schema/pg.rs @@ -296,14 +296,11 @@ macro_rules! for_all_tables { display, epochs, events, - events_partition_0, objects, objects_history, - objects_history_partition_0, objects_snapshot, packages, transactions, - transactions_partition_0, tx_calls, tx_changed_objects, tx_digests, From 4419234c8a77a1e22b10b1de0e4c2ed1d8b435ed Mon Sep 17 00:00:00 2001 From: Tony Lee Date: Tue, 30 Jul 2024 23:04:21 +0100 Subject: [PATCH 036/232] Deepbook SDK (#18610) --- .changeset/little-students-train.md | 5 + examples/trading/api/sui-utils.ts | 10 +- pnpm-lock.yaml | 52 +- sdk/deepbook-v3/.gitignore | 132 ++++ sdk/deepbook-v3/.npmignore | 1 + sdk/deepbook-v3/.prettierignore | 9 + sdk/deepbook-v3/README.md | 1 + .../examples/deepbookMarketMaker.ts | 126 ++++ sdk/deepbook-v3/examples/tsconfig.json | 12 + sdk/deepbook-v3/examples/useDeepbookClient.ts | 47 ++ sdk/deepbook-v3/package.json | 53 ++ sdk/deepbook-v3/src/client.ts | 429 +++++++++++++ sdk/deepbook-v3/src/index.ts | 10 + .../src/transactions/balanceManager.ts | 182 ++++++ sdk/deepbook-v3/src/transactions/deepbook.ts | 587 ++++++++++++++++++ .../src/transactions/deepbookAdmin.ts | 140 +++++ .../src/transactions/flashLoans.ts | 121 ++++ .../src/transactions/governance.ts | 119 ++++ sdk/deepbook-v3/src/types/index.ts | 98 +++ sdk/deepbook-v3/src/utils/config.ts | 103 +++ sdk/deepbook-v3/src/utils/constants.ts | 106 ++++ sdk/deepbook-v3/tsconfig.esm.json | 7 + sdk/deepbook-v3/tsconfig.json | 11 + sdk/deepbook-v3/typedoc.json | 6 + sdk/deepbook-v3/vitest.config.ts | 18 + .../src/transactions/Transaction.ts | 7 + 26 files changed, 2386 insertions(+), 6 deletions(-) create mode 100644 .changeset/little-students-train.md create mode 100644 sdk/deepbook-v3/.gitignore create mode 100644 sdk/deepbook-v3/.npmignore create mode 100644 sdk/deepbook-v3/.prettierignore create mode 100644 sdk/deepbook-v3/README.md create mode 100644 sdk/deepbook-v3/examples/deepbookMarketMaker.ts create mode 100644 sdk/deepbook-v3/examples/tsconfig.json create mode 100644 sdk/deepbook-v3/examples/useDeepbookClient.ts create mode 100644 sdk/deepbook-v3/package.json create mode 100644 sdk/deepbook-v3/src/client.ts create mode 100644 sdk/deepbook-v3/src/index.ts create mode 100644 sdk/deepbook-v3/src/transactions/balanceManager.ts create mode 100644 sdk/deepbook-v3/src/transactions/deepbook.ts create mode 100644 sdk/deepbook-v3/src/transactions/deepbookAdmin.ts create mode 100644 sdk/deepbook-v3/src/transactions/flashLoans.ts create mode 100644 sdk/deepbook-v3/src/transactions/governance.ts create mode 100644 sdk/deepbook-v3/src/types/index.ts create mode 100644 sdk/deepbook-v3/src/utils/config.ts create mode 100644 sdk/deepbook-v3/src/utils/constants.ts create mode 100644 sdk/deepbook-v3/tsconfig.esm.json create mode 100644 sdk/deepbook-v3/tsconfig.json create mode 100644 sdk/deepbook-v3/typedoc.json create mode 100644 sdk/deepbook-v3/vitest.config.ts diff --git a/.changeset/little-students-train.md b/.changeset/little-students-train.md new file mode 100644 index 0000000000000..e385945bec383 --- /dev/null +++ b/.changeset/little-students-train.md @@ -0,0 +1,5 @@ +--- +'@mysten/sui': minor +--- + +Add setGasBudgetIfNotSet helper to Transaction class diff --git a/examples/trading/api/sui-utils.ts b/examples/trading/api/sui-utils.ts index 67ef63369398c..24b5bdcbc69e7 100644 --- a/examples/trading/api/sui-utils.ts +++ b/examples/trading/api/sui-utils.ts @@ -7,7 +7,7 @@ import { homedir } from 'os'; import path from 'path'; import { getFullnodeUrl, SuiClient } from '@mysten/sui/client'; import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519'; -import { TransactionBlock } from '@mysten/sui/transactions'; +import { Transaction } from '@mysten/sui/transactions'; import { fromB64 } from '@mysten/sui/utils'; export type Network = 'mainnet' | 'testnet' | 'devnet' | 'localnet'; @@ -49,12 +49,12 @@ export const getClient = (network: Network) => { }; /** A helper to sign & execute a transaction. */ -export const signAndExecute = async (txb: TransactionBlock, network: Network) => { +export const signAndExecute = async (txb: Transaction, network: Network) => { const client = getClient(network); const signer = getSigner(); - return client.signAndExecuteTransactionBlock({ - transactionBlock: txb, + return client.signAndExecuteTransaction({ + transaction: txb, signer, options: { showEffects: true, @@ -73,7 +73,7 @@ export const publishPackage = async ({ network: Network; exportFileName: string; }) => { - const txb = new TransactionBlock(); + const txb = new Transaction(); const { modules, dependencies } = JSON.parse( execSync(`${SUI_BIN} move build --dump-bytecode-as-base64 --path ${packagePath}`, { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 82044fbddcdd9..6fe93ff432deb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1320,6 +1320,43 @@ importers: specifier: ^7.2.0 version: 7.2.0 + sdk/deepbook-v3: + dependencies: + '@mysten/sui': + specifier: workspace:* + version: link:../typescript + devDependencies: + '@iarna/toml': + specifier: ^2.2.5 + version: 2.2.5 + '@mysten/build-scripts': + specifier: workspace:* + version: link:../build-scripts + '@types/node': + specifier: ^20.14.10 + version: 20.14.10 + tmp: + specifier: ^0.2.3 + version: 0.2.3 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@swc/core@1.6.13)(@types/node@20.14.10)(typescript@5.5.3) + ts-retry-promise: + specifier: ^0.8.1 + version: 0.8.1 + 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) + 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) + wait-on: + specifier: ^7.2.0 + version: 7.2.0 + sdk/docs: dependencies: '@mysten/bcs': @@ -2071,6 +2108,7 @@ packages: '@babel/plugin-proposal-class-properties@7.18.6': resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. peerDependencies: '@babel/core': ^7.0.0-0 @@ -2083,12 +2121,14 @@ packages: '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6': resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead. peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-numeric-separator@7.18.6': resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead. peerDependencies: '@babel/core': ^7.0.0-0 @@ -2102,12 +2142,14 @@ packages: '@babel/plugin-proposal-optional-chaining@7.21.0': resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead. peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-private-methods@7.18.6': resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead. peerDependencies: '@babel/core': ^7.0.0-0 @@ -5615,6 +5657,7 @@ packages: '@sentry/cli@1.74.6': resolution: {integrity: sha512-pJ7JJgozyjKZSTjOGi86chIngZMLUlYt2HOog+OJn+WGvqEkVymu8m462j1DiXAnex9NspB4zLLNuZ/R6rTQHg==} engines: {node: '>= 8'} + hasBin: true '@sentry/core@6.19.7': resolution: {integrity: sha512-tOfZ/umqB2AcHPGbIrsFLcvApdTm9ggpi/kQZFkej7kMphjT+SGBiQfYtjyg9jcRW+ilAR4JXC9BGKsdEQ+8Vw==} @@ -7226,6 +7269,7 @@ packages: are-we-there-yet@1.1.7: resolution: {integrity: sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==} + deprecated: This package is no longer supported. arg@1.0.0: resolution: {integrity: sha512-Wk7TEzl1KqvTGs/uyhmHO/3XLd3t1UeU4IstvPXVzGPM522cTjqjNZ99esCkcL52sjqjo8e8CTBcWhkxvGzoAw==} @@ -9499,6 +9543,7 @@ packages: gauge@2.7.4: resolution: {integrity: sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==} + deprecated: This package is no longer supported. gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} @@ -10057,6 +10102,7 @@ packages: inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -11466,6 +11512,7 @@ packages: npmlog@4.1.2: resolution: {integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==} + deprecated: This package is no longer supported. nth-check@2.0.1: resolution: {integrity: sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==} @@ -11564,6 +11611,7 @@ packages: onchange@7.1.0: resolution: {integrity: sha512-ZJcqsPiWUAUpvmnJri5TPBooqJOPmC0ttN65juhN15Q8xA+Nbg3BaxBHXQ45EistKKlKElb0edmbPWnKSBkvMg==} + hasBin: true onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} @@ -13080,6 +13128,7 @@ packages: sign-addon@5.3.0: resolution: {integrity: sha512-7nHlCzhQgVMLBNiXVEgbG/raq48awOW0lYMN5uo1BaB3mp0+k8M8pvDwbfTlr3apcxZJsk9HQsAW1POwoJugpQ==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -13422,6 +13471,7 @@ packages: svgo@3.0.2: resolution: {integrity: sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==} engines: {node: '>=14.0.0'} + hasBin: true swap-case@2.0.2: resolution: {integrity: sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw==} @@ -17476,7 +17526,7 @@ snapshots: dependencies: '@jridgewell/set-array': 1.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.22 + '@jridgewell/trace-mapping': 0.3.25 '@jridgewell/gen-mapping@0.3.5': dependencies: diff --git a/sdk/deepbook-v3/.gitignore b/sdk/deepbook-v3/.gitignore new file mode 100644 index 0000000000000..41ed5da947df8 --- /dev/null +++ b/sdk/deepbook-v3/.gitignore @@ -0,0 +1,132 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* +.DS_Store +.idea/ + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/sdk/deepbook-v3/.npmignore b/sdk/deepbook-v3/.npmignore new file mode 100644 index 0000000000000..d838da9865693 --- /dev/null +++ b/sdk/deepbook-v3/.npmignore @@ -0,0 +1 @@ +examples/ diff --git a/sdk/deepbook-v3/.prettierignore b/sdk/deepbook-v3/.prettierignore new file mode 100644 index 0000000000000..b0894a4b75616 --- /dev/null +++ b/sdk/deepbook-v3/.prettierignore @@ -0,0 +1,9 @@ +dist/ +package-lock.json +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.next/ +.swc/ +out/ +CHANGELOG.md diff --git a/sdk/deepbook-v3/README.md b/sdk/deepbook-v3/README.md new file mode 100644 index 0000000000000..c96f50141b529 --- /dev/null +++ b/sdk/deepbook-v3/README.md @@ -0,0 +1 @@ +# Deepbook TypeScript SDK diff --git a/sdk/deepbook-v3/examples/deepbookMarketMaker.ts b/sdk/deepbook-v3/examples/deepbookMarketMaker.ts new file mode 100644 index 0000000000000..5c463f792798e --- /dev/null +++ b/sdk/deepbook-v3/examples/deepbookMarketMaker.ts @@ -0,0 +1,126 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 +import { getFullnodeUrl, SuiClient } from '@mysten/sui/client'; +import { decodeSuiPrivateKey } from '@mysten/sui/cryptography'; +import type { Keypair } from '@mysten/sui/cryptography'; +import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519'; +import type { Transaction } from '@mysten/sui/transactions'; + +import { DeepBookClient } from '../src/index.js'; // Adjust path according to new structure +import type { BalanceManager } from '../src/types/index.js'; + +export class DeepBookMarketMaker extends DeepBookClient { + keypair: Keypair; + suiClient: SuiClient; + + constructor( + keypair: string | Keypair, + env: 'testnet' | 'mainnet', + balanceManagers?: { [key: string]: BalanceManager }, + adminCap?: string, + ) { + let resolvedKeypair: Keypair; + + if (typeof keypair === 'string') { + resolvedKeypair = DeepBookMarketMaker.#getSignerFromPK(keypair); + } else { + resolvedKeypair = keypair; + } + + const address = resolvedKeypair.toSuiAddress(); + + super({ + address: address, + env: env, + client: new SuiClient({ + url: getFullnodeUrl(env), + }), + balanceManagers: balanceManagers, + adminCap: adminCap, + }); + + this.keypair = resolvedKeypair; + this.suiClient = new SuiClient({ + url: getFullnodeUrl(env), + }); + } + + static #getSignerFromPK = (privateKey: string) => { + const { schema, secretKey } = decodeSuiPrivateKey(privateKey); + if (schema === 'ED25519') return Ed25519Keypair.fromSecretKey(secretKey); + + throw new Error(`Unsupported schema: ${schema}`); + }; + + signAndExecute = async (tx: Transaction) => { + return this.suiClient.signAndExecuteTransaction({ + transaction: tx, + signer: this.keypair, + options: { + showEffects: true, + showObjectChanges: true, + }, + }); + }; + + getActiveAddress() { + return this.keypair.getPublicKey().toSuiAddress(); + } + + // Example of a flash loan transaction + // Borrow 1 DEEP from DEEP_SUI pool + // Swap 0.5 DBUSDC for SUI in SUI_DBUSDC pool, pay with deep borrowed + // Swap SUI back to DEEP + // Return 1 DEEP to DEEP_SUI pool + flashLoanExample = async (tx: Transaction) => { + const borrowAmount = 1; + const [deepCoin, flashLoan] = tx.add(this.flashLoans.borrowBaseAsset('DEEP_SUI', borrowAmount)); + + // Execute trade using borrowed DEEP + const [baseOut, quoteOut, deepOut] = tx.add( + this.deepBook.swapExactQuoteForBase({ + poolKey: 'SUI_DBUSDC', + amount: 0.5, + deepAmount: 1, + minOut: 0, + deepCoin: deepCoin, + }), + ); + + tx.transferObjects([baseOut, quoteOut, deepOut], this.getActiveAddress()); + + // Execute second trade to get back DEEP for repayment + const [baseOut2, quoteOut2, deepOut2] = tx.add( + this.deepBook.swapExactQuoteForBase({ + poolKey: 'DEEP_SUI', + amount: 10, + deepAmount: 0, + minOut: 0, + }), + ); + + tx.transferObjects([quoteOut2, deepOut2], this.getActiveAddress()); + + // Return borrowed DEEP + const loanRemain = tx.add( + this.flashLoans.returnBaseAsset('DEEP_SUI', borrowAmount, baseOut2, flashLoan), + ); + tx.transferObjects([loanRemain], this.getActiveAddress()); + }; + + placeLimitOrderExample = (tx: Transaction) => { + tx.add( + this.deepBook.placeLimitOrder({ + poolKey: 'SUI_DBUSDC', + balanceManagerKey: 'MANAGER_1', + clientOrderId: '123456789', + price: 1, + quantity: 10, + isBid: true, + // orderType default: no restriction + // selfMatchingOption default: allow self matching + // payWithDeep default: true + }), + ); + }; +} diff --git a/sdk/deepbook-v3/examples/tsconfig.json b/sdk/deepbook-v3/examples/tsconfig.json new file mode 100644 index 0000000000000..6a6cfe178ea1a --- /dev/null +++ b/sdk/deepbook-v3/examples/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.esm.json", + "include": ["./**/*", "../src"], + "compilerOptions": { + "rootDir": "../..", + "noEmit": true, + "moduleResolution": "Node", + "emitDeclarationOnly": false, + "resolveJsonModule": true, + "module": "esnext" + } +} diff --git a/sdk/deepbook-v3/examples/useDeepbookClient.ts b/sdk/deepbook-v3/examples/useDeepbookClient.ts new file mode 100644 index 0000000000000..80198d1d52d36 --- /dev/null +++ b/sdk/deepbook-v3/examples/useDeepbookClient.ts @@ -0,0 +1,47 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 +import { Transaction } from '@mysten/sui/transactions'; +import { config } from 'dotenv'; + +import { DeepBookMarketMaker } from './deepbookMarketMaker.js'; + +// Load private key from .env file +config(); + +(async () => { + const privateKey = process.env.PRIVATE_KEY; + if (!privateKey) { + throw new Error('Private key not found'); + } + + // Initialize with balance managers if created + const balanceManagers = { + MANAGER_1: { + address: '0x6149bfe6808f0d6a9db1c766552b7ae1df477f5885493436214ed4228e842393', + tradeCap: '', + }, + }; + const mmClient = new DeepBookMarketMaker( + privateKey, + 'testnet', + balanceManagers, + process.env.ADMIN_CAP, + ); + + const tx = new Transaction(); + + // Read only call + console.log(await mmClient.checkManagerBalance('MANAGER_1', 'SUI')); + console.log(await mmClient.getLevel2Range('SUI_DBUSDC', 0.1, 100, true)); + + // // Balance manager contract call + // mmClient.balanceManager.depositIntoManager('MANAGER_1', 'SUI', 10)(tx); + + // // Example PTB call + // mmClient.placeLimitOrderExample(tx); + // mmClient.flashLoanExample(tx); + + let res = await mmClient.signAndExecute(tx); + + console.dir(res, { depth: null }); +})(); diff --git a/sdk/deepbook-v3/package.json b/sdk/deepbook-v3/package.json new file mode 100644 index 0000000000000..4f9e493a7db12 --- /dev/null +++ b/sdk/deepbook-v3/package.json @@ -0,0 +1,53 @@ +{ + "name": "@mysten/deepbook-v3", + "author": "Mysten Labs ", + "description": "Sui Deepbook SDK", + "version": "0.0.0", + "license": "Apache-2.0", + "type": "commonjs", + "main": "./dist/cjs/index.js", + "module": "./dist/esm/index.js", + "types": "./dist/cjs/index.d.ts", + "exports": { + ".": { + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js" + } + }, + "files": [ + "CHANGELOG.md", + "dist", + "src" + ], + "engines": { + "node": ">=18" + }, + "scripts": { + "clean": "rm -rf tsconfig.tsbuildinfo ./dist", + "build": "build-package", + "prepublishOnly": "pnpm build", + "prettier:check": "prettier -c --ignore-unknown .", + "prettier:fix": "prettier -w --ignore-unknown .", + "eslint:check": "eslint --max-warnings=0 .", + "eslint:fix": "pnpm run eslint:check --fix", + "lint": "pnpm run eslint:check && pnpm run prettier:check", + "lint:fix": "pnpm run eslint:fix && pnpm run prettier:fix", + "test:e2e": "wait-on http://127.0.0.1:9123 -l --timeout 120000 && vitest run e2e", + "prepare:e2e": "cargo build --bin sui --profile dev && cross-env RUST_LOG=warn,sui=error,anemo_tower=warn,consensus=off cargo run --bin sui -- start --with-faucet --force-regenesis" + }, + "dependencies": { + "@mysten/sui": "workspace:*" + }, + "devDependencies": { + "@iarna/toml": "^2.2.5", + "@mysten/build-scripts": "workspace:*", + "@types/node": "^20.14.10", + "tmp": "^0.2.3", + "ts-node": "^10.9.2", + "ts-retry-promise": "^0.8.1", + "typescript": "^5.5.3", + "vite": "^5.3.3", + "vitest": "^2.0.1", + "wait-on": "^7.2.0" + } +} diff --git a/sdk/deepbook-v3/src/client.ts b/sdk/deepbook-v3/src/client.ts new file mode 100644 index 0000000000000..5269f5b447a51 --- /dev/null +++ b/sdk/deepbook-v3/src/client.ts @@ -0,0 +1,429 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 +import { bcs } from '@mysten/sui/bcs'; +import type { SuiClient } from '@mysten/sui/client'; +import { Transaction } from '@mysten/sui/transactions'; +import { normalizeSuiAddress } from '@mysten/sui/utils'; + +import { BalanceManagerContract } from './transactions/balanceManager.js'; +import { DeepBookContract } from './transactions/deepbook.js'; +import { DeepBookAdminContract } from './transactions/deepbookAdmin.js'; +import { FlashLoanContract } from './transactions/flashLoans.js'; +import { GovernanceContract } from './transactions/governance.js'; +import type { BalanceManager, Environment } from './types/index.js'; +import { DEEP_SCALAR, DeepBookConfig, FLOAT_SCALAR } from './utils/config.js'; +import type { CoinMap, PoolMap } from './utils/constants.js'; + +/** + * DeepBookClient class for managing DeepBook operations. + */ +export class DeepBookClient { + client: SuiClient; + #config: DeepBookConfig; + #address: string; + balanceManager: BalanceManagerContract; + deepBook: DeepBookContract; + deepBookAdmin: DeepBookAdminContract; + flashLoans: FlashLoanContract; + governance: GovernanceContract; + + /** + * @param {SuiClient} client SuiClient instance + * @param {string} address Address of the client + * @param {Environment} env Environment configuration + * @param {Object.} [balanceManagers] Optional initial BalanceManager map + * @param {CoinMap} [coins] Optional initial CoinMap + * @param {PoolMap} [pools] Optional initial PoolMap + * @param {string} [adminCap] Optional admin capability + */ + constructor({ + client, + address, + env, + balanceManagers, + coins, + pools, + adminCap, + }: { + client: SuiClient; + address: string; + env: Environment; + balanceManagers?: { [key: string]: BalanceManager }; + coins?: CoinMap; + pools?: PoolMap; + adminCap?: string; + }) { + this.client = client; + this.#address = normalizeSuiAddress(address); + this.#config = new DeepBookConfig({ + address: this.#address, + env, + balanceManagers, + coins, + pools, + adminCap, + }); + this.balanceManager = new BalanceManagerContract(this.#config); + this.deepBook = new DeepBookContract(this.#config); + this.deepBookAdmin = new DeepBookAdminContract(this.#config); + this.flashLoans = new FlashLoanContract(this.#config); + this.governance = new GovernanceContract(this.#config); + } + + /** + * @description Check the balance of a balance manager for a specific coin + * @param {string} managerKey Key of the balance manager + * @param {string} coinKey Key of the coin + * @returns {Promise<{ coinType: string, balance: number }>} An object with coin type and balance + */ + async checkManagerBalance(managerKey: string, coinKey: string) { + const tx = new Transaction(); + const coin = this.#config.getCoin(coinKey); + + tx.add(this.balanceManager.checkManagerBalance(managerKey, coinKey)); + const res = await this.client.devInspectTransactionBlock({ + sender: this.#address, + transactionBlock: tx, + }); + + const bytes = res.results![0].returnValues![0][0]; + const parsed_balance = bcs.U64.parse(new Uint8Array(bytes)); + const balanceNumber = Number(parsed_balance); + const adjusted_balance = balanceNumber / coin.scalar; + + return { + coinType: coin.type, + balance: adjusted_balance, + }; + } + + /** + * @description Check if a pool is whitelisted + * @param {string} poolKey Key of the pool + * @returns {Promise} Boolean indicating if the pool is whitelisted + */ + async whitelisted(poolKey: string) { + const tx = new Transaction(); + + tx.add(this.deepBook.whitelisted(poolKey)); + const res = await this.client.devInspectTransactionBlock({ + sender: normalizeSuiAddress(this.#address), + transactionBlock: tx, + }); + + const bytes = res.results![0].returnValues![0][0]; + const whitelisted = bcs.Bool.parse(new Uint8Array(bytes)); + + return whitelisted; + } + + /** + * @description Get the quote quantity out for a given base quantity + * @param {string} poolKey Key of the pool + * @param {number} baseQuantity Base quantity to convert + * @returns {Promise<{ baseQuantity: number, baseOut: number, quoteOut: number, deepRequired: number }>} + * An object with base quantity, base out, quote out, and deep required for the dry run + */ + async getQuoteQuantityOut(poolKey: string, baseQuantity: number) { + const tx = new Transaction(); + const pool = this.#config.getPool(poolKey); + const baseScalar = this.#config.getCoin(pool.baseCoin).scalar; + const quoteScalar = this.#config.getCoin(pool.quoteCoin).scalar; + + tx.add(this.deepBook.getQuoteQuantityOut(poolKey, baseQuantity)); + const res = await this.client.devInspectTransactionBlock({ + sender: normalizeSuiAddress(this.#address), + transactionBlock: tx, + }); + + const baseOut = Number(bcs.U64.parse(new Uint8Array(res.results![0].returnValues![0][0]))); + const quoteOut = Number(bcs.U64.parse(new Uint8Array(res.results![0].returnValues![1][0]))); + const deepRequired = Number(bcs.U64.parse(new Uint8Array(res.results![0].returnValues![2][0]))); + + return { + baseQuantity, + baseOut: baseOut / baseScalar, + quoteOut: quoteOut / quoteScalar, + deepRequired: deepRequired / DEEP_SCALAR, + }; + } + + /** + * @description Get the base quantity out for a given quote quantity + * @param {string} poolKey Key of the pool + * @param {number} quoteQuantity Quote quantity to convert + * @returns {Promise<{ quoteQuantity: number, baseOut: number, quoteOut: number, deepRequired: number }>} + * An object with quote quantity, base out, quote out, and deep required for the dry run + */ + async getBaseQuantityOut(poolKey: string, quoteQuantity: number) { + const tx = new Transaction(); + const pool = this.#config.getPool(poolKey); + const baseScalar = this.#config.getCoin(pool.baseCoin).scalar; + const quoteScalar = this.#config.getCoin(pool.quoteCoin).scalar; + + tx.add(this.deepBook.getBaseQuantityOut(poolKey, quoteQuantity)); + const res = await this.client.devInspectTransactionBlock({ + sender: normalizeSuiAddress(this.#address), + transactionBlock: tx, + }); + + const baseOut = Number(bcs.U64.parse(new Uint8Array(res.results![0].returnValues![0][0]))); + const quoteOut = Number(bcs.U64.parse(new Uint8Array(res.results![0].returnValues![1][0]))); + const deepRequired = Number(bcs.U64.parse(new Uint8Array(res.results![0].returnValues![2][0]))); + + return { + quoteQuantity: quoteQuantity, + baseOut: baseOut / baseScalar, + quoteOut: quoteOut / quoteScalar, + deepRequired: deepRequired / DEEP_SCALAR, + }; + } + + /** + * @description Get the output quantities for given base and quote quantities. Only one quantity can be non-zero + * @param {string} poolKey Key of the pool + * @param {number} baseQuantity Base quantity to convert + * @param {number} quoteQuantity Quote quantity to convert + * @returns {Promise<{ baseQuantity: number, quoteQuantity: number, baseOut: number, quoteOut: number, deepRequired: number }>} + * An object with base quantity, quote quantity, base out, quote out, and deep required for the dry run + */ + async getQuantityOut(poolKey: string, baseQuantity: number, quoteQuantity: number) { + const tx = new Transaction(); + const pool = this.#config.getPool(poolKey); + const baseScalar = this.#config.getCoin(pool.baseCoin).scalar; + const quoteScalar = this.#config.getCoin(pool.quoteCoin).scalar; + + tx.add(this.deepBook.getQuantityOut(poolKey, baseQuantity, quoteQuantity)); + const res = await this.client.devInspectTransactionBlock({ + sender: normalizeSuiAddress(this.#address), + transactionBlock: tx, + }); + + const baseOut = Number(bcs.U64.parse(new Uint8Array(res.results![0].returnValues![0][0]))); + const quoteOut = Number(bcs.U64.parse(new Uint8Array(res.results![0].returnValues![1][0]))); + const deepRequired = Number(bcs.U64.parse(new Uint8Array(res.results![0].returnValues![2][0]))); + + return { + baseQuantity, + quoteQuantity, + baseOut: baseOut / baseScalar, + quoteOut: quoteOut / quoteScalar, + deepRequired: deepRequired / DEEP_SCALAR, + }; + } + + /** + * @description Get open orders for a balance manager in a pool + * @param {string} poolKey Key of the pool + * @param {string} managerKey Key of the balance manager + * @returns {Promise} An array of open order IDs + */ + async accountOpenOrders(poolKey: string, managerKey: string) { + const tx = new Transaction(); + + tx.add(this.deepBook.accountOpenOrders(poolKey, managerKey)); + const res = await this.client.devInspectTransactionBlock({ + sender: normalizeSuiAddress(this.#address), + transactionBlock: tx, + }); + + const order_ids = res.results![0].returnValues![0][0]; + const VecSet = bcs.struct('VecSet', { + constants: bcs.vector(bcs.U128), + }); + + return VecSet.parse(new Uint8Array(order_ids)).constants; + } + + /** + * @description Get the order information for a specific order in a pool + * @param {string} poolKey Key of the pool + * @param {string} orderId Order ID + * @returns {Promise} A promise that resolves to an object containing the order information + */ + async getOrder(poolKey: string, orderId: string) { + const tx = new Transaction(); + + tx.add(this.deepBook.getOrder(poolKey, orderId)); + const res = await this.client.devInspectTransactionBlock({ + sender: normalizeSuiAddress(this.#address), + transactionBlock: tx, + }); + + const ID = bcs.struct('ID', { + bytes: bcs.Address, + }); + const OrderDeepPrice = bcs.struct('OrderDeepPrice', { + asset_is_base: bcs.bool(), + deep_per_asset: bcs.u64(), + }); + const Order = bcs.struct('Order', { + balance_manager_id: ID, + order_id: bcs.u128(), + client_order_id: bcs.u64(), + quantity: bcs.u64(), + filled_quantity: bcs.u64(), + fee_is_deep: bcs.bool(), + order_deep_price: OrderDeepPrice, + epoch: bcs.u64(), + status: bcs.u8(), + expire_timestamp: bcs.u64(), + }); + + const orderInformation = res.results![0].returnValues![0][0]; + return Order.parse(new Uint8Array(orderInformation)); + } + + /** + * @description Get level 2 order book specifying range of price + * @param {string} poolKey Key of the pool + * @param {number} priceLow Lower bound of the price range + * @param {number} priceHigh Upper bound of the price range + * @param {boolean} isBid Whether to get bid or ask orders + * @returns {Promise<{ prices: Array, quantities: Array }>} + * An object with arrays of prices and quantities + */ + async getLevel2Range(poolKey: string, priceLow: number, priceHigh: number, isBid: boolean) { + const tx = new Transaction(); + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + + tx.add(this.deepBook.getLevel2Range(poolKey, priceLow, priceHigh, isBid)); + const res = await this.client.devInspectTransactionBlock({ + sender: normalizeSuiAddress(this.#address), + transactionBlock: tx, + }); + + const prices = res.results![0].returnValues![0][0]; + const parsed_prices = bcs.vector(bcs.u64()).parse(new Uint8Array(prices)); + const quantities = res.results![0].returnValues![1][0]; + const parsed_quantities = bcs.vector(bcs.u64()).parse(new Uint8Array(quantities)); + + return { + prices: parsed_prices.map( + (price) => (Number(price) / FLOAT_SCALAR / quoteCoin.scalar) * baseCoin.scalar, + ), + quantities: parsed_quantities.map((price) => Number(price) / baseCoin.scalar), + }; + } + + /** + * @description Get level 2 order book ticks from mid-price for a pool + * @param {string} poolKey Key of the pool + * @param {number} ticks Number of ticks from mid-price + * @returns {Promise<{ bid_prices: Array, bid_quantities: Array, ask_prices: Array, ask_quantities: Array }>} + * An object with arrays of prices and quantities + */ + async getLevel2TicksFromMid(poolKey: string, ticks: number) { + const tx = new Transaction(); + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + + tx.add(this.deepBook.getLevel2TicksFromMid(poolKey, ticks)); + const res = await this.client.devInspectTransactionBlock({ + sender: normalizeSuiAddress(this.#address), + transactionBlock: tx, + }); + + const bid_prices = res.results![0].returnValues![0][0]; + const bid_parsed_prices = bcs.vector(bcs.u64()).parse(new Uint8Array(bid_prices)); + const bid_quantities = res.results![0].returnValues![1][0]; + const bid_parsed_quantities = bcs.vector(bcs.u64()).parse(new Uint8Array(bid_quantities)); + + const ask_prices = res.results![0].returnValues![2][0]; + const ask_parsed_prices = bcs.vector(bcs.u64()).parse(new Uint8Array(ask_prices)); + const ask_quantities = res.results![0].returnValues![3][0]; + const ask_parsed_quantities = bcs.vector(bcs.u64()).parse(new Uint8Array(ask_quantities)); + + return { + bid_prices: bid_parsed_prices.map( + (price) => (Number(price) / FLOAT_SCALAR / quoteCoin.scalar) * baseCoin.scalar, + ), + bid_quantities: bid_parsed_quantities.map((quantity) => Number(quantity) / baseCoin.scalar), + ask_prices: ask_parsed_prices.map( + (price) => (Number(price) / FLOAT_SCALAR / quoteCoin.scalar) * baseCoin.scalar, + ), + ask_quantities: ask_parsed_quantities.map((quantity) => Number(quantity) / baseCoin.scalar), + }; + } + + /** + * @description Get the vault balances for a pool + * @param {string} poolKey Key of the pool + * @returns {Promise<{ base: number, quote: number, deep: number }>} + * An object with base, quote, and deep balances in the vault + */ + async vaultBalances(poolKey: string) { + const tx = new Transaction(); + const pool = this.#config.getPool(poolKey); + const baseScalar = this.#config.getCoin(pool.baseCoin).scalar; + const quoteScalar = this.#config.getCoin(pool.quoteCoin).scalar; + + tx.add(this.deepBook.vaultBalances(poolKey)); + const res = await this.client.devInspectTransactionBlock({ + sender: normalizeSuiAddress(this.#address), + transactionBlock: tx, + }); + + const baseInVault = Number(bcs.U64.parse(new Uint8Array(res.results![0].returnValues![0][0]))); + const quoteInVault = Number(bcs.U64.parse(new Uint8Array(res.results![0].returnValues![1][0]))); + const deepInVault = Number(bcs.U64.parse(new Uint8Array(res.results![0].returnValues![2][0]))); + + return { + base: baseInVault / baseScalar, + quote: quoteInVault / quoteScalar, + deep: deepInVault / DEEP_SCALAR, + }; + } + + /** + * @description Get the pool ID by asset types + * @param {string} baseType Type of the base asset + * @param {string} quoteType Type of the quote asset + * @returns {Promise} The address of the pool + */ + async getPoolIdByAssets(baseType: string, quoteType: string) { + const tx = new Transaction(); + tx.add(this.deepBook.getPoolIdByAssets(baseType, quoteType)); + + const res = await this.client.devInspectTransactionBlock({ + sender: normalizeSuiAddress(this.#address), + transactionBlock: tx, + }); + + const ID = bcs.struct('ID', { + bytes: bcs.Address, + }); + const address = ID.parse(new Uint8Array(res.results![0].returnValues![0][0]))['bytes']; + + return address; + } + + /** + * @description Get the mid price for a pool + * @param {string} poolKey Key of the pool + * @returns {Promise} The mid price + */ + async midPrice(poolKey: string) { + const tx = new Transaction(); + const pool = this.#config.getPool(poolKey); + tx.add(this.deepBook.midPrice(poolKey)); + + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + + const res = await this.client.devInspectTransactionBlock({ + sender: normalizeSuiAddress(this.#address), + transactionBlock: tx, + }); + + const bytes = res.results![0].returnValues![0][0]; + const parsed_mid_price = Number(bcs.U64.parse(new Uint8Array(bytes))); + const adjusted_mid_price = + (parsed_mid_price * baseCoin.scalar) / quoteCoin.scalar / FLOAT_SCALAR; + + return adjusted_mid_price; + } +} diff --git a/sdk/deepbook-v3/src/index.ts b/sdk/deepbook-v3/src/index.ts new file mode 100644 index 0000000000000..f8f6f0d28a03e --- /dev/null +++ b/sdk/deepbook-v3/src/index.ts @@ -0,0 +1,10 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +export { DeepBookClient } from './client.js'; +export { BalanceManagerContract } from './transactions/balanceManager.js'; +export { DeepBookContract } from './transactions/deepbook.js'; +export { DeepBookAdminContract } from './transactions/deepbookAdmin.js'; +export { FlashLoanContract } from './transactions/flashLoans.js'; +export { GovernanceContract } from './transactions/governance.js'; +export { DeepBookConfig } from './utils/config.js'; diff --git a/sdk/deepbook-v3/src/transactions/balanceManager.ts b/sdk/deepbook-v3/src/transactions/balanceManager.ts new file mode 100644 index 0000000000000..5b6496b3d274d --- /dev/null +++ b/sdk/deepbook-v3/src/transactions/balanceManager.ts @@ -0,0 +1,182 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 +import { coinWithBalance } from '@mysten/sui/transactions'; +import type { Transaction } from '@mysten/sui/transactions'; + +import type { DeepBookConfig } from '../utils/config.js'; + +/** + * BalanceManagerContract class for managing BalanceManager operations. + */ +export class BalanceManagerContract { + #config: DeepBookConfig; + + /** + * @param {DeepBookConfig} config Configuration for BalanceManagerContract + */ + constructor(config: DeepBookConfig) { + this.#config = config; + } + + /** + * @description Create and share a new BalanceManager + * @returns A function that takes a Transaction object + */ + createAndShareBalanceManager = () => (tx: Transaction) => { + const manager = tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::balance_manager::new`, + }); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::balance_manager::share`, + arguments: [manager], + }); + }; + + /** + * @description Deposit funds into the BalanceManager + * @param {string} managerKey The key of the BalanceManager + * @param {string} coinKey The key of the coin to deposit + * @param {number} amountToDeposit The amount to deposit + * @returns A function that takes a Transaction object + */ + depositIntoManager = + (managerKey: string, coinKey: string, amountToDeposit: number) => (tx: Transaction) => { + tx.setSenderIfNotSet(this.#config.address); + const managerId = this.#config.getBalanceManager(managerKey).address; + const coin = this.#config.getCoin(coinKey); + const deposit = coinWithBalance({ + type: coin.type, + balance: amountToDeposit * coin.scalar, + }); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::balance_manager::deposit`, + arguments: [tx.object(managerId), deposit], + typeArguments: [coin.type], + }); + }; + + /** + * @description Withdraw funds from the BalanceManager + * @param {string} managerKey The key of the BalanceManager + * @param {string} coinKey The key of the coin to withdraw + * @param {number} amountToWithdraw The amount to withdraw + * @param {string} recipient The recipient of the withdrawn funds + * @returns A function that takes a Transaction object + */ + withdrawFromManager = + (managerKey: string, coinKey: string, amountToWithdraw: number, recipient: string) => + (tx: Transaction) => { + const managerId = this.#config.getBalanceManager(managerKey).address; + const coin = this.#config.getCoin(coinKey); + const coinObject = tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::balance_manager::withdraw`, + arguments: [tx.object(managerId), tx.pure.u64(amountToWithdraw * coin.scalar)], + typeArguments: [coin.type], + }); + + tx.transferObjects([coinObject], recipient); + }; + + /** + * @description Withdraw all funds from the BalanceManager + * @param {string} managerKey The key of the BalanceManager + * @param {string} coinKey The key of the coin to withdraw + * @param {string} recipient The recipient of the withdrawn funds + * @returns A function that takes a Transaction object + */ + withdrawAllFromManager = + (managerKey: string, coinKey: string, recipient: string) => (tx: Transaction) => { + const managerId = this.#config.getBalanceManager(managerKey).address; + const coin = this.#config.getCoin(coinKey); + const withdrawalCoin = tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::balance_manager::withdraw_all`, + arguments: [tx.object(managerId)], + typeArguments: [coin.type], + }); + + tx.transferObjects([withdrawalCoin], recipient); + }; + + /** + * @description Check the balance of the BalanceManager + * @param {string} managerKey The key of the BalanceManager + * @param {string} coinKey The key of the coin to check the balance of + * @returns A function that takes a Transaction object + */ + checkManagerBalance = (managerKey: string, coinKey: string) => (tx: Transaction) => { + const managerId = this.#config.getBalanceManager(managerKey).address; + const coin = this.#config.getCoin(coinKey); + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::balance_manager::balance`, + arguments: [tx.object(managerId)], + typeArguments: [coin.type], + }); + }; + + /** + * @description Generate a trade proof for the BalanceManager. Calls the appropriate function based on whether tradeCap is set. + * @param {string} managerKey The key of the BalanceManager + * @returns A function that takes a Transaction object + */ + generateProof = (managerKey: string) => (tx: Transaction) => { + const balanceManager = this.#config.getBalanceManager(managerKey); + return tx.add( + balanceManager.tradeCap + ? this.generateProofAsTrader(balanceManager.address, balanceManager.tradeCap) + : this.generateProofAsOwner(balanceManager.address), + ); + }; + + /** + * @description Generate a trade proof as the owner + * @param {string} managerId The ID of the BalanceManager + * @returns A function that takes a Transaction object + */ + generateProofAsOwner = (managerId: string) => (tx: Transaction) => { + return tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::balance_manager::generate_proof_as_owner`, + arguments: [tx.object(managerId)], + }); + }; + + /** + * @description Generate a trade proof as a trader + * @param {string} managerId The ID of the BalanceManager + * @param {string} tradeCapId The ID of the tradeCap + * @returns A function that takes a Transaction object + */ + generateProofAsTrader = (managerId: string, tradeCapId: string) => (tx: Transaction) => { + return tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::balance_manager::generate_proof_as_trader`, + arguments: [tx.object(managerId), tx.object(tradeCapId)], + }); + }; + + /** + * @description Get the owner of the BalanceManager + * @param {string} managerKey The key of the BalanceManager + * @returns A function that takes a Transaction object + */ + owner = (managerKey: string) => (tx: Transaction) => { + const managerId = this.#config.getBalanceManager(managerKey).address; + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::balance_manager::owner`, + arguments: [tx.object(managerId)], + }); + }; + + /** + * @description Get the ID of the BalanceManager + * @param {string} managerKey The key of the BalanceManager + * @returns A function that takes a Transaction object + */ + id = (managerKey: string) => (tx: Transaction) => { + const managerId = this.#config.getBalanceManager(managerKey).address; + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::balance_manager::id`, + arguments: [tx.object(managerId)], + }); + }; +} diff --git a/sdk/deepbook-v3/src/transactions/deepbook.ts b/sdk/deepbook-v3/src/transactions/deepbook.ts new file mode 100644 index 0000000000000..aff618b9a41d6 --- /dev/null +++ b/sdk/deepbook-v3/src/transactions/deepbook.ts @@ -0,0 +1,587 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 +import { coinWithBalance } from '@mysten/sui/transactions'; +import type { Transaction } from '@mysten/sui/transactions'; +import { SUI_CLOCK_OBJECT_ID } from '@mysten/sui/utils'; + +import { OrderType, SelfMatchingOptions } from '../types/index.js'; +import type { PlaceLimitOrderParams, PlaceMarketOrderParams, SwapParams } from '../types/index.js'; +import type { DeepBookConfig } from '../utils/config.js'; +import { DEEP_SCALAR, FLOAT_SCALAR, GAS_BUDGET, MAX_TIMESTAMP } from '../utils/config.js'; + +/** + * DeepBookContract class for managing DeepBook operations. + */ +export class DeepBookContract { + #config: DeepBookConfig; + + /** + * @param {DeepBookConfig} config Configuration for DeepBookContract + */ + constructor(config: DeepBookConfig) { + this.#config = config; + } + + /** + * @description Place a limit order + * @param {PlaceLimitOrderParams} params Parameters for placing a limit order + * @returns A function that takes a Transaction object + */ + placeLimitOrder = (params: PlaceLimitOrderParams) => (tx: Transaction) => { + const { + poolKey, + balanceManagerKey, + clientOrderId, + price, + quantity, + isBid, + expiration = MAX_TIMESTAMP, + orderType = OrderType.NO_RESTRICTION, + selfMatchingOption = SelfMatchingOptions.SELF_MATCHING_ALLOWED, + payWithDeep = true, + } = params; + + tx.setGasBudgetIfNotSet(GAS_BUDGET); + const pool = this.#config.getPool(poolKey); + const balanceManager = this.#config.getBalanceManager(balanceManagerKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + const inputPrice = (price * FLOAT_SCALAR * quoteCoin.scalar) / baseCoin.scalar; + const inputQuantity = quantity * baseCoin.scalar; + + const tradeProof = tx.add(this.#config.balanceManager.generateProof(balanceManagerKey)); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::place_limit_order`, + arguments: [ + tx.object(pool.address), + tx.object(balanceManager.address), + tradeProof, + tx.pure.u64(clientOrderId), + tx.pure.u8(orderType), + tx.pure.u8(selfMatchingOption), + tx.pure.u64(inputPrice), + tx.pure.u64(inputQuantity), + tx.pure.bool(isBid), + tx.pure.bool(payWithDeep), + tx.pure.u64(expiration), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Place a market order + * @param {PlaceMarketOrderParams} params Parameters for placing a market order + * @returns A function that takes a Transaction object + */ + placeMarketOrder = (params: PlaceMarketOrderParams) => (tx: Transaction) => { + const { + poolKey, + balanceManagerKey, + clientOrderId, + quantity, + isBid, + selfMatchingOption = SelfMatchingOptions.SELF_MATCHING_ALLOWED, + payWithDeep = true, + } = params; + + tx.setGasBudgetIfNotSet(GAS_BUDGET); + const pool = this.#config.getPool(poolKey); + const balanceManager = this.#config.getBalanceManager(balanceManagerKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + const tradeProof = tx.add(this.#config.balanceManager.generateProof(balanceManagerKey)); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::place_market_order`, + arguments: [ + tx.object(pool.address), + tx.object(balanceManager.address), + tradeProof, + tx.pure.u64(clientOrderId), + tx.pure.u8(selfMatchingOption), + tx.pure.u64(quantity * baseCoin.scalar), + tx.pure.bool(isBid), + tx.pure.bool(payWithDeep), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Modify an existing order + * @param {string} poolKey The key to identify the pool + * @param {string} balanceManagerKey The key to identify the BalanceManager + * @param {string} orderId Order ID to modify + * @param {number} newQuantity New quantity for the order + * @returns A function that takes a Transaction object + */ + modifyOrder = + (poolKey: string, balanceManagerKey: string, orderId: string, newQuantity: number) => + (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const balanceManager = this.#config.getBalanceManager(balanceManagerKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + const tradeProof = tx.add(this.#config.balanceManager.generateProof(balanceManagerKey)); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::modify_order`, + arguments: [ + tx.object(pool.address), + tx.object(balanceManager.address), + tradeProof, + tx.pure.u128(orderId), + tx.pure.u64(newQuantity), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Cancel an existing order + * @param {string} poolKey The key to identify the pool + * @param {string} balanceManagerKey The key to identify the BalanceManager + * @param {string} orderId Order ID to cancel + * @returns A function that takes a Transaction object + */ + cancelOrder = + (poolKey: string, balanceManagerKey: string, orderId: string) => (tx: Transaction) => { + tx.setGasBudgetIfNotSet(GAS_BUDGET); + const pool = this.#config.getPool(poolKey); + const balanceManager = this.#config.getBalanceManager(balanceManagerKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + const tradeProof = tx.add(this.#config.balanceManager.generateProof(balanceManagerKey)); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::cancel_order`, + arguments: [ + tx.object(pool.address), + tx.object(balanceManager.address), + tradeProof, + tx.pure.u128(orderId), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Cancel all open orders for a balance manager + * @param {string} poolKey The key to identify the pool + * @param {string} balanceManagerKey The key to identify the BalanceManager + * @returns A function that takes a Transaction object + */ + cancelAllOrders = (poolKey: string, balanceManagerKey: string) => (tx: Transaction) => { + tx.setGasBudgetIfNotSet(GAS_BUDGET); + const pool = this.#config.getPool(poolKey); + const balanceManager = this.#config.getBalanceManager(balanceManagerKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + const tradeProof = tx.add(this.#config.balanceManager.generateProof(balanceManagerKey)); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::cancel_all_orders`, + arguments: [ + tx.object(pool.address), + tx.object(balanceManager.address), + tradeProof, + tx.object(SUI_CLOCK_OBJECT_ID), + ], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Withdraw settled amounts for a balance manager + * @param {string} poolKey The key to identify the pool + * @param {string} balanceManagerKey The key to identify the BalanceManager + * @returns A function that takes a Transaction object + */ + withdrawSettledAmounts = (poolKey: string, balanceManagerKey: string) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const balanceManager = this.#config.getBalanceManager(balanceManagerKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + const tradeProof = tx.add(this.#config.balanceManager.generateProof(balanceManagerKey)); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::withdraw_settled_amounts`, + arguments: [tx.object(pool.address), tx.object(balanceManager.address), tradeProof], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Add a deep price point for a target pool using a reference pool + * @param {string} targetPoolKey The key to identify the target pool + * @param {string} referencePoolKey The key to identify the reference pool + * @returns A function that takes a Transaction object + */ + addDeepPricePoint = (targetPoolKey: string, referencePoolKey: string) => (tx: Transaction) => { + const targetPool = this.#config.getPool(targetPoolKey); + const referencePool = this.#config.getPool(referencePoolKey); + const targetBaseCoin = this.#config.getCoin(targetPool.baseCoin); + const targetQuoteCoin = this.#config.getCoin(targetPool.quoteCoin); + const referenceBaseCoin = this.#config.getCoin(referencePool.baseCoin); + const referenceQuoteCoin = this.#config.getCoin(referencePool.quoteCoin); + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::add_deep_price_point`, + arguments: [ + tx.object(targetPool.address), + tx.object(referencePool.address), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + typeArguments: [ + targetBaseCoin.type, + targetQuoteCoin.type, + referenceBaseCoin.type, + referenceQuoteCoin.type, + ], + }); + }; + + /** + * @description Claim rebates for a balance manager + * @param {string} poolKey The key to identify the pool + * @param {string} balanceManagerKey The key to identify the BalanceManager + * @returns A function that takes a Transaction object + */ + claimRebates = (poolKey: string, balanceManagerKey: string) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const balanceManager = this.#config.getBalanceManager(balanceManagerKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + const tradeProof = tx.add(this.#config.balanceManager.generateProof(balanceManagerKey)); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::claim_rebates`, + arguments: [tx.object(pool.address), tx.object(balanceManager.address), tradeProof], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Gets an order + * @param {string} poolKey The key to identify the pool + * @param {string} orderId Order ID to get + * @returns A function that takes a Transaction object + */ + getOrder = (poolKey: string, orderId: string) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::get_order`, + arguments: [tx.object(pool.address), tx.pure.u128(orderId)], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Burn DEEP tokens from the pool + * @param {string} poolKey The key to identify the pool + * @returns A function that takes a Transaction object + */ + burnDeep = (poolKey: string) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::burn_deep`, + arguments: [tx.object(pool.address), tx.object(this.#config.DEEP_TREASURY_ID)], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Get the mid price for a pool + * @param {string} poolKey The key to identify the pool + * @returns A function that takes a Transaction object + */ + midPrice = (poolKey: string) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::mid_price`, + arguments: [tx.object(pool.address), tx.object(SUI_CLOCK_OBJECT_ID)], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Check if a pool is whitelisted + * @param {string} poolKey The key to identify the pool + * @returns A function that takes a Transaction object + */ + whitelisted = (poolKey: string) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::whitelisted`, + arguments: [tx.object(pool.address)], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Get the quote quantity out for a given base quantity in + * @param {string} poolKey The key to identify the pool + * @param {number} baseQuantity Base quantity to convert + * @returns A function that takes a Transaction object + */ + getQuoteQuantityOut = (poolKey: string, baseQuantity: number) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::get_quote_quantity_out`, + arguments: [ + tx.object(pool.address), + tx.pure.u64(baseQuantity * baseCoin.scalar), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Get the base quantity out for a given quote quantity in + * @param {string} poolKey The key to identify the pool + * @param {number} quoteQuantity Quote quantity to convert + * @returns A function that takes a Transaction object + */ + getBaseQuantityOut = (poolKey: string, quoteQuantity: number) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + const quoteScalar = quoteCoin.scalar; + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::get_base_quantity_out`, + arguments: [ + tx.object(pool.address), + tx.pure.u64(quoteQuantity * quoteScalar), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Get the quantity out for a given base or quote quantity + * @param {string} poolKey The key to identify the pool + * @param {number} baseQuantity Base quantity to convert + * @param {number} quoteQuantity Quote quantity to convert + * @returns A function that takes a Transaction object + */ + getQuantityOut = + (poolKey: string, baseQuantity: number, quoteQuantity: number) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + const quoteScalar = quoteCoin.scalar; + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::get_quantity_out`, + arguments: [ + tx.object(pool.address), + tx.pure.u64(baseQuantity * baseCoin.scalar), + tx.pure.u64(quoteQuantity * quoteScalar), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Get open orders for a balance manager in a pool + * @param {string} poolKey The key to identify the pool + * @param {string} managerKey Key of the balance manager + * @returns A function that takes a Transaction object + */ + accountOpenOrders = (poolKey: string, managerKey: string) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const manager = this.#config.getBalanceManager(managerKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::account_open_orders`, + arguments: [tx.object(pool.address), tx.object(manager.address)], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Get level 2 order book specifying range of price + * @param {string} poolKey The key to identify the pool + * @param {number} priceLow Lower bound of the price range + * @param {number} priceHigh Upper bound of the price range + * @param {boolean} isBid Whether to get bid or ask orders + * @returns A function that takes a Transaction object + */ + getLevel2Range = + (poolKey: string, priceLow: number, priceHigh: number, isBid: boolean) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::get_level2_range`, + arguments: [ + tx.object(pool.address), + tx.pure.u64((priceLow * FLOAT_SCALAR * quoteCoin.scalar) / baseCoin.scalar), + tx.pure.u64((priceHigh * FLOAT_SCALAR * quoteCoin.scalar) / baseCoin.scalar), + tx.pure.bool(isBid), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Get level 2 order book ticks from mid-price for a pool + * @param {string} poolKey The key to identify the pool + * @param {number} tickFromMid Number of ticks from mid-price + * @returns A function that takes a Transaction object + */ + getLevel2TicksFromMid = (poolKey: string, tickFromMid: number) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::get_level2_ticks_from_mid`, + arguments: [ + tx.object(pool.address), + tx.pure.u64(tickFromMid), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Get the vault balances for a pool + * @param {string} poolKey The key to identify the pool + * @returns A function that takes a Transaction object + */ + vaultBalances = (poolKey: string) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::vault_balances`, + arguments: [tx.object(pool.address)], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Get the pool ID by asset types + * @param {string} baseType Type of the base asset + * @param {string} quoteType Type of the quote asset + * @returns A function that takes a Transaction object + */ + getPoolIdByAssets = (baseType: string, quoteType: string) => (tx: Transaction) => { + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::get_pool_id_by_asset`, + arguments: [tx.object(this.#config.REGISTRY_ID)], + typeArguments: [baseType, quoteType], + }); + }; + + /** + * @description Swap exact base amount for quote amount + * @param {SwapParams} params Parameters for the swap + * @returns A function that takes a Transaction object + */ + swapExactBaseForQuote = (params: SwapParams) => (tx: Transaction) => { + tx.setGasBudgetIfNotSet(GAS_BUDGET); + tx.setSenderIfNotSet(this.#config.address); + + if (params.quoteCoin) { + throw new Error('quoteCoin is not accepted for swapping base asset'); + } + const { poolKey, amount: baseAmount, deepAmount, minOut: minQuote } = params; + + let pool = this.#config.getPool(poolKey); + let deepCoinType = this.#config.getCoin('DEEP').type; + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + + const baseCoinInput = + params.baseCoin ?? + coinWithBalance({ type: baseCoin.type, balance: baseAmount * baseCoin.scalar }); + + const deepCoin = + params.deepCoin ?? coinWithBalance({ type: deepCoinType, balance: deepAmount * DEEP_SCALAR }); + + const [baseCoinResult, quoteCoinResult, deepCoinResult] = tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::swap_exact_base_for_quote`, + arguments: [ + tx.object(pool.address), + baseCoinInput, + deepCoin, + tx.pure.u64(quoteCoin.scalar * minQuote), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + + return [baseCoinResult, quoteCoinResult, deepCoinResult] as const; + }; + + /** + * @description Swap exact quote amount for base amount + * @param {SwapParams} params Parameters for the swap + * @returns A function that takes a Transaction object + */ + swapExactQuoteForBase = (params: SwapParams) => (tx: Transaction) => { + tx.setGasBudgetIfNotSet(GAS_BUDGET); + tx.setSenderIfNotSet(this.#config.address); + + if (params.baseCoin) { + throw new Error('baseCoin is not accepted for swapping quote asset'); + } + const { poolKey, amount: quoteAmount, deepAmount, minOut: minBase } = params; + + let pool = this.#config.getPool(poolKey); + let deepCoinType = this.#config.getCoin('DEEP').type; + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + + const quoteCoinInput = + params.quoteCoin ?? + coinWithBalance({ type: quoteCoin.type, balance: quoteAmount * quoteCoin.scalar }); + + const deepCoin = + params.deepCoin ?? coinWithBalance({ type: deepCoinType, balance: deepAmount * DEEP_SCALAR }); + + const [baseCoinResult, quoteCoinResult, deepCoinResult] = tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::swap_exact_quote_for_base`, + arguments: [ + tx.object(pool.address), + quoteCoinInput, + deepCoin, + tx.pure.u64(baseCoin.scalar * minBase), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + + return [baseCoinResult, quoteCoinResult, deepCoinResult] as const; + }; +} diff --git a/sdk/deepbook-v3/src/transactions/deepbookAdmin.ts b/sdk/deepbook-v3/src/transactions/deepbookAdmin.ts new file mode 100644 index 0000000000000..801f8f7bb9ce0 --- /dev/null +++ b/sdk/deepbook-v3/src/transactions/deepbookAdmin.ts @@ -0,0 +1,140 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +import { coinWithBalance } from '@mysten/sui/transactions'; +import type { Transaction } from '@mysten/sui/transactions'; + +import type { CreatePoolAdminParams } from '../types/index.js'; +import type { DeepBookConfig } from '../utils/config.js'; +import { FLOAT_SCALAR, POOL_CREATION_FEE } from '../utils/config.js'; + +/** + * DeepBookAdminContract class for managing admin actions. + */ +export class DeepBookAdminContract { + #config: DeepBookConfig; + + /** + * @param {DeepBookConfig} config Configuration for DeepBookAdminContract + */ + constructor(config: DeepBookConfig) { + this.#config = config; + } + + /** + * @returns The admin capability required for admin operations + * @throws Error if the admin capability is not set + */ + #adminCap() { + const adminCap = this.#config.adminCap; + if (!adminCap) { + throw new Error('ADMIN_CAP environment variable not set'); + } + return adminCap; + } + + /** + * @description Create a new pool as admin + * @param {CreatePoolAdminParams} params Parameters for creating pool as admin + * @returns A function that takes a Transaction object + */ + createPoolAdmin = (params: CreatePoolAdminParams) => (tx: Transaction) => { + tx.setSenderIfNotSet(this.#config.address); + const { baseCoinKey, quoteCoinKey, tickSize, lotSize, minSize, whitelisted, stablePool } = + params; + const baseCoin = this.#config.getCoin(baseCoinKey); + const quoteCoin = this.#config.getCoin(quoteCoinKey); + const deepCoinType = this.#config.getCoin('DEEP').type; + + const creationFee = coinWithBalance({ type: deepCoinType, balance: POOL_CREATION_FEE }); + const baseScalar = baseCoin.scalar; + const quoteScalar = quoteCoin.scalar; + + const adjustedTickSize = (tickSize * FLOAT_SCALAR * quoteScalar) / baseScalar; + const adjustedLotSize = lotSize * baseScalar; + const adjustedMinSize = minSize * baseScalar; + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::create_pool_admin`, + arguments: [ + tx.object(this.#config.REGISTRY_ID), // registry_id + tx.pure.u64(adjustedTickSize), // adjusted tick_size + tx.pure.u64(adjustedLotSize), // adjusted lot_size + tx.pure.u64(adjustedMinSize), // adjusted min_size + creationFee, // 0x2::balance::Balance<0x2::sui::SUI> + tx.pure.bool(whitelisted), + tx.pure.bool(stablePool), + tx.object(this.#adminCap()), + ], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Unregister a pool as admin + * @param {string} poolKey The key of the pool to be unregistered by admin + * @returns A function that takes a Transaction object + */ + unregisterPoolAdmin = (poolKey: string) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::unregister_pool_admin`, + arguments: [tx.object(this.#config.REGISTRY_ID), tx.object(this.#adminCap())], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Update the allowed versions for a pool + * @param {string} poolKey The key of the pool to be updated + * @returns A function that takes a Transaction object + */ + updateAllowedVersions = (poolKey: string) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::update_allowed_versions`, + arguments: [ + tx.object(pool.address), + tx.object(this.#config.REGISTRY_ID), + tx.object(this.#adminCap()), + ], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Enable a specific version + * @param {number} version The version to be enabled + * @returns A function that takes a Transaction object + */ + enableVersion = (version: number) => (tx: Transaction) => { + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::registry::enable_version`, + arguments: [ + tx.object(this.#config.REGISTRY_ID), + tx.pure.u64(version), + tx.object(this.#adminCap()), + ], + }); + }; + + /** + * @description Disable a specific version + * @param {number} version The version to be disabled + * @returns A function that takes a Transaction object + */ + disableVersion = (version: number) => (tx: Transaction) => { + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::registry::disable_version`, + arguments: [ + tx.object(this.#config.REGISTRY_ID), + tx.pure.u64(version), + tx.object(this.#adminCap()), + ], + }); + }; +} diff --git a/sdk/deepbook-v3/src/transactions/flashLoans.ts b/sdk/deepbook-v3/src/transactions/flashLoans.ts new file mode 100644 index 0000000000000..bffdae0c09572 --- /dev/null +++ b/sdk/deepbook-v3/src/transactions/flashLoans.ts @@ -0,0 +1,121 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 +import type { Transaction, TransactionObjectArgument } from '@mysten/sui/transactions'; + +import type { DeepBookConfig } from '../utils/config.js'; + +/** + * FlashLoanContract class for managing flash loans. + */ +export class FlashLoanContract { + #config: DeepBookConfig; + + /** + * @param {DeepBookConfig} config Configuration object for DeepBook + */ + constructor(config: DeepBookConfig) { + this.#config = config; + } + + /** + * @description Borrow base asset from the pool + * @param {string} poolKey The key to identify the pool + * @param {number} borrowAmount The amount to borrow + * @returns A function that takes a Transaction object + */ + borrowBaseAsset = (poolKey: string, borrowAmount: number) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + const [baseCoinResult, flashLoan] = tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::borrow_flashloan_base`, + arguments: [tx.object(pool.address), tx.pure.u64(borrowAmount * baseCoin.scalar)], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + return [baseCoinResult, flashLoan] as const; + }; + + /** + * @description Return base asset to the pool after a flash loan. + * @param {string} poolKey The key to identify the pool + * @param {number} borrowAmount The amount of the base asset to return + * @param {TransactionObjectArgument} baseCoinInput Coin object representing the base asset to be returned + * @param {TransactionObjectArgument} flashLoan FlashLoan object representing the loan to be settled + * @returns A function that takes a Transaction object + */ + returnBaseAsset = + ( + poolKey: string, + borrowAmount: number, + baseCoinInput: TransactionObjectArgument, + flashLoan: TransactionObjectArgument, + ) => + (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + const borrowScalar = baseCoin.scalar; + + const [baseCoinReturn] = tx.splitCoins(baseCoinInput, [ + tx.pure.u64(borrowAmount * borrowScalar), + ]); + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::return_flashloan_base`, + arguments: [tx.object(pool.address), baseCoinReturn, flashLoan], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + + return baseCoinInput; + }; + + /** + * @description Borrow quote asset from the pool + * @param {string} poolKey The key to identify the pool + * @param {number} borrowAmount The amount to borrow + * @returns A function that takes a Transaction object + */ + borrowQuoteAsset = (poolKey: string, borrowAmount: number) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + const [quoteCoinResult, flashLoan] = tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::borrow_flashloan_quote`, + arguments: [tx.object(pool.address), tx.pure.u64(borrowAmount * quoteCoin.scalar)], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + return [quoteCoinResult, flashLoan] as const; + }; + + /** + * @description Return quote asset to the pool after a flash loan. + * @param {string} poolKey The key to identify the pool + * @param {number} borrowAmount The amount of the quote asset to return + * @param {TransactionObjectArgument} quoteCoinInput Coin object representing the quote asset to be returned + * @param {TransactionObjectArgument} flashLoan FlashLoan object representing the loan to be settled + * @returns A function that takes a Transaction object + */ + returnQuoteAsset = + ( + poolKey: string, + borrowAmount: number, + quoteCoinInput: TransactionObjectArgument, + flashLoan: TransactionObjectArgument, + ) => + (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + const borrowScalar = quoteCoin.scalar; + + const [quoteCoinReturn] = tx.splitCoins(quoteCoinInput, [ + tx.pure.u64(borrowAmount * borrowScalar), + ]); + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::return_flashloan_quote`, + arguments: [tx.object(pool.address), quoteCoinReturn, flashLoan], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + + return quoteCoinInput; + }; +} diff --git a/sdk/deepbook-v3/src/transactions/governance.ts b/sdk/deepbook-v3/src/transactions/governance.ts new file mode 100644 index 0000000000000..9428e8df982de --- /dev/null +++ b/sdk/deepbook-v3/src/transactions/governance.ts @@ -0,0 +1,119 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 +import type { Transaction } from '@mysten/sui/transactions'; + +import type { ProposalParams } from '../types/index.js'; +import type { DeepBookConfig } from '../utils/config.js'; +import { DEEP_SCALAR, FLOAT_SCALAR } from '../utils/config.js'; + +/** + * GovernanceContract class for managing governance operations in DeepBook. + */ +export class GovernanceContract { + #config: DeepBookConfig; + + /** + * @param {DeepBookConfig} config Configuration for GovernanceContract + */ + constructor(config: DeepBookConfig) { + this.#config = config; + } + + /** + * @description Stake a specified amount in the pool + * @param {string} poolKey The key to identify the pool + * @param {string} balanceManagerKey The key to identify the BalanceManager + * @param {number} stakeAmount The amount to stake + * @returns A function that takes a Transaction object + */ + stake = + (poolKey: string, balanceManagerKey: string, stakeAmount: number) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const balanceManager = this.#config.getBalanceManager(balanceManagerKey); + const tradeProof = tx.add(this.#config.balanceManager.generateProof(balanceManagerKey)); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::stake`, + arguments: [ + tx.object(pool.address), + tx.object(balanceManager.address), + tradeProof, + tx.pure.u64(stakeAmount * DEEP_SCALAR), + ], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Unstake from the pool + * @param {string} poolKey The key to identify the pool + * @param {string} balanceManagerKey The key to identify the BalanceManager + * @returns A function that takes a Transaction object + */ + unstake = (poolKey: string, balanceManagerKey: string) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const balanceManager = this.#config.getBalanceManager(balanceManagerKey); + const tradeProof = tx.add(this.#config.balanceManager.generateProof(balanceManagerKey)); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::unstake`, + arguments: [tx.object(pool.address), tx.object(balanceManager.address), tradeProof], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Submit a governance proposal + * @param {ProposalParams} params Parameters for the proposal + * @returns A function that takes a Transaction object + */ + submitProposal = (params: ProposalParams) => (tx: Transaction) => { + const { poolKey, balanceManagerKey, takerFee, makerFee, stakeRequired } = params; + + const pool = this.#config.getPool(poolKey); + const balanceManager = this.#config.getBalanceManager(balanceManagerKey); + const tradeProof = tx.add(this.#config.balanceManager.generateProof(balanceManagerKey)); + const baseCoin = this.#config.getCoin(pool.baseCoin); + const quoteCoin = this.#config.getCoin(pool.quoteCoin); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::submit_proposal`, + arguments: [ + tx.object(pool.address), + tx.object(balanceManager.address), + tradeProof, + tx.pure.u64(takerFee * FLOAT_SCALAR), + tx.pure.u64(makerFee * FLOAT_SCALAR), + tx.pure.u64(stakeRequired * DEEP_SCALAR), + ], + typeArguments: [baseCoin.type, quoteCoin.type], + }); + }; + + /** + * @description Vote on a proposal + * @param {string} poolKey The key to identify the pool + * @param {string} balanceManagerKey The key to identify the BalanceManager + * @param {string} proposal_id The ID of the proposal to vote on + * @returns A function that takes a Transaction object + */ + vote = (poolKey: string, balanceManagerKey: string, proposal_id: string) => (tx: Transaction) => { + const pool = this.#config.getPool(poolKey); + const balanceManager = this.#config.getBalanceManager(balanceManagerKey); + const tradeProof = tx.add(this.#config.balanceManager.generateProof(balanceManagerKey)); + + tx.moveCall({ + target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::vote`, + arguments: [ + tx.object(pool.address), + tx.object(balanceManager.address), + tradeProof, + tx.pure.id(proposal_id), + ], + }); + }; +} diff --git a/sdk/deepbook-v3/src/types/index.ts b/sdk/deepbook-v3/src/types/index.ts new file mode 100644 index 0000000000000..4d51820cbd7e9 --- /dev/null +++ b/sdk/deepbook-v3/src/types/index.ts @@ -0,0 +1,98 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +import type { TransactionObjectArgument } from '@mysten/sui/transactions'; + +// SPDX-License-Identifier: Apache-2.0 +export interface BalanceManager { + address: string; + tradeCap: string | undefined; +} + +export interface Coin { + address: string; + type: string; + scalar: number; +} + +export interface Pool { + address: string; + baseCoin: string; + quoteCoin: string; +} + +// Trading constants +export enum OrderType { + NO_RESTRICTION, + IMMEDIATE_OR_CANCEL, + FILL_OR_KILL, + POST_ONLY, +} + +// Self matching options +export enum SelfMatchingOptions { + SELF_MATCHING_ALLOWED, + CANCEL_TAKER, + CANCEL_MAKER, +} + +export interface PlaceLimitOrderParams { + poolKey: string; + balanceManagerKey: string; + clientOrderId: string; + price: number; + quantity: number; + isBid: boolean; + expiration?: number | bigint; + orderType?: OrderType; + selfMatchingOption?: SelfMatchingOptions; + payWithDeep?: boolean; +} + +export interface PlaceMarketOrderParams { + poolKey: string; + balanceManagerKey: string; + clientOrderId: string; + quantity: number; + isBid: boolean; + selfMatchingOption?: SelfMatchingOptions; + payWithDeep?: boolean; +} + +export interface ProposalParams { + poolKey: string; + balanceManagerKey: string; + takerFee: number; + makerFee: number; + stakeRequired: number; +} + +export interface SwapParams { + poolKey: string; + amount: number; + deepAmount: number; + minOut: number; + deepCoin?: TransactionObjectArgument; + baseCoin?: TransactionObjectArgument; + quoteCoin?: TransactionObjectArgument; +} + +export interface CreatePoolAdminParams { + baseCoinKey: string; + quoteCoinKey: string; + tickSize: number; + lotSize: number; + minSize: number; + whitelisted: boolean; + stablePool: boolean; + deepCoin?: TransactionObjectArgument; + baseCoin?: TransactionObjectArgument; +} + +export interface Config { + DEEPBOOK_PACKAGE_ID: string; + REGISTRY_ID: string; + DEEP_TREASURY_ID: string; +} + +export type Environment = 'mainnet' | 'testnet'; diff --git a/sdk/deepbook-v3/src/utils/config.ts b/sdk/deepbook-v3/src/utils/config.ts new file mode 100644 index 0000000000000..750ef6d7e68e4 --- /dev/null +++ b/sdk/deepbook-v3/src/utils/config.ts @@ -0,0 +1,103 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 +import { normalizeSuiAddress } from '@mysten/sui/utils'; + +import { BalanceManagerContract } from '../transactions/balanceManager.js'; +import type { BalanceManager, Environment } from '../types/index.js'; +import type { CoinMap, PoolMap } from './constants.js'; +import { + mainnetCoins, + mainnetPackageIds, + mainnetPools, + testnetCoins, + testnetPackageIds, + testnetPools, +} from './constants.js'; + +export const FLOAT_SCALAR = 1000000000; +export const POOL_CREATION_FEE = 10000 * 1000000; +export const MAX_TIMESTAMP = 1844674407370955161n; +export const GAS_BUDGET = 0.5 * 500000000; // Adjust based on benchmarking +export const DEEP_SCALAR = 1000000; + +export class DeepBookConfig { + #coins: CoinMap; + #pools: PoolMap; + balanceManagers: { [key: string]: BalanceManager }; + address: string; + + DEEPBOOK_PACKAGE_ID: string; + REGISTRY_ID: string; + DEEP_TREASURY_ID: string; + adminCap?: string; + + balanceManager: BalanceManagerContract; + + constructor({ + env, + address, + adminCap, + balanceManagers, + coins, + pools, + }: { + env: Environment; + address: string; + adminCap?: string; + balanceManagers?: { [key: string]: BalanceManager }; + coins?: CoinMap; + pools?: PoolMap; + }) { + this.address = normalizeSuiAddress(address); + this.adminCap = adminCap; + this.balanceManagers = balanceManagers || {}; + + if (env === 'mainnet') { + this.#coins = coins || mainnetCoins; + this.#pools = pools || mainnetPools; + this.DEEPBOOK_PACKAGE_ID = mainnetPackageIds.DEEPBOOK_PACKAGE_ID; + this.REGISTRY_ID = mainnetPackageIds.REGISTRY_ID; + this.DEEP_TREASURY_ID = mainnetPackageIds.DEEP_TREASURY_ID; + } else { + this.#coins = coins || testnetCoins; + this.#pools = pools || testnetPools; + this.DEEPBOOK_PACKAGE_ID = testnetPackageIds.DEEPBOOK_PACKAGE_ID; + this.REGISTRY_ID = testnetPackageIds.REGISTRY_ID; + this.DEEP_TREASURY_ID = testnetPackageIds.DEEP_TREASURY_ID; + } + + this.balanceManager = new BalanceManagerContract(this); + } + + // Getters + getCoin(key: string) { + const coin = this.#coins[key]; + if (!coin) { + throw new Error(`Coin not found for key: ${key}`); + } + + return coin; + } + + getPool(key: string) { + const pool = this.#pools[key]; + if (!pool) { + throw new Error(`Pool not found for key: ${key}`); + } + + return pool; + } + + /** + * @description Get the balance manager by key + * @param managerKey Key of the balance manager + * @returns The BalanceManager object + */ + getBalanceManager(managerKey: string): BalanceManager { + if (!Object.hasOwn(this.balanceManagers, managerKey)) { + throw new Error(`Balance manager with key ${managerKey} not found.`); + } + + return this.balanceManagers[managerKey]; + } +} diff --git a/sdk/deepbook-v3/src/utils/constants.ts b/sdk/deepbook-v3/src/utils/constants.ts new file mode 100644 index 0000000000000..1db35b98ac594 --- /dev/null +++ b/sdk/deepbook-v3/src/utils/constants.ts @@ -0,0 +1,106 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +import type { Coin, Pool } from '../types/index.js'; + +export type CoinMap = Record; +export type PoolMap = Record; +export interface DeepbookPackageIds { + DEEPBOOK_PACKAGE_ID: string; + REGISTRY_ID: string; + DEEP_TREASURY_ID: string; +} + +export const testnetPackageIds = { + DEEPBOOK_PACKAGE_ID: '0xbf8c499152c205dc5af0a89e95911e2c7d6d1c8b6a4d3a96058aee5fef3ae03a', + REGISTRY_ID: '0x1e9619bb7e1ffb94f2ba9d1bd55d922921931e138dd0b7a1a3c5ede8d1cf9424', + DEEP_TREASURY_ID: '0x69fffdae0075f8f71f4fa793549c11079266910e8905169845af1f5d00e09dcb', +} satisfies DeepbookPackageIds; + +export const mainnetPackageIds = { + DEEPBOOK_PACKAGE_ID: '', + REGISTRY_ID: '', + DEEP_TREASURY_ID: '', +}; + +export const testnetCoins: CoinMap = { + DEEP: { + address: `0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8`, + type: `0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP`, + scalar: 1000000, + }, + SUI: { + address: `0x0000000000000000000000000000000000000000000000000000000000000002`, + type: `0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI`, + scalar: 1000000000, + }, + DBUSDC: { + address: `0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7`, + type: `0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDC::DBUSDC`, + scalar: 1000000, + }, + DBUSDT: { + address: `0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7`, + type: `0xf7152c05930480cd740d7311b5b8b45c6f488e3a53a11c3f74a6fac36a52e0d7::DBUSDT::DBUSDT`, + scalar: 1000000, + }, +}; + +export const mainnetCoins: CoinMap = { + DEEP: { + address: `0xdeeb7a4662eec9f2f3def03fb937a663dddaa2e215b8078a284d026b7946c270`, + type: `0xdeeb7a4662eec9f2f3def03fb937a663dddaa2e215b8078a284d026b7946c270::deep::DEEP`, + scalar: 1000000, + }, + SUI: { + address: `0x0000000000000000000000000000000000000000000000000000000000000002`, + type: `0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI`, + scalar: 1000000000, + }, + USDC: { + address: `0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf`, + type: `0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN`, + scalar: 1000000, + }, + WETH: { + address: `0xaf8cd5edc19c4512f4259f0bee101a40d41ebed738ade5874359610ef8eeced5`, + type: `0xaf8cd5edc19c4512f4259f0bee101a40d41ebed738ade5874359610ef8eeced5::coin::COIN`, + scalar: 100000000, + }, + USDT: { + address: `0xc060006111016b8a020ad5b33834984a437aaa7d3c74c18e09a95d48aceab08c`, + type: `0xc060006111016b8a020ad5b33834984a437aaa7d3c74c18e09a95d48aceab08c::coin::COIN`, + scalar: 1000000, + }, +}; + +export const testnetPools: PoolMap = { + DEEP_SUI: { + address: `0xfd25055cdcfff6ef7031c941d4fd407880fa19dd24d0f526c3b38c633fefe49e`, + baseCoin: 'DEEP', + quoteCoin: 'SUI', + }, + SUI_DBUSDC: { + address: `0x9b7d3cf0c35cdebb9909705548249c4240004f7300e1b0dfc90ac232c8f431d1`, + baseCoin: 'SUI', + quoteCoin: 'DBUSDC', + }, + DEEP_DBUSDC: { + address: `0xa136f878686acd23ea6881f31b937ffd378bbdb4eaebf54bd793c5f9e2ab53f0`, + baseCoin: 'DEEP', + quoteCoin: 'DBUSDC', + }, + DBUSDT_DBUSDC: { + address: `0x19a667bb5c80ee40155b8bf9aada0d787d7a86cb44f51b4b8a88df7d5a4cd0d0`, + baseCoin: 'DBUSDT', + quoteCoin: 'DBUSDC', + }, +}; + +export const mainnetPools: PoolMap = { + DEEP_SUI: { + address: ``, + baseCoin: 'DEEP', + quoteCoin: 'SUI', + }, +}; diff --git a/sdk/deepbook-v3/tsconfig.esm.json b/sdk/deepbook-v3/tsconfig.esm.json new file mode 100644 index 0000000000000..5048bdf8ffc62 --- /dev/null +++ b/sdk/deepbook-v3/tsconfig.esm.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "outDir": "dist/esm" + } +} diff --git a/sdk/deepbook-v3/tsconfig.json b/sdk/deepbook-v3/tsconfig.json new file mode 100644 index 0000000000000..b4ed52cfe8a5e --- /dev/null +++ b/sdk/deepbook-v3/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../build-scripts/tsconfig.shared.json", + "include": ["src"], + "compilerOptions": { + "module": "CommonJS", + "outDir": "dist/cjs", + "isolatedModules": true, + "rootDir": "src" + }, + "references": [{ "path": "../typescript" }] +} diff --git a/sdk/deepbook-v3/typedoc.json b/sdk/deepbook-v3/typedoc.json new file mode 100644 index 0000000000000..ccb323f01e813 --- /dev/null +++ b/sdk/deepbook-v3/typedoc.json @@ -0,0 +1,6 @@ +{ + "entryPoints": ["src"], + "excludeInternal": true, + "excludePrivate": true, + "intentionallyNotExported": [] +} diff --git a/sdk/deepbook-v3/vitest.config.ts b/sdk/deepbook-v3/vitest.config.ts new file mode 100644 index 0000000000000..3f63e2781ad8e --- /dev/null +++ b/sdk/deepbook-v3/vitest.config.ts @@ -0,0 +1,18 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + maxConcurrency: 8, + hookTimeout: 1000000, + testTimeout: 1000000, + env: { + NODE_ENV: 'test', + }, + }, + resolve: { + alias: {}, + }, +}); diff --git a/sdk/typescript/src/transactions/Transaction.ts b/sdk/typescript/src/transactions/Transaction.ts index 2dc90a4dd10a4..75cac56c04735 100644 --- a/sdk/typescript/src/transactions/Transaction.ts +++ b/sdk/typescript/src/transactions/Transaction.ts @@ -179,6 +179,13 @@ export class Transaction { setGasBudget(budget: number | bigint) { this.#data.gasConfig.budget = String(budget); } + + setGasBudgetIfNotSet(budget: number | bigint) { + if (this.#data.gasData.budget == null) { + this.#data.gasConfig.budget = String(budget); + } + } + setGasOwner(owner: string) { this.#data.gasConfig.owner = owner; } From 82a0a27f95022ffa8c9efa0546410c6ed13d6ed2 Mon Sep 17 00:00:00 2001 From: shio-coder <165585716+shio-coder@users.noreply.github.com> Date: Tue, 30 Jul 2024 15:16:44 -0700 Subject: [PATCH 037/232] Soft bundle basic observability (#18807) ## Description The PR adds several metrics and logging to Soft Bundle. A node operator will have the ability to understand what bundles are submitted, as well as the content. This can be particularly useful when tracing down a transaction. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-core/src/authority_server.rs | 228 ++++++++++++++---------- 1 file changed, 133 insertions(+), 95 deletions(-) diff --git a/crates/sui-core/src/authority_server.rs b/crates/sui-core/src/authority_server.rs index 31aec18b6aecc..b61975ef79d40 100644 --- a/crates/sui-core/src/authority_server.rs +++ b/crates/sui-core/src/authority_server.rs @@ -173,6 +173,7 @@ pub struct ValidatorServiceMetrics { pub submit_certificate_consensus_latency: MystenHistogram, pub handle_certificate_consensus_latency: MystenHistogram, pub handle_certificate_non_consensus_latency: MystenHistogram, + pub handle_soft_bundle_certificates_consensus_latency: MystenHistogram, num_rejected_tx_in_epoch_boundary: IntCounter, num_rejected_cert_in_epoch_boundary: IntCounter, @@ -228,6 +229,11 @@ impl ValidatorServiceMetrics { "Latency of handling a non-consensus transaction certificate", registry, ), + handle_soft_bundle_certificates_consensus_latency: MystenHistogram::new_in_registry( + "validator_service_handle_soft_bundle_certificates_consensus_latency", + "Latency of handling a consensus soft bundle", + registry, + ), num_rejected_tx_in_epoch_boundary: register_int_counter_with_registry!( "validator_service_num_rejected_tx_in_epoch_boundary", "Number of rejected transaction during epoch transitioning", @@ -451,24 +457,29 @@ impl ValidatorService { SuiError::FullNodeCantHandleCertificate.into() ); - let shared_object_tx = certificates[0].contains_shared_object(); + let shared_object_tx = certificates + .iter() + .any(|cert| cert.contains_shared_object()); - let _metrics_guard = if wait_for_effects { - if shared_object_tx { - self.metrics - .handle_certificate_consensus_latency - .start_timer() + let metrics = if certificates.len() == 1 { + if wait_for_effects { + if shared_object_tx { + &self.metrics.handle_certificate_consensus_latency + } else { + &self.metrics.handle_certificate_non_consensus_latency + } } else { - self.metrics - .handle_certificate_non_consensus_latency - .start_timer() + &self.metrics.submit_certificate_consensus_latency } } else { - self.metrics - .submit_certificate_consensus_latency - .start_timer() + // `soft_bundle_validity_check` ensured that all certificates contain shared objects. + &self + .metrics + .handle_soft_bundle_certificates_consensus_latency }; + let _metrics_guard = metrics.start_timer(); + // 1) Check if the certificate is already executed. // This is only needed when we have only one certificate (not a soft bundle). // When multiple certificates are provided, we will either submit all of them or none of them to consensus. @@ -827,6 +838,11 @@ impl ValidatorService { request: tonic::Request, ) -> WrappedServiceResponse { let epoch_store = self.state.load_epoch_store_one_call_per_task(); + let client_addr = if self.client_id_source.is_none() { + self.get_client_ip_addr(&request, &ClientIdSource::SocketAddr) + } else { + self.get_client_ip_addr(&request, self.client_id_source.as_ref().unwrap()) + }; let request = request.into_inner(); let certificates = NonEmpty::from_vec(request.certificates) @@ -840,6 +856,18 @@ impl ValidatorService { self.soft_bundle_validity_check(&certificates, &epoch_store) .await?; + info!( + "Received Soft Bundle with {} certificates, from {}, tx digests are [{}]", + certificates.len(), + client_addr + .map(|x| x.to_string()) + .unwrap_or_else(|| "unknown".to_string()), + certificates + .iter() + .map(|x| x.digest().to_string()) + .collect::>() + .join(", ") + ); let span = error_span!("handle_soft_bundle_certificates_v3"); self.handle_certificates( certificates, @@ -909,6 +937,98 @@ impl ValidatorService { Ok((tonic::Response::new(response), Weight::one())) } + fn get_client_ip_addr( + &self, + request: &tonic::Request, + source: &ClientIdSource, + ) -> Option { + match source { + ClientIdSource::SocketAddr => { + let socket_addr: Option = request.remote_addr(); + + // We will hit this case if the IO type used does not + // implement Connected or when using a unix domain socket. + // TODO: once we have confirmed that no legitimate traffic + // is hitting this case, we should reject such requests that + // hit this case. + if let Some(socket_addr) = socket_addr { + Some(socket_addr.ip()) + } else { + if cfg!(msim) { + // Ignore the error from simtests. + } else if cfg!(test) { + panic!("Failed to get remote address from request"); + } else { + self.metrics.connection_ip_not_found.inc(); + error!("Failed to get remote address from request"); + } + None + } + } + ClientIdSource::XForwardedFor(num_hops) => { + let do_header_parse = |op: &MetadataValue| { + match op.to_str() { + Ok(header_val) => { + let header_contents = + header_val.split(',').map(str::trim).collect::>(); + if *num_hops == 0 { + error!( + "x-forwarded-for: 0 specified. x-forwarded-for contents: {:?}. Please assign nonzero value for \ + number of hops here, or use `socket-addr` client-id-source type if requests are not being proxied \ + to this node. Skipping traffic controller request handling.", + header_contents, + ); + return None; + } + let contents_len = header_contents.len(); + let Some(client_ip) = header_contents.get(contents_len - num_hops) + else { + error!( + "x-forwarded-for header value of {:?} contains {} values, but {} hops were specificed. \ + Expected at least {} values. Skipping traffic controller request handling.", + header_contents, + contents_len, + num_hops, + contents_len, + ); + return None; + }; + client_ip.parse::().ok().or_else(|| { + client_ip.parse::().ok().map(|socket_addr| socket_addr.ip()).or_else(|| { + self.metrics.forwarded_header_parse_error.inc(); + error!( + "Failed to parse x-forwarded-for header value of {:?} to ip address or socket. \ + Please ensure that your proxy is configured to resolve client domains to an \ + IP address before writing header", + client_ip, + ); + None + }) + }) + } + Err(e) => { + // TODO: once we have confirmed that no legitimate traffic + // is hitting this case, we should reject such requests that + // hit this case. + self.metrics.forwarded_header_invalid.inc(); + error!("Invalid UTF-8 in x-forwarded-for header: {:?}", e); + None + } + } + }; + if let Some(op) = request.metadata().get("x-forwarded-for") { + do_header_parse(op) + } else if let Some(op) = request.metadata().get("X-Forwarded-For") { + do_header_parse(op) + } else { + self.metrics.forwarded_header_not_included.inc(); + error!("x-forwarded-for header not present for request despite node configuring x-forwarded-for tracking type"); + None + } + } + } + } + async fn handle_traffic_req(&self, client: Option) -> Result<(), tonic::Status> { if let Some(traffic_controller) = &self.traffic_controller { if !traffic_controller.check(&client, &None).await { @@ -985,89 +1105,7 @@ macro_rules! handle_with_decoration { return $self.$func_name($request).await.map(|(result, _)| result); } - let client = match $self.client_id_source.as_ref().unwrap() { - ClientIdSource::SocketAddr => { - let socket_addr: Option = $request.remote_addr(); - - // We will hit this case if the IO type used does not - // implement Connected or when using a unix domain socket. - // TODO: once we have confirmed that no legitimate traffic - // is hitting this case, we should reject such requests that - // hit this case. - if let Some(socket_addr) = socket_addr { - Some(socket_addr.ip()) - } else { - if cfg!(msim) { - // Ignore the error from simtests. - } else if cfg!(test) { - panic!("Failed to get remote address from request"); - } else { - $self.metrics.connection_ip_not_found.inc(); - error!("Failed to get remote address from request"); - } - None - } - } - ClientIdSource::XForwardedFor(num_hops) => { - let do_header_parse = |op: &MetadataValue| { - match op.to_str() { - Ok(header_val) => { - let header_contents = header_val.split(',').map(str::trim).collect::>(); - if *num_hops == 0 { - error!( - "x-forwarded-for: 0 specified. x-forwarded-for contents: {:?}. Please assign nonzero value for \ - number of hops here, or use `socket-addr` client-id-source type if requests are not being proxied \ - to this node. Skipping traffic controller request handling.", - header_contents, - ); - return None; - } - let contents_len = header_contents.len(); - let Some(client_ip) = header_contents.get(contents_len - num_hops) else { - error!( - "x-forwarded-for header value of {:?} contains {} values, but {} hops were specificed. \ - Expected at least {} values. Skipping traffic controller request handling.", - header_contents, - contents_len, - num_hops, - contents_len, - ); - return None; - }; - client_ip.parse::().ok().or_else(|| { - client_ip.parse::().ok().map(|socket_addr| socket_addr.ip()).or_else(|| { - $self.metrics.forwarded_header_parse_error.inc(); - error!( - "Failed to parse x-forwarded-for header value of {:?} to ip address or socket. \ - Please ensure that your proxy is configured to resolve client domains to an \ - IP address before writing header", - client_ip, - ); - None - }) - }) - } - Err(e) => { - // TODO: once we have confirmed that no legitimate traffic - // is hitting this case, we should reject such requests that - // hit this case. - $self.metrics.forwarded_header_invalid.inc(); - error!("Invalid UTF-8 in x-forwarded-for header: {:?}", e); - None - } - } - }; - if let Some(op) = $request.metadata().get("x-forwarded-for") { - do_header_parse(op) - } else if let Some(op) = $request.metadata().get("X-Forwarded-For") { - do_header_parse(op) - } else { - $self.metrics.forwarded_header_not_included.inc(); - error!("x-forwarded-for header not present for request despite node configuring x-forwarded-for tracking type"); - None - } - } - }; + let client = $self.get_client_ip_addr(&$request, $self.client_id_source.as_ref().unwrap()); // check if either IP is blocked, in which case return early $self.handle_traffic_req(client.clone()).await?; From b7b2a760cc7597e481a757fd5e9d4a32df25e166 Mon Sep 17 00:00:00 2001 From: John Martin Date: Tue, 30 Jul 2024 15:40:18 -0700 Subject: [PATCH 038/232] [docs] update snapshot docs based off operator feedback (#18855) ## Description Got some feedback from a validator operator that the docs were a bit confusing, attempting to improve them. --------- Co-authored-by: ronny-mysten <118224482+ronny-mysten@users.noreply.github.com> --- docs/content/guides/operator/snapshots.mdx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/content/guides/operator/snapshots.mdx b/docs/content/guides/operator/snapshots.mdx index f0cf266ddc63a..b545803218a96 100644 --- a/docs/content/guides/operator/snapshots.mdx +++ b/docs/content/guides/operator/snapshots.mdx @@ -60,9 +60,9 @@ To restore from a RocksDB snapshot, follow these steps: - `--latest`: Rather than explicitly passing a epoch via `--epoch`, you can pass the `--latest` flag, which will automatically select the latest snapshot` - `--network`: Network to download snapshot for. Defaults to "mainnet". - `--path`: Path to snapshot directory on local filesystem. - - `--no-sign-request`: If set, `--snapshot-bucket` and `--snapshot-bucket-type` are ignored, and Cloudflare R2 is used. - - `--snapshot-bucket`: Source snapshot bucket name, eg `mysten-mainnet-snapshots`. - - `--snapshot-bucket-type`: Snapshot bucket type. GCS and S3 currently supported. + - `--no-sign-request`: If set, `--snapshot-bucket` and `--snapshot-bucket-type` are ignored, and a static Cloudflare R2 bucket is used. + - `--snapshot-bucket`: Source snapshot bucket name, eg `mysten-mainnet-snapshots`. This cannot be used with `--no-sign-request`. + - `--snapshot-bucket-type`: Snapshot bucket type. GCS and S3 currently supported. This cannot be used with `--no-sign-request`. - `--skip-indexes`: Skips downloading the very large `indexes/` dir, used by jsonrpc on the fullnode The following environment variables are used if `--no-sign-request` is not set: @@ -77,7 +77,7 @@ To restore from a RocksDB snapshot, follow these steps: :::info -When you restore a Full node from a snapshot, write it to the path `/opt/sui/db/authorities_db/full_node_db/live`. To restore a Validator node, use the path `/opt/sui/db/authorities_db/live`. +When you restore a Full node from a snapshot, write it to the path `/opt/sui/db/authorities_db/full_node_db/live`. When restoring a Validator node, you can shorten the database destination to `/opt/sui/db/authorities_db/live`. ::: @@ -101,8 +101,8 @@ The following steps can be used to restore a node from a Formal snapshot: - `--network`: Network to download snapshot for. Defaults to "mainnet". - `--path`: Path to snapshot directory on local filesystem. - `--no-sign-request`: If set, `--snapshot-bucket` and `--snapshot-bucket-type` are ignored, and Cloudflare R2 is used. - - `--snapshot-bucket`: Source snapshot bucket name, eg `mysten-mainnet-snapshots`. - - `--snapshot-bucket-type`: Snapshot bucket type. GCS and S3 currently supported. + - `--snapshot-bucket`: Source snapshot bucket name, eg `mysten-mainnet-snapshots`. This cannot be used with `--no-sign-request`. + - `--snapshot-bucket-type`: Snapshot bucket type. GCS and S3 currently supported. This cannot be used with `--no-sign-request`. The following environment variables are used if `--no-sign-request` is not set: * *AWS*: `AWS_SNAPSHOT_ACCESS_KEY_ID`, `AWS_SNAPSHOT_SECRET_ACCESS_KEY`, `AWS_SNAPSHOT_REGION` From 0148d85f3b4a0d3bb7ea880c33825cef7fa9dfbe Mon Sep 17 00:00:00 2001 From: Adam Welc Date: Tue, 30 Jul 2024 17:08:01 -0700 Subject: [PATCH 039/232] [move-ide] Added smart auto-completion for colon-colon (#18778) ## Description Currently, this PR implements most of functionality for `::` auto-completion when the cursor is on one of the identifiers in the name access chains. There are some missing parts here, arguably better split to separate PRs: - handling of access chains in attributes - handling of access chainss in `use` statements Also, this PR actually removes `:` completion (auto-completion start when typing the first character after `:`) which is consistent with what rust-analyzer does. While we can implement auto-completion right after `:`, the question is whether we should ## Test plan All new and old tests must pass --- Cargo.lock | 1 + external-crates/move/Cargo.lock | 1 + .../move/crates/move-analyzer/Cargo.toml | 1 + .../editors/code/language-configuration.json | 5 +- .../crates/move-analyzer/src/compiler_info.rs | 5 +- .../crates/move-analyzer/src/completion.rs | 834 +++++++++++++++-- .../move/crates/move-analyzer/src/symbols.rs | 52 +- .../tests/colon_colon_completion.exp | 851 ++++++++++++++++++ .../tests/colon_colon_completion.ide | 144 +++ .../tests/completion/sources/colon_colon.move | 62 ++ .../tests/completion/sources/dot.move | 4 +- .../move-analyzer/tests/ide_testsuite.rs | 4 +- .../src/expansion/path_expander.rs | 4 +- .../crates/move-compiler/src/shared/ide.rs | 12 +- 14 files changed, 1870 insertions(+), 110 deletions(-) create mode 100644 external-crates/move/crates/move-analyzer/tests/colon_colon_completion.exp create mode 100644 external-crates/move/crates/move-analyzer/tests/colon_colon_completion.ide create mode 100644 external-crates/move/crates/move-analyzer/tests/completion/sources/colon_colon.move diff --git a/Cargo.lock b/Cargo.lock index bbe62c92437e6..3fd86fa962aff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6882,6 +6882,7 @@ dependencies = [ "move-ir-types", "move-package", "move-symbol-pool", + "once_cell", "serde", "serde_json", "sha2 0.9.9", diff --git a/external-crates/move/Cargo.lock b/external-crates/move/Cargo.lock index 38787089bbf45..8fc7ec9946862 100644 --- a/external-crates/move/Cargo.lock +++ b/external-crates/move/Cargo.lock @@ -1542,6 +1542,7 @@ dependencies = [ "move-ir-types", "move-package", "move-symbol-pool", + "once_cell", "serde", "serde_json", "sha2", diff --git a/external-crates/move/crates/move-analyzer/Cargo.toml b/external-crates/move/crates/move-analyzer/Cargo.toml index a70912daea5d7..b1be5edc5fae5 100644 --- a/external-crates/move/crates/move-analyzer/Cargo.toml +++ b/external-crates/move/crates/move-analyzer/Cargo.toml @@ -25,6 +25,7 @@ move-compiler.workspace = true move-ir-types.workspace = true move-package.workspace = true move-symbol-pool.workspace = true +once_cell.workspace = true serde.workspace = true serde_json.workspace = true sha2.workspace = true 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 bb8418a84785d..5b20cd79bc16d 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,11 +12,10 @@ { "open": "\"", "close": "\"", "notIn": ["string", "comment"] }, { "open": "/*", "close": "*/", "notIn": ["string"] } ], - "brackets": [ + "brackets": ["{", "}"], ["[", "]"], - ["(", ")"], - ["<", ">"], + ["(", ")"] ], "comments": { "lineComment": "//", diff --git a/external-crates/move/crates/move-analyzer/src/compiler_info.rs b/external-crates/move/crates/move-analyzer/src/compiler_info.rs index 583ad6a464fb4..10ab08bcf91cf 100644 --- a/external-crates/move/crates/move-analyzer/src/compiler_info.rs +++ b/external-crates/move/crates/move-analyzer/src/compiler_info.rs @@ -12,6 +12,7 @@ pub struct CompilerInfo { pub macro_info: BTreeMap, pub expanded_lambdas: BTreeSet, pub dot_autocomplete_info: BTreeMap>, + pub path_autocomplete_info: BTreeMap, /// Locations of binders in enum variants that are expanded from an ellipsis (and should /// not be displayed in any way by the IDE) pub ellipsis_binders: BTreeSet, @@ -61,8 +62,8 @@ impl CompilerInfo { CI::IDEAnnotation::EllipsisMatchEntries(_) => { self.ellipsis_binders.insert(loc); } - CI::IDEAnnotation::PathAutocompleteInfo(_) => { - // TODO: Integrate this into the provided compiler information. + CI::IDEAnnotation::PathAutocompleteInfo(info) => { + self.path_autocomplete_info.insert(loc, *info); } } } diff --git a/external-crates/move/crates/move-analyzer/src/completion.rs b/external-crates/move/crates/move-analyzer/src/completion.rs index de0173b4938e2..cc563fea052cb 100644 --- a/external-crates/move/crates/move-analyzer/src/completion.rs +++ b/external-crates/move/crates/move-analyzer/src/completion.rs @@ -6,11 +6,13 @@ use crate::{ context::Context, symbols::{ self, mod_ident_to_ide_string, ret_type_to_ide_str, type_args_to_ide_string, - type_list_to_ide_string, type_to_ide_string, CursorContext, CursorDefinition, DefInfo, - FunType, PrecompiledPkgDeps, SymbolicatorRunner, Symbols, + type_list_to_ide_string, type_to_ide_string, ChainCompletionKind, CursorContext, + CursorDefinition, DefInfo, FunType, PrecompiledPkgDeps, SymbolicatorRunner, Symbols, + VariantInfo, }, utils, }; +use itertools::Itertools; use lsp_server::Request; use lsp_types::{ CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionParams, @@ -19,26 +21,50 @@ use lsp_types::{ use move_command_line_common::files::FileHash; use move_compiler::{ editions::Edition, - expansion::ast::{ModuleIdent_, Visibility}, + expansion::ast::{Address, ModuleIdent, ModuleIdent_, Visibility}, linters::LintLevel, naming::ast::{Type, Type_}, parser::{ - ast::{self as P, Ability_}, + ast::{self as P, Ability_, LeadingNameAccess, LeadingNameAccess_}, keywords::{BUILTINS, CONTEXTUAL_KEYWORDS, KEYWORDS, PRIMITIVE_TYPES}, lexer::{Lexer, Tok}, }, - shared::{ide::AutocompleteMethod, Identifier, Name}, + shared::{ + ide::{AliasAutocompleteInfo, AutocompleteMethod}, + Identifier, Name, NumericalAddress, + }, }; -use move_ir_types::location::Loc; +use move_ir_types::location::{sp, Loc}; use move_symbol_pool::Symbol; +use once_cell::sync::Lazy; + use std::{ - collections::{BTreeMap, HashSet}, + collections::{BTreeMap, BTreeSet, HashSet}, path::{Path, PathBuf}, sync::{Arc, Mutex}, }; use vfs::VfsPath; +/// Describes kind of the name access chain component. +enum ChainComponentKind { + Package(LeadingNameAccess), + Module(ModuleIdent), + Member(ModuleIdent, Symbol), +} + +/// Information about access chain component - its location and kind. +struct ChainComponentInfo { + loc: Loc, + kind: ChainComponentKind, +} + +impl ChainComponentInfo { + fn new(loc: Loc, kind: ChainComponentKind) -> Self { + Self { loc, kind } + } +} + /// Constructs an `lsp_types::CompletionItem` with the given `label` and `kind`. fn completion_item(label: &str, kind: CompletionItemKind) -> CompletionItem { CompletionItem { @@ -48,14 +74,14 @@ fn completion_item(label: &str, kind: CompletionItemKind) -> CompletionItem { } } -/// Return a list of completion items corresponding to each one of Move's keywords. +/// List of completion items corresponding to each one of Move's keywords. /// /// Currently, this does not filter keywords out based on whether they are valid at the completion /// request's cursor position, but in the future it ought to. For example, this function returns /// all specification language keywords, but in the future it should be modified to only do so /// within a spec block. -fn keywords() -> Vec { - KEYWORDS +static KEYWORD_COMPLETIONS: Lazy> = Lazy::new(|| { + let mut keywords = KEYWORDS .iter() .chain(CONTEXTUAL_KEYWORDS.iter()) .chain(PRIMITIVE_TYPES.iter()) @@ -67,24 +93,28 @@ fn keywords() -> Vec { }; completion_item(label, kind) }) - .collect() -} + .collect::>(); + keywords.extend(PRIMITIVE_TYPE_COMPLETIONS.clone()); + keywords +}); -/// Return a list of completion items of Move's primitive types -fn primitive_types() -> Vec { - PRIMITIVE_TYPES +/// List of completion items of Move's primitive types. +static PRIMITIVE_TYPE_COMPLETIONS: Lazy> = Lazy::new(|| { + let mut primitive_types = PRIMITIVE_TYPES .iter() .map(|label| completion_item(label, CompletionItemKind::KEYWORD)) - .collect() -} + .collect::>(); + primitive_types.push(completion_item("address", CompletionItemKind::KEYWORD)); + primitive_types +}); -/// Return a list of completion items corresponding to each one of Move's builtin functions. -fn builtins() -> Vec { +/// List of completion items corresponding to each one of Move's builtin functions. +static BUILTIN_COMPLETIONS: Lazy> = Lazy::new(|| { BUILTINS .iter() .map(|label| completion_item(label, CompletionItemKind::FUNCTION)) .collect() -} +}); /// Lexes the Move source file at the given path and returns a list of completion items /// corresponding to the non-keyword identifiers therein. @@ -197,7 +227,7 @@ fn context_specific_lbrace( } fn fun_def_info(symbols: &Symbols, mod_ident: ModuleIdent_, name: Symbol) -> Option<&DefInfo> { - let Some(mdef) = symbols + let Some(mod_defs) = symbols .file_mods .values() .flatten() @@ -206,7 +236,7 @@ fn fun_def_info(symbols: &Symbols, mod_ident: ModuleIdent_, name: Symbol) -> Opt return None; }; - let Some(fdef) = mdef.functions.get(&name) else { + let Some(fdef) = mod_defs.functions.get(&name) else { return None; }; symbols.def_info(&fdef.name_loc) @@ -230,8 +260,8 @@ fn lamda_snippet(sp!(_, ty): &Type, snippet_idx: &mut i32) -> Option { fn call_completion_item( mod_ident: &ModuleIdent_, - fun_type: &FunType, - method_name: &Symbol, + is_macro: bool, + method_name_opt: Option<&Symbol>, function_name: &Symbol, type_args: &[Type], arg_names: &[Name], @@ -244,13 +274,14 @@ fn call_completion_item( type_list_to_ide_string(arg_types, /* verbose */ false), ret_type_to_ide_str(ret_type, /* verbose */ false) ); - // we omit the first argument which is guaranteed to be there - // as this is a method and needs a receiver + // if it's a method call we omit the first argument which is guaranteed to be there as this is a + // method and needs a receiver + let omitted_arg_count = if method_name_opt.is_some() { 1 } else { 0 }; let mut snippet_idx = 0; let arg_snippet = arg_names .iter() .zip(arg_types) - .skip(1) + .skip(omitted_arg_count) .map(|(name, ty)| { lamda_snippet(ty, &mut snippet_idx).unwrap_or_else(|| { let mut arg_name = name.to_string(); @@ -263,11 +294,7 @@ fn call_completion_item( }) .collect::>() .join(", "); - let macro_suffix = if matches!(fun_type, FunType::Macro) { - "!" - } else { - "" - }; + let macro_suffix = if is_macro { "!" } else { "" }; let label_details = Some(CompletionItemLabelDetails { detail: Some(format!( " ({}::{})", @@ -277,18 +304,23 @@ fn call_completion_item( description: Some(sig_string), }); + let method_name = method_name_opt.unwrap_or(function_name); CompletionItem { - label: format!("{}{}()", method_name, macro_suffix), + label: format!("{method_name}{macro_suffix}()"), label_details, kind: Some(CompletionItemKind::METHOD), - insert_text: Some(format!("{}{}({})", method_name, macro_suffix, arg_snippet)), + insert_text: Some(format!("{method_name}{macro_suffix}({arg_snippet})")), insert_text_format: Some(InsertTextFormat::SNIPPET), ..Default::default() } } /// Handle dot auto-completion at a given position. -fn dot(symbols: &Symbols, use_fpath: &Path, position: &Position) -> Vec { +fn dot_completions( + symbols: &Symbols, + use_fpath: &Path, + position: &Position, +) -> Vec { let mut completions = vec![]; let Some(fhash) = symbols.file_hash(use_fpath) else { eprintln!("no dot completions due to missing file"); @@ -320,8 +352,8 @@ fn dot(symbols: &Symbols, use_fpath: &Path, position: &Position) -> Vec Vec Vec { + use ChainCompletionKind as CT; + + let mut completions = vec![]; + + let Some(mod_defs) = symbols + .file_mods + .values() + .flatten() + .find(|mdef| mdef.ident == prefix_mod_ident.value) + else { + return completions; + }; + + // list all members or only publicly visible ones + let mut same_module = false; + let mut same_package = false; + if let Some(cursor_mod_ident) = cursor.module { + if &cursor_mod_ident == prefix_mod_ident { + same_module = true; + } + if cursor_mod_ident.value.address == prefix_mod_ident.value.address { + same_package = true; + } + } + + if matches!(chain_kind, CT::Function) || matches!(chain_kind, CT::All) { + let fun_completions = mod_defs + .functions + .iter() + .filter_map(|(fname, fdef)| { + symbols + .def_info(&fdef.name_loc) + .map(|def_info| (fname, def_info)) + }) + .filter(|(_, def_info)| { + if let DefInfo::Function(_, visibility, ..) = def_info { + match visibility { + Visibility::Internal => same_module, + Visibility::Package(_) => same_package, + _ => true, + } + } else { + false + } + }) + .filter_map(|(fname, def_info)| { + if let DefInfo::Function( + _, + _, + fun_type, + _, + type_args, + arg_names, + arg_types, + ret_type, + _, + ) = def_info + { + Some(call_completion_item( + &prefix_mod_ident.value, + matches!(fun_type, FunType::Macro), + None, + fname, + type_args, + arg_names, + arg_types, + ret_type, + )) + } else { + None + } + }); + completions.extend(fun_completions); + } + + if matches!(chain_kind, CT::Type) || matches!(chain_kind, CT::All) { + completions.extend( + mod_defs + .structs + .keys() + .map(|sname| completion_item(sname, CompletionItemKind::STRUCT)), + ); + completions.extend( + mod_defs + .enums + .keys() + .map(|ename| completion_item(ename, CompletionItemKind::ENUM)), + ); + } + + if matches!(chain_kind, CT::All) && same_module { + completions.extend( + mod_defs + .constants + .keys() + .map(|cname| completion_item(cname, CompletionItemKind::CONSTANT)), + ); + } + + completions +} + +/// Returns completion item if a given name/alias identifies a valid member of a given module +/// available in the completion scope as if it was a single-length name chain. +fn single_name_member_completion( + symbols: &Symbols, + mod_ident: &ModuleIdent_, + member_alias: &Symbol, + member_name: &Symbol, + chain_kind: ChainCompletionKind, +) -> Option { + use ChainCompletionKind as CT; + + let Some(mod_defs) = symbols + .file_mods + .values() + .flatten() + .find(|mdef| mdef.ident == *mod_ident) + else { + return None; + }; + + // is it a function? + if let Some(fdef) = mod_defs.functions.get(member_name) { + if !(matches!(chain_kind, CT::Function) || matches!(chain_kind, CT::All)) { + return None; + } + let Some(DefInfo::Function(.., fun_type, _, type_args, arg_names, arg_types, ret_type, _)) = + symbols.def_info(&fdef.name_loc) + else { + return None; + }; + return Some(call_completion_item( + mod_ident, + matches!(fun_type, FunType::Macro), + None, + member_alias, + type_args, + arg_names, + arg_types, + ret_type, + )); + }; + + // is it a struct? + if mod_defs.structs.get(member_name).is_some() { + if !(matches!(chain_kind, CT::Type) || matches!(chain_kind, CT::All)) { + return None; + } + return Some(completion_item( + member_alias.as_str(), + CompletionItemKind::STRUCT, + )); + } + + // is it an enum? + if mod_defs.enums.get(member_name).is_some() { + if !(matches!(chain_kind, CT::Type) || matches!(chain_kind, CT::All)) { + return None; + } + return Some(completion_item( + member_alias.as_str(), + CompletionItemKind::ENUM, + )); + } + + // is it a const? + if mod_defs.constants.get(member_name).is_some() { + if !matches!(chain_kind, CT::All) { + return None; + } + return Some(completion_item( + member_alias.as_str(), + CompletionItemKind::CONSTANT, + )); + } + + None +} + +/// Returns completion items for all members of a given module as if they were single-length name +/// chains. +fn all_single_name_member_completions( + symbols: &Symbols, + members_info: &BTreeSet<(Symbol, ModuleIdent, Name)>, + chain_kind: ChainCompletionKind, +) -> Vec { + let mut completions = vec![]; + for (member_alias, sp!(_, mod_ident), member_name) in members_info { + let Some(member_completion) = single_name_member_completion( + symbols, + mod_ident, + member_alias, + &member_name.value, + chain_kind, + ) else { + continue; + }; + completions.push(member_completion); + } + completions +} + +/// Checks if a given module identifier represents a module in a package identifier by +/// `leading_name`. +fn is_pkg_mod_ident(mod_ident: &ModuleIdent_, leading_name: &LeadingNameAccess) -> bool { + match mod_ident.address { + Address::NamedUnassigned(name) => matches!(leading_name.value, + LeadingNameAccess_::Name(n) | LeadingNameAccess_::GlobalAddress(n) if name == n), + Address::Numerical { + name, + value, + name_conflict: _, + } => match leading_name.value { + LeadingNameAccess_::AnonymousAddress(addr) if addr == value.value => true, + LeadingNameAccess_::Name(addr_name) | LeadingNameAccess_::GlobalAddress(addr_name) + if Some(addr_name) == name => + { + true + } + _ => false, + }, + } +} + +/// Gets module identifiers for a given package identified by `leading_name`. +fn pkg_mod_identifiers( + symbols: &Symbols, + modules: &BTreeMap, + leading_name: &LeadingNameAccess, +) -> BTreeSet { + modules + .values() + .filter(|mod_ident| is_pkg_mod_ident(&mod_ident.value, leading_name)) + .copied() + .chain( + symbols + .file_mods + .values() + .flatten() + .map(|mdef| sp(mdef.name_loc, mdef.ident)) + .filter(|mod_ident| is_pkg_mod_ident(&mod_ident.value, leading_name)), + ) + .collect::>() +} + +/// Computes completion for a single enum variant. +fn variant_completion(symbols: &Symbols, vinfo: &VariantInfo) -> Option { + let Some(DefInfo::Variant(_, _, vname, is_positional, field_names, ..)) = + symbols.def_info.get(&vinfo.name.loc) + else { + return None; + }; + + let label = if field_names.is_empty() { + vname.to_string() + } else if *is_positional { + format!("{vname}()") + } else { + format!("{vname}{{}}") + }; + let field_snippet = field_names + .iter() + .enumerate() + .map(|(snippet_idx, fname)| { + if *is_positional { + format!("${{{}}}", snippet_idx + 1) + } else { + format!("${{{}:{}}}", snippet_idx + 1, fname) + } + }) + .collect::>() + .join(", "); + let insert_text = if *is_positional { + format!("{vname}({field_snippet})") + } else { + format!("{vname}{{{field_snippet}}}") + }; + + Some(CompletionItem { + label, + kind: Some(CompletionItemKind::ENUM_MEMBER), + insert_text: Some(insert_text), + insert_text_format: Some(InsertTextFormat::SNIPPET), + ..Default::default() + }) +} + +/// Computes completions for variants of a given enum. +fn all_variant_completions( + symbols: &Symbols, + mod_ident: &ModuleIdent, + datatype_name: Symbol, +) -> Vec { + let Some(mod_defs) = symbols + .file_mods + .values() + .flatten() + .find(|mdef| mdef.ident == mod_ident.value) + else { + return vec![]; + }; + + let Some(edef) = mod_defs.enums.get(&datatype_name) else { + return vec![]; + }; + + let Some(DefInfo::Enum(.., variants, _)) = symbols.def_info.get(&edef.name_loc) else { + return vec![]; + }; + + variants + .iter() + .filter_map(|vinfo| variant_completion(symbols, vinfo)) + .collect_vec() +} + +/// Computes completions for a given chain entry: `prev_kind` determines the kind of previous chain +/// component, and `chain_kind` contains information about the entity that the whole chain may +/// represent (e.g., a type of or a function). +fn name_chain_entry_completions( + symbols: &Symbols, + cursor: &CursorContext, + info: &AliasAutocompleteInfo, + prev_kind: ChainComponentKind, + chain_kind: ChainCompletionKind, + completions: &mut Vec, +) { + match prev_kind { + ChainComponentKind::Package(leading_name) => { + for mod_ident in pkg_mod_identifiers(symbols, &info.modules, &leading_name) { + completions.push(completion_item( + mod_ident.value.module.value().as_str(), + CompletionItemKind::MODULE, + )); + } + } + ChainComponentKind::Module(mod_ident) => { + completions.extend(name_chain_member_completions( + symbols, cursor, &mod_ident, chain_kind, + )); + } + ChainComponentKind::Member(mod_ident, member_name) => { + completions.extend(all_variant_completions(symbols, &mod_ident, member_name)); + } + } +} + +/// Computes the kind of the next chain component (based on what the previous one, represented by +/// `prev_kind` was). +fn next_name_chain_component_kind( + symbols: &Symbols, + info: &AliasAutocompleteInfo, + prev_kind: ChainComponentKind, + component_name: Name, +) -> Option { + match prev_kind { + ChainComponentKind::Package(leading_name) => { + pkg_mod_identifiers(symbols, &info.modules, &leading_name) + .into_iter() + .find(|mod_ident| mod_ident.value.module.value() == component_name.value) + .map(ChainComponentKind::Module) + } + ChainComponentKind::Module(mod_ident) => { + Some(ChainComponentKind::Member(mod_ident, component_name.value)) + } + ChainComponentKind::Member(_, _) => None, // no more "after" completions to be processed + } +} + +/// Walks down a name chain, looking for the relevant portion that contains the cursor. When it +/// finds, it calls to `name_chain_entry_completions` to compute and return the completions. +fn completions_for_name_chain_entry( + symbols: &Symbols, + cursor: &CursorContext, + info: &AliasAutocompleteInfo, + prev_info: ChainComponentInfo, + chain_kind: ChainCompletionKind, + path_entries: &[Name], + path_index: usize, + colon_colon_triggered: bool, + completions: &mut Vec, +) { + let ChainComponentInfo { + loc: prev_loc, + kind: prev_kind, + } = prev_info; + + let mut at_colon_colon = false; + if path_index == path_entries.len() { + // the only reason we would not return here is if we were at `::` which is past the location + // of the last path component + if colon_colon_triggered && cursor.loc.start() > prev_loc.end() { + at_colon_colon = true; + } else { + return; + } + } + + if !at_colon_colon { + // we are not at the last `::` but we may be at an intermediate one + if colon_colon_triggered + && path_index < path_entries.len() + && cursor.loc.start() > prev_loc.end() + && cursor.loc.end() <= path_entries[path_index].loc.start() + { + at_colon_colon = true; + } + } + + // we are at `::`, or at some component's identifier + if at_colon_colon || path_entries[path_index].loc.contains(&cursor.loc) { + name_chain_entry_completions(symbols, cursor, info, prev_kind, chain_kind, completions); + } else { + let component_name = path_entries[path_index]; + if let Some(next_kind) = + next_name_chain_component_kind(symbols, info, prev_kind, component_name) + { + completions_for_name_chain_entry( + symbols, + cursor, + info, + ChainComponentInfo::new(component_name.loc, next_kind), + chain_kind, + path_entries, + path_index + 1, + colon_colon_triggered, + completions, + ); + } + } +} + +/// Check if a given address represents a package within the current program. +fn is_package_address( + symbols: &Symbols, + info: &AliasAutocompleteInfo, + pkg_addr: NumericalAddress, +) -> bool { + if info.addresses.iter().any(|(_, a)| a == &pkg_addr) { + return true; + } + + symbols.file_mods.values().flatten().any(|mdef| { + matches!(mdef.ident.address, + Address::Numerical { value, .. } if value.value == pkg_addr) + }) +} + +/// Check if a given name represents a package within the current program. +fn is_package_name(symbols: &Symbols, info: &AliasAutocompleteInfo, pkg_name: Name) -> bool { + if info.addresses.contains_key(&pkg_name.value) { + return true; + } + + symbols + .file_mods + .values() + .flatten() + .map(|mdef| &mdef.ident) + .any(|mod_ident| match &mod_ident.address { + Address::NamedUnassigned(name) if name == &pkg_name => true, + Address::Numerical { + name: Some(name), .. + } if name == &pkg_name => true, + _ => false, + }) +} + +/// Get all packages that could be a target of auto-completion, whether they are part of +/// `AliasAutocompleteInfo` or not. +fn all_packages(symbols: &Symbols, info: &AliasAutocompleteInfo) -> BTreeSet { + let mut addresses = BTreeSet::new(); + for (n, a) in &info.addresses { + addresses.insert(n.to_string()); + addresses.insert(a.to_string()); + } + + symbols + .file_mods + .values() + .flatten() + .map(|mdef| &mdef.ident) + .for_each(|mod_ident| match &mod_ident.address { + Address::Numerical { name, value, .. } => { + if let Some(n) = name { + addresses.insert(n.to_string()); + } + addresses.insert(value.to_string()); + } + Address::NamedUnassigned(n) => { + addresses.insert(n.to_string()); + } + }); + + addresses +} + +/// Computes the kind of the fist chain component. +fn first_name_chain_component_kind( + symbols: &Symbols, + info: &AliasAutocompleteInfo, + leading_name: LeadingNameAccess, +) -> Option { + match leading_name.value { + LeadingNameAccess_::Name(n) => { + if is_package_name(symbols, info, n) { + Some(ChainComponentKind::Package(leading_name)) + } else if let Some(mod_ident) = info.modules.get(&n.value) { + Some(ChainComponentKind::Module(*mod_ident)) + } else if let Some((mod_ident, member_name)) = + info.members + .iter() + .find_map(|(alias_name, mod_ident, member_name)| { + if alias_name == &n.value { + Some((*mod_ident, member_name)) + } else { + None + } + }) + { + Some(ChainComponentKind::Member(mod_ident, member_name.value)) + } else { + None + } + } + LeadingNameAccess_::AnonymousAddress(addr) => { + if is_package_address(symbols, info, addr) { + Some(ChainComponentKind::Package(leading_name)) + } else { + None + } + } + LeadingNameAccess_::GlobalAddress(n) => { + // if leading name is global address then the first component can only be a + // package + if is_package_name(symbols, info, n) { + Some(ChainComponentKind::Package(leading_name)) + } else { + None + } + } + } +} + +/// Handle name chain auto-completion at a given position. The gist of this approach is to first +/// identify what the first component of the access chain represents (as it may be a package, module +/// or a member) and if the chain has other components, recursively process them in turn to either +/// - finish auto-completion if cursor is on a given component's identifier +/// - identify what the subsequent component represents and keep going +fn name_chain_completions( + symbols: &Symbols, + cursor: &CursorContext, + colon_colon_triggered: bool, +) -> (Vec, bool) { + eprintln!("looking for colon(s)"); + let mut completions = vec![]; + let mut only_custom_items = false; + let Some((sp!(_, chain), chain_kind)) = cursor.find_access_chain() else { + eprintln!("no access chain"); + return (completions, only_custom_items); + }; + + let (leading_name, path_entries) = match &chain { + P::NameAccessChain_::Single(entry) => ( + sp(entry.name.loc, LeadingNameAccess_::Name(entry.name)), + vec![], + ), + P::NameAccessChain_::Path(name_path) => ( + name_path.root.name, + name_path.entries.iter().map(|e| e.name).collect::>(), + ), + }; + + // there may be access chains for which there is not auto-completion info generated by the + // compiler but which still have to be handled (e.g., chains starting with numeric address) + let info = symbols + .compiler_info + .path_autocomplete_info + .get(&leading_name.loc) + .cloned() + .unwrap_or_else(AliasAutocompleteInfo::new); + + eprintln!("found access chain for auto-completion (adddreses: {}, modules: {}, members: {}, tparams: {}", + info.addresses.len(), info.modules.len(), info.members.len(), info.type_params.len()); + + // if we are auto-completing for an access chain, there is no need to include default completions + only_custom_items = true; + + if leading_name.loc.contains(&cursor.loc) { + // at first position of the chain suggest all packages that are available regardless of what + // the leading name represents, as a package always fits at that position, for example: + // OxCAFE::... + // some_name::... + // ::some_name + // + completions.extend( + all_packages(symbols, &info) + .iter() + .map(|n| completion_item(n.as_str(), CompletionItemKind::UNIT)), + ); + + // only if leading name is actually a name, modules or module members are a correct + // auto-completion in the first position + if let LeadingNameAccess_::Name(_) = &leading_name.value { + completions.extend( + info.modules + .keys() + .map(|n| completion_item(n.as_str(), CompletionItemKind::MODULE)), + ); + completions.extend(all_single_name_member_completions( + symbols, + &info.members, + chain_kind, + )); + if matches!(chain_kind, ChainCompletionKind::Type) { + completions.extend(PRIMITIVE_TYPE_COMPLETIONS.clone()); + completions.extend( + info.type_params + .iter() + .map(|t| completion_item(t.as_str(), CompletionItemKind::TYPE_PARAMETER)), + ); + } + } + } else if let Some(next_kind) = first_name_chain_component_kind(symbols, &info, leading_name) { + completions_for_name_chain_entry( + symbols, + cursor, + &info, + ChainComponentInfo::new(leading_name.loc, next_kind), + chain_kind, + &path_entries, + /* path_index */ 0, + colon_colon_triggered, + &mut completions, + ); + } + + eprintln!("found {} access chain completions", completions.len()); + + (completions, only_custom_items) +} + /// Handle context-specific auto-completion requests with no trigger character. fn context_specific_no_trigger( symbols: &Symbols, @@ -369,7 +1053,7 @@ fn context_specific_no_trigger( position: &Position, ) -> (Vec, bool) { eprintln!("looking for dot"); - let mut completions = dot(symbols, use_fpath, position); + let mut completions = dot_completions(symbols, use_fpath, position); eprintln!("dot found: {}", !completions.is_empty()); if !completions.is_empty() { // found dot completions - do not look for any other @@ -549,9 +1233,10 @@ pub fn on_completion_request( } let items = completions_with_context(context, ide_files_root, pkg_dependencies, &path, pos) .unwrap_or_default(); + let items_len = items.len(); let result = serde_json::to_value(items).expect("could not serialize completion response"); - eprintln!("about to send completion response"); + eprintln!("about to send completion response with {items_len} items"); let response = lsp_server::Response::new_ok(request.id.clone(), result); if let Err(err) = context .connection @@ -653,13 +1338,14 @@ fn compute_completion_items(symbols: &Symbols, path: &Path, pos: Position) -> Ve } } if !only_custom_items { + eprintln!("including identifiers"); let identifiers = identifiers(&file_source, symbols, path); items.extend(identifiers); } } else { // no file content - items.extend(keywords()); - items.extend(builtins()); + items.extend(KEYWORD_COMPLETIONS.clone()); + items.extend(BUILTIN_COMPLETIONS.clone()); } items } @@ -675,14 +1361,10 @@ fn cursor_completion_items( ) -> (Vec, bool) { let cursor_leader = get_cursor_token(file_source, &pos); match cursor_leader { - Some(Tok::Colon) => { - // TODO: sweep current scope and find types there. - (primitive_types(), false) - } // TODO: consider using `cursor.position` for this instead Some(Tok::Period) => { eprintln!("found period"); - let items = dot(symbols, path, &pos); + let items = dot_completions(symbols, path, &pos); let items_is_empty = items.is_empty(); eprintln!("found items: {}", !items_is_empty); // whether completions have been found for the dot or not @@ -692,60 +1374,32 @@ fn cursor_completion_items( // with u64 receiver being visible) (items, true) } - // TODO: consider using `cursor.position` for this instead Some(Tok::ColonColon) => { - // `.` or `::` must be followed by identifiers, which are added to the completion items - // below. - // TODO: consider the cursor and see if we can handle a path there - match &cursor.position { - symbols::CursorPosition::Exp(sp!(_, P::Exp_::Name(name))) => { - match &name.value { - P::NameAccessChain_::Single(_name) => { - // This is technically unreachable because we wouldn't be at a `::` - (vec![], false) - } - P::NameAccessChain_::Path(path) => { - let P::NamePath { - root, - entries, - is_incomplete: _, - } = path; - if root.name.loc.contains(&cursor.loc) { - // This is technically unreachable because we wouldn't be at a `::` - (vec![], false) - } else { - for entry in entries { - if entry.name.loc.contains(&cursor.loc) { - // TODO: figure out what the name parts refers to, look it - // up in typing, and go from there. - return (vec![], false); - } - } - (vec![], false) - } - } - } - } - _ => (vec![], false), - } + name_chain_completions(symbols, cursor, /* colon_colon_triggered */ true) } // Carve out to suggest UID for struct with key ability Some(Tok::LBrace) => ( context_specific_lbrace(symbols, cursor).unwrap_or_default(), true, ), + // TODO: should we handle auto-completion on `:`? If we model our support after + // rust-analyzer then it does not do this - it starts auto-completing types after the first + // character beyond `:` is typed _ => { eprintln!("no relevant cursor leader"); let mut items = vec![]; let mut only_custom_items = false; - if let symbols::CursorPosition::Exp(sp!(_, P::Exp_::Name(_name))) = &cursor.position { - // TODO: match on the name and use provided compiler info to resolve this - // (see PR 18108 for more details on that information) + let (path_items, path_custom) = + name_chain_completions(symbols, cursor, /* colon_colon_triggered */ false); + items.extend(path_items); + only_custom_items |= path_custom; + if !only_custom_items { + eprintln!("checking default items"); + let (default_items, default_custom) = + default_items(symbols, path, file_source, pos); + items.extend(default_items); + only_custom_items |= default_custom; } - eprintln!("checking default items"); - let (default_items, default_custom) = default_items(symbols, path, file_source, pos); - items.extend(default_items); - only_custom_items |= default_custom; (items, only_custom_items) } } @@ -764,8 +1418,8 @@ fn default_items( context_specific_no_trigger(symbols, path, file_source, &pos); let mut items = custom_items; if !only_custom_items { - items.extend(keywords()); - items.extend(builtins()); + items.extend(KEYWORD_COMPLETIONS.clone()); + items.extend(BUILTIN_COMPLETIONS.clone()); } (items, only_custom_items) } diff --git a/external-crates/move/crates/move-analyzer/src/symbols.rs b/external-crates/move/crates/move-analyzer/src/symbols.rs index cff3cac4793d9..0c84122b67ab2 100644 --- a/external-crates/move/crates/move-analyzer/src/symbols.rs +++ b/external-crates/move/crates/move-analyzer/src/symbols.rs @@ -153,9 +153,9 @@ pub enum FunType { #[derive(Debug, Clone, Eq, PartialEq)] pub struct VariantInfo { - name: Symbol, - empty: bool, - positional: bool, + pub name: Name, + pub empty: bool, + pub positional: bool, } /// Information about a definition of some identifier #[derive(Debug, Clone, Eq, PartialEq)] @@ -394,6 +394,13 @@ pub struct CursorContext { pub loc: Loc, } +#[derive(Clone, Debug, Copy)] +pub enum ChainCompletionKind { + Type, + Function, + All, +} + impl CursorContext { fn new(loc: Loc) -> Self { CursorContext { @@ -403,6 +410,43 @@ impl CursorContext { loc, } } + + /// Returns access chain at cursor position (if any) along with the information of what the chain's + /// auto-completed target kind should be + pub fn find_access_chain(&self) -> Option<(P::NameAccessChain, ChainCompletionKind)> { + // TODO: handle access chains in uses and attributes + use ChainCompletionKind as CT; + use CursorPosition as CP; + let chain_info = match &self.position { + CP::Exp(sp!(_, exp)) => match exp { + P::Exp_::Name(chain) => Some((chain.clone(), CT::All)), + P::Exp_::Call(chain, _) => Some((chain.clone(), CT::Function)), + P::Exp_::Pack(chain, _) => Some((chain.clone(), CT::Type)), + _ => None, + }, + CP::Binding(sp!(_, bind)) => { + if let P::Bind_::Unpack(chain, _) = bind { + Some((*(chain.clone()), CT::Type)) + } else { + None + } + } + CP::Type(sp!(_, ty)) => { + if let P::Type_::Apply(chain) = ty { + Some((*(chain.clone()), CT::Type)) + } else { + None + } + } + _ => None, + }; + if let Some((c, _)) = &chain_info { + if c.loc.contains(&self.loc) { + return chain_info; + } + } + None + } } #[derive(Clone, Debug)] @@ -2157,7 +2201,7 @@ fn get_mod_outer_defs( }; let field_names = field_defs.iter().map(|f| sp(f.loc, f.name)).collect(); def_info_variants.push(VariantInfo { - name: *vname, + name: sp(vname_loc, *vname), empty: field_defs.is_empty(), positional, }); diff --git a/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.exp b/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.exp new file mode 100644 index 0000000000000..a7e12503b1a2b --- /dev/null +++ b/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.exp @@ -0,0 +1,851 @@ +== colon_colon.move ======================================================== +-- test 0 ------------------- +use line: 15, use_col: 68 +Struct 'CompletionStruct' +Struct 'SomeStruct' +Enum 'SomeEnum' +Enum 'TargEnum' + +-- test 1 ------------------- +use line: 21, use_col: 22 +Unit '0x1' +Unit '0xA' +Unit '0xCAFE' +Unit 'Completion' +Unit 'std' +Module 'CC' +Module 'Self' +Module 'option' +Module 'vector' +Struct 'CompletionStruct' +Struct 'Option' +Enum 'SE' +Constant 'SOME_CONST' +Enum 'SomeEnum' +Struct 'SomeStruct' +Enum 'TargEnum' +Method 'complete_chains()' + INSERT TEXT: 'complete_chains(${1:s})' + TARGET : '(Completion::colon_colon::complete_chains)' + TYPE : 'fun (SomeStruct)' +Method 'multi_colon_colon()' + INSERT TEXT: 'multi_colon_colon()' + TARGET : '(Completion::colon_colon::multi_colon_colon)' + TYPE : 'fun ()' +Method 'one_colon_colon()' + INSERT TEXT: 'one_colon_colon()' + TARGET : '(Completion::colon_colon::one_colon_colon)' + TYPE : 'fun ()' +Method 'sbar()' + INSERT TEXT: 'sbar(${1:_param1}, ${2:_param2})' + TARGET : '(Completion::colon_colon::sbar)' + TYPE : 'fun (u64, SomeStruct)' +Method 'sbaz()' + INSERT TEXT: 'sbaz()' + TARGET : '(Completion::colon_colon::sbaz)' + TYPE : 'fun ()' +Method 'single_ident()' + INSERT TEXT: 'single_ident()' + TARGET : '(Completion::colon_colon::single_ident)' + TYPE : 'fun ()' +Method 'targ_chain()' + INSERT TEXT: 'targ_chain()' + TARGET : '(Completion::colon_colon::targ_chain)' + TYPE : 'fun ()' +Method 'targ_type()' + INSERT TEXT: 'targ_type(${1:p})' + TARGET : '(Completion::colon_colon::targ_type)' + TYPE : 'fun (SOME_TYPE)' + +-- test 2 ------------------- +use line: 21, use_col: 34 +Module 'colon_colon' +Module 'dot' +Module 'init' +Module 'init_otw' +Module 'macro_dot' +Module 'object' +Module 'other_mod_dot' + +-- test 3 ------------------- +use line: 21, use_col: 47 +Method 'complete_chains()' + INSERT TEXT: 'complete_chains(${1:s})' + TARGET : '(Completion::colon_colon::complete_chains)' + TYPE : 'fun (SomeStruct)' +Method 'multi_colon_colon()' + INSERT TEXT: 'multi_colon_colon()' + TARGET : '(Completion::colon_colon::multi_colon_colon)' + TYPE : 'fun ()' +Method 'one_colon_colon()' + INSERT TEXT: 'one_colon_colon()' + TARGET : '(Completion::colon_colon::one_colon_colon)' + TYPE : 'fun ()' +Method 'sbar()' + INSERT TEXT: 'sbar(${1:_param1}, ${2:_param2})' + TARGET : '(Completion::colon_colon::sbar)' + TYPE : 'fun (u64, SomeStruct)' +Method 'sbaz()' + INSERT TEXT: 'sbaz()' + TARGET : '(Completion::colon_colon::sbaz)' + TYPE : 'fun ()' +Method 'single_ident()' + INSERT TEXT: 'single_ident()' + TARGET : '(Completion::colon_colon::single_ident)' + TYPE : 'fun ()' +Method 'targ_chain()' + INSERT TEXT: 'targ_chain()' + TARGET : '(Completion::colon_colon::targ_chain)' + TYPE : 'fun ()' +Method 'targ_type()' + INSERT TEXT: 'targ_type(${1:p})' + TARGET : '(Completion::colon_colon::targ_type)' + TYPE : 'fun (SOME_TYPE)' +Struct 'CompletionStruct' +Struct 'SomeStruct' +Enum 'SomeEnum' +Enum 'TargEnum' +Constant 'SOME_CONST' + +-- test 4 ------------------- +use line: 21, use_col: 57 +EnumMember 'SomeNamedVariant{}' + INSERT TEXT: 'SomeNamedVariant{${1:name1}, ${2:name2}}' +EnumMember 'SomePositionalVariant()' + INSERT TEXT: 'SomePositionalVariant(${1}, ${2})' +EnumMember 'SomeVariant' + INSERT TEXT: 'SomeVariant{}' + +-- test 5 ------------------- +use line: 22, use_col: 11 +Unit '0x1' +Unit '0xA' +Unit '0xCAFE' +Unit 'Completion' +Unit 'std' + +-- test 6 ------------------- +use line: 22, use_col: 46 +EnumMember 'SomeNamedVariant{}' + INSERT TEXT: 'SomeNamedVariant{${1:name1}, ${2:name2}}' +EnumMember 'SomePositionalVariant()' + INSERT TEXT: 'SomePositionalVariant(${1}, ${2})' +EnumMember 'SomeVariant' + INSERT TEXT: 'SomeVariant{}' + +-- test 7 ------------------- +use line: 23, use_col: 25 +Method 'aliased()' + INSERT TEXT: 'aliased()' + TARGET : '(Completion::dot::aliased)' + TYPE : 'fun ()' +Method 'bar()' + INSERT TEXT: 'bar(${1:s}, ${2:_param1}, ${3:_param2})' + TARGET : '(Completion::dot::bar)' + TYPE : 'fun (SomeStruct, u64, T): SomeStruct' +Method 'foo()' + INSERT TEXT: 'foo(${1:_s})' + TARGET : '(Completion::dot::foo)' + TYPE : 'fun (SomeStruct)' +Method 'shadowed()' + INSERT TEXT: 'shadowed()' + TARGET : '(Completion::dot::shadowed)' + TYPE : 'fun ()' + +-- test 8 ------------------- +use line: 24, use_col: 9 +Unit '0x1' +Unit '0xA' +Unit '0xCAFE' +Unit 'Completion' +Unit 'std' + +-- test 9 ------------------- +use line: 24, use_col: 40 +EnumMember 'SomeNamedVariant{}' + INSERT TEXT: 'SomeNamedVariant{${1:name1}, ${2:name2}}' +EnumMember 'SomePositionalVariant()' + INSERT TEXT: 'SomePositionalVariant(${1}, ${2})' +EnumMember 'SomeVariant' + INSERT TEXT: 'SomeVariant{}' + +-- test 10 ------------------- +use line: 25, use_col: 9 +Unit '0x1' +Unit '0xA' +Unit '0xCAFE' +Unit 'Completion' +Unit 'std' +Module 'CC' +Module 'Self' +Module 'option' +Module 'vector' +Method 'complete_chains()' + INSERT TEXT: 'complete_chains(${1:s})' + TARGET : '(Completion::colon_colon::complete_chains)' + TYPE : 'fun (SomeStruct)' +Method 'multi_colon_colon()' + INSERT TEXT: 'multi_colon_colon()' + TARGET : '(Completion::colon_colon::multi_colon_colon)' + TYPE : 'fun ()' +Method 'one_colon_colon()' + INSERT TEXT: 'one_colon_colon()' + TARGET : '(Completion::colon_colon::one_colon_colon)' + TYPE : 'fun ()' +Method 'sbar()' + INSERT TEXT: 'sbar(${1:_param1}, ${2:_param2})' + TARGET : '(Completion::colon_colon::sbar)' + TYPE : 'fun (u64, SomeStruct)' +Method 'sbaz()' + INSERT TEXT: 'sbaz()' + TARGET : '(Completion::colon_colon::sbaz)' + TYPE : 'fun ()' +Method 'single_ident()' + INSERT TEXT: 'single_ident()' + TARGET : '(Completion::colon_colon::single_ident)' + TYPE : 'fun ()' +Method 'targ_chain()' + INSERT TEXT: 'targ_chain()' + TARGET : '(Completion::colon_colon::targ_chain)' + TYPE : 'fun ()' +Method 'targ_type()' + INSERT TEXT: 'targ_type(${1:p})' + TARGET : '(Completion::colon_colon::targ_type)' + TYPE : 'fun (SOME_TYPE)' + +-- test 11 ------------------- +use line: 25, use_col: 17 +Method 'and!()' + INSERT TEXT: 'and!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::and)' + TYPE : 'fun <$T, $U>(Option, |$T| -> Option): Option' +Method 'and_ref!()' + INSERT TEXT: 'and_ref!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::and_ref)' + TYPE : 'fun <$T, $U>(&Option, |&$T| -> Option): Option' +Method 'borrow()' + INSERT TEXT: 'borrow(${1:t})' + TARGET : '(std::option::borrow)' + TYPE : 'fun (&Option): &Element' +Method 'borrow_mut()' + INSERT TEXT: 'borrow_mut(${1:t})' + TARGET : '(std::option::borrow_mut)' + TYPE : 'fun (&mut Option): &mut Element' +Method 'borrow_with_default()' + INSERT TEXT: 'borrow_with_default(${1:t}, ${2:default_ref})' + TARGET : '(std::option::borrow_with_default)' + TYPE : 'fun (&Option, &Element): &Element' +Method 'contains()' + INSERT TEXT: 'contains(${1:t}, ${2:e_ref})' + TARGET : '(std::option::contains)' + TYPE : 'fun (&Option, &Element): bool' +Method 'destroy!()' + INSERT TEXT: 'destroy!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::destroy)' + TYPE : 'fun <$T>(Option, |$T| -> ())' +Method 'destroy_none()' + INSERT TEXT: 'destroy_none(${1:t})' + TARGET : '(std::option::destroy_none)' + TYPE : 'fun (Option)' +Method 'destroy_or!()' + INSERT TEXT: 'destroy_or!(${1:o}, ${2:default})' + TARGET : '(std::option::destroy_or)' + TYPE : 'fun <$T>(Option, $T): $T' +Method 'destroy_some()' + INSERT TEXT: 'destroy_some(${1:t})' + TARGET : '(std::option::destroy_some)' + TYPE : 'fun (Option): Element' +Method 'destroy_with_default()' + INSERT TEXT: 'destroy_with_default(${1:t}, ${2:default})' + TARGET : '(std::option::destroy_with_default)' + TYPE : 'fun (Option, Element): Element' +Method 'do!()' + INSERT TEXT: 'do!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::do)' + TYPE : 'fun <$T>(Option, |$T| -> ())' +Method 'do_mut!()' + INSERT TEXT: 'do_mut!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::do_mut)' + TYPE : 'fun <$T>(&mut Option, |&mut $T| -> ())' +Method 'do_ref!()' + INSERT TEXT: 'do_ref!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::do_ref)' + TYPE : 'fun <$T>(&Option, |&$T| -> ())' +Method 'extract()' + INSERT TEXT: 'extract(${1:t})' + TARGET : '(std::option::extract)' + TYPE : 'fun (&mut Option): Element' +Method 'fill()' + INSERT TEXT: 'fill(${1:t}, ${2:e})' + TARGET : '(std::option::fill)' + TYPE : 'fun (&mut Option, Element)' +Method 'filter!()' + INSERT TEXT: 'filter!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::filter)' + TYPE : 'fun <$T>(Option, |&$T| -> bool): Option' +Method 'get_with_default()' + INSERT TEXT: 'get_with_default(${1:t}, ${2:default})' + TARGET : '(std::option::get_with_default)' + TYPE : 'fun (&Option, Element): Element' +Method 'is_none()' + INSERT TEXT: 'is_none(${1:t})' + TARGET : '(std::option::is_none)' + TYPE : 'fun (&Option): bool' +Method 'is_some()' + INSERT TEXT: 'is_some(${1:t})' + TARGET : '(std::option::is_some)' + TYPE : 'fun (&Option): bool' +Method 'is_some_and!()' + INSERT TEXT: 'is_some_and!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::is_some_and)' + TYPE : 'fun <$T>(&Option, |&$T| -> bool): bool' +Method 'map!()' + INSERT TEXT: 'map!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::map)' + TYPE : 'fun <$T, $U>(Option, |$T| -> $U): Option' +Method 'map_ref!()' + INSERT TEXT: 'map_ref!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::map_ref)' + TYPE : 'fun <$T, $U>(&Option, |&$T| -> $U): Option' +Method 'none()' + INSERT TEXT: 'none()' + TARGET : '(std::option::none)' + TYPE : 'fun (): Option' +Method 'or!()' + INSERT TEXT: 'or!(${1:o}, ${2:default})' + TARGET : '(std::option::or)' + TYPE : 'fun <$T>(Option, Option): Option' +Method 'some()' + INSERT TEXT: 'some(${1:e})' + TARGET : '(std::option::some)' + TYPE : 'fun (Element): Option' +Method 'swap()' + INSERT TEXT: 'swap(${1:t}, ${2:e})' + TARGET : '(std::option::swap)' + TYPE : 'fun (&mut Option, Element): Element' +Method 'swap_or_fill()' + INSERT TEXT: 'swap_or_fill(${1:t}, ${2:e})' + TARGET : '(std::option::swap_or_fill)' + TYPE : 'fun (&mut Option, Element): Option' +Method 'to_vec()' + INSERT TEXT: 'to_vec(${1:t})' + TARGET : '(std::option::to_vec)' + TYPE : 'fun (Option): vector' + +-- test 12 ------------------- +use line: 26, use_col: 20 +Method 'sha2_256()' + INSERT TEXT: 'sha2_256(${1:data})' + TARGET : '(std::hash::sha2_256)' + TYPE : 'fun (vector): vector' +Method 'sha3_256()' + INSERT TEXT: 'sha3_256(${1:data})' + TARGET : '(std::hash::sha3_256)' + TYPE : 'fun (vector): vector' + +-- test 13 ------------------- +use line: 27, use_col: 9 +Unit '0x1' +Unit '0xA' +Unit '0xCAFE' +Unit 'Completion' +Unit 'std' +Module 'CC' +Module 'Self' +Module 'option' +Module 'vector' +Struct 'CompletionStruct' +Struct 'Option' +Enum 'SE' +Constant 'SOME_CONST' +Enum 'SomeEnum' +Struct 'SomeStruct' +Enum 'TargEnum' +Method 'complete_chains()' + INSERT TEXT: 'complete_chains(${1:s})' + TARGET : '(Completion::colon_colon::complete_chains)' + TYPE : 'fun (SomeStruct)' +Method 'multi_colon_colon()' + INSERT TEXT: 'multi_colon_colon()' + TARGET : '(Completion::colon_colon::multi_colon_colon)' + TYPE : 'fun ()' +Method 'one_colon_colon()' + INSERT TEXT: 'one_colon_colon()' + TARGET : '(Completion::colon_colon::one_colon_colon)' + TYPE : 'fun ()' +Method 'sbar()' + INSERT TEXT: 'sbar(${1:_param1}, ${2:_param2})' + TARGET : '(Completion::colon_colon::sbar)' + TYPE : 'fun (u64, SomeStruct)' +Method 'sbaz()' + INSERT TEXT: 'sbaz()' + TARGET : '(Completion::colon_colon::sbaz)' + TYPE : 'fun ()' +Method 'single_ident()' + INSERT TEXT: 'single_ident()' + TARGET : '(Completion::colon_colon::single_ident)' + TYPE : 'fun ()' +Method 'targ_chain()' + INSERT TEXT: 'targ_chain()' + TARGET : '(Completion::colon_colon::targ_chain)' + TYPE : 'fun ()' +Method 'targ_type()' + INSERT TEXT: 'targ_type(${1:p})' + TARGET : '(Completion::colon_colon::targ_type)' + TYPE : 'fun (SOME_TYPE)' + +-- test 14 ------------------- +use line: 27, use_col: 23 +EnumMember 'SomeNamedVariant{}' + INSERT TEXT: 'SomeNamedVariant{${1:name1}, ${2:name2}}' +EnumMember 'SomePositionalVariant()' + INSERT TEXT: 'SomePositionalVariant(${1}, ${2})' +EnumMember 'SomeVariant' + INSERT TEXT: 'SomeVariant{}' + +-- test 15 ------------------- +use line: 28, use_col: 13 +Method 'complete_chains()' + INSERT TEXT: 'complete_chains(${1:s})' + TARGET : '(Completion::colon_colon::complete_chains)' + TYPE : 'fun (SomeStruct)' +Method 'multi_colon_colon()' + INSERT TEXT: 'multi_colon_colon()' + TARGET : '(Completion::colon_colon::multi_colon_colon)' + TYPE : 'fun ()' +Method 'one_colon_colon()' + INSERT TEXT: 'one_colon_colon()' + TARGET : '(Completion::colon_colon::one_colon_colon)' + TYPE : 'fun ()' +Method 'sbar()' + INSERT TEXT: 'sbar(${1:_param1}, ${2:_param2})' + TARGET : '(Completion::colon_colon::sbar)' + TYPE : 'fun (u64, SomeStruct)' +Method 'sbaz()' + INSERT TEXT: 'sbaz()' + TARGET : '(Completion::colon_colon::sbaz)' + TYPE : 'fun ()' +Method 'single_ident()' + INSERT TEXT: 'single_ident()' + TARGET : '(Completion::colon_colon::single_ident)' + TYPE : 'fun ()' +Method 'targ_chain()' + INSERT TEXT: 'targ_chain()' + TARGET : '(Completion::colon_colon::targ_chain)' + TYPE : 'fun ()' +Method 'targ_type()' + INSERT TEXT: 'targ_type(${1:p})' + TARGET : '(Completion::colon_colon::targ_type)' + TYPE : 'fun (SOME_TYPE)' + +-- test 16 ------------------- +use line: 29, use_col: 9 +Unit '0x1' +Unit '0xA' +Unit '0xCAFE' +Unit 'Completion' +Unit 'std' +Module 'CC' +Module 'Self' +Module 'option' +Module 'vector' +Struct 'CompletionStruct' +Struct 'Option' +Enum 'SE' +Constant 'SOME_CONST' +Enum 'SomeEnum' +Struct 'SomeStruct' +Enum 'TargEnum' +Method 'complete_chains()' + INSERT TEXT: 'complete_chains(${1:s})' + TARGET : '(Completion::colon_colon::complete_chains)' + TYPE : 'fun (SomeStruct)' +Method 'multi_colon_colon()' + INSERT TEXT: 'multi_colon_colon()' + TARGET : '(Completion::colon_colon::multi_colon_colon)' + TYPE : 'fun ()' +Method 'one_colon_colon()' + INSERT TEXT: 'one_colon_colon()' + TARGET : '(Completion::colon_colon::one_colon_colon)' + TYPE : 'fun ()' +Method 'sbar()' + INSERT TEXT: 'sbar(${1:_param1}, ${2:_param2})' + TARGET : '(Completion::colon_colon::sbar)' + TYPE : 'fun (u64, SomeStruct)' +Method 'sbaz()' + INSERT TEXT: 'sbaz()' + TARGET : '(Completion::colon_colon::sbaz)' + TYPE : 'fun ()' +Method 'single_ident()' + INSERT TEXT: 'single_ident()' + TARGET : '(Completion::colon_colon::single_ident)' + TYPE : 'fun ()' +Method 'targ_chain()' + INSERT TEXT: 'targ_chain()' + TARGET : '(Completion::colon_colon::targ_chain)' + TYPE : 'fun ()' +Method 'targ_type()' + INSERT TEXT: 'targ_type(${1:p})' + TARGET : '(Completion::colon_colon::targ_type)' + TYPE : 'fun (SOME_TYPE)' + +-- test 17 ------------------- +use line: 33, use_col: 33 +Unit '0x1' +Unit '0xA' +Unit '0xCAFE' +Unit 'Completion' +Unit 'std' +Module 'CC' +Module 'Self' +Module 'option' +Module 'vector' +Struct 'CompletionStruct' +Struct 'Option' +Enum 'SE' +Enum 'SomeEnum' +Struct 'SomeStruct' +Enum 'TargEnum' +Keyword 'u8' +Keyword 'u16' +Keyword 'u32' +Keyword 'u64' +Keyword 'u128' +Keyword 'u256' +Keyword 'bool' +Keyword 'vector' +Keyword 'address' + +-- test 18 ------------------- +use line: 33, use_col: 37 +Struct 'CompletionStruct' +Struct 'SomeStruct' +Enum 'SomeEnum' +Enum 'TargEnum' + +-- test 19 ------------------- +use line: 34, use_col: 31 +Unit '0x1' +Unit '0xA' +Unit '0xCAFE' +Unit 'Completion' +Unit 'std' +Keyword 'u8' +Keyword 'u16' +Keyword 'u32' +Keyword 'u64' +Keyword 'u128' +Keyword 'u256' +Keyword 'bool' +Keyword 'vector' +Keyword 'address' + +-- test 20 ------------------- +use line: 38, use_col: 9 +Unit '0x1' +Unit '0xA' +Unit '0xCAFE' +Unit 'Completion' +Unit 'std' +Module 'Self' +Module 'option' +Module 'vector' +Struct 'CompletionStruct' +Struct 'Option' +Constant 'SOME_CONST' +Enum 'SomeEnum' +Struct 'SomeStruct' +Enum 'TargEnum' +Method 'complete_chains()' + INSERT TEXT: 'complete_chains(${1:s})' + TARGET : '(Completion::colon_colon::complete_chains)' + TYPE : 'fun (SomeStruct)' +Method 'multi_colon_colon()' + INSERT TEXT: 'multi_colon_colon()' + TARGET : '(Completion::colon_colon::multi_colon_colon)' + TYPE : 'fun ()' +Method 'one_colon_colon()' + INSERT TEXT: 'one_colon_colon()' + TARGET : '(Completion::colon_colon::one_colon_colon)' + TYPE : 'fun ()' +Method 'sbar()' + INSERT TEXT: 'sbar(${1:_param1}, ${2:_param2})' + TARGET : '(Completion::colon_colon::sbar)' + TYPE : 'fun (u64, SomeStruct)' +Method 'sbaz()' + INSERT TEXT: 'sbaz()' + TARGET : '(Completion::colon_colon::sbaz)' + TYPE : 'fun ()' +Method 'single_ident()' + INSERT TEXT: 'single_ident()' + TARGET : '(Completion::colon_colon::single_ident)' + TYPE : 'fun ()' +Method 'targ_chain()' + INSERT TEXT: 'targ_chain()' + TARGET : '(Completion::colon_colon::targ_chain)' + TYPE : 'fun ()' +Method 'targ_type()' + INSERT TEXT: 'targ_type(${1:p})' + TARGET : '(Completion::colon_colon::targ_type)' + TYPE : 'fun (SOME_TYPE)' + +-- test 21 ------------------- +use line: 55, use_col: 26 +Struct 'CompletionStruct' +Struct 'SomeStruct' +Enum 'SomeEnum' +Enum 'TargEnum' + +-- test 22 ------------------- +use line: 55, use_col: 39 +EnumMember 'Variant{}' + INSERT TEXT: 'Variant{${1:field}}' + +-- test 23 ------------------- +use line: 59, use_col: 21 +Unit '0x1' +Unit '0xA' +Unit '0xCAFE' +Unit 'Completion' +Unit 'std' +Module 'Self' +Module 'option' +Module 'vector' +Struct 'CompletionStruct' +Struct 'Option' +Enum 'SomeEnum' +Struct 'SomeStruct' +Enum 'TargEnum' +Keyword 'u8' +Keyword 'u16' +Keyword 'u32' +Keyword 'u64' +Keyword 'u128' +Keyword 'u256' +Keyword 'bool' +Keyword 'vector' +Keyword 'address' +TypeParameter 'SOME_TYPE' + +-- test 24 ------------------- +use line: 21, use_col: 33 +Module 'colon_colon' +Module 'dot' +Module 'init' +Module 'init_otw' +Module 'macro_dot' +Module 'object' +Module 'other_mod_dot' + +-- test 25 ------------------- +use line: 21, use_col: 46 +Method 'complete_chains()' + INSERT TEXT: 'complete_chains(${1:s})' + TARGET : '(Completion::colon_colon::complete_chains)' + TYPE : 'fun (SomeStruct)' +Method 'multi_colon_colon()' + INSERT TEXT: 'multi_colon_colon()' + TARGET : '(Completion::colon_colon::multi_colon_colon)' + TYPE : 'fun ()' +Method 'one_colon_colon()' + INSERT TEXT: 'one_colon_colon()' + TARGET : '(Completion::colon_colon::one_colon_colon)' + TYPE : 'fun ()' +Method 'sbar()' + INSERT TEXT: 'sbar(${1:_param1}, ${2:_param2})' + TARGET : '(Completion::colon_colon::sbar)' + TYPE : 'fun (u64, SomeStruct)' +Method 'sbaz()' + INSERT TEXT: 'sbaz()' + TARGET : '(Completion::colon_colon::sbaz)' + TYPE : 'fun ()' +Method 'single_ident()' + INSERT TEXT: 'single_ident()' + TARGET : '(Completion::colon_colon::single_ident)' + TYPE : 'fun ()' +Method 'targ_chain()' + INSERT TEXT: 'targ_chain()' + TARGET : '(Completion::colon_colon::targ_chain)' + TYPE : 'fun ()' +Method 'targ_type()' + INSERT TEXT: 'targ_type(${1:p})' + TARGET : '(Completion::colon_colon::targ_type)' + TYPE : 'fun (SOME_TYPE)' +Struct 'CompletionStruct' +Struct 'SomeStruct' +Enum 'SomeEnum' +Enum 'TargEnum' +Constant 'SOME_CONST' + +-- test 26 ------------------- +use line: 21, use_col: 56 +EnumMember 'SomeNamedVariant{}' + INSERT TEXT: 'SomeNamedVariant{${1:name1}, ${2:name2}}' +EnumMember 'SomePositionalVariant()' + INSERT TEXT: 'SomePositionalVariant(${1}, ${2})' +EnumMember 'SomeVariant' + INSERT TEXT: 'SomeVariant{}' + +-- test 27 ------------------- +use line: 25, use_col: 16 +Method 'and!()' + INSERT TEXT: 'and!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::and)' + TYPE : 'fun <$T, $U>(Option, |$T| -> Option): Option' +Method 'and_ref!()' + INSERT TEXT: 'and_ref!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::and_ref)' + TYPE : 'fun <$T, $U>(&Option, |&$T| -> Option): Option' +Method 'borrow()' + INSERT TEXT: 'borrow(${1:t})' + TARGET : '(std::option::borrow)' + TYPE : 'fun (&Option): &Element' +Method 'borrow_mut()' + INSERT TEXT: 'borrow_mut(${1:t})' + TARGET : '(std::option::borrow_mut)' + TYPE : 'fun (&mut Option): &mut Element' +Method 'borrow_with_default()' + INSERT TEXT: 'borrow_with_default(${1:t}, ${2:default_ref})' + TARGET : '(std::option::borrow_with_default)' + TYPE : 'fun (&Option, &Element): &Element' +Method 'contains()' + INSERT TEXT: 'contains(${1:t}, ${2:e_ref})' + TARGET : '(std::option::contains)' + TYPE : 'fun (&Option, &Element): bool' +Method 'destroy!()' + INSERT TEXT: 'destroy!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::destroy)' + TYPE : 'fun <$T>(Option, |$T| -> ())' +Method 'destroy_none()' + INSERT TEXT: 'destroy_none(${1:t})' + TARGET : '(std::option::destroy_none)' + TYPE : 'fun (Option)' +Method 'destroy_or!()' + INSERT TEXT: 'destroy_or!(${1:o}, ${2:default})' + TARGET : '(std::option::destroy_or)' + TYPE : 'fun <$T>(Option, $T): $T' +Method 'destroy_some()' + INSERT TEXT: 'destroy_some(${1:t})' + TARGET : '(std::option::destroy_some)' + TYPE : 'fun (Option): Element' +Method 'destroy_with_default()' + INSERT TEXT: 'destroy_with_default(${1:t}, ${2:default})' + TARGET : '(std::option::destroy_with_default)' + TYPE : 'fun (Option, Element): Element' +Method 'do!()' + INSERT TEXT: 'do!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::do)' + TYPE : 'fun <$T>(Option, |$T| -> ())' +Method 'do_mut!()' + INSERT TEXT: 'do_mut!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::do_mut)' + TYPE : 'fun <$T>(&mut Option, |&mut $T| -> ())' +Method 'do_ref!()' + INSERT TEXT: 'do_ref!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::do_ref)' + TYPE : 'fun <$T>(&Option, |&$T| -> ())' +Method 'extract()' + INSERT TEXT: 'extract(${1:t})' + TARGET : '(std::option::extract)' + TYPE : 'fun (&mut Option): Element' +Method 'fill()' + INSERT TEXT: 'fill(${1:t}, ${2:e})' + TARGET : '(std::option::fill)' + TYPE : 'fun (&mut Option, Element)' +Method 'filter!()' + INSERT TEXT: 'filter!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::filter)' + TYPE : 'fun <$T>(Option, |&$T| -> bool): Option' +Method 'get_with_default()' + INSERT TEXT: 'get_with_default(${1:t}, ${2:default})' + TARGET : '(std::option::get_with_default)' + TYPE : 'fun (&Option, Element): Element' +Method 'is_none()' + INSERT TEXT: 'is_none(${1:t})' + TARGET : '(std::option::is_none)' + TYPE : 'fun (&Option): bool' +Method 'is_some()' + INSERT TEXT: 'is_some(${1:t})' + TARGET : '(std::option::is_some)' + TYPE : 'fun (&Option): bool' +Method 'is_some_and!()' + INSERT TEXT: 'is_some_and!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::is_some_and)' + TYPE : 'fun <$T>(&Option, |&$T| -> bool): bool' +Method 'map!()' + INSERT TEXT: 'map!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::map)' + TYPE : 'fun <$T, $U>(Option, |$T| -> $U): Option' +Method 'map_ref!()' + INSERT TEXT: 'map_ref!(${1:o}, |${2}| ${3})' + TARGET : '(std::option::map_ref)' + TYPE : 'fun <$T, $U>(&Option, |&$T| -> $U): Option' +Method 'none()' + INSERT TEXT: 'none()' + TARGET : '(std::option::none)' + TYPE : 'fun (): Option' +Method 'or!()' + INSERT TEXT: 'or!(${1:o}, ${2:default})' + TARGET : '(std::option::or)' + TYPE : 'fun <$T>(Option, Option): Option' +Method 'some()' + INSERT TEXT: 'some(${1:e})' + TARGET : '(std::option::some)' + TYPE : 'fun (Element): Option' +Method 'swap()' + INSERT TEXT: 'swap(${1:t}, ${2:e})' + TARGET : '(std::option::swap)' + TYPE : 'fun (&mut Option, Element): Element' +Method 'swap_or_fill()' + INSERT TEXT: 'swap_or_fill(${1:t}, ${2:e})' + TARGET : '(std::option::swap_or_fill)' + TYPE : 'fun (&mut Option, Element): Option' +Method 'to_vec()' + INSERT TEXT: 'to_vec(${1:t})' + TARGET : '(std::option::to_vec)' + TYPE : 'fun (Option): vector' + +-- test 28 ------------------- +use line: 26, use_col: 19 +Method 'sha2_256()' + INSERT TEXT: 'sha2_256(${1:data})' + TARGET : '(std::hash::sha2_256)' + TYPE : 'fun (vector): vector' +Method 'sha3_256()' + INSERT TEXT: 'sha3_256(${1:data})' + TARGET : '(std::hash::sha3_256)' + TYPE : 'fun (vector): vector' + +-- test 29 ------------------- +use line: 27, use_col: 23 +EnumMember 'SomeNamedVariant{}' + INSERT TEXT: 'SomeNamedVariant{${1:name1}, ${2:name2}}' +EnumMember 'SomePositionalVariant()' + INSERT TEXT: 'SomePositionalVariant(${1}, ${2})' +EnumMember 'SomeVariant' + INSERT TEXT: 'SomeVariant{}' + +-- test 30 ------------------- +use line: 41, use_col: 20 +Module 'colon_colon' +Module 'dot' +Module 'init' +Module 'init_otw' +Module 'macro_dot' +Module 'object' +Module 'other_mod_dot' + +-- test 31 ------------------- +use line: 44, use_col: 43 +EnumMember 'SomeNamedVariant{}' + INSERT TEXT: 'SomeNamedVariant{${1:name1}, ${2:name2}}' +EnumMember 'SomePositionalVariant()' + INSERT TEXT: 'SomePositionalVariant(${1}, ${2})' +EnumMember 'SomeVariant' + INSERT TEXT: 'SomeVariant{}' + +-- test 32 ------------------- +use line: 55, use_col: 38 +EnumMember 'Variant{}' + INSERT TEXT: 'Variant{${1:field}}' + diff --git a/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.ide b/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.ide new file mode 100644 index 0000000000000..337947a444689 --- /dev/null +++ b/external-crates/move/crates/move-analyzer/tests/colon_colon_completion.ide @@ -0,0 +1,144 @@ +// Tests `::` completions +{ + "Completion": { + "project": "tests/completion", + "file_tests": { + "colon_colon.move": [ + // cursor on identifier + { + "use_line": 15, + "use_col": 68 + }, + { + "use_line": 21, + "use_col": 22 + }, + { + "use_line": 21, + "use_col": 34 + }, + { + "use_line": 21, + "use_col": 47 + }, + { + "use_line": 21, + "use_col": 57 + }, + { + "use_line": 22, + "use_col": 11 + }, + { + "use_line": 22, + "use_col": 46 + }, + { + "use_line": 23, + "use_col": 25 + }, + { + "use_line": 24, + "use_col": 9 + }, + { + "use_line": 24, + "use_col": 40 + }, + { + "use_line": 25, + "use_col": 9 + }, + { + "use_line": 25, + "use_col": 17 + }, + { + "use_line": 26, + "use_col": 20 + }, + { + "use_line": 27, + "use_col": 9 + }, + { + "use_line": 27, + "use_col": 23 + }, + { + "use_line": 28, + "use_col": 13 + }, + { + "use_line": 29, + "use_col": 9 + }, + { + "use_line": 33, + "use_col": 33 + }, + { + "use_line": 33, + "use_col": 37 + }, + { + "use_line": 34, + "use_col": 31 + }, + { + "use_line": 38, + "use_col": 9 + }, + { + "use_line": 55, + "use_col": 26 + }, + { + "use_line": 55, + "use_col": 39 + }, + { + "use_line": 59, + "use_col": 21 + }, + // cursor after `::` + { + "use_line": 21, + "use_col": 33 + }, + { + "use_line": 21, + "use_col": 46 + }, + { + "use_line": 21, + "use_col": 56 + }, + { + "use_line": 25, + "use_col": 16 + }, + { + "use_line": 26, + "use_col": 19 + }, + { + "use_line": 27, + "use_col": 23 + }, + { + "use_line": 41, + "use_col": 20 + }, + { + "use_line": 44, + "use_col": 43 + }, + { + "use_line": 55, + "use_col": 38 + } + ] + } + } +} diff --git a/external-crates/move/crates/move-analyzer/tests/completion/sources/colon_colon.move b/external-crates/move/crates/move-analyzer/tests/completion/sources/colon_colon.move new file mode 100644 index 0000000000000..ad31305e31116 --- /dev/null +++ b/external-crates/move/crates/move-analyzer/tests/completion/sources/colon_colon.move @@ -0,0 +1,62 @@ +module Completion::colon_colon { + + const SOME_CONST: u64 = 42; + + public struct SomeStruct has drop {} + + public enum SomeEnum has drop { + SomeVariant, + SomeNamedVariant { name1: u64, name2: u64}, + SomePositionalVariant(u64, u64), + } + + public struct CompletionStruct has drop {} + + public fun sbar(_param1: u64, _param2: Completion::colon_colon::SomeStruct) {} + public fun sbaz() {} + + public fun complete_chains(s: SomeStruct) { + use Completion::colon_colon as CC; + use Completion::colon_colon::SomeEnum as SE; + let _local = Completion::colon_colon::SomeEnum::SomeVariant; + ::Completion::colon_colon::SomeEnum::SomeVariant; + Completion::dot::shadowed(); + 0xCAFE::colon_colon::SomeEnum::SomeVariant; + option::none(); + std::hash::sha2_256(vector::empty()); + CC::SomeEnum::SomeVariant; + CC::sbar(42, s); + SE::SomeVariant; + SE::SomePositionalVariant(7, 42); + SE::SomeNamedVariant{name1: 7, name2: 42}; + + let _struct_vec: vector = vector::empty(); + let _prim_vec: vector = vector::empty(); + } + + public fun single_ident() { + C + } + public fun one_colon_colon() { + Completion:: + } + public fun multi_colon_colon() { + Completion::colon_colon::SomeEnum:: + } + + public enum TargEnum has drop { + Variant{field: T} + } + + public fun targ_chain() { + use Completion::colon_colon as CC; + // to test that variant for enums with explict type arguments auto-complete correctly + // and that type argument auto-completes correctly + CC::TargEnum::Variant{field: CC::SomeStruct{}}; + } + + public fun targ_type(p: SOME_TYPE) { + let _local: SOME_TYPE = p; + } + +} diff --git a/external-crates/move/crates/move-analyzer/tests/completion/sources/dot.move b/external-crates/move/crates/move-analyzer/tests/completion/sources/dot.move index e312112788da4..d53b13553dc46 100644 --- a/external-crates/move/crates/move-analyzer/tests/completion/sources/dot.move +++ b/external-crates/move/crates/move-analyzer/tests/completion/sources/dot.move @@ -10,7 +10,7 @@ module Completion::dot { s } - public fun simple() { + fun simple() { let s = SomeStruct { some_field: 42 }; s.; } @@ -21,7 +21,7 @@ module Completion::dot { s.; } - public fun shadowed() { + public(package) fun shadowed() { use fun bar as SomeStruct.foo; let s = SomeStruct { some_field: 42 }; s.; diff --git a/external-crates/move/crates/move-analyzer/tests/ide_testsuite.rs b/external-crates/move/crates/move-analyzer/tests/ide_testsuite.rs index 8f642d137ce2f..97d0d6d39b7a6 100644 --- a/external-crates/move/crates/move-analyzer/tests/ide_testsuite.rs +++ b/external-crates/move/crates/move-analyzer/tests/ide_testsuite.rs @@ -204,7 +204,9 @@ impl CompletionTest { )?; for i in items { writeln!(output, "{:?} '{}'", i.kind.unwrap(), i.label)?; - writeln!(output, " INSERT TEXT: '{}'", i.insert_text.unwrap())?; + if let Some(insert_text) = i.insert_text { + writeln!(output, " INSERT TEXT: '{}'", insert_text)?; + } if let Some(label_details) = i.label_details { if let Some(detail) = label_details.detail { writeln!(output, " TARGET : '{}'", detail.trim())?; diff --git a/external-crates/move/crates/move-compiler/src/expansion/path_expander.rs b/external-crates/move/crates/move-compiler/src/expansion/path_expander.rs index d97869a4580c2..20ae8acd5aa7e 100644 --- a/external-crates/move/crates/move-compiler/src/expansion/path_expander.rs +++ b/external-crates/move/crates/move-compiler/src/expansion/path_expander.rs @@ -1215,10 +1215,10 @@ impl PathExpander for LegacyPathExpander { if context.env.ide_mode() && context.is_source_definition { let mut info = AliasAutocompleteInfo::new(); for (name, addr) in context.named_address_mapping.unwrap().iter() { - info.addresses.insert((*name, *addr)); + info.addresses.insert(*name, *addr); } for (_, name, (_, mident)) in self.aliases.modules.iter() { - info.modules.insert((*name, *mident)); + info.modules.insert(*name, *mident); } for (_, name, (_, (mident, member))) in self.aliases.members.iter() { info.members.insert((*name, *mident, *member)); diff --git a/external-crates/move/crates/move-compiler/src/shared/ide.rs b/external-crates/move/crates/move-compiler/src/shared/ide.rs index 51f9738aacc6d..6816278d34ed8 100644 --- a/external-crates/move/crates/move-compiler/src/shared/ide.rs +++ b/external-crates/move/crates/move-compiler/src/shared/ide.rs @@ -82,9 +82,9 @@ pub struct DotAutocompleteInfo { #[derive(Default, Debug, Clone)] pub struct AliasAutocompleteInfo { /// Numerical addresses that are valid autocompletes - pub addresses: BTreeSet<(Symbol, NumericalAddress)>, + pub addresses: BTreeMap, /// Modules that are valid autocompletes - pub modules: BTreeSet<(Symbol, E::ModuleIdent)>, + pub modules: BTreeMap, /// Members that are valid autocompletes pub members: BTreeSet<(Symbol, E::ModuleIdent, Name)>, /// Type parameters that are valid autocompletes @@ -213,8 +213,8 @@ impl BTreeMap, ), ) -> Self { - let mut addresses: BTreeSet<(Symbol, NumericalAddress)> = BTreeSet::new(); - let mut modules: BTreeSet<(Symbol, E::ModuleIdent)> = BTreeSet::new(); + let mut addresses: BTreeMap = BTreeMap::new(); + let mut modules: BTreeMap = BTreeMap::new(); let mut members: BTreeSet<(Symbol, E::ModuleIdent, Name)> = BTreeSet::new(); let mut type_params: BTreeSet = BTreeSet::new(); @@ -224,10 +224,10 @@ impl { match entry { LeadingAccessEntry::Address(addr) => { - addresses.insert((*symbol, *addr)); + addresses.insert(*symbol, *addr); } LeadingAccessEntry::Module(mident) => { - modules.insert((*symbol, *mident)); + modules.insert(*symbol, *mident); } LeadingAccessEntry::Member(mident, name) => { members.insert((*symbol, *mident, *name)); From c1b1e1e74c82b950e8d531f1b84c605d1ea957ca Mon Sep 17 00:00:00 2001 From: "sui-merge-bot[bot]" <114704316+sui-merge-bot[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2024 17:26:38 -0700 Subject: [PATCH 040/232] Version Packages (#18865) This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @mysten/sui@1.4.0 ### Minor Changes - 4419234: Add setGasBudgetIfNotSet helper to Transaction class ## @mysten/create-dapp@0.3.14 ### Patch Changes - Updated dependencies [4419234] - @mysten/sui@1.4.0 - @mysten/dapp-kit@0.14.14 ## @mysten/dapp-kit@0.14.14 ### Patch Changes - Updated dependencies [4419234] - @mysten/sui@1.4.0 - @mysten/wallet-standard@0.12.14 - @mysten/zksend@0.10.3 ## @mysten/deepbook@0.8.13 ### Patch Changes - Updated dependencies [4419234] - @mysten/sui@1.4.0 ## @mysten/deepbook-v3@0.0.1 ### Patch Changes - Updated dependencies [4419234] - @mysten/sui@1.4.0 ## @mysten/enoki@0.3.13 ### Patch Changes - Updated dependencies [4419234] - @mysten/sui@1.4.0 - @mysten/zklogin@0.7.13 ## @mysten/graphql-transport@0.2.13 ### Patch Changes - Updated dependencies [4419234] - @mysten/sui@1.4.0 ## @mysten/kiosk@0.9.13 ### Patch Changes - Updated dependencies [4419234] - @mysten/sui@1.4.0 ## @mysten/suins-toolkit@0.5.13 ### Patch Changes - Updated dependencies [4419234] - @mysten/sui@1.4.0 ## @mysten/wallet-standard@0.12.14 ### Patch Changes - Updated dependencies [4419234] - @mysten/sui@1.4.0 ## @mysten/zklogin@0.7.13 ### Patch Changes - Updated dependencies [4419234] - @mysten/sui@1.4.0 ## @mysten/zksend@0.10.3 ### Patch Changes - Updated dependencies [4419234] - @mysten/sui@1.4.0 - @mysten/wallet-standard@0.12.14 Co-authored-by: github-actions[bot] --- .changeset/little-students-train.md | 5 ----- sdk/create-dapp/CHANGELOG.md | 8 ++++++++ sdk/create-dapp/package.json | 2 +- sdk/dapp-kit/CHANGELOG.md | 9 +++++++++ sdk/dapp-kit/package.json | 2 +- sdk/deepbook-v3/CHANGELOG.md | 8 ++++++++ sdk/deepbook-v3/package.json | 2 +- sdk/deepbook/CHANGELOG.md | 7 +++++++ sdk/deepbook/package.json | 2 +- sdk/enoki/CHANGELOG.md | 8 ++++++++ sdk/enoki/package.json | 2 +- sdk/graphql-transport/CHANGELOG.md | 7 +++++++ sdk/graphql-transport/package.json | 2 +- sdk/kiosk/CHANGELOG.md | 7 +++++++ sdk/kiosk/package.json | 2 +- sdk/suins-toolkit/CHANGELOG.md | 7 +++++++ sdk/suins-toolkit/package.json | 2 +- sdk/typescript/CHANGELOG.md | 6 ++++++ sdk/typescript/package.json | 2 +- sdk/typescript/src/version.ts | 2 +- sdk/wallet-standard/CHANGELOG.md | 7 +++++++ sdk/wallet-standard/package.json | 2 +- sdk/zklogin/CHANGELOG.md | 7 +++++++ sdk/zklogin/package.json | 2 +- sdk/zksend/CHANGELOG.md | 8 ++++++++ sdk/zksend/package.json | 2 +- 26 files changed, 102 insertions(+), 18 deletions(-) delete mode 100644 .changeset/little-students-train.md create mode 100644 sdk/deepbook-v3/CHANGELOG.md diff --git a/.changeset/little-students-train.md b/.changeset/little-students-train.md deleted file mode 100644 index e385945bec383..0000000000000 --- a/.changeset/little-students-train.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@mysten/sui': minor ---- - -Add setGasBudgetIfNotSet helper to Transaction class diff --git a/sdk/create-dapp/CHANGELOG.md b/sdk/create-dapp/CHANGELOG.md index 7dc995b870935..ec047f044c484 100644 --- a/sdk/create-dapp/CHANGELOG.md +++ b/sdk/create-dapp/CHANGELOG.md @@ -1,5 +1,13 @@ # @mysten/create-dapp +## 0.3.14 + +### Patch Changes + +- Updated dependencies [4419234] + - @mysten/sui@1.4.0 + - @mysten/dapp-kit@0.14.14 + ## 0.3.13 ### Patch Changes diff --git a/sdk/create-dapp/package.json b/sdk/create-dapp/package.json index 6659c4ad1b4f6..02ec5048f18f9 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.13", + "version": "0.3.14", "license": "Apache-2.0", "files": [ "CHANGELOG.md", diff --git a/sdk/dapp-kit/CHANGELOG.md b/sdk/dapp-kit/CHANGELOG.md index f135f0a73ec72..fc819753cf457 100644 --- a/sdk/dapp-kit/CHANGELOG.md +++ b/sdk/dapp-kit/CHANGELOG.md @@ -1,5 +1,14 @@ # @mysten/dapp-kit +## 0.14.14 + +### Patch Changes + +- Updated dependencies [4419234] + - @mysten/sui@1.4.0 + - @mysten/wallet-standard@0.12.14 + - @mysten/zksend@0.10.3 + ## 0.14.13 ### Patch Changes diff --git a/sdk/dapp-kit/package.json b/sdk/dapp-kit/package.json index e0434880360d3..765e78db7cdd7 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.13", + "version": "0.14.14", "license": "Apache-2.0", "files": [ "CHANGELOG.md", diff --git a/sdk/deepbook-v3/CHANGELOG.md b/sdk/deepbook-v3/CHANGELOG.md new file mode 100644 index 0000000000000..845fd5533ac3e --- /dev/null +++ b/sdk/deepbook-v3/CHANGELOG.md @@ -0,0 +1,8 @@ +# @mysten/deepbook-v3 + +## 0.0.1 + +### Patch Changes + +- Updated dependencies [4419234] + - @mysten/sui@1.4.0 diff --git a/sdk/deepbook-v3/package.json b/sdk/deepbook-v3/package.json index 4f9e493a7db12..65b04d5f4b021 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.0.0", + "version": "0.0.1", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", diff --git a/sdk/deepbook/CHANGELOG.md b/sdk/deepbook/CHANGELOG.md index af43215170667..5076f14779df8 100644 --- a/sdk/deepbook/CHANGELOG.md +++ b/sdk/deepbook/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/deepbook +## 0.8.13 + +### Patch Changes + +- Updated dependencies [4419234] + - @mysten/sui@1.4.0 + ## 0.8.12 ### Patch Changes diff --git a/sdk/deepbook/package.json b/sdk/deepbook/package.json index 13a37f92d87a8..f6d513daa57b9 100644 --- a/sdk/deepbook/package.json +++ b/sdk/deepbook/package.json @@ -2,7 +2,7 @@ "name": "@mysten/deepbook", "author": "Mysten Labs ", "description": "Sui Deepbook SDK", - "version": "0.8.12", + "version": "0.8.13", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", diff --git a/sdk/enoki/CHANGELOG.md b/sdk/enoki/CHANGELOG.md index 6eef2d643958d..a45096d61fd5f 100644 --- a/sdk/enoki/CHANGELOG.md +++ b/sdk/enoki/CHANGELOG.md @@ -1,5 +1,13 @@ # @mysten/enoki +## 0.3.13 + +### Patch Changes + +- Updated dependencies [4419234] + - @mysten/sui@1.4.0 + - @mysten/zklogin@0.7.13 + ## 0.3.12 ### Patch Changes diff --git a/sdk/enoki/package.json b/sdk/enoki/package.json index 0e3a43927fbcb..08cedda3d86b5 100644 --- a/sdk/enoki/package.json +++ b/sdk/enoki/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/enoki", - "version": "0.3.12", + "version": "0.3.13", "description": "TODO: Description", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/graphql-transport/CHANGELOG.md b/sdk/graphql-transport/CHANGELOG.md index f3bfe224d25a0..65aae50cc9222 100644 --- a/sdk/graphql-transport/CHANGELOG.md +++ b/sdk/graphql-transport/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/graphql-transport +## 0.2.13 + +### Patch Changes + +- Updated dependencies [4419234] + - @mysten/sui@1.4.0 + ## 0.2.12 ### Patch Changes diff --git a/sdk/graphql-transport/package.json b/sdk/graphql-transport/package.json index 0be11e4a40531..9c14c1e984065 100644 --- a/sdk/graphql-transport/package.json +++ b/sdk/graphql-transport/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/graphql-transport", - "version": "0.2.12", + "version": "0.2.13", "description": "A GraphQL transport to allow SuiClient to work with RPC 2.0", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/kiosk/CHANGELOG.md b/sdk/kiosk/CHANGELOG.md index 891253731a6ba..98ec1d3e946a2 100644 --- a/sdk/kiosk/CHANGELOG.md +++ b/sdk/kiosk/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/kiosk +## 0.9.13 + +### Patch Changes + +- Updated dependencies [4419234] + - @mysten/sui@1.4.0 + ## 0.9.12 ### Patch Changes diff --git a/sdk/kiosk/package.json b/sdk/kiosk/package.json index 695df31903cc8..0465ac5de25a2 100644 --- a/sdk/kiosk/package.json +++ b/sdk/kiosk/package.json @@ -2,7 +2,7 @@ "name": "@mysten/kiosk", "author": "Mysten Labs ", "description": "Sui Kiosk library", - "version": "0.9.12", + "version": "0.9.13", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", diff --git a/sdk/suins-toolkit/CHANGELOG.md b/sdk/suins-toolkit/CHANGELOG.md index 4acb88d72e4d5..3d8c11338da5f 100644 --- a/sdk/suins-toolkit/CHANGELOG.md +++ b/sdk/suins-toolkit/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/suins-toolkit +## 0.5.13 + +### Patch Changes + +- Updated dependencies [4419234] + - @mysten/sui@1.4.0 + ## 0.5.12 ### Patch Changes diff --git a/sdk/suins-toolkit/package.json b/sdk/suins-toolkit/package.json index 9b1317563af6d..1a639f7e0eae3 100644 --- a/sdk/suins-toolkit/package.json +++ b/sdk/suins-toolkit/package.json @@ -2,7 +2,7 @@ "name": "@mysten/suins-toolkit", "author": "Mysten Labs ", "description": "SuiNS TypeScript SDK", - "version": "0.5.12", + "version": "0.5.13", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", diff --git a/sdk/typescript/CHANGELOG.md b/sdk/typescript/CHANGELOG.md index 50a7a169572fd..ac6c4b5f51cc5 100644 --- a/sdk/typescript/CHANGELOG.md +++ b/sdk/typescript/CHANGELOG.md @@ -1,5 +1,11 @@ # @mysten/sui.js +## 1.4.0 + +### Minor Changes + +- 4419234: Add setGasBudgetIfNotSet helper to Transaction class + ## 1.3.1 ### Patch Changes diff --git a/sdk/typescript/package.json b/sdk/typescript/package.json index 3a36f2dbc3bb1..529a358e94bad 100644 --- a/sdk/typescript/package.json +++ b/sdk/typescript/package.json @@ -3,7 +3,7 @@ "author": "Mysten Labs ", "description": "Sui TypeScript API(Work in Progress)", "homepage": "https://sdk.mystenlabs.com", - "version": "1.3.1", + "version": "1.4.0", "license": "Apache-2.0", "sideEffects": false, "files": [ diff --git a/sdk/typescript/src/version.ts b/sdk/typescript/src/version.ts index 65c5ca24d0111..a5c0e46aa8ae3 100644 --- a/sdk/typescript/src/version.ts +++ b/sdk/typescript/src/version.ts @@ -3,5 +3,5 @@ // This file is generated by genversion.mjs. Do not edit it directly. -export const PACKAGE_VERSION = '1.3.1'; +export const PACKAGE_VERSION = '1.4.0'; export const TARGETED_RPC_VERSION = '1.31.0'; diff --git a/sdk/wallet-standard/CHANGELOG.md b/sdk/wallet-standard/CHANGELOG.md index 28dcd06a3c85e..fbc04b8c1b091 100644 --- a/sdk/wallet-standard/CHANGELOG.md +++ b/sdk/wallet-standard/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/wallet-standard +## 0.12.14 + +### Patch Changes + +- Updated dependencies [4419234] + - @mysten/sui@1.4.0 + ## 0.12.13 ### Patch Changes diff --git a/sdk/wallet-standard/package.json b/sdk/wallet-standard/package.json index 557e319fb512f..dad76b36663f2 100644 --- a/sdk/wallet-standard/package.json +++ b/sdk/wallet-standard/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/wallet-standard", - "version": "0.12.13", + "version": "0.12.14", "description": "A suite of standard utilities for implementing wallets based on the Wallet Standard.", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/zklogin/CHANGELOG.md b/sdk/zklogin/CHANGELOG.md index e1631060c8a43..48a610942550e 100644 --- a/sdk/zklogin/CHANGELOG.md +++ b/sdk/zklogin/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/zklogin +## 0.7.13 + +### Patch Changes + +- Updated dependencies [4419234] + - @mysten/sui@1.4.0 + ## 0.7.12 ### Patch Changes diff --git a/sdk/zklogin/package.json b/sdk/zklogin/package.json index dbf79fc4a363c..dafc163f96909 100644 --- a/sdk/zklogin/package.json +++ b/sdk/zklogin/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/zklogin", - "version": "0.7.12", + "version": "0.7.13", "description": "Utilities for interacting with zkLogin in Sui", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/zksend/CHANGELOG.md b/sdk/zksend/CHANGELOG.md index f651293603f8c..32120070eee18 100644 --- a/sdk/zksend/CHANGELOG.md +++ b/sdk/zksend/CHANGELOG.md @@ -1,5 +1,13 @@ # @mysten/zksend +## 0.10.3 + +### Patch Changes + +- Updated dependencies [4419234] + - @mysten/sui@1.4.0 + - @mysten/wallet-standard@0.12.14 + ## 0.10.2 ### Patch Changes diff --git a/sdk/zksend/package.json b/sdk/zksend/package.json index 7e4cef01284a2..ee92604ee8111 100644 --- a/sdk/zksend/package.json +++ b/sdk/zksend/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/zksend", - "version": "0.10.2", + "version": "0.10.3", "description": "TODO: Write Description", "license": "Apache-2.0", "author": "Mysten Labs ", From e8ca7ad1ec873307e55b5f65faeed48b107e48d9 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 29 Jul 2024 15:33:58 -0500 Subject: [PATCH 041/232] rest: implement client with support for all existing endpoints --- Cargo.toml | 2 +- .../src/{client.rs => client/mod.rs} | 126 ++-- crates/sui-rest-api/src/client/sdk.rs | 555 ++++++++++++++++++ crates/sui-rest-api/src/system.rs | 2 +- crates/sui-rest-api/src/transactions/mod.rs | 2 + 5 files changed, 596 insertions(+), 91 deletions(-) rename crates/sui-rest-api/src/{client.rs => client/mod.rs} (55%) create mode 100644 crates/sui-rest-api/src/client/sdk.rs diff --git a/Cargo.toml b/Cargo.toml index 06a78c6547ade..200ba684e93ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -416,7 +416,7 @@ rayon = "1.5.3" rcgen = "0.13" regex = "1.7.1" reqwest = { version = "0.12", default_features = false, features = [ - "blocking", + "http2", "json", "rustls-tls", ] } diff --git a/crates/sui-rest-api/src/client.rs b/crates/sui-rest-api/src/client/mod.rs similarity index 55% rename from crates/sui-rest-api/src/client.rs rename to crates/sui-rest-api/src/client/mod.rs index 15aa09dfcfc52..15000c16e971c 100644 --- a/crates/sui-rest-api/src/client.rs +++ b/crates/sui-rest-api/src/client/mod.rs @@ -1,9 +1,10 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::checkpoints::ListCheckpointsQueryParameters; +pub mod sdk; + use crate::transactions::ExecuteTransactionQueryParameters; -use anyhow::{anyhow, Result}; +use anyhow::Result; use sui_types::base_types::{ObjectID, SequenceNumber, SuiAddress}; use sui_types::crypto::AuthorityStrongQuorumSignInfo; use sui_types::effects::{TransactionEffects, TransactionEvents}; @@ -13,89 +14,65 @@ use sui_types::object::Object; use sui_types::transaction::Transaction; use sui_types::TypeTag; +use self::sdk::Response; + #[derive(Clone)] pub struct Client { - inner: reqwest::Client, - base_url: String, + inner: sdk::Client, } impl Client { - pub fn new>(base_url: S) -> Self { + pub fn new>(base_url: S) -> Self { Self { - inner: reqwest::Client::new(), - base_url: base_url.into(), + inner: sdk::Client::new(base_url.as_ref()).unwrap(), } } pub async fn get_latest_checkpoint(&self) -> Result { - let url = format!("{}/checkpoints", self.base_url); - - let query = ListCheckpointsQueryParameters { - limit: Some(1), - start: None, - direction: None, - }; - - let response = self - .inner - .get(url) - .query(&query) - .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) - .send() - .await?; - - let mut page: Vec = self.bcs(response).await?; - - page.pop() - .ok_or_else(|| anyhow!("server returned empty checkpoint list")) + self.inner + .get_latest_checkpoint() + .await + .map(Response::into_inner) + .map(Into::into) } pub async fn get_full_checkpoint( &self, checkpoint_sequence_number: CheckpointSequenceNumber, ) -> Result { - let url = format!( - "{}/checkpoints/{checkpoint_sequence_number}/full", - self.base_url - ); + let url = self + .inner + .url() + .join(&format!("checkpoints/{checkpoint_sequence_number}/full"))?; let response = self .inner + .client() .get(url) .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) .send() .await?; - self.bcs(response).await + self.inner.bcs(response).await.map(Response::into_inner) } pub async fn get_checkpoint_summary( &self, checkpoint_sequence_number: CheckpointSequenceNumber, ) -> Result { - let url = format!("{}/checkpoints/{checkpoint_sequence_number}", self.base_url); - - let response = self - .inner - .get(url) - .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) - .send() - .await?; - - self.bcs(response).await + self.inner + .get_checkpoint(checkpoint_sequence_number) + .await + .map(Response::into_inner) + .map(Into::into) } pub async fn get_object(&self, object_id: ObjectID) -> Result { - let url = format!("{}/objects/{object_id}", self.base_url); - - let response = self - .inner - .get(url) - .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) - .send() - .await?; - - self.bcs(response).await + self.inner + .get_object(object_id.into()) + .await + .map(Response::into_inner) + .map(Into::into) } pub async fn get_object_with_version( @@ -103,16 +80,11 @@ impl Client { object_id: ObjectID, version: SequenceNumber, ) -> Result { - let url = format!("{}/objects/{object_id}/version/{version}", self.base_url); - - let response = self - .inner - .get(url) - .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) - .send() - .await?; - - self.bcs(response).await + self.inner + .get_object_with_version(object_id.into(), version.into()) + .await + .map(Response::into_inner) + .map(Into::into) } pub async fn execute_transaction( @@ -126,7 +98,7 @@ impl Client { signatures: &'a [sui_types::signature::GenericSignature], } - let url = format!("{}/transactions", self.base_url); + let url = self.inner.url().join("transactions")?; let body = bcs::to_bytes(&SignedTransaction { transaction: &transaction.inner().intent_message.value, signatures: &transaction.inner().tx_signatures, @@ -134,6 +106,7 @@ impl Client { let response = self .inner + .client() .post(url) .query(parameters) .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) @@ -142,32 +115,7 @@ impl Client { .send() .await?; - self.bcs(response).await - } - - fn check_response(&self, response: reqwest::Response) -> Result { - if !response.status().is_success() { - let status = response.status(); - return Err(anyhow::anyhow!("request failed with status {status}")); - } - - Ok(response) - } - - #[allow(unused)] - async fn json(&self, response: reqwest::Response) -> Result { - let response = self.check_response(response)?; - - let json = response.json().await?; - Ok(json) - } - - async fn bcs(&self, response: reqwest::Response) -> Result { - let response = self.check_response(response)?; - - let bytes = response.bytes().await?; - let bcs = bcs::from_bytes(&bytes)?; - Ok(bcs) + self.inner.bcs(response).await.map(Response::into_inner) } } diff --git a/crates/sui-rest-api/src/client/sdk.rs b/crates/sui-rest-api/src/client/sdk.rs new file mode 100644 index 0000000000000..9db18f9d8cc61 --- /dev/null +++ b/crates/sui-rest-api/src/client/sdk.rs @@ -0,0 +1,555 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::anyhow; +use anyhow::Result; +use reqwest::header::HeaderValue; +use reqwest::StatusCode; +use reqwest::Url; +use sui_sdk2::types::Address; +use sui_sdk2::types::CheckpointData; +use sui_sdk2::types::CheckpointDigest; +use sui_sdk2::types::CheckpointSequenceNumber; +use sui_sdk2::types::EpochId; +use sui_sdk2::types::Object; +use sui_sdk2::types::ObjectId; +use sui_sdk2::types::SignedCheckpointSummary; +use sui_sdk2::types::SignedTransaction; +use sui_sdk2::types::StructTag; +use sui_sdk2::types::TransactionDigest; +use sui_sdk2::types::ValidatorCommittee; +use sui_sdk2::types::Version; +use tap::Pipe; + +use crate::accounts::AccountOwnedObjectInfo; +use crate::accounts::ListAccountOwnedObjectsQueryParameters; +use crate::checkpoints::ListCheckpointsQueryParameters; +use crate::coins::CoinInfo; +use crate::health::Threshold; +use crate::info::NodeInfo; +use crate::objects::DynamicFieldInfo; +use crate::objects::ListDynamicFieldsQueryParameters; +use crate::system::GasInfo; +use crate::system::ProtocolConfigResponse; +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::TransactionExecutionResponse; +use crate::transactions::TransactionResponse; +use crate::types::X_SUI_CHAIN; +use crate::types::X_SUI_CHAIN_ID; +use crate::types::X_SUI_CHECKPOINT_HEIGHT; +use crate::types::X_SUI_CURSOR; +use crate::types::X_SUI_EPOCH; +use crate::types::X_SUI_LOWEST_AVAILABLE_CHECKPOINT; +use crate::types::X_SUI_LOWEST_AVAILABLE_CHECKPOINT_OBJECTS; +use crate::types::X_SUI_TIMESTAMP_MS; +use crate::ExecuteTransactionQueryParameters; + +static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); + +#[derive(Clone, Debug)] +pub struct Client { + inner: reqwest::Client, + url: Box, // Boxed to save space +} + +impl Client { + pub fn new(url: &str) -> Result { + let mut url = Url::parse(url)?; + + if url.cannot_be_a_base() { + return Err(anyhow!("provided url '{url}' cannot be used as a base")); + } + + url.set_path("/v2/"); + + let inner = reqwest::ClientBuilder::new() + .user_agent(USER_AGENT) + .http2_prior_knowledge() + .build()?; + + Self { + inner, + url: Box::new(url), + } + .pipe(Ok) + } + + pub(super) fn client(&self) -> &reqwest::Client { + &self.inner + } + + pub(super) fn url(&self) -> &Url { + &self.url + } + + pub async fn node_info(&self) -> Result> { + let url = self.url.join("")?; + + let response = self + .inner + .get(url) + .header(reqwest::header::ACCEPT, crate::APPLICATION_JSON) + .send() + .await?; + + self.json(response).await + } + + pub async fn health_check(&self, threshold_seconds: Option) -> Result> { + let url = self.url.join("health")?; + let query = Threshold { threshold_seconds }; + + let response = self.inner.get(url).query(&query).send().await?; + + self.empty(response).await + } + + pub async fn get_coin_info(&self, coin_type: &StructTag) -> Result> { + let url = self.url.join(&format!("coins/{coin_type}"))?; + + let response = self + .inner + .get(url) + .header(reqwest::header::ACCEPT, crate::APPLICATION_JSON) + .send() + .await?; + + self.json(response).await + } + + pub async fn list_account_objects( + &self, + account: Address, + parameters: &ListAccountOwnedObjectsQueryParameters, + ) -> Result>> { + let url = self.url.join(&format!("account/{account}/objects"))?; + + let response = self + .inner + .get(url) + .query(parameters) + .header(reqwest::header::ACCEPT, crate::APPLICATION_JSON) + .send() + .await?; + + self.json(response).await + } + + pub async fn get_object(&self, object_id: ObjectId) -> Result> { + let url = self.url.join(&format!("objects/{object_id}"))?; + + let response = self + .inner + .get(url) + .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) + .send() + .await?; + + self.bcs(response).await + } + + pub async fn get_object_with_version( + &self, + object_id: ObjectId, + version: Version, + ) -> Result> { + let url = self + .url + .join(&format!("objects/{object_id}/version/{version}"))?; + + let response = self + .inner + .get(url) + .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) + .send() + .await?; + + self.bcs(response).await + } + + pub async fn list_dynamic_fields( + &self, + object_id: ObjectId, + parameters: &ListDynamicFieldsQueryParameters, + ) -> Result>> { + let url = self.url.join(&format!("objects/{object_id}"))?; + + let response = self + .inner + .get(url) + .query(parameters) + .header(reqwest::header::ACCEPT, crate::APPLICATION_JSON) + .send() + .await?; + + self.json(response).await + } + + pub async fn get_gas_info(&self) -> Result> { + let url = self.url.join("system/gas")?; + + let response = self + .inner + .get(url) + .header(reqwest::header::ACCEPT, crate::APPLICATION_JSON) + .send() + .await?; + + self.json(response).await + } + + pub async fn get_reference_gas_price(&self) -> Result { + self.get_gas_info() + .await + .map(Response::into_inner) + .map(|info| info.reference_gas_price) + } + + pub async fn get_current_protocol_config(&self) -> Result> { + let url = self.url.join("system/protocol")?; + + let response = self + .inner + .get(url) + .header(reqwest::header::ACCEPT, crate::APPLICATION_JSON) + .send() + .await?; + + self.json(response).await + } + + pub async fn get_protocol_config( + &self, + version: u64, + ) -> Result> { + let url = self.url.join(&format!("system/protocol/{version}"))?; + + let response = self + .inner + .get(url) + .header(reqwest::header::ACCEPT, crate::APPLICATION_JSON) + .send() + .await?; + + self.json(response).await + } + + pub async fn get_system_state_summary(&self) -> Result> { + let url = self.url.join("system")?; + + let response = self + .inner + .get(url) + .header(reqwest::header::ACCEPT, crate::APPLICATION_JSON) + .send() + .await?; + + self.json(response).await + } + + pub async fn get_current_committee(&self) -> Result> { + let url = self.url.join("system/committee")?; + + let response = self + .inner + .get(url) + .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) + .send() + .await?; + + self.bcs(response).await + } + + pub async fn get_committee(&self, epoch: EpochId) -> Result> { + let url = self.url.join(&format!("system/committee/{epoch}"))?; + + let response = self + .inner + .get(url) + .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) + .send() + .await?; + + self.bcs(response).await + } + + pub async fn get_checkpoint( + &self, + checkpoint_sequence_number: CheckpointSequenceNumber, + ) -> Result> { + let url = self + .url + .join(&format!("checkpoints/{checkpoint_sequence_number}"))?; + + let response = self + .inner + .get(url) + .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) + .send() + .await?; + + self.bcs(response).await + } + + pub async fn get_latest_checkpoint(&self) -> Result> { + let parameters = ListCheckpointsQueryParameters { + limit: Some(1), + start: None, + direction: None, + }; + + let (mut page, parts) = self.list_checkpoints(¶meters).await?.into_parts(); + + let checkpoint = page + .pop() + .ok_or_else(|| anyhow!("server returned empty checkpoint list"))?; + + Ok(Response::new(checkpoint, parts)) + } + + pub async fn list_checkpoints( + &self, + parameters: &ListCheckpointsQueryParameters, + ) -> Result>> { + let url = self.url.join("checkpoints")?; + + let response = self + .inner + .get(url) + .query(parameters) + .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) + .send() + .await?; + + self.bcs(response).await + } + + pub async fn get_full_checkpoint( + &self, + checkpoint_sequence_number: CheckpointSequenceNumber, + ) -> Result> { + let url = self + .url + .join(&format!("checkpoints/{checkpoint_sequence_number}/full"))?; + + let response = self + .inner + .get(url) + .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) + .send() + .await?; + + self.bcs(response).await + } + + pub async fn get_transaction( + &self, + transaction: &TransactionDigest, + ) -> Result> { + let url = self.url.join(&format!("transactions/{transaction}"))?; + + let response = self + .inner + .get(url) + .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) + .send() + .await?; + + self.bcs(response).await + } + + pub async fn list_transactions( + &self, + parameters: &ListTransactionsQueryParameters, + ) -> Result>> { + let url = self.url.join("transactions")?; + + let response = self + .inner + .get(url) + .query(parameters) + .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) + .send() + .await?; + + self.bcs(response).await + } + + pub async fn execute_transaction( + &self, + parameters: &ExecuteTransactionQueryParameters, + transaction: &SignedTransaction, + ) -> Result> { + let url = self.url.join("transactions")?; + + let body = bcs::to_bytes(transaction)?; + + let response = self + .inner + .post(url) + .query(parameters) + .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS) + .header(reqwest::header::CONTENT_TYPE, crate::APPLICATION_BCS) + .body(body) + .send() + .await?; + + self.bcs(response).await + } + + fn check_response( + &self, + response: reqwest::Response, + ) -> Result<(reqwest::Response, ResponseParts)> { + if !response.status().is_success() { + let status = response.status(); + return Err(anyhow::anyhow!("request failed with status {status}")); + } + + let parts = ResponseParts::from_reqwest_response(&response); + + Ok((response, parts)) + } + + async fn empty(&self, response: reqwest::Response) -> Result> { + let (_response, parts) = self.check_response(response)?; + Ok(Response::new((), parts)) + } + + async fn json( + &self, + response: reqwest::Response, + ) -> Result> { + let (response, parts) = self.check_response(response)?; + + let json = response.json().await?; + Ok(Response::new(json, parts)) + } + + pub(super) async fn bcs( + &self, + response: reqwest::Response, + ) -> Result> { + let (response, parts) = self.check_response(response)?; + + let bytes = response.bytes().await?; + let bcs = bcs::from_bytes(&bytes)?; + Ok(Response::new(bcs, parts)) + } +} + +#[derive(Debug)] +pub struct ResponseParts { + pub status: StatusCode, + pub chain_id: Option, + pub chain: Option, + pub epoch: Option, + pub checkpoint_height: Option, + pub timestamp_ms: Option, + pub lowest_available_checkpoint: Option, + pub lowest_available_checkpoint_objects: Option, + pub cursor: Option, + pub min_supported_protocol_version: Option, + pub max_supported_protocol_version: Option, +} + +impl ResponseParts { + fn from_reqwest_response(response: &reqwest::Response) -> Self { + let headers = response.headers(); + let status = response.status(); + let chain_id = headers + .get(X_SUI_CHAIN_ID) + .map(HeaderValue::as_bytes) + .and_then(|s| CheckpointDigest::from_base58(s).ok()); + let chain = headers + .get(X_SUI_CHAIN) + .and_then(|h| h.to_str().ok()) + .map(ToOwned::to_owned); + let epoch = headers + .get(X_SUI_EPOCH) + .and_then(|h| h.to_str().ok()) + .and_then(|s| s.parse().ok()); + let checkpoint_height = headers + .get(X_SUI_CHECKPOINT_HEIGHT) + .and_then(|h| h.to_str().ok()) + .and_then(|s| s.parse().ok()); + let timestamp_ms = headers + .get(X_SUI_TIMESTAMP_MS) + .and_then(|h| h.to_str().ok()) + .and_then(|s| s.parse().ok()); + let lowest_available_checkpoint = headers + .get(X_SUI_LOWEST_AVAILABLE_CHECKPOINT) + .and_then(|h| h.to_str().ok()) + .and_then(|s| s.parse().ok()); + let lowest_available_checkpoint_objects = headers + .get(X_SUI_LOWEST_AVAILABLE_CHECKPOINT_OBJECTS) + .and_then(|h| h.to_str().ok()) + .and_then(|s| s.parse().ok()); + let cursor = headers + .get(X_SUI_CURSOR) + .and_then(|h| h.to_str().ok()) + .map(ToOwned::to_owned); + let min_supported_protocol_version = headers + .get(X_SUI_MIN_SUPPORTED_PROTOCOL_VERSION) + .and_then(|h| h.to_str().ok()) + .and_then(|s| s.parse().ok()); + let max_supported_protocol_version = headers + .get(X_SUI_MAX_SUPPORTED_PROTOCOL_VERSION) + .and_then(|h| h.to_str().ok()) + .and_then(|s| s.parse().ok()); + + Self { + status, + chain_id, + chain, + epoch, + checkpoint_height, + timestamp_ms, + lowest_available_checkpoint, + lowest_available_checkpoint_objects, + cursor, + min_supported_protocol_version, + max_supported_protocol_version, + } + } +} + +#[derive(Debug)] +pub struct Response { + inner: T, + + parts: ResponseParts, +} + +impl Response { + pub fn new(inner: T, parts: ResponseParts) -> Self { + Self { inner, parts } + } + + pub fn inner(&self) -> &T { + &self.inner + } + + pub fn into_inner(self) -> T { + self.inner + } + + pub fn parts(&self) -> &ResponseParts { + &self.parts + } + + pub fn into_parts(self) -> (T, ResponseParts) { + (self.inner, self.parts) + } + + pub fn map(self, f: F) -> Response + where + F: FnOnce(T) -> U, + { + let (inner, state) = self.into_parts(); + Response::new(f(inner), state) + } +} diff --git a/crates/sui-rest-api/src/system.rs b/crates/sui-rest-api/src/system.rs index e358a895350e8..2a4fd9994b2bc 100644 --- a/crates/sui-rest-api/src/system.rs +++ b/crates/sui-rest-api/src/system.rs @@ -779,5 +779,5 @@ async fn get_gas_info( pub struct GasInfo { #[serde(with = "serde_with::As::")] #[schemars(with = "crate::_schemars::U64")] - reference_gas_price: u64, + pub reference_gas_price: u64, } diff --git a/crates/sui-rest-api/src/transactions/mod.rs b/crates/sui-rest-api/src/transactions/mod.rs index 5eab49a8897d6..26921104d8c58 100644 --- a/crates/sui-rest-api/src/transactions/mod.rs +++ b/crates/sui-rest-api/src/transactions/mod.rs @@ -2,8 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 mod execution; +pub use execution::EffectsFinality; pub use execution::ExecuteTransaction; pub use execution::ExecuteTransactionQueryParameters; +pub use execution::TransactionExecutionResponse; pub use execution::TransactionExecutor; use axum::extract::{Path, Query, State}; From be249a05b5b3cdc6d0bbc1d149a4ea79cba9e5eb Mon Sep 17 00:00:00 2001 From: Patrick Kuo Date: Wed, 31 Jul 2024 16:50:30 +0100 Subject: [PATCH 042/232] [bridge indexer] - indexer refactoring (#18761) ## Description Describe the changes or additions included in this PR. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --------- Co-authored-by: Lu Zhang <8418040+longbowlu@users.noreply.github.com> --- .../src/eth_bridge_indexer.rs | 374 ++++++++++++++++ crates/sui-bridge-indexer/src/eth_worker.rs | 263 ------------ .../sui-bridge-indexer/src/indexer_builder.rs | 402 ++++++++++++++++++ crates/sui-bridge-indexer/src/lib.rs | 13 +- crates/sui-bridge-indexer/src/main.rs | 111 +++-- .../src/sui_bridge_indexer.rs | 291 +++++++++++++ .../src/sui_checkpoint_ingestion.rs | 270 +----------- crates/sui-bridge-indexer/src/sui_worker.rs | 212 --------- 8 files changed, 1148 insertions(+), 788 deletions(-) create mode 100644 crates/sui-bridge-indexer/src/eth_bridge_indexer.rs delete mode 100644 crates/sui-bridge-indexer/src/eth_worker.rs create mode 100644 crates/sui-bridge-indexer/src/indexer_builder.rs create mode 100644 crates/sui-bridge-indexer/src/sui_bridge_indexer.rs delete mode 100644 crates/sui-bridge-indexer/src/sui_worker.rs diff --git a/crates/sui-bridge-indexer/src/eth_bridge_indexer.rs b/crates/sui-bridge-indexer/src/eth_bridge_indexer.rs new file mode 100644 index 0000000000000..ba83cf996382c --- /dev/null +++ b/crates/sui-bridge-indexer/src/eth_bridge_indexer.rs @@ -0,0 +1,374 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::str::FromStr; +use std::sync::Arc; +use std::time::Duration; + +use anyhow::{anyhow, Error}; +use async_trait::async_trait; +use ethers::prelude::Transaction; +use ethers::providers::{Http, Middleware, Provider}; +use ethers::types::{Address as EthAddress, Block, H256}; +use tokio::task::JoinHandle; +use tracing::info; + +use mysten_metrics::metered_channel::Receiver; +use mysten_metrics::{metered_channel, spawn_monitored_task}; +use sui_bridge::abi::{EthBridgeEvent, EthSuiBridgeEvents}; +use sui_bridge::eth_client::EthClient; +use sui_bridge::eth_syncer::EthSyncer; +use sui_bridge::metered_eth_provider::{new_metered_eth_provider, MeteredEthHttpProvier}; +use sui_bridge::metrics::BridgeMetrics; +use sui_bridge::retry_with_max_elapsed_time; +use sui_bridge::types::{EthEvent, EthLog, RawEthLog}; + +use crate::indexer_builder::{DataMapper, Datasource}; +use crate::latest_eth_syncer::LatestEthSyncer; +use crate::metrics::BridgeIndexerMetrics; +use crate::sui_bridge_indexer::PgBridgePersistent; +use crate::{ + BridgeDataSource, ProcessedTxnData, TokenTransfer, TokenTransferData, TokenTransferStatus, +}; + +type EthData = (EthLog, Block, Transaction); +type RawEthData = (RawEthLog, Block, Transaction); + +pub struct EthFinalizedDatasource { + bridge_address: EthAddress, + eth_rpc_url: String, + bridge_metrics: Arc, + indexer_metrics: BridgeIndexerMetrics, +} + +impl EthFinalizedDatasource { + pub fn new( + eth_sui_bridge_contract_address: String, + eth_rpc_url: String, + bridge_metrics: Arc, + indexer_metrics: BridgeIndexerMetrics, + ) -> Result { + let bridge_address = EthAddress::from_str(ð_sui_bridge_contract_address)?; + Ok(Self { + bridge_address, + eth_rpc_url, + bridge_metrics, + indexer_metrics, + }) + } +} + +#[async_trait] +impl Datasource for EthFinalizedDatasource { + async fn start_data_retrieval( + &self, + task_name: String, + starting_checkpoint: u64, + target_checkpoint: u64, + ) -> Result<(JoinHandle>, Receiver<(u64, Vec)>), Error> { + let eth_client = Arc::new( + EthClient::::new( + &self.eth_rpc_url, + HashSet::from_iter(vec![self.bridge_address]), + self.bridge_metrics.clone(), + ) + .await?, + ); + + let provider = Arc::new( + Provider::::try_from(&self.eth_rpc_url)? + .interval(std::time::Duration::from_millis(2000)), + ); + + info!("Starting from finalized block: {}", starting_checkpoint); + + let finalized_contract_addresses = + HashMap::from_iter(vec![(self.bridge_address, starting_checkpoint)]); + + let (task_handles, mut eth_events_rx, _) = + EthSyncer::new(eth_client, finalized_contract_addresses) + .run(self.bridge_metrics.clone()) + .await + .map_err(|e| anyhow!(format!("{e:?}")))?; + + let (data_sender, data_receiver) = metered_channel::channel( + 1000, + &mysten_metrics::get_metrics() + .unwrap() + .channel_inflight + .with_label_values(&[&task_name]), + ); + let indexer_metrics = self.indexer_metrics.clone(); + let handle = spawn_monitored_task!(async { + 'outer: while let Some((_, _, logs)) = eth_events_rx.recv().await { + // group logs by block, BTreeMap is used here to keep blocks in ascending order. + let blocks = logs.into_iter().fold( + BTreeMap::new(), + |mut result: BTreeMap<_, Vec<_>>, log| { + let block_number = log.block_number; + result.entry(block_number).or_default().push(log); + result + }, + ); + for (block_number, logs) in blocks { + if block_number > target_checkpoint { + break 'outer; + } + let mut data = vec![]; + let Ok(Ok(Some(block))) = retry_with_max_elapsed_time!( + provider.get_block(block_number), + Duration::from_secs(300) + ) else { + panic!("Failed to query block {block_number} from Ethereum after retry"); + }; + + for log in logs { + let tx_hash = log.tx_hash(); + let Ok(Ok(Some(transaction))) = retry_with_max_elapsed_time!( + provider.get_transaction(tx_hash), + Duration::from_secs(300) + ) else { + panic!( + "Failed to query transaction {tx_hash} from Ethereum after retry" + ); + }; + info!( + "Retrieved eth log {} for block {}", + log.tx_hash, log.block_number + ); + data.push((log, block.clone(), transaction)); + } + indexer_metrics + .last_committed_eth_block + .set(block_number as i64); + + if data_sender.send((block_number, data)).await.is_err() { + // exit data retrieval loop when receiver dropped + break 'outer; + } + } + } + task_handles.iter().for_each(|h| h.abort()); + Ok::<_, Error>(()) + }); + Ok((handle, data_receiver)) + } +} + +pub struct EthUnfinalizedDatasource { + bridge_address: EthAddress, + eth_rpc_url: String, + bridge_metrics: Arc, + indexer_metrics: BridgeIndexerMetrics, +} + +impl EthUnfinalizedDatasource { + pub fn new( + eth_sui_bridge_contract_address: String, + eth_rpc_url: String, + bridge_metrics: Arc, + indexer_metrics: BridgeIndexerMetrics, + ) -> Result { + let bridge_address = EthAddress::from_str(ð_sui_bridge_contract_address)?; + Ok(Self { + bridge_address, + eth_rpc_url, + bridge_metrics, + indexer_metrics, + }) + } +} + +#[async_trait] +impl Datasource for EthUnfinalizedDatasource { + async fn start_data_retrieval( + &self, + task_name: String, + starting_checkpoint: u64, + target_checkpoint: u64, + ) -> Result< + ( + JoinHandle>, + Receiver<(u64, Vec)>, + ), + Error, + > { + let eth_client = Arc::new( + EthClient::::new( + &self.eth_rpc_url, + HashSet::from_iter(vec![self.bridge_address]), + self.bridge_metrics.clone(), + ) + .await?, + ); + + let provider = Arc::new( + new_metered_eth_provider(&self.eth_rpc_url, self.bridge_metrics.clone())? + .interval(Duration::from_millis(2000)), + ); + + info!("Starting from unfinalized block: {}", starting_checkpoint); + + let unfinalized_contract_addresses = + HashMap::from_iter(vec![(self.bridge_address, starting_checkpoint)]); + + let (task_handles, mut eth_events_rx) = LatestEthSyncer::new( + eth_client, + provider.clone(), + unfinalized_contract_addresses.clone(), + ) + .run(self.indexer_metrics.clone()) + .await + .map_err(|e| anyhow!(format!("{e:?}")))?; + + let (data_sender, data_receiver) = metered_channel::channel( + 1000, + &mysten_metrics::get_metrics() + .unwrap() + .channel_inflight + .with_label_values(&[&task_name]), + ); + let indexer_metrics = self.indexer_metrics.clone(); + let handle = spawn_monitored_task!(async { + 'outer: while let Some((_, _, logs)) = eth_events_rx.recv().await { + // group logs by block + let blocks = logs.into_iter().fold( + BTreeMap::new(), + |mut result: BTreeMap<_, Vec<_>>, log| { + let block_number = log.block_number; + result.entry(block_number).or_default().push(log); + result + }, + ); + for (block_number, logs) in blocks { + if block_number > target_checkpoint { + break 'outer; + } + let mut data = vec![]; + let block = provider.get_block(block_number).await?.unwrap(); + + for log in logs { + let tx_hash = log.tx_hash(); + let transaction = provider.get_transaction(tx_hash).await?.unwrap(); + data.push((log.clone(), block.clone(), transaction.clone())); + info!( + "Processing eth log {} for block {}", + log.tx_hash, log.block_number + ) + } + indexer_metrics + .last_committed_unfinalized_eth_block + .set(block_number as i64); + if data_sender.send((block_number, data)).await.is_err() { + // exit data retrieval loop when receiver dropped + break 'outer; + } + } + } + task_handles.iter().for_each(|h| h.abort()); + Ok::<_, Error>(()) + }); + Ok((handle, data_receiver)) + } +} + +#[derive(Clone)] +pub struct EthDataMapper { + pub finalized: bool, + pub metrics: BridgeIndexerMetrics, +} + +impl DataMapper<(E, Block, Transaction), ProcessedTxnData> for EthDataMapper { + fn map( + &self, + (log, block, transaction): (E, Block, Transaction), + ) -> Result, Error> { + let eth_bridge_event = EthBridgeEvent::try_from_log(log.log()); + if eth_bridge_event.is_none() { + return Ok(vec![]); + } + self.metrics.total_eth_bridge_transactions.inc(); + let bridge_event = eth_bridge_event.unwrap(); + let timestamp_ms = block.timestamp.as_u64() * 1000; + let gas = transaction.gas; + + let transfer = match bridge_event { + EthBridgeEvent::EthSuiBridgeEvents(bridge_event) => match bridge_event { + EthSuiBridgeEvents::TokensDepositedFilter(bridge_event) => { + info!( + "Observed {} Eth Deposit at block {}", + if self.finalized { + "Finalized" + } else { + "Unfinalized" + }, + log.block_number() + ); + if self.finalized { + self.metrics.total_eth_token_deposited.inc(); + } + ProcessedTxnData::TokenTransfer(TokenTransfer { + chain_id: bridge_event.source_chain_id, + nonce: bridge_event.nonce, + block_height: log.block_number(), + timestamp_ms, + txn_hash: transaction.hash.as_bytes().to_vec(), + txn_sender: bridge_event.sender_address.as_bytes().to_vec(), + status: if self.finalized { + TokenTransferStatus::Deposited + } else { + TokenTransferStatus::DepositedUnfinalized + }, + gas_usage: gas.as_u64() as i64, + data_source: BridgeDataSource::Eth, + data: Some(TokenTransferData { + sender_address: bridge_event.sender_address.as_bytes().to_vec(), + destination_chain: bridge_event.destination_chain_id, + recipient_address: bridge_event.recipient_address.to_vec(), + token_id: bridge_event.token_id, + amount: bridge_event.sui_adjusted_amount, + }), + }) + } + EthSuiBridgeEvents::TokensClaimedFilter(bridge_event) => { + // Only write unfinalized claims + if self.finalized { + return Ok(vec![]); + } + info!("Observed Unfinalized Eth Claim"); + self.metrics.total_eth_token_transfer_claimed.inc(); + ProcessedTxnData::TokenTransfer(TokenTransfer { + chain_id: bridge_event.source_chain_id, + nonce: bridge_event.nonce, + block_height: log.block_number(), + timestamp_ms, + txn_hash: transaction.hash.as_bytes().to_vec(), + txn_sender: bridge_event.sender_address.to_vec(), + status: TokenTransferStatus::Claimed, + gas_usage: gas.as_u64() as i64, + data_source: BridgeDataSource::Eth, + data: None, + }) + } + EthSuiBridgeEvents::PausedFilter(_) + | EthSuiBridgeEvents::UnpausedFilter(_) + | EthSuiBridgeEvents::UpgradedFilter(_) + | EthSuiBridgeEvents::InitializedFilter(_) => { + // TODO: handle these events + self.metrics.total_eth_bridge_txn_other.inc(); + return Ok(vec![]); + } + }, + EthBridgeEvent::EthBridgeCommitteeEvents(_) + | EthBridgeEvent::EthBridgeLimiterEvents(_) + | EthBridgeEvent::EthBridgeConfigEvents(_) + | EthBridgeEvent::EthCommitteeUpgradeableContractEvents(_) => { + // TODO: handle these events + self.metrics.total_eth_bridge_txn_other.inc(); + return Ok(vec![]); + } + }; + Ok(vec![transfer]) + } +} diff --git a/crates/sui-bridge-indexer/src/eth_worker.rs b/crates/sui-bridge-indexer/src/eth_worker.rs deleted file mode 100644 index 797e3ebcdd068..0000000000000 --- a/crates/sui-bridge-indexer/src/eth_worker.rs +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::config::IndexerConfig; -use crate::latest_eth_syncer::LatestEthSyncer; -use crate::metrics::BridgeIndexerMetrics; -use crate::postgres_manager::get_latest_eth_token_transfer; -use crate::postgres_manager::{write, PgPool}; -use crate::{ - BridgeDataSource, ProcessedTxnData, TokenTransfer, TokenTransferData, TokenTransferStatus, -}; -use anyhow::{anyhow, Result}; -use ethers::providers::Middleware; -use ethers::providers::Provider; -use ethers::types::Address as EthAddress; -use mysten_metrics::spawn_logged_monitored_task; -use std::collections::HashMap; -use std::str::FromStr; -use std::sync::Arc; -use sui_bridge::abi::{EthBridgeEvent, EthSuiBridgeEvents}; -use sui_bridge::metered_eth_provider::{new_metered_eth_provider, MeteredEthHttpProvier}; -use sui_bridge::metrics::BridgeMetrics; -use sui_bridge::types::EthEvent; -use sui_bridge::{eth_client::EthClient, eth_syncer::EthSyncer}; -use tokio::task::JoinHandle; -use tracing::info; -use tracing::log::error; - -#[derive(Clone)] -pub struct EthBridgeWorker { - provider: Arc>, - pg_pool: PgPool, - bridge_metrics: Arc, - metrics: BridgeIndexerMetrics, - bridge_address: EthAddress, - config: IndexerConfig, -} - -impl EthBridgeWorker { - pub fn new( - pg_pool: PgPool, - bridge_metrics: Arc, - metrics: BridgeIndexerMetrics, - config: IndexerConfig, - ) -> Result { - let bridge_address = EthAddress::from_str(&config.eth_sui_bridge_contract_address)?; - - let provider = Arc::new( - new_metered_eth_provider(&config.eth_rpc_url, bridge_metrics.clone())? - .interval(std::time::Duration::from_millis(2000)), - ); - - Ok(Self { - provider, - pg_pool, - bridge_metrics, - metrics, - bridge_address, - config, - }) - } - - pub async fn start_indexing_finalized_events( - &self, - eth_client: Arc>, - ) -> Result> { - let newest_finalized_block = match get_latest_eth_token_transfer(&self.pg_pool, true)? { - Some(transfer) => transfer.block_height as u64, - None => self.config.start_block, - }; - - info!("Starting from finalized block: {}", newest_finalized_block); - - let finalized_contract_addresses = - HashMap::from_iter(vec![(self.bridge_address, newest_finalized_block)]); - - let (_task_handles, eth_events_rx, _) = - EthSyncer::new(eth_client, finalized_contract_addresses) - .run(self.bridge_metrics.clone()) - .await - .map_err(|e| anyhow!(format!("{e:?}")))?; - - let provider_clone = self.provider.clone(); - let pg_pool_clone = self.pg_pool.clone(); - let metrics_clone = self.metrics.clone(); - - Ok(spawn_logged_monitored_task!( - process_eth_events( - provider_clone, - pg_pool_clone, - metrics_clone, - eth_events_rx, - true - ), - "finalized indexer handler" - )) - } - - pub async fn start_indexing_unfinalized_events( - &self, - eth_client: Arc>, - ) -> Result> { - let newest_unfinalized_block_recorded = - match get_latest_eth_token_transfer(&self.pg_pool, false)? { - Some(transfer) => transfer.block_height as u64, - None => self.config.start_block, - }; - - info!( - "Starting from unfinalized block: {}", - newest_unfinalized_block_recorded - ); - - let unfinalized_contract_addresses = HashMap::from_iter(vec![( - self.bridge_address, - newest_unfinalized_block_recorded, - )]); - - let (_task_handles, eth_events_rx) = LatestEthSyncer::new( - eth_client, - self.provider.clone(), - unfinalized_contract_addresses.clone(), - ) - .run(self.metrics.clone()) - .await - .map_err(|e| anyhow!(format!("{e:?}")))?; - - let provider_clone = self.provider.clone(); - let pg_pool_clone = self.pg_pool.clone(); - let metrics_clone = self.metrics.clone(); - - Ok(spawn_logged_monitored_task!( - process_eth_events( - provider_clone, - pg_pool_clone, - metrics_clone, - eth_events_rx, - false - ), - "unfinalized indexer handler" - )) - } - - pub fn bridge_address(&self) -> EthAddress { - self.bridge_address - } -} - -async fn process_eth_events( - provider: Arc>, - pg_pool: PgPool, - metrics: BridgeIndexerMetrics, - mut eth_events_rx: mysten_metrics::metered_channel::Receiver<(EthAddress, u64, Vec)>, - finalized: bool, -) { - let progress_gauge = if finalized { - metrics.last_committed_eth_block.clone() - } else { - metrics.last_committed_unfinalized_eth_block.clone() - }; - while let Some((_, _, logs)) = eth_events_rx.recv().await { - // TODO: This for-loop can be optimzied to group tx / block info - // and reduce the queries issued to eth full node - for log in logs.iter() { - let eth_bridge_event = EthBridgeEvent::try_from_log(log.log()); - if eth_bridge_event.is_none() { - continue; - } - metrics.total_eth_bridge_transactions.inc(); - let bridge_event = eth_bridge_event.unwrap(); - let block_number = log.block_number(); - let block = provider.get_block(block_number).await.unwrap().unwrap(); - let timestamp = block.timestamp.as_u64() * 1000; - let tx_hash = log.tx_hash(); - let transaction = provider.get_transaction(tx_hash).await.unwrap().unwrap(); - let gas = transaction.gas; - - let transfer: TokenTransfer = match bridge_event { - EthBridgeEvent::EthSuiBridgeEvents(bridge_event) => match bridge_event { - EthSuiBridgeEvents::TokensDepositedFilter(bridge_event) => { - info!( - "Observed {} Eth Deposit at block {}", - if finalized { - "Finalized" - } else { - "Unfinalized" - }, - block_number - ); - if finalized { - metrics.total_eth_token_deposited.inc(); - } - TokenTransfer { - chain_id: bridge_event.source_chain_id, - nonce: bridge_event.nonce, - block_height: block_number, - timestamp_ms: timestamp, - txn_hash: tx_hash.as_bytes().to_vec(), - txn_sender: bridge_event.sender_address.as_bytes().to_vec(), - status: if finalized { - TokenTransferStatus::Deposited - } else { - TokenTransferStatus::DepositedUnfinalized - }, - gas_usage: gas.as_u64() as i64, - data_source: BridgeDataSource::Eth, - data: Some(TokenTransferData { - sender_address: bridge_event.sender_address.as_bytes().to_vec(), - destination_chain: bridge_event.destination_chain_id, - recipient_address: bridge_event.recipient_address.to_vec(), - token_id: bridge_event.token_id, - amount: bridge_event.sui_adjusted_amount, - }), - } - } - EthSuiBridgeEvents::TokensClaimedFilter(bridge_event) => { - // Only write unfinalized claims - if finalized { - continue; - } - info!("Observed Unfinalized Eth Claim"); - metrics.total_eth_token_transfer_claimed.inc(); - TokenTransfer { - chain_id: bridge_event.source_chain_id, - nonce: bridge_event.nonce, - block_height: block_number, - timestamp_ms: timestamp, - txn_hash: tx_hash.as_bytes().to_vec(), - txn_sender: bridge_event.sender_address.to_vec(), - status: TokenTransferStatus::Claimed, - gas_usage: gas.as_u64() as i64, - data_source: BridgeDataSource::Eth, - data: None, - } - } - EthSuiBridgeEvents::PausedFilter(_) - | EthSuiBridgeEvents::UnpausedFilter(_) - | EthSuiBridgeEvents::UpgradedFilter(_) - | EthSuiBridgeEvents::InitializedFilter(_) => { - metrics.total_eth_bridge_txn_other.inc(); - continue; - } - }, - EthBridgeEvent::EthBridgeCommitteeEvents(_) - | EthBridgeEvent::EthBridgeLimiterEvents(_) - | EthBridgeEvent::EthBridgeConfigEvents(_) - | EthBridgeEvent::EthCommitteeUpgradeableContractEvents(_) => { - metrics.total_eth_bridge_txn_other.inc(); - continue; - } - }; - - // TODO: we either scream here or keep retrying this until we succeed - if let Err(e) = write(&pg_pool, vec![ProcessedTxnData::TokenTransfer(transfer)]) { - error!("Error writing token transfer to database: {:?}", e); - } else { - progress_gauge.set(block_number as i64); - } - } - } - - panic!("Eth event stream ended unexpectedly"); -} diff --git a/crates/sui-bridge-indexer/src/indexer_builder.rs b/crates/sui-bridge-indexer/src/indexer_builder.rs new file mode 100644 index 0000000000000..7b2c762b66b0b --- /dev/null +++ b/crates/sui-bridge-indexer/src/indexer_builder.rs @@ -0,0 +1,402 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::cmp::min; +use std::path::PathBuf; +use std::sync::Arc; + +use anyhow::Error; +use async_trait::async_trait; +use tokio::sync::oneshot; +use tokio::sync::oneshot::Sender; +use tokio::task::JoinHandle; +use tracing::info; + +use mysten_metrics::metered_channel::Receiver; +use mysten_metrics::{metered_channel, spawn_monitored_task}; +use sui_data_ingestion_core::{ + DataIngestionMetrics, IndexerExecutor, ProgressStore, ReaderOptions, Worker, WorkerPool, +}; +use sui_types::full_checkpoint_content::{CheckpointData, CheckpointTransaction}; +use sui_types::messages_checkpoint::CheckpointSequenceNumber; + +use crate::sui_checkpoint_ingestion::{Task, Tasks}; + +pub struct IndexerBuilder { + name: String, + datasource: D, + data_mapper: M, + backfill_strategy: BackfillStrategy, +} + +impl IndexerBuilder { + pub fn new(name: &str, datasource: D, data_mapper: M) -> IndexerBuilder { + IndexerBuilder { + name: name.into(), + datasource, + data_mapper, + backfill_strategy: BackfillStrategy::Simple, + } + } + pub fn build( + self, + start_from_checkpoint: u64, + genesis_checkpoint: u64, + persistent: P, + ) -> Indexer + where + P: Persistent, + { + Indexer { + name: self.name, + storage: persistent, + datasource: self.datasource.into(), + backfill_strategy: self.backfill_strategy, + start_from_checkpoint, + data_mapper: self.data_mapper, + genesis_checkpoint, + } + } + + pub fn with_backfill_strategy(mut self, backfill: BackfillStrategy) -> Self { + self.backfill_strategy = backfill; + self + } +} + +pub struct Indexer { + name: String, + storage: P, + datasource: Arc, + data_mapper: M, + backfill_strategy: BackfillStrategy, + start_from_checkpoint: u64, + genesis_checkpoint: u64, +} + +impl Indexer { + pub async fn start(mut self) -> Result<(), Error> + where + D: Datasource + 'static + Send + Sync, + M: DataMapper + 'static + Clone, + P: Persistent + 'static, + T: Send, + { + // Update tasks first + let tasks = self.storage.tasks(&self.name)?; + // create checkpoint workers base on backfill config and existing tasks in the db + match tasks.live_task() { + None => { + // Scenario 1: No task in database, start live task and backfill tasks + self.storage.register_task( + format!("{} - Live", self.name), + self.start_from_checkpoint, + i64::MAX, + )?; + // Create backfill tasks + if self.start_from_checkpoint != self.genesis_checkpoint { + self.create_backfill_tasks(self.genesis_checkpoint)? + } + } + Some(mut live_task) => { + if self.start_from_checkpoint > live_task.checkpoint { + // Scenario 2: there are existing tasks in DB and start_from_checkpoint > current checkpoint + // create backfill task to finish at start_from_checkpoint + // update live task to start from start_from_checkpoint and finish at u64::MAX + self.create_backfill_tasks(live_task.checkpoint)?; + live_task.checkpoint = self.start_from_checkpoint; + self.storage.update_task(live_task)?; + } else { + // Scenario 3: start_from_checkpoint < current checkpoint + // ignore start_from_checkpoint, resume all task as it is. + } + } + } + + // get updated tasks from storage and start workers + let updated_tasks = self.storage.tasks(&self.name)?; + // Start latest checkpoint worker + // Tasks are ordered in checkpoint descending order, realtime update task always come first + // tasks won't be empty here, ok to unwrap. + let (live_task, backfill_tasks) = updated_tasks.split_first().unwrap(); + + let live_task_future = self.datasource.start_ingestion_task( + live_task.task_name.clone(), + live_task.checkpoint, + live_task.target_checkpoint, + self.storage.clone(), + self.data_mapper.clone(), + ); + + let backfill_tasks = backfill_tasks.to_vec(); + let storage_clone = self.storage.clone(); + let data_mapper_clone = self.data_mapper.clone(); + let datasource_clone = self.datasource.clone(); + + let handle = spawn_monitored_task!(async { + // Execute task one by one + for backfill_task in backfill_tasks { + datasource_clone + .start_ingestion_task( + backfill_task.task_name.clone(), + backfill_task.checkpoint, + backfill_task.target_checkpoint, + storage_clone.clone(), + data_mapper_clone.clone(), + ) + .await + .expect("Backfill task failed"); + } + }); + live_task_future.await?; + tokio::try_join!(handle)?; + + Ok(()) + } + + // Create backfill tasks according to backfill strategy + fn create_backfill_tasks(&mut self, mut current_cp: u64) -> Result<(), Error> + where + P: Persistent + 'static, + { + match self.backfill_strategy { + BackfillStrategy::Simple => self.storage.register_task( + format!("{} - backfill - {}", self.name, self.start_from_checkpoint), + current_cp + 1, + self.start_from_checkpoint as i64, + ), + BackfillStrategy::Partitioned { task_size } => { + while current_cp < self.start_from_checkpoint { + let target_cp = min(current_cp + task_size, self.start_from_checkpoint); + self.storage.register_task( + format!("{} - backfill - {target_cp}", self.name), + current_cp + 1, + target_cp as i64, + )?; + current_cp = target_cp; + } + Ok(()) + } + BackfillStrategy::Disabled => Ok(()), + } + } +} + +pub trait Persistent: IndexerProgressStore + Sync + Send + Clone { + fn write(&self, data: Vec) -> Result<(), Error>; +} + +#[async_trait] +pub trait IndexerProgressStore: Send { + async fn load_progress(&self, task_name: String) -> anyhow::Result; + async fn save_progress( + &mut self, + task_name: String, + checkpoint_number: u64, + ) -> anyhow::Result<()>; + + fn tasks(&self, task_prefix: &str) -> Result, Error>; + + fn register_task( + &mut self, + task_name: String, + checkpoint: u64, + target_checkpoint: i64, + ) -> Result<(), anyhow::Error>; + + fn update_task(&mut self, task: Task) -> Result<(), Error>; +} + +#[async_trait] +pub trait Datasource { + async fn start_ingestion_task( + &self, + task_name: String, + starting_checkpoint: u64, + target_checkpoint: u64, + mut storage: P, + data_mapper: M, + ) -> Result<(), Error> + where + M: DataMapper + 'static, + P: Persistent + 'static, + { + // todo: add metrics for number of tasks + let (join_handle, mut data_channel) = self + .start_data_retrieval(task_name.clone(), starting_checkpoint, target_checkpoint) + .await?; + while let Some((block_number, data)) = data_channel.recv().await { + if !data.is_empty() { + let processed_data = data.into_iter().try_fold(vec![], |mut result, d| { + result.append(&mut data_mapper.map(d)?); + Ok::, Error>(result) + })?; + // TODO: we might be able to write data and progress in a single transaction. + storage.write(processed_data)?; + } + storage + .save_progress(task_name.clone(), block_number) + .await?; + } + join_handle.abort(); + join_handle.await? + } + + async fn start_data_retrieval( + &self, + task_name: String, + starting_checkpoint: u64, + target_checkpoint: u64, + ) -> Result<(JoinHandle>, Receiver<(u64, Vec)>), Error>; +} + +pub struct SuiCheckpointDatasource { + remote_store_url: String, + concurrency: usize, + checkpoint_path: PathBuf, + metrics: DataIngestionMetrics, +} +impl SuiCheckpointDatasource { + pub fn new( + remote_store_url: String, + concurrency: usize, + checkpoint_path: PathBuf, + metrics: DataIngestionMetrics, + ) -> Self { + SuiCheckpointDatasource { + remote_store_url, + concurrency, + checkpoint_path, + metrics, + } + } +} + +#[async_trait] +impl Datasource for SuiCheckpointDatasource +where + P: Persistent + 'static, + R: Sync + Send + 'static, +{ + async fn start_data_retrieval( + &self, + task_name: String, + starting_checkpoint: u64, + target_checkpoint: u64, + ) -> Result< + ( + JoinHandle>, + Receiver<(u64, Vec)>, + ), + Error, + > { + let (exit_sender, exit_receiver) = oneshot::channel(); + let progress_store = PerTaskInMemProgressStore { + current_checkpoint: starting_checkpoint, + exit_checkpoint: target_checkpoint, + exit_sender: Some(exit_sender), + }; + let mut executor = IndexerExecutor::new(progress_store, 1, self.metrics.clone()); + let (data_sender, data_receiver) = metered_channel::channel( + 1000, + &mysten_metrics::get_metrics() + .unwrap() + .channel_inflight + .with_label_values(&[&task_name]), + ); + let worker = IndexerWorker::new(data_sender); + let worker_pool = WorkerPool::new(worker, task_name, self.concurrency); + executor.register(worker_pool).await?; + let checkpoint_path = self.checkpoint_path.clone(); + let remote_store_url = self.remote_store_url.clone(); + let join_handle = spawn_monitored_task!(async { + executor + .run( + checkpoint_path, + Some(remote_store_url), + vec![], // optional remote store access options + ReaderOptions::default(), + exit_receiver, + ) + .await?; + Ok(()) + }); + Ok((join_handle, data_receiver)) + } +} + +pub enum BackfillStrategy { + Simple, + Partitioned { task_size: u64 }, + Disabled, +} + +pub trait DataMapper: Sync + Send { + fn map(&self, data: T) -> Result, anyhow::Error>; +} + +struct PerTaskInMemProgressStore { + pub current_checkpoint: u64, + pub exit_checkpoint: u64, + pub exit_sender: Option>, +} + +#[async_trait] +impl ProgressStore for PerTaskInMemProgressStore { + async fn load( + &mut self, + _task_name: String, + ) -> Result { + Ok(self.current_checkpoint) + } + + async fn save( + &mut self, + _task_name: String, + checkpoint_number: CheckpointSequenceNumber, + ) -> anyhow::Result<()> { + if checkpoint_number >= self.exit_checkpoint { + if let Some(sender) = self.exit_sender.take() { + let _ = sender.send(()); + } + } + self.current_checkpoint = checkpoint_number; + Ok(()) + } +} + +pub struct IndexerWorker { + data_sender: metered_channel::Sender<(u64, Vec)>, +} + +impl IndexerWorker { + pub fn new(data_sender: metered_channel::Sender<(u64, Vec)>) -> Self { + Self { data_sender } + } +} + +pub type CheckpointTxnData = (CheckpointTransaction, u64, u64); + +#[async_trait] +impl Worker for IndexerWorker { + async fn process_checkpoint(&self, checkpoint: CheckpointData) -> anyhow::Result<()> { + info!( + "Received checkpoint [{}] {}: {}", + checkpoint.checkpoint_summary.epoch, + checkpoint.checkpoint_summary.sequence_number, + checkpoint.transactions.len(), + ); + let checkpoint_num = checkpoint.checkpoint_summary.sequence_number; + let timestamp_ms = checkpoint.checkpoint_summary.timestamp_ms; + + let transactions = checkpoint + .transactions + .into_iter() + .map(|tx| (tx, checkpoint_num, timestamp_ms)) + .collect(); + Ok(self + .data_sender + .send((checkpoint_num, transactions)) + .await?) + } +} diff --git a/crates/sui-bridge-indexer/src/lib.rs b/crates/sui-bridge-indexer/src/lib.rs index 2aed0d793e3c2..b216d29e1976c 100644 --- a/crates/sui-bridge-indexer/src/lib.rs +++ b/crates/sui-bridge-indexer/src/lib.rs @@ -1,13 +1,14 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::models::TokenTransferData as DBTokenTransferData; -use crate::models::{SuiErrorTransactions, TokenTransfer as DBTokenTransfer}; use std::fmt::{Display, Formatter}; + use sui_types::base_types::{SuiAddress, TransactionDigest}; +use crate::models::TokenTransferData as DBTokenTransferData; +use crate::models::{SuiErrorTransactions, TokenTransfer as DBTokenTransfer}; + pub mod config; -pub mod eth_worker; pub mod latest_eth_syncer; pub mod metrics; pub mod models; @@ -16,9 +17,13 @@ pub mod schema; pub mod sui_checkpoint_ingestion; pub mod sui_transaction_handler; pub mod sui_transaction_queries; -pub mod sui_worker; pub mod types; +pub mod indexer_builder; + +pub mod eth_bridge_indexer; +pub mod sui_bridge_indexer; + #[derive(Clone)] pub enum ProcessedTxnData { TokenTransfer(TokenTransfer), diff --git a/crates/sui-bridge-indexer/src/main.rs b/crates/sui-bridge-indexer/src/main.rs index 6685fed04dca7..e9ed0e9bfcac1 100644 --- a/crates/sui-bridge-indexer/src/main.rs +++ b/crates/sui-bridge-indexer/src/main.rs @@ -1,31 +1,32 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use std::env; +use std::path::PathBuf; +use std::sync::Arc; + use anyhow::Result; use clap::*; +use tokio::task::JoinHandle; +use tracing::info; + +use mysten_metrics::metered_channel::channel; use mysten_metrics::spawn_logged_monitored_task; use mysten_metrics::start_prometheus_server; -use std::collections::HashSet; -use std::env; -use std::path::PathBuf; -use std::sync::Arc; -use sui_bridge::eth_client::EthClient; -use sui_bridge::metered_eth_provider::MeteredEthHttpProvier; use sui_bridge::metrics::BridgeMetrics; -use sui_bridge_indexer::eth_worker::EthBridgeWorker; +use sui_bridge_indexer::config::IndexerConfig; +use sui_bridge_indexer::eth_bridge_indexer::{ + EthDataMapper, EthFinalizedDatasource, EthUnfinalizedDatasource, +}; +use sui_bridge_indexer::indexer_builder::{IndexerBuilder, SuiCheckpointDatasource}; use sui_bridge_indexer::metrics::BridgeIndexerMetrics; use sui_bridge_indexer::postgres_manager::{get_connection_pool, read_sui_progress_store}; +use sui_bridge_indexer::sui_bridge_indexer::{PgBridgePersistent, SuiBridgeDataMapper}; use sui_bridge_indexer::sui_transaction_handler::handle_sui_transactions_loop; use sui_bridge_indexer::sui_transaction_queries::start_sui_tx_polling_task; +use sui_config::Config; use sui_data_ingestion_core::DataIngestionMetrics; use sui_sdk::SuiClientBuilder; -use tokio::task::JoinHandle; - -use mysten_metrics::metered_channel::channel; -use sui_bridge_indexer::config::IndexerConfig; -use sui_bridge_indexer::sui_checkpoint_ingestion::SuiCheckpointSyncer; -use sui_config::Config; -use tracing::info; #[derive(Parser, Clone, Debug)] struct Args { @@ -51,7 +52,6 @@ async fn main() -> Result<()> { .join("config.yaml") }; let config = IndexerConfig::load(&config_path)?; - let config_clone = config.clone(); // Init metrics server let registry_service = start_prometheus_server( @@ -71,35 +71,45 @@ async fn main() -> Result<()> { let ingestion_metrics = DataIngestionMetrics::new(®istry); let bridge_metrics = Arc::new(BridgeMetrics::new(®istry)); - // unwrap safe: db_url must be set in `load_config` above let db_url = config.db_url.clone(); - // TODO: retry_with_max_elapsed_time - let eth_worker = EthBridgeWorker::new( - get_connection_pool(db_url.clone()), + let datastore = PgBridgePersistent::new(get_connection_pool(db_url.clone())); + let eth_checkpoint_datasource = EthFinalizedDatasource::new( + config.eth_sui_bridge_contract_address.clone(), + config.eth_rpc_url.clone(), bridge_metrics.clone(), indexer_meterics.clone(), - config.clone(), )?; - - let eth_client = Arc::new( - EthClient::::new( - &config.eth_rpc_url, - HashSet::from_iter(vec![eth_worker.bridge_address()]), - bridge_metrics.clone(), - ) - .await?, - ); - - let unfinalized_handle = eth_worker - .start_indexing_unfinalized_events(eth_client.clone()) - .await?; - let finalized_handle = eth_worker - .start_indexing_finalized_events(eth_client.clone()) - .await?; - let handles = vec![unfinalized_handle, finalized_handle]; + let eth_finalized_indexer = IndexerBuilder::new( + "FinalizedEthBridgeIndexer", + eth_checkpoint_datasource, + EthDataMapper { + finalized: true, + metrics: indexer_meterics.clone(), + }, + ) + .build(config.start_block, config.start_block, datastore.clone()); + let finalized_indexer_fut = spawn_logged_monitored_task!(eth_finalized_indexer.start()); + + let eth_unfinalized_datasource = EthUnfinalizedDatasource::new( + config.eth_sui_bridge_contract_address.clone(), + config.eth_rpc_url.clone(), + bridge_metrics.clone(), + indexer_meterics.clone(), + )?; + let eth_unfinalized_indexer = IndexerBuilder::new( + "UnFinalizedEthBridgeIndexer", + eth_unfinalized_datasource, + EthDataMapper { + finalized: false, + metrics: indexer_meterics.clone(), + }, + ) + .build(config.start_block, config.start_block, datastore.clone()); + let unfinalized_indexer_fut = spawn_logged_monitored_task!(eth_unfinalized_indexer.start()); if let Some(sui_rpc_url) = config.sui_rpc_url.clone() { + // Todo: impl datasource for sui RPC datasource start_processing_sui_checkpoints_by_querying_txns( sui_rpc_url, db_url.clone(), @@ -108,13 +118,30 @@ async fn main() -> Result<()> { ) .await?; } else { - let pg_pool = get_connection_pool(db_url.clone()); - SuiCheckpointSyncer::new(pg_pool, config.bridge_genesis_checkpoint) - .start(&config_clone, indexer_meterics, ingestion_metrics) - .await?; + let sui_checkpoint_datasource = SuiCheckpointDatasource::new( + config.remote_store_url, + config.concurrency as usize, + config.checkpoints_path.clone().into(), + ingestion_metrics.clone(), + ); + let indexer = IndexerBuilder::new( + "SuiBridgeIndexer", + sui_checkpoint_datasource, + SuiBridgeDataMapper { + metrics: indexer_meterics.clone(), + }, + ) + .build( + config + .resume_from_checkpoint + .unwrap_or(config.bridge_genesis_checkpoint), + config.bridge_genesis_checkpoint, + datastore, + ); + indexer.start().await?; } // We are not waiting for the sui tasks to finish here, which is ok. - futures::future::join_all(handles).await; + futures::future::join_all(vec![finalized_indexer_fut, unfinalized_indexer_fut]).await; Ok(()) } diff --git a/crates/sui-bridge-indexer/src/sui_bridge_indexer.rs b/crates/sui-bridge-indexer/src/sui_bridge_indexer.rs new file mode 100644 index 0000000000000..41f219beb18f1 --- /dev/null +++ b/crates/sui-bridge-indexer/src/sui_bridge_indexer.rs @@ -0,0 +1,291 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::{anyhow, Error}; +use async_trait::async_trait; +use diesel::dsl::now; +use diesel::{Connection, OptionalExtension, QueryDsl, RunQueryDsl, SelectableHelper}; +use diesel::{ExpressionMethods, TextExpressionMethods}; +use tracing::info; + +use sui_bridge::events::{ + MoveTokenDepositedEvent, MoveTokenTransferApproved, MoveTokenTransferClaimed, +}; +use sui_types::effects::TransactionEffectsAPI; +use sui_types::event::Event; +use sui_types::execution_status::ExecutionStatus; +use sui_types::full_checkpoint_content::CheckpointTransaction; +use sui_types::{BRIDGE_ADDRESS, SUI_BRIDGE_OBJECT_ID}; + +use crate::indexer_builder::{CheckpointTxnData, DataMapper, IndexerProgressStore, Persistent}; +use crate::metrics::BridgeIndexerMetrics; +use crate::postgres_manager::PgPool; +use crate::schema::progress_store::{columns, dsl}; +use crate::schema::{sui_error_transactions, token_transfer, token_transfer_data}; +use crate::sui_checkpoint_ingestion::Task; +use crate::{ + models, schema, BridgeDataSource, ProcessedTxnData, SuiTxnError, TokenTransfer, + TokenTransferData, TokenTransferStatus, +}; + +/// Persistent layer impl +#[derive(Clone)] +pub struct PgBridgePersistent { + pool: PgPool, +} + +impl PgBridgePersistent { + pub fn new(pool: PgPool) -> Self { + Self { pool } + } +} + +// TODO: this is shared between SUI and ETH, move to different file. +impl Persistent for PgBridgePersistent { + fn write(&self, data: Vec) -> Result<(), Error> { + if data.is_empty() { + return Ok(()); + } + let connection = &mut self.pool.get()?; + connection.transaction(|conn| { + for d in data { + match d { + ProcessedTxnData::TokenTransfer(t) => { + diesel::insert_into(token_transfer::table) + .values(&t.to_db()) + .on_conflict_do_nothing() + .execute(conn)?; + + if let Some(d) = t.to_data_maybe() { + diesel::insert_into(token_transfer_data::table) + .values(&d) + .on_conflict_do_nothing() + .execute(conn)?; + } + } + ProcessedTxnData::Error(e) => { + diesel::insert_into(sui_error_transactions::table) + .values(&e.to_db()) + .on_conflict_do_nothing() + .execute(conn)?; + } + } + } + Ok(()) + }) + } +} + +#[async_trait] +impl IndexerProgressStore for PgBridgePersistent { + async fn load_progress(&self, task_name: String) -> anyhow::Result { + let mut conn = self.pool.get()?; + let cp: Option = dsl::progress_store + .find(&task_name) + .select(models::ProgressStore::as_select()) + .first(&mut conn) + .optional()?; + Ok(cp + .ok_or(anyhow!("Cannot found progress for task {task_name}"))? + .checkpoint as u64) + } + + async fn save_progress( + &mut self, + task_name: String, + checkpoint_number: u64, + ) -> anyhow::Result<()> { + let mut conn = self.pool.get()?; + diesel::insert_into(schema::progress_store::table) + .values(&models::ProgressStore { + task_name, + checkpoint: checkpoint_number as i64, + // Target checkpoint and timestamp will only be written for new entries + target_checkpoint: i64::MAX, + // Timestamp is defaulted to current time in DB if None + timestamp: None, + }) + .on_conflict(dsl::task_name) + .do_update() + .set(( + columns::checkpoint.eq(checkpoint_number as i64), + columns::timestamp.eq(now), + )) + .execute(&mut conn)?; + Ok(()) + } + + fn tasks(&self, prefix: &str) -> Result, anyhow::Error> { + let mut conn = self.pool.get()?; + // get all unfinished tasks + let cp: Vec = dsl::progress_store + // TODO: using like could be error prone, change the progress store schema to stare the task name properly. + .filter(columns::task_name.like(format!("{prefix} - %"))) + .filter(columns::checkpoint.lt(columns::target_checkpoint)) + .order_by(columns::target_checkpoint.desc()) + .load(&mut conn)?; + Ok(cp.into_iter().map(|d| d.into()).collect()) + } + + fn register_task( + &mut self, + task_name: String, + checkpoint: u64, + target_checkpoint: i64, + ) -> Result<(), anyhow::Error> { + let mut conn = self.pool.get()?; + diesel::insert_into(schema::progress_store::table) + .values(models::ProgressStore { + task_name, + checkpoint: checkpoint as i64, + target_checkpoint, + // Timestamp is defaulted to current time in DB if None + timestamp: None, + }) + .execute(&mut conn)?; + Ok(()) + } + + fn update_task(&mut self, task: Task) -> Result<(), anyhow::Error> { + let mut conn = self.pool.get()?; + diesel::update(dsl::progress_store.filter(columns::task_name.eq(task.task_name))) + .set(( + columns::checkpoint.eq(task.checkpoint as i64), + columns::target_checkpoint.eq(task.target_checkpoint as i64), + columns::timestamp.eq(now), + )) + .execute(&mut conn)?; + Ok(()) + } +} + +/// Data mapper impl +#[derive(Clone)] +pub struct SuiBridgeDataMapper { + pub metrics: BridgeIndexerMetrics, +} + +impl DataMapper for SuiBridgeDataMapper { + fn map( + &self, + (data, checkpoint_num, timestamp_ms): CheckpointTxnData, + ) -> Result, Error> { + self.metrics.total_sui_bridge_transactions.inc(); + if !data + .input_objects + .iter() + .any(|obj| obj.id() == SUI_BRIDGE_OBJECT_ID) + { + return Ok(vec![]); + } + + match &data.events { + Some(events) => { + let token_transfers = events.data.iter().try_fold(vec![], |mut result, ev| { + if let Some(data) = process_sui_event(ev, &data, checkpoint_num, timestamp_ms)? + { + result.push(data); + } + Ok::<_, anyhow::Error>(result) + })?; + + if !token_transfers.is_empty() { + info!( + "SUI: Extracted {} bridge token transfer data entries for tx {}.", + token_transfers.len(), + data.transaction.digest() + ); + } + Ok(token_transfers) + } + None => { + if let ExecutionStatus::Failure { error, command } = data.effects.status() { + Ok(vec![ProcessedTxnData::Error(SuiTxnError { + tx_digest: *data.transaction.digest(), + sender: data.transaction.sender_address(), + timestamp_ms, + failure_status: error.to_string(), + cmd_idx: command.map(|idx| idx as u64), + })]) + } else { + Ok(vec![]) + } + } + } + } +} + +fn process_sui_event( + ev: &Event, + tx: &CheckpointTransaction, + checkpoint: u64, + timestamp_ms: u64, +) -> Result, anyhow::Error> { + Ok(if ev.type_.address == BRIDGE_ADDRESS { + match ev.type_.name.as_str() { + "TokenDepositedEvent" => { + info!("Observed Sui Deposit {:?}", ev); + // todo: metrics.total_sui_token_deposited.inc(); + let move_event: MoveTokenDepositedEvent = bcs::from_bytes(&ev.contents)?; + Some(ProcessedTxnData::TokenTransfer(TokenTransfer { + chain_id: move_event.source_chain, + nonce: move_event.seq_num, + block_height: checkpoint, + timestamp_ms, + txn_hash: tx.transaction.digest().inner().to_vec(), + txn_sender: ev.sender.to_vec(), + status: TokenTransferStatus::Deposited, + gas_usage: tx.effects.gas_cost_summary().net_gas_usage(), + data_source: BridgeDataSource::Sui, + data: Some(TokenTransferData { + destination_chain: move_event.target_chain, + sender_address: move_event.sender_address.clone(), + recipient_address: move_event.target_address.clone(), + token_id: move_event.token_type, + amount: move_event.amount_sui_adjusted, + }), + })) + } + "TokenTransferApproved" => { + info!("Observed Sui Approval {:?}", ev); + // todo: metrics.total_sui_token_transfer_approved.inc(); + let event: MoveTokenTransferApproved = bcs::from_bytes(&ev.contents)?; + Some(ProcessedTxnData::TokenTransfer(TokenTransfer { + chain_id: event.message_key.source_chain, + nonce: event.message_key.bridge_seq_num, + block_height: checkpoint, + timestamp_ms, + txn_hash: tx.transaction.digest().inner().to_vec(), + txn_sender: ev.sender.to_vec(), + status: TokenTransferStatus::Approved, + gas_usage: tx.effects.gas_cost_summary().net_gas_usage(), + data_source: BridgeDataSource::Sui, + data: None, + })) + } + "TokenTransferClaimed" => { + info!("Observed Sui Claim {:?}", ev); + // todo: metrics.total_sui_token_transfer_claimed.inc(); + let event: MoveTokenTransferClaimed = bcs::from_bytes(&ev.contents)?; + Some(ProcessedTxnData::TokenTransfer(TokenTransfer { + chain_id: event.message_key.source_chain, + nonce: event.message_key.bridge_seq_num, + block_height: checkpoint, + timestamp_ms, + txn_hash: tx.transaction.digest().inner().to_vec(), + txn_sender: ev.sender.to_vec(), + status: TokenTransferStatus::Claimed, + gas_usage: tx.effects.gas_cost_summary().net_gas_usage(), + data_source: BridgeDataSource::Sui, + data: None, + })) + } + _ => { + // todo: metrics.total_sui_bridge_txn_other.inc(); + None + } + } + } else { + None + }) +} diff --git a/crates/sui-bridge-indexer/src/sui_checkpoint_ingestion.rs b/crates/sui-bridge-indexer/src/sui_checkpoint_ingestion.rs index 53d1d701f6694..a0cf1a6b14201 100644 --- a/crates/sui-bridge-indexer/src/sui_checkpoint_ingestion.rs +++ b/crates/sui-bridge-indexer/src/sui_checkpoint_ingestion.rs @@ -1,217 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::config::IndexerConfig; -use crate::metrics::BridgeIndexerMetrics; -use crate::postgres_manager::PgPool; -use crate::schema::progress_store::{columns, dsl}; -use crate::sui_worker::SuiBridgeWorker; -use crate::{models, schema}; -use async_trait::async_trait; -use diesel::dsl::now; -use diesel::{ - delete, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl, SelectableHelper, -}; -use mysten_metrics::spawn_monitored_task; -use std::cmp::min; -use sui_data_ingestion_core::{ - DataIngestionMetrics, IndexerExecutor, ProgressStore, ReaderOptions, WorkerPool, -}; -use sui_types::base_types::TransactionDigest; -use sui_types::messages_checkpoint::CheckpointSequenceNumber; -use tokio::sync::oneshot; -use tokio::sync::oneshot::Sender; - -pub struct SuiCheckpointSyncer { - pool: PgPool, - bridge_genesis_checkpoint: u64, -} - -impl SuiCheckpointSyncer { - pub fn new(pool: PgPool, bridge_genesis_checkpoint: u64) -> Self { - // read all task from db - SuiCheckpointSyncer { - pool: pool.clone(), - bridge_genesis_checkpoint, - } - } - pub async fn start( - self, - config: &IndexerConfig, - indexer_meterics: BridgeIndexerMetrics, - ingestion_metrics: DataIngestionMetrics, - ) -> anyhow::Result<(), anyhow::Error> { - // Update tasks first - let tasks = self.tasks()?; - // checkpoint workers - match tasks.latest_checkpoint_task() { - None => { - // No task in database, start latest checkpoint task and backfill tasks - // if resume_from_checkpoint, use it for the latest task, if not set, use bridge_genesis_checkpoint - let start_from_cp = config - .resume_from_checkpoint - .unwrap_or(self.bridge_genesis_checkpoint); - self.register_task(new_task_name(), start_from_cp, i64::MAX)?; - - // Create backfill tasks - if start_from_cp != config.bridge_genesis_checkpoint { - let mut current_cp = self.bridge_genesis_checkpoint; - while current_cp < start_from_cp { - let target_cp = min(current_cp + config.back_fill_lot_size, start_from_cp); - self.register_task(new_task_name(), current_cp, target_cp as i64)?; - current_cp = target_cp; - } - } - } - Some(mut task) => { - match config.resume_from_checkpoint { - Some(cp) if task.checkpoint < cp => { - // Scenario 1: resume_from_checkpoint is set, and it's > current checkpoint - // create new task from resume_from_checkpoint to u64::MAX - // Update old task to finish at resume_from_checkpoint - let mut target_cp = cp; - while target_cp - task.checkpoint > config.back_fill_lot_size { - self.register_task( - new_task_name(), - target_cp - config.back_fill_lot_size, - target_cp as i64, - )?; - target_cp -= config.back_fill_lot_size; - } - task.target_checkpoint = target_cp; - self.update_task(task)?; - self.register_task(new_task_name(), cp, i64::MAX)?; - } - _ => { - // Scenario 2: resume_from_checkpoint is set, but it's < current checkpoint or not set - // ignore resume_from_checkpoint, resume all task as it is. - } - } - } - } - - // get updated tasks and start workers - let updated_tasks = self.tasks()?; - // Start latest checkpoint worker - // Tasks are ordered in checkpoint descending order, realtime update task always come first - // tasks won't be empty here, ok to unwrap. - let (realtime_task, backfill_tasks) = updated_tasks.split_first().unwrap(); - let realtime_task_future = Self::start_executor( - self.pool.clone(), - self.bridge_genesis_checkpoint, - ingestion_metrics.clone(), - indexer_meterics.clone(), - config, - realtime_task, - ); - - let backfill_tasks = backfill_tasks.to_vec(); - let config_clone = config.clone(); - let pool_clone = self.pool.clone(); - let bridge_genesis_checkpoint = self.bridge_genesis_checkpoint; - let handle = spawn_monitored_task!(async { - for backfill_task in backfill_tasks { - Self::start_executor( - pool_clone.clone(), - bridge_genesis_checkpoint, - ingestion_metrics.clone(), - indexer_meterics.clone(), - &config_clone, - &backfill_task, - ) - .await - .expect("Backfill task failed"); - } - }); - realtime_task_future.await?; - tokio::try_join!(handle)?; - Ok(()) - } - - pub fn tasks(&self) -> Result, anyhow::Error> { - let mut conn = self.pool.get()?; - // clean up completed task - delete(dsl::progress_store.filter(columns::checkpoint.ge(columns::target_checkpoint))) - .execute(&mut conn)?; - // get all unfinished tasks - let cp: Vec = dsl::progress_store - .order_by(columns::checkpoint.desc()) - .load(&mut conn)?; - Ok(cp.into_iter().map(|d| d.into()).collect()) - } - - pub fn register_task( - &self, - task_name: String, - checkpoint: u64, - target_checkpoint: i64, - ) -> Result<(), anyhow::Error> { - let mut conn = self.pool.get()?; - diesel::insert_into(schema::progress_store::table) - .values(models::ProgressStore { - task_name, - checkpoint: checkpoint as i64, - target_checkpoint, - timestamp: None, - }) - .execute(&mut conn)?; - Ok(()) - } - - pub fn update_task(&self, task: Task) -> Result<(), anyhow::Error> { - let mut conn = self.pool.get()?; - diesel::update(dsl::progress_store.filter(columns::task_name.eq(task.task_name))) - .set(( - columns::checkpoint.eq(task.checkpoint as i64), - columns::target_checkpoint.eq(task.target_checkpoint as i64), - columns::timestamp.eq(now), - )) - .execute(&mut conn)?; - Ok(()) - } - - async fn start_executor( - pool: PgPool, - bridge_genesis_checkpoint: u64, - ingestion_metrics: DataIngestionMetrics, - indexer_meterics: BridgeIndexerMetrics, - config: &IndexerConfig, - task: &Task, - ) -> anyhow::Result<()> { - let (exit_sender, exit_receiver) = oneshot::channel(); - - let progress_store = PgProgressStore { - pool: pool.clone(), - bridge_genesis_checkpoint, - exit_checkpoint: task.target_checkpoint, - exit_sender: Some(exit_sender), - }; - - let mut executor = IndexerExecutor::new( - progress_store, - 1, /* workflow types */ - ingestion_metrics, - ); - - let indexer_metrics_cloned = indexer_meterics.clone(); - - let worker = SuiBridgeWorker::new(vec![], pool, indexer_metrics_cloned); - let worker_pool = - WorkerPool::new(worker, task.task_name.clone(), config.concurrency as usize); - executor.register(worker_pool).await?; - executor - .run( - config.checkpoints_path.clone().into(), - Some(config.remote_store_url.clone()), - vec![], // optional remote store access options - ReaderOptions::default(), - exit_receiver, - ) - .await?; - - Ok(()) - } -} +use crate::models; #[derive(Clone)] pub struct Task { @@ -234,11 +24,11 @@ impl From for Task { } pub trait Tasks { - fn latest_checkpoint_task(&self) -> Option; + fn live_task(&self) -> Option; } impl Tasks for Vec { - fn latest_checkpoint_task(&self) -> Option { + fn live_task(&self) -> Option { self.iter().fold(None, |result, other_task| match &result { Some(task) if task.checkpoint < other_task.checkpoint => Some(other_task.clone()), None => Some(other_task.clone()), @@ -246,57 +36,3 @@ impl Tasks for Vec { }) } } - -pub struct PgProgressStore { - pool: PgPool, - bridge_genesis_checkpoint: u64, - exit_checkpoint: u64, - exit_sender: Option>, -} - -#[async_trait] -impl ProgressStore for PgProgressStore { - async fn load(&mut self, task_name: String) -> anyhow::Result { - let mut conn = self.pool.get()?; - let cp: Option = dsl::progress_store - .find(task_name) - .select(models::ProgressStore::as_select()) - .first(&mut conn) - .optional()?; - Ok(cp - .map(|d| d.checkpoint as u64) - .unwrap_or(self.bridge_genesis_checkpoint)) - } - - async fn save( - &mut self, - task_name: String, - checkpoint_number: CheckpointSequenceNumber, - ) -> anyhow::Result<()> { - if checkpoint_number >= self.exit_checkpoint { - if let Some(sender) = self.exit_sender.take() { - let _ = sender.send(()); - } - } - let mut conn = self.pool.get()?; - diesel::insert_into(schema::progress_store::table) - .values(&models::ProgressStore { - task_name, - checkpoint: checkpoint_number as i64, - target_checkpoint: i64::MAX, - timestamp: None, - }) - .on_conflict(dsl::task_name) - .do_update() - .set(( - columns::checkpoint.eq(checkpoint_number as i64), - columns::timestamp.eq(now), - )) - .execute(&mut conn)?; - Ok(()) - } -} - -fn new_task_name() -> String { - format!("bridge worker - {}", TransactionDigest::random()) -} diff --git a/crates/sui-bridge-indexer/src/sui_worker.rs b/crates/sui-bridge-indexer/src/sui_worker.rs deleted file mode 100644 index 673140a4bc8fe..0000000000000 --- a/crates/sui-bridge-indexer/src/sui_worker.rs +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::postgres_manager::{write, PgPool}; -use crate::{ - metrics::BridgeIndexerMetrics, BridgeDataSource, ProcessedTxnData, SuiTxnError, TokenTransfer, - TokenTransferData, TokenTransferStatus, -}; -use anyhow::Result; -use async_trait::async_trait; -use std::collections::BTreeSet; -use sui_bridge::events::{ - MoveTokenDepositedEvent, MoveTokenTransferApproved, MoveTokenTransferClaimed, -}; -use sui_data_ingestion_core::Worker; -use sui_types::event::Event; -use sui_types::execution_status::ExecutionStatus; -use sui_types::{ - base_types::ObjectID, - effects::TransactionEffectsAPI, - full_checkpoint_content::{CheckpointData, CheckpointTransaction}, - transaction::{TransactionDataAPI, TransactionKind}, - BRIDGE_ADDRESS, SUI_BRIDGE_OBJECT_ID, -}; -use tap::tap::TapFallible; -use tracing::info; - -pub struct SuiBridgeWorker { - bridge_object_ids: BTreeSet, - pg_pool: PgPool, - metrics: BridgeIndexerMetrics, -} - -impl SuiBridgeWorker { - pub fn new( - bridge_object_ids: Vec, - pg_pool: PgPool, - metrics: BridgeIndexerMetrics, - ) -> Self { - let mut bridge_object_ids = bridge_object_ids.into_iter().collect::>(); - bridge_object_ids.insert(SUI_BRIDGE_OBJECT_ID); - Self { - bridge_object_ids, - pg_pool, - metrics, - } - } - - // Return true if the transaction relates to the bridge and is of interest. - fn is_bridge_transaction(&self, tx: &CheckpointTransaction) -> bool { - // TODO: right now this returns true for programmable transactions that - // have the bridge object as input. We can extend later to cover other cases - let txn_data = tx.transaction.transaction_data(); - if let TransactionKind::ProgrammableTransaction(_) = txn_data.kind() { - return tx - .input_objects - .iter() - .any(|obj| self.bridge_object_ids.contains(&obj.id())); - }; - false - } - - // Process a transaction that has been identified as a bridge transaction. - fn process_transaction( - &self, - tx: &CheckpointTransaction, - checkpoint: u64, - timestamp_ms: u64, - ) -> Result> { - self.metrics.total_sui_bridge_transactions.inc(); - match &tx.events { - Some(events) => { - let token_transfers = events.data.iter().try_fold(vec![], |mut result, ev| { - if let Some(data) = - Self::process_sui_event(ev, tx, checkpoint, timestamp_ms, &self.metrics)? - { - result.push(data); - } - Ok::<_, anyhow::Error>(result) - })?; - - if !token_transfers.is_empty() { - info!( - "SUI: Extracted {} bridge token transfer data entries for tx {}.", - token_transfers.len(), - tx.transaction.digest() - ); - } - Ok(token_transfers) - } - None => { - if let ExecutionStatus::Failure { error, command } = tx.effects.status() { - Ok(vec![ProcessedTxnData::Error(SuiTxnError { - tx_digest: *tx.transaction.digest(), - sender: tx.transaction.sender_address(), - timestamp_ms, - failure_status: error.to_string(), - cmd_idx: command.map(|idx| idx as u64), - })]) - } else { - Ok(vec![]) - } - } - } - } - - fn process_sui_event( - ev: &Event, - tx: &CheckpointTransaction, - checkpoint: u64, - timestamp_ms: u64, - metrics: &BridgeIndexerMetrics, - ) -> Result> { - Ok(if ev.type_.address == BRIDGE_ADDRESS { - match ev.type_.name.as_str() { - "TokenDepositedEvent" => { - info!("Observed Sui Deposit {:?}", ev); - metrics.total_sui_token_deposited.inc(); - let move_event: MoveTokenDepositedEvent = bcs::from_bytes(&ev.contents)?; - Some(ProcessedTxnData::TokenTransfer(TokenTransfer { - chain_id: move_event.source_chain, - nonce: move_event.seq_num, - block_height: checkpoint, - timestamp_ms, - txn_hash: tx.transaction.digest().inner().to_vec(), - txn_sender: ev.sender.to_vec(), - status: TokenTransferStatus::Deposited, - gas_usage: tx.effects.gas_cost_summary().net_gas_usage(), - data_source: BridgeDataSource::Sui, - data: Some(TokenTransferData { - destination_chain: move_event.target_chain, - sender_address: move_event.sender_address.clone(), - recipient_address: move_event.target_address.clone(), - token_id: move_event.token_type, - amount: move_event.amount_sui_adjusted, - }), - })) - } - "TokenTransferApproved" => { - info!("Observed Sui Approval {:?}", ev); - metrics.total_sui_token_transfer_approved.inc(); - let event: MoveTokenTransferApproved = bcs::from_bytes(&ev.contents)?; - Some(ProcessedTxnData::TokenTransfer(TokenTransfer { - chain_id: event.message_key.source_chain, - nonce: event.message_key.bridge_seq_num, - block_height: checkpoint, - timestamp_ms, - txn_hash: tx.transaction.digest().inner().to_vec(), - txn_sender: ev.sender.to_vec(), - status: TokenTransferStatus::Approved, - gas_usage: tx.effects.gas_cost_summary().net_gas_usage(), - data_source: BridgeDataSource::Sui, - data: None, - })) - } - "TokenTransferClaimed" => { - info!("Observed Sui Claim {:?}", ev); - metrics.total_sui_token_transfer_claimed.inc(); - let event: MoveTokenTransferClaimed = bcs::from_bytes(&ev.contents)?; - Some(ProcessedTxnData::TokenTransfer(TokenTransfer { - chain_id: event.message_key.source_chain, - nonce: event.message_key.bridge_seq_num, - block_height: checkpoint, - timestamp_ms, - txn_hash: tx.transaction.digest().inner().to_vec(), - txn_sender: ev.sender.to_vec(), - status: TokenTransferStatus::Claimed, - gas_usage: tx.effects.gas_cost_summary().net_gas_usage(), - data_source: BridgeDataSource::Sui, - data: None, - })) - } - _ => { - metrics.total_sui_bridge_txn_other.inc(); - None - } - } - } else { - None - }) - } -} - -#[async_trait] -impl Worker for SuiBridgeWorker { - async fn process_checkpoint(&self, checkpoint: CheckpointData) -> Result<()> { - info!( - "Processing checkpoint [{}] {}: {}", - checkpoint.checkpoint_summary.epoch, - checkpoint.checkpoint_summary.sequence_number, - checkpoint.transactions.len(), - ); - let checkpoint_num = checkpoint.checkpoint_summary.sequence_number; - let timestamp_ms = checkpoint.checkpoint_summary.timestamp_ms; - - let bridge_data = checkpoint - .transactions - .iter() - .filter(|txn| self.is_bridge_transaction(txn)) - .try_fold(vec![], |mut result, txn| { - result.append(&mut self.process_transaction(txn, checkpoint_num, timestamp_ms)?); - Ok::<_, anyhow::Error>(result) - })?; - - write(&self.pg_pool, bridge_data).tap_ok(|_| { - info!("Processed checkpoint [{}] successfully", checkpoint_num,); - self.metrics - .last_committed_sui_checkpoint - .set(checkpoint_num as i64); - }) - } -} From c9a6d67e47ab5deab86639f8620a981d9e767033 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 31 Jul 2024 11:26:39 -0500 Subject: [PATCH 043/232] jsonrpc: use ExecuteTransactionRequestV3 when executing a transaction This patch deprecates the `WaitForLocalExecution` request type that was previously relied upon to calculate object and balance changes. Instead the new `ExecuteTransactionRequestV3` functionality is used to request output object data directly from the validators so that we no longer need to rely on waiting for local execution. The `WaitForLocalExecution` feature still exists and will still properly wait to return a response to a client until after the transaction is executed locally in order to retain the Read-after-Write semantics that some clients may presently rely on. --- .../sui-core/src/transaction_orchestrator.rs | 200 +++++++----------- crates/sui-e2e-tests/tests/full_node_tests.rs | 47 ++-- .../tests/transaction_orchestrator_tests.rs | 31 +-- crates/sui-json-rpc/src/balance_changes.rs | 30 +++ .../src/transaction_execution_api.rs | 104 +++++---- .../test/compatability.test.ts | 3 - 6 files changed, 197 insertions(+), 218 deletions(-) diff --git a/crates/sui-core/src/transaction_orchestrator.rs b/crates/sui-core/src/transaction_orchestrator.rs index 6b655d0358d56..1db608f9e7b6c 100644 --- a/crates/sui-core/src/transaction_orchestrator.rs +++ b/crates/sui-core/src/transaction_orchestrator.rs @@ -35,9 +35,9 @@ use sui_types::effects::{TransactionEffectsAPI, VerifiedCertifiedTransactionEffe use sui_types::error::{SuiError, SuiResult}; use sui_types::executable_transaction::VerifiedExecutableTransaction; use sui_types::quorum_driver_types::{ - ExecuteTransactionRequest, ExecuteTransactionRequestType, ExecuteTransactionRequestV3, - ExecuteTransactionResponse, ExecuteTransactionResponseV3, FinalizedEffects, - QuorumDriverEffectsQueueResult, QuorumDriverError, QuorumDriverResponse, QuorumDriverResult, + ExecuteTransactionRequestType, ExecuteTransactionRequestV3, ExecuteTransactionResponseV3, + FinalizedEffects, IsTransactionExecutedLocally, QuorumDriverEffectsQueueResult, + QuorumDriverError, QuorumDriverResponse, QuorumDriverResult, }; use sui_types::sui_system_state::SuiSystemState; use sui_types::transaction::VerifiedTransaction; @@ -148,125 +148,60 @@ where #[instrument(name = "tx_orchestrator_execute_transaction", level = "debug", skip_all, fields( tx_digest = ?request.transaction.digest(), - tx_type = ?request.transaction_type(), + tx_type = ?request_type, ), err)] pub async fn execute_transaction_block( &self, - request: ExecuteTransactionRequest, + request: ExecuteTransactionRequestV3, + request_type: ExecuteTransactionRequestType, client_addr: Option, - ) -> Result { - // TODO check if tx is already executed on this node. - // Note: since EffectsCert is not stored today, we need to gather that from validators - // (and maybe store it for caching purposes) + ) -> Result<(ExecuteTransactionResponseV3, IsTransactionExecutedLocally), QuorumDriverError> + { let epoch_store = self.validator_state.load_epoch_store_one_call_per_task(); - let transaction = epoch_store - .verify_transaction(request.transaction) - .map_err(QuorumDriverError::InvalidUserSignature)?; - let (_in_flight_metrics_guards, good_response_metrics) = self.update_metrics(&transaction); - let tx_digest = *transaction.digest(); - debug!(?tx_digest, "TO Received transaction execution request."); + let (transaction, response) = self + .execute_transaction_impl(&epoch_store, request, client_addr) + .await?; - let (_e2e_latency_timer, _txn_finality_timer) = if transaction.contains_shared_object() { - ( - self.metrics.request_latency_shared_obj.start_timer(), - self.metrics - .wait_for_finality_latency_shared_obj - .start_timer(), + let executed_locally = if matches!( + request_type, + ExecuteTransactionRequestType::WaitForLocalExecution + ) { + let executable_tx = VerifiedExecutableTransaction::new_from_quorum_execution( + transaction, + response.effects_cert.executed_epoch(), + ); + Self::execute_finalized_tx_locally_with_timeout( + &self.validator_state, + &epoch_store, + &executable_tx, + &response.effects_cert, + &self.metrics, ) + .await + .is_ok() } else { - ( - self.metrics.request_latency_single_writer.start_timer(), - self.metrics - .wait_for_finality_latency_single_writer - .start_timer(), - ) + false }; - // TODO: refactor all the gauge and timer metrics with `monitored_scope` - let wait_for_finality_gauge = self.metrics.wait_for_finality_in_flight.clone(); - wait_for_finality_gauge.inc(); - let _wait_for_finality_gauge = scopeguard::guard(wait_for_finality_gauge, |in_flight| { - in_flight.dec(); - }); - - let ticket = self - .submit( - transaction.clone(), - ExecuteTransactionRequestV3::new_v2(transaction.clone()), - client_addr, - ) - .await - .map_err(|e| { - warn!(?tx_digest, "QuorumDriverInternalError: {e:?}"); - QuorumDriverError::QuorumDriverInternalError(e) - })?; - - let wait_for_local_execution = matches!( - request.request_type, - ExecuteTransactionRequestType::WaitForLocalExecution - ); - - let Ok(result) = timeout(WAIT_FOR_FINALITY_TIMEOUT, ticket).await else { - debug!(?tx_digest, "Timeout waiting for transaction finality."); - self.metrics.wait_for_finality_timeout.inc(); - return Err(QuorumDriverError::TimeoutBeforeFinality); + let QuorumDriverResponse { + effects_cert, + events, + input_objects, + output_objects, + auxiliary_data, + } = response; + + let response = ExecuteTransactionResponseV3 { + effects: FinalizedEffects::new_from_effects_cert(effects_cert.into()), + events, + input_objects, + output_objects, + auxiliary_data, }; - drop(_txn_finality_timer); - drop(_wait_for_finality_gauge); - self.metrics.wait_for_finality_finished.inc(); - - match result { - Err(err) => { - warn!(?tx_digest, "QuorumDriverInternalError: {err:?}"); - Err(QuorumDriverError::QuorumDriverInternalError(err)) - } - Ok(Err(err)) => Err(err), - Ok(Ok(response)) => { - good_response_metrics.inc(); - let QuorumDriverResponse { effects_cert, .. } = response; - if !wait_for_local_execution { - debug!(?tx_digest, ?wait_for_local_execution, "success"); - return Ok(ExecuteTransactionResponse::EffectsCert(Box::new(( - FinalizedEffects::new_from_effects_cert(effects_cert.into()), - response.events.unwrap_or_default(), - false, - )))); - } - - let executable_tx = VerifiedExecutableTransaction::new_from_quorum_execution( - transaction, - effects_cert.executed_epoch(), - ); - - match Self::execute_finalized_tx_locally_with_timeout( - &self.validator_state, - &epoch_store, - &executable_tx, - &effects_cert, - &self.metrics, - ) - .await - { - Ok(_) => { - debug!(?tx_digest, ?wait_for_local_execution, "success"); - - Ok(ExecuteTransactionResponse::EffectsCert(Box::new(( - FinalizedEffects::new_from_effects_cert(effects_cert.into()), - response.events.unwrap_or_default(), - true, - )))) - } - Err(_) => Ok(ExecuteTransactionResponse::EffectsCert(Box::new(( - FinalizedEffects::new_from_effects_cert(effects_cert.into()), - response.events.unwrap_or_default(), - false, - )))), - } - } - } + Ok((response, executed_locally)) } // Utilize the handle_certificate_v3 validator api to request input/output objects @@ -277,11 +212,37 @@ where request: ExecuteTransactionRequestV3, client_addr: Option, ) -> Result { - // TODO check if tx is already executed on this node. - // Note: since EffectsCert is not stored today, we need to gather that from validators - // (and maybe store it for caching purposes) let epoch_store = self.validator_state.load_epoch_store_one_call_per_task(); + let QuorumDriverResponse { + effects_cert, + events, + input_objects, + output_objects, + auxiliary_data, + } = self + .execute_transaction_impl(&epoch_store, request, client_addr) + .await + .map(|(_, r)| r)?; + + Ok(ExecuteTransactionResponseV3 { + effects: FinalizedEffects::new_from_effects_cert(effects_cert.into()), + events, + input_objects, + output_objects, + auxiliary_data, + }) + } + + // TODO check if tx is already executed on this node. + // Note: since EffectsCert is not stored today, we need to gather that from validators + // (and maybe store it for caching purposes) + pub async fn execute_transaction_impl( + &self, + epoch_store: &AuthorityPerEpochStore, + request: ExecuteTransactionRequestV3, + client_addr: Option, + ) -> Result<(VerifiedTransaction, QuorumDriverResponse), QuorumDriverError> { let transaction = epoch_store .verify_transaction(request.transaction.clone()) .map_err(QuorumDriverError::InvalidUserSignature)?; @@ -313,7 +274,7 @@ where }); let ticket = self - .submit(transaction, request, client_addr) + .submit(transaction.clone(), request, client_addr) .await .map_err(|e| { warn!(?tx_digest, "QuorumDriverInternalError: {e:?}"); @@ -338,20 +299,7 @@ where Ok(Err(err)) => Err(err), Ok(Ok(response)) => { good_response_metrics.inc(); - let QuorumDriverResponse { - effects_cert, - events, - input_objects, - output_objects, - auxiliary_data, - } = response; - Ok(ExecuteTransactionResponseV3 { - effects: FinalizedEffects::new_from_effects_cert(effects_cert.into()), - events, - input_objects, - output_objects, - auxiliary_data, - }) + Ok((transaction, response)) } } } diff --git a/crates/sui-e2e-tests/tests/full_node_tests.rs b/crates/sui-e2e-tests/tests/full_node_tests.rs index 841d149c8d4ae..87ee651e03879 100644 --- a/crates/sui-e2e-tests/tests/full_node_tests.rs +++ b/crates/sui-e2e-tests/tests/full_node_tests.rs @@ -36,8 +36,7 @@ use sui_types::messages_grpc::TransactionInfoRequest; use sui_types::object::{Object, ObjectRead, Owner, PastObjectRead}; use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder; use sui_types::quorum_driver_types::{ - ExecuteTransactionRequest, ExecuteTransactionRequestType, ExecuteTransactionResponse, - QuorumDriverResponse, + ExecuteTransactionRequestType, ExecuteTransactionRequestV3, QuorumDriverResponse, }; use sui_types::storage::ObjectStore; use sui_types::transaction::{ @@ -754,16 +753,13 @@ async fn test_full_node_transaction_orchestrator_basic() -> Result<(), anyhow::E let digest = *txn.digest(); let res = transaction_orchestrator .execute_transaction_block( - ExecuteTransactionRequest { - transaction: txn, - request_type: ExecuteTransactionRequestType::WaitForLocalExecution, - }, + ExecuteTransactionRequestV3::new_v2(txn), + ExecuteTransactionRequestType::WaitForLocalExecution, None, ) .await .unwrap_or_else(|e| panic!("Failed to execute transaction {:?}: {:?}", digest, e)); - let ExecuteTransactionResponse::EffectsCert(res) = res; let ( tx, QuorumDriverResponse { @@ -772,11 +768,17 @@ async fn test_full_node_transaction_orchestrator_basic() -> Result<(), anyhow::E .. }, ) = rx.recv().await.unwrap().unwrap(); - let (cte, events, is_executed_locally) = *res; + let (response, is_executed_locally) = res; assert_eq!(*tx.digest(), digest); - assert_eq!(cte.effects.digest(), *certified_txn_effects.digest()); + assert_eq!( + response.effects.effects.digest(), + *certified_txn_effects.digest() + ); assert!(is_executed_locally); - assert_eq!(events.digest(), txn_events.unwrap_or_default().digest()); + assert_eq!( + response.events.unwrap_or_default().digest(), + txn_events.unwrap_or_default().digest() + ); // verify that the node has sequenced and executed the txn fullnode.state().get_executed_transaction_and_effects(digest, kv_store.clone()).await .unwrap_or_else(|e| panic!("Fullnode does not know about the txn {:?} that was executed with WaitForLocalExecution: {:?}", digest, e)); @@ -786,16 +788,13 @@ async fn test_full_node_transaction_orchestrator_basic() -> Result<(), anyhow::E let digest = *txn.digest(); let res = transaction_orchestrator .execute_transaction_block( - ExecuteTransactionRequest { - transaction: txn, - request_type: ExecuteTransactionRequestType::WaitForEffectsCert, - }, + ExecuteTransactionRequestV3::new_v2(txn), + ExecuteTransactionRequestType::WaitForEffectsCert, None, ) .await .unwrap_or_else(|e| panic!("Failed to execute transaction {:?}: {:?}", digest, e)); - let ExecuteTransactionResponse::EffectsCert(res) = res; let ( tx, QuorumDriverResponse { @@ -804,10 +803,16 @@ async fn test_full_node_transaction_orchestrator_basic() -> Result<(), anyhow::E .. }, ) = rx.recv().await.unwrap().unwrap(); - let (cte, events, is_executed_locally) = *res; + let (response, is_executed_locally) = res; assert_eq!(*tx.digest(), digest); - assert_eq!(cte.effects.digest(), *certified_txn_effects.digest()); - assert_eq!(txn_events.unwrap_or_default().digest(), events.digest()); + assert_eq!( + response.effects.effects.digest(), + *certified_txn_effects.digest() + ); + assert_eq!( + txn_events.unwrap_or_default().digest(), + response.events.unwrap_or_default().digest() + ); assert!(!is_executed_locally); fullnode .state() @@ -1189,10 +1194,8 @@ async fn test_pass_back_no_object() -> Result<(), anyhow::Error> { let digest = *tx.digest(); let _res = transaction_orchestrator .execute_transaction_block( - ExecuteTransactionRequest { - transaction: tx, - request_type: ExecuteTransactionRequestType::WaitForLocalExecution, - }, + ExecuteTransactionRequestV3::new_v2(tx), + ExecuteTransactionRequestType::WaitForLocalExecution, None, ) .await diff --git a/crates/sui-e2e-tests/tests/transaction_orchestrator_tests.rs b/crates/sui-e2e-tests/tests/transaction_orchestrator_tests.rs index 3c0a50eb8074f..01abe0d6078f5 100644 --- a/crates/sui-e2e-tests/tests/transaction_orchestrator_tests.rs +++ b/crates/sui-e2e-tests/tests/transaction_orchestrator_tests.rs @@ -13,8 +13,8 @@ use sui_test_transaction_builder::{ }; use sui_types::effects::TransactionEffectsAPI; use sui_types::quorum_driver_types::{ - ExecuteTransactionRequest, ExecuteTransactionRequestType, ExecuteTransactionRequestV3, - ExecuteTransactionResponse, FinalizedEffects, QuorumDriverError, + ExecuteTransactionRequestType, ExecuteTransactionRequestV3, ExecuteTransactionResponseV3, + FinalizedEffects, IsTransactionExecutedLocally, QuorumDriverError, }; use sui_types::transaction::Transaction; use test_cluster::TestClusterBuilder; @@ -63,7 +63,7 @@ async fn test_blocking_execution() -> Result<(), anyhow::Error> { let txn = txns.swap_remove(0); let digest = *txn.digest(); - let res = execute_with_orchestrator( + let (_, executed_locally) = execute_with_orchestrator( &orchestrator, txn, ExecuteTransactionRequestType::WaitForLocalExecution, @@ -71,8 +71,6 @@ async fn test_blocking_execution() -> Result<(), anyhow::Error> { .await .unwrap_or_else(|e| panic!("Failed to execute transaction {:?}: {:?}", digest, e)); - let ExecuteTransactionResponse::EffectsCert(result) = res; - let (_, _, executed_locally) = *result; assert!(executed_locally); let metrics = KeyValueStoreMetrics::new_for_tests(); @@ -250,18 +248,15 @@ async fn test_tx_across_epoch_boundaries() { tokio::task::spawn(async move { match to .execute_transaction_block( - ExecuteTransactionRequest { - transaction: tx.clone(), - request_type: ExecuteTransactionRequestType::WaitForEffectsCert, - }, - Some(make_socket_addr()), + ExecuteTransactionRequestV3::new_v2(tx.clone()), + ExecuteTransactionRequestType::WaitForEffectsCert, + None, ) .await { - Ok(ExecuteTransactionResponse::EffectsCert(res)) => { + Ok((response, _)) => { info!(?tx_digest, "tx result: ok"); - let (effects_cert, _, _) = *res; - result_tx.send(effects_cert).await.unwrap(); + result_tx.send(response.effects).await.unwrap(); } Err(QuorumDriverError::TimeoutBeforeFinality) => { info!(?tx_digest, "tx result: timeout and will retry") @@ -294,15 +289,9 @@ async fn execute_with_orchestrator( orchestrator: &TransactiondOrchestrator, txn: Transaction, request_type: ExecuteTransactionRequestType, -) -> Result { +) -> Result<(ExecuteTransactionResponseV3, IsTransactionExecutedLocally), QuorumDriverError> { orchestrator - .execute_transaction_block( - ExecuteTransactionRequest { - transaction: txn, - request_type, - }, - Some(make_socket_addr()), - ) + .execute_transaction_block(ExecuteTransactionRequestV3::new_v2(txn), request_type, None) .await } diff --git a/crates/sui-json-rpc/src/balance_changes.rs b/crates/sui-json-rpc/src/balance_changes.rs index 91b4b3011edaf..ff4e445713623 100644 --- a/crates/sui-json-rpc/src/balance_changes.rs +++ b/crates/sui-json-rpc/src/balance_changes.rs @@ -211,6 +211,36 @@ impl

ObjectProviderCache

{ provider, } } + + pub fn new_with_output_objects(provider: P, output_objects: Vec) -> Self { + let mut object_cache = BTreeMap::new(); + let mut last_version_cache = BTreeMap::new(); + + for object in output_objects { + let object_id = object.id(); + let version = object.version(); + + let key = (object_id, version); + object_cache.insert(key, object.clone()); + + match last_version_cache.get_mut(&key) { + Some(existing_seq_number) => { + if version > *existing_seq_number { + *existing_seq_number = version + } + } + None => { + last_version_cache.insert(key, version); + } + } + } + + Self { + object_cache: RwLock::new(object_cache), + last_version_cache: RwLock::new(last_version_cache), + provider, + } + } } #[async_trait] diff --git a/crates/sui-json-rpc/src/transaction_execution_api.rs b/crates/sui-json-rpc/src/transaction_execution_api.rs index dc875e54f7059..749cb3ebee8a4 100644 --- a/crates/sui-json-rpc/src/transaction_execution_api.rs +++ b/crates/sui-json-rpc/src/transaction_execution_api.rs @@ -26,7 +26,7 @@ use sui_types::crypto::default_hash; use sui_types::digests::TransactionDigest; use sui_types::effects::TransactionEffectsAPI; use sui_types::quorum_driver_types::{ - ExecuteTransactionRequest, ExecuteTransactionRequestType, ExecuteTransactionResponse, + ExecuteTransactionRequestType, ExecuteTransactionRequestV3, ExecuteTransactionResponseV3, }; use sui_types::signature::GenericSignature; use sui_types::sui_serde::BigInt; @@ -75,11 +75,10 @@ impl TransactionExecutionApi { tx_bytes: Base64, signatures: Vec, opts: Option, - request_type: Option, ) -> Result< ( + ExecuteTransactionRequestV3, SuiTransactionBlockResponseOptions, - ExecuteTransactionRequestType, SuiAddress, Vec, Transaction, @@ -89,12 +88,7 @@ impl TransactionExecutionApi { SuiRpcInputError, > { let opts = opts.unwrap_or_default(); - let request_type = match (request_type, opts.require_local_execution()) { - (Some(ExecuteTransactionRequestType::WaitForEffectsCert), true) => { - Err(SuiRpcInputError::InvalidExecuteTransactionRequestType)? - } - (t, _) => t.unwrap_or_else(|| opts.default_execution_request_type()), - }; + let tx_data: TransactionData = self.convert_bytes(tx_bytes)?; let sender = tx_data.sender(); let input_objs = tx_data.input_objects().unwrap_or_default(); @@ -118,9 +112,18 @@ impl TransactionExecutionApi { } else { None }; + + let request = ExecuteTransactionRequestV3 { + transaction: txn.clone(), + include_events: opts.show_events, + include_input_objects: opts.show_balance_changes || opts.show_object_changes, + include_output_objects: opts.show_balance_changes || opts.show_object_changes, + include_auxiliary_data: false, + }; + Ok(( + request, opts, - request_type, sender, input_objs, txn, @@ -136,25 +139,24 @@ impl TransactionExecutionApi { opts: Option, request_type: Option, ) -> Result { - let (opts, request_type, sender, input_objs, txn, transaction, raw_transaction) = - self.prepare_execute_transaction_block(tx_bytes, signatures, opts, request_type)?; + let request_type = + request_type.unwrap_or(ExecuteTransactionRequestType::WaitForEffectsCert); + let (request, opts, sender, input_objs, txn, transaction, raw_transaction) = + self.prepare_execute_transaction_block(tx_bytes, signatures, opts)?; let digest = *txn.digest(); let transaction_orchestrator = self.transaction_orchestrator.clone(); let orch_timer = self.metrics.orchestrator_latency_ms.start_timer(); - let response = spawn_monitored_task!(transaction_orchestrator.execute_transaction_block( - ExecuteTransactionRequest { - transaction: txn, - request_type, - }, - None, - )) + let (response, is_executed_locally) = spawn_monitored_task!( + transaction_orchestrator.execute_transaction_block(request, request_type, None) + ) .await? .map_err(Error::from)?; drop(orch_timer); self.handle_post_orchestration( response, + is_executed_locally, opts, digest, input_objs, @@ -167,7 +169,8 @@ impl TransactionExecutionApi { async fn handle_post_orchestration( &self, - response: ExecuteTransactionResponse, + response: ExecuteTransactionResponseV3, + is_executed_locally: bool, opts: SuiTransactionBlockResponseOptions, digest: TransactionDigest, input_objs: Vec, @@ -176,49 +179,56 @@ impl TransactionExecutionApi { sender: SuiAddress, ) -> Result { let _post_orch_timer = self.metrics.post_orchestrator_latency_ms.start_timer(); - let ExecuteTransactionResponse::EffectsCert(cert) = response; - let (effects, transaction_events, is_executed_locally) = *cert; - let mut events: Option = None; - if opts.show_events { + + let events = if opts.show_events { let epoch_store = self.state.load_epoch_store_one_call_per_task(); let backing_package_store = self.state.get_backing_package_store(); let mut layout_resolver = epoch_store .executor() .type_layout_resolver(Box::new(backing_package_store.as_ref())); - events = Some(SuiTransactionBlockEvents::try_from( - transaction_events, + Some(SuiTransactionBlockEvents::try_from( + response.events.unwrap_or_default(), digest, None, layout_resolver.as_mut(), - )?); - } - - let object_cache = ObjectProviderCache::new(self.state.clone()); - let balance_changes = if opts.show_balance_changes && is_executed_locally { - Some( - get_balance_changes_from_effect(&object_cache, &effects.effects, input_objs, None) - .await?, - ) + )?) } else { None }; - let object_changes = if opts.show_object_changes && is_executed_locally { - Some( + + let object_cache = response.output_objects.map(|output_objects| { + ObjectProviderCache::new_with_output_objects(self.state.clone(), output_objects) + }); + + let balance_changes = match &object_cache { + Some(object_cache) if opts.show_balance_changes => Some( + get_balance_changes_from_effect( + object_cache, + &response.effects.effects, + input_objs, + None, + ) + .await?, + ), + _ => None, + }; + + let object_changes = match &object_cache { + Some(object_cache) if opts.show_object_changes => Some( get_object_changes( - &object_cache, + object_cache, sender, - effects.effects.modified_at_versions(), - effects.effects.all_changed_objects(), - effects.effects.all_removed_objects(), + response.effects.effects.modified_at_versions(), + response.effects.effects.all_changed_objects(), + response.effects.effects.all_removed_objects(), ) .await?, - ) - } else { - None + ), + _ => None, }; let raw_effects = if opts.show_raw_effects { - bcs::to_bytes(&effects.effects)? + bcs::to_bytes(&response.effects.effects)? } else { vec![] }; @@ -227,7 +237,9 @@ impl TransactionExecutionApi { digest, transaction, raw_transaction, - effects: opts.show_effects.then_some(effects.effects.try_into()?), + effects: opts + .show_effects + .then_some(response.effects.effects.try_into()?), events, object_changes, balance_changes, diff --git a/sdk/graphql-transport/test/compatability.test.ts b/sdk/graphql-transport/test/compatability.test.ts index 20cf47a227fdb..d8a0fbee58d81 100644 --- a/sdk/graphql-transport/test/compatability.test.ts +++ b/sdk/graphql-transport/test/compatability.test.ts @@ -608,9 +608,6 @@ describe('GraphQL SuiClient compatibility', () => { }, })) as SuiTransactionBlockResponse & { rawEffects: unknown }; - // Deleted gas coin isn't included in changes when executing transaction block - rpc.objectChanges?.pop(); - expect(graphql).toEqual(rpc); }); From 0851b31567e87b4d009fbe832cb09f45a43fabdd Mon Sep 17 00:00:00 2001 From: hayes-mysten <135670682+hayes-mysten@users.noreply.github.com> Date: Wed, 31 Jul 2024 10:41:18 -0700 Subject: [PATCH 044/232] [sdk] deprecate requestType option in SDK (#18854) ## Description Describe the changes or additions included in this PR. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .changeset/beige-windows-build.md | 6 ++ sdk/typescript/scripts/generate.ts | 14 +++- sdk/typescript/src/client/types/generated.ts | 66 +++++++++++++++---- sdk/typescript/src/client/types/params.ts | 5 +- .../suiSignAndExecuteTransactionBlock.ts | 3 +- 5 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 .changeset/beige-windows-build.md diff --git a/.changeset/beige-windows-build.md b/.changeset/beige-windows-build.md new file mode 100644 index 0000000000000..205f9bb83ee67 --- /dev/null +++ b/.changeset/beige-windows-build.md @@ -0,0 +1,6 @@ +--- +'@mysten/wallet-standard': minor +'@mysten/sui': minor +--- + +Deprecate requestType option when executing transactions diff --git a/sdk/typescript/scripts/generate.ts b/sdk/typescript/scripts/generate.ts index 906bfd075b1a0..e8b70d7ef725c 100644 --- a/sdk/typescript/scripts/generate.ts +++ b/sdk/typescript/scripts/generate.ts @@ -55,6 +55,7 @@ const options: { { alias?: string; typeAlias?: string; + deprecated?: string; } >; } @@ -173,6 +174,9 @@ const options: { alias: 'signature', typeAlias: 'string | string[]', }, + requestType: { + deprecated: 'requestType will be ignored by JSON RPC in the future', + }, }, }, suix_queryEvents: { @@ -190,7 +194,7 @@ const options: { }, tx_bytes: { alias: 'transactionBlock', - typeAlias: 'TransactionBlock | Uint8Array | string', + typeAlias: 'Transaction | Uint8Array | string', }, gas_price: { typeAlias: 'bigint | number', @@ -339,7 +343,7 @@ methodGenerator.imports.push( ts.factory.createImportSpecifier( false, undefined, - ts.factory.createIdentifier('TransactionBlock'), + ts.factory.createIdentifier('Transaction'), ), ]), ), @@ -364,8 +368,12 @@ async function createMethodParams(method: OpenRpcMethod) { return !methodOptions.flattenParams?.includes(param.name); }) .map(async (param) => { + const paramOptions = + methodOptions.params?.[normalizeParamName(method.name, param.name)] ?? {}; return withDescription( - param, + paramOptions.deprecated + ? { description: `@deprecated ${paramOptions.deprecated}` } + : param, ts.factory.createPropertySignature( undefined, normalizeParamName(method.name, param.name), diff --git a/sdk/typescript/src/client/types/generated.ts b/sdk/typescript/src/client/types/generated.ts index 4b81412996da6..461fe8e3ae6ad 100644 --- a/sdk/typescript/src/client/types/generated.ts +++ b/sdk/typescript/src/client/types/generated.ts @@ -95,6 +95,10 @@ export type CompressedSignature = | { ZkLogin: string; }; +/** Uses an enum to allow for future expansion of the ConsensusDeterminedVersionAssignments. */ +export type ConsensusDeterminedVersionAssignments = { + CancelledTransactions: [string, [string, string][]][]; +}; export type SuiParsedData = | { dataType: 'moveObject'; @@ -356,13 +360,6 @@ export type InputObjectKind = mutable?: boolean; }; }; -export interface LoadedChildObject { - objectId: string; - sequenceNumber: string; -} -export interface LoadedChildObjectsResponse { - loadedChildObjects: LoadedChildObject[]; -} export interface MoveCallParams { arguments: unknown[]; function: string; @@ -396,7 +393,15 @@ export type MoveValue = id: string; } | MoveStruct - | null; + | null + | MoveVariant; +export interface MoveVariant { + fields: { + [key: string]: MoveValue; + }; + type: string; + variant: string; +} /** The struct that contains signatures and public keys necessary for authenticating a MultiSig. */ export interface MultiSig { /** A bitmap that indicates the position of which public key the signature should be authenticated with. */ @@ -410,8 +415,8 @@ export interface MultiSig { sigs: CompressedSignature[]; } /** - * Deprecated, use [struct MultiSig] instead. The struct that contains signatures and public keys - * necessary for authenticating a MultiSigLegacy. + * Deprecated, use [struct MultiSig] instead. The struct that contains signatures and public keys necessary + * for authenticating a MultiSigLegacy. */ export interface MultiSigLegacy { /** A bitmap that indicates the position of which public key the signature should be authenticated with. */ @@ -715,6 +720,24 @@ export interface PaginatedTransactionResponse { hasNextPage: boolean; nextCursor?: string | null; } +/** + * An passkey authenticator with parsed fields. See field defition below. Can be initialized from + * [struct RawPasskeyAuthenticator]. + */ +export interface PasskeyAuthenticator { + /** + * `authenticatorData` is a bytearray that encodes + * [Authenticator Data](https://www.w3.org/TR/webauthn-2/#sctn-authenticator-data) structure returned + * by the authenticator attestation response as is. + */ + authenticator_data: number[]; + /** + * `clientDataJSON` contains a JSON-compatible UTF-8 encoded string of the client data which is passed + * to the authenticator by the client during the authentication request (see + * [CollectedClientData](https://www.w3.org/TR/webauthn-2/#dictdef-collectedclientdata)) + */ + client_data_json: string; +} export interface ProtocolConfig { attributes: { [key: string]: ProtocolConfigValue | null; @@ -738,6 +761,9 @@ export type ProtocolConfigValue = } | { f64: string; + } + | { + bool: string; }; export type PublicKey = | { @@ -751,6 +777,9 @@ export type PublicKey = } | { ZkLogin: string; + } + | { + Passkey: string; }; export type RPCTransactionRequestParams = | { @@ -895,6 +924,12 @@ export type SuiEndOfEpochTransactionKind = } | { AuthenticatorStateExpire: SuiAuthenticatorStateExpire; + } + | { + BridgeStateCreate: string; + } + | { + BridgeCommitteeUpdate: string; }; export interface SuiExecutionResult { /** The value of any arguments that were mutably borrowed. Non-mut borrowed values are not included */ @@ -1375,6 +1410,15 @@ export type SuiTransactionBlockKind = epoch: string; kind: 'ConsensusCommitPrologueV2'; round: string; + } + | { + commit_timestamp_ms: string; + consensus_commit_digest: string; + consensus_determined_version_assignments: ConsensusDeterminedVersionAssignments; + epoch: string; + kind: 'ConsensusCommitPrologueV3'; + round: string; + sub_dag_index?: string | null; }; export interface SuiTransactionBlockResponse { balanceChanges?: BalanceChange[] | null; @@ -1468,9 +1512,9 @@ export interface TransferObjectParams { } /** Identifies a struct and the module it was defined in */ export interface TypeOrigin { + datatype_name: string; module_name: string; package: string; - struct_name: string; } /** Upgraded package info for the linkage table */ export interface UpgradeInfo { diff --git a/sdk/typescript/src/client/types/params.ts b/sdk/typescript/src/client/types/params.ts index 97c5e0c322ae5..3c1d030771b19 100644 --- a/sdk/typescript/src/client/types/params.ts +++ b/sdk/typescript/src/client/types/params.ts @@ -56,7 +56,7 @@ export interface ExecuteTransactionBlockParams { signature: string | string[]; /** options for specifying the content to be returned */ options?: RpcTypes.SuiTransactionBlockResponseOptions | null | undefined; - /** The request type, derived from `SuiTransactionBlockResponseOptions` if None */ + /** @deprecated requestType will be ignored by JSON RPC in the future */ requestType?: RpcTypes.ExecuteTransactionRequestType | null | undefined; } /** Return the first four bytes of the chain's genesis checkpoint digest. */ @@ -85,9 +85,6 @@ export interface GetEventsParams { } /** Return the sequence number of the latest checkpoint that has been executed */ export interface GetLatestCheckpointSequenceNumberParams {} -export interface GetLoadedChildObjectsParams { - digest: string; -} /** Return the argument types of a Move function, based on normalized Type. */ export interface GetMoveFunctionArgTypesParams { package: string; diff --git a/sdk/wallet-standard/src/features/suiSignAndExecuteTransactionBlock.ts b/sdk/wallet-standard/src/features/suiSignAndExecuteTransactionBlock.ts index 941aabdc9a77a..4601d53cde112 100644 --- a/sdk/wallet-standard/src/features/suiSignAndExecuteTransactionBlock.ts +++ b/sdk/wallet-standard/src/features/suiSignAndExecuteTransactionBlock.ts @@ -37,8 +37,7 @@ export type SuiSignAndExecuteTransactionBlockMethod = ( /** Input for signing and sending transactions. */ export interface SuiSignAndExecuteTransactionBlockInput extends SuiSignTransactionBlockInput { /** - * `WaitForEffectsCert` or `WaitForLocalExecution`, see details in `ExecuteTransactionRequestType`. - * Defaults to `WaitForLocalExecution` if options.showEffects or options.showEvents is true + * @deprecated requestType will be ignored by JSON RPC in the future */ requestType?: ExecuteTransactionRequestType; /** specify which fields to return (e.g., transaction, effects, events, etc). By default, only the transaction digest will be returned. */ From 801989d439b052fee233c6291fd6162ef1be4936 Mon Sep 17 00:00:00 2001 From: Tom Cat <48447545+tx-tomcat@users.noreply.github.com> Date: Thu, 1 Aug 2024 01:37:39 +0700 Subject: [PATCH 045/232] [Linter] Missing key (#16616) ## Description Adds a new linter rule targeting structs that have an 'id' field of type 'UID' but lack the 'key' ability. Key features of this linter: - Identifies structs with an 'id' field of type 'UID'. - Checks if these structs have the 'key' ability. - Issues a warning if the 'key' ability is missing. The linter specifically looks for structs that represent Sui objects. These are identified by: `has_id_field_of_type_uid`. This function checks for a field named "id" of type `sui::object::UID` ## Test plan Added more use case including false positive, false negative case ## Release notes - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [X] CLI: A new Move lint will warn when a struct has a `id: UID` field, but lacks the `key` ability - [ ] Rust SDK: --------- Co-authored-by: jamedzung Co-authored-by: Todd Nowacki --- .../src/sui_mode/linters/missing_key.rs | 80 +++++++++++++++++++ .../move-compiler/src/sui_mode/linters/mod.rs | 11 +++ .../linter/edge_case_lint_missing_key.move | 21 +++++ .../false_negative_lint_missing_key.exp | 10 +++ .../false_negative_lint_missing_key.move | 31 +++++++ .../linter/no_trigger_lint_missing_key.exp | 60 ++++++++++++++ .../linter/no_trigger_lint_missing_key.move | 43 ++++++++++ .../linter/suppress_lint_missing_key.move | 15 ++++ .../linter/trigger_lint_missing_key.exp | 20 +++++ .../linter/trigger_lint_missing_key.move | 36 +++++++++ 10 files changed, 327 insertions(+) create mode 100644 external-crates/move/crates/move-compiler/src/sui_mode/linters/missing_key.rs create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/edge_case_lint_missing_key.move create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/false_negative_lint_missing_key.exp create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/false_negative_lint_missing_key.move create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/no_trigger_lint_missing_key.exp create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/no_trigger_lint_missing_key.move create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/suppress_lint_missing_key.move create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/trigger_lint_missing_key.exp create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/trigger_lint_missing_key.move diff --git a/external-crates/move/crates/move-compiler/src/sui_mode/linters/missing_key.rs b/external-crates/move/crates/move-compiler/src/sui_mode/linters/missing_key.rs new file mode 100644 index 0000000000000..cd20476c33a53 --- /dev/null +++ b/external-crates/move/crates/move-compiler/src/sui_mode/linters/missing_key.rs @@ -0,0 +1,80 @@ +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +//! This linter rule checks for structs with an `id` field of type `UID` without the `key` ability. + +use super::{LinterDiagnosticCategory, LinterDiagnosticCode, LINT_WARNING_PREFIX}; +use crate::expansion::ast::ModuleIdent; +use crate::parser::ast::DatatypeName; +use crate::{ + diag, + diagnostics::{ + codes::{custom, DiagnosticInfo, Severity}, + WarningFilters, + }, + naming::ast::{StructDefinition, StructFields}, + parser::ast::Ability_, + shared::CompilationEnv, + typing::{ + ast as T, + visitor::{TypingVisitorConstructor, TypingVisitorContext}, + }, +}; + +const MISSING_KEY_ABILITY_DIAG: DiagnosticInfo = custom( + LINT_WARNING_PREFIX, + Severity::Warning, + LinterDiagnosticCategory::Sui as u8, + LinterDiagnosticCode::MissingKey as u8, + "The struct's first field is 'id' of type 'sui::object::UID' but is missing the 'key' ability.", +); + +pub struct MissingKeyVisitor; + +pub struct Context<'a> { + env: &'a mut CompilationEnv, +} +impl TypingVisitorConstructor for MissingKeyVisitor { + type Context<'a> = Context<'a>; + + fn context<'a>(env: &'a mut CompilationEnv, _program: &T::Program) -> Self::Context<'a> { + Context { env } + } +} + +impl TypingVisitorContext for Context<'_> { + fn add_warning_filter_scope(&mut self, filter: WarningFilters) { + self.env.add_warning_filter_scope(filter) + } + + fn pop_warning_filter_scope(&mut self) { + self.env.pop_warning_filter_scope() + } + + const VISIT_TYPES: bool = true; + + fn visit_struct_custom( + &mut self, + _module: ModuleIdent, + _struct_name: DatatypeName, + sdef: &mut StructDefinition, + ) -> bool { + if first_field_has_id_field_of_type_uid(sdef) && lacks_key_ability(sdef) { + let uid_msg = + "Struct's first field has an 'id' field of type 'sui::object::UID' but is missing the 'key' ability."; + let diagnostic = diag!(MISSING_KEY_ABILITY_DIAG, (sdef.loc, uid_msg)); + self.env.add_diag(diagnostic); + } + false + } +} + +fn first_field_has_id_field_of_type_uid(sdef: &StructDefinition) -> bool { + matches!(&sdef.fields, StructFields::Defined(_, fields) if fields.iter().any(|(_, symbol, ftype)| { + ftype.0 == 0 && symbol == &symbol!("id") && ftype.1.value.is("sui", "object", "UID") + })) +} + +fn lacks_key_ability(sdef: &StructDefinition) -> bool { + !sdef.abilities.has_ability_(Ability_::Key) +} diff --git a/external-crates/move/crates/move-compiler/src/sui_mode/linters/mod.rs b/external-crates/move/crates/move-compiler/src/sui_mode/linters/mod.rs index 012d78fc852fb..7f48af22a73cc 100644 --- a/external-crates/move/crates/move-compiler/src/sui_mode/linters/mod.rs +++ b/external-crates/move/crates/move-compiler/src/sui_mode/linters/mod.rs @@ -18,6 +18,7 @@ pub mod coin_field; pub mod collection_equality; pub mod custom_state_change; pub mod freeze_wrapped; +pub mod missing_key; pub mod public_random; pub mod self_transfer; pub mod share_owned; @@ -68,6 +69,7 @@ pub const COIN_FIELD_FILTER_NAME: &str = "coin_field"; pub const FREEZE_WRAPPED_FILTER_NAME: &str = "freeze_wrapped"; pub const COLLECTION_EQUALITY_FILTER_NAME: &str = "collection_equality"; pub const PUBLIC_RANDOM_FILTER_NAME: &str = "public_random"; +pub const MISSING_KEY_FILTER_NAME: &str = "missing_key"; pub const RANDOM_MOD_NAME: &str = "random"; pub const RANDOM_STRUCT_NAME: &str = "Random"; @@ -84,6 +86,7 @@ pub enum LinterDiagnosticCode { FreezeWrapped, CollectionEquality, PublicRandom, + MissingKey, } pub fn known_filters() -> (Option, Vec) { @@ -131,7 +134,14 @@ pub fn known_filters() -> (Option, Vec) { LinterDiagnosticCode::PublicRandom as u8, Some(PUBLIC_RANDOM_FILTER_NAME), ), + WarningFilter::code( + Some(LINT_WARNING_PREFIX), + LinterDiagnosticCategory::Sui as u8, + LinterDiagnosticCode::MissingKey as u8, + Some(MISSING_KEY_FILTER_NAME), + ), ]; + (Some(ALLOW_ATTR_CATEGORY.into()), filters) } @@ -147,6 +157,7 @@ pub fn linter_visitors(level: LintLevel) -> Vec { freeze_wrapped::FreezeWrappedVisitor.visitor(), collection_equality::CollectionEqualityVisitor.visitor(), public_random::PublicRandomVisitor.visitor(), + missing_key::MissingKeyVisitor.visitor(), ] } } diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/edge_case_lint_missing_key.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/edge_case_lint_missing_key.move new file mode 100644 index 0000000000000..644fddfdcb112 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/edge_case_lint_missing_key.move @@ -0,0 +1,21 @@ +module a::edge_cases { + use sui::another::UID as AnotherUID; + + // Test case with a different UID type + struct DifferentUID { + id: AnotherUID, + } + +} + +module sui::object { + struct UID has store { + id: address, + } +} + +module sui::another { + struct UID has store { + id: address, + } +} diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/false_negative_lint_missing_key.exp b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/false_negative_lint_missing_key.exp new file mode 100644 index 0000000000000..59e10198d3f98 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/false_negative_lint_missing_key.exp @@ -0,0 +1,10 @@ +warning[Lint W99007]: The struct's first field is 'id' of type 'sui::object::UID' but is missing the 'key' ability. + ┌─ tests/sui_mode/linter/false_negative_lint_missing_key.move:22:5 + │ +22 │ ╭ struct Wrapper { +23 │ │ id: UID, +24 │ │ } + │ ╰─────^ Struct's first field has an 'id' field of type 'sui::object::UID' but is missing the 'key' ability. + │ + = This warning can be suppressed with '#[allow(lint(missing_key))]' applied to the 'module' or module member ('const', 'fun', or 'struct') + diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/false_negative_lint_missing_key.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/false_negative_lint_missing_key.move new file mode 100644 index 0000000000000..c1db6e30d4591 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/false_negative_lint_missing_key.move @@ -0,0 +1,31 @@ +module a::trigger_lint_cases { + use sui::object::UID; + + // False negative cases (should trigger warning but might not): + + // 1. Different field name + struct FN1_MissingKeyWithDifferentFieldName { + uid: UID, + } + + // 2. UID field not first + struct FN2_MissingKeyUIDNotFirst { + point: u64, + id: UID, + } + + // 3. Nested UID + struct FN3_MissingKeyNestedUID { + wrapper: Wrapper, + } + + struct Wrapper { + id: UID, + } +} + +module sui::object { + struct UID has store { + id: address, + } +} \ No newline at end of file diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/no_trigger_lint_missing_key.exp b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/no_trigger_lint_missing_key.exp new file mode 100644 index 0000000000000..0bb5344b4bbc7 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/no_trigger_lint_missing_key.exp @@ -0,0 +1,60 @@ +error[Sui E02007]: invalid object declaration + ┌─ tests/sui_mode/linter/no_trigger_lint_missing_key.move:13:9 + │ +12 │ struct FP1_HasKeyButDifferentFieldName has key { + │ --- The 'key' ability is used to declare objects in Sui +13 │ uid: UID, + │ ^^^ Invalid object 'FP1_HasKeyButDifferentFieldName'. Structs with the 'key' ability must have 'id: sui::object::UID' as their first field + +error[Sui E02007]: invalid object declaration + ┌─ tests/sui_mode/linter/no_trigger_lint_missing_key.move:18:9 + │ +17 │ struct FP2_HasKeyUIDNotFirst has key { + │ --- The 'key' ability is used to declare objects in Sui +18 │ point: u64, + │ ^^^^^ Invalid object 'FP2_HasKeyUIDNotFirst'. Structs with the 'key' ability must have 'id: sui::object::UID' as their first field + +error[Sui E02007]: invalid object declaration + ┌─ tests/sui_mode/linter/no_trigger_lint_missing_key.move:24:9 + │ +23 │ struct FP3_HasKeyButIDNotUID has key { + │ --- The 'key' ability is used to declare objects in Sui +24 │ id: address, + │ ^^ ------- But found type: 'address' + │ │ + │ Invalid object 'FP3_HasKeyButIDNotUID'. Structs with the 'key' ability must have 'id: sui::object::UID' as their first field + +warning[Lint W99007]: The struct's first field is 'id' of type 'sui::object::UID' but is missing the 'key' ability. + ┌─ tests/sui_mode/linter/no_trigger_lint_missing_key.move:34:5 + │ +34 │ ╭ struct FP5_HasAbilityButNotKey has store, copy, drop { +35 │ │ id: UID, +36 │ │ } + │ ╰─────^ Struct's first field has an 'id' field of type 'sui::object::UID' but is missing the 'key' ability. + │ + = This warning can be suppressed with '#[allow(lint(missing_key))]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +error[E05001]: ability constraint not satisfied + ┌─ tests/sui_mode/linter/no_trigger_lint_missing_key.move:35:13 + │ +35 │ id: UID, + │ ^^^ + │ │ + │ Invalid field type. The struct was declared with the ability 'copy' so all fields require the ability 'copy' + │ The type 'sui::object::UID' does not have the ability 'copy' + · +40 │ struct UID has store { + │ --- To satisfy the constraint, the 'copy' ability would need to be added here + +error[E05001]: ability constraint not satisfied + ┌─ tests/sui_mode/linter/no_trigger_lint_missing_key.move:35:13 + │ +35 │ id: UID, + │ ^^^ + │ │ + │ Invalid field type. The struct was declared with the ability 'drop' so all fields require the ability 'drop' + │ The type 'sui::object::UID' does not have the ability 'drop' + · +40 │ struct UID has store { + │ --- To satisfy the constraint, the 'drop' ability would need to be added here + diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/no_trigger_lint_missing_key.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/no_trigger_lint_missing_key.move new file mode 100644 index 0000000000000..4d5b6a0009d80 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/no_trigger_lint_missing_key.move @@ -0,0 +1,43 @@ +module a::no_trigger_lint_cases { + use sui::object::UID; + + // This should not trigger the linter warning (true negative) + struct HasKeyAbility has key { + id: UID, + } + + // False positive cases (should not trigger warning but might): + + // 1. Has key but different field name + struct FP1_HasKeyButDifferentFieldName has key { + uid: UID, + } + + // 2. Has key but UID field not first + struct FP2_HasKeyUIDNotFirst has key { + point: u64, + id: UID, + } + + // 3. Has key with ID field of different type + struct FP3_HasKeyButIDNotUID has key { + id: address, + } + + // 4. Suppress warning + #[allow(lint(missing_key))] + struct SuppressWarning { + id: UID, + } + + // 5. Has ability but not key + struct FP5_HasAbilityButNotKey has store, copy, drop { + id: UID, + } +} + +module sui::object { + struct UID has store { + id: address, + } +} \ No newline at end of file diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/suppress_lint_missing_key.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/suppress_lint_missing_key.move new file mode 100644 index 0000000000000..83d07d6655c94 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/suppress_lint_missing_key.move @@ -0,0 +1,15 @@ +module a::trigger_lint_cases { + use sui::object::UID; + + // 4. Suppress warning + #[allow(lint(missing_key))] + struct SuppressWarning { + id: UID, + } +} + +module sui::object { + struct UID has store { + id: address, + } +} \ No newline at end of file diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/trigger_lint_missing_key.exp b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/trigger_lint_missing_key.exp new file mode 100644 index 0000000000000..597643f3a342d --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/trigger_lint_missing_key.exp @@ -0,0 +1,20 @@ +warning[Lint W99007]: The struct's first field is 'id' of type 'sui::object::UID' but is missing the 'key' ability. + ┌─ tests/sui_mode/linter/trigger_lint_missing_key.move:5:5 + │ +5 │ ╭ struct MissingKeyAbility { +6 │ │ id: UID, +7 │ │ } + │ ╰─────^ Struct's first field has an 'id' field of type 'sui::object::UID' but is missing the 'key' ability. + │ + = This warning can be suppressed with '#[allow(lint(missing_key))]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[Lint W99007]: The struct's first field is 'id' of type 'sui::object::UID' but is missing the 'key' ability. + ┌─ tests/sui_mode/linter/trigger_lint_missing_key.move:27:5 + │ +27 │ ╭ struct Wrapper { +28 │ │ id: UID, +29 │ │ } + │ ╰─────^ Struct's first field has an 'id' field of type 'sui::object::UID' but is missing the 'key' ability. + │ + = This warning can be suppressed with '#[allow(lint(missing_key))]' applied to the 'module' or module member ('const', 'fun', or 'struct') + diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/trigger_lint_missing_key.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/trigger_lint_missing_key.move new file mode 100644 index 0000000000000..9d9c2952df71a --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/trigger_lint_missing_key.move @@ -0,0 +1,36 @@ +module a::trigger_lint_cases { + use sui::object::UID; + + // This should trigger the linter warning (true positive) + struct MissingKeyAbility { + id: UID, + } + + // False negative cases (should trigger warning but might not): + + // 1. Different field name + struct FN1_MissingKeyWithDifferentFieldName { + uid: UID, + } + + // 2. UID field not first + struct FN2_MissingKeyUIDNotFirst { + point: u64, + id: UID, + } + + // 3. Nested UID + struct FN3_MissingKeyNestedUID { + wrapper: Wrapper, + } + + struct Wrapper { + id: UID, + } +} + +module sui::object { + struct UID has store { + id: address, + } +} \ No newline at end of file From 5a0febd2d09cc6a9a42236577e6238ef02bf21e0 Mon Sep 17 00:00:00 2001 From: Tom Cat <48447545+tx-tomcat@users.noreply.github.com> Date: Thu, 1 Aug 2024 03:55:21 +0700 Subject: [PATCH 046/232] [Linter] Warn against freezing a type T with a capability-like name (ends in Cap or Capability) and capability-like usage (one or more functions gated with &T inside the package that declares T) (#17273) ## Description The lint identifies function calls that may incorrectly freeze such types, which can lead to design issues. Key Features: 1. Checks for specific freezing functions defined in constants. 2. Uses regex to identify capability-like type names. 3. Reports warnings for potential misuse of freezing on capability-like types. Implementation Details: The lint focuses on public_freeze and freeze functions in the sui::transfer module. It uses a regex pattern to match type names ending with "Cap", "Capability", or similar variations. When a match is found, it reports a warning with a custom diagnostic message. # Test plan Added more use case including false positive, false negative case ## Release notes - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [X] CLI: Move will now lint against freezing a potential capability object - [ ] Rust SDK: --------- Co-authored-by: jamedzung Co-authored-by: Todd Nowacki --- .../sui_mode/linters/freezing_capability.rs | 116 ++++++++++++++++++ .../move-compiler/src/sui_mode/linters/mod.rs | 34 +++-- .../freezing_capability_false_negatives.move | 43 +++++++ .../freezing_capability_false_positives.exp | 16 +++ .../freezing_capability_false_positives.move | 51 ++++++++ .../freezing_capability_suppression.move | 46 +++++++ .../freezing_capability_true_negatives.move | 43 +++++++ .../freezing_capability_true_positives.exp | 24 ++++ .../freezing_capability_true_positives.move | 43 +++++++ 9 files changed, 405 insertions(+), 11 deletions(-) create mode 100644 external-crates/move/crates/move-compiler/src/sui_mode/linters/freezing_capability.rs create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_negatives.move create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_positives.exp create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_positives.move create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_suppression.move create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_negatives.move create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_positives.exp create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_positives.move diff --git a/external-crates/move/crates/move-compiler/src/sui_mode/linters/freezing_capability.rs b/external-crates/move/crates/move-compiler/src/sui_mode/linters/freezing_capability.rs new file mode 100644 index 0000000000000..aa371ee00e3e6 --- /dev/null +++ b/external-crates/move/crates/move-compiler/src/sui_mode/linters/freezing_capability.rs @@ -0,0 +1,116 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! Implements lint to warn against freezing capability-like types in Sui, identifying function calls that may incorrectly freeze such types. +//! The lint checks for specific freezing functions defined in constants and inspects their type arguments for capability-like type names. + +use super::{LinterDiagnosticCategory, LinterDiagnosticCode, LINT_WARNING_PREFIX}; +use crate::{ + diag, + diagnostics::{ + codes::{custom, DiagnosticInfo, Severity}, + WarningFilters, + }, + naming::ast::TypeName_, + shared::{CompilationEnv, Identifier}, + sui_mode::linters::{FREEZE_FUN, PUBLIC_FREEZE_FUN, SUI_PKG_NAME, TRANSFER_MOD_NAME}, + typing::{ + ast as T, + visitor::{TypingVisitorConstructor, TypingVisitorContext}, + }, +}; +use move_ir_types::location::*; +use regex::Regex; + +const FREEZE_CAPABILITY_DIAG: DiagnosticInfo = custom( + LINT_WARNING_PREFIX, + Severity::Warning, + LinterDiagnosticCategory::Sui as u8, + LinterDiagnosticCode::FreezingCapability as u8, + "Freezing a capability-like type can lead to design issues.", +); + +const FREEZE_FUNCTIONS: &[(&str, &str, &str)] = &[ + (SUI_PKG_NAME, TRANSFER_MOD_NAME, PUBLIC_FREEZE_FUN), + (SUI_PKG_NAME, TRANSFER_MOD_NAME, FREEZE_FUN), +]; + +pub struct WarnFreezeCapability; + +pub struct Context<'a> { + env: &'a mut CompilationEnv, + capability_regex: Regex, +} + +impl TypingVisitorConstructor for WarnFreezeCapability { + type Context<'a> = Context<'a>; + fn context<'a>(env: &'a mut CompilationEnv, _program: &T::Program) -> Self::Context<'a> { + Context { + env, + capability_regex: Regex::new(r"Cap(ability)?(\w*v?\d*)?$").unwrap(), + } + } +} + +impl<'a> TypingVisitorContext for Context<'a> { + fn visit_module_custom( + &mut self, + _ident: crate::expansion::ast::ModuleIdent, + mdef: &mut T::ModuleDefinition, + ) -> bool { + // skips if true + mdef.attributes.is_test_or_test_only() + } + + fn visit_function_custom( + &mut self, + _module: crate::expansion::ast::ModuleIdent, + _function_name: crate::parser::ast::FunctionName, + fdef: &mut T::Function, + ) -> bool { + // skips if true + fdef.attributes.is_test_or_test_only() + } + + fn visit_exp_custom(&mut self, exp: &mut T::Exp) -> bool { + if let T::UnannotatedExp_::ModuleCall(fun) = &exp.exp.value { + if self.is_freeze_function(fun) { + self.check_type_arguments(fun, exp.exp.loc); + } + } + false + } + + fn add_warning_filter_scope(&mut self, filter: WarningFilters) { + self.env.add_warning_filter_scope(filter) + } + + fn pop_warning_filter_scope(&mut self) { + self.env.pop_warning_filter_scope() + } +} + +impl<'a> Context<'a> { + fn is_freeze_function(&self, fun: &T::ModuleCall) -> bool { + FREEZE_FUNCTIONS.iter().any(|(addr, module, fname)| { + fun.module.value.is(*addr, *module) && &fun.name.value().as_str() == fname + }) + } + + fn check_type_arguments(&mut self, fun: &T::ModuleCall, loc: Loc) { + for sp!(_, type_arg) in &fun.type_arguments { + if let Some(sp!(_, TypeName_::ModuleType(_, struct_name))) = type_arg.type_name() { + if self.capability_regex.is_match(struct_name.value().as_str()) { + self.report_freeze_capability(loc); + break; + } + } + } + } + + fn report_freeze_capability(&mut self, loc: Loc) { + let msg = "Freezing a capability-like type can lead to design issues."; + let diag = diag!(FREEZE_CAPABILITY_DIAG, (loc, msg)); + self.env.add_diag(diag); + } +} diff --git a/external-crates/move/crates/move-compiler/src/sui_mode/linters/mod.rs b/external-crates/move/crates/move-compiler/src/sui_mode/linters/mod.rs index 7f48af22a73cc..84e3e7ed9a125 100644 --- a/external-crates/move/crates/move-compiler/src/sui_mode/linters/mod.rs +++ b/external-crates/move/crates/move-compiler/src/sui_mode/linters/mod.rs @@ -18,6 +18,7 @@ pub mod coin_field; pub mod collection_equality; pub mod custom_state_change; pub mod freeze_wrapped; +pub mod freezing_capability; pub mod missing_key; pub mod public_random; pub mod self_transfer; @@ -70,6 +71,7 @@ pub const FREEZE_WRAPPED_FILTER_NAME: &str = "freeze_wrapped"; pub const COLLECTION_EQUALITY_FILTER_NAME: &str = "collection_equality"; pub const PUBLIC_RANDOM_FILTER_NAME: &str = "public_random"; pub const MISSING_KEY_FILTER_NAME: &str = "missing_key"; +pub const FREEZING_CAPABILITY_FILTER_NAME: &str = "freezing_capability"; pub const RANDOM_MOD_NAME: &str = "random"; pub const RANDOM_STRUCT_NAME: &str = "Random"; @@ -87,6 +89,7 @@ pub enum LinterDiagnosticCode { CollectionEquality, PublicRandom, MissingKey, + FreezingCapability, } pub fn known_filters() -> (Option, Vec) { @@ -140,6 +143,12 @@ pub fn known_filters() -> (Option, Vec) { LinterDiagnosticCode::MissingKey as u8, Some(MISSING_KEY_FILTER_NAME), ), + WarningFilter::code( + Some(LINT_WARNING_PREFIX), + LinterDiagnosticCategory::Sui as u8, + LinterDiagnosticCode::FreezingCapability as u8, + Some(FREEZING_CAPABILITY_FILTER_NAME), + ), ]; (Some(ALLOW_ATTR_CATEGORY.into()), filters) @@ -148,17 +157,20 @@ pub fn known_filters() -> (Option, Vec) { pub fn linter_visitors(level: LintLevel) -> Vec { match level { LintLevel::None => vec![], - LintLevel::Default | LintLevel::All => { - vec![ - share_owned::ShareOwnedVerifier.visitor(), - self_transfer::SelfTransferVerifier.visitor(), - custom_state_change::CustomStateChangeVerifier.visitor(), - coin_field::CoinFieldVisitor.visitor(), - freeze_wrapped::FreezeWrappedVisitor.visitor(), - collection_equality::CollectionEqualityVisitor.visitor(), - public_random::PublicRandomVisitor.visitor(), - missing_key::MissingKeyVisitor.visitor(), - ] + LintLevel::Default => vec![ + share_owned::ShareOwnedVerifier.visitor(), + self_transfer::SelfTransferVerifier.visitor(), + custom_state_change::CustomStateChangeVerifier.visitor(), + coin_field::CoinFieldVisitor.visitor(), + freeze_wrapped::FreezeWrappedVisitor.visitor(), + collection_equality::CollectionEqualityVisitor.visitor(), + public_random::PublicRandomVisitor.visitor(), + missing_key::MissingKeyVisitor.visitor(), + ], + LintLevel::All => { + let mut visitors = linter_visitors(LintLevel::Default); + visitors.extend([freezing_capability::WarnFreezeCapability.visitor()]); + visitors } } } diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_negatives.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_negatives.move new file mode 100644 index 0000000000000..d0371f2f4695a --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_negatives.move @@ -0,0 +1,43 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +module a::test_false_negatives { + use sui::object::UID; + use sui::transfer; + + struct AdminRights has key { + id: UID + } + + struct PrivilegeToken has key { + id: UID + } + + struct AccessControl has key { + id: UID + } + + public fun freeze_admin_rights(w: AdminRights) { + transfer::public_freeze_object(w); + } + + public fun freeze_privilege_token(w: PrivilegeToken) { + transfer::public_freeze_object(w); + } + + public fun freeze_access_control(w: AccessControl) { + transfer::public_freeze_object(w); + } +} + +module sui::object { + struct UID has store { + id: address, + } +} + +module sui::transfer { + public fun public_freeze_object(_: T) { + abort 0 + } +} \ No newline at end of file diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_positives.exp b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_positives.exp new file mode 100644 index 0000000000000..920ef1eaee67c --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_positives.exp @@ -0,0 +1,16 @@ +warning[Lint W99008]: Freezing a capability-like type can lead to design issues. + ┌─ tests/sui_mode/linter/freezing_capability_false_positives.move:25:9 + │ +25 │ transfer::public_freeze_object(w); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Freezing a capability-like type can lead to design issues. + │ + = This warning can be suppressed with '#[allow(lint(freezing_capability))]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[Lint W99008]: Freezing a capability-like type can lead to design issues. + ┌─ tests/sui_mode/linter/freezing_capability_false_positives.move:37:9 + │ +37 │ transfer::public_freeze_object(w); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Freezing a capability-like type can lead to design issues. + │ + = This warning can be suppressed with '#[allow(lint(freezing_capability))]' applied to the 'module' or module member ('const', 'fun', or 'struct') + diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_positives.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_positives.move new file mode 100644 index 0000000000000..452dd131a86f0 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_positives.move @@ -0,0 +1,51 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +module a::test_false_positives { + use sui::object::UID; + use sui::transfer; + + struct Capture has key { + id: UID + } + + struct Handicap has key { + id: UID + } + + struct Recap has key { + id: UID + } + + struct MyCapybara has key { + id: UID + } + + public fun freeze_capture(w: Capture) { + transfer::public_freeze_object(w); + } + + public fun freeze_handicap(w: Handicap) { + transfer::public_freeze_object(w); + } + + public fun freeze_recap(w: Recap) { + transfer::public_freeze_object(w); + } + + public fun freeze_capybara(w: MyCapybara) { + transfer::public_freeze_object(w); + } +} + +module sui::object { + struct UID has store { + id: address, + } +} + +module sui::transfer { + public fun public_freeze_object(_: T) { + abort 0 + } +} \ No newline at end of file diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_suppression.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_suppression.move new file mode 100644 index 0000000000000..5a48458cd77e4 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_suppression.move @@ -0,0 +1,46 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +module a::test_suppression { + use sui::object::UID; + use sui::transfer; + + struct SuperAdminCap has key { + id: UID + } + + struct MasterCapability has key { + id: UID + } + + struct RootCapV3 has key { + id: UID + } + + #[allow(lint(freezing_capability))] + public fun freeze_super_admin(w: SuperAdminCap) { + transfer::public_freeze_object(w); + } + + #[allow(lint(freezing_capability))] + public fun freeze_master_cap(w: MasterCapability) { + transfer::public_freeze_object(w); + } + + #[allow(lint(freezing_capability))] + public fun freeze_root_cap(w: RootCapV3) { + transfer::public_freeze_object(w); + } +} + +module sui::object { + struct UID has store { + id: address, + } +} + +module sui::transfer { + public fun public_freeze_object(_: T) { + abort 0 + } +} \ No newline at end of file diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_negatives.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_negatives.move new file mode 100644 index 0000000000000..e63b71a98e588 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_negatives.move @@ -0,0 +1,43 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +module a::test_true_negatives { + use sui::object::UID; + use sui::transfer; + + struct NormalStruct has key { + id: UID + } + + struct Data has key { + id: UID + } + + struct Token has key { + id: UID + } + + public fun freeze_normal(w: NormalStruct) { + transfer::public_freeze_object(w); + } + + public fun freeze_data(w: Data) { + transfer::public_freeze_object(w); + } + + public fun freeze_token(w: Token) { + transfer::public_freeze_object(w); + } +} + +module sui::object { + struct UID has store { + id: address, + } +} + +module sui::transfer { + public fun public_freeze_object(_: T) { + abort 0 + } +} \ No newline at end of file diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_positives.exp b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_positives.exp new file mode 100644 index 0000000000000..935933ef6a714 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_positives.exp @@ -0,0 +1,24 @@ +warning[Lint W99008]: Freezing a capability-like type can lead to design issues. + ┌─ tests/sui_mode/linter/freezing_capability_true_positives.move:21:9 + │ +21 │ transfer::public_freeze_object(w); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Freezing a capability-like type can lead to design issues. + │ + = This warning can be suppressed with '#[allow(lint(freezing_capability))]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[Lint W99008]: Freezing a capability-like type can lead to design issues. + ┌─ tests/sui_mode/linter/freezing_capability_true_positives.move:25:9 + │ +25 │ transfer::public_freeze_object(w); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Freezing a capability-like type can lead to design issues. + │ + = This warning can be suppressed with '#[allow(lint(freezing_capability))]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[Lint W99008]: Freezing a capability-like type can lead to design issues. + ┌─ tests/sui_mode/linter/freezing_capability_true_positives.move:29:9 + │ +29 │ transfer::public_freeze_object(w); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Freezing a capability-like type can lead to design issues. + │ + = This warning can be suppressed with '#[allow(lint(freezing_capability))]' applied to the 'module' or module member ('const', 'fun', or 'struct') + diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_positives.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_positives.move new file mode 100644 index 0000000000000..093051c49855c --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_positives.move @@ -0,0 +1,43 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +module a::test_true_positives { + use sui::object::UID; + use sui::transfer; + + struct AdminCap has key { + id: UID + } + + struct UserCapability has key { + id: UID + } + + struct OwnerCapV2 has key { + id: UID + } + + public fun freeze_cap1(w: AdminCap) { + transfer::public_freeze_object(w); + } + + public fun freeze_cap2(w: UserCapability) { + transfer::public_freeze_object(w); + } + + public fun freeze_cap3(w: OwnerCapV2) { + transfer::public_freeze_object(w); + } +} + +module sui::object { + struct UID has store { + id: address, + } +} + +module sui::transfer { + public fun public_freeze_object(_: T) { + abort 0 + } +} \ No newline at end of file From 32347c20c248f6691c6ab89ba53266e2f677ed9b Mon Sep 17 00:00:00 2001 From: mwtian <81660174+mwtian@users.noreply.github.com> Date: Wed, 31 Jul 2024 22:54:28 -0700 Subject: [PATCH 047/232] [rocksdb] increase write stopping threshold for L0 files (#18872) ## Description Currently for column families with high write rate, write stalling and stopping can happen after there are 24 pending L0 files, for example in consensus DB during consensus catchup. This hurts the throughput significantly and reduces the stability of the system. The # of L0 files to compact is reduced back to the default (4), to speed up L0 compactions. Also, for DB that optimizes for write throughput, the thresholds to stall and stop writes are further increased. Logs observed from one validator: ``` 2024/07/31-03:59:57.695047 2702658 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 24 level-0 files rate 16777216 2024/07/31-04:00:13.393421 2702607 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 24 level-0 files rate 13421772 2024/07/31-04:00:13.393593 2702607 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 24 level-0 files rate 10737417 2024/07/31-04:00:14.418687 2702901 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 25 level-0 files rate 8589933 2024/07/31-04:00:43.754068 2702656 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 25 level-0 files rate 10737416 2024/07/31-04:00:52.471606 2702597 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 25 level-0 files rate 8589932 2024/07/31-04:00:52.471784 2702597 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 25 level-0 files rate 9620723 2024/07/31-04:00:53.677837 2702901 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 26 level-0 files rate 7696578 2024/07/31-04:01:26.237337 2702597 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 26 level-0 files rate 8620167 2024/07/31-04:01:26.237494 2702597 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 26 level-0 files rate 6896133 2024/07/31-04:01:27.389744 2702901 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 27 level-0 files rate 5516906 2024/07/31-04:02:21.401986 2702597 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 27 level-0 files rate 4413524 2024/07/31-04:02:21.402179 2702597 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 27 level-0 files rate 3530819 2024/07/31-04:02:22.441728 2702901 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 28 level-0 files rate 2118491 2024/07/31-04:03:18.346778 2702614 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 28 level-0 files rate 10066329 2024/07/31-04:03:18.346980 2702614 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 28 level-0 files rate 6039797 2024/07/31-04:03:19.198853 2702901 [WARN] [db/column_family.cc:991] [blocks] Stalling writes because we have 29 level-0 files rate 3623878 ``` There is no logs for stopping writes at 30 level-0 files. ## Test plan CI. Private testnet. --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/typed-store/src/rocks/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/typed-store/src/rocks/mod.rs b/crates/typed-store/src/rocks/mod.rs index 6b759c340e064..734e10f4c7269 100644 --- a/crates/typed-store/src/rocks/mod.rs +++ b/crates/typed-store/src/rocks/mod.rs @@ -60,7 +60,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 = 6; +const DEFAULT_L0_NUM_FILES_COMPACTION_TRIGGER: usize = 4; 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"; @@ -2346,7 +2346,7 @@ impl DBOptions { let target_file_size_base = 64 << 20; self.options .set_target_file_size_base(target_file_size_base); - // Level 1 default to 64MiB * 6 ~ 384MiB. + // Level 1 default to 64MiB * 4 ~ 256MiB. let max_level_zero_file_num = read_size_from_env(ENV_VAR_L0_NUM_FILES_COMPACTION_TRIGGER) .unwrap_or(DEFAULT_L0_NUM_FILES_COMPACTION_TRIGGER); self.options @@ -2395,10 +2395,10 @@ impl DBOptions { max_level_zero_file_num.try_into().unwrap(), ); self.options.set_level_zero_slowdown_writes_trigger( - (max_level_zero_file_num * 4).try_into().unwrap(), + (max_level_zero_file_num * 12).try_into().unwrap(), ); self.options - .set_level_zero_stop_writes_trigger((max_level_zero_file_num * 5).try_into().unwrap()); + .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( From b5ca0ebe9d702ff5b5b17bb17663cc9f20a840c5 Mon Sep 17 00:00:00 2001 From: Anastasios Kichidis Date: Thu, 1 Aug 2024 10:55:00 +0100 Subject: [PATCH 048/232] [Consensus] refactored data remover for store pruning (#18839) ## Description This PR refactors the store pruning for the consensus db. The following have been done: * renamed component from `EpochDataRemover` to `ConsensusStorePruner` as it's more accurate and easier to locate * use the `safe_drop_db` method to ensure more safety/robustness against deletions * added node configuration for the consensus db epoch retention and run interval * made the component attempt to prune old epoch dbs not only during epoch change but periodically as well (configurable) to ensure that there is a retry approach in case of transient failures enhancing the robustness. * more testing ## Test plan CI/private-testnet --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-config/src/node.rs | 19 + .../src/epoch/consensus_store_pruner.rs | 363 ++++++++++++++++++ crates/sui-core/src/epoch/data_removal.rs | 122 ------ crates/sui-core/src/epoch/mod.rs | 2 +- .../src/unit_tests/epoch_data_tests.rs | 54 --- crates/sui-node/src/lib.rs | 28 +- .../src/node_config_builder.rs | 2 + ...ests__network_config_snapshot_matches.snap | 14 + 8 files changed, 413 insertions(+), 191 deletions(-) create mode 100644 crates/sui-core/src/epoch/consensus_store_pruner.rs delete mode 100644 crates/sui-core/src/epoch/data_removal.rs delete mode 100644 crates/sui-core/src/unit_tests/epoch_data_tests.rs diff --git a/crates/sui-config/src/node.rs b/crates/sui-config/src/node.rs index 80ae3dd0a6fb1..32e289c465bad 100644 --- a/crates/sui-config/src/node.rs +++ b/crates/sui-config/src/node.rs @@ -412,6 +412,14 @@ pub struct ConsensusConfig { // Base consensus DB path for all epochs. pub db_path: PathBuf, + // The number of epochs for which to retain the consensus DBs. Setting it to 0 will make a consensus DB getting + // dropped as soon as system is switched to a new epoch. + pub db_retention_epochs: Option, + + // Pruner will run on every epoch change but it will also check periodically on every `db_pruner_period_secs` + // seconds to see if there are any epoch DBs to remove. + pub db_pruner_period_secs: Option, + /// Maximum number of pending transactions to submit to consensus, including those /// in submission wait. /// Assuming 10_000 txn tps * 10 sec consensus latency = 100_000 inflight consensus txns, @@ -455,6 +463,17 @@ impl ConsensusConfig { pub fn narwhal_config(&self) -> &NarwhalParameters { &self.narwhal_config } + + pub fn db_retention_epochs(&self) -> u64 { + self.db_retention_epochs.unwrap_or(0) + } + + pub fn db_pruner_period(&self) -> Duration { + // Default to 1 hour + self.db_pruner_period_secs + .map(Duration::from_secs) + .unwrap_or(Duration::from_secs(3_600)) + } } #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/crates/sui-core/src/epoch/consensus_store_pruner.rs b/crates/sui-core/src/epoch/consensus_store_pruner.rs new file mode 100644 index 0000000000000..49d1c70c1c9b5 --- /dev/null +++ b/crates/sui-core/src/epoch/consensus_store_pruner.rs @@ -0,0 +1,363 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use consensus_config::Epoch; +use mysten_metrics::spawn_logged_monitored_task; +use prometheus::{ + register_int_counter_vec_with_registry, register_int_counter_with_registry, + register_int_gauge_with_registry, IntCounter, IntCounterVec, IntGauge, Registry, +}; +use std::fs; +use std::path::PathBuf; +use std::time::Duration; +use tokio::{ + sync::mpsc, + time::{sleep, Instant}, +}; +use tracing::{error, info}; +use typed_store::rocks::safe_drop_db; + +struct Metrics { + last_pruned_consensus_db_epoch: IntGauge, + successfully_pruned_consensus_dbs: IntCounter, + error_pruning_consensus_dbs: IntCounterVec, +} + +impl Metrics { + fn new(registry: &Registry) -> Self { + Self { + last_pruned_consensus_db_epoch: register_int_gauge_with_registry!( + "last_pruned_consensus_db_epoch", + "The last epoch for which the consensus store was pruned", + registry + ) + .unwrap(), + successfully_pruned_consensus_dbs: register_int_counter_with_registry!( + "successfully_pruned_consensus_dbs", + "The number of consensus dbs successfully pruned", + registry + ) + .unwrap(), + error_pruning_consensus_dbs: register_int_counter_vec_with_registry!( + "error_pruning_consensus_dbs", + "The number of errors encountered while pruning consensus dbs", + &["mode"], + registry + ) + .unwrap(), + } + } +} + +pub struct ConsensusStorePruner { + tx_remove: mpsc::Sender, + _handle: tokio::task::JoinHandle<()>, +} + +impl ConsensusStorePruner { + pub fn new( + base_path: PathBuf, + epoch_retention: u64, + epoch_prune_period: Duration, + registry: &Registry, + ) -> Self { + let (tx_remove, mut rx_remove) = mpsc::channel(1); + let metrics = Metrics::new(registry); + + let _handle = spawn_logged_monitored_task!(async { + info!("Starting consensus store pruner with epoch retention {epoch_retention} and prune period {epoch_prune_period:?}"); + + let mut timeout = tokio::time::interval_at( + Instant::now() + Duration::from_secs(60), // allow some time for the node to boot etc before attempting to prune + epoch_prune_period, + ); + + let mut latest_epoch = 0; + loop { + tokio::select! { + _ = timeout.tick() => { + Self::prune_old_epoch_data(&base_path, latest_epoch, epoch_retention, &metrics).await; + } + result = rx_remove.recv() => { + if result.is_none() { + info!("Closing consensus store pruner"); + break; + } + latest_epoch = result.unwrap(); + Self::prune_old_epoch_data(&base_path, latest_epoch, epoch_retention, &metrics).await; + } + } + } + }); + + Self { tx_remove, _handle } + } + + /// This method will remove all epoch data stores and directories that are older than the current epoch minus the epoch retention. The method ensures + /// that always the `current_epoch` data is retained. + pub async fn prune(&self, current_epoch: Epoch) { + let result = self.tx_remove.send(current_epoch).await; + if result.is_err() { + error!( + "Error sending message to data removal task for epoch {:?}", + current_epoch, + ); + } + } + + async fn prune_old_epoch_data( + storage_base_path: &PathBuf, + current_epoch: Epoch, + epoch_retention: u64, + metrics: &Metrics, + ) { + let drop_boundary = current_epoch.saturating_sub(epoch_retention); + + info!( + "Consensus store prunning for current epoch {}. Will remove epochs < {:?}", + current_epoch, drop_boundary + ); + + // Get all the epoch stores in the base path directory + let files = match fs::read_dir(storage_base_path) { + Ok(f) => f, + Err(e) => { + error!( + "Can not read the files in the storage path directory for epoch cleanup: {:?}", + e + ); + return; + } + }; + + // Look for any that are less than the drop boundary and drop + for file_res in files { + let f = match file_res { + Ok(f) => f, + Err(e) => { + error!( + "Error while cleaning up storage of previous epochs: {:?}", + e + ); + continue; + } + }; + + let name = f.file_name(); + let file_epoch_string = match name.to_str() { + Some(f) => f, + None => continue, + }; + + let file_epoch = match file_epoch_string.to_owned().parse::() { + Ok(f) => f, + Err(e) => { + error!( + "Could not parse file \"{file_epoch_string}\" in storage path into epoch for cleanup: {:?}", + e + ); + continue; + } + }; + + if file_epoch < drop_boundary { + if let Err(e) = safe_drop_db(f.path()) { + error!( + "Could not prune old consensus storage \"{:?}\" directory with safe approach. Will fallback to force delete: {:?}", + f.path(), + e + ); + + metrics + .error_pruning_consensus_dbs + .with_label_values(&["safe"]) + .inc(); + + const WAIT_BEFORE_FORCE_DELETE: Duration = Duration::from_secs(5); + sleep(WAIT_BEFORE_FORCE_DELETE).await; + + if let Err(err) = fs::remove_dir_all(f.path()) { + error!( + "Could not prune old consensus storage \"{:?}\" directory with force delete: {:?}", + f.path(), + err + ); + metrics + .error_pruning_consensus_dbs + .with_label_values(&["force"]) + .inc(); + } else { + info!( + "Successfully pruned consensus epoch storage directory with force delete: {:?}", + f.path() + ); + let last_epoch = metrics.last_pruned_consensus_db_epoch.get(); + metrics + .last_pruned_consensus_db_epoch + .set(last_epoch.max(file_epoch as i64)); + metrics.successfully_pruned_consensus_dbs.inc(); + } + } else { + info!( + "Successfully pruned consensus epoch storage directory: {:?}", + f.path() + ); + let last_epoch = metrics.last_pruned_consensus_db_epoch.get(); + metrics + .last_pruned_consensus_db_epoch + .set(last_epoch.max(file_epoch as i64)); + metrics.successfully_pruned_consensus_dbs.inc(); + } + } + } + + info!( + "Completed old epoch data removal process for epoch {:?}", + current_epoch + ); + } +} + +#[cfg(test)] +mod tests { + use crate::epoch::consensus_store_pruner::{ConsensusStorePruner, Metrics}; + use prometheus::Registry; + use std::fs; + use tokio::time::sleep; + + #[tokio::test] + async fn test_remove_old_epoch_data() { + telemetry_subscribers::init_for_testing(); + let metrics = Metrics::new(&Registry::new()); + + { + // Epoch 0 should not be removed when it's current epoch. + let epoch_retention = 0; + let current_epoch = 0; + + let base_directory = tempfile::tempdir().unwrap().into_path(); + + create_epoch_directories(&base_directory, vec!["0", "other"]); + + ConsensusStorePruner::prune_old_epoch_data( + &base_directory, + current_epoch, + epoch_retention, + &metrics, + ) + .await; + + let epochs_left = read_epoch_directories(&base_directory); + + assert_eq!(epochs_left.len(), 1); + assert_eq!(epochs_left[0], 0); + } + + { + // Every directory should be retained only for 1 epoch. We expect any epoch directories < 99 to be removed. + let epoch_retention = 1; + let current_epoch = 100; + + let base_directory = tempfile::tempdir().unwrap().into_path(); + + create_epoch_directories(&base_directory, vec!["97", "98", "99", "100", "other"]); + + ConsensusStorePruner::prune_old_epoch_data( + &base_directory, + current_epoch, + epoch_retention, + &metrics, + ) + .await; + + let epochs_left = read_epoch_directories(&base_directory); + + assert_eq!(epochs_left.len(), 2); + assert_eq!(epochs_left[0], 99); + assert_eq!(epochs_left[1], 100); + } + + { + // Every directory should be retained only for 0 epochs. That means only the current epoch directory should be retained and everything else + // deleted. + let epoch_retention = 0; + let current_epoch = 100; + + let base_directory = tempfile::tempdir().unwrap().into_path(); + + create_epoch_directories(&base_directory, vec!["97", "98", "99", "100", "other"]); + + ConsensusStorePruner::prune_old_epoch_data( + &base_directory, + current_epoch, + epoch_retention, + &metrics, + ) + .await; + + let epochs_left = read_epoch_directories(&base_directory); + + assert_eq!(epochs_left.len(), 1); + assert_eq!(epochs_left[0], 100); + } + } + + #[tokio::test(flavor = "current_thread")] + async fn test_consensus_store_pruner() { + let epoch_retention = 1; + let epoch_prune_period = std::time::Duration::from_millis(500); + + let base_directory = tempfile::tempdir().unwrap().into_path(); + + // We create some directories up to epoch 100 + create_epoch_directories(&base_directory, vec!["97", "98", "99", "100", "other"]); + + let pruner = ConsensusStorePruner::new( + base_directory.clone(), + epoch_retention, + epoch_prune_period, + &Registry::new(), + ); + + // We let the pruner run for a couple of times to prune the old directories. Since the default epoch of 0 is used no dirs should be pruned. + sleep(3 * epoch_prune_period).await; + + // We expect the directories to be the same as before + let epoch_dirs = read_epoch_directories(&base_directory); + assert_eq!(epoch_dirs.len(), 4); + + // Then we update the epoch and instruct to prune for current epoch = 100 + pruner.prune(100).await; + + // We let the pruner run and check again the directories - no directories of epoch < 99 should be left + sleep(2 * epoch_prune_period).await; + + let epoch_dirs = read_epoch_directories(&base_directory); + assert_eq!(epoch_dirs.len(), 2); + assert_eq!(epoch_dirs[0], 99); + assert_eq!(epoch_dirs[1], 100); + } + + fn create_epoch_directories(base_directory: &std::path::Path, epochs: Vec<&str>) { + for epoch in epochs { + let mut path = base_directory.to_path_buf(); + path.push(epoch); + fs::create_dir(path).unwrap(); + } + } + + fn read_epoch_directories(base_directory: &std::path::Path) -> Vec { + let files = fs::read_dir(base_directory).unwrap(); + + let mut epochs = Vec::new(); + for file_res in files { + let file_epoch_string = file_res.unwrap().file_name().to_str().unwrap().to_owned(); + if let Ok(file_epoch) = file_epoch_string.parse::() { + epochs.push(file_epoch); + } + } + + epochs.sort(); + epochs + } +} diff --git a/crates/sui-core/src/epoch/data_removal.rs b/crates/sui-core/src/epoch/data_removal.rs deleted file mode 100644 index 76455294e6cf5..0000000000000 --- a/crates/sui-core/src/epoch/data_removal.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -#[cfg(test)] -#[path = "../unit_tests/epoch_data_tests.rs"] -pub mod epoch_data_tests; - -use mysten_metrics::spawn_monitored_task; -use narwhal_config::Epoch; -use std::fs; -use std::path::PathBuf; -use tokio::sync::mpsc; - -pub struct EpochDataRemover { - base_path: PathBuf, - tx_remove: mpsc::Sender, -} - -impl EpochDataRemover { - pub fn new(base_path: PathBuf) -> Self { - let (tx_remove, _rx_remove) = mpsc::channel(1); - Self { - base_path, - tx_remove, - } - } - - pub async fn run(&mut self) { - let (tx_remove, mut rx_remove) = mpsc::channel(1); - self.tx_remove = tx_remove; - let base_path = self.base_path.clone(); - spawn_monitored_task!(async { - tracing::info!("Starting Epoch Data Remover"); - loop { - match rx_remove.recv().await { - Some(epoch) => { - remove_old_epoch_data(base_path.clone(), epoch); - } - None => { - tracing::info!("Closing Epoch Data Remover"); - break; - } - } - } - }); - } - - pub async fn remove_old_data(&self, latest_closed_epoch: Epoch) { - let result = self.tx_remove.send(latest_closed_epoch).await; - if result.is_err() { - tracing::error!( - "Error sending message to data removal task for epoch {:?}", - latest_closed_epoch, - ); - } - } -} - -pub(crate) fn remove_old_epoch_data(storage_base_path: PathBuf, epoch: Epoch) { - if epoch < 1 { - return; - } - - // Keep previous epoch data as a safety buffer and remove starting from epoch - 1 - let drop_boundary = epoch - 1; - - tracing::info!( - "Starting old epoch data removal for epoch {:?}", - drop_boundary - ); - - // Get all the epoch stores in the base path directory - let files = match fs::read_dir(storage_base_path) { - Ok(f) => f, - Err(e) => { - tracing::error!("Data Remover cannot read the files in the storage path directory for epoch cleanup: {:?}", e); - return; - } - }; - - // Look for any that are less than or equal to the drop boundary and drop - for file_res in files { - let f = match file_res { - Ok(f) => f, - Err(e) => { - tracing::error!( - "Data Remover error while cleaning up storage of previous epochs: {:?}", - e - ); - continue; - } - }; - - let name = f.file_name(); - let file_epoch_string = match name.to_str() { - Some(f) => f, - None => continue, - }; - - let file_epoch = match file_epoch_string.to_owned().parse::() { - Ok(f) => f, - Err(e) => { - tracing::error!("Data Remover could not parse file in storage path into epoch for cleanup: {:?}",e); - continue; - } - }; - - if file_epoch <= drop_boundary { - if let Err(e) = fs::remove_dir_all(f.path()) { - tracing::error!( - "Data Remover could not remove old epoch storage directory: {:?}", - e - ); - } - } - } - - tracing::info!( - "Completed old epoch data removal process for epoch {:?}", - epoch - ); -} diff --git a/crates/sui-core/src/epoch/mod.rs b/crates/sui-core/src/epoch/mod.rs index 5a8961ca226b4..70c7f5cee189f 100644 --- a/crates/sui-core/src/epoch/mod.rs +++ b/crates/sui-core/src/epoch/mod.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 pub mod committee_store; -pub mod data_removal; +pub mod consensus_store_pruner; pub mod epoch_metrics; pub mod randomness; pub mod reconfiguration; diff --git a/crates/sui-core/src/unit_tests/epoch_data_tests.rs b/crates/sui-core/src/unit_tests/epoch_data_tests.rs deleted file mode 100644 index 62b856d2dda0d..0000000000000 --- a/crates/sui-core/src/unit_tests/epoch_data_tests.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::epoch::data_removal; -use std::fs; -use std::path::PathBuf; - -#[tokio::test] -async fn test_remove_old_epoch_data() { - // Create the storage paths - let base_path_string = narwhal_test_utils::temp_dir().to_str().unwrap().to_owned(); - - let mut base_path = PathBuf::new(); - base_path.push(base_path_string.clone()); - - let mut path_other = PathBuf::new(); - path_other.push(base_path_string.clone() + "/other"); - let mut path_98 = base_path.clone(); - path_98.push(base_path_string.clone() + "/98"); - let mut path_99 = base_path.clone(); - path_99.push(base_path_string.clone() + "/99"); - let mut path_100 = base_path.clone(); - path_100.push(base_path_string.clone() + "/100"); - - // Remove the directories created next in case it wasn't cleaned up before the last test run terminated - _ = fs::remove_dir_all(base_path.clone()); - - // Create some epoch directories - fs::create_dir(base_path.clone()).unwrap(); - fs::create_dir(path_other.clone()).unwrap(); - fs::create_dir(path_98.clone()).unwrap(); - fs::create_dir(path_99.clone()).unwrap(); - fs::create_dir(path_100.clone()).unwrap(); - - // With the current epoch of 100, remove old epochs - data_removal::remove_old_epoch_data(base_path.clone(), 100); - - // Now ensure the epoch directories older than 100 were removed - let files = fs::read_dir(base_path_string).unwrap(); - - let mut epochs_left = Vec::new(); - for file_res in files { - let file_epoch_string = file_res.unwrap().file_name().to_str().unwrap().to_owned(); - if let Ok(file_epoch) = file_epoch_string.parse::() { - epochs_left.push(file_epoch); - } - } - - // Remove the directories we created before the test possibly terminates - _ = fs::remove_dir_all(base_path); - - assert_eq!(epochs_left.len(), 1); - assert_eq!(epochs_left[0], 100); -} diff --git a/crates/sui-node/src/lib.rs b/crates/sui-node/src/lib.rs index fb2b4f8416329..3623cef268df5 100644 --- a/crates/sui-node/src/lib.rs +++ b/crates/sui-node/src/lib.rs @@ -85,7 +85,7 @@ use sui_core::consensus_throughput_calculator::{ use sui_core::consensus_validator::{SuiTxValidator, SuiTxValidatorMetrics}; use sui_core::db_checkpoint_handler::DBCheckpointHandler; use sui_core::epoch::committee_store::CommitteeStore; -use sui_core::epoch::data_removal::EpochDataRemover; +use sui_core::epoch::consensus_store_pruner::ConsensusStorePruner; use sui_core::epoch::epoch_metrics::EpochMetrics; use sui_core::epoch::reconfiguration::ReconfigurationInitiator; use sui_core::module_cache_metrics::ResolverMetrics; @@ -146,7 +146,7 @@ pub struct ValidatorComponents { validator_server_handle: JoinHandle>, validator_overload_monitor_handle: Option>, consensus_manager: ConsensusManager, - consensus_epoch_data_remover: EpochDataRemover, + consensus_store_pruner: ConsensusStorePruner, consensus_adapter: Arc, // dropping this will eventually stop checkpoint tasks. The receiver side of this channel // is copied into each checkpoint service task, and they are listening to any change to this @@ -1178,11 +1178,13 @@ impl SuiNode { let consensus_manager = ConsensusManager::new(&config, consensus_config, registry_service, client); - let mut consensus_epoch_data_remover = - EpochDataRemover::new(consensus_manager.get_storage_base_path()); - // This only gets started up once, not on every epoch. (Make call to remove every epoch.) - consensus_epoch_data_remover.run().await; + let consensus_store_pruner = ConsensusStorePruner::new( + consensus_manager.get_storage_base_path(), + consensus_config.db_retention_epochs(), + consensus_config.db_pruner_period(), + ®istry_service.default_registry(), + ); let checkpoint_metrics = CheckpointMetrics::new(®istry_service.default_registry()); let sui_tx_validator_metrics = @@ -1223,7 +1225,7 @@ impl SuiNode { state_sync_handle, randomness_handle, consensus_manager, - consensus_epoch_data_remover, + consensus_store_pruner, accumulator, validator_server_handle, validator_overload_monitor_handle, @@ -1243,7 +1245,7 @@ impl SuiNode { state_sync_handle: state_sync::Handle, randomness_handle: randomness::Handle, consensus_manager: ConsensusManager, - consensus_epoch_data_remover: EpochDataRemover, + consensus_store_pruner: ConsensusStorePruner, accumulator: Weak, validator_server_handle: JoinHandle>, validator_overload_monitor_handle: Option>, @@ -1335,7 +1337,7 @@ impl SuiNode { validator_server_handle, validator_overload_monitor_handle, consensus_manager, - consensus_epoch_data_remover, + consensus_store_pruner, consensus_adapter, checkpoint_service_exit, checkpoint_metrics, @@ -1644,7 +1646,7 @@ impl SuiNode { validator_server_handle, validator_overload_monitor_handle, consensus_manager, - consensus_epoch_data_remover, + consensus_store_pruner, consensus_adapter, checkpoint_service_exit, checkpoint_metrics, @@ -1680,9 +1682,7 @@ impl SuiNode { let weak_accumulator = Arc::downgrade(&new_accumulator); *accumulator_guard = Some(new_accumulator); - consensus_epoch_data_remover - .remove_old_data(next_epoch - 1) - .await; + consensus_store_pruner.prune(next_epoch).await; if self.state.is_validator(&new_epoch_store) { // Only restart Narwhal if this node is still a validator in the new epoch. @@ -1696,7 +1696,7 @@ impl SuiNode { self.state_sync_handle.clone(), self.randomness_handle.clone(), consensus_manager, - consensus_epoch_data_remover, + consensus_store_pruner, weak_accumulator, validator_server_handle, validator_overload_monitor_handle, diff --git a/crates/sui-swarm-config/src/node_config_builder.rs b/crates/sui-swarm-config/src/node_config_builder.rs index 5ae24ec616cda..8e4859249deb1 100644 --- a/crates/sui-swarm-config/src/node_config_builder.rs +++ b/crates/sui-swarm-config/src/node_config_builder.rs @@ -135,6 +135,8 @@ impl ValidatorConfigBuilder { let consensus_config = ConsensusConfig { address: consensus_address, db_path: consensus_db_path, + db_retention_epochs: None, + db_pruner_period_secs: None, max_pending_transactions: None, max_submit_position: self.max_submit_position, submit_delay_step_override_millis: self.submit_delay_step_override_millis, 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 61811145ed2a6..770b2b03a55f2 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 @@ -19,6 +19,8 @@ validator_configs: admin-interface-port: 8888 consensus-config: db-path: /tmp/foo/ + db-retention-epochs: ~ + db-pruner-period-secs: ~ max-pending-transactions: ~ max-submit-position: ~ submit-delay-step-override-millis: ~ @@ -156,6 +158,8 @@ validator_configs: admin-interface-port: 8888 consensus-config: db-path: /tmp/foo/ + db-retention-epochs: ~ + db-pruner-period-secs: ~ max-pending-transactions: ~ max-submit-position: ~ submit-delay-step-override-millis: ~ @@ -293,6 +297,8 @@ validator_configs: admin-interface-port: 8888 consensus-config: db-path: /tmp/foo/ + db-retention-epochs: ~ + db-pruner-period-secs: ~ max-pending-transactions: ~ max-submit-position: ~ submit-delay-step-override-millis: ~ @@ -430,6 +436,8 @@ validator_configs: admin-interface-port: 8888 consensus-config: db-path: /tmp/foo/ + db-retention-epochs: ~ + db-pruner-period-secs: ~ max-pending-transactions: ~ max-submit-position: ~ submit-delay-step-override-millis: ~ @@ -567,6 +575,8 @@ validator_configs: admin-interface-port: 8888 consensus-config: db-path: /tmp/foo/ + db-retention-epochs: ~ + db-pruner-period-secs: ~ max-pending-transactions: ~ max-submit-position: ~ submit-delay-step-override-millis: ~ @@ -704,6 +714,8 @@ validator_configs: admin-interface-port: 8888 consensus-config: db-path: /tmp/foo/ + db-retention-epochs: ~ + db-pruner-period-secs: ~ max-pending-transactions: ~ max-submit-position: ~ submit-delay-step-override-millis: ~ @@ -841,6 +853,8 @@ validator_configs: admin-interface-port: 8888 consensus-config: db-path: /tmp/foo/ + db-retention-epochs: ~ + db-pruner-period-secs: ~ max-pending-transactions: ~ max-submit-position: ~ submit-delay-step-override-millis: ~ From 1df30b2af1868153dad77fba3a2a43f297bc46a6 Mon Sep 17 00:00:00 2001 From: devan-ko <143364659+devan-ko@users.noreply.github.com> Date: Thu, 1 Aug 2024 20:30:05 +0900 Subject: [PATCH 049/232] feat: enable ambrus aws cognito as zklogin provider (#18867) --- crates/sui-config/src/node.rs | 1 + ...ot_tests__network_config_snapshot_matches.snap | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/crates/sui-config/src/node.rs b/crates/sui-config/src/node.rs index 32e289c465bad..7c584a462ecfc 100644 --- a/crates/sui-config/src/node.rs +++ b/crates/sui-config/src/node.rs @@ -247,6 +247,7 @@ 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(), ]); map.insert(Chain::Mainnet, providers.clone()); map.insert(Chain::Testnet, providers); 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 770b2b03a55f2..02d6693f87ab4 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 @@ -99,11 +99,13 @@ validator_configs: zklogin-oauth-providers: Mainnet: - Apple + - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Facebook - Google - Twitch Testnet: - Apple + - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Facebook - Google - Twitch @@ -238,11 +240,13 @@ validator_configs: zklogin-oauth-providers: Mainnet: - Apple + - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Facebook - Google - Twitch Testnet: - Apple + - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Facebook - Google - Twitch @@ -377,11 +381,13 @@ validator_configs: zklogin-oauth-providers: Mainnet: - Apple + - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Facebook - Google - Twitch Testnet: - Apple + - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Facebook - Google - Twitch @@ -516,11 +522,13 @@ validator_configs: zklogin-oauth-providers: Mainnet: - Apple + - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Facebook - Google - Twitch Testnet: - Apple + - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Facebook - Google - Twitch @@ -655,11 +663,13 @@ validator_configs: zklogin-oauth-providers: Mainnet: - Apple + - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Facebook - Google - Twitch Testnet: - Apple + - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Facebook - Google - Twitch @@ -794,11 +804,13 @@ validator_configs: zklogin-oauth-providers: Mainnet: - Apple + - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Facebook - Google - Twitch Testnet: - Apple + - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Facebook - Google - Twitch @@ -933,11 +945,13 @@ validator_configs: zklogin-oauth-providers: Mainnet: - Apple + - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Facebook - Google - Twitch Testnet: - Apple + - "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8" - Facebook - Google - Twitch @@ -983,4 +997,3 @@ account_keys: - mfPjCoE6SX0Sl84MnmNS/LS+tfPpkn7I8tziuk2g0WM= - 5RWlYF22jS9i76zLl8jP2D3D8GC5ht+IP1dWUBGZxi8= genesis: "[fake genesis]" - From e1540fde9d40af20f67f63fade2b0670f9da8958 Mon Sep 17 00:00:00 2001 From: plam-ml <127577476+plam-ml@users.noreply.github.com> Date: Thu, 1 Aug 2024 06:32:47 -0700 Subject: [PATCH 050/232] add blank and rel (#18881) ## Description - Add target and rel ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- apps/wallet/src/ui/app/pages/home/interstitial/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/wallet/src/ui/app/pages/home/interstitial/index.tsx b/apps/wallet/src/ui/app/pages/home/interstitial/index.tsx index b60006959dc03..409a878474f70 100644 --- a/apps/wallet/src/ui/app/pages/home/interstitial/index.tsx +++ b/apps/wallet/src/ui/app/pages/home/interstitial/index.tsx @@ -76,6 +76,8 @@ function Interstitial({ className="flex appearance-none border-none rounded-full bg-[#4CA2FF] h-10 px-6 cursor-pointer items-center no-underline" onClick={onClick} to={bannerUrl} + target="_blank" + rel="noreferrer noopener" > {buttonText || 'Join for a chance to win'} From b4f6f3cfd0e600b6db6b153692e633bcf25ec877 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Thu, 1 Aug 2024 08:03:31 -0700 Subject: [PATCH 051/232] [Rust SDK] Handle unwrap in wallet context (#18882) ## Description Handle unwrap in wallet context ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-sdk/src/wallet_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sui-sdk/src/wallet_context.rs b/crates/sui-sdk/src/wallet_context.rs index a14ce9564c203..863f2ebeafb16 100644 --- a/crates/sui-sdk/src/wallet_context.rs +++ b/crates/sui-sdk/src/wallet_context.rs @@ -183,7 +183,7 @@ impl WalletContext { budget: u64, forbidden_gas_objects: BTreeSet, ) -> Result<(u64, SuiObjectData), anyhow::Error> { - for o in self.gas_objects(address).await.unwrap() { + for o in self.gas_objects(address).await? { if o.0 >= budget && !forbidden_gas_objects.contains(&o.1.object_id) { return Ok((o.0, o.1)); } From 44150d4d4786743e0799c6cafd19169d737b90ee Mon Sep 17 00:00:00 2001 From: mamos-mysten <122397493+mamos-mysten@users.noreply.github.com> Date: Thu, 1 Aug 2024 09:48:34 -0700 Subject: [PATCH 052/232] [wallet-ext]: fix: hidden assets button (#18884) ## Description Fixes appearance of hide assets button in the wallet extension ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- apps/wallet/src/ui/app/pages/home/nfts/VisualAssets.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wallet/src/ui/app/pages/home/nfts/VisualAssets.tsx b/apps/wallet/src/ui/app/pages/home/nfts/VisualAssets.tsx index 6d68b2990c426..d1327c728ac06 100644 --- a/apps/wallet/src/ui/app/pages/home/nfts/VisualAssets.tsx +++ b/apps/wallet/src/ui/app/pages/home/nfts/VisualAssets.tsx @@ -44,7 +44,7 @@ export default function VisualAssets({ items }: { items: SuiObjectData[] }) {
{!isKioskOwnerToken(kioskClient.network, object) && - !bnl.some((item) => item?.objectType !== object.type) ? ( + !bnl.some((item) => item?.objectType === object.type) ? (
- {meta.touched ? ( + {meta.touched && !isValidating ? (
{warningData === RecipientWarningType.OBJECT ? ( From f8fa78597a237259e2de8b6e50650ffd5bc4a695 Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Tue, 13 Aug 2024 00:34:40 +0100 Subject: [PATCH 100/232] fix: [GraphQL/Owner] rootVersion: UInt53 (#18966) ## Description The PR introducing the `rootVersion` parameter to `Query.owner` raced with the PR that introduced `UInt53`. This PR fixes the race by using `UInt53` as the type for `rootVersion`. ## Test plan ``` sui-graphql-rpc$ cargo nextest run ``` --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [x] GraphQL: `Query.owner`'s `rootVersion` parameter should accepts a `UInt53` rather than an `Int`. - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-graphql-rpc/schema/current_progress_schema.graphql | 2 +- crates/sui-graphql-rpc/src/types/query.rs | 4 ++-- .../tests/snapshots/snapshot_tests__schema_sdl_export.snap | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql b/crates/sui-graphql-rpc/schema/current_progress_schema.graphql index 688f1490b5f64..cbde89c2c6fc5 100644 --- a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql +++ b/crates/sui-graphql-rpc/schema/current_progress_schema.graphql @@ -3033,7 +3033,7 @@ type Query { state at the latest checkpoint known to the GraphQL RPC. Similarly, `Owner.asObject` will return the object's version at the latest checkpoint. """ - owner(address: SuiAddress!, rootVersion: Int): Owner + owner(address: SuiAddress!, rootVersion: UInt53): Owner """ The object corresponding to the given address at the (optionally) given version. When no version is given, the latest version is returned. diff --git a/crates/sui-graphql-rpc/src/types/query.rs b/crates/sui-graphql-rpc/src/types/query.rs index f10d28f06236f..10a6f4c9a8464 100644 --- a/crates/sui-graphql-rpc/src/types/query.rs +++ b/crates/sui-graphql-rpc/src/types/query.rs @@ -195,13 +195,13 @@ impl Query { &self, ctx: &Context<'_>, address: SuiAddress, - root_version: Option, + root_version: Option, ) -> Result> { let Watermark { checkpoint, .. } = *ctx.data()?; Ok(Some(Owner { address, checkpoint_viewed_at: checkpoint, - root_version, + root_version: root_version.map(|v| v.into()), })) } diff --git a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap index fae839c9b487a..c82e1181d7f6f 100644 --- a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap +++ b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap @@ -3037,7 +3037,7 @@ type Query { state at the latest checkpoint known to the GraphQL RPC. Similarly, `Owner.asObject` will return the object's version at the latest checkpoint. """ - owner(address: SuiAddress!, rootVersion: Int): Owner + owner(address: SuiAddress!, rootVersion: UInt53): Owner """ The object corresponding to the given address at the (optionally) given version. When no version is given, the latest version is returned. From 7d103a93cd73cd16c6652246a2ec7fd24aa2b06e Mon Sep 17 00:00:00 2001 From: ronny-mysten <118224482+ronny-mysten@users.noreply.github.com> Date: Mon, 12 Aug 2024 17:43:37 -0600 Subject: [PATCH 101/232] [docs][site] Landing page links (#18967) ## Description Changing order of landing page links. Testing effects on page views. Reexamining specific links used is beyond scope. Previous image Current image ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- docs/site/src/pages/index.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/site/src/pages/index.js b/docs/site/src/pages/index.js index bfd85f5122d3a..7fa05c6824877 100644 --- a/docs/site/src/pages/index.js +++ b/docs/site/src/pages/index.js @@ -42,17 +42,6 @@ export default function Home() {
- - - Tokenomics - - - Cryptography - - - Standards - - + + + Tokenomics + + + Cryptography + + + Standards + + Date: Mon, 12 Aug 2024 16:57:00 -0700 Subject: [PATCH 102/232] [docs] Local ingestion custom indexer docs update and example (#18957) ## Description Added the local ingestion custom indexer example as well as doc improvement ## Test plan Manually --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --------- Co-authored-by: ronny-mysten <118224482+ronny-mysten@users.noreply.github.com> --- .../developer/advanced/custom-indexer.mdx | 96 +++++-------------- examples/custom-indexer/rust/Cargo.toml | 12 ++- examples/custom-indexer/rust/README.md | 29 +++++- examples/custom-indexer/rust/local_reader.rs | 46 +++++++++ .../rust/{main.rs => remote_reader.rs} | 0 5 files changed, 106 insertions(+), 77 deletions(-) create mode 100644 examples/custom-indexer/rust/local_reader.rs rename examples/custom-indexer/rust/{main.rs => remote_reader.rs} (100%) diff --git a/docs/content/guides/developer/advanced/custom-indexer.mdx b/docs/content/guides/developer/advanced/custom-indexer.mdx index eb325e53a2825..bd1e1a763185a 100644 --- a/docs/content/guides/developer/advanced/custom-indexer.mdx +++ b/docs/content/guides/developer/advanced/custom-indexer.mdx @@ -37,6 +37,8 @@ The most straightforward stream source is to subscribe to a remote store of chec - Testnet: `https://checkpoints.testnet.sui.io` - Mainnet: `https://checkpoints.mainnet.sui.io` +The checkpoint files are stored in the following format: `https://checkpoints.testnet.sui.io/.chk`. You can download the checkpoint file by sending an HTTP GET request to the relevant URL. Try it yourself for checkpoint 1 at [https://checkpoints.testnet.sui.io/1.chk](https://checkpoints.testnet.sui.io/1.chk). + ```mermaid flowchart LR A("fa:fa-cloud Cloud storage(S3, GCP)"); @@ -53,6 +55,12 @@ flowchart LR B-->External ``` +The Sui data ingestion framework provides a helper function to quickly bootstrap an indexer workflow. + +{@inject: examples/custom-indexer/rust/remote_reader.rs} + +This is suitable for setups with a single ingestion pipeline where progress tracking is managed outside of the framework. + ### Local reader Colocate the data ingestion daemon with a Full node and enable checkpoint dumping on the latter to set up a local stream source. After enabling, the Full node starts dumping executed checkpoints as files to a local directory, and the data ingestion daemon subscribes to changes in the directory through an inotify-like mechanism. This approach allows minimizing ingestion latency (checkpoint are processed immediately after a checkpoint executor on a Full node) and getting rid of dependency on an externally managed bucket. @@ -84,77 +92,12 @@ flowchart LR C<-->D("fa:fa-floppy-disk Progress store"); ``` - -### Hybrid mode - -Specify both a local and remote store as a fallback to ensure constant data flow. The framework always prioritizes locally available checkpoint data over remote data. It's useful when you want to start utilizing your own Full node for data ingestion but need to partially backfill historical data or just have a failover. - - -## Examples - -The Sui data ingestion framework provides a helper function to quickly bootstrap an indexer workflow. -```rust -struct CustomWorker; - -#[async_trait] -impl Worker for CustomWorker { - async fn process_checkpoint(&self, checkpoint: CheckpointData) -> Result<()> { - // custom processing logic - ... - Ok(()) - } -} - -#[tokio::main] -async fn main() -> Result<()> { - let (executor, term_sender) = setup_single_workflow( - CustomWorker, - "https://checkpoints.mainnet.sui.io".to_string(), - 0, /* initial checkpoint number */ - 5, /* concurrency */ - None, /* extra reader options */ - ).await?; - executor.await?; - Ok(()) -} -``` -This is suitable for setups with a single ingestion pipeline where progress tracking is managed outside of the framework. - -For more complex setups, refer to the following example: -```rust -struct CustomWorker; - -#[async_trait] -impl Worker for CustomWorker { - async fn process_checkpoint(&self, checkpoint: CheckpointData) -> Result<()> { - // custom processing logic - ... - Ok(()) - } -} - -#[tokio::main] -async fn main() -> Result<()> { - let (exit_sender, exit_receiver) = oneshot::channel(); - let metrics = DataIngestionMetrics::new(&Registry::new()); - let progress_store = FileProgressStore::new("path_to_file"); - let mut executor = IndexerExecutor::new(progress_store, 1 /* number of workflow types */, metrics); - let worker_pool = WorkerPool::new(CustomWorker, "custom worker", 100); - executor.register(worker_pool).await?; - executor.run( - PathBuf::from("..."), // path to a local directory - Some("https://checkpoints.mainnet.sui.io".to_string()), - vec![], // optional remote store access options - exit_receiver, - ).await?; - Ok(()) -} -``` +{@inject: examples/custom-indexer/rust/local_reader.rs} Let's highlight a couple lines of code: ```rust -let worker_pool = WorkerPool::new(CustomWorker, "custom worker", 100); +let worker_pool = WorkerPool::new(CustomWorker, "local_reader".to_string(), concurrency); executor.register(worker_pool).await?; ``` @@ -162,9 +105,19 @@ The data ingestion executor can run multiple workflows simultaneously. For each The concurrency parameter specifies how many threads the workflow uses. Having a concurrency value greater than 1 is helpful when tasks are idempotent and can be processed in parallel and out of order. The executor only updates the progress/watermark to a certain checkpoint when all preceding checkpoints are processed. -## Source code for an implementation {#source-code} +### Hybrid mode + +Specify both a local and remote store as a fallback to ensure constant data flow. The framework always prioritizes locally available checkpoint data over remote data. It's useful when you want to start utilizing your own Full node for data ingestion but need to partially backfill historical data or just have a failover. +```rust +executor.run( + PathBuf::from("./chk".to_string()), // path to a local directory + Some("https://checkpoints.testnet.sui.io".to_string()), // Remote Checkpoint Store + vec![], // optional remote store access options + ReaderOptions::default(), + exit_receiver, + ).await?; +``` -Find the following source code in the [Sui repo](https://github.com/mystenlabs/sui/tree/main/examples/custom-indexer/rust). ### Manifest @@ -172,11 +125,10 @@ Code for the cargo.toml manifest file for the custom indexer. {@inject: examples/custom-indexer/rust/cargo.toml} -### Rust source +## Source code for an implementation {#source-code} -Code for the main.rs file that creates the custom indexer. +Find the following source code in the [Sui repo](https://github.com/mystenlabs/sui/tree/main/examples/custom-indexer/rust). -{@inject: examples/custom-indexer/rust/main.rs} ## Related links diff --git a/examples/custom-indexer/rust/Cargo.toml b/examples/custom-indexer/rust/Cargo.toml index 5605e2cc8eefd..b805cd1b024b9 100644 --- a/examples/custom-indexer/rust/Cargo.toml +++ b/examples/custom-indexer/rust/Cargo.toml @@ -8,10 +8,16 @@ async-trait = "0.1.81" tokio = { version = "1.38.0", features = ["full"]} sui_types = { git = "https://github.com/mystenlabs/sui", package = "sui-types"} sui_data_ingestion_core = { git = "https://github.com/mystenlabs/sui", package = "sui-data-ingestion-core"} +prometheus = "0.13.3" anyhow = "1.0.86" -[workspace] +[[bin]] +name = "local_reader" +path = "local_reader.rs" [[bin]] -name = "custom-indexer" -path = "main.rs" +name = "remote_reader" +path = "remote_reader.rs" + +[workspace] + diff --git a/examples/custom-indexer/rust/README.md b/examples/custom-indexer/rust/README.md index 0c4b45bac7a21..37541f56153d1 100644 --- a/examples/custom-indexer/rust/README.md +++ b/examples/custom-indexer/rust/README.md @@ -13,6 +13,31 @@ cargo build ``` ## How to run -```bash -cargo run main.rs +### Remote Reader example +```sh +cargo run --bin remote_reader +``` + +### Local Reader example +The local reader example saves progress in a file called `/tmp/local_reader_progress` and monitors checkpoint files in the `chk` directory + + +To test the local reader example, create the `/tmp/local_reader_progress` file first +```sh +echo "{\"local_reader\": 1}" > /tmp/local_reader_progress +``` + +then, create the `chk` directory in the same level as the `local_reader.rs` file +```sh +mkdir -p chk +``` + +then, run the local reader example +```sh +cargo run --bin local_reader +``` + +Finally, copy the checkpoint files to the `chk` directory and the program should process the checkpoint files as they come in +```sh +cp $YOUR_CHECKPOINT_FILE chk/ ``` diff --git a/examples/custom-indexer/rust/local_reader.rs b/examples/custom-indexer/rust/local_reader.rs new file mode 100644 index 0000000000000..6d372749f3600 --- /dev/null +++ b/examples/custom-indexer/rust/local_reader.rs @@ -0,0 +1,46 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use tokio::sync::oneshot; +use anyhow::Result; +use async_trait::async_trait; +use sui_types::full_checkpoint_content::CheckpointData; +use sui_data_ingestion_core as sdic; +use sdic::{Worker, WorkerPool, ReaderOptions}; +use sdic::{DataIngestionMetrics, FileProgressStore, IndexerExecutor}; +use prometheus::Registry; +use std::path::PathBuf; +use std::env; + +struct CustomWorker; + +#[async_trait] +impl Worker for CustomWorker { + async fn process_checkpoint(&self, checkpoint: CheckpointData) -> Result<()> { + // custom processing logic + println!("Processing Local checkpoint: {}", checkpoint.checkpoint_summary.to_string()); + Ok(()) + } +} + +#[tokio::main] +async fn main() -> Result<()> { + let concurrency = 5; + let (exit_sender, exit_receiver) = oneshot::channel(); + let metrics = DataIngestionMetrics::new(&Registry::new()); + let backfill_progress_file_path = + env::var("BACKFILL_PROGRESS_FILE_PATH").unwrap_or("/tmp/local_reader_progress".to_string()); + let progress_store = FileProgressStore::new(PathBuf::from(backfill_progress_file_path)); + let mut executor = IndexerExecutor::new(progress_store, 1 /* number of workflow types */, metrics); + let worker_pool = WorkerPool::new(CustomWorker, "local_reader".to_string(), concurrency); + + executor.register(worker_pool).await?; + executor.run( + PathBuf::from("./chk".to_string()), // path to a local directory + None, + vec![], // optional remote store access options + ReaderOptions::default(), /* remote_read_batch_size */ + exit_receiver, + ).await?; + Ok(()) +} diff --git a/examples/custom-indexer/rust/main.rs b/examples/custom-indexer/rust/remote_reader.rs similarity index 100% rename from examples/custom-indexer/rust/main.rs rename to examples/custom-indexer/rust/remote_reader.rs From 7d30e9475b88fced5f162863ad85db301b76a01b Mon Sep 17 00:00:00 2001 From: hayes-mysten <135670682+hayes-mysten@users.noreply.github.com> Date: Mon, 12 Aug 2024 19:16:09 -0700 Subject: [PATCH 103/232] Fix cmdk not rendering account list (#18968) ## Description Describe the changes or additions included in this PR. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .../src/components/connect.tsx | 61 +++++++++++-------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/dapps/multisig-toolkit/src/components/connect.tsx b/dapps/multisig-toolkit/src/components/connect.tsx index 1dcddd002c47d..7f750bcb285a4 100644 --- a/dapps/multisig-toolkit/src/components/connect.tsx +++ b/dapps/multisig-toolkit/src/components/connect.tsx @@ -15,7 +15,14 @@ import { useState } from 'react'; import { cn } from '@/lib/utils'; import { Button } from './ui/button'; -import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from './ui/command'; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from './ui/command'; import { Popover, PopoverContent, PopoverTrigger } from './ui/popover'; function ConnectedButton() { @@ -41,37 +48,39 @@ function ConnectedButton() { - No account found. - - {accounts.map((account) => ( + + No account found. + + {accounts.map((account) => ( + { + switchAccount({ account }); + setOpen(false); + }} + > + + {formatAddress(account.address)} + + ))} + { - switchAccount({ account }); - setOpen(false); + disconnect(); }} > - - {formatAddress(account.address)} + Disconnect - ))} - - { - disconnect(); - }} - > - Disconnect - - + + From 77071e27701bb57d0156649104cd3e618cec5b55 Mon Sep 17 00:00:00 2001 From: hayes-mysten <135670682+hayes-mysten@users.noreply.github.com> Date: Mon, 12 Aug 2024 19:35:57 -0700 Subject: [PATCH 104/232] [multisig toolkit] fix mouse events in account selector (#18969) ## Description Describe the changes or additions included in this PR. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- dapps/multisig-toolkit/src/components/ui/command.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dapps/multisig-toolkit/src/components/ui/command.tsx b/dapps/multisig-toolkit/src/components/ui/command.tsx index 8a160da4d7ab2..5226361fda57d 100644 --- a/dapps/multisig-toolkit/src/components/ui/command.tsx +++ b/dapps/multisig-toolkit/src/components/ui/command.tsx @@ -1,6 +1,6 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -import { DialogProps } from '@radix-ui/react-dialog'; +import { type DialogProps } from '@radix-ui/react-dialog'; import { Command as CommandPrimitive } from 'cmdk'; import { Search } from 'lucide-react'; import * as React from 'react'; @@ -113,7 +113,7 @@ const CommandItem = React.forwardRef< Date: Mon, 12 Aug 2024 20:08:46 -0700 Subject: [PATCH 105/232] [denylist] Fix sign check early return (#18951) ## Description Describe the changes or additions included in this PR. ## Test plan Will add test in a separate PR. --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .../coin_deny_multiple_coin_types.exp | 53 ++++++++++++++ .../coin_deny_multiple_coin_types.move | 70 +++++++++++++++++++ crates/sui-types/src/deny_list_v2.rs | 2 +- 3 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 crates/sui-adapter-transactional-tests/tests/deny_list_v2/coin_deny_multiple_coin_types.exp create mode 100644 crates/sui-adapter-transactional-tests/tests/deny_list_v2/coin_deny_multiple_coin_types.move diff --git a/crates/sui-adapter-transactional-tests/tests/deny_list_v2/coin_deny_multiple_coin_types.exp b/crates/sui-adapter-transactional-tests/tests/deny_list_v2/coin_deny_multiple_coin_types.exp new file mode 100644 index 0000000000000..be2fe6b845377 --- /dev/null +++ b/crates/sui-adapter-transactional-tests/tests/deny_list_v2/coin_deny_multiple_coin_types.exp @@ -0,0 +1,53 @@ +processed 6 tasks + +init: +A: object(0,0) + +task 1, lines 11-60: +//# publish --sender A +created: object(1,0), object(1,1), object(1,2), object(1,3), object(1,4), object(1,5), object(1,6), object(1,7), object(1,8), object(1,9), object(1,10) +mutated: object(0,0) +unchanged_shared: 0x0000000000000000000000000000000000000000000000000000000000000403 +gas summary: computation_cost: 1000000, storage_cost: 34260800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, lines 61-63: +//# view-object 1,1 +Owner: Account Address ( A ) +Version: 2 +Contents: sui::coin::Coin { + id: sui::object::UID { + id: sui::object::ID { + bytes: fake(1,1), + }, + }, + balance: sui::balance::Balance { + value: 10000u64, + }, +} + +task 3, lines 64-66: +//# view-object 1,2 +Owner: Account Address ( A ) +Version: 2 +Contents: sui::coin::Coin { + id: sui::object::UID { + id: sui::object::ID { + bytes: fake(1,2), + }, + }, + balance: sui::balance::Balance { + value: 10000u64, + }, +} + +task 4, line 67: +//# run sui::coin::deny_list_v2_add --args object(0x403) object(1,6) @A --type-args test::regulated_coin2::REGULATED_COIN2 --sender A +events: Event { package_id: sui, transaction_module: Identifier("coin"), sender: A, type_: StructTag { address: sui, module: Identifier("deny_list"), name: Identifier("PerTypeConfigCreated"), type_params: [] }, contents: [0, 0, 0, 0, 0, 0, 0, 0, 98, 101, 100, 100, 50, 98, 100, 54, 51, 99, 50, 51, 102, 99, 52, 54, 52, 55, 102, 51, 101, 99, 101, 57, 48, 55, 57, 57, 49, 51, 102, 53, 99, 49, 98, 52, 98, 55, 97, 101, 101, 52, 56, 51, 50, 51, 55, 50, 97, 102, 52, 48, 99, 100, 48, 51, 49, 57, 50, 101, 97, 48, 52, 51, 97, 58, 58, 114, 101, 103, 117, 108, 97, 116, 101, 100, 95, 99, 111, 105, 110, 50, 58, 58, 82, 69, 71, 85, 76, 65, 84, 69, 68, 95, 67, 79, 73, 78, 50, 157, 35, 217, 228, 182, 26, 20, 142, 175, 244, 33, 216, 213, 187, 161, 23, 168, 25, 82, 72, 79, 110, 47, 210, 76, 90, 37, 190, 166, 158, 5, 16] } +created: object(4,0), object(4,1), object(4,2) +mutated: 0x0000000000000000000000000000000000000000000000000000000000000403, object(0,0), object(1,6) +gas summary: computation_cost: 1000000, storage_cost: 12220800, storage_rebate: 2761308, non_refundable_storage_fee: 27892 + +task 5, lines 69-70: +//# programmable --sender A --inputs object(1,1) object(1,2) @A +//> TransferObjects([Input(0), Input(1)], Input(2)) +Error: Error checking transaction input objects: AddressDeniedForCoin { address: @A, coin_type: "object(1,0)::regulated_coin2::REGULATED_COIN2" } diff --git a/crates/sui-adapter-transactional-tests/tests/deny_list_v2/coin_deny_multiple_coin_types.move b/crates/sui-adapter-transactional-tests/tests/deny_list_v2/coin_deny_multiple_coin_types.move new file mode 100644 index 0000000000000..1ed5f26f09769 --- /dev/null +++ b/crates/sui-adapter-transactional-tests/tests/deny_list_v2/coin_deny_multiple_coin_types.move @@ -0,0 +1,70 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// This test verifies when sending two objects of different coin types in the same transaction, +// if one is denied but not the other, the transaction check should still fail. +// More importantly, if the second type is denied but not the first, the fact that +// the first type doesn't even have a denylist entry should not matter. + +//# init --accounts A --addresses test=0x0 + +//# publish --sender A +module test::regulated_coin1 { + use sui::coin; + + public struct REGULATED_COIN1 has drop {} + + fun init(otw: REGULATED_COIN1, ctx: &mut TxContext) { + let (mut treasury_cap, deny_cap, metadata) = coin::create_regulated_currency_v2( + otw, + 9, + b"RC", + b"REGULATED_COIN", + b"A new regulated coin", + option::none(), + false, + ctx + ); + let coin = coin::mint(&mut treasury_cap, 10000, ctx); + transfer::public_transfer(coin, tx_context::sender(ctx)); + transfer::public_transfer(deny_cap, tx_context::sender(ctx)); + transfer::public_freeze_object(treasury_cap); + transfer::public_freeze_object(metadata); + } +} + +module test::regulated_coin2 { + use sui::coin; + + public struct REGULATED_COIN2 has drop {} + + fun init(otw: REGULATED_COIN2, ctx: &mut TxContext) { + let (mut treasury_cap, deny_cap, metadata) = coin::create_regulated_currency_v2( + otw, + 9, + b"RC", + b"REGULATED_COIN", + b"A new regulated coin", + option::none(), + false, + ctx + ); + let coin = coin::mint(&mut treasury_cap, 10000, ctx); + transfer::public_transfer(coin, tx_context::sender(ctx)); + transfer::public_transfer(deny_cap, tx_context::sender(ctx)); + transfer::public_freeze_object(treasury_cap); + transfer::public_freeze_object(metadata); + } +} + +// Coin1 +//# view-object 1,1 + +// Coin2 +//# view-object 1,2 + +// Deny account A for coin2. +//# run sui::coin::deny_list_v2_add --args object(0x403) object(1,6) @A --type-args test::regulated_coin2::REGULATED_COIN2 --sender A + +//# programmable --sender A --inputs object(1,1) object(1,2) @A +//> TransferObjects([Input(0), Input(1)], Input(2)) diff --git a/crates/sui-types/src/deny_list_v2.rs b/crates/sui-types/src/deny_list_v2.rs index 6ac9f049acb95..ebe0e982d030c 100644 --- a/crates/sui-types/src/deny_list_v2.rs +++ b/crates/sui-types/src/deny_list_v2.rs @@ -109,7 +109,7 @@ pub fn check_coin_deny_list_v2_during_signing( let coin_types = input_object_coin_types_for_denylist_check(input_objects, receiving_objects); for coin_type in coin_types { let Some(deny_list) = get_per_type_coin_deny_list_v2(&coin_type, object_store) else { - return Ok(()); + continue; }; if check_global_pause(&deny_list, object_store, None) { return Err(UserInputError::CoinTypeGlobalPause { coin_type }); From 1913ce6baa4a17adc764690f06e3118d0c964e9c Mon Sep 17 00:00:00 2001 From: mwtian <81660174+mwtian@users.noreply.github.com> Date: Tue, 13 Aug 2024 07:29:40 +0100 Subject: [PATCH 106/232] [Consensus] avoid subtracting from current Instant (#18939) ## Description On Windows, `Instant::now()` can be close to 0 and we cannot subtract from that. ## Test plan CI. Verified on a Windows machine. --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- consensus/core/src/context.rs | 48 +++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/consensus/core/src/context.rs b/consensus/core/src/context.rs index 64467cf72ca73..440a63a9b1e75 100644 --- a/consensus/core/src/context.rs +++ b/consensus/core/src/context.rs @@ -96,34 +96,50 @@ impl Context { } } -/// A clock that allows to derive the current UNIX system timestamp while guaranteeing that -/// timestamp will be monotonically incremented having tolerance to ntp and system clock changes and corrections. +/// A clock that allows to derive the current UNIX system timestamp while guaranteeing that timestamp +/// will be monotonically incremented, tolerating ntp and system clock changes and corrections. /// Explicitly avoid to make `[Clock]` cloneable to ensure that a single instance is shared behind an `[Arc]` /// wherever is needed in order to make sure that consecutive calls to receive the system timestamp /// will remain monotonically increasing. pub(crate) struct Clock { - unix_epoch_instant: Instant, + initial_instant: Instant, + initial_system_time: SystemTime, } impl Clock { pub fn new() -> Self { - let now = Instant::now(); - let duration_since_unix_epoch = - match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { - Ok(d) => d, - Err(e) => panic!("SystemTime before UNIX EPOCH! {e}"), - }; - let unix_epoch_instant = now.checked_sub(duration_since_unix_epoch).unwrap(); - - Self { unix_epoch_instant } + Self { + initial_instant: Instant::now(), + initial_system_time: SystemTime::now(), + } } // Returns the current time expressed as UNIX timestamp in milliseconds. - // Calculated with Rust Instant to ensure monotonicity. + // Calculated with Tokio Instant to ensure monotonicity, + // and to allow testing with tokio clock. pub(crate) fn timestamp_utc_ms(&self) -> BlockTimestampMs { - Instant::now() - .checked_duration_since(self.unix_epoch_instant) - .unwrap() + let now: Instant = Instant::now(); + let monotonic_system_time = self + .initial_system_time + .checked_add( + now.checked_duration_since(self.initial_instant) + .unwrap_or_else(|| { + panic!( + "current instant ({:?}) < initial instant ({:?})", + now, self.initial_instant + ) + }), + ) + .expect("Computing system time should not overflow"); + monotonic_system_time + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap_or_else(|_| { + panic!( + "system time ({:?}) < UNIX_EPOCH ({:?})", + monotonic_system_time, + SystemTime::UNIX_EPOCH, + ) + }) .as_millis() as BlockTimestampMs } } From c90056e379e00bc710037bb847793426ef9765ac Mon Sep 17 00:00:00 2001 From: Patrick Kuo Date: Tue, 13 Aug 2024 15:46:29 +0100 Subject: [PATCH 107/232] [bridge-indexer] - simplify bridge generic (#18908) ## Description Describe the changes or additions included in this PR. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .../src/eth_bridge_indexer.rs | 49 +++--------- .../sui-bridge-indexer/src/indexer_builder.rs | 76 +++++++++---------- 2 files changed, 47 insertions(+), 78 deletions(-) diff --git a/crates/sui-bridge-indexer/src/eth_bridge_indexer.rs b/crates/sui-bridge-indexer/src/eth_bridge_indexer.rs index 3a5b33d185376..75577adbf5c8c 100644 --- a/crates/sui-bridge-indexer/src/eth_bridge_indexer.rs +++ b/crates/sui-bridge-indexer/src/eth_bridge_indexer.rs @@ -18,16 +18,15 @@ use sui_bridge::retry_with_max_elapsed_time; use tokio::task::JoinHandle; use tracing::info; -use mysten_metrics::metered_channel::Receiver; use mysten_metrics::{metered_channel, spawn_monitored_task}; use sui_bridge::abi::{EthBridgeEvent, EthSuiBridgeEvents}; use sui_bridge::metrics::BridgeMetrics; use sui_bridge::types::{EthEvent, RawEthLog}; -use crate::indexer_builder::{DataMapper, Datasource}; +use crate::indexer_builder::{CheckpointData, DataMapper, Datasource}; use crate::metrics::BridgeIndexerMetrics; -use crate::sui_bridge_indexer::PgBridgePersistent; + use crate::{ BridgeDataSource, ProcessedTxnData, TokenTransfer, TokenTransferData, TokenTransferStatus, }; @@ -55,32 +54,18 @@ impl EthSubscriptionDatasource { } } #[async_trait] -impl Datasource for EthSubscriptionDatasource { +impl Datasource for EthSubscriptionDatasource { async fn start_data_retrieval( &self, - task_name: String, starting_checkpoint: u64, target_checkpoint: u64, - ) -> Result< - ( - JoinHandle>, - Receiver<(u64, Vec)>, - ), - Error, - > { + data_sender: metered_channel::Sender>, + ) -> Result>, Error> { let filter = Filter::new() .address(self.bridge_address) .from_block(starting_checkpoint) .to_block(target_checkpoint); - let (data_sender, data_receiver) = metered_channel::channel( - 1000, - &mysten_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&[&task_name]), - ); - let eth_ws_url = self.eth_ws_url.clone(); let indexer_metrics: BridgeIndexerMetrics = self.indexer_metrics.clone(); @@ -142,7 +127,7 @@ impl Datasource for EthSubscri Ok::<_, Error>(()) }); - Ok((handle, data_receiver)) + Ok(handle) } } @@ -170,19 +155,13 @@ impl EthSyncDatasource { } } #[async_trait] -impl Datasource for EthSyncDatasource { +impl Datasource for EthSyncDatasource { async fn start_data_retrieval( &self, - task_name: String, starting_checkpoint: u64, target_checkpoint: u64, - ) -> Result< - ( - JoinHandle>, - Receiver<(u64, Vec)>, - ), - Error, - > { + data_sender: metered_channel::Sender>, + ) -> Result>, Error> { let client: Arc> = Arc::new( EthClient::::new( &self.eth_http_url, @@ -197,14 +176,6 @@ impl Datasource for EthSyncDat .interval(std::time::Duration::from_millis(2000)), ); - let (data_sender, data_receiver) = metered_channel::channel( - 1000, - &mysten_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&[&task_name]), - ); - let bridge_address = self.bridge_address; let indexer_metrics: BridgeIndexerMetrics = self.indexer_metrics.clone(); let client = Arc::clone(&client); @@ -265,7 +236,7 @@ impl Datasource for EthSyncDat Ok::<_, Error>(()) }); - Ok((handle, data_receiver)) + Ok(handle) } } diff --git a/crates/sui-bridge-indexer/src/indexer_builder.rs b/crates/sui-bridge-indexer/src/indexer_builder.rs index 37b616fa41a2b..ca9f96046bb4e 100644 --- a/crates/sui-bridge-indexer/src/indexer_builder.rs +++ b/crates/sui-bridge-indexer/src/indexer_builder.rs @@ -12,16 +12,20 @@ use tokio::sync::oneshot::Sender; use tokio::task::JoinHandle; use tracing::info; -use mysten_metrics::metered_channel::Receiver; use mysten_metrics::{metered_channel, spawn_monitored_task}; use sui_data_ingestion_core::{ DataIngestionMetrics, IndexerExecutor, ProgressStore, ReaderOptions, Worker, WorkerPool, }; -use sui_types::full_checkpoint_content::{CheckpointData, CheckpointTransaction}; +use sui_types::digests::TransactionDigest; +use sui_types::full_checkpoint_content::{ + CheckpointData as SuiCheckpointData, CheckpointTransaction, +}; use sui_types::messages_checkpoint::CheckpointSequenceNumber; use crate::sui_checkpoint_ingestion::{Task, Tasks}; +pub type CheckpointData = (u64, Vec); + pub struct IndexerBuilder { name: String, datasource: D, @@ -86,8 +90,8 @@ pub struct Indexer { impl Indexer { pub async fn start(mut self) -> Result<(), Error> where - D: Datasource + 'static + Send + Sync, - M: DataMapper + 'static + Clone, + D: Datasource + 'static, + M: DataMapper + 'static, P: Persistent + 'static, T: Send, { @@ -186,7 +190,7 @@ impl Indexer { // Create backfill tasks according to backfill strategy fn create_backfill_tasks(&mut self, mut current_cp: u64) -> Result<(), Error> where - P: Persistent + 'static, + P: Persistent, { match self.backfill_strategy { BackfillStrategy::Simple => self.storage.register_task( @@ -237,8 +241,8 @@ pub trait IndexerProgressStore: Send { } #[async_trait] -pub trait Datasource { - async fn start_ingestion_task( +pub trait Datasource: Sync + Send { + async fn start_ingestion_task( &self, task_name: String, starting_checkpoint: u64, @@ -247,13 +251,21 @@ pub trait Datasource { data_mapper: M, ) -> Result<(), Error> where - M: DataMapper + 'static, - P: Persistent + 'static, + M: DataMapper, + P: Persistent, { // todo: add metrics for number of tasks - let (join_handle, mut data_channel) = self - .start_data_retrieval(task_name.clone(), starting_checkpoint, target_checkpoint) + let (data_sender, mut data_channel) = metered_channel::channel( + 1000, + &mysten_metrics::get_metrics() + .unwrap() + .channel_inflight + .with_label_values(&[&task_name]), + ); + let join_handle = self + .start_data_retrieval(starting_checkpoint, target_checkpoint, data_sender) .await?; + while let Some((block_number, data)) = data_channel.recv().await { if !data.is_empty() { let processed_data = data.into_iter().try_fold(vec![], |mut result, d| { @@ -273,10 +285,10 @@ pub trait Datasource { async fn start_data_retrieval( &self, - task_name: String, starting_checkpoint: u64, target_checkpoint: u64, - ) -> Result<(JoinHandle>, Receiver<(u64, Vec)>), Error>; + data_sender: metered_channel::Sender>, + ) -> Result>, Error>; } pub struct SuiCheckpointDatasource { @@ -302,23 +314,13 @@ impl SuiCheckpointDatasource { } #[async_trait] -impl Datasource for SuiCheckpointDatasource -where - P: Persistent + 'static, - R: Sync + Send + 'static, -{ +impl Datasource for SuiCheckpointDatasource { async fn start_data_retrieval( &self, - task_name: String, starting_checkpoint: u64, target_checkpoint: u64, - ) -> Result< - ( - JoinHandle>, - Receiver<(u64, Vec)>, - ), - Error, - > { + data_sender: metered_channel::Sender>, + ) -> Result>, Error> { let (exit_sender, exit_receiver) = oneshot::channel(); let progress_store = PerTaskInMemProgressStore { current_checkpoint: starting_checkpoint, @@ -326,19 +328,16 @@ where exit_sender: Some(exit_sender), }; let mut executor = IndexerExecutor::new(progress_store, 1, self.metrics.clone()); - let (data_sender, data_receiver) = metered_channel::channel( - 1000, - &mysten_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&[&task_name]), - ); let worker = IndexerWorker::new(data_sender); - let worker_pool = WorkerPool::new(worker, task_name, self.concurrency); + let worker_pool = WorkerPool::new( + worker, + TransactionDigest::random().to_string(), + self.concurrency, + ); executor.register(worker_pool).await?; let checkpoint_path = self.checkpoint_path.clone(); let remote_store_url = self.remote_store_url.clone(); - let join_handle = spawn_monitored_task!(async { + Ok(spawn_monitored_task!(async { executor .run( checkpoint_path, @@ -349,8 +348,7 @@ where ) .await?; Ok(()) - }); - Ok((join_handle, data_receiver)) + })) } } @@ -360,7 +358,7 @@ pub enum BackfillStrategy { Disabled, } -pub trait DataMapper: Sync + Send { +pub trait DataMapper: Sync + Send + Clone { fn map(&self, data: T) -> Result, anyhow::Error>; } @@ -408,7 +406,7 @@ pub type CheckpointTxnData = (CheckpointTransaction, u64, u64); #[async_trait] impl Worker for IndexerWorker { - async fn process_checkpoint(&self, checkpoint: CheckpointData) -> anyhow::Result<()> { + async fn process_checkpoint(&self, checkpoint: SuiCheckpointData) -> anyhow::Result<()> { info!( "Received checkpoint [{}] {}: {}", checkpoint.checkpoint_summary.epoch, From 7f05c707874a271f4b6b5d01f0a460a8fdb064ca Mon Sep 17 00:00:00 2001 From: Xun Li Date: Tue, 13 Aug 2024 07:58:33 -0700 Subject: [PATCH 108/232] Fix fullnode event resolution (#18958) ## Description Introduce a new struct that contains both local package store and newly published packages. This allows us to resolve new events using both. ## Test plan I will add some tests later. --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-e2e-tests/tests/full_node_tests.rs | 27 ++++++++++ .../sources/init_with_events.move | 10 ++++ .../src/transaction_execution_api.rs | 26 ++++++---- crates/sui-types/src/storage/mod.rs | 51 +++++++++++++++++-- 4 files changed, 99 insertions(+), 15 deletions(-) create mode 100644 crates/sui-e2e-tests/tests/move_test_code/sources/init_with_events.move diff --git a/crates/sui-e2e-tests/tests/full_node_tests.rs b/crates/sui-e2e-tests/tests/full_node_tests.rs index 87ee651e03879..90c018f08aa2f 100644 --- a/crates/sui-e2e-tests/tests/full_node_tests.rs +++ b/crates/sui-e2e-tests/tests/full_node_tests.rs @@ -7,6 +7,7 @@ use jsonrpsee::rpc_params; use move_core_types::annotated_value::MoveStructLayout; use move_core_types::ident_str; use rand::rngs::OsRng; +use std::path::PathBuf; use std::sync::Arc; use sui::client_commands::{OptsWithGas, SuiClientCommandResult, SuiClientCommands}; use sui_config::node::RunWithRange; @@ -1407,3 +1408,29 @@ async fn test_full_node_run_with_range_epoch() -> Result<(), anyhow::Error> { Ok(()) } + +// This test checks that the fullnode is able to resolve events emitted from a transaction +// that references the structs defined in the package published by the transaction itself, +// without local execution. +#[sim_test] +async fn publish_init_events_without_local_execution() { + let test_cluster = TestClusterBuilder::new().build().await; + let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/move_test_code"); + let tx_data = test_cluster + .test_transaction_builder() + .await + .publish(path) + .build(); + let tx = test_cluster.sign_transaction(&tx_data); + let client = test_cluster.wallet.get_client().await.unwrap(); + let response = client + .quorum_driver_api() + .execute_transaction_block( + tx, + SuiTransactionBlockResponseOptions::new().with_events(), + Some(ExecuteTransactionRequestType::WaitForEffectsCert), + ) + .await + .unwrap(); + assert_eq!(response.events.unwrap().data.len(), 1); +} diff --git a/crates/sui-e2e-tests/tests/move_test_code/sources/init_with_events.move b/crates/sui-e2e-tests/tests/move_test_code/sources/init_with_events.move new file mode 100644 index 0000000000000..0230961c8e19e --- /dev/null +++ b/crates/sui-e2e-tests/tests/move_test_code/sources/init_with_events.move @@ -0,0 +1,10 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +module move_test_code::init_with_event { + public struct Event has drop, copy {} + + fun init(_ctx: &mut TxContext) { + sui::event::emit(Event {}); + } +} \ No newline at end of file diff --git a/crates/sui-json-rpc/src/transaction_execution_api.rs b/crates/sui-json-rpc/src/transaction_execution_api.rs index 749cb3ebee8a4..d38a70be4c553 100644 --- a/crates/sui-json-rpc/src/transaction_execution_api.rs +++ b/crates/sui-json-rpc/src/transaction_execution_api.rs @@ -10,6 +10,12 @@ use fastcrypto::traits::ToFromBytes; use jsonrpsee::core::RpcResult; use jsonrpsee::RpcModule; +use crate::authority_state::StateRead; +use crate::error::{Error, SuiRpcInputError}; +use crate::{ + get_balance_changes_from_effect, get_object_changes, with_tracing, ObjectProviderCache, + SuiRpcModule, +}; use mysten_metrics::spawn_monitored_task; use shared_crypto::intent::{AppId, Intent, IntentMessage, IntentScope, IntentVersion}; use sui_core::authority::AuthorityState; @@ -29,19 +35,13 @@ use sui_types::quorum_driver_types::{ ExecuteTransactionRequestType, ExecuteTransactionRequestV3, ExecuteTransactionResponseV3, }; use sui_types::signature::GenericSignature; +use sui_types::storage::PostExecutionPackageResolver; use sui_types::sui_serde::BigInt; use sui_types::transaction::{ InputObjectKind, Transaction, TransactionData, TransactionDataAPI, TransactionKind, }; use tracing::instrument; -use crate::authority_state::StateRead; -use crate::error::{Error, SuiRpcInputError}; -use crate::{ - get_balance_changes_from_effect, get_object_changes, with_tracing, ObjectProviderCache, - SuiRpcModule, -}; - pub struct TransactionExecutionApi { state: Arc, transaction_orchestrator: Arc>, @@ -117,7 +117,10 @@ impl TransactionExecutionApi { transaction: txn.clone(), include_events: opts.show_events, include_input_objects: opts.show_balance_changes || opts.show_object_changes, - include_output_objects: opts.show_balance_changes || opts.show_object_changes, + include_output_objects: opts.show_balance_changes + || opts.show_object_changes + // In order to resolve events, we may need access to the newly published packages. + || opts.show_events, include_auxiliary_data: false, }; @@ -182,10 +185,13 @@ impl TransactionExecutionApi { let events = if opts.show_events { let epoch_store = self.state.load_epoch_store_one_call_per_task(); - let backing_package_store = self.state.get_backing_package_store(); + let backing_package_store = PostExecutionPackageResolver::new( + self.state.get_backing_package_store().clone(), + &response.output_objects, + ); let mut layout_resolver = epoch_store .executor() - .type_layout_resolver(Box::new(backing_package_store.as_ref())); + .type_layout_resolver(Box::new(backing_package_store)); Some(SuiTransactionBlockEvents::try_from( response.events.unwrap_or_default(), digest, diff --git a/crates/sui-types/src/storage/mod.rs b/crates/sui-types/src/storage/mod.rs index c9de3fd31080a..93cb31330eb24 100644 --- a/crates/sui-types/src/storage/mod.rs +++ b/crates/sui-types/src/storage/mod.rs @@ -22,6 +22,10 @@ use itertools::Itertools; use move_binary_format::CompiledModule; use move_core_types::language_storage::ModuleId; pub use object_store_trait::ObjectStore; +pub use read_store::AccountOwnedObjectInfo; +pub use read_store::CoinInfo; +pub use read_store::DynamicFieldIndexInfo; +pub use read_store::DynamicFieldKey; pub use read_store::ReadStore; pub use read_store::RestStateReader; use serde::{Deserialize, Serialize}; @@ -33,11 +37,6 @@ use std::fmt::{Display, Formatter}; use std::sync::Arc; pub use write_store::WriteStore; -pub use read_store::AccountOwnedObjectInfo; -pub use read_store::CoinInfo; -pub use read_store::DynamicFieldIndexInfo; -pub use read_store::DynamicFieldKey; - /// A potential input to a transaction. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum InputKey { @@ -340,6 +339,48 @@ pub fn get_module_by_id( .map(|bytes| CompiledModule::deserialize_with_defaults(&bytes).unwrap())) } +/// A `BackingPackageStore` that resolves packages from a backing store, but also includes any +/// packages that were published in the current transaction execution. This can be used to resolve +/// Move modules right after transaction execution, but newly published packages have not yet been +/// committed to the backing store on a fullnode. +pub struct PostExecutionPackageResolver { + backing_store: Arc, + new_packages: BTreeMap, +} + +impl PostExecutionPackageResolver { + pub fn new( + backing_store: Arc, + output_objects: &Option>, + ) -> Self { + let new_packages = output_objects + .iter() + .flatten() + .filter_map(|o| { + if o.is_package() { + Some((o.id(), PackageObject::new(o.clone()))) + } else { + None + } + }) + .collect(); + Self { + backing_store, + new_packages, + } + } +} + +impl BackingPackageStore for PostExecutionPackageResolver { + fn get_package_object(&self, package_id: &ObjectID) -> SuiResult> { + if let Some(package) = self.new_packages.get(package_id) { + Ok(Some(package.clone())) + } else { + self.backing_store.get_package_object(package_id) + } + } +} + pub trait ParentSync { /// This function is only called by older protocol versions. /// It creates an explicit dependency to tombstones, which is not desired. From bb3fc0b87b80e0c9f479cc4d5089e0f243aac582 Mon Sep 17 00:00:00 2001 From: Zihe Huang Date: Tue, 13 Aug 2024 08:48:09 -0700 Subject: [PATCH 109/232] [docs] change custom indexer example Cargo.toml capitalization (#18970) ## Description Code inject link was broken. Renaming to fix it. Also added deprecation warning for event subscription ## Test plan by inspection --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --------- Co-authored-by: Ronny Roland --- .../developer/advanced/custom-indexer.mdx | 2 +- .../guides/developer/sui-101/using-events.mdx | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/content/guides/developer/advanced/custom-indexer.mdx b/docs/content/guides/developer/advanced/custom-indexer.mdx index bd1e1a763185a..7eb800f4108d2 100644 --- a/docs/content/guides/developer/advanced/custom-indexer.mdx +++ b/docs/content/guides/developer/advanced/custom-indexer.mdx @@ -123,7 +123,7 @@ executor.run( Code for the cargo.toml manifest file for the custom indexer. -{@inject: examples/custom-indexer/rust/cargo.toml} +{@inject: examples/custom-indexer/rust/Cargo.toml} ## Source code for an implementation {#source-code} diff --git a/docs/content/guides/developer/sui-101/using-events.mdx b/docs/content/guides/developer/sui-101/using-events.mdx index 79a788b75d069..00797838a7d7d 100644 --- a/docs/content/guides/developer/sui-101/using-events.mdx +++ b/docs/content/guides/developer/sui-101/using-events.mdx @@ -119,11 +119,11 @@ Move smart contracts can call other smart contracts that emit events. For exampl ## Examples -## Subscribe to event +### Subscribe to event This example leverages the Sui TypeScript SDK to subscribe to events the package with ID `` emits. Each time the event fires, the code displays the response to the console. -### TypeScript +#### TypeScript To create the event subscription, you can use a basic Node.js app. You need the Sui TypeScript SDK, so install the module using `npm install @mysten/sui` at the root of your project. In your TypeScript code, import `JsonRpcProvider` and a connection from the library. @@ -161,7 +161,7 @@ process.on('SIGINT', async () => { }); ``` -### Response +#### Response When the subscribed to event fires, the example displays the following JSON representation of the event. @@ -186,7 +186,7 @@ subscribeEvent { } ``` -### Rust SDK +#### Rust SDK ```rust use futures::StreamExt; @@ -207,7 +207,7 @@ async fn main() -> Result<()> { } ``` -## Filtering event queries +### Filtering event queries To filter the events returned from your queries, use the following data structures. @@ -230,7 +230,13 @@ This set of filters applies only to event querying APIs. It differs from the fil | `Object` | Return events associated with the given object | `{"Object":"0x727b37454ab13d5c1dbb22e8741bff72b145d1e660f71b275c01f24e7860e5e5"}` | | `TimeRange` | Return events emitted in [start_time, end_time] interval | `{"TimeRange":{"startTime":1669039504014, "endTime":1669039604014}}` | -## Filtering events for subscription +### Filtering events for subscription (deprecated) + +:::warning + +This section is deprecated beginning with Sui client version 1.28. Use the [custom indexer](../advanced/custom-indexer.mdx) section to learn about how to stream checkpoints and filter events continuously. + +::: To create a subscription, you can set a filter to return only the set of events you're interested in listening for. @@ -241,6 +247,7 @@ This set of filters applies only to event subscription APIs. It differs from the ::: + | Filter | Description | JSON-RPC Parameter Example | | ----------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------- | | `Package` | Move package ID | `{"Package":""}` | From fe922b7022b92c5047a3e05c4d078484571bf643 Mon Sep 17 00:00:00 2001 From: Eugene Boguslavsky Date: Tue, 13 Aug 2024 17:24:45 +0100 Subject: [PATCH 110/232] Revert "[indexer] merge in indexer breaking change park" (#18975) Reverts MystenLabs/sui#18899 --- .../src/types/transaction_block.rs | 20 +- .../mysql/2024-04-24-180249_packages/up.sql | 13 +- .../2024-05-05-155158_obj_indices/down.sql | 1 - .../2024-05-05-155158_obj_indices/up.sql | 9 - .../pg/2023-08-19-044020_events/up.sql | 5 +- .../2023-08-19-044026_transactions/down.sql | 1 - .../pg/2023-08-19-044026_transactions/up.sql | 8 +- .../pg/2023-08-19-044044_checkpoints/up.sql | 20 +- .../pg/2023-08-19-060729_packages/up.sql | 14 +- .../pg/2023-10-06-204335_tx_indices/down.sql | 5 +- .../pg/2023-10-06-204335_tx_indices/up.sql | 61 +-- .../up.sql | 6 +- .../pg/2024-05-05-155158_obj_indices/down.sql | 1 - .../pg/2024-05-05-155158_obj_indices/up.sql | 31 -- .../2024-06-14-045801_event_indices/down.sql | 7 - .../pg/2024-06-14-045801_event_indices/up.sql | 74 --- .../src/handlers/checkpoint_handler.rs | 66 +-- crates/sui-indexer/src/handlers/committer.rs | 8 - crates/sui-indexer/src/handlers/mod.rs | 6 +- crates/sui-indexer/src/indexer_reader.rs | 8 +- crates/sui-indexer/src/metrics.rs | 16 - crates/sui-indexer/src/models/checkpoints.rs | 4 - .../sui-indexer/src/models/event_indices.rs | 145 ------ crates/sui-indexer/src/models/mod.rs | 2 - crates/sui-indexer/src/models/obj_indices.rs | 40 -- crates/sui-indexer/src/models/packages.rs | 6 - crates/sui-indexer/src/models/tx_indices.rs | 117 ++--- crates/sui-indexer/src/schema/mod.rs | 39 +- crates/sui-indexer/src/schema/mysql.rs | 128 +---- crates/sui-indexer/src/schema/pg.rs | 149 +----- crates/sui-indexer/src/store/indexer_store.rs | 9 +- crates/sui-indexer/src/store/mod.rs | 30 -- .../sui-indexer/src/store/pg_indexer_store.rs | 450 ++---------------- .../src/store/pg_partition_manager.rs | 78 +-- crates/sui-indexer/src/types.rs | 49 +- 35 files changed, 178 insertions(+), 1448 deletions(-) delete mode 100644 crates/sui-indexer/migrations/mysql/2024-05-05-155158_obj_indices/down.sql delete mode 100644 crates/sui-indexer/migrations/mysql/2024-05-05-155158_obj_indices/up.sql delete mode 100644 crates/sui-indexer/migrations/pg/2024-05-05-155158_obj_indices/down.sql delete mode 100644 crates/sui-indexer/migrations/pg/2024-05-05-155158_obj_indices/up.sql delete mode 100644 crates/sui-indexer/migrations/pg/2024-06-14-045801_event_indices/down.sql delete mode 100644 crates/sui-indexer/migrations/pg/2024-06-14-045801_event_indices/up.sql delete mode 100644 crates/sui-indexer/src/models/event_indices.rs delete mode 100644 crates/sui-indexer/src/models/obj_indices.rs diff --git a/crates/sui-graphql-rpc/src/types/transaction_block.rs b/crates/sui-graphql-rpc/src/types/transaction_block.rs index c75a34ee2fcda..5009d076145d8 100644 --- a/crates/sui-graphql-rpc/src/types/transaction_block.rs +++ b/crates/sui-graphql-rpc/src/types/transaction_block.rs @@ -8,14 +8,14 @@ use async_graphql::{ dataloader::Loader, *, }; -use diesel::{ExpressionMethods, JoinOnDsl, QueryDsl, SelectableHelper}; +use diesel::{BoolExpressionMethods, ExpressionMethods, JoinOnDsl, QueryDsl, SelectableHelper}; use fastcrypto::encoding::{Base58, Encoding}; use serde::{Deserialize, Serialize}; use sui_indexer::{ models::transactions::StoredTransaction, schema::{ - transactions, tx_calls_fun, tx_changed_objects, tx_digests, tx_input_objects, - tx_recipients, tx_senders, + transactions, tx_calls, tx_changed_objects, tx_digests, tx_input_objects, tx_recipients, + tx_senders, }, }; use sui_types::{ @@ -318,15 +318,15 @@ impl TransactionBlock { let mut query = tx::dsl::transactions.into_boxed(); if let Some(f) = &filter.function { - let sub_query = tx_calls_fun::dsl::tx_calls_fun - .select(tx_calls_fun::dsl::tx_sequence_number) + let sub_query = tx_calls::dsl::tx_calls + .select(tx_calls::dsl::tx_sequence_number) .into_boxed(); query = query.filter(tx::dsl::tx_sequence_number.eq_any(f.apply( sub_query, - tx_calls_fun::dsl::package, - tx_calls_fun::dsl::module, - tx_calls_fun::dsl::func, + tx_calls::dsl::package, + tx_calls::dsl::module, + tx_calls::dsl::func, ))); } @@ -507,7 +507,9 @@ impl Loader for Db { let transactions: Vec = self .execute(move |conn| { conn.results(move || { - let join = ds::tx_sequence_number.eq(tx::tx_sequence_number); + let join = ds::cp_sequence_number + .eq(tx::checkpoint_sequence_number) + .and(ds::tx_sequence_number.eq(tx::tx_sequence_number)); tx::transactions .inner_join(ds::tx_digests.on(join)) diff --git a/crates/sui-indexer/migrations/mysql/2024-04-24-180249_packages/up.sql b/crates/sui-indexer/migrations/mysql/2024-04-24-180249_packages/up.sql index 7ee89206f254f..f3fe2539038fc 100644 --- a/crates/sui-indexer/migrations/mysql/2024-04-24-180249_packages/up.sql +++ b/crates/sui-indexer/migrations/mysql/2024-04-24-180249_packages/up.sql @@ -1,14 +1,7 @@ CREATE TABLE packages ( - package_id BLOB NOT NULL, - original_id BLOB NOT NULL, - package_version BIGINT NOT NULL, + package_id blob NOT NULL, -- bcs serialized MovePackage - move_package MEDIUMBLOB NOT NULL, - checkpoint_sequence_number BIGINT NOT NULL, - CONSTRAINT packages_pk PRIMARY KEY (package_id(32), original_id(32), package_version), - CONSTRAINT packages_unique_package_id UNIQUE (package_id(32)) + move_package MEDIUMBLOB NOT NULL, + CONSTRAINT packages_pk PRIMARY KEY (package_id(255)) ); - -CREATE INDEX packages_cp_id_version ON packages (checkpoint_sequence_number, original_id(32), package_version); -CREATE INDEX packages_id_version_cp ON packages (original_id(32), package_version, checkpoint_sequence_number); diff --git a/crates/sui-indexer/migrations/mysql/2024-05-05-155158_obj_indices/down.sql b/crates/sui-indexer/migrations/mysql/2024-05-05-155158_obj_indices/down.sql deleted file mode 100644 index 7a3a7670f24c2..0000000000000 --- a/crates/sui-indexer/migrations/mysql/2024-05-05-155158_obj_indices/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE IF EXISTS objects_version; diff --git a/crates/sui-indexer/migrations/mysql/2024-05-05-155158_obj_indices/up.sql b/crates/sui-indexer/migrations/mysql/2024-05-05-155158_obj_indices/up.sql deleted file mode 100644 index e501b71a073c0..0000000000000 --- a/crates/sui-indexer/migrations/mysql/2024-05-05-155158_obj_indices/up.sql +++ /dev/null @@ -1,9 +0,0 @@ --- The Postgres version of this table is partitioned by the first byte --- of object_id, but this kind of partition is not easily supported in --- MySQL, so this variant is unpartitioned for now. -CREATE TABLE objects_version ( - object_id BLOB NOT NULL, - object_version BIGINT NOT NULL, - cp_sequence_number BIGINT NOT NULL, - PRIMARY KEY (object_id(32), object_version) -) diff --git a/crates/sui-indexer/migrations/pg/2023-08-19-044020_events/up.sql b/crates/sui-indexer/migrations/pg/2023-08-19-044020_events/up.sql index dfbfa3ea14495..a6c0d70566e7b 100644 --- a/crates/sui-indexer/migrations/pg/2023-08-19-044020_events/up.sql +++ b/crates/sui-indexer/migrations/pg/2023-08-19-044020_events/up.sql @@ -1,4 +1,3 @@ --- TODO: modify queries in indexer reader to take advantage of the new indices CREATE TABLE events ( tx_sequence_number BIGINT NOT NULL, @@ -24,8 +23,8 @@ CREATE TABLE events timestamp_ms BIGINT NOT NULL, -- bcs of the Event contents (Event.contents) bcs BYTEA NOT NULL, - PRIMARY KEY(tx_sequence_number, event_sequence_number) -) PARTITION BY RANGE (tx_sequence_number); + PRIMARY KEY(tx_sequence_number, event_sequence_number, checkpoint_sequence_number) +) PARTITION BY RANGE (checkpoint_sequence_number); CREATE TABLE events_partition_0 PARTITION OF events FOR VALUES FROM (0) TO (MAXVALUE); CREATE INDEX events_package ON events (package, tx_sequence_number, event_sequence_number); CREATE INDEX events_package_module ON events (package, module, tx_sequence_number, event_sequence_number); diff --git a/crates/sui-indexer/migrations/pg/2023-08-19-044026_transactions/down.sql b/crates/sui-indexer/migrations/pg/2023-08-19-044026_transactions/down.sql index 15e9dc9f1cb82..e5850457f922e 100644 --- a/crates/sui-indexer/migrations/pg/2023-08-19-044026_transactions/down.sql +++ b/crates/sui-indexer/migrations/pg/2023-08-19-044026_transactions/down.sql @@ -1,3 +1,2 @@ -- This file should undo anything in `up.sql` DROP TABLE IF EXISTS transactions; -DROP TABLE IF EXISTS transactions_partition_0; diff --git a/crates/sui-indexer/migrations/pg/2023-08-19-044026_transactions/up.sql b/crates/sui-indexer/migrations/pg/2023-08-19-044026_transactions/up.sql index f5404e3610751..ede66ad44798c 100644 --- a/crates/sui-indexer/migrations/pg/2023-08-19-044026_transactions/up.sql +++ b/crates/sui-indexer/migrations/pg/2023-08-19-044026_transactions/up.sql @@ -18,6 +18,10 @@ CREATE TABLE transactions ( -- number of successful commands in this transaction, bound by number of command -- in a programmaable transaction. success_command_count smallint NOT NULL, - PRIMARY KEY (tx_sequence_number) -) PARTITION BY RANGE (tx_sequence_number); + PRIMARY KEY (tx_sequence_number, checkpoint_sequence_number) +) PARTITION BY RANGE (checkpoint_sequence_number); CREATE TABLE transactions_partition_0 PARTITION OF transactions FOR VALUES FROM (0) TO (MAXVALUE); +CREATE INDEX transactions_transaction_digest ON transactions (transaction_digest); +CREATE INDEX transactions_checkpoint_sequence_number ON transactions (checkpoint_sequence_number); +-- only create index for system transactions (0). See types.rs +CREATE INDEX transactions_transaction_kind ON transactions (transaction_kind) WHERE transaction_kind = 0; diff --git a/crates/sui-indexer/migrations/pg/2023-08-19-044044_checkpoints/up.sql b/crates/sui-indexer/migrations/pg/2023-08-19-044044_checkpoints/up.sql index ddb63b020de70..5f7281a2e1a1d 100644 --- a/crates/sui-indexer/migrations/pg/2023-08-19-044044_checkpoints/up.sql +++ b/crates/sui-indexer/migrations/pg/2023-08-19-044044_checkpoints/up.sql @@ -1,15 +1,15 @@ CREATE TABLE checkpoints ( - sequence_number BIGINT PRIMARY KEY, - checkpoint_digest BYTEA NOT NULL, - epoch BIGINT NOT NULL, + sequence_number bigint PRIMARY KEY, + checkpoint_digest bytea NOT NULL, + epoch bigint NOT NULL, -- total transactions in the network at the end of this checkpoint (including itself) - network_total_transactions BIGINT NOT NULL, - previous_checkpoint_digest BYTEA, + network_total_transactions bigint NOT NULL, + previous_checkpoint_digest bytea, -- if this checkpoitn is the last checkpoint of an epoch end_of_epoch boolean NOT NULL, -- array of TranscationDigest in bytes included in this checkpoint - tx_digests BYTEA[] NOT NULL, + tx_digests bytea[] NOT NULL, timestamp_ms BIGINT NOT NULL, total_gas_cost BIGINT NOT NULL, computation_cost BIGINT NOT NULL, @@ -17,13 +17,11 @@ CREATE TABLE checkpoints storage_rebate BIGINT NOT NULL, non_refundable_storage_fee BIGINT NOT NULL, -- bcs serialized Vec bytes - checkpoint_commitments BYTEA NOT NULL, + checkpoint_commitments bytea NOT NULL, -- bcs serialized AggregateAuthoritySignature bytes - validator_signature BYTEA NOT NULL, + validator_signature bytea NOT NULL, -- bcs serialzied EndOfEpochData bytes, if the checkpoint marks end of an epoch - end_of_epoch_data BYTEA, - min_tx_sequence_number BIGINT, - max_tx_sequence_number BIGINT + end_of_epoch_data bytea ); CREATE INDEX checkpoints_epoch ON checkpoints (epoch, sequence_number); diff --git a/crates/sui-indexer/migrations/pg/2023-08-19-060729_packages/up.sql b/crates/sui-indexer/migrations/pg/2023-08-19-060729_packages/up.sql index f08a5549608eb..a95489af4dc41 100644 --- a/crates/sui-indexer/migrations/pg/2023-08-19-060729_packages/up.sql +++ b/crates/sui-indexer/migrations/pg/2023-08-19-060729_packages/up.sql @@ -1,14 +1,6 @@ -CREATE TABLE packages +CREATE TABLE packages ( - package_id bytea NOT NULL, - original_id bytea NOT NULL, - package_version bigint NOT NULL, + package_id bytea PRIMARY KEY, -- bcs serialized MovePackage - move_package bytea NOT NULL, - checkpoint_sequence_number bigint NOT NULL, - CONSTRAINT packages_pkey PRIMARY KEY (package_id, original_id, package_version), - CONSTRAINT packages_unique_package_id UNIQUE (package_id) + move_package bytea NOT NULL ); - -CREATE INDEX packages_cp_id_version ON packages (checkpoint_sequence_number, original_id, package_version); -CREATE INDEX packages_id_version_cp ON packages (original_id, package_version, checkpoint_sequence_number); diff --git a/crates/sui-indexer/migrations/pg/2023-10-06-204335_tx_indices/down.sql b/crates/sui-indexer/migrations/pg/2023-10-06-204335_tx_indices/down.sql index f5604c0db5357..8e4f29f981c22 100644 --- a/crates/sui-indexer/migrations/pg/2023-10-06-204335_tx_indices/down.sql +++ b/crates/sui-indexer/migrations/pg/2023-10-06-204335_tx_indices/down.sql @@ -2,8 +2,5 @@ DROP TABLE IF EXISTS tx_senders; DROP TABLE IF EXISTS tx_recipients; DROP TABLE IF EXISTS tx_input_objects; DROP TABLE IF EXISTS tx_changed_objects; -DROP TABLE IF EXISTS tx_calls_pkg; -DROP TABLE IF EXISTS tx_calls_mod; -DROP TABLE IF EXISTS tx_calls_fun; +DROP TABLE IF EXISTS tx_calls; DROP TABLE IF EXISTS tx_digests; -DROP TABLE IF EXISTS tx_kinds; diff --git a/crates/sui-indexer/migrations/pg/2023-10-06-204335_tx_indices/up.sql b/crates/sui-indexer/migrations/pg/2023-10-06-204335_tx_indices/up.sql index 0bcd824e31254..ed81a281f2b0a 100644 --- a/crates/sui-indexer/migrations/pg/2023-10-06-204335_tx_indices/up.sql +++ b/crates/sui-indexer/migrations/pg/2023-10-06-204335_tx_indices/up.sql @@ -1,70 +1,57 @@ CREATE TABLE tx_senders ( + cp_sequence_number BIGINT NOT NULL, tx_sequence_number BIGINT NOT NULL, + -- SuiAddress in bytes. sender BYTEA NOT NULL, - PRIMARY KEY(sender, tx_sequence_number) + PRIMARY KEY(sender, tx_sequence_number, cp_sequence_number) ); +CREATE INDEX tx_senders_tx_sequence_number_index ON tx_senders (tx_sequence_number, cp_sequence_number); CREATE TABLE tx_recipients ( + cp_sequence_number BIGINT NOT NULL, tx_sequence_number BIGINT NOT NULL, + -- SuiAddress in bytes. recipient BYTEA NOT NULL, - sender BYTEA NOT NULL, - PRIMARY KEY(recipient, tx_sequence_number) + PRIMARY KEY(recipient, tx_sequence_number, cp_sequence_number) ); -CREATE INDEX tx_recipients_sender ON tx_recipients (sender, recipient, tx_sequence_number); +CREATE INDEX tx_recipients_tx_sequence_number_index ON tx_recipients (tx_sequence_number, cp_sequence_number); CREATE TABLE tx_input_objects ( + cp_sequence_number BIGINT NOT NULL, tx_sequence_number BIGINT NOT NULL, + -- Object ID in bytes. object_id BYTEA NOT NULL, - sender BYTEA NOT NULL, - PRIMARY KEY(object_id, tx_sequence_number) + PRIMARY KEY(object_id, tx_sequence_number, cp_sequence_number) ); CREATE INDEX tx_input_objects_tx_sequence_number_index ON tx_input_objects (tx_sequence_number); -CREATE INDEX tx_input_objects_sender ON tx_input_objects (sender, object_id, tx_sequence_number); CREATE TABLE tx_changed_objects ( + cp_sequence_number BIGINT NOT NULL, tx_sequence_number BIGINT NOT NULL, + -- Object Id in bytes. object_id BYTEA NOT NULL, - sender BYTEA NOT NULL, - PRIMARY KEY(object_id, tx_sequence_number) + PRIMARY KEY(object_id, tx_sequence_number, cp_sequence_number) ); CREATE INDEX tx_changed_objects_tx_sequence_number_index ON tx_changed_objects (tx_sequence_number); -CREATE INDEX tx_changed_objects_sender ON tx_changed_objects (sender, object_id, tx_sequence_number); - -CREATE TABLE tx_calls_pkg ( - tx_sequence_number BIGINT NOT NULL, - package BYTEA NOT NULL, - sender BYTEA NOT NULL, - PRIMARY KEY(package, tx_sequence_number) -); -CREATE INDEX tx_calls_pkg_sender ON tx_calls_pkg (sender, package, tx_sequence_number); - -CREATE TABLE tx_calls_mod ( - tx_sequence_number BIGINT NOT NULL, - package BYTEA NOT NULL, - module TEXT NOT NULL, - sender BYTEA NOT NULL, - PRIMARY KEY(package, module, tx_sequence_number) -); -CREATE INDEX tx_calls_mod_sender ON tx_calls_mod (sender, package, module, tx_sequence_number); -CREATE TABLE tx_calls_fun ( +CREATE TABLE tx_calls ( + cp_sequence_number BIGINT NOT NULL, tx_sequence_number BIGINT NOT NULL, package BYTEA NOT NULL, module TEXT NOT NULL, func TEXT NOT NULL, - sender BYTEA NOT NULL, - PRIMARY KEY(package, module, func, tx_sequence_number) + -- 1. Using Primary Key as a unique index. + -- 2. Diesel does not like tables with no primary key. + PRIMARY KEY(package, tx_sequence_number, cp_sequence_number) ); -CREATE INDEX tx_calls_fun_sender ON tx_calls_fun (sender, package, module, func, tx_sequence_number); +CREATE INDEX tx_calls_module ON tx_calls (package, module, tx_sequence_number, cp_sequence_number); +CREATE INDEX tx_calls_func ON tx_calls (package, module, func, tx_sequence_number, cp_sequence_number); +CREATE INDEX tx_calls_tx_sequence_number ON tx_calls (tx_sequence_number, cp_sequence_number); +-- un-partitioned table for tx_digest -> (cp_sequence_number, tx_sequence_number) lookup. CREATE TABLE tx_digests ( tx_digest BYTEA PRIMARY KEY, + cp_sequence_number BIGINT NOT NULL, tx_sequence_number BIGINT NOT NULL ); CREATE INDEX tx_digests_tx_sequence_number ON tx_digests (tx_sequence_number); - -CREATE TABLE tx_kinds ( - tx_sequence_number BIGINT NOT NULL, - tx_kind SMALLINT NOT NULL, - PRIMARY KEY(tx_kind, tx_sequence_number) -); diff --git a/crates/sui-indexer/migrations/pg/2023-11-29-193859_advance_partition/up.sql b/crates/sui-indexer/migrations/pg/2023-11-29-193859_advance_partition/up.sql index 8ca64b86a7081..cb24af8e09934 100644 --- a/crates/sui-indexer/migrations/pg/2023-11-29-193859_advance_partition/up.sql +++ b/crates/sui-indexer/migrations/pg/2023-11-29-193859_advance_partition/up.sql @@ -1,10 +1,10 @@ -CREATE OR REPLACE PROCEDURE advance_partition(table_name TEXT, last_epoch BIGINT, new_epoch BIGINT, last_epoch_start BIGINT, new_epoch_start BIGINT) +CREATE OR REPLACE PROCEDURE advance_partition(table_name TEXT, last_epoch BIGINT, new_epoch BIGINT, last_epoch_start_cp BIGINT, new_epoch_start_cp BIGINT) LANGUAGE plpgsql AS $$ BEGIN EXECUTE format('ALTER TABLE %I DETACH PARTITION %I_partition_%s', table_name, table_name, last_epoch); - EXECUTE format('ALTER TABLE %I ATTACH PARTITION %I_partition_%s FOR VALUES FROM (%L) TO (%L)', table_name, table_name, last_epoch, last_epoch_start, new_epoch_start); - EXECUTE format('CREATE TABLE IF NOT EXISTS %I_partition_%s PARTITION OF %I FOR VALUES FROM (%L) TO (MAXVALUE)', table_name, new_epoch, table_name, new_epoch_start); + EXECUTE format('ALTER TABLE %I ATTACH PARTITION %I_partition_%s FOR VALUES FROM (%L) TO (%L)', table_name, table_name, last_epoch, last_epoch_start_cp, new_epoch_start_cp); + EXECUTE format('CREATE TABLE IF NOT EXISTS %I_partition_%s PARTITION OF %I FOR VALUES FROM (%L) TO (MAXVALUE)', table_name, new_epoch, table_name, new_epoch_start_cp); END; $$; diff --git a/crates/sui-indexer/migrations/pg/2024-05-05-155158_obj_indices/down.sql b/crates/sui-indexer/migrations/pg/2024-05-05-155158_obj_indices/down.sql deleted file mode 100644 index 7a3a7670f24c2..0000000000000 --- a/crates/sui-indexer/migrations/pg/2024-05-05-155158_obj_indices/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE IF EXISTS objects_version; diff --git a/crates/sui-indexer/migrations/pg/2024-05-05-155158_obj_indices/up.sql b/crates/sui-indexer/migrations/pg/2024-05-05-155158_obj_indices/up.sql deleted file mode 100644 index 666e5a2423319..0000000000000 --- a/crates/sui-indexer/migrations/pg/2024-05-05-155158_obj_indices/up.sql +++ /dev/null @@ -1,31 +0,0 @@ --- Indexing table mapping an object's ID and version to its checkpoint --- sequence number, partitioned by the first byte of its Object ID. -CREATE TABLE objects_version ( - object_id bytea NOT NULL, - object_version bigint NOT NULL, - cp_sequence_number bigint NOT NULL, - PRIMARY KEY (object_id, object_version) -) PARTITION BY RANGE (object_id); - --- Create a partition for each first byte value. -DO $$ -DECLARE - lo text; - hi text; -BEGIN - FOR i IN 0..254 LOOP - lo := LPAD(TO_HEX(i), 2, '0'); - hi := LPAD(TO_HEX(i + 1), 2, '0'); - EXECUTE FORMAT($F$ - CREATE TABLE objects_version_%1$s PARTITION OF objects_version FOR VALUES - FROM (E'\\x%1$s00000000000000000000000000000000000000000000000000000000000000') - TO (E'\\x%2$s00000000000000000000000000000000000000000000000000000000000000'); - $F$, lo, hi); - END LOOP; -END; -$$ LANGUAGE plpgsql; - --- Special case for the last partition, because of the upper bound. -CREATE TABLE objects_version_ff PARTITION OF objects_version FOR VALUES -FROM (E'\\xff00000000000000000000000000000000000000000000000000000000000000') -TO (MAXVALUE); diff --git a/crates/sui-indexer/migrations/pg/2024-06-14-045801_event_indices/down.sql b/crates/sui-indexer/migrations/pg/2024-06-14-045801_event_indices/down.sql deleted file mode 100644 index 30a964a320f8f..0000000000000 --- a/crates/sui-indexer/migrations/pg/2024-06-14-045801_event_indices/down.sql +++ /dev/null @@ -1,7 +0,0 @@ -DROP TABLE IF EXISTS event_emit_package; -DROP TABLE IF EXISTS event_emit_module; -DROP TABLE IF EXISTS event_struct_package; -DROP TABLE IF EXISTS event_struct_module; -DROP TABLE IF EXISTS event_struct_name; -DROP TABLE IF EXISTS event_struct_type; -DROP TABLE IF EXISTS event_senders; \ No newline at end of file diff --git a/crates/sui-indexer/migrations/pg/2024-06-14-045801_event_indices/up.sql b/crates/sui-indexer/migrations/pg/2024-06-14-045801_event_indices/up.sql deleted file mode 100644 index a89625146a9fd..0000000000000 --- a/crates/sui-indexer/migrations/pg/2024-06-14-045801_event_indices/up.sql +++ /dev/null @@ -1,74 +0,0 @@ -CREATE TABLE event_emit_package -( - package BYTEA NOT NULL, - tx_sequence_number BIGINT NOT NULL, - event_sequence_number BIGINT NOT NULL, - sender BYTEA NOT NULL, - PRIMARY KEY(package, tx_sequence_number, event_sequence_number) -); -CREATE INDEX event_emit_package_sender ON event_emit_package (sender, package, tx_sequence_number, event_sequence_number); - -CREATE TABLE event_emit_module -( - package BYTEA NOT NULL, - module TEXT NOT NULL, - tx_sequence_number BIGINT NOT NULL, - event_sequence_number BIGINT NOT NULL, - sender BYTEA NOT NULL, - PRIMARY KEY(package, module, tx_sequence_number, event_sequence_number) -); -CREATE INDEX event_emit_module_sender ON event_emit_module (sender, package, module, tx_sequence_number, event_sequence_number); - -CREATE TABLE event_struct_package -( - package BYTEA NOT NULL, - tx_sequence_number BIGINT NOT NULL, - event_sequence_number BIGINT NOT NULL, - sender BYTEA NOT NULL, - PRIMARY KEY(package, tx_sequence_number, event_sequence_number) -); -CREATE INDEX event_struct_package_sender ON event_struct_package (sender, package, tx_sequence_number, event_sequence_number); - - -CREATE TABLE event_struct_module -( - package BYTEA NOT NULL, - module TEXT NOT NULL, - tx_sequence_number BIGINT NOT NULL, - event_sequence_number BIGINT NOT NULL, - sender BYTEA NOT NULL, - PRIMARY KEY(package, module, tx_sequence_number, event_sequence_number) -); -CREATE INDEX event_struct_module_sender ON event_struct_module (sender, package, module, tx_sequence_number, event_sequence_number); - -CREATE TABLE event_struct_name -( - package BYTEA NOT NULL, - module TEXT NOT NULL, - type_name TEXT NOT NULL, - tx_sequence_number BIGINT NOT NULL, - event_sequence_number BIGINT NOT NULL, - sender BYTEA NOT NULL, - PRIMARY KEY(package, module, type_name, tx_sequence_number, event_sequence_number) -); -CREATE INDEX event_struct_name_sender ON event_struct_name (sender, package, module, type_name, tx_sequence_number, event_sequence_number); - -CREATE TABLE event_struct_instantiation -( - package BYTEA NOT NULL, - module TEXT NOT NULL, - type_instantiation TEXT NOT NULL, - tx_sequence_number BIGINT NOT NULL, - event_sequence_number BIGINT NOT NULL, - sender BYTEA NOT NULL, - PRIMARY KEY(package, module, type_instantiation, tx_sequence_number, event_sequence_number) -); -CREATE INDEX event_struct_instantiation_sender ON event_struct_instantiation (sender, package, module, type_instantiation, tx_sequence_number, event_sequence_number); - -CREATE TABLE event_senders -( - sender BYTEA NOT NULL, - tx_sequence_number BIGINT NOT NULL, - event_sequence_number BIGINT NOT NULL, - PRIMARY KEY(sender, tx_sequence_number, event_sequence_number) -); diff --git a/crates/sui-indexer/src/handlers/checkpoint_handler.rs b/crates/sui-indexer/src/handlers/checkpoint_handler.rs index 7a8f8efe67fa9..b3897f2fe8935 100644 --- a/crates/sui-indexer/src/handlers/checkpoint_handler.rs +++ b/crates/sui-indexer/src/handlers/checkpoint_handler.rs @@ -49,8 +49,8 @@ use crate::db::ConnectionPool; use crate::store::package_resolver::{IndexerStorePackageResolver, InterimPackageResolver}; use crate::store::{IndexerStore, PgIndexerStore}; use crate::types::{ - EventIndex, IndexedCheckpoint, IndexedDeletedObject, IndexedEpochInfo, IndexedEvent, - IndexedObject, IndexedPackage, IndexedTransaction, IndexerResult, TransactionKind, TxIndex, + IndexedCheckpoint, IndexedDeletedObject, IndexedEpochInfo, IndexedEvent, IndexedObject, + IndexedPackage, IndexedTransaction, IndexerResult, TransactionKind, TxIndex, }; use super::tx_processor::EpochEndIndexingObjectStore; @@ -215,7 +215,6 @@ where 0, //first_checkpoint_id None, ), - network_total_transactions: 0, })); } @@ -242,12 +241,7 @@ where let event = bcs::from_bytes::(&epoch_event.contents)?; // Now we just entered epoch X, we want to calculate the diff between - // TotalTransactionsByEndOfEpoch(X-1) and TotalTransactionsByEndOfEpoch(X-2). Note that on - // the indexer's chain-reading side, this is not guaranteed to have the latest data. Rather - // than impose a wait on the reading side, however, we overwrite this on the persisting - // side, where we can guarantee that the previous epoch's checkpoints have been written to - // db. - + // TotalTransactionsByEndOfEpoch(X-1) and TotalTransactionsByEndOfEpoch(X-2) let network_tx_count_prev_epoch = match system_state.epoch { // If first epoch change, this number is 0 1 => Ok(0), @@ -271,7 +265,6 @@ where checkpoint_summary.sequence_number + 1, // first_checkpoint_id Some(&event), ), - network_total_transactions: checkpoint_summary.network_total_transactions, })) } @@ -294,21 +287,20 @@ where let object_history_changes: TransactionObjectChangesToCommit = Self::index_objects_history(data.clone(), package_resolver.clone()).await?; - let (checkpoint, db_transactions, db_events, db_tx_indices, db_event_indices, db_displays) = { + let (checkpoint, db_transactions, db_events, db_indices, db_displays) = { let CheckpointData { transactions, checkpoint_summary, checkpoint_contents, } = data; - let (db_transactions, db_events, db_tx_indices, db_event_indices, db_displays) = - Self::index_transactions( - transactions, - &checkpoint_summary, - &checkpoint_contents, - &metrics, - ) - .await?; + let (db_transactions, db_events, db_indices, db_displays) = Self::index_transactions( + transactions, + &checkpoint_summary, + &checkpoint_contents, + &metrics, + ) + .await?; let successful_tx_num: u64 = db_transactions.iter().map(|t| t.successful_tx_num).sum(); ( @@ -319,8 +311,7 @@ where ), db_transactions, db_events, - db_tx_indices, - db_event_indices, + db_indices, db_displays, ) }; @@ -343,8 +334,7 @@ where checkpoint, transactions: db_transactions, events: db_events, - tx_indices: db_tx_indices, - event_indices: db_event_indices, + tx_indices: db_indices, display_updates: db_displays, object_changes, object_history_changes, @@ -362,7 +352,6 @@ where Vec, Vec, Vec, - Vec, BTreeMap, )> { let checkpoint_seq = checkpoint_summary.sequence_number(); @@ -383,8 +372,7 @@ where let mut db_transactions = Vec::new(); let mut db_events = Vec::new(); let mut db_displays = BTreeMap::new(); - let mut db_tx_indices = Vec::new(); - let mut db_event_indices = Vec::new(); + let mut db_indices = Vec::new(); for tx in transactions { let CheckpointTransaction { @@ -402,7 +390,6 @@ where checkpoint_seq, tx_digest, sender_signed_data.digest() ))); } - let tx = sender_signed_data.transaction_data(); let events = events .as_ref() @@ -426,12 +413,6 @@ where ) })); - db_event_indices.extend( - events.iter().enumerate().map(|(idx, event)| { - EventIndex::from_event(tx_sequence_number, idx as u64, event) - }), - ); - db_displays.extend( events .iter() @@ -459,7 +440,7 @@ where object_changes, balance_change, events, - transaction_kind: transaction_kind.clone(), + transaction_kind, successful_tx_num: if fx.status().is_ok() { tx.kind().tx_count() as u64 } else { @@ -487,8 +468,8 @@ where // Payers let payers = vec![tx.gas_owner()]; - // Sender - let sender = tx.sender(); + // Senders + let senders = vec![tx.sender()]; // Recipients let recipients = fx @@ -508,26 +489,19 @@ where .map(|(p, m, f)| (*<&ObjectID>::clone(p), m.to_string(), f.to_string())) .collect(); - db_tx_indices.push(TxIndex { + db_indices.push(TxIndex { tx_sequence_number, transaction_digest: tx_digest, checkpoint_sequence_number: *checkpoint_seq, input_objects, changed_objects, - sender, + senders, payers, recipients, move_calls, - tx_kind: transaction_kind, }); } - Ok(( - db_transactions, - db_events, - db_tx_indices, - db_event_indices, - db_displays, - )) + Ok((db_transactions, db_events, db_indices, db_displays)) } pub(crate) async fn index_objects( diff --git a/crates/sui-indexer/src/handlers/committer.rs b/crates/sui-indexer/src/handlers/committer.rs index 58d6fe94e1f7c..eb1dc7365f2b8 100644 --- a/crates/sui-indexer/src/handlers/committer.rs +++ b/crates/sui-indexer/src/handlers/committer.rs @@ -89,7 +89,6 @@ async fn commit_checkpoints( let mut tx_batch = vec![]; let mut events_batch = vec![]; let mut tx_indices_batch = vec![]; - let mut event_indices_batch = vec![]; let mut display_updates_batch = BTreeMap::new(); let mut object_changes_batch = vec![]; let mut object_history_changes_batch = vec![]; @@ -100,7 +99,6 @@ async fn commit_checkpoints( checkpoint, transactions, events, - event_indices, tx_indices, display_updates, object_changes, @@ -112,7 +110,6 @@ async fn commit_checkpoints( tx_batch.push(transactions); events_batch.push(events); tx_indices_batch.push(tx_indices); - event_indices_batch.push(event_indices); display_updates_batch.extend(display_updates.into_iter()); object_changes_batch.push(object_changes); object_history_changes_batch.push(object_history_changes); @@ -126,10 +123,6 @@ async fn commit_checkpoints( let tx_batch = tx_batch.into_iter().flatten().collect::>(); let tx_indices_batch = tx_indices_batch.into_iter().flatten().collect::>(); let events_batch = events_batch.into_iter().flatten().collect::>(); - let event_indices_batch = event_indices_batch - .into_iter() - .flatten() - .collect::>(); let packages_batch = packages_batch.into_iter().flatten().collect::>(); let checkpoint_num = checkpoint_batch.len(); let tx_count = tx_batch.len(); @@ -140,7 +133,6 @@ async fn commit_checkpoints( state.persist_transactions(tx_batch), state.persist_tx_indices(tx_indices_batch), state.persist_events(events_batch), - state.persist_event_indices(event_indices_batch), state.persist_displays(display_updates_batch), state.persist_packages(packages_batch), state.persist_objects(object_changes_batch.clone()), diff --git a/crates/sui-indexer/src/handlers/mod.rs b/crates/sui-indexer/src/handlers/mod.rs index 2a6578fc18295..ca27e92a0bf41 100644 --- a/crates/sui-indexer/src/handlers/mod.rs +++ b/crates/sui-indexer/src/handlers/mod.rs @@ -6,8 +6,8 @@ use std::collections::BTreeMap; use crate::{ models::display::StoredDisplay, types::{ - EventIndex, IndexedCheckpoint, IndexedDeletedObject, IndexedEpochInfo, IndexedEvent, - IndexedObject, IndexedPackage, IndexedTransaction, TxIndex, + IndexedCheckpoint, IndexedDeletedObject, IndexedEpochInfo, IndexedEvent, IndexedObject, + IndexedPackage, IndexedTransaction, TxIndex, }, }; @@ -22,7 +22,6 @@ pub struct CheckpointDataToCommit { pub checkpoint: IndexedCheckpoint, pub transactions: Vec, pub events: Vec, - pub event_indices: Vec, pub tx_indices: Vec, pub display_updates: BTreeMap, pub object_changes: TransactionObjectChangesToCommit, @@ -41,5 +40,4 @@ pub struct TransactionObjectChangesToCommit { pub struct EpochToCommit { pub last_epoch: Option, pub new_epoch: IndexedEpochInfo, - pub network_total_transactions: u64, } diff --git a/crates/sui-indexer/src/indexer_reader.rs b/crates/sui-indexer/src/indexer_reader.rs index f05098a764185..00408945140bd 100644 --- a/crates/sui-indexer/src/indexer_reader.rs +++ b/crates/sui-indexer/src/indexer_reader.rs @@ -777,14 +777,14 @@ impl IndexerReader { let package = Hex::encode(package.to_vec()); match (module, function) { (Some(module), Some(function)) => ( - "tx_calls_fun".into(), + "tx_calls".into(), format!( "package = '\\x{}'::bytea AND module = '{}' AND func = '{}'", package, module, function ), ), (Some(module), None) => ( - "tx_calls_mod".into(), + "tx_calls".into(), format!( "package = '\\x{}'::bytea AND module = '{}'", package, module @@ -792,11 +792,11 @@ impl IndexerReader { ), (None, Some(_)) => { return Err(IndexerError::InvalidArgumentError( - "Function cannot be present without Module.".into(), + "Function cannot be present wihtout Module.".into(), )); } (None, None) => ( - "tx_calls_pkg".into(), + "tx_calls".into(), format!("package = '\\x{}'::bytea", package), ), } diff --git a/crates/sui-indexer/src/metrics.rs b/crates/sui-indexer/src/metrics.rs index 34978836db5d2..36d788f5fd580 100644 --- a/crates/sui-indexer/src/metrics.rs +++ b/crates/sui-indexer/src/metrics.rs @@ -139,8 +139,6 @@ pub struct IndexerMetrics { pub checkpoint_db_commit_latency_objects_history_chunks: Histogram, pub checkpoint_db_commit_latency_events: Histogram, pub checkpoint_db_commit_latency_events_chunks: Histogram, - pub checkpoint_db_commit_latency_event_indices: Histogram, - pub checkpoint_db_commit_latency_event_indices_chunks: Histogram, pub checkpoint_db_commit_latency_packages: Histogram, pub checkpoint_db_commit_latency_tx_indices: Histogram, pub checkpoint_db_commit_latency_tx_indices_chunks: Histogram, @@ -496,20 +494,6 @@ impl IndexerMetrics { registry, ) .unwrap(), - checkpoint_db_commit_latency_event_indices: register_histogram_with_registry!( - "checkpoint_db_commit_latency_event_indices", - "Time spent commiting event indices", - DATA_INGESTION_LATENCY_SEC_BUCKETS.to_vec(), - registry, - ) - .unwrap(), - checkpoint_db_commit_latency_event_indices_chunks: register_histogram_with_registry!( - "checkpoint_db_commit_latency_event_indices_chunks", - "Time spent commiting event indices chunks", - DATA_INGESTION_LATENCY_SEC_BUCKETS.to_vec(), - registry, - ) - .unwrap(), checkpoint_db_commit_latency_packages: register_histogram_with_registry!( "checkpoint_db_commit_latency_packages", "Time spent commiting packages", diff --git a/crates/sui-indexer/src/models/checkpoints.rs b/crates/sui-indexer/src/models/checkpoints.rs index 69e2618c82297..260fcfb5944f2 100644 --- a/crates/sui-indexer/src/models/checkpoints.rs +++ b/crates/sui-indexer/src/models/checkpoints.rs @@ -42,8 +42,6 @@ pub struct StoredCheckpoint { pub checkpoint_commitments: Vec, pub validator_signature: Vec, pub end_of_epoch_data: Option>, - pub min_tx_sequence_number: Option, - pub max_tx_sequence_number: Option, } impl From<&IndexedCheckpoint> for StoredCheckpoint { @@ -85,8 +83,6 @@ impl From<&IndexedCheckpoint> for StoredCheckpoint { .as_ref() .map(|d| bcs::to_bytes(d).unwrap()), end_of_epoch: c.end_of_epoch_data.is_some(), - min_tx_sequence_number: Some(c.min_tx_sequence_number as i64), - max_tx_sequence_number: Some(c.max_tx_sequence_number as i64), } } } diff --git a/crates/sui-indexer/src/models/event_indices.rs b/crates/sui-indexer/src/models/event_indices.rs deleted file mode 100644 index 08f17cce339d5..0000000000000 --- a/crates/sui-indexer/src/models/event_indices.rs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{ - schema::{ - event_emit_module, event_emit_package, event_senders, event_struct_instantiation, - event_struct_module, event_struct_name, event_struct_package, - }, - types::EventIndex, -}; -use diesel::prelude::*; - -#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] -#[diesel(table_name = event_emit_package)] -pub struct StoredEventEmitPackage { - pub tx_sequence_number: i64, - pub event_sequence_number: i64, - pub package: Vec, - pub sender: Vec, -} - -#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] -#[diesel(table_name = event_emit_module)] -pub struct StoredEventEmitModule { - pub tx_sequence_number: i64, - pub event_sequence_number: i64, - pub package: Vec, - pub module: String, - pub sender: Vec, -} - -#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] -#[diesel(table_name = event_senders)] -pub struct StoredEventSenders { - pub tx_sequence_number: i64, - pub event_sequence_number: i64, - pub sender: Vec, -} - -#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] -#[diesel(table_name = event_struct_package)] -pub struct StoredEventStructPackage { - pub tx_sequence_number: i64, - pub event_sequence_number: i64, - pub package: Vec, - pub sender: Vec, -} - -#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] -#[diesel(table_name = event_struct_module)] -pub struct StoredEventStructModule { - pub tx_sequence_number: i64, - pub event_sequence_number: i64, - pub package: Vec, - pub module: String, - pub sender: Vec, -} - -#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] -#[diesel(table_name = event_struct_name)] -pub struct StoredEventStructName { - pub tx_sequence_number: i64, - pub event_sequence_number: i64, - pub package: Vec, - pub module: String, - pub type_name: String, - pub sender: Vec, -} - -#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] -#[diesel(table_name = event_struct_instantiation)] -pub struct StoredEventStructInstantiation { - pub tx_sequence_number: i64, - pub event_sequence_number: i64, - pub package: Vec, - pub module: String, - pub type_instantiation: String, - pub sender: Vec, -} - -impl EventIndex { - pub fn split( - self: EventIndex, - ) -> ( - StoredEventEmitPackage, - StoredEventEmitModule, - StoredEventSenders, - StoredEventStructPackage, - StoredEventStructModule, - StoredEventStructName, - StoredEventStructInstantiation, - ) { - let tx_sequence_number = self.tx_sequence_number as i64; - let event_sequence_number = self.event_sequence_number as i64; - ( - StoredEventEmitPackage { - tx_sequence_number, - event_sequence_number, - package: self.emit_package.to_vec(), - sender: self.sender.to_vec(), - }, - StoredEventEmitModule { - tx_sequence_number, - event_sequence_number, - package: self.emit_package.to_vec(), - module: self.emit_module.clone(), - sender: self.sender.to_vec(), - }, - StoredEventSenders { - tx_sequence_number, - event_sequence_number, - sender: self.sender.to_vec(), - }, - StoredEventStructPackage { - tx_sequence_number, - event_sequence_number, - package: self.type_package.to_vec(), - sender: self.sender.to_vec(), - }, - StoredEventStructModule { - tx_sequence_number, - event_sequence_number, - package: self.type_package.to_vec(), - module: self.type_module.clone(), - sender: self.sender.to_vec(), - }, - StoredEventStructName { - tx_sequence_number, - event_sequence_number, - package: self.type_package.to_vec(), - module: self.type_module.clone(), - type_name: self.type_name.clone(), - sender: self.sender.to_vec(), - }, - StoredEventStructInstantiation { - tx_sequence_number, - event_sequence_number, - package: self.type_package.to_vec(), - module: self.type_module.clone(), - type_instantiation: self.type_instantiation.clone(), - sender: self.sender.to_vec(), - }, - ) - } -} diff --git a/crates/sui-indexer/src/models/mod.rs b/crates/sui-indexer/src/models/mod.rs index b677e09f1aaad..3b8233ec45021 100644 --- a/crates/sui-indexer/src/models/mod.rs +++ b/crates/sui-indexer/src/models/mod.rs @@ -4,9 +4,7 @@ pub mod checkpoints; pub mod display; pub mod epoch; -pub mod event_indices; pub mod events; -pub mod obj_indices; pub mod objects; pub mod packages; pub mod transactions; diff --git a/crates/sui-indexer/src/models/obj_indices.rs b/crates/sui-indexer/src/models/obj_indices.rs deleted file mode 100644 index 7e5298008834c..0000000000000 --- a/crates/sui-indexer/src/models/obj_indices.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use diesel::prelude::*; - -use crate::schema::objects_version; - -use super::objects::StoredDeletedObject; -use super::objects::StoredObject; - -/// Model types related to tables that support efficient execution of queries on the `objects`, -/// `objects_history` and `objects_snapshot` tables. - -#[derive(Queryable, Insertable, Debug, Identifiable, Clone, QueryableByName)] -#[diesel(table_name = objects_version, primary_key(object_id, object_version))] -pub struct StoredObjectVersion { - pub object_id: Vec, - pub object_version: i64, - pub cp_sequence_number: i64, -} - -impl From<&StoredObject> for StoredObjectVersion { - fn from(o: &StoredObject) -> Self { - Self { - object_id: o.object_id.clone(), - object_version: o.object_version, - cp_sequence_number: o.checkpoint_sequence_number, - } - } -} - -impl From<&StoredDeletedObject> for StoredObjectVersion { - fn from(o: &StoredDeletedObject) -> Self { - Self { - object_id: o.object_id.clone(), - object_version: o.object_version, - cp_sequence_number: o.checkpoint_sequence_number, - } - } -} diff --git a/crates/sui-indexer/src/models/packages.rs b/crates/sui-indexer/src/models/packages.rs index 97c8e8fc5b459..63f61f01fc428 100644 --- a/crates/sui-indexer/src/models/packages.rs +++ b/crates/sui-indexer/src/models/packages.rs @@ -10,20 +10,14 @@ use diesel::prelude::*; #[diesel(table_name = packages, primary_key(package_id))] pub struct StoredPackage { pub package_id: Vec, - pub original_id: Vec, - pub package_version: i64, pub move_package: Vec, - pub checkpoint_sequence_number: i64, } impl From for StoredPackage { fn from(p: IndexedPackage) -> Self { Self { package_id: p.package_id.to_vec(), - original_id: p.move_package.original_package_id().to_vec(), - package_version: p.move_package.version().value() as i64, move_package: bcs::to_bytes(&p.move_package).unwrap(), - checkpoint_sequence_number: p.checkpoint_sequence_number as i64, } } } diff --git a/crates/sui-indexer/src/models/tx_indices.rs b/crates/sui-indexer/src/models/tx_indices.rs index 4f942c2e7af0b..86c4ae4c73819 100644 --- a/crates/sui-indexer/src/models/tx_indices.rs +++ b/crates/sui-indexer/src/models/tx_indices.rs @@ -3,8 +3,7 @@ use crate::{ schema::{ - tx_calls_fun, tx_calls_mod, tx_calls_pkg, tx_changed_objects, tx_digests, tx_input_objects, - tx_kinds, tx_recipients, tx_senders, + tx_calls, tx_changed_objects, tx_digests, tx_input_objects, tx_recipients, tx_senders, }, types::TxIndex, }; @@ -25,6 +24,7 @@ pub struct TxDigest { #[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] #[diesel(table_name = tx_senders)] pub struct StoredTxSenders { + pub cp_sequence_number: i64, pub tx_sequence_number: i64, pub sender: Vec, } @@ -32,52 +32,35 @@ pub struct StoredTxSenders { #[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] #[diesel(table_name = tx_recipients)] pub struct StoredTxRecipients { + pub cp_sequence_number: i64, 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 { + pub cp_sequence_number: i64, pub tx_sequence_number: i64, pub object_id: Vec, - pub sender: Vec, } #[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] #[diesel(table_name = tx_changed_objects)] pub struct StoredTxChangedObject { + pub cp_sequence_number: i64, pub tx_sequence_number: i64, pub object_id: Vec, - pub sender: Vec, -} - -#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] -#[diesel(table_name = tx_calls_pkg)] -pub struct StoredTxPkg { - pub tx_sequence_number: i64, - pub package: Vec, - pub sender: Vec, -} - -#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] -#[diesel(table_name = tx_calls_mod)] -pub struct StoredTxMod { - pub tx_sequence_number: i64, - pub package: Vec, - pub module: String, - pub sender: Vec, } #[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] -#[diesel(table_name = tx_calls_fun)] -pub struct StoredTxFun { +#[diesel(table_name = tx_calls)] +pub struct StoredTxCalls { + pub cp_sequence_number: i64, pub tx_sequence_number: i64, pub package: Vec, pub module: String, pub func: String, - pub sender: Vec, } #[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] @@ -85,13 +68,7 @@ pub struct StoredTxFun { pub struct StoredTxDigest { pub tx_digest: Vec, pub tx_sequence_number: i64, -} - -#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] -#[diesel(table_name = tx_kinds)] -pub struct StoredTxKind { - pub tx_kind: i16, - pub tx_sequence_number: i64, + pub cp_sequence_number: i64, } #[allow(clippy::type_complexity)] @@ -103,109 +80,71 @@ impl TxIndex { Vec, Vec, Vec, - Vec, - Vec, - Vec, + Vec, Vec, - Vec, ) { let tx_sequence_number = self.tx_sequence_number as i64; - let tx_sender = StoredTxSenders { - tx_sequence_number, - sender: self.sender.to_vec(), - }; + let cp_sequence_number = self.checkpoint_sequence_number as i64; + let tx_senders = self + .senders + .iter() + .map(|s| StoredTxSenders { + cp_sequence_number, + tx_sequence_number, + sender: s.to_vec(), + }) + .collect(); let tx_recipients = self .recipients .iter() .map(|s| StoredTxRecipients { + cp_sequence_number, tx_sequence_number, recipient: s.to_vec(), - sender: self.sender.to_vec(), }) .collect(); let tx_input_objects = self .input_objects .iter() .map(|o| StoredTxInputObject { + cp_sequence_number, tx_sequence_number, object_id: bcs::to_bytes(&o).unwrap(), - sender: self.sender.to_vec(), }) .collect(); let tx_changed_objects = self .changed_objects .iter() .map(|o| StoredTxChangedObject { + cp_sequence_number, tx_sequence_number, object_id: bcs::to_bytes(&o).unwrap(), - sender: self.sender.to_vec(), }) .collect(); - - let mut packages = Vec::new(); - let mut packages_modules = Vec::new(); - let mut packages_modules_funcs = Vec::new(); - - for (pkg, pkg_mod, pkg_mod_func) in self + let tx_calls = self .move_calls .iter() - .map(|(p, m, f)| (*p, (*p, m.clone()), (*p, m.clone(), f.clone()))) - { - packages.push(pkg); - packages_modules.push(pkg_mod); - packages_modules_funcs.push(pkg_mod_func); - } - - let tx_pkgs = packages - .iter() - .map(|p| StoredTxPkg { - tx_sequence_number, - package: p.to_vec(), - sender: self.sender.to_vec(), - }) - .collect(); - - let tx_mods = packages_modules - .iter() - .map(|(p, m)| StoredTxMod { - tx_sequence_number, - package: p.to_vec(), - module: m.to_string(), - sender: self.sender.to_vec(), - }) - .collect(); - - let tx_calls = packages_modules_funcs - .iter() - .map(|(p, m, f)| StoredTxFun { + .map(|(p, m, f)| StoredTxCalls { + cp_sequence_number, tx_sequence_number, package: p.to_vec(), module: m.to_string(), func: f.to_string(), - sender: self.sender.to_vec(), }) .collect(); - let stored_tx_digest = StoredTxDigest { tx_digest: self.transaction_digest.into_inner().to_vec(), tx_sequence_number, - }; - - let tx_kind = StoredTxKind { - tx_kind: self.tx_kind as i16, - tx_sequence_number, + cp_sequence_number, }; ( - vec![tx_sender], + tx_senders, tx_recipients, tx_input_objects, tx_changed_objects, - tx_pkgs, - tx_mods, tx_calls, vec![stored_tx_digest], - vec![tx_kind], ) } } diff --git a/crates/sui-indexer/src/schema/mod.rs b/crates/sui-indexer/src/schema/mod.rs index 8f98297ac4d80..d1d408d76a307 100644 --- a/crates/sui-indexer/src/schema/mod.rs +++ b/crates/sui-indexer/src/schema/mod.rs @@ -16,28 +16,17 @@ mod inner { pub use crate::schema::pg::checkpoints; pub use crate::schema::pg::display; pub use crate::schema::pg::epochs; - pub use crate::schema::pg::event_emit_module; - pub use crate::schema::pg::event_emit_package; - pub use crate::schema::pg::event_senders; - pub use crate::schema::pg::event_struct_instantiation; - pub use crate::schema::pg::event_struct_module; - pub use crate::schema::pg::event_struct_name; - pub use crate::schema::pg::event_struct_package; pub use crate::schema::pg::events; pub use crate::schema::pg::objects; pub use crate::schema::pg::objects_history; pub use crate::schema::pg::objects_snapshot; - pub use crate::schema::pg::objects_version; pub use crate::schema::pg::packages; pub use crate::schema::pg::pruner_cp_watermark; pub use crate::schema::pg::transactions; - pub use crate::schema::pg::tx_calls_fun; - pub use crate::schema::pg::tx_calls_mod; - pub use crate::schema::pg::tx_calls_pkg; + pub use crate::schema::pg::tx_calls; pub use crate::schema::pg::tx_changed_objects; pub use crate::schema::pg::tx_digests; pub use crate::schema::pg::tx_input_objects; - pub use crate::schema::pg::tx_kinds; pub use crate::schema::pg::tx_recipients; pub use crate::schema::pg::tx_senders; } @@ -49,28 +38,17 @@ mod inner { pub use crate::schema::mysql::checkpoints; pub use crate::schema::mysql::display; pub use crate::schema::mysql::epochs; - pub use crate::schema::mysql::event_emit_module; - pub use crate::schema::mysql::event_emit_package; - pub use crate::schema::mysql::event_senders; - pub use crate::schema::mysql::event_struct_instantiation; - pub use crate::schema::mysql::event_struct_module; - pub use crate::schema::mysql::event_struct_name; - pub use crate::schema::mysql::event_struct_package; pub use crate::schema::mysql::events; pub use crate::schema::mysql::objects; pub use crate::schema::mysql::objects_history; pub use crate::schema::mysql::objects_snapshot; - pub use crate::schema::mysql::objects_version; pub use crate::schema::mysql::packages; pub use crate::schema::mysql::pruner_cp_watermark; pub use crate::schema::mysql::transactions; - pub use crate::schema::mysql::tx_calls_fun; - pub use crate::schema::mysql::tx_calls_mod; - pub use crate::schema::mysql::tx_calls_pkg; + pub use crate::schema::mysql::tx_calls; pub use crate::schema::mysql::tx_changed_objects; pub use crate::schema::mysql::tx_digests; pub use crate::schema::mysql::tx_input_objects; - pub use crate::schema::mysql::tx_kinds; pub use crate::schema::mysql::tx_recipients; pub use crate::schema::mysql::tx_senders; } @@ -79,28 +57,17 @@ pub use inner::chain_identifier; pub use inner::checkpoints; pub use inner::display; pub use inner::epochs; -pub use inner::event_emit_module; -pub use inner::event_emit_package; -pub use inner::event_senders; -pub use inner::event_struct_instantiation; -pub use inner::event_struct_module; -pub use inner::event_struct_name; -pub use inner::event_struct_package; pub use inner::events; pub use inner::objects; pub use inner::objects_history; pub use inner::objects_snapshot; -pub use inner::objects_version; pub use inner::packages; pub use inner::pruner_cp_watermark; pub use inner::transactions; -pub use inner::tx_calls_fun; -pub use inner::tx_calls_mod; -pub use inner::tx_calls_pkg; +pub use inner::tx_calls; pub use inner::tx_changed_objects; pub use inner::tx_digests; pub use inner::tx_input_objects; -pub use inner::tx_kinds; pub use inner::tx_recipients; pub use inner::tx_senders; diff --git a/crates/sui-indexer/src/schema/mysql.rs b/crates/sui-indexer/src/schema/mysql.rs index e7805ae562be0..10cdc089c8884 100644 --- a/crates/sui-indexer/src/schema/mysql.rs +++ b/crates/sui-indexer/src/schema/mysql.rs @@ -26,8 +26,6 @@ diesel::table! { checkpoint_commitments -> Mediumblob, validator_signature -> Blob, end_of_epoch_data -> Nullable, - min_tx_sequence_number -> Nullable, - max_tx_sequence_number -> Nullable } } @@ -82,74 +80,6 @@ diesel::table! { } } -diesel::table! { - event_emit_module (package, module, tx_sequence_number, event_sequence_number) { - package -> Blob, - module -> Text, - tx_sequence_number -> Bigint, - event_sequence_number -> Bigint, - sender -> Blob, - } -} - -diesel::table! { - event_emit_package (package, tx_sequence_number, event_sequence_number) { - package -> Blob, - tx_sequence_number -> Bigint, - event_sequence_number -> Bigint, - sender -> Blob, - } -} - -diesel::table! { - event_senders (sender, tx_sequence_number, event_sequence_number) { - sender -> Blob, - tx_sequence_number -> Bigint, - event_sequence_number -> Bigint, - } -} - -diesel::table! { - event_struct_instantiation (package, module, type_instantiation, tx_sequence_number, event_sequence_number) { - package -> Blob, - module -> Text, - type_instantiation -> Text, - tx_sequence_number -> Bigint, - event_sequence_number -> Bigint, - sender -> Blob, - } -} - -diesel::table! { - event_struct_module (package, module, tx_sequence_number, event_sequence_number) { - package -> Blob, - module -> Text, - tx_sequence_number -> Bigint, - event_sequence_number -> Bigint, - sender -> Blob, - } -} - -diesel::table! { - event_struct_name (package, module, type_name, tx_sequence_number, event_sequence_number) { - package -> Blob, - module -> Text, - type_name -> Text, - tx_sequence_number -> Bigint, - event_sequence_number -> Bigint, - sender -> Blob, - } -} - -diesel::table! { - event_struct_package (package, tx_sequence_number, event_sequence_number) { - package -> Blob, - tx_sequence_number -> Bigint, - event_sequence_number -> Bigint, - sender -> Blob, - } -} - diesel::table! { objects (object_id) { object_id -> Blob, @@ -218,21 +148,10 @@ diesel::table! { } } -diesel::table! { - objects_version (object_id, object_version) { - object_id -> Blob, - object_version -> Bigint, - cp_sequence_number -> Bigint, - } -} - diesel::table! { packages (package_id) { package_id -> Blob, - original_id -> Blob, - package_version -> Bigint, move_package -> Mediumblob, - checkpoint_sequence_number -> Bigint, } } @@ -261,29 +180,12 @@ diesel::table! { } diesel::table! { - tx_calls_fun (package, module, func, tx_sequence_number) { + tx_calls (package, tx_sequence_number, cp_sequence_number) { + cp_sequence_number -> Bigint, tx_sequence_number -> Bigint, package -> Blob, module -> Text, func -> Text, - sender -> Blob, - } -} - -diesel::table! { - tx_calls_mod (package, module, tx_sequence_number) { - tx_sequence_number -> Bigint, - package -> Blob, - module -> Text, - sender -> Blob, - } -} - -diesel::table! { - tx_calls_pkg (package, tx_sequence_number) { - tx_sequence_number -> Bigint, - package -> Blob, - sender -> Blob, } } @@ -292,7 +194,6 @@ diesel::table! { cp_sequence_number -> Bigint, tx_sequence_number -> Bigint, object_id -> Blob, - sender -> Blob, } } @@ -309,14 +210,6 @@ diesel::table! { cp_sequence_number -> Bigint, tx_sequence_number -> Bigint, object_id -> Blob, - sender -> Blob, - } -} - -diesel::table! { - tx_kinds (tx_kind, tx_sequence_number) { - tx_sequence_number -> Bigint, - tx_kind -> Smallint, } } @@ -325,7 +218,6 @@ diesel::table! { cp_sequence_number -> Bigint, tx_sequence_number -> Bigint, recipient -> Blob, - sender -> Blob, } } @@ -343,30 +235,18 @@ macro_rules! for_all_tables { $action!( chain_identifier, checkpoints, - pruner_cp_watermark, - display, epochs, - event_emit_module, - event_emit_package, - event_senders, - event_struct_instantiation, - event_struct_module, - event_struct_name, - event_struct_package, events, objects, objects_history, objects_snapshot, - objects_version, packages, + pruner_cp_watermark, transactions, - tx_calls_fun, - tx_calls_mod, - tx_calls_pkg, + tx_calls, tx_changed_objects, tx_digests, tx_input_objects, - tx_kinds, tx_recipients, tx_senders ); diff --git a/crates/sui-indexer/src/schema/pg.rs b/crates/sui-indexer/src/schema/pg.rs index 1ef8e0aed8e81..564f7cd721343 100644 --- a/crates/sui-indexer/src/schema/pg.rs +++ b/crates/sui-indexer/src/schema/pg.rs @@ -26,8 +26,6 @@ diesel::table! { checkpoint_commitments -> Bytea, validator_signature -> Bytea, end_of_epoch_data -> Nullable, - min_tx_sequence_number -> Nullable, - max_tx_sequence_number -> Nullable } } @@ -73,75 +71,7 @@ diesel::table! { } diesel::table! { - event_emit_module (package, module, tx_sequence_number, event_sequence_number) { - package -> Bytea, - module -> Text, - tx_sequence_number -> Int8, - event_sequence_number -> Int8, - sender -> Bytea, - } -} - -diesel::table! { - event_emit_package (package, tx_sequence_number, event_sequence_number) { - package -> Bytea, - tx_sequence_number -> Int8, - event_sequence_number -> Int8, - sender -> Bytea, - } -} - -diesel::table! { - event_senders (sender, tx_sequence_number, event_sequence_number) { - sender -> Bytea, - tx_sequence_number -> Int8, - event_sequence_number -> Int8, - } -} - -diesel::table! { - event_struct_instantiation (package, module, type_instantiation, tx_sequence_number, event_sequence_number) { - package -> Bytea, - module -> Text, - type_instantiation -> Text, - tx_sequence_number -> Int8, - event_sequence_number -> Int8, - sender -> Bytea, - } -} - -diesel::table! { - event_struct_module (package, module, tx_sequence_number, event_sequence_number) { - package -> Bytea, - module -> Text, - tx_sequence_number -> Int8, - event_sequence_number -> Int8, - sender -> Bytea, - } -} - -diesel::table! { - event_struct_name (package, module, type_name, tx_sequence_number, event_sequence_number) { - package -> Bytea, - module -> Text, - type_name -> Text, - tx_sequence_number -> Int8, - event_sequence_number -> Int8, - sender -> Bytea, - } -} - -diesel::table! { - event_struct_package (package, tx_sequence_number, event_sequence_number) { - package -> Bytea, - tx_sequence_number -> Int8, - event_sequence_number -> Int8, - sender -> Bytea, - } -} - -diesel::table! { - events (tx_sequence_number, event_sequence_number) { + events (tx_sequence_number, event_sequence_number, checkpoint_sequence_number) { tx_sequence_number -> Int8, event_sequence_number -> Int8, transaction_digest -> Bytea, @@ -159,7 +89,7 @@ diesel::table! { } diesel::table! { - events_partition_0 (tx_sequence_number, event_sequence_number) { + events_partition_0 (tx_sequence_number, event_sequence_number, checkpoint_sequence_number) { tx_sequence_number -> Int8, event_sequence_number -> Int8, transaction_digest -> Bytea, @@ -268,25 +198,14 @@ diesel::table! { } diesel::table! { - objects_version (object_id, object_version) { - object_id -> Bytea, - object_version -> Int8, - cp_sequence_number -> Int8, - } -} - -diesel::table! { - packages (package_id, original_id, package_version) { + packages (package_id) { package_id -> Bytea, - original_id -> Bytea, - package_version -> Int8, move_package -> Bytea, - checkpoint_sequence_number -> Int8, } } diesel::table! { - transactions (tx_sequence_number) { + transactions (tx_sequence_number, checkpoint_sequence_number) { tx_sequence_number -> Int8, transaction_digest -> Bytea, raw_transaction -> Bytea, @@ -302,7 +221,7 @@ diesel::table! { } diesel::table! { - transactions_partition_0 (tx_sequence_number) { + transactions_partition_0 (tx_sequence_number, checkpoint_sequence_number) { tx_sequence_number -> Int8, transaction_digest -> Bytea, raw_transaction -> Bytea, @@ -318,72 +237,50 @@ diesel::table! { } diesel::table! { - tx_calls_fun (package, module, func, tx_sequence_number) { + tx_calls (package, tx_sequence_number, cp_sequence_number) { + cp_sequence_number -> Int8, tx_sequence_number -> Int8, package -> Bytea, module -> Text, func -> Text, - sender -> Bytea, } } diesel::table! { - tx_calls_mod (package, module, tx_sequence_number) { - tx_sequence_number -> Int8, - package -> Bytea, - module -> Text, - sender -> Bytea, - } -} - -diesel::table! { - tx_calls_pkg (package, tx_sequence_number) { - tx_sequence_number -> Int8, - package -> Bytea, - sender -> Bytea, - } -} - -diesel::table! { - tx_changed_objects (object_id, tx_sequence_number) { + tx_changed_objects (object_id, tx_sequence_number, cp_sequence_number) { + cp_sequence_number -> Int8, tx_sequence_number -> Int8, object_id -> Bytea, - sender -> Bytea, } } diesel::table! { tx_digests (tx_digest) { tx_digest -> Bytea, + cp_sequence_number -> Int8, tx_sequence_number -> Int8, } } diesel::table! { - tx_input_objects (object_id, tx_sequence_number) { + tx_input_objects (object_id, tx_sequence_number, cp_sequence_number) { + cp_sequence_number -> Int8, tx_sequence_number -> Int8, object_id -> Bytea, - sender -> Bytea, - } -} - -diesel::table! { - tx_kinds (tx_kind, tx_sequence_number) { - tx_sequence_number -> Int8, - tx_kind -> Int2, } } diesel::table! { - tx_recipients (recipient, tx_sequence_number) { + tx_recipients (recipient, tx_sequence_number, cp_sequence_number) { + cp_sequence_number -> Int8, tx_sequence_number -> Int8, recipient -> Bytea, - sender -> Bytea, } } diesel::table! { - tx_senders (sender, tx_sequence_number) { + tx_senders (sender, tx_sequence_number, cp_sequence_number) { + cp_sequence_number -> Int8, tx_sequence_number -> Int8, sender -> Bytea, } @@ -398,33 +295,21 @@ macro_rules! for_all_tables { pruner_cp_watermark, display, epochs, - event_emit_module, - event_emit_package, - event_senders, - event_struct_instantiation, - event_struct_module, - event_struct_name, - event_struct_package, events, objects, objects_history, objects_snapshot, - objects_version, packages, transactions, - tx_calls_fun, - tx_calls_mod, - tx_calls_pkg, + tx_calls, tx_changed_objects, tx_digests, tx_input_objects, - tx_kinds, tx_recipients, tx_senders ); }; } - pub use for_all_tables; for_all_tables!(diesel::allow_tables_to_appear_in_same_query); diff --git a/crates/sui-indexer/src/store/indexer_store.rs b/crates/sui-indexer/src/store/indexer_store.rs index 97516929ffe24..868fe31416a6c 100644 --- a/crates/sui-indexer/src/store/indexer_store.rs +++ b/crates/sui-indexer/src/store/indexer_store.rs @@ -10,9 +10,7 @@ use crate::errors::IndexerError; use crate::handlers::{EpochToCommit, TransactionObjectChangesToCommit}; use crate::models::display::StoredDisplay; use crate::models::objects::{StoredDeletedObject, StoredObject}; -use crate::types::{ - EventIndex, IndexedCheckpoint, IndexedEvent, IndexedPackage, IndexedTransaction, TxIndex, -}; +use crate::types::{IndexedCheckpoint, IndexedEvent, IndexedPackage, IndexedTransaction, TxIndex}; #[allow(clippy::large_enum_variant)] pub enum ObjectChangeToCommit { @@ -65,11 +63,6 @@ pub trait IndexerStore: Any + Clone + Sync + Send + 'static { async fn persist_tx_indices(&self, indices: Vec) -> Result<(), IndexerError>; async fn persist_events(&self, events: Vec) -> Result<(), IndexerError>; - async fn persist_event_indices( - &self, - event_indices: Vec, - ) -> Result<(), IndexerError>; - async fn persist_displays( &self, display_updates: BTreeMap, diff --git a/crates/sui-indexer/src/store/mod.rs b/crates/sui-indexer/src/store/mod.rs index 920cc5817499c..ae04c2ea1c1c2 100644 --- a/crates/sui-indexer/src/store/mod.rs +++ b/crates/sui-indexer/src/store/mod.rs @@ -312,36 +312,6 @@ pub mod diesel_macro { }}; } - #[macro_export] - macro_rules! persist_chunk_into_table { - ($table:expr, $chunk:expr, $pool:expr) => {{ - let now = std::time::Instant::now(); - let chunk_len = $chunk.len(); - transactional_blocking_with_retry!( - $pool, - |conn| { - for chunk in $chunk.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { - insert_or_ignore_into!($table, chunk, conn); - } - Ok::<(), IndexerError>(()) - }, - PG_DB_COMMIT_SLEEP_DURATION - ) - .tap_ok(|_| { - let elapsed = now.elapsed().as_secs_f64(); - info!( - elapsed, - "Persisted {} rows to {}", - chunk_len, - stringify!($table), - ); - }) - .tap_err(|e| { - tracing::error!("Failed to persist {} with error: {}", stringify!($table), e); - }) - }}; - } - pub use blocking_call_is_ok_or_panic; pub use read_only_blocking; pub use read_only_repeatable_blocking; diff --git a/crates/sui-indexer/src/store/pg_indexer_store.rs b/crates/sui-indexer/src/store/pg_indexer_store.rs index 04fcd1fbc5210..39e16c2e394ab 100644 --- a/crates/sui-indexer/src/store/pg_indexer_store.rs +++ b/crates/sui-indexer/src/store/pg_indexer_store.rs @@ -33,7 +33,6 @@ use crate::models::checkpoints::StoredCpTx; use crate::models::display::StoredDisplay; use crate::models::epoch::StoredEpochInfo; use crate::models::events::StoredEvent; -use crate::models::obj_indices::StoredObjectVersion; use crate::models::objects::{ StoredDeletedHistoryObject, StoredDeletedObject, StoredHistoryObject, StoredObject, StoredObjectSnapshot, @@ -41,16 +40,13 @@ use crate::models::objects::{ use crate::models::packages::StoredPackage; use crate::models::transactions::StoredTransaction; use crate::schema::{ - chain_identifier, checkpoints, display, epochs, event_emit_module, event_emit_package, - event_senders, event_struct_instantiation, event_struct_module, event_struct_name, - event_struct_package, events, objects, objects_history, objects_snapshot, objects_version, - packages, pruner_cp_watermark, transactions, tx_calls_fun, tx_calls_mod, tx_calls_pkg, - tx_changed_objects, tx_digests, tx_input_objects, tx_kinds, tx_recipients, tx_senders, + chain_identifier, checkpoints, display, epochs, events, objects, objects_history, + objects_snapshot, packages, pruner_cp_watermark, transactions, tx_calls, tx_changed_objects, + tx_digests, tx_input_objects, tx_recipients, tx_senders, }; -use crate::types::EventIndex; use crate::types::{IndexedCheckpoint, IndexedEvent, IndexedPackage, IndexedTransaction, TxIndex}; use crate::{ - insert_or_ignore_into, on_conflict_do_update, persist_chunk_into_table, read_only_blocking, + insert_or_ignore_into, on_conflict_do_update, read_only_blocking, transactional_blocking_with_retry, }; @@ -73,7 +69,7 @@ macro_rules! chunk { }}; } -macro_rules! prune_tx_or_event_indice_table { +macro_rules! prune_tx_indice_table { ($table:ident, $conn:expr, $min_tx:expr, $max_tx:expr, $context_msg:expr) => { diesel::delete($table::table.filter($table::tx_sequence_number.between($min_tx, $max_tx))) .execute($conn) @@ -466,12 +462,6 @@ impl PgIndexerStore { .eq(excluded(objects_snapshot::checkpoint_sequence_number)), objects_snapshot::owner_type.eq(excluded(objects_snapshot::owner_type)), objects_snapshot::owner_id.eq(excluded(objects_snapshot::owner_id)), - objects_snapshot::object_type_package - .eq(excluded(objects_snapshot::object_type_package)), - objects_snapshot::object_type_module - .eq(excluded(objects_snapshot::object_type_module)), - objects_snapshot::object_type_name - .eq(excluded(objects_snapshot::object_type_name)), objects_snapshot::object_type .eq(excluded(objects_snapshot::object_type)), objects_snapshot::serialized_object @@ -494,9 +484,6 @@ impl PgIndexerStore { .eq(excluded.checkpoint_sequence_number), objects_snapshot::owner_type.eq(excluded.owner_type), objects_snapshot::owner_id.eq(excluded.owner_id), - objects_snapshot::object_type_package.eq(excluded.object_type_package), - objects_snapshot::object_type_module.eq(excluded.object_type_module), - objects_snapshot::object_type_name.eq(excluded.object_type_name), objects_snapshot::object_type.eq(excluded.object_type), objects_snapshot::serialized_object.eq(excluded.serialized_object), objects_snapshot::coin_type.eq(excluded.coin_type), @@ -535,16 +522,13 @@ impl PgIndexerStore { .checkpoint_db_commit_latency_objects_history_chunks .start_timer(); let mut mutated_objects: Vec = vec![]; - let mut object_versions: Vec = vec![]; let mut deleted_object_ids: Vec = vec![]; for object in objects { match object { ObjectChangeToCommit::MutatedObject(stored_object) => { - object_versions.push(StoredObjectVersion::from(&stored_object)); mutated_objects.push(stored_object.into()); } ObjectChangeToCommit::DeletedObject(stored_deleted_object) => { - object_versions.push(StoredObjectVersion::from(&stored_deleted_object)); deleted_object_ids.push(stored_deleted_object.into()); } } @@ -563,11 +547,6 @@ impl PgIndexerStore { ); } - for object_version_chunk in object_versions.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) - { - insert_or_ignore_into!(objects_version::table, object_version_chunk, conn); - } - for deleted_objects_chunk in deleted_object_ids.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { @@ -827,144 +806,13 @@ impl PgIndexerStore { }) } - async fn persist_event_indices_chunk( - &self, - indices: Vec, - ) -> Result<(), IndexerError> { - let guard = self - .metrics - .checkpoint_db_commit_latency_event_indices_chunks - .start_timer(); - let len = indices.len(); - let ( - event_emit_packages, - event_emit_modules, - event_senders, - event_struct_packages, - event_struct_modules, - event_struct_names, - event_struct_instantiations, - ) = indices.into_iter().map(|i| i.split()).fold( - ( - Vec::new(), - Vec::new(), - Vec::new(), - Vec::new(), - Vec::new(), - Vec::new(), - Vec::new(), - ), - |( - mut event_emit_packages, - mut event_emit_modules, - mut event_senders, - mut event_struct_packages, - mut event_struct_modules, - mut event_struct_names, - mut event_struct_instantiations, - ), - index| { - event_emit_packages.push(index.0); - event_emit_modules.push(index.1); - event_senders.push(index.2); - event_struct_packages.push(index.3); - event_struct_modules.push(index.4); - event_struct_names.push(index.5); - event_struct_instantiations.push(index.6); - ( - event_emit_packages, - event_emit_modules, - event_senders, - event_struct_packages, - event_struct_modules, - event_struct_names, - event_struct_instantiations, - ) - }, - ); - - // Now persist all the event indices in parallel into their tables. - let mut futures = vec![]; - futures.push(self.spawn_blocking_task(move |this| { - persist_chunk_into_table!( - event_emit_package::table, - event_emit_packages, - &this.blocking_cp - ) - })); - - futures.push(self.spawn_blocking_task(move |this| { - persist_chunk_into_table!( - event_emit_module::table, - event_emit_modules, - &this.blocking_cp - ) - })); - - futures.push(self.spawn_blocking_task(move |this| { - persist_chunk_into_table!(event_senders::table, event_senders, &this.blocking_cp) - })); - - futures.push(self.spawn_blocking_task(move |this| { - persist_chunk_into_table!( - event_struct_package::table, - event_struct_packages, - &this.blocking_cp - ) - })); - - futures.push(self.spawn_blocking_task(move |this| { - persist_chunk_into_table!( - event_struct_module::table, - event_struct_modules, - &this.blocking_cp - ) - })); - - futures.push(self.spawn_blocking_task(move |this| { - persist_chunk_into_table!( - event_struct_name::table, - event_struct_names, - &this.blocking_cp - ) - })); - - futures.push(self.spawn_blocking_task(move |this| { - persist_chunk_into_table!( - event_struct_instantiation::table, - event_struct_instantiations, - &this.blocking_cp - ) - })); - - futures::future::join_all(futures) - .await - .into_iter() - .collect::, _>>() - .map_err(|e| { - tracing::error!("Failed to join event indices futures in a chunk: {}", e); - IndexerError::from(e) - })? - .into_iter() - .collect::, _>>() - .map_err(|e| { - IndexerError::PostgresWriteError(format!( - "Failed to persist all event indices in a chunk: {:?}", - e - )) - })?; - let elapsed = guard.stop_and_record(); - info!(elapsed, "Persisted {} chunked event indices", len); - Ok(()) - } - async fn persist_tx_indices_chunk(&self, indices: Vec) -> Result<(), IndexerError> { let guard = self .metrics .checkpoint_db_commit_latency_tx_indices_chunks .start_timer(); let len = indices.len(); - let (senders, recipients, input_objects, changed_objects, pkgs, mods, funs, digests, kinds) = + let (senders, recipients, input_objects, changed_objects, calls, digests) = indices.into_iter().map(|i| i.split()).fold( ( Vec::new(), @@ -973,41 +821,29 @@ impl PgIndexerStore { Vec::new(), Vec::new(), Vec::new(), - Vec::new(), - Vec::new(), - Vec::new(), ), |( mut tx_senders, mut tx_recipients, mut tx_input_objects, mut tx_changed_objects, - mut tx_pkgs, - mut tx_mods, - mut tx_funs, + mut tx_calls, mut tx_digests, - mut tx_kinds, ), index| { tx_senders.extend(index.0); tx_recipients.extend(index.1); 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_calls.extend(index.4); + tx_digests.extend(index.5); ( tx_senders, tx_recipients, tx_input_objects, tx_changed_objects, - tx_pkgs, - tx_mods, - tx_funs, + tx_calls, tx_digests, - tx_kinds, ) }, ); @@ -1096,15 +932,14 @@ impl PgIndexerStore { tracing::error!("Failed to persist tx_changed_objects with error: {}", e); }) })); - futures.push(self.spawn_blocking_task(move |this| { let now = Instant::now(); - let rows_len = pkgs.len(); + let calls_len = calls.len(); transactional_blocking_with_retry!( &this.blocking_cp, |conn| { - for chunk in pkgs.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { - insert_or_ignore_into!(tx_calls_pkg::table, chunk, conn); + for chunk in calls.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { + insert_or_ignore_into!(tx_calls::table, chunk, conn); } Ok::<(), IndexerError>(()) }, @@ -1112,60 +947,12 @@ impl PgIndexerStore { ) .tap_ok(|_| { let elapsed = now.elapsed().as_secs_f64(); - info!( - elapsed, - "Persisted {} rows to tx_calls_pkg tables", rows_len - ); + info!(elapsed, "Persisted {} rows to tx_calls tables", calls_len); }) .tap_err(|e| { - tracing::error!("Failed to persist tx_calls_pkg with error: {}", e); + tracing::error!("Failed to persist tx_calls with error: {}", e); }) })); - - futures.push(self.spawn_blocking_task(move |this| { - let now = Instant::now(); - let rows_len = mods.len(); - transactional_blocking_with_retry!( - &this.blocking_cp, - |conn| { - for chunk in mods.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { - insert_or_ignore_into!(tx_calls_mod::table, chunk, conn); - } - Ok::<(), IndexerError>(()) - }, - PG_DB_COMMIT_SLEEP_DURATION - ) - .tap_ok(|_| { - let elapsed = now.elapsed().as_secs_f64(); - info!(elapsed, "Persisted {} rows to tx_calls_mod table", rows_len); - }) - .tap_err(|e| { - tracing::error!("Failed to persist tx_calls_mod with error: {}", e); - }) - })); - - futures.push(self.spawn_blocking_task(move |this| { - let now = Instant::now(); - let rows_len = funs.len(); - transactional_blocking_with_retry!( - &this.blocking_cp, - |conn| { - for chunk in funs.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { - insert_or_ignore_into!(tx_calls_fun::table, chunk, conn); - } - Ok::<(), IndexerError>(()) - }, - PG_DB_COMMIT_SLEEP_DURATION - ) - .tap_ok(|_| { - let elapsed = now.elapsed().as_secs_f64(); - info!(elapsed, "Persisted {} rows to tx_calls_fun table", rows_len); - }) - .tap_err(|e| { - tracing::error!("Failed to persist tx_calls_fun with error: {}", e); - }) - })); - futures.push(self.spawn_blocking_task(move |this| { let now = Instant::now(); let calls_len = digests.len(); @@ -1173,7 +960,12 @@ impl PgIndexerStore { &this.blocking_cp, |conn| { for chunk in digests.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { - insert_or_ignore_into!(tx_digests::table, chunk, conn); + diesel::insert_into(tx_digests::table) + .values(chunk) + .on_conflict_do_nothing() + .execute(conn) + .map_err(IndexerError::from) + .context("Failed to write tx_digests chunk to PostgresDB")?; } Ok::<(), IndexerError>(()) }, @@ -1188,28 +980,6 @@ impl PgIndexerStore { }) })); - futures.push(self.spawn_blocking_task(move |this| { - let now = Instant::now(); - let rows_len = kinds.len(); - transactional_blocking_with_retry!( - &this.blocking_cp, - |conn| { - for chunk in kinds.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { - insert_or_ignore_into!(tx_kinds::table, chunk, conn); - } - Ok::<(), IndexerError>(()) - }, - Duration::from_secs(60) - ) - .tap_ok(|_| { - let elapsed = now.elapsed().as_secs_f64(); - info!(elapsed, "Persisted {} rows to tx_kinds tables", rows_len); - }) - .tap_err(|e| { - tracing::error!("Failed to persist tx_kinds with error: {}", e); - }) - })); - futures::future::join_all(futures) .await .into_iter() @@ -1237,35 +1007,12 @@ impl PgIndexerStore { .checkpoint_db_commit_latency_epoch .start_timer(); let epoch_id = epoch.new_epoch.epoch; - transactional_blocking_with_retry!( &self.blocking_cp, |conn| { if let Some(last_epoch) = &epoch.last_epoch { let last_epoch_id = last_epoch.epoch; - // Overwrites the `epoch_total_transactions` field on `epoch.last_epoch` because - // we are not guaranteed to have the latest data in db when this is set on - // indexer's chain-reading side. However, when we `persist_epoch`, the - // checkpoints from an epoch ago must have been indexed. - let previous_epoch_network_total_transactions = match epoch_id { - 0 | 1 => 0, - _ => { - let prev_epoch_id = epoch_id - 2; - let result = checkpoints::table - .filter(checkpoints::epoch.eq(prev_epoch_id as i64)) - .select(max(checkpoints::network_total_transactions)) - .first::>(conn) - .map(|o| o.unwrap_or(0))?; - - result as u64 - } - }; - - let epoch_total_transactions = epoch.network_total_transactions - - previous_epoch_network_total_transactions; - - let mut last_epoch = StoredEpochInfo::from_epoch_end_info(last_epoch); - last_epoch.epoch_total_transactions = Some(epoch_total_transactions as i64); + let last_epoch = StoredEpochInfo::from_epoch_end_info(last_epoch); info!(last_epoch_id, "Persisting epoch end data."); on_conflict_do_update!( epochs::table, @@ -1347,14 +1094,6 @@ impl PgIndexerStore { EpochPartitionData::compose_data(epoch_to_commit, last_epoch); let table_partitions = self.partition_manager.get_table_partitions()?; for (table, (_, last_partition)) in table_partitions { - // Only advance epoch partition for epoch partitioned tables. - if !self - .partition_manager - .get_strategy(&table) - .is_epoch_partitioned() - { - continue; - } let guard = self.metrics.advance_epoch_latency.start_timer(); self.partition_manager.advance_epoch( table.clone(), @@ -1408,121 +1147,47 @@ impl PgIndexerStore { ) } - fn prune_event_indices_table(&self, min_tx: u64, max_tx: u64) -> Result<(), IndexerError> { - let (min_tx, max_tx) = (min_tx as i64, max_tx as i64); - transactional_blocking_with_retry!( - &self.blocking_cp, - |conn| { - prune_tx_or_event_indice_table!( - event_emit_module, - conn, - min_tx, - max_tx, - "Failed to prune event_emit_module table" - ); - prune_tx_or_event_indice_table!( - event_emit_package, - conn, - min_tx, - max_tx, - "Failed to prune event_emit_package table" - ); - prune_tx_or_event_indice_table![ - event_senders, - conn, - min_tx, - max_tx, - "Failed to prune event_senders table" - ]; - prune_tx_or_event_indice_table![ - event_struct_instantiation, - conn, - min_tx, - max_tx, - "Failed to prune event_struct_instantiation table" - ]; - prune_tx_or_event_indice_table![ - event_struct_module, - conn, - min_tx, - max_tx, - "Failed to prune event_struct_module table" - ]; - prune_tx_or_event_indice_table![ - event_struct_name, - conn, - min_tx, - max_tx, - "Failed to prune event_struct_name table" - ]; - prune_tx_or_event_indice_table![ - event_struct_package, - conn, - min_tx, - max_tx, - "Failed to prune event_struct_package table" - ]; - Ok::<(), IndexerError>(()) - }, - PG_DB_COMMIT_SLEEP_DURATION - ) - } - fn prune_tx_indices_table(&self, min_tx: u64, max_tx: u64) -> Result<(), IndexerError> { let (min_tx, max_tx) = (min_tx as i64, max_tx as i64); transactional_blocking_with_retry!( &self.blocking_cp, |conn| { - prune_tx_or_event_indice_table!( + prune_tx_indice_table!( tx_senders, conn, min_tx, max_tx, "Failed to prune tx_senders table" ); - prune_tx_or_event_indice_table!( + prune_tx_indice_table!( tx_recipients, conn, min_tx, max_tx, "Failed to prune tx_recipients table" ); - prune_tx_or_event_indice_table![ + prune_tx_indice_table![ tx_input_objects, conn, min_tx, max_tx, "Failed to prune tx_input_objects table" ]; - prune_tx_or_event_indice_table![ + prune_tx_indice_table![ tx_changed_objects, conn, min_tx, max_tx, "Failed to prune tx_changed_objects table" ]; - prune_tx_or_event_indice_table![ - tx_calls_pkg, + prune_tx_indice_table![ + tx_calls, conn, min_tx, max_tx, - "Failed to prune tx_calls_pkg table" + "Failed to prune tx_calls table" ]; - prune_tx_or_event_indice_table![ - tx_calls_mod, - conn, - min_tx, - max_tx, - "Failed to prune tx_calls_mod table" - ]; - prune_tx_or_event_indice_table![ - tx_calls_fun, - conn, - min_tx, - max_tx, - "Failed to prune tx_calls_fun table" - ]; - prune_tx_or_event_indice_table![ + prune_tx_indice_table![ tx_digests, conn, min_tx, @@ -1956,46 +1621,6 @@ impl IndexerStore for PgIndexerStore { .await } - async fn persist_event_indices(&self, indices: Vec) -> Result<(), IndexerError> { - if indices.is_empty() { - return Ok(()); - } - let len = indices.len(); - let guard = self - .metrics - .checkpoint_db_commit_latency_event_indices - .start_timer(); - let chunks = chunk!(indices, self.config.parallel_chunk_size); - - let futures = chunks - .into_iter() - .map(|chunk| { - self.spawn_task(move |this: Self| async move { - this.persist_event_indices_chunk(chunk).await - }) - }) - .collect::>(); - futures::future::join_all(futures) - .await - .into_iter() - .collect::, _>>() - .map_err(|e| { - tracing::error!("Failed to join persist_event_indices_chunk futures: {}", e); - IndexerError::from(e) - })? - .into_iter() - .collect::, _>>() - .map_err(|e| { - IndexerError::PostgresWriteError(format!( - "Failed to persist all event_indices chunks: {:?}", - e - )) - })?; - let elapsed = guard.stop_and_record(); - info!(elapsed, "Persisted {} event_indices chunks", len); - Ok(()) - } - async fn persist_tx_indices(&self, indices: Vec) -> Result<(), IndexerError> { if indices.is_empty() { return Ok(()); @@ -2091,21 +1716,6 @@ impl IndexerStore for PgIndexerStore { "Pruned transactions for checkpoint {} from tx {} to tx {}", cp, min_tx, max_tx ); - self.execute_in_blocking_worker(move |this| { - this.prune_event_indices_table(min_tx, max_tx) - }) - .await - .unwrap_or_else(|e| { - tracing::error!( - "Failed to prune events of transactions for cp {}: {}", - cp, - e - ); - }); - info!( - "Pruned events of transactions for checkpoint {} from tx {} to tx {}", - cp, min_tx, max_tx - ); self.metrics.last_pruned_transaction.set(max_tx as i64); self.execute_in_blocking_worker(move |this| this.prune_cp_tx_table(cp)) diff --git a/crates/sui-indexer/src/store/pg_partition_manager.rs b/crates/sui-indexer/src/store/pg_partition_manager.rs index f27078ca47048..f67f1ed6b4041 100644 --- a/crates/sui-indexer/src/store/pg_partition_manager.rs +++ b/crates/sui-indexer/src/store/pg_partition_manager.rs @@ -4,7 +4,7 @@ use diesel::r2d2::R2D2Connection; use diesel::sql_types::{BigInt, VarChar}; use diesel::{QueryableByName, RunQueryDsl}; -use std::collections::{BTreeMap, HashMap}; +use std::collections::BTreeMap; use std::time::Duration; use tracing::{error, info}; @@ -44,42 +44,22 @@ GROUP BY table_name; pub struct PgPartitionManager { cp: ConnectionPool, - partition_strategies: HashMap<&'static str, PgPartitionStrategy>, } impl Clone for PgPartitionManager { fn clone(&self) -> PgPartitionManager { Self { cp: self.cp.clone(), - partition_strategies: self.partition_strategies.clone(), } } } -#[derive(Clone, Copy)] -pub enum PgPartitionStrategy { - CheckpointSequenceNumber, - TxSequenceNumber, - ObjectId, -} - -impl PgPartitionStrategy { - pub fn is_epoch_partitioned(&self) -> bool { - matches!( - self, - Self::CheckpointSequenceNumber | Self::TxSequenceNumber - ) - } -} - #[derive(Clone, Debug)] pub struct EpochPartitionData { last_epoch: u64, next_epoch: u64, last_epoch_start_cp: u64, next_epoch_start_cp: u64, - last_epoch_start_tx: u64, - next_epoch_start_tx: u64, } impl EpochPartitionData { @@ -88,35 +68,18 @@ impl EpochPartitionData { let last_epoch_start_cp = last_db_epoch.first_checkpoint_id as u64; let next_epoch = epoch.new_epoch.epoch; let next_epoch_start_cp = epoch.new_epoch.first_checkpoint_id; - - // Determining the tx_sequence_number range for the epoch partition differs from the - // checkpoint_sequence_number range, because the former is a sum of total transactions - - // this sum already addresses the off-by-one. - let next_epoch_start_tx = epoch.network_total_transactions; - let last_epoch_start_tx = - next_epoch_start_tx - last_db_epoch.epoch_total_transactions.unwrap() as u64; - Self { last_epoch, next_epoch, last_epoch_start_cp, next_epoch_start_cp, - last_epoch_start_tx, - next_epoch_start_tx, } } } impl PgPartitionManager { pub fn new(cp: ConnectionPool) -> Result { - let mut partition_strategies = HashMap::new(); - partition_strategies.insert("events", PgPartitionStrategy::TxSequenceNumber); - partition_strategies.insert("transactions", PgPartitionStrategy::TxSequenceNumber); - partition_strategies.insert("objects_version", PgPartitionStrategy::ObjectId); - let manager = Self { - cp, - partition_strategies, - }; + let manager = Self { cp }; let tables = manager.get_table_partitions()?; info!( "Found {} tables with partitions : [{:?}]", @@ -153,41 +116,12 @@ impl PgPartitionManager { ) } - /// Tries to fetch the partitioning strategy for the given partitioned table. Defaults to - /// `CheckpointSequenceNumber` as the majority of our tables are partitioned on an epoch's - /// checkpoints today. - pub fn get_strategy(&self, table_name: &str) -> PgPartitionStrategy { - self.partition_strategies - .get(table_name) - .copied() - .unwrap_or(PgPartitionStrategy::CheckpointSequenceNumber) - } - - pub fn determine_epoch_partition_range( - &self, - table_name: &str, - data: &EpochPartitionData, - ) -> Option<(u64, u64)> { - match self.get_strategy(table_name) { - PgPartitionStrategy::CheckpointSequenceNumber => { - Some((data.last_epoch_start_cp, data.next_epoch_start_cp)) - } - PgPartitionStrategy::TxSequenceNumber => { - Some((data.last_epoch_start_tx, data.next_epoch_start_tx)) - } - PgPartitionStrategy::ObjectId => None, - } - } - pub fn advance_epoch( &self, table: String, last_partition: u64, data: &EpochPartitionData, ) -> Result<(), IndexerError> { - let Some(partition_range) = self.determine_epoch_partition_range(&table, data) else { - return Ok(()); - }; if data.next_epoch == 0 { tracing::info!("Epoch 0 partition has been created in the initial setup."); return Ok(()); @@ -202,8 +136,8 @@ impl PgPartitionManager { .bind::(table.clone()) .bind::(data.last_epoch as i64) .bind::(data.next_epoch as i64) - .bind::(partition_range.0 as i64) - .bind::(partition_range.1 as i64), + .bind::(data.last_epoch_start_cp as i64) + .bind::(data.next_epoch_start_cp as i64), conn, ) }, @@ -214,14 +148,14 @@ impl PgPartitionManager { transactional_blocking_with_retry!( &self.cp, |conn| { - RunQueryDsl::execute(diesel::sql_query(format!("ALTER TABLE {table_name} REORGANIZE PARTITION {table_name}_partition_{last_epoch} INTO (PARTITION {table_name}_partition_{last_epoch} VALUES LESS THAN ({next_epoch_start}), PARTITION {table_name}_partition_{next_epoch} VALUES LESS THAN MAXVALUE)", table_name = table.clone(), last_epoch = data.last_epoch as i64, next_epoch_start = partition_range.1 as i64, next_epoch = data.next_epoch as i64)), conn) + RunQueryDsl::execute(diesel::sql_query(format!("ALTER TABLE {table_name} REORGANIZE PARTITION {table_name}_partition_{last_epoch} INTO (PARTITION {table_name}_partition_{last_epoch} VALUES LESS THAN ({next_epoch_start_cp}), PARTITION {table_name}_partition_{next_epoch} VALUES LESS THAN MAXVALUE)", table_name = table.clone(), last_epoch = data.last_epoch as i64, next_epoch_start_cp = data.next_epoch_start_cp as i64, next_epoch = data.next_epoch as i64)), conn) }, Duration::from_secs(10) )?; info!( "Advanced epoch partition for table {} from {} to {}, prev partition upper bound {}", - table, last_partition, data.next_epoch, partition_range.0 + table, last_partition, data.next_epoch, data.last_epoch_start_cp ); } else if last_partition != data.next_epoch { // skip when the partition is already advanced once, which is possible when indexer diff --git a/crates/sui-indexer/src/types.rs b/crates/sui-indexer/src/types.rs index b3fd4dabddac5..7eb2c0071ce7f 100644 --- a/crates/sui-indexer/src/types.rs +++ b/crates/sui-indexer/src/types.rs @@ -89,8 +89,6 @@ impl IndexedCheckpoint { } } -/// Represents system state and summary info at the start and end of an epoch. Optional fields are -/// populated at epoch boundary, since they cannot be determined at the start of the epoch. #[derive(Clone, Debug, Default)] pub struct IndexedEpochInfo { pub epoch: u64, @@ -136,9 +134,6 @@ impl IndexedEpochInfo { } } - /// Creates `IndexedEpochInfo` for epoch X-1 at the boundary of epoch X-1 to X. - /// `network_total_tx_num_at_last_epoch_end` is needed to determine the number of transactions - /// that occurred in the epoch X-1. pub fn from_end_of_epoch_data( system_state_summary: &SuiSystemStateSummary, last_checkpoint_summary: &CertifiedCheckpointSummary, @@ -222,47 +217,6 @@ impl IndexedEvent { } } -#[derive(Debug, Clone)] -pub struct EventIndex { - pub tx_sequence_number: u64, - pub event_sequence_number: u64, - pub sender: SuiAddress, - pub emit_package: ObjectID, - pub emit_module: String, - pub type_package: ObjectID, - pub type_module: String, - /// Struct name of the event, without type parameters. - pub type_name: String, - /// Type instantiation of the event, with type name and type parameters, if any. - pub type_instantiation: String, -} - -impl EventIndex { - pub fn from_event( - tx_sequence_number: u64, - event_sequence_number: u64, - event: &sui_types::event::Event, - ) -> Self { - let type_instantiation = event - .type_ - .to_canonical_string(/* with_prefix */ true) - .splitn(3, "::") - .collect::>()[2] - .to_string(); - Self { - tx_sequence_number, - event_sequence_number, - sender: event.sender, - emit_package: event.package_id, - emit_module: event.transaction_module.to_string(), - type_package: event.type_.address.into(), - type_module: event.type_.module.to_string(), - type_name: event.type_.name.to_string(), - type_instantiation, - } - } -} - #[derive(Debug, Copy, Clone)] pub enum OwnerType { Immutable = 0, @@ -409,13 +363,12 @@ pub struct IndexedTransaction { #[derive(Debug, Clone)] pub struct TxIndex { pub tx_sequence_number: u64, - pub tx_kind: TransactionKind, pub transaction_digest: TransactionDigest, pub checkpoint_sequence_number: u64, pub input_objects: Vec, pub changed_objects: Vec, pub payers: Vec, - pub sender: SuiAddress, + pub senders: Vec, pub recipients: Vec, pub move_calls: Vec<(ObjectID, String, String)>, } From a3e32fec739bb02cf3dca1c3510a4179f481bd99 Mon Sep 17 00:00:00 2001 From: hayes-mysten <135670682+hayes-mysten@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:26:46 -0700 Subject: [PATCH 111/232] [ts sdk] replace WaitForLocalExecution with waitForTransaction (#18929) ## Description Describe the changes or additions included in this PR. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .changeset/seven-pets-count.md | 5 +++++ sdk/typescript/src/client/client.ts | 32 ++++++++++++++++++++--------- 2 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 .changeset/seven-pets-count.md diff --git a/.changeset/seven-pets-count.md b/.changeset/seven-pets-count.md new file mode 100644 index 0000000000000..c5f6d407ac158 --- /dev/null +++ b/.changeset/seven-pets-count.md @@ -0,0 +1,5 @@ +--- +'@mysten/sui': minor +--- + +`WaitForLocalExecution` now waits using client.waitForTransaction rather than sending requestType to the RPC node. This change will preserve readAfterWrite consistency when local execution is removed from fullnodes, at the cost of more network requests and higher latency. diff --git a/sdk/typescript/src/client/client.ts b/sdk/typescript/src/client/client.ts index 530e846b1fea9..c425f9f3d2148 100644 --- a/sdk/typescript/src/client/client.ts +++ b/sdk/typescript/src/client/client.ts @@ -406,20 +406,32 @@ export class SuiClient { }); } - async executeTransactionBlock( - input: ExecuteTransactionBlockParams, - ): Promise { - return await this.transport.request({ + async executeTransactionBlock({ + transactionBlock, + signature, + options, + requestType, + }: ExecuteTransactionBlockParams): Promise { + const result: SuiTransactionBlockResponse = await this.transport.request({ method: 'sui_executeTransactionBlock', params: [ - typeof input.transactionBlock === 'string' - ? input.transactionBlock - : toB64(input.transactionBlock), - Array.isArray(input.signature) ? input.signature : [input.signature], - input.options, - input.requestType, + typeof transactionBlock === 'string' ? transactionBlock : toB64(transactionBlock), + Array.isArray(signature) ? signature : [signature], + options, ], }); + + if (requestType === 'WaitForLocalExecution') { + try { + await this.waitForTransaction({ + digest: result.digest, + }); + } catch (_) { + // Ignore error while waiting for transaction + } + } + + return result; } async signAndExecuteTransaction({ From c5766bce0e51044c065b950fb6d1e8d50d94fd0d Mon Sep 17 00:00:00 2001 From: "sui-merge-bot[bot]" <114704316+sui-merge-bot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 18:56:19 +0000 Subject: [PATCH 112/232] Version Packages (#18976) This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @mysten/sui@1.6.0 ### Minor Changes - a3e32fe: `WaitForLocalExecution` now waits using client.waitForTransaction rather than sending requestType to the RPC node. This change will preserve readAfterWrite consistency when local execution is removed from fullnodes, at the cost of more network requests and higher latency. ## @mysten/create-dapp@0.3.16 ### Patch Changes - Updated dependencies [a3e32fe] - @mysten/sui@1.6.0 - @mysten/dapp-kit@0.14.16 ## @mysten/dapp-kit@0.14.16 ### Patch Changes - Updated dependencies [a3e32fe] - @mysten/sui@1.6.0 - @mysten/wallet-standard@0.13.1 - @mysten/zksend@0.10.5 ## @mysten/deepbook@0.8.15 ### Patch Changes - Updated dependencies [a3e32fe] - @mysten/sui@1.6.0 ## @mysten/deepbook-v3@0.2.1 ### Patch Changes - Updated dependencies [a3e32fe] - @mysten/sui@1.6.0 ## @mysten/enoki@0.3.16 ### Patch Changes - Updated dependencies [a3e32fe] - @mysten/sui@1.6.0 - @mysten/zklogin@0.7.16 ## @mysten/graphql-transport@0.2.15 ### Patch Changes - Updated dependencies [a3e32fe] - @mysten/sui@1.6.0 ## @mysten/kiosk@0.9.15 ### Patch Changes - Updated dependencies [a3e32fe] - @mysten/sui@1.6.0 ## @mysten/suins-toolkit@0.5.15 ### Patch Changes - Updated dependencies [a3e32fe] - @mysten/sui@1.6.0 ## @mysten/wallet-standard@0.13.1 ### Patch Changes - Updated dependencies [a3e32fe] - @mysten/sui@1.6.0 ## @mysten/zklogin@0.7.16 ### Patch Changes - Updated dependencies [a3e32fe] - @mysten/sui@1.6.0 ## @mysten/zksend@0.10.5 ### Patch Changes - Updated dependencies [a3e32fe] - @mysten/sui@1.6.0 - @mysten/wallet-standard@0.13.1 Co-authored-by: github-actions[bot] --- .changeset/seven-pets-count.md | 5 ----- sdk/create-dapp/CHANGELOG.md | 8 ++++++++ sdk/create-dapp/package.json | 2 +- sdk/dapp-kit/CHANGELOG.md | 9 +++++++++ sdk/dapp-kit/package.json | 2 +- sdk/deepbook-v3/CHANGELOG.md | 7 +++++++ sdk/deepbook-v3/package.json | 2 +- sdk/deepbook/CHANGELOG.md | 7 +++++++ sdk/deepbook/package.json | 2 +- sdk/enoki/CHANGELOG.md | 8 ++++++++ sdk/enoki/package.json | 2 +- sdk/graphql-transport/CHANGELOG.md | 7 +++++++ sdk/graphql-transport/package.json | 2 +- sdk/kiosk/CHANGELOG.md | 7 +++++++ sdk/kiosk/package.json | 2 +- sdk/suins-toolkit/CHANGELOG.md | 7 +++++++ sdk/suins-toolkit/package.json | 2 +- sdk/typescript/CHANGELOG.md | 8 ++++++++ sdk/typescript/package.json | 2 +- sdk/typescript/src/version.ts | 2 +- sdk/wallet-standard/CHANGELOG.md | 7 +++++++ sdk/wallet-standard/package.json | 2 +- sdk/zklogin/CHANGELOG.md | 7 +++++++ sdk/zklogin/package.json | 2 +- sdk/zksend/CHANGELOG.md | 8 ++++++++ sdk/zksend/package.json | 2 +- 26 files changed, 103 insertions(+), 18 deletions(-) delete mode 100644 .changeset/seven-pets-count.md diff --git a/.changeset/seven-pets-count.md b/.changeset/seven-pets-count.md deleted file mode 100644 index c5f6d407ac158..0000000000000 --- a/.changeset/seven-pets-count.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@mysten/sui': minor ---- - -`WaitForLocalExecution` now waits using client.waitForTransaction rather than sending requestType to the RPC node. This change will preserve readAfterWrite consistency when local execution is removed from fullnodes, at the cost of more network requests and higher latency. diff --git a/sdk/create-dapp/CHANGELOG.md b/sdk/create-dapp/CHANGELOG.md index 6be6c02ea471d..2a4eca5756568 100644 --- a/sdk/create-dapp/CHANGELOG.md +++ b/sdk/create-dapp/CHANGELOG.md @@ -1,5 +1,13 @@ # @mysten/create-dapp +## 0.3.16 + +### Patch Changes + +- Updated dependencies [a3e32fe] + - @mysten/sui@1.6.0 + - @mysten/dapp-kit@0.14.16 + ## 0.3.15 ### Patch Changes diff --git a/sdk/create-dapp/package.json b/sdk/create-dapp/package.json index cd056e2674ecf..fc0aaf9d83d75 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.15", + "version": "0.3.16", "license": "Apache-2.0", "files": [ "CHANGELOG.md", diff --git a/sdk/dapp-kit/CHANGELOG.md b/sdk/dapp-kit/CHANGELOG.md index 4a84bce2afdc8..325c408779cf6 100644 --- a/sdk/dapp-kit/CHANGELOG.md +++ b/sdk/dapp-kit/CHANGELOG.md @@ -1,5 +1,14 @@ # @mysten/dapp-kit +## 0.14.16 + +### Patch Changes + +- Updated dependencies [a3e32fe] + - @mysten/sui@1.6.0 + - @mysten/wallet-standard@0.13.1 + - @mysten/zksend@0.10.5 + ## 0.14.15 ### Patch Changes diff --git a/sdk/dapp-kit/package.json b/sdk/dapp-kit/package.json index bd53555902844..c08e0a972f9a0 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.15", + "version": "0.14.16", "license": "Apache-2.0", "files": [ "CHANGELOG.md", diff --git a/sdk/deepbook-v3/CHANGELOG.md b/sdk/deepbook-v3/CHANGELOG.md index 2dd8404416161..a029c48c94577 100644 --- a/sdk/deepbook-v3/CHANGELOG.md +++ b/sdk/deepbook-v3/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/deepbook-v3 +## 0.2.1 + +### Patch Changes + +- Updated dependencies [a3e32fe] + - @mysten/sui@1.6.0 + ## 0.2.0 ### Minor Changes diff --git a/sdk/deepbook-v3/package.json b/sdk/deepbook-v3/package.json index b21c2db8dbe0f..7c95c5f0bf1fd 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.2.0", + "version": "0.2.1", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", diff --git a/sdk/deepbook/CHANGELOG.md b/sdk/deepbook/CHANGELOG.md index f2bb6f521eb79..1d346a039b693 100644 --- a/sdk/deepbook/CHANGELOG.md +++ b/sdk/deepbook/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/deepbook +## 0.8.15 + +### Patch Changes + +- Updated dependencies [a3e32fe] + - @mysten/sui@1.6.0 + ## 0.8.14 ### Patch Changes diff --git a/sdk/deepbook/package.json b/sdk/deepbook/package.json index b493663de506b..c11789a3e9f34 100644 --- a/sdk/deepbook/package.json +++ b/sdk/deepbook/package.json @@ -2,7 +2,7 @@ "name": "@mysten/deepbook", "author": "Mysten Labs ", "description": "Sui Deepbook SDK", - "version": "0.8.14", + "version": "0.8.15", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", diff --git a/sdk/enoki/CHANGELOG.md b/sdk/enoki/CHANGELOG.md index b0450ea215835..50929112b623f 100644 --- a/sdk/enoki/CHANGELOG.md +++ b/sdk/enoki/CHANGELOG.md @@ -1,5 +1,13 @@ # @mysten/enoki +## 0.3.16 + +### Patch Changes + +- Updated dependencies [a3e32fe] + - @mysten/sui@1.6.0 + - @mysten/zklogin@0.7.16 + ## 0.3.15 ### Patch Changes diff --git a/sdk/enoki/package.json b/sdk/enoki/package.json index a7f427a4cd115..26543cb1d6966 100644 --- a/sdk/enoki/package.json +++ b/sdk/enoki/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/enoki", - "version": "0.3.15", + "version": "0.3.16", "description": "TODO: Description", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/graphql-transport/CHANGELOG.md b/sdk/graphql-transport/CHANGELOG.md index 03699d2c3810b..f6fa15b284900 100644 --- a/sdk/graphql-transport/CHANGELOG.md +++ b/sdk/graphql-transport/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/graphql-transport +## 0.2.15 + +### Patch Changes + +- Updated dependencies [a3e32fe] + - @mysten/sui@1.6.0 + ## 0.2.14 ### Patch Changes diff --git a/sdk/graphql-transport/package.json b/sdk/graphql-transport/package.json index c073c39d8dd05..7b396110e747a 100644 --- a/sdk/graphql-transport/package.json +++ b/sdk/graphql-transport/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/graphql-transport", - "version": "0.2.14", + "version": "0.2.15", "description": "A GraphQL transport to allow SuiClient to work with RPC 2.0", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/kiosk/CHANGELOG.md b/sdk/kiosk/CHANGELOG.md index 6a70a56716abe..1993ef4851266 100644 --- a/sdk/kiosk/CHANGELOG.md +++ b/sdk/kiosk/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/kiosk +## 0.9.15 + +### Patch Changes + +- Updated dependencies [a3e32fe] + - @mysten/sui@1.6.0 + ## 0.9.14 ### Patch Changes diff --git a/sdk/kiosk/package.json b/sdk/kiosk/package.json index 780eeb46433c2..1a529d482e0d7 100644 --- a/sdk/kiosk/package.json +++ b/sdk/kiosk/package.json @@ -2,7 +2,7 @@ "name": "@mysten/kiosk", "author": "Mysten Labs ", "description": "Sui Kiosk library", - "version": "0.9.14", + "version": "0.9.15", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", diff --git a/sdk/suins-toolkit/CHANGELOG.md b/sdk/suins-toolkit/CHANGELOG.md index 177ff6486d001..a7e448c820f74 100644 --- a/sdk/suins-toolkit/CHANGELOG.md +++ b/sdk/suins-toolkit/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/suins-toolkit +## 0.5.15 + +### Patch Changes + +- Updated dependencies [a3e32fe] + - @mysten/sui@1.6.0 + ## 0.5.14 ### Patch Changes diff --git a/sdk/suins-toolkit/package.json b/sdk/suins-toolkit/package.json index 6cbe8f4048b94..2330823f46b50 100644 --- a/sdk/suins-toolkit/package.json +++ b/sdk/suins-toolkit/package.json @@ -2,7 +2,7 @@ "name": "@mysten/suins-toolkit", "author": "Mysten Labs ", "description": "SuiNS TypeScript SDK", - "version": "0.5.14", + "version": "0.5.15", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", diff --git a/sdk/typescript/CHANGELOG.md b/sdk/typescript/CHANGELOG.md index c0387fd12f3d2..df8b8bcb3e706 100644 --- a/sdk/typescript/CHANGELOG.md +++ b/sdk/typescript/CHANGELOG.md @@ -1,5 +1,13 @@ # @mysten/sui.js +## 1.6.0 + +### Minor Changes + +- a3e32fe: `WaitForLocalExecution` now waits using client.waitForTransaction rather than sending + requestType to the RPC node. This change will preserve readAfterWrite consistency when local + execution is removed from fullnodes, at the cost of more network requests and higher latency. + ## 1.5.0 ### Minor Changes diff --git a/sdk/typescript/package.json b/sdk/typescript/package.json index 92a8627c1d19f..bfe5f446379d0 100644 --- a/sdk/typescript/package.json +++ b/sdk/typescript/package.json @@ -3,7 +3,7 @@ "author": "Mysten Labs ", "description": "Sui TypeScript API(Work in Progress)", "homepage": "https://sdk.mystenlabs.com", - "version": "1.5.0", + "version": "1.6.0", "license": "Apache-2.0", "sideEffects": false, "files": [ diff --git a/sdk/typescript/src/version.ts b/sdk/typescript/src/version.ts index 3286cbdb6ae9e..3145c701257e6 100644 --- a/sdk/typescript/src/version.ts +++ b/sdk/typescript/src/version.ts @@ -3,5 +3,5 @@ // This file is generated by genversion.mjs. Do not edit it directly. -export const PACKAGE_VERSION = '1.5.0'; +export const PACKAGE_VERSION = '1.6.0'; export const TARGETED_RPC_VERSION = '1.32.0'; diff --git a/sdk/wallet-standard/CHANGELOG.md b/sdk/wallet-standard/CHANGELOG.md index 058d65fbd3db3..528b98fbf37e3 100644 --- a/sdk/wallet-standard/CHANGELOG.md +++ b/sdk/wallet-standard/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/wallet-standard +## 0.13.1 + +### Patch Changes + +- Updated dependencies [a3e32fe] + - @mysten/sui@1.6.0 + ## 0.13.0 ### Minor Changes diff --git a/sdk/wallet-standard/package.json b/sdk/wallet-standard/package.json index 7247acdd51532..de8f30e2cf204 100644 --- a/sdk/wallet-standard/package.json +++ b/sdk/wallet-standard/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/wallet-standard", - "version": "0.13.0", + "version": "0.13.1", "description": "A suite of standard utilities for implementing wallets based on the Wallet Standard.", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/zklogin/CHANGELOG.md b/sdk/zklogin/CHANGELOG.md index c22888a088104..df730218b15fb 100644 --- a/sdk/zklogin/CHANGELOG.md +++ b/sdk/zklogin/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/zklogin +## 0.7.16 + +### Patch Changes + +- Updated dependencies [a3e32fe] + - @mysten/sui@1.6.0 + ## 0.7.15 ### Patch Changes diff --git a/sdk/zklogin/package.json b/sdk/zklogin/package.json index d8bf3881e86ff..0b0a942bcf6fe 100644 --- a/sdk/zklogin/package.json +++ b/sdk/zklogin/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/zklogin", - "version": "0.7.15", + "version": "0.7.16", "description": "Utilities for interacting with zkLogin in Sui", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/zksend/CHANGELOG.md b/sdk/zksend/CHANGELOG.md index a5579f1de1fa8..f412d4e578f58 100644 --- a/sdk/zksend/CHANGELOG.md +++ b/sdk/zksend/CHANGELOG.md @@ -1,5 +1,13 @@ # @mysten/zksend +## 0.10.5 + +### Patch Changes + +- Updated dependencies [a3e32fe] + - @mysten/sui@1.6.0 + - @mysten/wallet-standard@0.13.1 + ## 0.10.4 ### Patch Changes diff --git a/sdk/zksend/package.json b/sdk/zksend/package.json index dbc811d9bf12f..f83f941144117 100644 --- a/sdk/zksend/package.json +++ b/sdk/zksend/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/zksend", - "version": "0.10.4", + "version": "0.10.5", "description": "TODO: Write Description", "license": "Apache-2.0", "author": "Mysten Labs ", From 9c4652f3e3663e5c9a08605154cdd4a6b17860f7 Mon Sep 17 00:00:00 2001 From: Tim Zakian <2895723+tzakian@users.noreply.github.com> Date: Tue, 13 Aug 2024 18:42:33 -0700 Subject: [PATCH 113/232] [move] Add further support for enums in Move model (#18980) --- .../move-docgen/tests/sources/enums_test.move | 19 +++- .../tests/sources/enums_test.spec_inline.md | 93 ++++++++++++++++++- .../sources/enums_test.spec_inline_no_fold.md | 83 ++++++++++++++++- .../tests/sources/enums_test.spec_separate.md | 93 ++++++++++++++++++- .../move-model/src/builder/exp_translator.rs | 2 +- .../crates/move-model/src/exp_generator.rs | 2 +- .../move/crates/move-model/src/model.rs | 19 +++- .../move/crates/move-model/src/ty.rs | 48 +++------- 8 files changed, 310 insertions(+), 49 deletions(-) diff --git a/external-crates/move/crates/move-docgen/tests/sources/enums_test.move b/external-crates/move/crates/move-docgen/tests/sources/enums_test.move index 1433eedc2013d..b85b53d003cbf 100644 --- a/external-crates/move/crates/move-docgen/tests/sources/enums_test.move +++ b/external-crates/move/crates/move-docgen/tests/sources/enums_test.move @@ -2,7 +2,7 @@ #[allow(unused)] module 0x42::m { /// This is a doc comment above an enum - public enum Enum { + public enum Enum has drop { /// This is a doc comment above a variant A, B(), @@ -20,7 +20,7 @@ module 0x42::m { B, } - public struct X { x: Enum } + public struct X has drop { x: Enum } public struct Y(Enum) public struct XG { x: GenericEnum } @@ -28,5 +28,20 @@ module 0x42::m { public struct XGG { x: GenericEnum } public struct YGG(GenericEnum) + + public struct VecMap has copy, drop, store { + contents: vector>, + } + + /// An entry in the map + public struct Entry has copy, drop, store { + key: K, + value: V, + } + + /// Doc comments `type_: VecMap` + fun f(x: VecMap): u64 { + 0 + } } diff --git a/external-crates/move/crates/move-docgen/tests/sources/enums_test.spec_inline.md b/external-crates/move/crates/move-docgen/tests/sources/enums_test.spec_inline.md index 5ccaaff903e05..38cd3f13aaa3d 100644 --- a/external-crates/move/crates/move-docgen/tests/sources/enums_test.spec_inline.md +++ b/external-crates/move/crates/move-docgen/tests/sources/enums_test.spec_inline.md @@ -12,8 +12,11 @@ This is a doc comment above an annotation. - [Struct `YG`](#0x42_m_YG) - [Struct `XGG`](#0x42_m_XGG) - [Struct `YGG`](#0x42_m_YGG) +- [Struct `VecMap`](#0x42_m_VecMap) +- [Struct `Entry`](#0x42_m_Entry) - [Enum `Enum`](#0x42_m_Enum) - [Enum `GenericEnum`](#0x42_m_GenericEnum) +- [Function `f`](#0x42_m_f)
@@ -26,7 +29,7 @@ This is a doc comment above an annotation. -
struct X
+
struct X has drop
 
@@ -180,6 +183,67 @@ This is a doc comment above an annotation. + + + + +## Struct `VecMap` + + + +
struct VecMap<K: copy, V> has copy, drop, store
+
+ + + +
+Fields + + +
+
+contents: vector<m::Entry<K, V>> +
+
+ +
+
+ + +
+ + + +## Struct `Entry` + +An entry in the map + + +
struct Entry<K: copy, V> has copy, drop, store
+
+ + + +
+Fields + + +
+
+key: K +
+
+ +
+
+value: V +
+
+ +
+
+ +
@@ -189,7 +253,7 @@ This is a doc comment above an annotation. This is a doc comment above an enum -
public enum Enum
+
public enum Enum has drop
 
@@ -315,4 +379,29 @@ Variant B + + + + +## Function `f` + +Doc comments type_: VecMap<u64, X> + + +
fun f(x: m::VecMap<u64, m::X>): u64
+
+ + + +
+Implementation + + +
fun f(x: VecMap<u64, X>): u64 {
+    0
+}
+
+ + +
diff --git a/external-crates/move/crates/move-docgen/tests/sources/enums_test.spec_inline_no_fold.md b/external-crates/move/crates/move-docgen/tests/sources/enums_test.spec_inline_no_fold.md index 6267f7db53ec4..f761ecf936c3b 100644 --- a/external-crates/move/crates/move-docgen/tests/sources/enums_test.spec_inline_no_fold.md +++ b/external-crates/move/crates/move-docgen/tests/sources/enums_test.spec_inline_no_fold.md @@ -12,8 +12,11 @@ This is a doc comment above an annotation. - [Struct `YG`](#0x42_m_YG) - [Struct `XGG`](#0x42_m_XGG) - [Struct `YGG`](#0x42_m_YGG) +- [Struct `VecMap`](#0x42_m_VecMap) +- [Struct `Entry`](#0x42_m_Entry) - [Enum `Enum`](#0x42_m_Enum) - [Enum `GenericEnum`](#0x42_m_GenericEnum) +- [Function `f`](#0x42_m_f)
@@ -26,7 +29,7 @@ This is a doc comment above an annotation. -
struct X
+
struct X has drop
 
@@ -164,6 +167,61 @@ This is a doc comment above an annotation. + + +## Struct `VecMap` + + + +
struct VecMap<K: copy, V> has copy, drop, store
+
+ + + +##### Fields + + +
+
+contents: vector<m::Entry<K, V>> +
+
+ +
+
+ + + + +## Struct `Entry` + +An entry in the map + + +
struct Entry<K: copy, V> has copy, drop, store
+
+ + + +##### Fields + + +
+
+key: K +
+
+ +
+
+value: V +
+
+ +
+
+ + ## Enum `Enum` @@ -171,7 +229,7 @@ This is a doc comment above an annotation. This is a doc comment above an enum -
public enum Enum
+
public enum Enum has drop
 
@@ -291,3 +349,24 @@ Variant B + + + + +## Function `f` + +Doc comments type_: VecMap<u64, X> + + +
fun f(x: m::VecMap<u64, m::X>): u64
+
+ + + +##### Implementation + + +
fun f(x: VecMap<u64, X>): u64 {
+    0
+}
+
diff --git a/external-crates/move/crates/move-docgen/tests/sources/enums_test.spec_separate.md b/external-crates/move/crates/move-docgen/tests/sources/enums_test.spec_separate.md index 5ccaaff903e05..38cd3f13aaa3d 100644 --- a/external-crates/move/crates/move-docgen/tests/sources/enums_test.spec_separate.md +++ b/external-crates/move/crates/move-docgen/tests/sources/enums_test.spec_separate.md @@ -12,8 +12,11 @@ This is a doc comment above an annotation. - [Struct `YG`](#0x42_m_YG) - [Struct `XGG`](#0x42_m_XGG) - [Struct `YGG`](#0x42_m_YGG) +- [Struct `VecMap`](#0x42_m_VecMap) +- [Struct `Entry`](#0x42_m_Entry) - [Enum `Enum`](#0x42_m_Enum) - [Enum `GenericEnum`](#0x42_m_GenericEnum) +- [Function `f`](#0x42_m_f)
@@ -26,7 +29,7 @@ This is a doc comment above an annotation. -
struct X
+
struct X has drop
 
@@ -180,6 +183,67 @@ This is a doc comment above an annotation. + + + + +## Struct `VecMap` + + + +
struct VecMap<K: copy, V> has copy, drop, store
+
+ + + +
+Fields + + +
+
+contents: vector<m::Entry<K, V>> +
+
+ +
+
+ + +
+ + + +## Struct `Entry` + +An entry in the map + + +
struct Entry<K: copy, V> has copy, drop, store
+
+ + + +
+Fields + + +
+
+key: K +
+
+ +
+
+value: V +
+
+ +
+
+ +
@@ -189,7 +253,7 @@ This is a doc comment above an annotation. This is a doc comment above an enum -
public enum Enum
+
public enum Enum has drop
 
@@ -315,4 +379,29 @@ Variant B + + + + +## Function `f` + +Doc comments type_: VecMap<u64, X> + + +
fun f(x: m::VecMap<u64, m::X>): u64
+
+ + + +
+Implementation + + +
fun f(x: VecMap<u64, X>): u64 {
+    0
+}
+
+ + +
diff --git a/external-crates/move/crates/move-model/src/builder/exp_translator.rs b/external-crates/move/crates/move-model/src/builder/exp_translator.rs index b6538796856a3..cba675c90343c 100644 --- a/external-crates/move/crates/move-model/src/builder/exp_translator.rs +++ b/external-crates/move/crates/move-model/src/builder/exp_translator.rs @@ -132,7 +132,7 @@ impl<'env, 'translator, 'module_translator> ExpTranslator<'env, 'translator, 'mo fn type_display_context(&self) -> TypeDisplayContext<'_> { TypeDisplayContext::WithoutEnv { symbol_pool: self.symbol_pool(), - reverse_struct_table: &self.parent.parent.reverse_datatype_table, + reverse_datatype_table: &self.parent.parent.reverse_datatype_table, } } diff --git a/external-crates/move/crates/move-model/src/exp_generator.rs b/external-crates/move/crates/move-model/src/exp_generator.rs index d1334af6f8d51..007a657960a9b 100644 --- a/external-crates/move/crates/move-model/src/exp_generator.rs +++ b/external-crates/move/crates/move-model/src/exp_generator.rs @@ -74,7 +74,7 @@ pub trait ExpGenerator<'env> { fn get_memory_of_node(&self, node_id: NodeId) -> QualifiedInstId { // We do have a call `f>` so extract the type from the function instantiation. let rty = &self.global_env().get_node_instantiation(node_id)[0]; - let (mid, sid, inst) = rty.require_struct(); + let (mid, sid, inst) = rty.require_datatype(); mid.qualified_inst(sid, inst.to_owned()) } } diff --git a/external-crates/move/crates/move-model/src/model.rs b/external-crates/move/crates/move-model/src/model.rs index b21f37d3d2d40..859be6bf46cc8 100644 --- a/external-crates/move/crates/move-model/src/model.rs +++ b/external-crates/move/crates/move-model/src/model.rs @@ -1121,7 +1121,7 @@ impl GlobalEnv { } /// Gets a StructEnv in this module by its `StructTag` - pub fn find_struct_by_tag( + pub fn find_datatype_by_tag( &self, tag: &language_storage::StructTag, ) -> Option> { @@ -1129,6 +1129,10 @@ impl GlobalEnv { .and_then(|menv| { menv.find_struct_by_identifier(tag.name.clone()) .map(|sid| menv.get_id().qualified(sid)) + .or_else(|| { + menv.find_enum_by_identifier(tag.name.clone()) + .map(|sid| menv.get_id().qualified(sid)) + }) }) } @@ -1242,16 +1246,23 @@ impl GlobalEnv { sid: DatatypeId, ts: &[Type], ) -> Option { - self.get_struct_type(mid, sid, ts)?.into_struct_tag() + self.get_datatype(mid, sid, ts)?.into_struct_tag() } /// Attempt to compute a struct type for (`mid`, `sid`, `ts`). - pub fn get_struct_type(&self, mid: ModuleId, sid: DatatypeId, ts: &[Type]) -> Option { + pub fn get_datatype(&self, mid: ModuleId, sid: DatatypeId, ts: &[Type]) -> Option { let menv = self.get_module(mid); + let name = menv + .find_struct(sid.symbol()) + .map(|senv| senv.get_identifier()) + .or_else(|| { + menv.find_enum(sid.symbol()) + .map(|eenv| eenv.get_identifier()) + })??; Some(MType::Struct { address: *menv.self_address(), module: menv.get_identifier(), - name: menv.get_struct(sid).get_identifier()?, + name, type_arguments: ts .iter() .map(|t| t.clone().into_normalized_type(self).unwrap()) diff --git a/external-crates/move/crates/move-model/src/ty.rs b/external-crates/move/crates/move-model/src/ty.rs index 50e9e201495c6..8083e4eaa7119 100644 --- a/external-crates/move/crates/move-model/src/ty.rs +++ b/external-crates/move/crates/move-model/src/ty.rs @@ -15,7 +15,7 @@ use move_core_types::language_storage::{StructTag, TypeTag}; use crate::{ ast::QualifiedSymbol, - model::{DatatypeId, GlobalEnv, ModuleId, QualifiedInstId, StructEnv}, + model::{DatatypeId, GlobalEnv, ModuleId}, symbol::{Symbol, SymbolPool}, }; @@ -233,38 +233,16 @@ impl Type { } } - /// If this is a struct type, replace the type instantiation. - pub fn replace_struct_instantiation(&self, inst: &[Type]) -> Type { + /// If this is a datatype, replace the type instantiation. + pub fn replace_datatype_instantiation(&self, inst: &[Type]) -> Type { match self { Type::Datatype(mid, sid, _) => Type::Datatype(*mid, *sid, inst.to_vec()), _ => self.clone(), } } - /// If this is a struct type, return the associated struct env and type parameters. - pub fn get_struct<'env>( - &'env self, - env: &'env GlobalEnv, - ) -> Option<(StructEnv<'env>, &'env [Type])> { - if let Type::Datatype(module_idx, struct_idx, params) = self { - Some((env.get_module(*module_idx).into_struct(*struct_idx), params)) - } else { - None - } - } - - /// If this is a struct type, return the associated QualifiedInstId. - pub fn get_struct_id(&self, env: &GlobalEnv) -> Option> { - self.get_struct(env).map(|(se, inst)| { - se.module_env - .get_id() - .qualified(se.get_id()) - .instantiate(inst.to_vec()) - }) - } - - /// Require this to be a struct, if so extracts its content. - pub fn require_struct(&self) -> (ModuleId, DatatypeId, &[Type]) { + /// Require this to be a datatype, if so extracts its content. + pub fn require_datatype(&self) -> (ModuleId, DatatypeId, &[Type]) { if let Type::Datatype(mid, sid, targs) = self { (*mid, *sid, targs.as_slice()) } else { @@ -414,10 +392,10 @@ impl Type { } /// Attempt to convert this type into a normalized::Type - pub fn into_struct_type(self, env: &GlobalEnv) -> Option { + pub fn into_datatype_ty(self, env: &GlobalEnv) -> Option { use Type::*; match self { - Datatype(mid, sid, ts) => env.get_struct_type(mid, sid, &ts), + Datatype(mid, sid, ts) => env.get_datatype(mid, sid, &ts), _ => None, } } @@ -428,7 +406,7 @@ impl Type { match self { Primitive(p) => Some(p.into_normalized_type().expect("Invariant violation: unexpected spec primitive")), Datatype(mid, sid, ts) => - env.get_struct_type(mid, sid, &ts), + env.get_datatype(mid, sid, &ts), Vector(et) => Some(MType::Vector( Box::new(et.into_normalized_type(env) .expect("Invariant violation: vector type argument contains incomplete, tuple, or spec type")) @@ -447,7 +425,7 @@ impl Type { /// Attempt to convert this type into a language_storage::StructTag pub fn into_struct_tag(self, env: &GlobalEnv) -> Option { - self.into_struct_type(env)?.into_struct_tag() + self.into_datatype_ty(env)?.into_struct_tag() } /// Attempt to convert this type into a language_storage::TypeTag @@ -469,8 +447,8 @@ impl Type { TypeTag::Address => Primitive(PrimitiveType::Address), TypeTag::Signer => Primitive(PrimitiveType::Signer), TypeTag::Struct(s) => { - let qid = env.find_struct_by_tag(s).unwrap_or_else(|| { - panic!("Invariant violation: couldn't resolve struct {:?}", s) + let qid = env.find_datatype_by_tag(s).unwrap_or_else(|| { + panic!("Invariant violation: couldn't resolve datatype {:?}", s) }); let type_args = s .type_params @@ -1159,7 +1137,7 @@ impl TypeInstantiationDerivation { pub enum TypeDisplayContext<'a> { WithoutEnv { symbol_pool: &'a SymbolPool, - reverse_struct_table: &'a BTreeMap<(ModuleId, DatatypeId), QualifiedSymbol>, + reverse_datatype_table: &'a BTreeMap<(ModuleId, DatatypeId), QualifiedSymbol>, }, WithEnv { env: &'a GlobalEnv, @@ -1273,7 +1251,7 @@ impl<'a> TypeDisplay<'a> { match self.context { TypeDisplayContext::WithoutEnv { symbol_pool, - reverse_struct_table, + reverse_datatype_table: reverse_struct_table, } => { if let Some(sym) = reverse_struct_table.get(&(mid, sid)) { sym.display(symbol_pool).to_string() From 542edc8764cd10dca97f8c0c1806227dcf9b03eb Mon Sep 17 00:00:00 2001 From: William Smith Date: Wed, 14 Aug 2024 11:49:23 -0400 Subject: [PATCH 114/232] [TrafficControl] Handle invalid client sig on rpc node (#18979) --- crates/sui-core/src/authority_server.rs | 2 +- .../tests/traffic_control_tests.rs | 58 +++++++++++++++++++ crates/sui-json-rpc/src/axum_router.rs | 3 + 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/crates/sui-core/src/authority_server.rs b/crates/sui-core/src/authority_server.rs index b61975ef79d40..cb819f18a1a16 100644 --- a/crates/sui-core/src/authority_server.rs +++ b/crates/sui-core/src/authority_server.rs @@ -1083,7 +1083,7 @@ fn make_tonic_request_for_testing(message: T) -> tonic::Request { // TODO: refine error matching here fn normalize(err: SuiError) -> Weight { - match dbg!(err) { + match err { SuiError::UserInputError { .. } | SuiError::InvalidSignature { .. } | SuiError::SignerSignatureAbsent { .. } diff --git a/crates/sui-e2e-tests/tests/traffic_control_tests.rs b/crates/sui-e2e-tests/tests/traffic_control_tests.rs index 1c4abc4ba8636..4ae24ddcc7ea6 100644 --- a/crates/sui-e2e-tests/tests/traffic_control_tests.rs +++ b/crates/sui-e2e-tests/tests/traffic_control_tests.rs @@ -5,6 +5,7 @@ //! they should nearly all be tokio::test rather than simtest. use core::panic; +use fastcrypto::encoding::Base64; use jsonrpsee::{ core::{client::ClientT, RpcResult}, rpc_params, @@ -316,6 +317,63 @@ async fn test_fullnode_traffic_control_spam_blocked() -> Result<(), anyhow::Erro panic!("Expected spam policy to trigger within {txn_count} requests"); } +#[tokio::test] +async fn test_fullnode_traffic_control_error_blocked() -> Result<(), anyhow::Error> { + let txn_count = 5; + let policy_config = PolicyConfig { + connection_blocklist_ttl_sec: 3, + error_policy_type: PolicyType::TestNConnIP(txn_count - 1), + dry_run: false, + ..Default::default() + }; + let test_cluster = TestClusterBuilder::new() + .with_fullnode_policy_config(Some(policy_config)) + .build() + .await; + + let jsonrpc_client = &test_cluster.fullnode_handle.rpc_client; + let context = test_cluster.wallet; + + let mut txns = batch_make_transfer_transactions(&context, txn_count as usize).await; + assert!( + txns.len() >= txn_count as usize, + "Expect at least {} txns. Do we generate enough gas objects during genesis?", + txn_count, + ); + + // it should take no more than 4 requests to be added to the blocklist + for _ in 0..txn_count { + let txn = txns.swap_remove(0); + let tx_digest = txn.digest(); + let (tx_bytes, _signatures) = txn.to_tx_bytes_and_signatures(); + // create invalid (empty) client signature + let signatures: Vec = vec![]; + let params = rpc_params![ + tx_bytes, + signatures, + SuiTransactionBlockResponseOptions::new(), + ExecuteTransactionRequestType::WaitForLocalExecution + ]; + let response: RpcResult = jsonrpc_client + .request("sui_executeTransactionBlock", params.clone()) + .await; + if let Err(err) = response { + if err.to_string().contains("Too many requests") { + return Ok(()); + } + } else { + let SuiTransactionBlockResponse { + digest, + confirmed_local_execution, + .. + } = response.unwrap(); + assert_eq!(&digest, tx_digest); + assert!(confirmed_local_execution.unwrap()); + } + } + panic!("Expected spam policy to trigger within {txn_count} requests"); +} + #[tokio::test] async fn test_validator_traffic_control_error_delegated() -> Result<(), anyhow::Error> { let n = 5; diff --git a/crates/sui-json-rpc/src/axum_router.rs b/crates/sui-json-rpc/src/axum_router.rs index 8ddebab687b2c..0fb6be835c835 100644 --- a/crates/sui-json-rpc/src/axum_router.rs +++ b/crates/sui-json-rpc/src/axum_router.rs @@ -24,6 +24,7 @@ use serde_json::value::RawValue; use sui_core::traffic_controller::{ metrics::TrafficControllerMetrics, policies::TrafficTally, TrafficController, }; +use sui_json_rpc_api::TRANSACTION_EXECUTION_CLIENT_ERROR_CODE; use sui_types::traffic_control::ClientIdSource; use sui_types::traffic_control::{PolicyConfig, Weight}; use tracing::error; @@ -278,6 +279,8 @@ fn handle_traffic_resp( fn normalize(err: ErrorCode) -> Weight { match err { ErrorCode::InvalidRequest | ErrorCode::InvalidParams => Weight::one(), + // e.g. invalid client signature + ErrorCode::ServerError(i) if i == TRANSACTION_EXECUTION_CLIENT_ERROR_CODE => Weight::one(), _ => Weight::zero(), } } From 84908a391478ee745c82e892f2db13ee1f76b121 Mon Sep 17 00:00:00 2001 From: Xun Li Date: Wed, 14 Aug 2024 08:55:08 -0700 Subject: [PATCH 115/232] [indexer] Simplify IndexedObject (#18981) ## Description All the other fields can be derived from Object. Remove them. ## Test plan CI --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-indexer/src/models/objects.rs | 53 ++++++++++++------- .../sui-indexer/src/store/pg_indexer_store.rs | 6 +-- crates/sui-indexer/src/types.rs | 31 ++--------- 3 files changed, 40 insertions(+), 50 deletions(-) diff --git a/crates/sui-indexer/src/models/objects.rs b/crates/sui-indexer/src/models/objects.rs index a6c74f19e0ce9..03f9cc3c81299 100644 --- a/crates/sui-indexer/src/models/objects.rs +++ b/crates/sui-indexer/src/models/objects.rs @@ -19,7 +19,7 @@ use sui_types::object::ObjectRead; use crate::errors::IndexerError; use crate::schema::{objects, objects_history, objects_snapshot}; -use crate::types::{IndexedDeletedObject, IndexedObject, ObjectStatus}; +use crate::types::{owner_to_owner_info, IndexedDeletedObject, IndexedObject, ObjectStatus}; #[derive(Queryable)] pub struct DynamicFieldColumn { @@ -229,30 +229,43 @@ impl From for StoredDeletedHistoryObject { impl From for StoredObject { fn from(o: IndexedObject) -> Self { + let IndexedObject { + checkpoint_sequence_number, + object, + df_info, + } = o; + let (owner_type, owner_id) = owner_to_owner_info(&object.owner); + let coin_type = object + .coin_type_maybe() + .map(|t| t.to_canonical_string(/* with_prefix */ true)); + let coin_balance = if coin_type.is_some() { + Some(object.get_coin_value_unsafe()) + } else { + None + }; Self { - object_id: o.object_id.to_vec(), - object_version: o.object_version as i64, - object_digest: o.object_digest.into_inner().to_vec(), - checkpoint_sequence_number: o.checkpoint_sequence_number as i64, - owner_type: o.owner_type as i16, - owner_id: o.owner_id.map(|id| id.to_vec()), - object_type: o - .object + object_id: object.id().to_vec(), + object_version: object.version().value() as i64, + object_digest: object.digest().into_inner().to_vec(), + checkpoint_sequence_number: checkpoint_sequence_number as i64, + owner_type: owner_type as i16, + owner_id: owner_id.map(|id| id.to_vec()), + object_type: object .type_() .map(|t| t.to_canonical_string(/* with_prefix */ true)), - object_type_package: o.object.type_().map(|t| t.address().to_vec()), - object_type_module: o.object.type_().map(|t| t.module().to_string()), - object_type_name: o.object.type_().map(|t| t.name().to_string()), - serialized_object: bcs::to_bytes(&o.object).unwrap(), - coin_type: o.coin_type, - coin_balance: o.coin_balance.map(|b| b as i64), - df_kind: o.df_info.as_ref().map(|k| match k.type_ { + object_type_package: object.type_().map(|t| t.address().to_vec()), + object_type_module: object.type_().map(|t| t.module().to_string()), + object_type_name: object.type_().map(|t| t.name().to_string()), + serialized_object: bcs::to_bytes(&object).unwrap(), + coin_type, + coin_balance: coin_balance.map(|b| b as i64), + df_kind: df_info.as_ref().map(|k| match k.type_ { DynamicFieldType::DynamicField => 0, DynamicFieldType::DynamicObject => 1, }), - df_name: o.df_info.as_ref().map(|n| bcs::to_bytes(&n.name).unwrap()), - df_object_type: o.df_info.as_ref().map(|v| v.object_type.clone()), - df_object_id: o.df_info.as_ref().map(|v| v.object_id.to_vec()), + df_name: df_info.as_ref().map(|n| bcs::to_bytes(&n.name).unwrap()), + df_object_type: df_info.as_ref().map(|v| v.object_type.clone()), + df_object_id: df_info.as_ref().map(|v| v.object_id.to_vec()), } } } @@ -486,7 +499,7 @@ impl TryFrom for SuiCoin { let balance = o .coin_balance .ok_or(IndexerError::PersistentStorageDataCorruptionError(format!( - "Object {} is supposed to be a coin but has an empy coin_balance column", + "Object {} is supposed to be a coin but has an empty coin_balance column", coin_object_id, )))?; Ok(SuiCoin { diff --git a/crates/sui-indexer/src/store/pg_indexer_store.rs b/crates/sui-indexer/src/store/pg_indexer_store.rs index 39e16c2e394ab..c19fede61bbdf 100644 --- a/crates/sui-indexer/src/store/pg_indexer_store.rs +++ b/crates/sui-indexer/src/store/pg_indexer_store.rs @@ -1773,15 +1773,15 @@ fn make_final_list_of_objects_to_commit( .flat_map(|changes| changes.changed_objects); let mut latest_objects = HashMap::new(); for object in mutated_objects { - if deleted_objects.contains_key(&object.object_id) { + if deleted_objects.contains_key(&object.object.id()) { continue; } - match latest_objects.entry(object.object_id) { + match latest_objects.entry(object.object.id()) { Entry::Vacant(e) => { e.insert(object); } Entry::Occupied(mut e) => { - if object.object_version > e.get().object_version { + if object.object.version() > e.get().object.version() { e.insert(object); } } diff --git a/crates/sui-indexer/src/types.rs b/crates/sui-indexer/src/types.rs index 7eb2c0071ce7f..0d313250e2359 100644 --- a/crates/sui-indexer/src/types.rs +++ b/crates/sui-indexer/src/types.rs @@ -16,7 +16,8 @@ use sui_types::dynamic_field::DynamicFieldInfo; use sui_types::effects::TransactionEffects; use sui_types::event::SystemEpochInfoEvent; use sui_types::messages_checkpoint::{ - CertifiedCheckpointSummary, CheckpointCommitment, CheckpointDigest, EndOfEpochData, + CertifiedCheckpointSummary, CheckpointCommitment, CheckpointDigest, CheckpointSequenceNumber, + EndOfEpochData, }; use sui_types::move_package::MovePackage; use sui_types::object::{Object, Owner}; @@ -282,44 +283,20 @@ pub enum DynamicFieldKind { #[derive(Clone, Debug)] pub struct IndexedObject { - pub object_id: ObjectID, - pub object_version: u64, - pub object_digest: ObjectDigest, - pub checkpoint_sequence_number: u64, - pub owner_type: OwnerType, - pub owner_id: Option, + pub checkpoint_sequence_number: CheckpointSequenceNumber, pub object: Object, - pub coin_type: Option, - pub coin_balance: Option, pub df_info: Option, } impl IndexedObject { pub fn from_object( - checkpoint_sequence_number: u64, + checkpoint_sequence_number: CheckpointSequenceNumber, object: Object, df_info: Option, ) -> Self { - let (owner_type, owner_id) = owner_to_owner_info(&object.owner); - let coin_type = object - .coin_type_maybe() - .map(|t| t.to_canonical_string(/* with_prefix */ true)); - let coin_balance = if coin_type.is_some() { - Some(object.get_coin_value_unsafe()) - } else { - None - }; - Self { checkpoint_sequence_number, - object_id: object.id(), - object_version: object.version().value(), - object_digest: object.digest(), - owner_type, - owner_id, object, - coin_type, - coin_balance, df_info, } } From 918d82a74cb36185b3602a7bb51a9a113c0a50fe Mon Sep 17 00:00:00 2001 From: mwtian <81660174+mwtian@users.noreply.github.com> Date: Wed, 14 Aug 2024 17:58:39 +0100 Subject: [PATCH 116/232] [Consensus] stop commit sync when consensus handler cannot keep up (#18438) ## Description `CommitSyncer` can download more transactions than what consensus handler and execution can process in the same amount of time. This PR adds backpressure to commit syncer based on # of commits not processed by consensus handler. Also, record commit votes from blocks fetched through Synchronizer for completeness. ## Test plan CI Private testnet --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: --- consensus/config/src/parameters.rs | 8 +- .../parameters_test__parameters.snap | 2 +- consensus/core/src/authority_node.rs | 37 +- consensus/core/src/authority_service.rs | 18 +- consensus/core/src/commit.rs | 27 - consensus/core/src/commit_consumer.rs | 74 ++ consensus/core/src/commit_observer.rs | 43 +- consensus/core/src/commit_syncer.rs | 731 +++++++++++------- consensus/core/src/commit_vote_monitor.rs | 118 +++ consensus/core/src/core.rs | 14 +- consensus/core/src/core_thread.rs | 64 +- consensus/core/src/lib.rs | 5 +- consensus/core/src/synchronizer.rs | 123 +-- crates/sui-core/src/consensus_handler.rs | 5 + .../consensus_manager/mysticeti_manager.rs | 6 +- 15 files changed, 811 insertions(+), 464 deletions(-) create mode 100644 consensus/core/src/commit_consumer.rs create mode 100644 consensus/core/src/commit_vote_monitor.rs diff --git a/consensus/config/src/parameters.rs b/consensus/config/src/parameters.rs index 4b3a85906c19a..8c6fa6bf58d26 100644 --- a/consensus/config/src/parameters.rs +++ b/consensus/config/src/parameters.rs @@ -57,8 +57,8 @@ pub struct Parameters { #[serde(default = "Parameters::default_commit_sync_batch_size")] pub commit_sync_batch_size: u32, - // Maximum number of commit batches being fetched, before throttling - // of outgoing commit fetches starts. + // This affects the maximum number of commit batches being fetched, and those fetched but not + // processed as consensus output, before throttling of outgoing commit fetches starts. #[serde(default = "Parameters::default_commit_sync_batches_ahead")] pub commit_sync_batches_ahead: usize, @@ -129,7 +129,9 @@ impl Parameters { } pub(crate) fn default_commit_sync_batches_ahead() -> usize { - 200 + // This is set to be a multiple of default commit_sync_parallel_fetches to allow fetching ahead, + // while keeping the total number of inflight fetches and unprocessed fetched commits limited. + 80 } pub(crate) fn default_sync_last_proposed_block_timeout() -> Duration { diff --git a/consensus/config/tests/snapshots/parameters_test__parameters.snap b/consensus/config/tests/snapshots/parameters_test__parameters.snap index b764571ffe0a2..533ab5ff84c8f 100644 --- a/consensus/config/tests/snapshots/parameters_test__parameters.snap +++ b/consensus/config/tests/snapshots/parameters_test__parameters.snap @@ -15,7 +15,7 @@ max_blocks_per_fetch: 1000 dag_state_cached_rounds: 500 commit_sync_parallel_fetches: 20 commit_sync_batch_size: 100 -commit_sync_batches_ahead: 200 +commit_sync_batches_ahead: 80 anemo: excessive_message_size: 8388608 tonic: diff --git a/consensus/core/src/authority_node.rs b/consensus/core/src/authority_node.rs index 06e57b10c0846..ffd2b421cda35 100644 --- a/consensus/core/src/authority_node.rs +++ b/consensus/core/src/authority_node.rs @@ -7,7 +7,7 @@ use consensus_config::{AuthorityIndex, Committee, NetworkKeyPair, Parameters, Pr use parking_lot::RwLock; use prometheus::Registry; use sui_protocol_config::{ConsensusNetwork, ProtocolConfig}; -use tracing::info; +use tracing::{info, warn}; use crate::{ authority_service::AuthorityService, @@ -15,7 +15,8 @@ use crate::{ block_verifier::SignedBlockVerifier, broadcaster::Broadcaster, commit_observer::CommitObserver, - commit_syncer::{CommitSyncer, CommitVoteMonitor}, + commit_syncer::{CommitSyncer, CommitSyncerHandle}, + commit_vote_monitor::CommitVoteMonitor, context::{Clock, Context}, core::{Core, CoreSignals}, core_thread::{ChannelCoreThreadDispatcher, CoreThreadHandle}, @@ -120,7 +121,7 @@ where start_time: Instant, transaction_client: Arc, synchronizer: Arc, - commit_syncer: CommitSyncer, + commit_syncer_handle: CommitSyncerHandle, leader_timeout_handle: LeaderTimeoutTaskHandle, core_thread_handle: CoreThreadHandle, // Only one of broadcaster and subscriber gets created, depending on @@ -209,6 +210,7 @@ where )) }; + let commit_consumer_monitor = commit_consumer.monitor(); let commit_observer = CommitObserver::new( context.clone(), commit_consumer, @@ -238,6 +240,7 @@ where LeaderTimeoutTask::start(core_dispatcher.clone(), &signals_receivers, context.clone()); let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone())); + let synchronizer = Synchronizer::start( network_client.clone(), context.clone(), @@ -247,14 +250,16 @@ where dag_state.clone(), ); - let commit_syncer = CommitSyncer::new( + let commit_syncer_handle = CommitSyncer::new( context.clone(), core_dispatcher.clone(), commit_vote_monitor.clone(), + commit_consumer_monitor, network_client.clone(), block_verifier.clone(), dag_state.clone(), - ); + ) + .start(); let network_service = Arc::new(AuthorityService::new( context.clone(), @@ -296,7 +301,7 @@ where start_time, transaction_client: Arc::new(tx_client), synchronizer, - commit_syncer, + commit_syncer_handle, leader_timeout_handle, core_thread_handle, broadcaster, @@ -312,8 +317,16 @@ where ); // First shutdown components calling into Core. - self.synchronizer.stop().await.ok(); - self.commit_syncer.stop().await; + if let Err(e) = self.synchronizer.stop().await { + if e.is_panic() { + std::panic::resume_unwind(e.into_panic()); + } + warn!( + "Failed to stop synchronizer when shutting down consensus: {:?}", + e + ); + }; + self.commit_syncer_handle.stop().await; self.leader_timeout_handle.stop().await; // Shutdown Core to stop block productions and broadcast. // When using streaming, all subscribers to broadcasted blocks stop after this. @@ -378,7 +391,7 @@ mod tests { let network_keypair = keypairs[own_index].0.clone(); let (sender, _receiver) = unbounded_channel("consensus_output"); - let commit_consumer = CommitConsumer::new(sender, 0, 0); + let commit_consumer = CommitConsumer::new(sender, 0); let authority = ConsensusAuthority::start( network_type, @@ -468,7 +481,7 @@ mod tests { // Stop authority 1. let index = committee.to_authority_index(1).unwrap(); authorities.remove(index.value()).stop().await; - sleep(Duration::from_secs(15)).await; + sleep(Duration::from_secs(10)).await; // Restart authority 1 and let it run. let (authority, receiver) = make_authority( @@ -481,7 +494,7 @@ mod tests { .await; output_receivers[index] = receiver; authorities.insert(index.value(), authority); - sleep(Duration::from_secs(15)).await; + sleep(Duration::from_secs(10)).await; // Stop all authorities and exit. for authority in authorities { @@ -678,7 +691,7 @@ mod tests { let network_keypair = keypairs[index].0.clone(); let (sender, receiver) = unbounded_channel("consensus_output"); - let commit_consumer = CommitConsumer::new(sender, 0, 0); + let commit_consumer = CommitConsumer::new(sender, 0); let authority = ConsensusAuthority::start( network_type, diff --git a/consensus/core/src/authority_service.rs b/consensus/core/src/authority_service.rs index feac2741570cc..83fd3e173e286 100644 --- a/consensus/core/src/authority_service.rs +++ b/consensus/core/src/authority_service.rs @@ -17,7 +17,7 @@ use crate::{ block::{BlockAPI as _, BlockRef, SignedBlock, VerifiedBlock, GENESIS_ROUND}, block_verifier::BlockVerifier, commit::{CommitAPI as _, CommitRange, TrustedCommit}, - commit_syncer::CommitVoteMonitor, + commit_vote_monitor::CommitVoteMonitor, context::Context, core_thread::CoreThreadDispatcher, dag_state::DagState, @@ -171,7 +171,7 @@ impl NetworkService for AuthorityService { // Observe the block for the commit votes. When local commit is lagging too much, // commit sync loop will trigger fetching. - self.commit_vote_monitor.observe(&verified_block); + self.commit_vote_monitor.observe_block(&verified_block); // Reject blocks when local commit index is lagging too far from quorum commit index. // @@ -567,7 +567,7 @@ mod tests { authority_service::AuthorityService, block::BlockAPI, block::{BlockRef, SignedBlock, TestBlock, VerifiedBlock}, - commit_syncer::CommitVoteMonitor, + commit_vote_monitor::CommitVoteMonitor, context::Context, core_thread::{CoreError, CoreThreadDispatcher}, dag_state::DagState, @@ -689,24 +689,24 @@ mod tests { let (context, _keys) = Context::new_for_test(4); let context = Arc::new(context); let block_verifier = Arc::new(crate::block_verifier::NoopBlockVerifier {}); + let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone())); let core_dispatcher = Arc::new(FakeCoreThreadDispatcher::new()); let (_tx_block_broadcast, rx_block_broadcast) = broadcast::channel(100); let network_client = Arc::new(FakeNetworkClient::default()); let store = Arc::new(MemStore::new()); let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store.clone()))); - let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone())); let synchronizer = Synchronizer::start( network_client, context.clone(), core_dispatcher.clone(), - commit_vote_monitor, + commit_vote_monitor.clone(), block_verifier.clone(), dag_state.clone(), ); let authority_service = Arc::new(AuthorityService::new( context.clone(), block_verifier, - Arc::new(CommitVoteMonitor::new(context.clone())), + commit_vote_monitor, synchronizer, core_dispatcher.clone(), rx_block_broadcast, @@ -747,24 +747,24 @@ mod tests { let (context, _keys) = Context::new_for_test(4); let context = Arc::new(context); let block_verifier = Arc::new(crate::block_verifier::NoopBlockVerifier {}); + let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone())); let core_dispatcher = Arc::new(FakeCoreThreadDispatcher::new()); let (_tx_block_broadcast, rx_block_broadcast) = broadcast::channel(100); let network_client = Arc::new(FakeNetworkClient::default()); let store = Arc::new(MemStore::new()); let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store.clone()))); - let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone())); let synchronizer = Synchronizer::start( network_client, context.clone(), core_dispatcher.clone(), - commit_vote_monitor, + commit_vote_monitor.clone(), block_verifier.clone(), dag_state.clone(), ); let authority_service = Arc::new(AuthorityService::new( context.clone(), block_verifier, - Arc::new(CommitVoteMonitor::new(context.clone())), + commit_vote_monitor, synchronizer, core_dispatcher.clone(), rx_block_broadcast, diff --git a/consensus/core/src/commit.rs b/consensus/core/src/commit.rs index 810fd9eb43b9d..f50d5ba469b89 100644 --- a/consensus/core/src/commit.rs +++ b/consensus/core/src/commit.rs @@ -13,7 +13,6 @@ use bytes::Bytes; use consensus_config::{AuthorityIndex, DefaultHashFunction, DIGEST_LENGTH}; use enum_dispatch::enum_dispatch; use fastcrypto::hash::{Digest, HashFunction as _}; -use mysten_metrics::monitored_mpsc::UnboundedSender; use serde::{Deserialize, Serialize}; use crate::{ @@ -398,32 +397,6 @@ pub fn load_committed_subdag_from_store( ) } -pub struct CommitConsumer { - // A channel to send the committed sub dags through - pub sender: UnboundedSender, - // Leader round of the last commit that the consumer has processed. - pub last_processed_commit_round: Round, - // 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. - // Set 0 to replay from the start (as generated commit sequence starts at index = 1). - pub last_processed_commit_index: CommitIndex, -} - -impl CommitConsumer { - pub fn new( - sender: UnboundedSender, - last_processed_commit_round: Round, - last_processed_commit_index: CommitIndex, - ) -> Self { - Self { - sender, - last_processed_commit_round, - last_processed_commit_index, - } - } -} - #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub(crate) enum Decision { Direct, diff --git a/consensus/core/src/commit_consumer.rs b/consensus/core/src/commit_consumer.rs new file mode 100644 index 0000000000000..47a96d80f0e2a --- /dev/null +++ b/consensus/core/src/commit_consumer.rs @@ -0,0 +1,74 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::sync::{atomic::AtomicU32, Arc}; + +use mysten_metrics::monitored_mpsc::UnboundedSender; + +use crate::{CommitIndex, CommittedSubDag}; + +pub struct CommitConsumer { + // A channel to send the committed sub dags through + pub(crate) 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. + // Set 0 to replay from the start (as generated commit sequence starts at index = 1). + pub(crate) last_processed_commit_index: CommitIndex, + // Allows the commit consumer to report its progress. + monitor: Arc, +} + +impl CommitConsumer { + pub fn new( + sender: UnboundedSender, + last_processed_commit_index: CommitIndex, + ) -> Self { + let monitor = Arc::new(CommitConsumerMonitor::new(last_processed_commit_index)); + Self { + sender, + last_processed_commit_index, + monitor, + } + } + + pub fn monitor(&self) -> Arc { + self.monitor.clone() + } +} + +pub struct CommitConsumerMonitor { + highest_handled_commit: AtomicU32, +} + +impl CommitConsumerMonitor { + pub(crate) fn new(last_handled_commit: CommitIndex) -> Self { + Self { + highest_handled_commit: AtomicU32::new(last_handled_commit), + } + } + + pub(crate) fn highest_handled_commit(&self) -> CommitIndex { + self.highest_handled_commit + .load(std::sync::atomic::Ordering::Acquire) + } + + pub fn set_highest_handled_commit(&self, highest_handled_commit: CommitIndex) { + self.highest_handled_commit + .store(highest_handled_commit, std::sync::atomic::Ordering::Release); + } +} + +#[cfg(test)] +mod test { + use crate::CommitConsumerMonitor; + + #[test] + fn test_commit_consumer_monitor() { + let monitor = CommitConsumerMonitor::new(10); + assert_eq!(monitor.highest_handled_commit(), 10); + + monitor.set_highest_handled_commit(100); + assert_eq!(monitor.highest_handled_commit(), 100); + } +} diff --git a/consensus/core/src/commit_observer.rs b/consensus/core/src/commit_observer.rs index 34cf4871234f0..f5c996687c029 100644 --- a/consensus/core/src/commit_observer.rs +++ b/consensus/core/src/commit_observer.rs @@ -210,11 +210,7 @@ mod tests { use super::*; use crate::{ - block::{BlockRef, Round}, - commit::DEFAULT_WAVE_LENGTH, - context::Context, - dag_state::DagState, - storage::mem_store::MemStore, + block::BlockRef, context::Context, dag_state::DagState, storage::mem_store::MemStore, test_dag_builder::DagBuilder, }; @@ -228,7 +224,6 @@ mod tests { context.clone(), mem_store.clone(), ))); - let last_processed_commit_round = 0; let last_processed_commit_index = 0; let (sender, mut receiver) = unbounded_channel("consensus_output"); @@ -239,11 +234,7 @@ mod tests { let mut observer = CommitObserver::new( context.clone(), - CommitConsumer::new( - sender, - last_processed_commit_round, - last_processed_commit_index, - ), + CommitConsumer::new(sender, last_processed_commit_index), dag_state.clone(), mem_store.clone(), leader_schedule, @@ -332,7 +323,6 @@ mod tests { context.clone(), mem_store.clone(), ))); - let last_processed_commit_round = 0; let last_processed_commit_index = 0; let (sender, mut receiver) = unbounded_channel("consensus_output"); @@ -343,11 +333,7 @@ mod tests { let mut observer = CommitObserver::new( context.clone(), - CommitConsumer::new( - sender.clone(), - last_processed_commit_round, - last_processed_commit_index, - ), + CommitConsumer::new(sender.clone(), last_processed_commit_index), dag_state.clone(), mem_store.clone(), leader_schedule.clone(), @@ -370,8 +356,6 @@ mod tests { // Commit first batch of leaders (2) and "receive" the subdags as the // consumer of the consensus output channel. let expected_last_processed_index: usize = 2; - let expected_last_processed_round = - expected_last_processed_index as u32 * DEFAULT_WAVE_LENGTH; let mut commits = observer .handle_commit( leaders @@ -443,11 +427,7 @@ mod tests { // last processed index from the consumer over consensus output channel let _observer = CommitObserver::new( context.clone(), - CommitConsumer::new( - sender, - expected_last_processed_round as Round, - expected_last_processed_index as CommitIndex, - ), + CommitConsumer::new(sender, expected_last_processed_index as CommitIndex), dag_state.clone(), mem_store.clone(), leader_schedule, @@ -480,7 +460,6 @@ mod tests { context.clone(), mem_store.clone(), ))); - let last_processed_commit_round = 0; let last_processed_commit_index = 0; let (sender, mut receiver) = unbounded_channel("consensus_output"); @@ -491,11 +470,7 @@ mod tests { let mut observer = CommitObserver::new( context.clone(), - CommitConsumer::new( - sender.clone(), - last_processed_commit_round, - last_processed_commit_index, - ), + CommitConsumer::new(sender.clone(), last_processed_commit_index), dag_state.clone(), mem_store.clone(), leader_schedule.clone(), @@ -518,8 +493,6 @@ mod tests { // Commit all of the leaders and "receive" the subdags as the consumer of // the consensus output channel. let expected_last_processed_index: usize = 10; - let expected_last_processed_round = - expected_last_processed_index as u32 * DEFAULT_WAVE_LENGTH; let commits = observer.handle_commit(leaders.clone()).unwrap(); // Check commits sent over consensus output channel is accurate @@ -548,11 +521,7 @@ mod tests { // last processed index from the consumer over consensus output channel let _observer = CommitObserver::new( context.clone(), - CommitConsumer::new( - sender, - expected_last_processed_round as Round, - expected_last_processed_index as CommitIndex, - ), + CommitConsumer::new(sender, expected_last_processed_index as CommitIndex), dag_state.clone(), mem_store.clone(), leader_schedule, diff --git a/consensus/core/src/commit_syncer.rs b/consensus/core/src/commit_syncer.rs index 6737561786168..3594548eaa95e 100644 --- a/consensus/core/src/commit_syncer.rs +++ b/consensus/core/src/commit_syncer.rs @@ -47,23 +47,60 @@ use tracing::{debug, info, warn}; use crate::{ block::{BlockAPI, BlockRef, SignedBlock, VerifiedBlock}, block_verifier::BlockVerifier, - commit::{ - Commit, CommitAPI as _, CommitDigest, CommitRange, CommitRef, TrustedCommit, - GENESIS_COMMIT_INDEX, - }, + commit::{Commit, CommitAPI as _, CommitDigest, CommitRange, CommitRef, TrustedCommit}, + commit_vote_monitor::CommitVoteMonitor, context::Context, core_thread::CoreThreadDispatcher, dag_state::DagState, error::{ConsensusError, ConsensusResult}, network::NetworkClient, stake_aggregator::{QuorumThreshold, StakeAggregator}, - CommitIndex, + CommitConsumerMonitor, CommitIndex, }; -pub(crate) struct CommitSyncer { +// Handle to stop the CommitSyncer loop. +pub(crate) struct CommitSyncerHandle { schedule_task: JoinHandle<()>, tx_shutdown: oneshot::Sender<()>, - _phantom: std::marker::PhantomData, +} + +impl CommitSyncerHandle { + pub(crate) async fn stop(self) { + let _ = self.tx_shutdown.send(()); + // Do not abort schedule task, which waits for fetches to shut down. + if let Err(e) = self.schedule_task.await { + if e.is_panic() { + std::panic::resume_unwind(e.into_panic()); + } + } + } +} + +pub(crate) struct CommitSyncer { + // States shared by scheduler and fetch tasks. + + // Shared components wrapper. + inner: Arc>, + // State of peers shared by fetch tasks, to determine the next peer to fetch against. + peer_state: Arc>, + + // States only used by the scheduler. + + // Inflight requests to fetch commits from different authorities. + inflight_fetches: JoinSet<(u32, Vec, Vec)>, + // Additional ranges of commits to fetch. + pending_fetches: BTreeSet, + // Fetched commits and blocks by commit range. + fetched_ranges: BTreeMap>, + // Highest commit index among inflight and pending fetches. + // Used to determine the start of new ranges to be fetched. + highest_scheduled_index: Option, + // Highest index among fetched commits, after commits and blocks are verified. + // Used for metrics. + highest_fetched_commit_index: CommitIndex, + // The commit index that is the max of highest local commit index and commit index inflight to Core. + // Used to determine if fetched blocks can be sent to Core without gaps. + synced_commit_index: CommitIndex, } impl CommitSyncer { @@ -71,219 +108,269 @@ impl CommitSyncer { context: Arc, core_thread_dispatcher: Arc, commit_vote_monitor: Arc, + commit_consumer_monitor: Arc, network_client: Arc, block_verifier: Arc, dag_state: Arc>, ) -> Self { - let fetch_state = Arc::new(Mutex::new(FetchState::new(&context))); + let peer_state = Arc::new(Mutex::new(PeerState::new(&context))); let inner = Arc::new(Inner { context, core_thread_dispatcher, commit_vote_monitor, + commit_consumer_monitor, network_client, block_verifier, dag_state, }); - let (tx_shutdown, rx_shutdown) = oneshot::channel(); - let schedule_task = - spawn_logged_monitored_task!(Self::schedule_loop(inner, fetch_state, rx_shutdown)); + let synced_commit_index = inner.dag_state.read().last_commit_index(); CommitSyncer { - schedule_task, - tx_shutdown, - _phantom: Default::default(), + inner, + peer_state, + inflight_fetches: JoinSet::new(), + pending_fetches: BTreeSet::new(), + fetched_ranges: BTreeMap::new(), + highest_scheduled_index: None, + highest_fetched_commit_index: 0, + synced_commit_index, } } - pub(crate) async fn stop(self) { - let _ = self.tx_shutdown.send(()); - // Do not abort schedule task, which waits for fetches to shut down. - let _ = self.schedule_task.await; + pub(crate) fn start(self) -> CommitSyncerHandle { + let (tx_shutdown, rx_shutdown) = oneshot::channel(); + let schedule_task = spawn_logged_monitored_task!(self.schedule_loop(rx_shutdown,)); + CommitSyncerHandle { + schedule_task, + tx_shutdown, + } } - async fn schedule_loop( - inner: Arc>, - fetch_state: Arc>, - mut rx_shutdown: oneshot::Receiver<()>, - ) { + async fn schedule_loop(mut self, mut rx_shutdown: oneshot::Receiver<()>) { let mut interval = tokio::time::interval(Duration::from_secs(2)); interval.set_missed_tick_behavior(MissedTickBehavior::Skip); - // Inflight requests to fetch commits from different authorities. - let mut inflight_fetches = JoinSet::new(); - // Additional ranges (inclusive start and end) of commits to fetch. - let mut pending_fetches = BTreeSet::::new(); - // Fetched commits and blocks by commit indices. - let mut fetched_blocks = BTreeMap::>::new(); - // Highest end index among inflight and pending fetches. - // Used to determine if and which new ranges to fetch. - let mut highest_scheduled_index = Option::::None; - // The commit index that is the max of local last commit index and highest commit index of blocks sent to Core. - // Used to determine if fetched blocks can be sent to Core without gaps. - let mut synced_commit_index = inner.dag_state.read().last_commit_index(); - let mut highest_fetched_commit_index = 0; loop { tokio::select! { // Periodically, schedule new fetches if the node is falling behind. _ = interval.tick() => { - let quorum_commit_index = inner.commit_vote_monitor.quorum_commit_index(); - let local_commit_index = inner.dag_state.read().last_commit_index(); - let metrics = &inner.context.metrics.node_metrics; - metrics.commit_sync_quorum_index.set(quorum_commit_index as i64); - metrics.commit_sync_local_index.set(local_commit_index as i64); - // Update synced_commit_index periodically to make sure it is not smaller than - // local commit index. - synced_commit_index = synced_commit_index.max(local_commit_index); - info!( - "Checking to schedule fetches: synced_commit_index={}, highest_scheduled_index={}, quorum_commit_index={}", - synced_commit_index, highest_scheduled_index.unwrap_or(0), quorum_commit_index, - ); - // TODO: pause commit sync when execution of commits is lagging behind, maybe through Core. - // TODO: cleanup inflight fetches that are no longer needed. - let fetch_after_index = synced_commit_index.max(highest_scheduled_index.unwrap_or(0)); - // When the node is falling behind, schedule pending fetches which will be executed on later. - 'pending: for prev_end in (fetch_after_index..=quorum_commit_index).step_by(inner.context.parameters.commit_sync_batch_size as usize) { - // Create range with inclusive start and end. - let range_start = prev_end + 1; - let range_end = prev_end + inner.context.parameters.commit_sync_batch_size; - // When the condition below is true, [range_start, range_end] contains less number of commits - // than the target batch size. Not creating the smaller batch is intentional, to avoid the - // cost of processing more and smaller batches. - // Block broadcast, subscription and synchronization will help the node catchup. - if range_end > quorum_commit_index { - break 'pending; - } - pending_fetches.insert((range_start..=range_end).into()); - // quorum_commit_index should be non-decreasing, so highest_scheduled_index should not - // decrease either. - highest_scheduled_index = Some(range_end); - } + self.try_schedule_once(); } - - // Processed fetched blocks. - Some(result) = inflight_fetches.join_next(), if !inflight_fetches.is_empty() => { + // Handles results from fetch tasks. + Some(result) = self.inflight_fetches.join_next(), if !self.inflight_fetches.is_empty() => { if let Err(e) = result { - warn!("Fetch cancelled or panicked, CommitSyncer shutting down: {}", e); + if e.is_panic() { + std::panic::resume_unwind(e.into_panic()); + } + warn!("Fetch cancelled. CommitSyncer shutting down: {}", e); // If any fetch is cancelled or panicked, try to shutdown and exit the loop. - inflight_fetches.shutdown().await; + self.inflight_fetches.shutdown().await; return; } - let (target_end, commits, blocks): (CommitIndex, Vec, Vec) = result.unwrap(); - assert!(!commits.is_empty()); - let metrics = &inner.context.metrics.node_metrics; - metrics.commit_sync_fetched_commits.inc_by(commits.len() as u64); - metrics.commit_sync_fetched_blocks.inc_by(blocks.len() as u64); - metrics.commit_sync_total_fetched_blocks_size.inc_by( - blocks.iter().map(|b| b.serialized().len() as u64).sum::() - ); - - let (commit_start, commit_end) = (commits.first().unwrap().index(), commits.last().unwrap().index()); - - highest_fetched_commit_index = highest_fetched_commit_index.max(commit_end); - metrics.commit_sync_highest_fetched_index.set(highest_fetched_commit_index.into()); - - // Allow returning partial results, and try fetching the rest separately. - if commit_end < target_end { - pending_fetches.insert((commit_end + 1..=target_end).into()); - } - // Make sure synced_commit_index is up to date. - synced_commit_index = synced_commit_index.max(inner.dag_state.read().last_commit_index()); - // Only add new blocks if at least some of them are not already synced. - if synced_commit_index < commit_end { - fetched_blocks.insert((commit_start..=commit_end).into(), blocks); - } - // Try to process as many fetched blocks as possible. - 'fetched: while let Some((fetched_commit_range, _blocks)) = fetched_blocks.first_key_value() { - // Only pop fetched_blocks if there is no gap with blocks already synced. - // Note: start, end and synced_commit_index are all inclusive. - let (fetched_commit_range, blocks) = if fetched_commit_range.start() <= synced_commit_index + 1 { - fetched_blocks.pop_first().unwrap() - } else { - // Found gap between earliest fetched block and latest synced block, - // so not sending additional blocks to Core. - metrics.commit_sync_gap_on_processing.inc(); - break 'fetched; - }; - // Avoid sending to Core a whole batch of already synced blocks. - if fetched_commit_range.end() <= synced_commit_index { - continue 'fetched; - } - debug!( - "Fetched certified blocks: {}", - blocks - .iter() - .map(|b| b.reference().to_string()) - .join(","), - ); - // If core thread cannot handle the incoming blocks, it is ok to block here. - // Also it is possible to have missing ancestors because an equivocating validator - // may produce blocks that are not included in commits but are ancestors to other blocks. - // Synchronizer is needed to fill in the missing ancestors in this case. - match inner.core_thread_dispatcher.add_blocks(blocks).await { - Ok(missing) => { - if !missing.is_empty() { - warn!("Fetched blocks have missing ancestors: {:?}", missing); - } - } - Err(e) => { - info!("Failed to add blocks, shutting down: {}", e); - return; - } - }; - // Once commits and blocks are sent to Core, ratchet up synced_commit_index - synced_commit_index = synced_commit_index.max(fetched_commit_range.end()); - } + let (target_end, commits, blocks) = result.unwrap(); + self.handle_fetch_result(target_end, commits, blocks).await; } - _ = &mut rx_shutdown => { // Shutdown requested. info!("CommitSyncer shutting down ..."); - inflight_fetches.shutdown().await; + self.inflight_fetches.shutdown().await; return; } } - // Cap parallel fetches based on configured limit and committee size, to avoid overloading the network. - // Also when there are too many fetched blocks that cannot be sent to Core before an earlier fetch - // has not finished, reduce parallelism so the earlier fetch can retry on a better host and succeed. - let target_parallel_fetches = inner - .context - .parameters - .commit_sync_parallel_fetches - .min(inner.context.committee.size() * 2 / 3) - .min( - inner - .context - .parameters - .commit_sync_batches_ahead - .saturating_sub(fetched_blocks.len()), - ) - .max(1); - // Start new fetches if there are pending batches and available slots. - loop { - if inflight_fetches.len() >= target_parallel_fetches { - break; - } - let Some(commit_range) = pending_fetches.pop_first() else { + self.try_start_fetches(); + } + } + + fn try_schedule_once(&mut self) { + let quorum_commit_index = self.inner.commit_vote_monitor.quorum_commit_index(); + let local_commit_index = self.inner.dag_state.read().last_commit_index(); + let metrics = &self.inner.context.metrics.node_metrics; + metrics + .commit_sync_quorum_index + .set(quorum_commit_index as i64); + metrics + .commit_sync_local_index + .set(local_commit_index as i64); + let highest_handled_index = self.inner.commit_consumer_monitor.highest_handled_commit(); + let highest_scheduled_index = self.highest_scheduled_index.unwrap_or(0); + // Update synced_commit_index periodically to make sure it is no smaller than + // local commit index. + self.synced_commit_index = self.synced_commit_index.max(local_commit_index); + let unhandled_commits_threshold = self.unhandled_commits_threshold(); + info!( + "Checking to schedule fetches: synced_commit_index={}, highest_handled_index={}, highest_scheduled_index={}, quorum_commit_index={}, unhandled_commits_threshold={}", + self.synced_commit_index, highest_handled_index, highest_scheduled_index, quorum_commit_index, unhandled_commits_threshold, + ); + + // TODO: cleanup inflight fetches that are no longer needed. + let fetch_after_index = self + .synced_commit_index + .max(self.highest_scheduled_index.unwrap_or(0)); + // When the node is falling behind, schedule pending fetches which will be executed on later. + for prev_end in (fetch_after_index..=quorum_commit_index) + .step_by(self.inner.context.parameters.commit_sync_batch_size as usize) + { + // Create range with inclusive start and end. + let range_start = prev_end + 1; + let range_end = prev_end + self.inner.context.parameters.commit_sync_batch_size; + // Commit range is not fetched when [range_start, range_end] contains less number of commits + // than the target batch size. This is to avoid the cost of processing more and smaller batches. + // Block broadcast, subscription and synchronization will help the node catchup. + if quorum_commit_index < range_end { + break; + } + // Pause scheduling new fetches when handling of commits is lagging. + if highest_handled_index + unhandled_commits_threshold < range_end { + warn!("Skip scheduling new commit fetches: consensus handler is lagging. highest_handled_index={}, highest_scheduled_index={}", highest_handled_index, highest_scheduled_index); + break; + } + self.pending_fetches + .insert((range_start..=range_end).into()); + // quorum_commit_index should be non-decreasing, so highest_scheduled_index should not + // decrease either. + self.highest_scheduled_index = Some(range_end); + } + } + + async fn handle_fetch_result( + &mut self, + target_end: CommitIndex, + commits: Vec, + blocks: Vec, + ) { + assert!(!commits.is_empty()); + let metrics = &self.inner.context.metrics.node_metrics; + metrics + .commit_sync_fetched_commits + .inc_by(commits.len() as u64); + metrics + .commit_sync_fetched_blocks + .inc_by(blocks.len() as u64); + metrics.commit_sync_total_fetched_blocks_size.inc_by( + blocks + .iter() + .map(|b| b.serialized().len() as u64) + .sum::(), + ); + + let (commit_start, commit_end) = ( + commits.first().unwrap().index(), + commits.last().unwrap().index(), + ); + self.highest_fetched_commit_index = self.highest_fetched_commit_index.max(commit_end); + metrics + .commit_sync_highest_fetched_index + .set(self.highest_fetched_commit_index as i64); + + // Allow returning partial results, and try fetching the rest separately. + if commit_end < target_end { + self.pending_fetches + .insert((commit_end + 1..=target_end).into()); + } + // Make sure synced_commit_index is up to date. + self.synced_commit_index = self + .synced_commit_index + .max(self.inner.dag_state.read().last_commit_index()); + // Only add new blocks if at least some of them are not already synced. + if self.synced_commit_index < commit_end { + self.fetched_ranges + .insert((commit_start..=commit_end).into(), blocks); + } + // Try to process as many fetched blocks as possible. + while let Some((fetched_commit_range, _blocks)) = self.fetched_ranges.first_key_value() { + // Only pop fetched_ranges if there is no gap with blocks already synced. + // Note: start, end and synced_commit_index are all inclusive. + let (fetched_commit_range, blocks) = + if fetched_commit_range.start() <= self.synced_commit_index + 1 { + self.fetched_ranges.pop_first().unwrap() + } else { + // Found gap between earliest fetched block and latest synced block, + // so not sending additional blocks to Core. + metrics.commit_sync_gap_on_processing.inc(); break; }; - inflight_fetches.spawn(Self::fetch_loop( - inner.clone(), - fetch_state.clone(), - commit_range, - )); + // Avoid sending to Core a whole batch of already synced blocks. + if fetched_commit_range.end() <= self.synced_commit_index { + continue; } - let metrics = &inner.context.metrics.node_metrics; - metrics - .commit_sync_inflight_fetches - .set(inflight_fetches.len() as i64); - metrics - .commit_sync_pending_fetches - .set(pending_fetches.len() as i64); - metrics - .commit_sync_highest_synced_index - .set(synced_commit_index as i64); + debug!( + "Fetched certified blocks: {}", + blocks.iter().map(|b| b.reference().to_string()).join(","), + ); + // If core thread cannot handle the incoming blocks, it is ok to block here. + // Also it is possible to have missing ancestors because an equivocating validator + // may produce blocks that are not included in commits but are ancestors to other blocks. + // Synchronizer is needed to fill in the missing ancestors in this case. + match self.inner.core_thread_dispatcher.add_blocks(blocks).await { + Ok(missing) => { + if !missing.is_empty() { + warn!("Fetched blocks have missing ancestors: {:?}", missing); + } + } + Err(e) => { + info!("Failed to add blocks, shutting down: {}", e); + return; + } + }; + // Once commits and blocks are sent to Core, ratchet up synced_commit_index + self.synced_commit_index = self.synced_commit_index.max(fetched_commit_range.end()); + } + + metrics + .commit_sync_inflight_fetches + .set(self.inflight_fetches.len() as i64); + metrics + .commit_sync_pending_fetches + .set(self.pending_fetches.len() as i64); + metrics + .commit_sync_highest_synced_index + .set(self.synced_commit_index as i64); + } + + fn try_start_fetches(&mut self) { + // Cap parallel fetches based on configured limit and committee size, to avoid overloading the network. + // Also when there are too many fetched blocks that cannot be sent to Core before an earlier fetch + // has not finished, reduce parallelism so the earlier fetch can retry on a better host and succeed. + let target_parallel_fetches = self + .inner + .context + .parameters + .commit_sync_parallel_fetches + .min(self.inner.context.committee.size() * 2 / 3) + .min( + self.inner + .context + .parameters + .commit_sync_batches_ahead + .saturating_sub(self.fetched_ranges.len()), + ) + .max(1); + // Start new fetches if there are pending batches and available slots. + loop { + if self.inflight_fetches.len() >= target_parallel_fetches { + break; + } + let Some(commit_range) = self.pending_fetches.pop_first() else { + break; + }; + self.inflight_fetches.spawn(Self::fetch_loop( + self.inner.clone(), + self.peer_state.clone(), + commit_range, + )); } + + let metrics = &self.inner.context.metrics.node_metrics; + metrics + .commit_sync_inflight_fetches + .set(self.inflight_fetches.len() as i64); + metrics + .commit_sync_pending_fetches + .set(self.pending_fetches.len() as i64); + metrics + .commit_sync_highest_synced_index + .set(self.synced_commit_index as i64); } // Retries fetching commits and blocks from available authorities, until a request succeeds @@ -291,7 +378,7 @@ impl CommitSyncer { // Returns the fetched commits and blocks referenced by the commits. async fn fetch_loop( inner: Arc>, - fetch_state: Arc>, + peer_state: Arc>, commit_range: CommitRange, ) -> (CommitIndex, Vec, Vec) { let _timer = inner @@ -302,7 +389,7 @@ impl CommitSyncer { .start_timer(); info!("Starting to fetch commits in {commit_range:?} ...",); loop { - match Self::fetch_once(inner.clone(), fetch_state.clone(), commit_range.clone()).await { + match Self::fetch_once(inner.clone(), peer_state.clone(), commit_range.clone()).await { Ok((commits, blocks)) => { info!("Finished fetching commits in {commit_range:?}",); return (commit_range.end(), commits, blocks); @@ -327,7 +414,7 @@ impl CommitSyncer { // and sent to Core for processing. async fn fetch_once( inner: Arc>, - fetch_state: Arc>, + peer_state: Arc>, commit_range: CommitRange, ) -> ConsensusResult<(Vec, Vec)> { const FETCH_COMMITS_TIMEOUT: Duration = Duration::from_secs(30); @@ -346,7 +433,7 @@ impl CommitSyncer { // 1. Find an available authority to fetch commits and blocks from, and wait // if it is not yet ready. let Some((available_time, retries, target_authority)) = - fetch_state.lock().available_authorities.pop_first() + peer_state.lock().available_authorities.pop_first() else { sleep(MAX_RETRY_INTERVAL).await; return Err(ConsensusError::NoAvailableAuthorityToFetchCommits); @@ -367,17 +454,17 @@ impl CommitSyncer { .await { Ok(result) => { - let mut fetch_state = fetch_state.lock(); + let mut peer_state = peer_state.lock(); let now = Instant::now(); - fetch_state + peer_state .available_authorities .insert((now, 0, target_authority)); result } Err(e) => { - let mut fetch_state = fetch_state.lock(); + let mut peer_state = peer_state.lock(); let now = Instant::now(); - fetch_state.available_authorities.insert(( + peer_state.available_authorities.insert(( now + FETCH_RETRY_BASE_INTERVAL * retries.min(FETCH_RETRY_INTERVAL_LIMIT), retries.saturating_add(1), target_authority, @@ -495,56 +582,35 @@ impl CommitSyncer { Ok((commits, fetched_blocks)) } -} -/// Monitors commit votes from received and verified blocks, -/// and keeps track of the highest commit voted by each authority and certified by a quorum. -pub(crate) struct CommitVoteMonitor { - context: Arc, - // Highest commit index voted by each authority. - highest_voted_commits: Mutex>, -} + fn unhandled_commits_threshold(&self) -> CommitIndex { + self.inner.context.parameters.commit_sync_batch_size + * (self.inner.context.parameters.commit_sync_batches_ahead as u32) + } -impl CommitVoteMonitor { - pub(crate) fn new(context: Arc) -> Self { - let highest_voted_commits = Mutex::new(vec![0; context.committee.size()]); - Self { - context, - highest_voted_commits, - } + #[cfg(test)] + fn pending_fetches(&self) -> BTreeSet { + self.pending_fetches.clone() } - // Records the highest commit index voted in each block. - pub(crate) fn observe(&self, block: &VerifiedBlock) { - let mut highest_voted_commits = self.highest_voted_commits.lock(); - for vote in block.commit_votes() { - if vote.index > highest_voted_commits[block.author()] { - highest_voted_commits[block.author()] = vote.index; - } - } + #[cfg(test)] + fn fetched_ranges(&self) -> BTreeMap> { + self.fetched_ranges.clone() } - // Finds the highest commit index certified by a quorum. - // When an authority votes for commit index S, it is also voting for all commit indices 1 <= i < S. - // So the quorum commit index is the smallest index S such that the sum of stakes of authorities - // voting for commit indices >= S passes the quorum threshold. - pub(crate) fn quorum_commit_index(&self) -> CommitIndex { - let highest_voted_commits = self.highest_voted_commits.lock(); - let mut highest_voted_commits = highest_voted_commits - .iter() - .zip(self.context.committee.authorities()) - .map(|(commit_index, (_, a))| (*commit_index, a.stake)) - .collect::>(); - // Sort by commit index then stake, in descending order. - highest_voted_commits.sort_by(|a, b| a.cmp(b).reverse()); - let mut total_stake = 0; - for (commit_index, stake) in highest_voted_commits { - total_stake += stake; - if total_stake >= self.context.committee.quorum_threshold() { - return commit_index; - } - } - GENESIS_COMMIT_INDEX + #[cfg(test)] + fn highest_scheduled_index(&self) -> Option { + self.highest_scheduled_index + } + + #[cfg(test)] + fn highest_fetched_commit_index(&self) -> CommitIndex { + self.highest_fetched_commit_index + } + + #[cfg(test)] + fn synced_commit_index(&self) -> CommitIndex { + self.synced_commit_index } } @@ -552,6 +618,7 @@ struct Inner { context: Arc, core_thread_dispatcher: Arc, commit_vote_monitor: Arc, + commit_consumer_monitor: Arc, network_client: Arc, block_verifier: Arc, dag_state: Arc>, @@ -636,7 +703,7 @@ impl Inner { } } -struct FetchState { +struct PeerState { // The value is a tuple of // - the next available time for the authority to fetch from, // - count of current consecutive failures fetching from the authority, reset on success, @@ -646,7 +713,7 @@ struct FetchState { available_authorities: BTreeSet<(Instant, u32, AuthorityIndex)>, } -impl FetchState { +impl PeerState { fn new(context: &Context) -> Self { // Randomize the initial order of authorities. let mut shuffled_authority_indices: Vec<_> = context @@ -670,58 +737,174 @@ impl FetchState { } } -// TODO: add more unit and integration tests. #[cfg(test)] -mod test { - use std::sync::Arc; +mod tests { + use std::{sync::Arc, time::Duration}; + + use bytes::Bytes; + use consensus_config::{AuthorityIndex, Parameters}; + use parking_lot::RwLock; - use super::CommitVoteMonitor; use crate::{ - block::{TestBlock, VerifiedBlock}, - commit::{CommitDigest, CommitRef}, + block::{BlockRef, TestBlock, VerifiedBlock}, + block_verifier::NoopBlockVerifier, + commit::CommitRange, + commit_syncer::CommitSyncer, + commit_vote_monitor::CommitVoteMonitor, context::Context, + core_thread::MockCoreThreadDispatcher, + dag_state::DagState, + error::ConsensusResult, + network::{BlockStream, NetworkClient}, + storage::mem_store::MemStore, + CommitConsumerMonitor, CommitDigest, CommitRef, Round, }; - #[tokio::test] - async fn test_commit_vote_monitor() { - let context = Arc::new(Context::new_for_test(4).0); - let monitor = CommitVoteMonitor::new(context.clone()); - - // Observe commit votes for indices 5, 6, 7, 8 from blocks. - let blocks = (0..4) - .map(|i| { - VerifiedBlock::new_for_test( - TestBlock::new(10, i) - .set_commit_votes(vec![CommitRef::new(5 + i, CommitDigest::MIN)]) - .build(), - ) - }) - .collect::>(); - for b in blocks { - monitor.observe(&b); + #[derive(Default)] + struct FakeNetworkClient {} + + #[async_trait::async_trait] + impl NetworkClient for FakeNetworkClient { + const SUPPORT_STREAMING: bool = true; + + async fn send_block( + &self, + _peer: AuthorityIndex, + _serialized_block: &VerifiedBlock, + _timeout: Duration, + ) -> ConsensusResult<()> { + unimplemented!("Unimplemented") } - // CommitIndex 6 is the highest index supported by a quorum. - assert_eq!(monitor.quorum_commit_index(), 6); - - // Observe new blocks with new votes from authority 0 and 1. - let blocks = (0..2) - .map(|i| { - VerifiedBlock::new_for_test( - TestBlock::new(11, i) - .set_commit_votes(vec![ - CommitRef::new(6 + i, CommitDigest::MIN), - CommitRef::new(7 + i, CommitDigest::MIN), - ]) - .build(), - ) - }) - .collect::>(); - for b in blocks { - monitor.observe(&b); + async fn subscribe_blocks( + &self, + _peer: AuthorityIndex, + _last_received: Round, + _timeout: Duration, + ) -> ConsensusResult { + unimplemented!("Unimplemented") } - // Highest commit index per authority should be 7, 8, 7, 8 now. - assert_eq!(monitor.quorum_commit_index(), 7); + async fn fetch_blocks( + &self, + _peer: AuthorityIndex, + _block_refs: Vec, + _highest_accepted_rounds: Vec, + _timeout: Duration, + ) -> ConsensusResult> { + unimplemented!("Unimplemented") + } + + async fn fetch_commits( + &self, + _peer: AuthorityIndex, + _commit_range: CommitRange, + _timeout: Duration, + ) -> ConsensusResult<(Vec, Vec)> { + unimplemented!("Unimplemented") + } + + async fn fetch_latest_blocks( + &self, + _peer: AuthorityIndex, + _authorities: Vec, + _timeout: Duration, + ) -> ConsensusResult> { + unimplemented!("Unimplemented") + } + } + + #[tokio::test(flavor = "current_thread", start_paused = true)] + async fn commit_syncer_start_and_pause_scheduling() { + // SETUP + let (context, _) = Context::new_for_test(4); + // Use smaller batches and fetch limits for testing. + let context = Context { + own_index: AuthorityIndex::new_for_test(3), + parameters: Parameters { + commit_sync_batch_size: 5, + commit_sync_batches_ahead: 5, + commit_sync_parallel_fetches: 5, + max_blocks_per_fetch: 5, + ..context.parameters + }, + ..context + }; + let context = Arc::new(context); + let block_verifier = Arc::new(NoopBlockVerifier {}); + let core_thread_dispatcher = Arc::new(MockCoreThreadDispatcher::default()); + let network_client = Arc::new(FakeNetworkClient::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())); + let commit_consumer_monitor = Arc::new(CommitConsumerMonitor::new(0)); + let mut commit_syncer = CommitSyncer::new( + context, + core_thread_dispatcher, + commit_vote_monitor.clone(), + commit_consumer_monitor.clone(), + network_client, + block_verifier, + dag_state, + ); + + // Check initial state. + assert!(commit_syncer.pending_fetches().is_empty()); + assert!(commit_syncer.fetched_ranges().is_empty()); + assert!(commit_syncer.highest_scheduled_index().is_none()); + assert_eq!(commit_syncer.highest_fetched_commit_index(), 0); + assert_eq!(commit_syncer.synced_commit_index(), 0); + + // Observe round 15 blocks voting for commit 10 from authorities 0 to 2 in CommitVoteMonitor + for i in 0..3 { + let test_block = TestBlock::new(15, i) + .set_commit_votes(vec![CommitRef::new(10, CommitDigest::MIN)]) + .build(); + let block = VerifiedBlock::new_for_test(test_block); + commit_vote_monitor.observe_block(&block); + } + + // Fetches should be scheduled after seeing progress of other validators. + commit_syncer.try_schedule_once(); + + // Verify state. + assert_eq!(commit_syncer.pending_fetches().len(), 2); + assert!(commit_syncer.fetched_ranges().is_empty()); + assert_eq!(commit_syncer.highest_scheduled_index(), Some(10)); + assert_eq!(commit_syncer.highest_fetched_commit_index(), 0); + assert_eq!(commit_syncer.synced_commit_index(), 0); + + // Observe round 40 blocks voting for commit 35 from authorities 0 to 2 in CommitVoteMonitor + for i in 0..3 { + let test_block = TestBlock::new(40, i) + .set_commit_votes(vec![CommitRef::new(35, CommitDigest::MIN)]) + .build(); + let block = VerifiedBlock::new_for_test(test_block); + commit_vote_monitor.observe_block(&block); + } + + // Fetches should be scheduled until the unhandled commits threshold. + commit_syncer.try_schedule_once(); + + // Verify commit syncer is paused after scheduling 15 commits to index 25. + assert_eq!(commit_syncer.unhandled_commits_threshold(), 25); + assert_eq!(commit_syncer.highest_scheduled_index(), Some(25)); + let pending_fetches = commit_syncer.pending_fetches(); + assert_eq!(pending_fetches.len(), 5); + + // Indicate commit index 25 is consumed, and try to schedule again. + commit_consumer_monitor.set_highest_handled_commit(25); + commit_syncer.try_schedule_once(); + + // Verify commit syncer schedules fetches up to index 35. + assert_eq!(commit_syncer.highest_scheduled_index(), Some(35)); + let pending_fetches = commit_syncer.pending_fetches(); + assert_eq!(pending_fetches.len(), 7); + + // Verify contiguous ranges are scheduled. + for (range, start) in pending_fetches.iter().zip((1..35).step_by(5)) { + assert_eq!(range.start(), start); + assert_eq!(range.end(), start + 4); + } } } diff --git a/consensus/core/src/commit_vote_monitor.rs b/consensus/core/src/commit_vote_monitor.rs new file mode 100644 index 0000000000000..654260661da4f --- /dev/null +++ b/consensus/core/src/commit_vote_monitor.rs @@ -0,0 +1,118 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::sync::Arc; + +use parking_lot::Mutex; + +use crate::{ + block::{BlockAPI as _, VerifiedBlock}, + commit::GENESIS_COMMIT_INDEX, + context::Context, + CommitIndex, +}; + +/// Monitors the progress of consensus commits across the network. +pub(crate) struct CommitVoteMonitor { + context: Arc, + // Highest commit index voted by each authority. + highest_voted_commits: Mutex>, +} + +impl CommitVoteMonitor { + pub(crate) fn new(context: Arc) -> Self { + let highest_voted_commits = Mutex::new(vec![0; context.committee.size()]); + Self { + context, + highest_voted_commits, + } + } + + /// Keeps track of the highest commit voted by each authority. + pub(crate) fn observe_block(&self, block: &VerifiedBlock) { + let mut highest_voted_commits = self.highest_voted_commits.lock(); + for vote in block.commit_votes() { + if vote.index > highest_voted_commits[block.author()] { + highest_voted_commits[block.author()] = vote.index; + } + } + } + + // Finds the highest commit index certified by a quorum. + // When an authority votes for commit index S, it is also voting for all commit indices 1 <= i < S. + // So the quorum commit index is the smallest index S such that the sum of stakes of authorities + // voting for commit indices >= S passes the quorum threshold. + pub(crate) fn quorum_commit_index(&self) -> CommitIndex { + let highest_voted_commits = self.highest_voted_commits.lock(); + let mut highest_voted_commits = highest_voted_commits + .iter() + .zip(self.context.committee.authorities()) + .map(|(commit_index, (_, a))| (*commit_index, a.stake)) + .collect::>(); + // Sort by commit index then stake, in descending order. + highest_voted_commits.sort_by(|a, b| a.cmp(b).reverse()); + let mut total_stake = 0; + for (commit_index, stake) in highest_voted_commits { + total_stake += stake; + if total_stake >= self.context.committee.quorum_threshold() { + return commit_index; + } + } + GENESIS_COMMIT_INDEX + } +} + +#[cfg(test)] +mod test { + use std::sync::Arc; + + use super::CommitVoteMonitor; + use crate::{ + block::{TestBlock, VerifiedBlock}, + commit::{CommitDigest, CommitRef}, + context::Context, + }; + + #[tokio::test] + async fn test_commit_vote_monitor() { + let context = Arc::new(Context::new_for_test(4).0); + let monitor = CommitVoteMonitor::new(context.clone()); + + // Observe commit votes for indices 5, 6, 7, 8 from blocks. + let blocks = (0..4) + .map(|i| { + VerifiedBlock::new_for_test( + TestBlock::new(10, i) + .set_commit_votes(vec![CommitRef::new(5 + i, CommitDigest::MIN)]) + .build(), + ) + }) + .collect::>(); + for b in blocks { + monitor.observe_block(&b); + } + + // CommitIndex 6 is the highest index supported by a quorum. + assert_eq!(monitor.quorum_commit_index(), 6); + + // Observe new blocks with new votes from authority 0 and 1. + let blocks = (0..2) + .map(|i| { + VerifiedBlock::new_for_test( + TestBlock::new(11, i) + .set_commit_votes(vec![ + CommitRef::new(6 + i, CommitDigest::MIN), + CommitRef::new(7 + i, CommitDigest::MIN), + ]) + .build(), + ) + }) + .collect::>(); + for b in blocks { + monitor.observe_block(&b); + } + + // Highest commit index per authority should be 7, 8, 7, 8 now. + assert_eq!(monitor.quorum_commit_index(), 7); + } +} diff --git a/consensus/core/src/core.rs b/consensus/core/src/core.rs index c9593548d20a6..d0a8f89169afb 100644 --- a/consensus/core/src/core.rs +++ b/consensus/core/src/core.rs @@ -876,7 +876,7 @@ impl CoreTextFixture { let (commit_sender, commit_receiver) = unbounded_channel("consensus_output"); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(commit_sender.clone(), 0, 0), + CommitConsumer::new(commit_sender.clone(), 0), dag_state.clone(), store.clone(), leader_schedule.clone(), @@ -974,7 +974,7 @@ mod test { let (sender, _receiver) = unbounded_channel("consensus_output"); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), 0, 0), + CommitConsumer::new(sender.clone(), 0), dag_state.clone(), store.clone(), leader_schedule.clone(), @@ -1091,7 +1091,7 @@ mod test { let (sender, _receiver) = unbounded_channel("consensus_output"); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), 0, 0), + CommitConsumer::new(sender.clone(), 0), dag_state.clone(), store.clone(), leader_schedule.clone(), @@ -1187,7 +1187,7 @@ mod test { let (sender, _receiver) = unbounded_channel("consensus_output"); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), 0, 0), + CommitConsumer::new(sender.clone(), 0), dag_state.clone(), store.clone(), leader_schedule.clone(), @@ -1296,7 +1296,7 @@ mod test { let (sender, _receiver) = unbounded_channel("consensus_output"); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), 0, 0), + CommitConsumer::new(sender.clone(), 0), dag_state.clone(), store.clone(), leader_schedule.clone(), @@ -1384,7 +1384,7 @@ mod test { let (sender, _receiver) = unbounded_channel("consensus_output"); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), 0, 0), + CommitConsumer::new(sender.clone(), 0), dag_state.clone(), store.clone(), leader_schedule.clone(), @@ -1571,7 +1571,7 @@ mod test { let (sender, _receiver) = unbounded_channel("consensus_output"); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), 0, 0), + CommitConsumer::new(sender.clone(), 0), dag_state.clone(), store.clone(), leader_schedule.clone(), diff --git a/consensus/core/src/core_thread.rs b/consensus/core/src/core_thread.rs index fca3406dd8d64..8b9c6a9b89783 100644 --- a/consensus/core/src/core_thread.rs +++ b/consensus/core/src/core_thread.rs @@ -8,6 +8,7 @@ use mysten_metrics::{ monitored_mpsc::{channel, Receiver, Sender, WeakSender}, monitored_scope, spawn_logged_monitored_task, }; +use parking_lot::Mutex; use thiserror::Error; use tokio::sync::{oneshot, watch}; use tracing::warn; @@ -231,6 +232,67 @@ impl CoreThreadDispatcher for ChannelCoreThreadDispatcher { } } +// TODO: complete the Mock for thread dispatcher to be used from several tests +#[derive(Default)] +pub(crate) struct MockCoreThreadDispatcher { + add_blocks: Mutex>, + missing_blocks: Mutex>, + last_known_proposed_round: Mutex>, +} + +impl MockCoreThreadDispatcher { + #[cfg(test)] + pub(crate) async fn get_add_blocks(&self) -> Vec { + let mut add_blocks = self.add_blocks.lock(); + add_blocks.drain(0..).collect() + } + + #[cfg(test)] + pub(crate) async fn stub_missing_blocks(&self, block_refs: BTreeSet) { + let mut missing_blocks = self.missing_blocks.lock(); + missing_blocks.extend(block_refs); + } + + #[cfg(test)] + pub(crate) async fn get_last_own_proposed_round(&self) -> Vec { + let last_known_proposed_round = self.last_known_proposed_round.lock(); + last_known_proposed_round.clone() + } +} + +#[async_trait] +impl CoreThreadDispatcher for MockCoreThreadDispatcher { + async fn add_blocks( + &self, + blocks: Vec, + ) -> Result, CoreError> { + let mut add_blocks = self.add_blocks.lock(); + add_blocks.extend(blocks); + Ok(BTreeSet::new()) + } + + async fn new_block(&self, _round: Round, _force: bool) -> Result<(), CoreError> { + Ok(()) + } + + async fn get_missing_blocks(&self) -> Result, CoreError> { + let mut missing_blocks = self.missing_blocks.lock(); + let result = missing_blocks.clone(); + missing_blocks.clear(); + Ok(result) + } + + fn set_consumer_availability(&self, _available: bool) -> Result<(), CoreError> { + todo!() + } + + fn set_last_known_proposed_round(&self, round: Round) -> Result<(), CoreError> { + let mut last_known_proposed_round = self.last_known_proposed_round.lock(); + last_known_proposed_round.push(round); + Ok(()) + } +} + #[cfg(test)] mod test { use mysten_metrics::monitored_mpsc::unbounded_channel; @@ -273,7 +335,7 @@ mod test { )); let commit_observer = CommitObserver::new( context.clone(), - CommitConsumer::new(sender.clone(), 0, 0), + CommitConsumer::new(sender.clone(), 0), dag_state.clone(), store, leader_schedule.clone(), diff --git a/consensus/core/src/lib.rs b/consensus/core/src/lib.rs index ba428b1eb7bc4..d9823f44bea2e 100644 --- a/consensus/core/src/lib.rs +++ b/consensus/core/src/lib.rs @@ -31,6 +31,8 @@ mod threshold_clock; mod transaction; mod universal_committer; +mod commit_consumer; +mod commit_vote_monitor; #[cfg(test)] mod test_dag; #[cfg(test)] @@ -40,7 +42,8 @@ mod test_dag_parser; pub use authority_node::ConsensusAuthority; pub use block::{BlockAPI, Round}; -pub use commit::{CommitConsumer, CommitDigest, CommitIndex, CommitRef, CommittedSubDag}; +pub use commit::{CommitDigest, CommitIndex, CommitRef, CommittedSubDag}; +pub use commit_consumer::{CommitConsumer, CommitConsumerMonitor}; pub use transaction::{TransactionClient, TransactionVerifier, ValidationError}; #[cfg(test)] diff --git a/consensus/core/src/synchronizer.rs b/consensus/core/src/synchronizer.rs index 504cd63249b6e..599cbd166bdd2 100644 --- a/consensus/core/src/synchronizer.rs +++ b/consensus/core/src/synchronizer.rs @@ -27,13 +27,12 @@ use tokio::{ }; use tracing::{debug, error, info, trace, warn}; -use crate::authority_service::COMMIT_LAG_MULTIPLIER; -use crate::commit_syncer::CommitVoteMonitor; +use crate::{authority_service::COMMIT_LAG_MULTIPLIER, core_thread::CoreThreadDispatcher}; use crate::{ block::{BlockRef, SignedBlock, VerifiedBlock}, block_verifier::BlockVerifier, + commit_vote_monitor::CommitVoteMonitor, context::Context, - core_thread::CoreThreadDispatcher, dag_state::DagState, error::{ConsensusError, ConsensusResult}, network::NetworkClient, @@ -259,34 +258,29 @@ impl Synchronizer Synchronizer Synchronizer Synchronizer, block_verifier: Arc, + commit_vote_monitor: Arc, context: Arc, core_dispatcher: Arc, dag_state: Arc>, @@ -453,6 +448,7 @@ impl Synchronizer Synchronizer, block_verifier: Arc, + commit_vote_monitor: Arc, context: Arc, commands_sender: Sender, sync_method: &str, @@ -530,6 +527,11 @@ impl Synchronizer Synchronizer Synchronizer Synchronizer>, - missing_blocks: Mutex>, - last_known_proposed_round: parking_lot::Mutex>, - } - - impl MockCoreThreadDispatcher { - async fn get_add_blocks(&self) -> Vec { - let mut lock = self.add_blocks.lock().await; - lock.drain(0..).collect() - } - - async fn stub_missing_blocks(&self, block_refs: BTreeSet) { - let mut lock = self.missing_blocks.lock().await; - lock.extend(block_refs); - } - - async fn get_last_own_proposed_round(&self) -> Vec { - let lock = self.last_known_proposed_round.lock(); - lock.clone() - } - } - - #[async_trait] - impl CoreThreadDispatcher for MockCoreThreadDispatcher { - async fn add_blocks( - &self, - blocks: Vec, - ) -> Result, CoreError> { - let mut lock = self.add_blocks.lock().await; - lock.extend(blocks); - Ok(BTreeSet::new()) - } - - async fn new_block(&self, _round: Round, _force: bool) -> Result<(), CoreError> { - Ok(()) - } - - async fn get_missing_blocks(&self) -> Result, CoreError> { - let mut lock = self.missing_blocks.lock().await; - let result = lock.clone(); - lock.clear(); - Ok(result) - } - - fn set_consumer_availability(&self, _available: bool) -> Result<(), CoreError> { - todo!() - } - - fn set_last_known_proposed_round(&self, round: Round) -> Result<(), CoreError> { - let mut lock = self.last_known_proposed_round.lock(); - lock.push(round); - Ok(()) - } - } - type FetchRequestKey = (Vec, AuthorityIndex); type FetchRequestResponse = (Vec, Option); type FetchLatestBlockKey = (AuthorityIndex, Vec); @@ -1272,10 +1217,10 @@ mod tests { let context = Arc::new(context); let block_verifier = Arc::new(NoopBlockVerifier {}); let core_dispatcher = Arc::new(MockCoreThreadDispatcher::default()); + let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone())); 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())); let handle = Synchronizer::start( network_client.clone(), @@ -1318,11 +1263,11 @@ mod tests { let (context, _) = Context::new_for_test(4); let context = Arc::new(context); let block_verifier = Arc::new(NoopBlockVerifier {}); + let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone())); 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())); let handle = Synchronizer::start( network_client.clone(), @@ -1376,11 +1321,11 @@ mod tests { let (context, _) = Context::new_for_test(4); let context = Arc::new(context); let block_verifier = Arc::new(NoopBlockVerifier {}); + let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone())); 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())); // Create some test blocks let expected_blocks = (0..10) @@ -1497,7 +1442,7 @@ mod tests { // 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); + 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. diff --git a/crates/sui-core/src/consensus_handler.rs b/crates/sui-core/src/consensus_handler.rs index 05c0168b831fd..0897ed724e03f 100644 --- a/crates/sui-core/src/consensus_handler.rs +++ b/crates/sui-core/src/consensus_handler.rs @@ -10,6 +10,7 @@ use std::{ use arc_swap::ArcSwap; use async_trait::async_trait; +use consensus_core::CommitConsumerMonitor; use lru::LruCache; use mysten_metrics::{monitored_mpsc::UnboundedReceiver, monitored_scope, spawn_monitored_task}; use narwhal_config::Committee; @@ -504,12 +505,16 @@ impl MysticetiConsensusHandler { pub fn new( mut consensus_handler: ConsensusHandler, mut receiver: UnboundedReceiver, + commit_consumer_monitor: Arc, ) -> Self { let handle = spawn_monitored_task!(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; consensus_handler .handle_consensus_output_internal(consensus_output) .await; + commit_consumer_monitor.set_highest_handled_commit(commit_index); } }); Self { diff --git a/crates/sui-core/src/consensus_manager/mysticeti_manager.rs b/crates/sui-core/src/consensus_manager/mysticeti_manager.rs index 3aa2abc343e5e..8d9053f57ad06 100644 --- a/crates/sui-core/src/consensus_manager/mysticeti_manager.rs +++ b/crates/sui-core/src/consensus_manager/mysticeti_manager.rs @@ -5,7 +5,7 @@ use std::{path::PathBuf, sync::Arc}; use arc_swap::ArcSwapOption; use async_trait::async_trait; use consensus_config::{Committee, NetworkKeyPair, Parameters, ProtocolKeyPair}; -use consensus_core::{CommitConsumer, CommitIndex, ConsensusAuthority, Round}; +use consensus_core::{CommitConsumer, CommitIndex, ConsensusAuthority}; use fastcrypto::ed25519; use mysten_metrics::{monitored_mpsc::unbounded_channel, RegistryID, RegistryService}; use narwhal_executor::ExecutionState; @@ -143,9 +143,9 @@ impl ConsensusManagerTrait for MysticetiManager { let consumer = CommitConsumer::new( commit_sender, // TODO(mysticeti): remove dependency on narwhal executor - consensus_handler.last_executed_sub_dag_round() as Round, consensus_handler.last_executed_sub_dag_index() as CommitIndex, ); + let monitor = consumer.monitor(); // TODO(mysticeti): Investigate if we need to return potential errors from // AuthorityNode and add retries here? @@ -173,7 +173,7 @@ 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); + let handler = MysticetiConsensusHandler::new(consensus_handler, commit_receiver, monitor); let mut consensus_handler = self.consensus_handler.lock().await; *consensus_handler = Some(handler); } From 64a5adbc2b5c188838be706fb9b263f8550453cf Mon Sep 17 00:00:00 2001 From: Cam Swords Date: Wed, 14 Aug 2024 10:11:41 -0700 Subject: [PATCH 117/232] [move][move-2024] Fix match compilation field ordering (#18947) ## Description Revise match compilation that ensures misordered fields are sorted when bindings are created. ## Test plan Two new tests to ensure things work correctly. I will add more tests ASAP as well. --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .../tests/matching/field_ordering.exp | 1 + .../tests/matching/field_ordering.move | 19 +++++++++ .../src/hlir/match_compilation.rs | 39 ++++++++++++++----- .../move-compiler/src/shared/matching.rs | 33 +++++++--------- .../src/typing/match_analysis.rs | 14 ++++++- .../matching/field_order_counterexample.exp | 8 ++++ .../matching/field_order_counterexample.move | 11 ++++++ .../tests/move_2024/matching/stloc_error.move | 30 ++++++++++++++ 8 files changed, 125 insertions(+), 30 deletions(-) create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/matching/field_ordering.exp create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/matching/field_ordering.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/matching/field_order_counterexample.exp create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/matching/field_order_counterexample.move create mode 100644 external-crates/move/crates/move-compiler/tests/move_2024/matching/stloc_error.move diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/matching/field_ordering.exp b/external-crates/move/crates/move-compiler-transactional-tests/tests/matching/field_ordering.exp new file mode 100644 index 0000000000000..fc5a4436b29d4 --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/matching/field_ordering.exp @@ -0,0 +1 @@ +processed 3 tasks diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/matching/field_ordering.move b/external-crates/move/crates/move-compiler-transactional-tests/tests/matching/field_ordering.move new file mode 100644 index 0000000000000..0b062b9decac5 --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/matching/field_ordering.move @@ -0,0 +1,19 @@ +//# init --edition 2024.beta + +//# publish +module 0x42::m; + +public enum E { + V { zero: u64, one: u64 } +} + +fun add1(n: u64): u64 { n + 1 } + +public fun main() { + let one = match (E::V { zero: 0, one: 1}) { + E::V { one: _, zero: one } => add1(one) + }; + assert!(one == 1) +} + +//# run 0x42::m::main diff --git a/external-crates/move/crates/move-compiler/src/hlir/match_compilation.rs b/external-crates/move/crates/move-compiler/src/hlir/match_compilation.rs index cea38a40416d1..df12006c03ad6 100644 --- a/external-crates/move/crates/move-compiler/src/hlir/match_compilation.rs +++ b/external-crates/move/crates/move-compiler/src/hlir/match_compilation.rs @@ -271,7 +271,7 @@ fn compile_match_head( ) -> MatchStep { debug_print!( context.debug.match_specialization, - ("-----\ncompiling with fringe queue entry" => fringe; dbg) + ("-----\ncompiling with fringe queue entry" => fringe; sdbg) ); if matrix.is_empty() { MatchStep::Failure @@ -333,8 +333,10 @@ fn compile_match_head( // If we have an actual destructuring anywhere, we do that and take the specialized // matrix (which holds the default matrix and bindings, for our purpose). If we don't, // we just take the default matrix. + let decl_fields = context.info.struct_fields(&mident, &datatype_name).unwrap(); let unpack = if let Some((ploc, arg_types)) = matrix.first_struct_ctors() { - let fringe_binders = context.make_imm_ref_match_binders(ploc, arg_types); + let fringe_binders = + context.make_imm_ref_match_binders(decl_fields, ploc, arg_types); let fringe_exps = make_fringe_entries(&fringe_binders); let mut inner_fringe = fringe.clone(); for fringe_exp in fringe_exps.into_iter().rev() { @@ -376,7 +378,12 @@ fn compile_match_head( let mut arms = BTreeMap::new(); for (ctor, (ploc, arg_types)) in ctors { unmatched_variants.remove(&ctor); - let fringe_binders = context.make_imm_ref_match_binders(ploc, arg_types); + let decl_fields = context + .info + .enum_variant_fields(&mident, &datatype_name, &ctor) + .unwrap(); + let fringe_binders = + context.make_imm_ref_match_binders(decl_fields, ploc, arg_types); let fringe_exps = make_fringe_entries(&fringe_binders); let mut inner_fringe = fringe.clone(); for fringe_exp in fringe_exps.into_iter().rev() { @@ -898,6 +905,12 @@ fn make_arm_variant_unpack_fields( ) -> (Vec<(FringeEntry, MatchPattern)>, Vec<(Field, Var, Type)>) { let field_pats = fields.clone().map(|_key, (ndx, (_, pat))| (ndx, pat)); + let decl_fields = context + .hlir_context + .info + .enum_variant_fields(&mident, &enum_, &variant) + .unwrap(); + let field_tys = { let field_tys = fields.map(|_key, (ndx, (ty, _))| (ndx, ty)); if let Some(mut_) = mut_ref { @@ -911,13 +924,12 @@ fn make_arm_variant_unpack_fields( field_tys } }; - let fringe_binders = context.hlir_context.make_unpack_binders(pat_loc, field_tys); + let fringe_binders = + context + .hlir_context + .make_unpack_binders(decl_fields.clone(), pat_loc, field_tys); let fringe_exps = make_fringe_entries(&fringe_binders); - let decl_fields = context - .hlir_context - .info - .enum_variant_fields(&mident, &enum_, &variant); let ordered_pats = order_fields_by_decl(decl_fields, field_pats); let mut unpack_fields: Vec<(Field, Var, Type)> = vec![]; @@ -946,6 +958,11 @@ fn make_arm_struct_unpack_fields( fields: Fields<(Type, MatchPattern)>, ) -> (Vec<(FringeEntry, MatchPattern)>, Vec<(Field, Var, Type)>) { let field_pats = fields.clone().map(|_key, (ndx, (_, pat))| (ndx, pat)); + let decl_fields = context + .hlir_context + .info + .struct_fields(&mident, &struct_) + .unwrap(); let field_tys = { let field_tys = fields.map(|_key, (ndx, (ty, _))| (ndx, ty)); @@ -960,10 +977,12 @@ fn make_arm_struct_unpack_fields( field_tys } }; - let fringe_binders = context.hlir_context.make_unpack_binders(pat_loc, field_tys); + let fringe_binders = + context + .hlir_context + .make_unpack_binders(decl_fields.clone(), pat_loc, field_tys); let fringe_exps = make_fringe_entries(&fringe_binders); - let decl_fields = context.hlir_context.info.struct_fields(&mident, &struct_); let ordered_pats = order_fields_by_decl(decl_fields, field_pats); let mut unpack_fields: Vec<(Field, Var, Type)> = vec![]; diff --git a/external-crates/move/crates/move-compiler/src/shared/matching.rs b/external-crates/move/crates/move-compiler/src/shared/matching.rs index 7ef8e6da583d8..26ca069a61b9b 100644 --- a/external-crates/move/crates/move-compiler/src/shared/matching.rs +++ b/external-crates/move/crates/move-compiler/src/shared/matching.rs @@ -78,6 +78,7 @@ pub trait MatchContext { fn make_imm_ref_match_binders( &mut self, + decl_fields: UniqueMap, pattern_loc: Loc, arg_types: Fields, ) -> Vec<(Field, N::Var, N::Type)> { @@ -92,7 +93,7 @@ pub trait MatchContext { } } - let fields = order_fields_by_decl(None, arg_types.clone()); + let fields = order_fields_by_decl(decl_fields, arg_types.clone()); fields .into_iter() .map(|(_, field_name, field_type)| { @@ -107,10 +108,11 @@ pub trait MatchContext { fn make_unpack_binders( &mut self, + decl_fields: UniqueMap, pattern_loc: Loc, arg_types: Fields, ) -> Vec<(Field, N::Var, N::Type)> { - let fields = order_fields_by_decl(None, arg_types.clone()); + let fields = order_fields_by_decl(decl_fields, arg_types.clone()); fields .into_iter() .map(|(_, field_name, field_type)| { @@ -283,7 +285,8 @@ impl PatternArm { let field_pats = fields.clone().map(|_key, (ndx, (_, pat))| (ndx, pat)); let decl_fields = context .program_info() - .enum_variant_fields(&mident, &enum_, &name); + .enum_variant_fields(&mident, &enum_, &name) + .unwrap(); let ordered_pats = order_fields_by_decl(decl_fields, field_pats); for (_, _, pat) in ordered_pats.into_iter().rev() { output.pats.push_front(pat); @@ -341,7 +344,10 @@ impl PatternArm { TP::Struct(mident, struct_, _, fields) | TP::BorrowStruct(_, mident, struct_, _, fields) => { let field_pats = fields.clone().map(|_key, (ndx, (_, pat))| (ndx, pat)); - let decl_fields = context.program_info().struct_fields(&mident, &struct_); + let decl_fields = context + .program_info() + .struct_fields(&mident, &struct_) + .unwrap(); let ordered_pats = order_fields_by_decl(decl_fields, field_pats); for (_, _, pat) in ordered_pats.into_iter().rev() { output.pats.push_front(pat); @@ -921,22 +927,13 @@ fn combine_pattern_fields( /// Helper function for creating an ordered list of fields Field information and Fields. pub fn order_fields_by_decl( - decl_fields: Option>, + decl_fields: UniqueMap, fields: Fields, ) -> Vec<(usize, Field, T)> { - let mut texp_fields: Vec<(usize, Field, T)> = if let Some(field_map) = decl_fields { - fields - .into_iter() - .map(|(f, (_exp_idx, t))| (*field_map.get(&f).unwrap(), f, t)) - .collect() - } else { - // If no field map, compiler error in typing. - fields - .into_iter() - .enumerate() - .map(|(ndx, (f, (_exp_idx, t)))| (ndx, f, t)) - .collect() - }; + let mut texp_fields: Vec<(usize, Field, T)> = fields + .into_iter() + .map(|(f, (_exp_idx, t))| (*decl_fields.get(&f).unwrap(), f, t)) + .collect(); texp_fields.sort_by(|(decl_idx1, _, _), (decl_idx2, _, _)| decl_idx1.cmp(decl_idx2)); texp_fields } diff --git a/external-crates/move/crates/move-compiler/src/typing/match_analysis.rs b/external-crates/move/crates/move-compiler/src/typing/match_analysis.rs index 55dbda9a44d3e..2e6b0dde0696c 100644 --- a/external-crates/move/crates/move-compiler/src/typing/match_analysis.rs +++ b/external-crates/move/crates/move-compiler/src/typing/match_analysis.rs @@ -373,7 +373,12 @@ fn find_counterexample_impl( // recur. If we don't, we check it as a default specialization. if let Some((ploc, arg_types)) = matrix.first_struct_ctors() { let ctor_arity = arg_types.len() as u32; - let fringe_binders = context.make_imm_ref_match_binders(ploc, arg_types); + let decl_fields = context + .modules + .struct_fields(&mident, &datatype_name) + .unwrap(); + let fringe_binders = + context.make_imm_ref_match_binders(decl_fields, ploc, arg_types); let is_positional = context .modules .struct_is_positional(&mident, &datatype_name); @@ -433,7 +438,12 @@ fn find_counterexample_impl( if unmatched_variants.is_empty() { for (ctor, (ploc, arg_types)) in ctors { let ctor_arity = arg_types.len() as u32; - let fringe_binders = context.make_imm_ref_match_binders(ploc, arg_types); + let decl_fields = context + .modules + .enum_variant_fields(&mident, &datatype_name, &ctor) + .unwrap(); + let fringe_binders = + context.make_imm_ref_match_binders(decl_fields, ploc, arg_types); let is_positional = context .modules diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/matching/field_order_counterexample.exp b/external-crates/move/crates/move-compiler/tests/move_2024/matching/field_order_counterexample.exp new file mode 100644 index 0000000000000..a8fb54edd0fc8 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/matching/field_order_counterexample.exp @@ -0,0 +1,8 @@ +error[E04036]: non-exhaustive pattern + ┌─ tests/move_2024/matching/field_order_counterexample.move:8:12 + │ +8 │ match (e) { + │ ^ Pattern 'E::V { zero: _0, one: _ }' not covered + │ + = When '_0' is not 0 + diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/matching/field_order_counterexample.move b/external-crates/move/crates/move-compiler/tests/move_2024/matching/field_order_counterexample.move new file mode 100644 index 0000000000000..eccb5f946e26f --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/matching/field_order_counterexample.move @@ -0,0 +1,11 @@ +module a::m; + +public enum E { + V { zero: u64, one: u64 } +} + +public fun bad(e: &E) { + match (e) { + E::V { one: _, zero: 0 } => (), + } +} diff --git a/external-crates/move/crates/move-compiler/tests/move_2024/matching/stloc_error.move b/external-crates/move/crates/move-compiler/tests/move_2024/matching/stloc_error.move new file mode 100644 index 0000000000000..3119c3442764c --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/move_2024/matching/stloc_error.move @@ -0,0 +1,30 @@ +module a::m; + +public enum Proposal has store, copy, drop { + ConfigProposalQuorum {approved: vector, rejected: vector}, + MpcTxProposalQuorum {approved: vector, rejected: vector}, + MpcTxProposalSpecific { require_approval_users: vector, threshold: u16, approved: vector, rejected: vector }, +} + +public struct Users {} + +public fun quorum_approves(_users: &Users, _approved: &vector): bool { false } + +public(package) fun is_proposal_approved(proposal: &Proposal, users: &Users): bool { + match (proposal) { + Proposal::ConfigProposalQuorum { approved: approved, rejected: _ } => { + users.quorum_approves(approved) + }, + Proposal::MpcTxProposalQuorum { approved:approved, rejected: _ } => { + users.quorum_approves(approved) + }, + Proposal::MpcTxProposalSpecific { + require_approval_users : _, + threshold: threshold, + approved: approved, + rejected: _ + } => { + ((approved.length() as u16) >= *threshold) + } + } +} From c743bd983fe19101e9d6bb6d28bf9a250f433123 Mon Sep 17 00:00:00 2001 From: Eugene Boguslavsky Date: Wed, 14 Aug 2024 12:28:28 -0700 Subject: [PATCH 118/232] Fix chocolatey binary path (#18992) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Fix chocolatey binary path ## Test plan 👀 --- chocolatey/sui.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chocolatey/sui.nuspec b/chocolatey/sui.nuspec index 6d649bbe60e09..e241fb7aff335 100644 --- a/chocolatey/sui.nuspec +++ b/chocolatey/sui.nuspec @@ -20,7 +20,7 @@ enclosed in quotation marks, you should use an editor that supports UTF-8, not t https://github.com/MystenLabs/sui/releases/tag/mainnet-v$version$ - + From 55312989030d886e28ecbe1bddaf755b55b86ad3 Mon Sep 17 00:00:00 2001 From: Theodore Chaikalis Date: Thu, 15 Aug 2024 01:30:16 +0300 Subject: [PATCH 119/232] Update import path for @mysten/sui/transactions (#18985) import path is @mysten/sui/transaction**s** --- sdk/docs/pages/typescript/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/docs/pages/typescript/index.mdx b/sdk/docs/pages/typescript/index.mdx index d3c9e91a09570..c006083efa3f8 100644 --- a/sdk/docs/pages/typescript/index.mdx +++ b/sdk/docs/pages/typescript/index.mdx @@ -95,7 +95,7 @@ what you need to keep your code light and compact. - [`@mysten/sui/client`](/typescript/sui-client) - A client for interacting with Sui RPC nodes. - [`@mysten/sui/bcs`](/typescript/bcs) - A BCS builder with pre-defined types for Sui. -- [`@mysten/sui/transaction`](/typescript/transaction-building/basics) - Utilities for building and +- [`@mysten/sui/transactions`](/typescript/transaction-building/basics) - Utilities for building and interacting with transactions. - [`@mysten/sui/keypairs/*`](/typescript/cryptography/keypairs) - Modular exports for specific KeyPair implementations. From b490bb1716338a39706635209bfe40809dc4b505 Mon Sep 17 00:00:00 2001 From: Theodore Chaikalis Date: Thu, 15 Aug 2024 01:31:13 +0300 Subject: [PATCH 120/232] Update useSignAndExecuteTransaction.mdx (#18984) `useClient` hook should be renamed to `useSuiClient` --- .../dapp-kit/wallet-hooks/useSignAndExecuteTransaction.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/docs/pages/dapp-kit/wallet-hooks/useSignAndExecuteTransaction.mdx b/sdk/docs/pages/dapp-kit/wallet-hooks/useSignAndExecuteTransaction.mdx index 45f9aebb4d826..c6ca63d1b815e 100644 --- a/sdk/docs/pages/dapp-kit/wallet-hooks/useSignAndExecuteTransaction.mdx +++ b/sdk/docs/pages/dapp-kit/wallet-hooks/useSignAndExecuteTransaction.mdx @@ -59,14 +59,14 @@ you can pass a custom `execute` function. ```ts import { ConnectButton, - useClient, + useSuiClient, useCurrentAccount, useSignAndExecuteTransaction, } from '@mysten/dapp-kit'; import { useState } from 'react'; function MyComponent() { - const client = useClient(); + const client = useSuiClient(); const { mutate: signAndExecuteTransaction } = useSignAndExecuteTransaction({ execute: async ({ bytes, signature }) => await client.executeTransactionBlock({ From c4343a297240fe8c684efd815ffe01d953d6a2d2 Mon Sep 17 00:00:00 2001 From: Tom Cat <48447545+tx-tomcat@users.noreply.github.com> Date: Thu, 15 Aug 2024 06:01:33 +0700 Subject: [PATCH 121/232] [Linter] Unnecessary while loop (#16876) # Description This linter encourages replacing `while(true)` loops with the more idiomatic loop construct. Here's a breakdown of how it works: It checks each expression in the AST. If the expression is a While loop, it examines the condition. If the condition is always true (using the `is_condition_always_true` function), it reports a diagnostic suggesting to use loop instead. The `is_condition_always_true` function checks if the condition is a boolean literal with the value true. ## Test plan Added more use case including false positive, false negative case ## Release notes - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [X] CLI: Move will now lint against `while (true)`, which should be replaced by `loop` - [ ] Rust SDK: --------- Co-authored-by: jamedzung Co-authored-by: Todd Nowacki --- .../crates/move-compiler/src/linters/mod.rs | 32 ++++++--- .../src/linters/unnecessary_while_loop.rs | 68 +++++++++++++++++++ ...false_negative_unnecessary_while_loop.move | 11 +++ .../suppress_unnecessary_while_loop.move | 7 ++ .../true_negative_unnecessary_while_loop.move | 11 +++ .../true_positive_unnecessary_while_loop.exp | 18 +++++ .../true_positive_unnecessary_while_loop.move | 6 ++ 7 files changed, 144 insertions(+), 9 deletions(-) create mode 100644 external-crates/move/crates/move-compiler/src/linters/unnecessary_while_loop.rs create mode 100644 external-crates/move/crates/move-compiler/tests/linter/false_negative_unnecessary_while_loop.move create mode 100644 external-crates/move/crates/move-compiler/tests/linter/suppress_unnecessary_while_loop.move create mode 100644 external-crates/move/crates/move-compiler/tests/linter/true_negative_unnecessary_while_loop.move create mode 100644 external-crates/move/crates/move-compiler/tests/linter/true_positive_unnecessary_while_loop.exp create mode 100644 external-crates/move/crates/move-compiler/tests/linter/true_positive_unnecessary_while_loop.move diff --git a/external-crates/move/crates/move-compiler/src/linters/mod.rs b/external-crates/move/crates/move-compiler/src/linters/mod.rs index 01ab4d3c2902f..0457c5d511f51 100644 --- a/external-crates/move/crates/move-compiler/src/linters/mod.rs +++ b/external-crates/move/crates/move-compiler/src/linters/mod.rs @@ -8,6 +8,7 @@ use crate::{ linters::constant_naming::ConstantNamingVisitor, typing::visitor::TypingVisitor, }; pub mod constant_naming; +mod unnecessary_while_loop; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum LintLevel { @@ -34,16 +35,26 @@ pub const ALLOW_ATTR_CATEGORY: &str = "lint"; pub const LINT_WARNING_PREFIX: &str = "Lint "; pub const CONSTANT_NAMING_FILTER_NAME: &str = "constant_naming"; pub const CONSTANT_NAMING_DIAG_CODE: u8 = 1; +pub const WHILE_TRUE_TO_LOOP_FILTER_NAME: &str = "while_true"; +pub const WHILE_TRUE_TO_LOOP_DIAG_CODE: u8 = 4; pub fn known_filters() -> (Option, Vec) { ( Some(ALLOW_ATTR_CATEGORY.into()), - vec![WarningFilter::code( - Some(LINT_WARNING_PREFIX), - LinterDiagnosticCategory::Style as u8, - CONSTANT_NAMING_DIAG_CODE, - Some(CONSTANT_NAMING_FILTER_NAME), - )], + vec![ + WarningFilter::code( + Some(LINT_WARNING_PREFIX), + LinterDiagnosticCategory::Style as u8, + CONSTANT_NAMING_DIAG_CODE, + Some(CONSTANT_NAMING_FILTER_NAME), + ), + WarningFilter::code( + Some(LINT_WARNING_PREFIX), + LinterDiagnosticCategory::Complexity as u8, + WHILE_TRUE_TO_LOOP_DIAG_CODE, + Some(WHILE_TRUE_TO_LOOP_FILTER_NAME), + ), + ], ) } @@ -51,9 +62,12 @@ pub fn linter_visitors(level: LintLevel) -> Vec { match level { LintLevel::None | LintLevel::Default => vec![], LintLevel::All => { - vec![constant_naming::ConstantNamingVisitor::visitor( - ConstantNamingVisitor, - )] + vec![ + constant_naming::ConstantNamingVisitor::visitor(ConstantNamingVisitor), + unnecessary_while_loop::WhileTrueToLoop::visitor( + unnecessary_while_loop::WhileTrueToLoop, + ), + ] } } } diff --git a/external-crates/move/crates/move-compiler/src/linters/unnecessary_while_loop.rs b/external-crates/move/crates/move-compiler/src/linters/unnecessary_while_loop.rs new file mode 100644 index 0000000000000..0985a02e7b01c --- /dev/null +++ b/external-crates/move/crates/move-compiler/src/linters/unnecessary_while_loop.rs @@ -0,0 +1,68 @@ +//! Encourages replacing `while(true)` with `loop` for infinite loops in Move for clarity and conciseness. +//! Identifies `while(true)` patterns, suggesting a more idiomatic approach using `loop`. +//! Aims to enhance code readability and adherence to Rust idioms. +use crate::{ + diag, + diagnostics::{ + codes::{custom, DiagnosticInfo, Severity}, + WarningFilters, + }, + expansion::ast::Value_, + shared::CompilationEnv, + typing::{ + ast::{self as T, UnannotatedExp_}, + visitor::{TypingVisitorConstructor, TypingVisitorContext}, + }, +}; + +use super::{LinterDiagnosticCategory, LINT_WARNING_PREFIX, WHILE_TRUE_TO_LOOP_DIAG_CODE}; + +const WHILE_TRUE_TO_LOOP_DIAG: DiagnosticInfo = custom( + LINT_WARNING_PREFIX, + Severity::Warning, + LinterDiagnosticCategory::Complexity as u8, + WHILE_TRUE_TO_LOOP_DIAG_CODE, + "unnecessary 'while (true)', replace with 'loop'", +); + +pub struct WhileTrueToLoop; + +pub struct Context<'a> { + env: &'a mut CompilationEnv, +} + +impl TypingVisitorConstructor for WhileTrueToLoop { + type Context<'a> = Context<'a>; + + fn context<'a>(env: &'a mut CompilationEnv, _program: &T::Program) -> Self::Context<'a> { + Context { env } + } +} + +impl TypingVisitorContext for Context<'_> { + fn add_warning_filter_scope(&mut self, filter: WarningFilters) { + self.env.add_warning_filter_scope(filter) + } + fn pop_warning_filter_scope(&mut self) { + self.env.pop_warning_filter_scope() + } + + fn visit_exp_custom(&mut self, exp: &mut T::Exp) -> bool { + let UnannotatedExp_::While(_, cond, _) = &exp.exp.value else { + return false; + }; + let UnannotatedExp_::Value(sp!(_, Value_::Bool(true))) = &cond.exp.value else { + return false; + }; + + let msg = "'while (true)' can be always replaced with 'loop'"; + let mut diag = diag!(WHILE_TRUE_TO_LOOP_DIAG, (exp.exp.loc, msg)); + diag.add_note( + "A 'loop' is more useful in these cases. Unlike 'while', 'loop' can have a \ + 'break' with a value, e.g. 'let x = loop { break 42 };'", + ); + self.env.add_diag(diag); + + false + } +} diff --git a/external-crates/move/crates/move-compiler/tests/linter/false_negative_unnecessary_while_loop.move b/external-crates/move/crates/move-compiler/tests/linter/false_negative_unnecessary_while_loop.move new file mode 100644 index 0000000000000..0f06f05bc8e51 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/linter/false_negative_unnecessary_while_loop.move @@ -0,0 +1,11 @@ +module 0x42::loop_test { + + // These should trigger but currently dont + public fun false_negative_obfuscated_true() { + let always_true = true; + while (always_true) {}; + while (true && true) {}; + while (true || false) {}; + while (1 > 0) {}; + } +} diff --git a/external-crates/move/crates/move-compiler/tests/linter/suppress_unnecessary_while_loop.move b/external-crates/move/crates/move-compiler/tests/linter/suppress_unnecessary_while_loop.move new file mode 100644 index 0000000000000..1477d966a74c3 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/linter/suppress_unnecessary_while_loop.move @@ -0,0 +1,7 @@ +module 0x42::loop_test { + + #[allow(lint(while_true))] + public fun suppressed_while_true() { + while (true) {}; + } +} diff --git a/external-crates/move/crates/move-compiler/tests/linter/true_negative_unnecessary_while_loop.move b/external-crates/move/crates/move-compiler/tests/linter/true_negative_unnecessary_while_loop.move new file mode 100644 index 0000000000000..d5ad1e0cfb6f6 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/linter/true_negative_unnecessary_while_loop.move @@ -0,0 +1,11 @@ +module 0x42::loop_test { + + public fun true_negative_while_with_condition() { + let b = false; + while (false) {}; + while (b) {}; + while (false && true) {}; + while (false || false) {}; + while (0 > 1) {}; + } +} diff --git a/external-crates/move/crates/move-compiler/tests/linter/true_positive_unnecessary_while_loop.exp b/external-crates/move/crates/move-compiler/tests/linter/true_positive_unnecessary_while_loop.exp new file mode 100644 index 0000000000000..2ba9789c925b3 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/linter/true_positive_unnecessary_while_loop.exp @@ -0,0 +1,18 @@ +warning[Lint W01004]: unnecessary 'while (true)', replace with 'loop' + ┌─ tests/linter/true_positive_unnecessary_while_loop.move:3:9 + │ +3 │ while (true) {}; + │ ^^^^^^^^^^^^^^^ 'while (true)' can be always replaced with 'loop' + │ + = A 'loop' is more useful in these cases. Unlike 'while', 'loop' can have a 'break' with a value, e.g. 'let x = loop { break 42 };' + = This warning can be suppressed with '#[allow(lint(while_true))]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[Lint W01004]: unnecessary 'while (true)', replace with 'loop' + ┌─ tests/linter/true_positive_unnecessary_while_loop.move:4:9 + │ +4 │ while (true) { break } + │ ^^^^^^^^^^^^^^^^^^^^^^ 'while (true)' can be always replaced with 'loop' + │ + = A 'loop' is more useful in these cases. Unlike 'while', 'loop' can have a 'break' with a value, e.g. 'let x = loop { break 42 };' + = This warning can be suppressed with '#[allow(lint(while_true))]' applied to the 'module' or module member ('const', 'fun', or 'struct') + diff --git a/external-crates/move/crates/move-compiler/tests/linter/true_positive_unnecessary_while_loop.move b/external-crates/move/crates/move-compiler/tests/linter/true_positive_unnecessary_while_loop.move new file mode 100644 index 0000000000000..eaf03b83ba5e8 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/linter/true_positive_unnecessary_while_loop.move @@ -0,0 +1,6 @@ +module 0x42::loop_test { + public fun true_positive_infinite_loop() { + while (true) {}; + while (true) { break } + } +} From 474cc58f32fe52119b2c4fe43ca775d69460fed9 Mon Sep 17 00:00:00 2001 From: Tom Cat <48447545+tx-tomcat@users.noreply.github.com> Date: Thu, 15 Aug 2024 06:38:24 +0700 Subject: [PATCH 122/232] [Linter] Public mut tx context (#16878) ## Description This linter checks public functions to ensure they use `&mut TxContext` instead of `&TxContext `for better upgradability. Here's a step-by-step breakdown of how to implement this linter: Implement the main logic : `check_function_parameters`: Iterate through function parameters. `is_immutable_tx_context`: Check if a parameter is an immutable `TxContext`. `is_tx_context_type`: Check if a type is `TxContext`. `is_sui_tx_context`: Verify if the type is from the `sui::tx_context` module. ## Test plan Added more use case including false positive, false negative case ## Release notes - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [X] CLI: Move will now lint against using `&TxContext` instead of `&mut TxContext` in public functions - [ ] Rust SDK: --------- Co-authored-by: jamedzung Co-authored-by: Todd Nowacki --- .../move-compiler/src/sui_mode/linters/mod.rs | 14 ++- .../sui_mode/linters/public_mut_tx_context.rs | 97 +++++++++++++++++++ .../sui_mode/linter/custom_state_change.move | 2 +- .../suppress_public_mut_tx_context.move | 20 ++++ .../true_negative_public_mut_tx_context.move | 26 +++++ .../true_positive_public_mut_tx_context.exp | 36 +++++++ .../true_positive_public_mut_tx_context.move | 29 ++++++ 7 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 external-crates/move/crates/move-compiler/src/sui_mode/linters/public_mut_tx_context.rs create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/suppress_public_mut_tx_context.move create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/true_negative_public_mut_tx_context.move create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/true_positive_public_mut_tx_context.exp create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/true_positive_public_mut_tx_context.move diff --git a/external-crates/move/crates/move-compiler/src/sui_mode/linters/mod.rs b/external-crates/move/crates/move-compiler/src/sui_mode/linters/mod.rs index 84e3e7ed9a125..d8e21d6bb3167 100644 --- a/external-crates/move/crates/move-compiler/src/sui_mode/linters/mod.rs +++ b/external-crates/move/crates/move-compiler/src/sui_mode/linters/mod.rs @@ -20,6 +20,7 @@ pub mod custom_state_change; pub mod freeze_wrapped; pub mod freezing_capability; pub mod missing_key; +pub mod public_mut_tx_context; pub mod public_random; pub mod self_transfer; pub mod share_owned; @@ -72,6 +73,7 @@ pub const COLLECTION_EQUALITY_FILTER_NAME: &str = "collection_equality"; pub const PUBLIC_RANDOM_FILTER_NAME: &str = "public_random"; pub const MISSING_KEY_FILTER_NAME: &str = "missing_key"; pub const FREEZING_CAPABILITY_FILTER_NAME: &str = "freezing_capability"; +pub const PREFER_MUTABLE_TX_CONTEXT_FILTER_NAME: &str = "prefer_mut_tx_context"; pub const RANDOM_MOD_NAME: &str = "random"; pub const RANDOM_STRUCT_NAME: &str = "Random"; @@ -90,6 +92,7 @@ pub enum LinterDiagnosticCode { PublicRandom, MissingKey, FreezingCapability, + PreferMutableTxContext, } pub fn known_filters() -> (Option, Vec) { @@ -149,6 +152,12 @@ pub fn known_filters() -> (Option, Vec) { LinterDiagnosticCode::FreezingCapability as u8, Some(FREEZING_CAPABILITY_FILTER_NAME), ), + WarningFilter::code( + Some(LINT_WARNING_PREFIX), + LinterDiagnosticCategory::Sui as u8, + LinterDiagnosticCode::PreferMutableTxContext as u8, + Some(PREFER_MUTABLE_TX_CONTEXT_FILTER_NAME), + ), ]; (Some(ALLOW_ATTR_CATEGORY.into()), filters) @@ -169,7 +178,10 @@ pub fn linter_visitors(level: LintLevel) -> Vec { ], LintLevel::All => { let mut visitors = linter_visitors(LintLevel::Default); - visitors.extend([freezing_capability::WarnFreezeCapability.visitor()]); + visitors.extend([ + freezing_capability::WarnFreezeCapability.visitor(), + public_mut_tx_context::PreferMutableTxContext.visitor(), + ]); visitors } } diff --git a/external-crates/move/crates/move-compiler/src/sui_mode/linters/public_mut_tx_context.rs b/external-crates/move/crates/move-compiler/src/sui_mode/linters/public_mut_tx_context.rs new file mode 100644 index 0000000000000..c664fa1aa3fb0 --- /dev/null +++ b/external-crates/move/crates/move-compiler/src/sui_mode/linters/public_mut_tx_context.rs @@ -0,0 +1,97 @@ +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +//! Enforces that public functions use `&mut TxContext` instead of `&TxContext` to ensure upgradability. +//! Detects and reports instances where a non-mutable reference to `TxContext` is used in public function signatures. +//! Promotes best practices for future-proofing smart contract code by allowing mutation of the transaction context. +use super::{LinterDiagnosticCategory, LinterDiagnosticCode, LINT_WARNING_PREFIX}; + +use crate::{ + diag, + diagnostics::{ + codes::{custom, DiagnosticInfo, Severity}, + WarningFilters, + }, + expansion::ast::{ModuleIdent, Visibility}, + naming::ast::Type_, + parser::ast::FunctionName, + shared::CompilationEnv, + sui_mode::{SUI_ADDR_NAME, TX_CONTEXT_MODULE_NAME, TX_CONTEXT_TYPE_NAME}, + typing::{ + ast as T, + visitor::{TypingVisitorConstructor, TypingVisitorContext}, + }, +}; +use move_ir_types::location::Loc; + +const REQUIRE_MUTABLE_TX_CONTEXT_DIAG: DiagnosticInfo = custom( + LINT_WARNING_PREFIX, + Severity::Warning, + LinterDiagnosticCategory::Sui as u8, + LinterDiagnosticCode::PreferMutableTxContext as u8, + "prefer '&mut TxContext' over '&TxContext'", +); + +pub struct PreferMutableTxContext; + +pub struct Context<'a> { + env: &'a mut CompilationEnv, +} + +impl TypingVisitorConstructor for PreferMutableTxContext { + type Context<'a> = Context<'a>; + + fn context<'a>(env: &'a mut CompilationEnv, _program: &T::Program) -> Self::Context<'a> { + Context { env } + } +} + +impl TypingVisitorContext for Context<'_> { + fn add_warning_filter_scope(&mut self, filter: WarningFilters) { + self.env.add_warning_filter_scope(filter) + } + fn pop_warning_filter_scope(&mut self) { + self.env.pop_warning_filter_scope() + } + + fn visit_module_custom(&mut self, ident: ModuleIdent, _mdef: &mut T::ModuleDefinition) -> bool { + // skip if in 'sui::tx_context' + ident.value.is(SUI_ADDR_NAME, TX_CONTEXT_MODULE_NAME) + } + + fn visit_function_custom( + &mut self, + _module: ModuleIdent, + _function_name: FunctionName, + fdef: &mut T::Function, + ) -> bool { + if !matches!(&fdef.visibility, Visibility::Public(_)) { + return false; + } + + for (_, _, sp!(loc, param_ty_)) in &fdef.signature.parameters { + if matches!( + param_ty_, + Type_::Ref(false, t) if t.value.is(SUI_ADDR_NAME, TX_CONTEXT_MODULE_NAME, TX_CONTEXT_TYPE_NAME), + ) { + report_non_mutable_tx_context(self.env, *loc); + } + } + + false + } +} + +fn report_non_mutable_tx_context(env: &mut CompilationEnv, loc: Loc) { + let msg = format!( + "'public' functions should prefer '&mut {0}' over '&{0}' for better upgradability.", + TX_CONTEXT_TYPE_NAME + ); + let mut diag = diag!(REQUIRE_MUTABLE_TX_CONTEXT_DIAG, (loc, msg)); + diag.add_note( + "When upgrading, the public function cannot be modified to take '&mut TxContext' instead \ + of '&TxContext'. As such, it is recommended to consider using '&mut TxContext' to \ + future-proof the function.", + ); + env.add_diag(diag); +} diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/custom_state_change.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/custom_state_change.move index 37edbb43fc4a7..ff6151946908a 100644 --- a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/custom_state_change.move +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/custom_state_change.move @@ -11,7 +11,7 @@ module a::test { id: UID } - #[allow(lint(self_transfer))] + #[allow(lint(self_transfer, prefer_mut_tx_context))] public fun custom_transfer_bad(o: S1, ctx: &TxContext) { transfer::transfer(o, tx_context::sender(ctx)) } diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/suppress_public_mut_tx_context.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/suppress_public_mut_tx_context.move new file mode 100644 index 0000000000000..2d8f98657902c --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/suppress_public_mut_tx_context.move @@ -0,0 +1,20 @@ +module 0x42::suppress_cases { + use sui::tx_context::TxContext; + + #[allow(lint(prefer_mut_tx_context))] + public fun suppressed_function(_ctx: &TxContext) { + } + + #[allow(lint(prefer_mut_tx_context))] + public fun multi_suppressed_function(_ctx: &TxContext) { + } + + #[allow(lint(prefer_mut_tx_context))] + public fun suppressed_multi_param(_a: u64, _ctx: &TxContext, _b: &mut TxContext) { + } +} + +// Mocking the sui::tx_context module +module sui::tx_context { + struct TxContext has drop {} +} diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/true_negative_public_mut_tx_context.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/true_negative_public_mut_tx_context.move new file mode 100644 index 0000000000000..1796cb046a7b7 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/true_negative_public_mut_tx_context.move @@ -0,0 +1,26 @@ +// tests the lint for preferring &mut TxContext over &TxContext in public functions +// these cases correctly should not trigger the lint +module 0x42::true_negative { + use sui::tx_context::TxContext; + + public fun correct_mint(_ctx: &mut TxContext) { + } + + public fun another_correct(_a: u64, _b: &mut TxContext, _c: u64) { + } + + fun private_function(_ctx: &TxContext) { + } + + public fun custom_module(_b: &mut sui::mock_tx_context::TxContext) {} + + +} + +module sui::tx_context { + struct TxContext has drop {} +} + +module sui::mock_tx_context { + struct TxContext has drop {} +} diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/true_positive_public_mut_tx_context.exp b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/true_positive_public_mut_tx_context.exp new file mode 100644 index 0000000000000..981d0e737b750 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/true_positive_public_mut_tx_context.exp @@ -0,0 +1,36 @@ +warning[Lint W99009]: prefer '&mut TxContext' over '&TxContext' + ┌─ tests/sui_mode/linter/true_positive_public_mut_tx_context.move:8:37 + │ +8 │ public fun incorrect_mint(_ctx: &TxContext) { + │ ^^^^^^^^^^ 'public' functions should prefer '&mut TxContext' over '&TxContext' for better upgradability. + │ + = When upgrading, the public function cannot be modified to take '&mut TxContext' instead of '&TxContext'. As such, it is recommended to consider using '&mut TxContext' to future-proof the function. + = This warning can be suppressed with '#[allow(lint(prefer_mut_tx_context))]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[Lint W99009]: prefer '&mut TxContext' over '&TxContext' + ┌─ tests/sui_mode/linter/true_positive_public_mut_tx_context.move:11:47 + │ +11 │ public fun another_incorrect(_a: u64, _b: &TxContext, _c: u64) { + │ ^^^^^^^^^^ 'public' functions should prefer '&mut TxContext' over '&TxContext' for better upgradability. + │ + = When upgrading, the public function cannot be modified to take '&mut TxContext' instead of '&TxContext'. As such, it is recommended to consider using '&mut TxContext' to future-proof the function. + = This warning can be suppressed with '#[allow(lint(prefer_mut_tx_context))]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[Lint W99009]: prefer '&mut TxContext' over '&TxContext' + ┌─ tests/sui_mode/linter/true_positive_public_mut_tx_context.move:14:54 + │ +14 │ public fun mixed_function(_a: &CustomStruct, _b: &TxContext, _c: &mut TxContext) {} + │ ^^^^^^^^^^ 'public' functions should prefer '&mut TxContext' over '&TxContext' for better upgradability. + │ + = When upgrading, the public function cannot be modified to take '&mut TxContext' instead of '&TxContext'. As such, it is recommended to consider using '&mut TxContext' to future-proof the function. + = This warning can be suppressed with '#[allow(lint(prefer_mut_tx_context))]' applied to the 'module' or module member ('const', 'fun', or 'struct') + +warning[Lint W99009]: prefer '&mut TxContext' over '&TxContext' + ┌─ tests/sui_mode/linter/true_positive_public_mut_tx_context.move:20:13 + │ +20 │ _b: &TxContext, // Should warn + │ ^^^^^^^^^^ 'public' functions should prefer '&mut TxContext' over '&TxContext' for better upgradability. + │ + = When upgrading, the public function cannot be modified to take '&mut TxContext' instead of '&TxContext'. As such, it is recommended to consider using '&mut TxContext' to future-proof the function. + = This warning can be suppressed with '#[allow(lint(prefer_mut_tx_context))]' applied to the 'module' or module member ('const', 'fun', or 'struct') + diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/true_positive_public_mut_tx_context.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/true_positive_public_mut_tx_context.move new file mode 100644 index 0000000000000..cee7d73f1b2e7 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/true_positive_public_mut_tx_context.move @@ -0,0 +1,29 @@ +// tests the lint for preferring &mut TxContext over &TxContext in public functions +// these cases correctly should trigger the lint +module 0x42::true_positive { + use sui::tx_context::TxContext; + + struct CustomStruct has drop {} + + public fun incorrect_mint(_ctx: &TxContext) { + } + + public fun another_incorrect(_a: u64, _b: &TxContext, _c: u64) { + } + + public fun mixed_function(_a: &CustomStruct, _b: &TxContext, _c: &mut TxContext) {} + + fun private_function(_ctx: &TxContext) {} + + public fun complex_function( + _a: u64, + _b: &TxContext, // Should warn + _c: &mut TxContext, + _d: &T, + _e: &CustomStruct + ) {} +} + +module sui::tx_context { + struct TxContext has drop {} +} From 220f2ce857e559286cfdb81b0a9106420ff7aea1 Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Sun, 4 Aug 2024 18:03:30 +0100 Subject: [PATCH 123/232] chore: [Source Validation] Split out error types (#18956) ## Description Move the error and aggregate error types into their own module, to declutter main source validation module. ## Test plan ``` sui-source-validation$ cargo nextest run ``` --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-source-validation/src/error.rs | 82 ++++++++++ crates/sui-source-validation/src/lib.rs | 175 ++++++---------------- 2 files changed, 130 insertions(+), 127 deletions(-) create mode 100644 crates/sui-source-validation/src/error.rs diff --git a/crates/sui-source-validation/src/error.rs b/crates/sui-source-validation/src/error.rs new file mode 100644 index 0000000000000..9f0be2545c22b --- /dev/null +++ b/crates/sui-source-validation/src/error.rs @@ -0,0 +1,82 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt; + +use move_core_types::account_address::AccountAddress; +use move_symbol_pool::Symbol; +use sui_json_rpc_types::SuiRawMoveObject; +use sui_sdk::error::Error as SdkError; +use sui_types::{base_types::ObjectID, error::SuiObjectResponseError}; + +#[derive(Debug, thiserror::Error)] +pub struct AggregateError(pub(crate) Vec); + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Cannot check local module for {package}: {message}")] + CannotCheckLocalModules { package: Symbol, message: String }, + + #[error("Could not read a dependency's on-chain object: {0:?}")] + DependencyObjectReadFailure(SdkError), + + #[error("Invalid module {name} with error: {message}")] + InvalidModuleFailure { name: String, message: String }, + + #[error("Local version of dependency {address}::{module} was not found.")] + LocalDependencyNotFound { + address: AccountAddress, + module: Symbol, + }, + + #[error( + "Local dependency did not match its on-chain version at {address}::{package}::{module}" + )] + ModuleBytecodeMismatch { + address: AccountAddress, + package: Symbol, + module: Symbol, + }, + + #[error("Dependency ID contains a Sui object, not a Move package: {0}")] + ObjectFoundWhenPackageExpected(ObjectID, SuiRawMoveObject), + + #[error("Could not deserialize on-chain dependency {address}::{module}.")] + OnChainDependencyDeserializationError { + address: AccountAddress, + module: Symbol, + }, + + #[error("On-chain version of dependency {package}::{module} was not found.")] + OnChainDependencyNotFound { package: Symbol, module: Symbol }, + + #[error("Dependency object does not exist or was deleted: {0:?}")] + SuiObjectRefFailure(SuiObjectResponseError), + + #[error("On-chain address cannot be zero")] + ZeroOnChainAddresSpecifiedFailure, +} + +impl fmt::Display for AggregateError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let Self(errors) = self; + match &errors[..] { + [] => unreachable!("Aggregate error with no errors"), + [error] => write!(f, "{}", error)?, + errors => { + writeln!(f, "Multiple source verification errors found:")?; + for error in errors { + write!(f, "\n- {}", error)?; + } + return Ok(()); + } + }; + Ok(()) + } +} + +impl From for AggregateError { + fn from(error: Error) -> Self { + Self(vec![error]) + } +} diff --git a/crates/sui-source-validation/src/lib.rs b/crates/sui-source-validation/src/lib.rs index 03911a2e00d82..1deda695c968c 100644 --- a/crates/sui-source-validation/src/lib.rs +++ b/crates/sui-source-validation/src/lib.rs @@ -1,47 +1,45 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use crate::error::{AggregateError, Error}; use anyhow::{anyhow, bail, ensure}; use colored::Colorize; -use core::fmt; use futures::future; use move_binary_format::CompiledModule; use move_bytecode_source_map::utils::source_map_from_file; +use move_command_line_common::env::MOVE_HOME; +use move_command_line_common::files::MOVE_COMPILED_EXTENSION; +use move_command_line_common::files::{ + extension_equals, find_filenames, MOVE_EXTENSION, SOURCE_MAP_EXTENSION, +}; +use move_compiler::compiled_unit::NamedCompiledModule; use move_compiler::editions::{Edition, Flavor}; use move_compiler::shared::NumericalAddress; +use move_core_types::account_address::AccountAddress; +use move_package::compilation::compiled_package::{ + CompiledPackage as MoveCompiledPackage, CompiledUnitWithSource, +}; use move_package::compilation::package_layout::CompiledPackageLayout; use move_package::lock_file::schema::{Header, ToolchainVersion}; use move_package::source_package::layout::SourcePackageLayout; use move_package::source_package::parsed_manifest::{FileName, PackageName}; +use move_symbol_pool::Symbol; +use std::collections::HashMap; use std::ffi::OsStr; use std::fs::File; use std::io::{self, Seek}; use std::path::{Path, PathBuf}; use std::process::Command; -use std::{collections::HashMap, fmt::Debug}; use sui_move_build::CompiledPackage; -use sui_types::error::SuiObjectResponseError; +use sui_sdk::apis::ReadApi; +use sui_sdk::error::Error as SdkError; +use sui_sdk::rpc_types::{SuiObjectDataOptions, SuiRawData, SuiRawMovePackage}; +use sui_types::base_types::ObjectID; use tar::Archive; use tempfile::TempDir; -use thiserror::Error; use tracing::{debug, info}; -use move_command_line_common::env::MOVE_HOME; -use move_command_line_common::files::MOVE_COMPILED_EXTENSION; -use move_command_line_common::files::{ - extension_equals, find_filenames, MOVE_EXTENSION, SOURCE_MAP_EXTENSION, -}; -use move_compiler::compiled_unit::NamedCompiledModule; -use move_core_types::account_address::AccountAddress; -use move_package::compilation::compiled_package::{ - CompiledPackage as MoveCompiledPackage, CompiledUnitWithSource, -}; -use move_symbol_pool::Symbol; -use sui_sdk::apis::ReadApi; -use sui_sdk::error::Error; - -use sui_sdk::rpc_types::{SuiObjectDataOptions, SuiRawData, SuiRawMoveObject, SuiRawMovePackage}; -use sui_types::base_types::ObjectID; +pub mod error; #[cfg(test)] mod tests; @@ -52,78 +50,6 @@ const PRE_TOOLCHAIN_MOVE_LOCK_VERSION: u64 = 0; // Used to detect lockfiles pre- const CANONICAL_UNIX_BINARY_NAME: &str = "sui"; const CANONICAL_WIN_BINARY_NAME: &str = "sui.exe"; -#[derive(Debug, Error)] -pub enum SourceVerificationError { - #[error("Could not read a dependency's on-chain object: {0:?}")] - DependencyObjectReadFailure(Error), - - #[error("Dependency object does not exist or was deleted: {0:?}")] - SuiObjectRefFailure(SuiObjectResponseError), - - #[error("Dependency ID contains a Sui object, not a Move package: {0}")] - ObjectFoundWhenPackageExpected(ObjectID, SuiRawMoveObject), - - #[error("On-chain version of dependency {package}::{module} was not found.")] - OnChainDependencyNotFound { package: Symbol, module: Symbol }, - - #[error("Could not deserialize on-chain dependency {address}::{module}.")] - OnChainDependencyDeserializationError { - address: AccountAddress, - module: Symbol, - }, - - #[error("Local version of dependency {address}::{module} was not found.")] - LocalDependencyNotFound { - address: AccountAddress, - module: Symbol, - }, - - #[error( - "Local dependency did not match its on-chain version at {address}::{package}::{module}" - )] - ModuleBytecodeMismatch { - address: AccountAddress, - package: Symbol, - module: Symbol, - }, - - #[error("Cannot check local module for {package}: {message}")] - CannotCheckLocalModules { package: Symbol, message: String }, - - #[error("On-chain address cannot be zero")] - ZeroOnChainAddresSpecifiedFailure, - - #[error("Invalid module {name} with error: {message}")] - InvalidModuleFailure { name: String, message: String }, -} - -#[derive(Debug, Error)] -pub struct AggregateSourceVerificationError(Vec); - -impl From for AggregateSourceVerificationError { - fn from(error: SourceVerificationError) -> Self { - AggregateSourceVerificationError(vec![error]) - } -} - -impl fmt::Display for AggregateSourceVerificationError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let AggregateSourceVerificationError(errors) = self; - match &errors[..] { - [] => unreachable!("Aggregate error with no errors"), - [error] => write!(f, "{}", error)?, - errors => { - writeln!(f, "Multiple source verification errors found:")?; - for error in errors { - write!(f, "\n- {}", error)?; - } - return Ok(()); - } - }; - Ok(()) - } -} - /// How to handle package source during bytecode verification. #[derive(PartialEq, Eq)] pub enum SourceMode { @@ -158,7 +84,7 @@ impl<'a> BytecodeSourceVerifier<'a> { &self, compiled_package: &CompiledPackage, root_on_chain_address: AccountAddress, - ) -> Result<(), AggregateSourceVerificationError> { + ) -> Result<(), AggregateError> { self.verify_package( compiled_package, /* verify_deps */ true, @@ -173,7 +99,7 @@ impl<'a> BytecodeSourceVerifier<'a> { &self, compiled_package: &CompiledPackage, root_on_chain_address: AccountAddress, - ) -> Result<(), AggregateSourceVerificationError> { + ) -> Result<(), AggregateError> { self.verify_package( compiled_package, /* verify_deps */ false, @@ -187,7 +113,7 @@ impl<'a> BytecodeSourceVerifier<'a> { pub async fn verify_package_deps( &self, compiled_package: &CompiledPackage, - ) -> Result<(), AggregateSourceVerificationError> { + ) -> Result<(), AggregateError> { self.verify_package( compiled_package, /* verify_deps */ true, @@ -205,13 +131,13 @@ impl<'a> BytecodeSourceVerifier<'a> { compiled_package: &CompiledPackage, verify_deps: bool, source_mode: SourceMode, - ) -> Result<(), AggregateSourceVerificationError> { + ) -> Result<(), AggregateError> { let mut on_chain_pkgs = vec![]; match &source_mode { SourceMode::Skip => (), // On-chain address for matching root package cannot be zero SourceMode::VerifyAt(AccountAddress::ZERO) => { - return Err(SourceVerificationError::ZeroOnChainAddresSpecifiedFailure.into()) + return Err(Error::ZeroOnChainAddresSpecifiedFailure.into()) } SourceMode::VerifyAt(root_address) => on_chain_pkgs.push(*root_address), SourceMode::Verify => { @@ -235,14 +161,14 @@ impl<'a> BytecodeSourceVerifier<'a> { let mut errors = Vec::new(); for ((address, module), (package, local_module)) in local_modules { let Some(on_chain_module) = on_chain_modules.remove(&(address, module)) else { - errors.push(SourceVerificationError::OnChainDependencyNotFound { package, module }); + errors.push(Error::OnChainDependencyNotFound { package, module }); continue; }; // compare local bytecode to on-chain bytecode to ensure integrity of our // dependencies if local_module != on_chain_module { - errors.push(SourceVerificationError::ModuleBytecodeMismatch { + errors.push(Error::ModuleBytecodeMismatch { address, package, module, @@ -251,20 +177,17 @@ impl<'a> BytecodeSourceVerifier<'a> { } if let Some(((address, module), _)) = on_chain_modules.into_iter().next() { - errors.push(SourceVerificationError::LocalDependencyNotFound { address, module }); + errors.push(Error::LocalDependencyNotFound { address, module }); } if !errors.is_empty() { - return Err(AggregateSourceVerificationError(errors)); + return Err(AggregateError(errors)); } Ok(()) } - async fn pkg_for_address( - &self, - addr: AccountAddress, - ) -> Result { + async fn pkg_for_address(&self, addr: AccountAddress) -> Result { // Move packages are specified with an AccountAddress, but are // fetched from a sui network via sui_getObject, which takes an object ID let obj_id = ObjectID::from(addr); @@ -276,30 +199,30 @@ impl<'a> BytecodeSourceVerifier<'a> { .rpc_client .get_object_with_options(obj_id, SuiObjectDataOptions::new().with_bcs()) .await - .map_err(SourceVerificationError::DependencyObjectReadFailure)?; + .map_err(Error::DependencyObjectReadFailure)?; let obj = obj_read .into_object() - .map_err(SourceVerificationError::SuiObjectRefFailure)? + .map_err(Error::SuiObjectRefFailure)? .bcs .ok_or_else(|| { - SourceVerificationError::DependencyObjectReadFailure(Error::DataError( + Error::DependencyObjectReadFailure(SdkError::DataError( "Bcs field is not found".to_string(), )) })?; match obj { SuiRawData::Package(pkg) => Ok(pkg), - SuiRawData::MoveObject(move_obj) => Err( - SourceVerificationError::ObjectFoundWhenPackageExpected(obj_id, move_obj), - ), + SuiRawData::MoveObject(move_obj) => { + Err(Error::ObjectFoundWhenPackageExpected(obj_id, move_obj)) + } } } async fn on_chain_modules( &self, addresses: impl Iterator + Clone, - ) -> Result { + ) -> Result { let resp = future::join_all(addresses.clone().map(|addr| self.pkg_for_address(addr))).await; let mut map = OnChainModules::new(); let mut err = vec![]; @@ -308,12 +231,10 @@ impl<'a> BytecodeSourceVerifier<'a> { let SuiRawMovePackage { module_map, .. } = pkg?; for (name, bytes) in module_map { let Ok(module) = CompiledModule::deserialize_with_defaults(&bytes) else { - err.push( - SourceVerificationError::OnChainDependencyDeserializationError { - address: storage_id, - module: name.into(), - }, - ); + err.push(Error::OnChainDependencyDeserializationError { + address: storage_id, + module: name.into(), + }); continue; }; @@ -323,7 +244,7 @@ impl<'a> BytecodeSourceVerifier<'a> { } if !err.is_empty() { - return Err(AggregateSourceVerificationError(err)); + return Err(AggregateError(err)); } Ok(map) @@ -333,19 +254,19 @@ impl<'a> BytecodeSourceVerifier<'a> { fn substitute_root_address( named_module: &NamedCompiledModule, root: AccountAddress, -) -> Result { +) -> Result { let mut module = named_module.module.clone(); let address_idx = module.self_handle().address; let Some(addr) = module.address_identifiers.get_mut(address_idx.0 as usize) else { - return Err(SourceVerificationError::InvalidModuleFailure { + return Err(Error::InvalidModuleFailure { name: named_module.name.to_string(), message: "Self address field missing".into(), }); }; if *addr != AccountAddress::ZERO { - return Err(SourceVerificationError::InvalidModuleFailure { + return Err(Error::InvalidModuleFailure { name: named_module.name.to_string(), message: "Self address already populated".to_string(), }); @@ -359,13 +280,13 @@ fn local_modules( compiled_package: &MoveCompiledPackage, include_deps: bool, source_mode: SourceMode, -) -> Result { +) -> Result { let mut map = LocalModules::new(); if include_deps { // Compile dependencies with prior compilers if needed. let deps_compiled_units = units_for_toolchain(&compiled_package.deps_compiled_units) - .map_err(|e| SourceVerificationError::CannotCheckLocalModules { + .map_err(|e| Error::CannotCheckLocalModules { package: compiled_package.compiled_package_info.package_name, message: e.to_string(), })?; @@ -397,7 +318,7 @@ fn local_modules( .collect::>(); units_for_toolchain(&root_compiled_units).map_err(|e| { - SourceVerificationError::CannotCheckLocalModules { + Error::CannotCheckLocalModules { package: compiled_package.compiled_package_info.package_name, message: e.to_string(), } @@ -410,7 +331,7 @@ fn local_modules( let module = m.name; let address = m.address.into_inner(); if address == AccountAddress::ZERO { - return Err(SourceVerificationError::InvalidModuleFailure { + return Err(Error::InvalidModuleFailure { name: module.to_string(), message: "Can't verify unpublished source".to_string(), }); @@ -432,7 +353,7 @@ fn local_modules( .collect::>(); units_for_toolchain(&root_compiled_units).map_err(|e| { - SourceVerificationError::CannotCheckLocalModules { + Error::CannotCheckLocalModules { package: compiled_package.compiled_package_info.package_name, message: e.to_string(), } From adfa70fd9cbed0fa3a47b9f008d458816f6dbf19 Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Wed, 14 Aug 2024 18:38:29 +0100 Subject: [PATCH 124/232] chore(verify-source): Split out toolchain logic (#18959) ## Description Move logic for dealing with toolchain versioning into its own module. ## Test plan ``` sui-source-validation$ cargo nextest run ``` ## Stack - #18956 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-source-validation/src/lib.rs | 376 +---------------- crates/sui-source-validation/src/tests.rs | 3 +- crates/sui-source-validation/src/toolchain.rs | 387 ++++++++++++++++++ 3 files changed, 392 insertions(+), 374 deletions(-) create mode 100644 crates/sui-source-validation/src/toolchain.rs diff --git a/crates/sui-source-validation/src/lib.rs b/crates/sui-source-validation/src/lib.rs index 1deda695c968c..3b86d7cfba461 100644 --- a/crates/sui-source-validation/src/lib.rs +++ b/crates/sui-source-validation/src/lib.rs @@ -2,54 +2,26 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::{AggregateError, Error}; -use anyhow::{anyhow, bail, ensure}; -use colored::Colorize; use futures::future; use move_binary_format::CompiledModule; -use move_bytecode_source_map::utils::source_map_from_file; -use move_command_line_common::env::MOVE_HOME; -use move_command_line_common::files::MOVE_COMPILED_EXTENSION; -use move_command_line_common::files::{ - extension_equals, find_filenames, MOVE_EXTENSION, SOURCE_MAP_EXTENSION, -}; use move_compiler::compiled_unit::NamedCompiledModule; -use move_compiler::editions::{Edition, Flavor}; -use move_compiler::shared::NumericalAddress; use move_core_types::account_address::AccountAddress; -use move_package::compilation::compiled_package::{ - CompiledPackage as MoveCompiledPackage, CompiledUnitWithSource, -}; -use move_package::compilation::package_layout::CompiledPackageLayout; -use move_package::lock_file::schema::{Header, ToolchainVersion}; -use move_package::source_package::layout::SourcePackageLayout; -use move_package::source_package::parsed_manifest::{FileName, PackageName}; +use move_package::compilation::compiled_package::CompiledPackage as MoveCompiledPackage; use move_symbol_pool::Symbol; use std::collections::HashMap; -use std::ffi::OsStr; -use std::fs::File; -use std::io::{self, Seek}; -use std::path::{Path, PathBuf}; -use std::process::Command; use sui_move_build::CompiledPackage; use sui_sdk::apis::ReadApi; use sui_sdk::error::Error as SdkError; use sui_sdk::rpc_types::{SuiObjectDataOptions, SuiRawData, SuiRawMovePackage}; use sui_types::base_types::ObjectID; -use tar::Archive; -use tempfile::TempDir; -use tracing::{debug, info}; +use toolchain::units_for_toolchain; pub mod error; +mod toolchain; #[cfg(test)] mod tests; -const CURRENT_COMPILER_VERSION: &str = env!("CARGO_PKG_VERSION"); -const LEGACY_COMPILER_VERSION: &str = CURRENT_COMPILER_VERSION; // TODO: update this when Move 2024 is released -const PRE_TOOLCHAIN_MOVE_LOCK_VERSION: u64 = 0; // Used to detect lockfiles pre-toolchain versioning support -const CANONICAL_UNIX_BINARY_NAME: &str = "sui"; -const CANONICAL_WIN_BINARY_NAME: &str = "sui.exe"; - /// How to handle package source during bytecode verification. #[derive(PartialEq, Eq)] pub enum SourceMode { @@ -388,345 +360,3 @@ fn local_modules( Ok(map) } - -fn current_toolchain() -> ToolchainVersion { - ToolchainVersion { - compiler_version: CURRENT_COMPILER_VERSION.into(), - edition: Edition::LEGACY, /* does not matter, unused for current_toolchain */ - flavor: Flavor::Sui, /* does not matter, unused for current_toolchain */ - } -} - -fn legacy_toolchain() -> ToolchainVersion { - ToolchainVersion { - compiler_version: LEGACY_COMPILER_VERSION.into(), - edition: Edition::LEGACY, - flavor: Flavor::Sui, - } -} - -/// Ensures `compiled_units` are compiled with the right compiler version, based on -/// Move.lock contents. This works by detecting if a compiled unit requires a prior compiler version: -/// - If so, download the compiler, recompile the unit, and return that unit in the result. -/// - If not, simply keep the current compiled unit. -fn units_for_toolchain( - compiled_units: &Vec<(PackageName, CompiledUnitWithSource)>, -) -> anyhow::Result> { - if std::env::var("SUI_RUN_TOOLCHAIN_BUILD").is_err() { - return Ok(compiled_units.clone()); - } - let mut package_version_map: HashMap)> = - HashMap::new(); - // First iterate over packages, mapping the required version for each package in `package_version_map`. - for (package, local_unit) in compiled_units { - if let Some((_, units)) = package_version_map.get_mut(package) { - // We've processed this package's required version. - units.push(local_unit.clone()); - continue; - } - - if sui_types::is_system_package(local_unit.unit.address.into_inner()) { - // System packages are always compiled with the current compiler. - package_version_map.insert(*package, (current_toolchain(), vec![local_unit.clone()])); - continue; - } - - let package_root = SourcePackageLayout::try_find_root(&local_unit.source_path)?; - let lock_file = package_root.join(SourcePackageLayout::Lock.path()); - if !lock_file.exists() { - // No lock file implies current compiler for this package. - package_version_map.insert(*package, (current_toolchain(), vec![local_unit.clone()])); - continue; - } - - let mut lock_file = File::open(lock_file)?; - let lock_version = Header::read(&mut lock_file)?.version; - if lock_version == PRE_TOOLCHAIN_MOVE_LOCK_VERSION { - // No need to attempt reading lock file toolchain - debug!("{package} on legacy compiler",); - package_version_map.insert(*package, (legacy_toolchain(), vec![local_unit.clone()])); - continue; - } - - // Read lock file toolchain info - lock_file.rewind()?; - let toolchain_version = ToolchainVersion::read(&mut lock_file)?; - match toolchain_version { - // No ToolchainVersion and new Move.lock version implies current compiler. - None => { - debug!("{package} on current compiler @ {CURRENT_COMPILER_VERSION}",); - package_version_map - .insert(*package, (current_toolchain(), vec![local_unit.clone()])); - } - // This dependency uses the current compiler. - Some(ToolchainVersion { - compiler_version, .. - }) if compiler_version == CURRENT_COMPILER_VERSION => { - debug!("{package} on current compiler @ {CURRENT_COMPILER_VERSION}",); - package_version_map - .insert(*package, (current_toolchain(), vec![local_unit.clone()])); - } - // This dependency needs a prior compiler. Mark it and compile. - Some(toolchain_version) => { - println!( - "{} {package} compiler @ {}", - "REQUIRE".bold().green(), - toolchain_version.compiler_version.yellow(), - ); - package_version_map.insert(*package, (toolchain_version, vec![local_unit.clone()])); - } - } - } - - let mut units = vec![]; - // Iterate over compiled units, and check if they need to be recompiled and replaced by a prior compiler's output. - for (package, (toolchain_version, local_units)) in package_version_map { - if toolchain_version.compiler_version == CURRENT_COMPILER_VERSION { - let local_units: Vec<_> = local_units.iter().map(|u| (package, u.clone())).collect(); - units.extend(local_units); - continue; - } - - if local_units.is_empty() { - bail!("Expected one or more modules, but none found"); - } - let package_root = SourcePackageLayout::try_find_root(&local_units[0].source_path)?; - let install_dir = tempfile::tempdir()?; // place compiled packages in this temp dir, don't pollute this packages build dir - download_and_compile( - package_root.clone(), - &install_dir, - &toolchain_version, - &package, - )?; - - let compiled_unit_paths = vec![package_root.clone()]; - let compiled_units = find_filenames(&compiled_unit_paths, |path| { - extension_equals(path, MOVE_COMPILED_EXTENSION) - })?; - let build_path = install_dir - .path() - .join(CompiledPackageLayout::path(&CompiledPackageLayout::Root)) - .join(package.as_str()); - debug!("build path is {}", build_path.display()); - - // Add all units compiled with the previous compiler. - for bytecode_path in compiled_units { - info!("bytecode path {bytecode_path}, {package}"); - let local_unit = decode_bytecode_file(build_path.clone(), &package, &bytecode_path)?; - units.push((package, local_unit)) - } - } - Ok(units) -} - -fn download_and_compile( - root: PathBuf, - install_dir: &TempDir, - ToolchainVersion { - compiler_version, - edition, - flavor, - }: &ToolchainVersion, - dep_name: &Symbol, -) -> anyhow::Result<()> { - let dest_dir = PathBuf::from_iter([&*MOVE_HOME, "binaries"]); // E.g., ~/.move/binaries - let dest_version = dest_dir.join(compiler_version); - let mut dest_canonical_path = dest_version.clone(); - dest_canonical_path.extend(["target", "release"]); - let mut dest_canonical_binary = dest_canonical_path.clone(); - - let platform = detect_platform(&root, compiler_version, &dest_canonical_path)?; - if platform == "windows-x86_64" { - dest_canonical_binary.push(CANONICAL_WIN_BINARY_NAME); - } else { - dest_canonical_binary.push(CANONICAL_UNIX_BINARY_NAME); - } - - if !dest_canonical_binary.exists() { - // Check the platform and proceed if we can download a binary. If not, the user should follow error instructions to sideload the binary. - // Download if binary does not exist. - let mainnet_url = format!( - "https://github.com/MystenLabs/sui/releases/download/mainnet-v{compiler_version}/sui-mainnet-v{compiler_version}-{platform}.tgz", - ); - - println!( - "{} mainnet compiler @ {} (this may take a while)", - "DOWNLOADING".bold().green(), - compiler_version.yellow() - ); - - let mut response = match ureq::get(&mainnet_url).call() { - Ok(response) => response, - Err(ureq::Error::Status(404, _)) => { - println!( - "{} sui mainnet compiler {} not available, attempting to download testnet compiler release...", - "WARNING".bold().yellow(), - compiler_version.yellow() - ); - println!( - "{} testnet compiler @ {} (this may take a while)", - "DOWNLOADING".bold().green(), - compiler_version.yellow() - ); - let testnet_url = format!("https://github.com/MystenLabs/sui/releases/download/testnet-v{compiler_version}/sui-testnet-v{compiler_version}-{platform}.tgz"); - ureq::get(&testnet_url).call()? - } - Err(e) => return Err(e.into()), - }.into_reader(); - - let dest_tarball = dest_version.join(format!("{}.tgz", compiler_version)); - debug!("tarball destination: {} ", dest_tarball.display()); - if let Some(parent) = dest_tarball.parent() { - std::fs::create_dir_all(parent) - .map_err(|e| anyhow!("failed to create directory for tarball: {e}"))?; - } - let mut dest_file = File::create(&dest_tarball)?; - io::copy(&mut response, &mut dest_file)?; - - // Extract the tarball using the tar crate - let tar_gz = File::open(&dest_tarball)?; - let tar = flate2::read::GzDecoder::new(tar_gz); - let mut archive = Archive::new(tar); - archive - .unpack(&dest_version) - .map_err(|e| anyhow!("failed to untar compiler binary: {e}"))?; - - let mut dest_binary = dest_version.clone(); - dest_binary.extend(["target", "release"]); - if platform == "windows-x86_64" { - dest_binary.push(&format!("sui-{platform}.exe")); - } else { - dest_binary.push(&format!("sui-{platform}")); - } - let dest_binary_os = OsStr::new(dest_binary.as_path()); - set_executable_permission(dest_binary_os)?; - std::fs::rename(dest_binary_os, dest_canonical_binary.clone())?; - } - - debug!( - "{} move build --default-move-edition {} --default-move-flavor {} -p {} --install-dir {}", - dest_canonical_binary.display(), - edition.to_string().as_str(), - flavor.to_string().as_str(), - root.display(), - install_dir.path().display(), - ); - info!( - "{} {} (compiler @ {})", - "BUILDING".bold().green(), - dep_name.as_str(), - compiler_version.yellow() - ); - Command::new(dest_canonical_binary) - .args([ - OsStr::new("move"), - OsStr::new("build"), - OsStr::new("--default-move-edition"), - OsStr::new(edition.to_string().as_str()), - OsStr::new("--default-move-flavor"), - OsStr::new(flavor.to_string().as_str()), - OsStr::new("-p"), - OsStr::new(root.as_path()), - OsStr::new("--install-dir"), - OsStr::new(install_dir.path()), - ]) - .output() - .map_err(|e| { - anyhow!("failed to build package from compiler binary {compiler_version}: {e}",) - })?; - Ok(()) -} - -fn detect_platform( - package_path: &Path, - compiler_version: &String, - dest_dir: &Path, -) -> anyhow::Result { - let s = match (std::env::consts::OS, std::env::consts::ARCH) { - ("macos", "aarch64") => "macos-arm64", - ("macos", "x86_64") => "macos-x86_64", - ("linux", "x86_64") => "ubuntu-x86_64", - ("windows", "x86_64") => "windows-x86_64", - (os, arch) => { - let mut binary_name = CANONICAL_UNIX_BINARY_NAME; - if os == "windows" { - binary_name = CANONICAL_WIN_BINARY_NAME; - }; - bail!( - "The package {} needs to be built with sui compiler version {compiler_version} but there \ - is no binary release available to download for your platform:\n\ - Operating System: {os}\n\ - Architecture: {arch}\n\ - You can manually put a {binary_name} binary for your platform in {} and rerun your command to continue.", - package_path.display(), - dest_dir.display(), - ) - } - }; - Ok(s.into()) -} - -#[cfg(unix)] -fn set_executable_permission(path: &OsStr) -> anyhow::Result<()> { - use std::fs; - use std::os::unix::prelude::PermissionsExt; - let mut perms = fs::metadata(path)?.permissions(); - perms.set_mode(0o755); - fs::set_permissions(path, perms)?; - Ok(()) -} - -#[cfg(not(unix))] -fn set_executable_permission(path: &OsStr) -> anyhow::Result<()> { - Command::new("icacls") - .args([path, OsStr::new("/grant"), OsStr::new("Everyone:(RX)")]) - .status()?; - Ok(()) -} - -fn decode_bytecode_file( - root_path: PathBuf, - package_name: &Symbol, - bytecode_path_str: &str, -) -> anyhow::Result { - let package_name_opt = Some(*package_name); - let bytecode_path = Path::new(bytecode_path_str); - let path_to_file = CompiledPackageLayout::path_to_file_after_category(bytecode_path); - let bytecode_bytes = std::fs::read(bytecode_path)?; - let source_map = source_map_from_file( - &root_path - .join(CompiledPackageLayout::SourceMaps.path()) - .join(&path_to_file) - .with_extension(SOURCE_MAP_EXTENSION), - )?; - let source_path = &root_path - .join(CompiledPackageLayout::Sources.path()) - .join(path_to_file) - .with_extension(MOVE_EXTENSION); - ensure!( - source_path.is_file(), - "Error decoding package: Unable to find corresponding source file for '{bytecode_path_str}' in package {package_name}" - ); - let module = CompiledModule::deserialize_with_defaults(&bytecode_bytes)?; - let (address_bytes, module_name) = { - let id = module.self_id(); - let parsed_addr = NumericalAddress::new( - id.address().into_bytes(), - move_compiler::shared::NumberFormat::Hex, - ); - let module_name = FileName::from(id.name().as_str()); - (parsed_addr, module_name) - }; - let unit = NamedCompiledModule { - package_name: package_name_opt, - address: address_bytes, - name: module_name, - module, - source_map, - address_name: None, - }; - Ok(CompiledUnitWithSource { - unit, - source_path: source_path.clone(), - }) -} diff --git a/crates/sui-source-validation/src/tests.rs b/crates/sui-source-validation/src/tests.rs index 596771fb78013..abd191b4ce80f 100644 --- a/crates/sui-source-validation/src/tests.rs +++ b/crates/sui-source-validation/src/tests.rs @@ -21,7 +21,8 @@ use sui_types::{ }; use test_cluster::TestClusterBuilder; -use crate::{BytecodeSourceVerifier, SourceMode, CURRENT_COMPILER_VERSION}; +use crate::toolchain::CURRENT_COMPILER_VERSION; +use crate::{BytecodeSourceVerifier, SourceMode}; #[tokio::test] async fn successful_verification() -> anyhow::Result<()> { diff --git a/crates/sui-source-validation/src/toolchain.rs b/crates/sui-source-validation/src/toolchain.rs new file mode 100644 index 0000000000000..54f87f2e1b83a --- /dev/null +++ b/crates/sui-source-validation/src/toolchain.rs @@ -0,0 +1,387 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::{ + collections::HashMap, + ffi::OsStr, + fs::File, + io::{self, Seek}, + path::{Path, PathBuf}, + process::Command, +}; + +use anyhow::{anyhow, bail, ensure}; +use colored::Colorize; +use move_binary_format::CompiledModule; +use move_bytecode_source_map::utils::source_map_from_file; +use move_command_line_common::{ + env::MOVE_HOME, + files::{ + extension_equals, find_filenames, MOVE_COMPILED_EXTENSION, MOVE_EXTENSION, + SOURCE_MAP_EXTENSION, + }, +}; +use move_compiler::{ + compiled_unit::NamedCompiledModule, + editions::{Edition, Flavor}, + shared::{files::FileName, NumericalAddress}, +}; +use move_package::{ + compilation::{ + compiled_package::CompiledUnitWithSource, package_layout::CompiledPackageLayout, + }, + lock_file::schema::{Header, ToolchainVersion}, + source_package::{layout::SourcePackageLayout, parsed_manifest::PackageName}, +}; +use move_symbol_pool::Symbol; +use tar::Archive; +use tempfile::TempDir; +use tracing::{debug, info}; + +pub(crate) const CURRENT_COMPILER_VERSION: &str = env!("CARGO_PKG_VERSION"); +const LEGACY_COMPILER_VERSION: &str = CURRENT_COMPILER_VERSION; // TODO: update this when Move 2024 is released +const PRE_TOOLCHAIN_MOVE_LOCK_VERSION: u64 = 0; // Used to detect lockfiles pre-toolchain versioning support +const CANONICAL_UNIX_BINARY_NAME: &str = "sui"; +const CANONICAL_WIN_BINARY_NAME: &str = "sui.exe"; + +pub(crate) fn current_toolchain() -> ToolchainVersion { + ToolchainVersion { + compiler_version: CURRENT_COMPILER_VERSION.into(), + edition: Edition::LEGACY, /* does not matter, unused for current_toolchain */ + flavor: Flavor::Sui, /* does not matter, unused for current_toolchain */ + } +} + +pub(crate) fn legacy_toolchain() -> ToolchainVersion { + ToolchainVersion { + compiler_version: LEGACY_COMPILER_VERSION.into(), + edition: Edition::LEGACY, + flavor: Flavor::Sui, + } +} + +/// Ensures `compiled_units` are compiled with the right compiler version, based on +/// Move.lock contents. This works by detecting if a compiled unit requires a prior compiler version: +/// - If so, download the compiler, recompile the unit, and return that unit in the result. +/// - If not, simply keep the current compiled unit. +pub(crate) fn units_for_toolchain( + compiled_units: &Vec<(PackageName, CompiledUnitWithSource)>, +) -> anyhow::Result> { + if std::env::var("SUI_RUN_TOOLCHAIN_BUILD").is_err() { + return Ok(compiled_units.clone()); + } + let mut package_version_map: HashMap)> = + HashMap::new(); + // First iterate over packages, mapping the required version for each package in `package_version_map`. + for (package, local_unit) in compiled_units { + if let Some((_, units)) = package_version_map.get_mut(package) { + // We've processed this package's required version. + units.push(local_unit.clone()); + continue; + } + + if sui_types::is_system_package(local_unit.unit.address.into_inner()) { + // System packages are always compiled with the current compiler. + package_version_map.insert(*package, (current_toolchain(), vec![local_unit.clone()])); + continue; + } + + let package_root = SourcePackageLayout::try_find_root(&local_unit.source_path)?; + let lock_file = package_root.join(SourcePackageLayout::Lock.path()); + if !lock_file.exists() { + // No lock file implies current compiler for this package. + package_version_map.insert(*package, (current_toolchain(), vec![local_unit.clone()])); + continue; + } + + let mut lock_file = File::open(lock_file)?; + let lock_version = Header::read(&mut lock_file)?.version; + if lock_version == PRE_TOOLCHAIN_MOVE_LOCK_VERSION { + // No need to attempt reading lock file toolchain + debug!("{package} on legacy compiler",); + package_version_map.insert(*package, (legacy_toolchain(), vec![local_unit.clone()])); + continue; + } + + // Read lock file toolchain info + lock_file.rewind()?; + let toolchain_version = ToolchainVersion::read(&mut lock_file)?; + match toolchain_version { + // No ToolchainVersion and new Move.lock version implies current compiler. + None => { + debug!("{package} on current compiler @ {CURRENT_COMPILER_VERSION}",); + package_version_map + .insert(*package, (current_toolchain(), vec![local_unit.clone()])); + } + // This dependency uses the current compiler. + Some(ToolchainVersion { + compiler_version, .. + }) if compiler_version == CURRENT_COMPILER_VERSION => { + debug!("{package} on current compiler @ {CURRENT_COMPILER_VERSION}",); + package_version_map + .insert(*package, (current_toolchain(), vec![local_unit.clone()])); + } + // This dependency needs a prior compiler. Mark it and compile. + Some(toolchain_version) => { + println!( + "{} {package} compiler @ {}", + "REQUIRE".bold().green(), + toolchain_version.compiler_version.yellow(), + ); + package_version_map.insert(*package, (toolchain_version, vec![local_unit.clone()])); + } + } + } + + let mut units = vec![]; + // Iterate over compiled units, and check if they need to be recompiled and replaced by a prior compiler's output. + for (package, (toolchain_version, local_units)) in package_version_map { + if toolchain_version.compiler_version == CURRENT_COMPILER_VERSION { + let local_units: Vec<_> = local_units.iter().map(|u| (package, u.clone())).collect(); + units.extend(local_units); + continue; + } + + if local_units.is_empty() { + bail!("Expected one or more modules, but none found"); + } + let package_root = SourcePackageLayout::try_find_root(&local_units[0].source_path)?; + let install_dir = tempfile::tempdir()?; // place compiled packages in this temp dir, don't pollute this packages build dir + download_and_compile( + package_root.clone(), + &install_dir, + &toolchain_version, + &package, + )?; + + let compiled_unit_paths = vec![package_root.clone()]; + let compiled_units = find_filenames(&compiled_unit_paths, |path| { + extension_equals(path, MOVE_COMPILED_EXTENSION) + })?; + let build_path = install_dir + .path() + .join(CompiledPackageLayout::path(&CompiledPackageLayout::Root)) + .join(package.as_str()); + debug!("build path is {}", build_path.display()); + + // Add all units compiled with the previous compiler. + for bytecode_path in compiled_units { + info!("bytecode path {bytecode_path}, {package}"); + let local_unit = decode_bytecode_file(build_path.clone(), &package, &bytecode_path)?; + units.push((package, local_unit)) + } + } + Ok(units) +} + +fn download_and_compile( + root: PathBuf, + install_dir: &TempDir, + ToolchainVersion { + compiler_version, + edition, + flavor, + }: &ToolchainVersion, + dep_name: &Symbol, +) -> anyhow::Result<()> { + let dest_dir = PathBuf::from_iter([&*MOVE_HOME, "binaries"]); // E.g., ~/.move/binaries + let dest_version = dest_dir.join(compiler_version); + let mut dest_canonical_path = dest_version.clone(); + dest_canonical_path.extend(["target", "release"]); + let mut dest_canonical_binary = dest_canonical_path.clone(); + + let platform = detect_platform(&root, compiler_version, &dest_canonical_path)?; + if platform == "windows-x86_64" { + dest_canonical_binary.push(CANONICAL_WIN_BINARY_NAME); + } else { + dest_canonical_binary.push(CANONICAL_UNIX_BINARY_NAME); + } + + if !dest_canonical_binary.exists() { + // Check the platform and proceed if we can download a binary. If not, the user should follow error instructions to sideload the binary. + // Download if binary does not exist. + let mainnet_url = format!( + "https://github.com/MystenLabs/sui/releases/download/mainnet-v{compiler_version}/sui-mainnet-v{compiler_version}-{platform}.tgz", + ); + + println!( + "{} mainnet compiler @ {} (this may take a while)", + "DOWNLOADING".bold().green(), + compiler_version.yellow() + ); + + let mut response = match ureq::get(&mainnet_url).call() { + Ok(response) => response, + Err(ureq::Error::Status(404, _)) => { + println!( + "{} sui mainnet compiler {} not available, attempting to download testnet compiler release...", + "WARNING".bold().yellow(), + compiler_version.yellow() + ); + println!( + "{} testnet compiler @ {} (this may take a while)", + "DOWNLOADING".bold().green(), + compiler_version.yellow() + ); + let testnet_url = format!("https://github.com/MystenLabs/sui/releases/download/testnet-v{compiler_version}/sui-testnet-v{compiler_version}-{platform}.tgz"); + ureq::get(&testnet_url).call()? + } + Err(e) => return Err(e.into()), + }.into_reader(); + + let dest_tarball = dest_version.join(format!("{}.tgz", compiler_version)); + debug!("tarball destination: {} ", dest_tarball.display()); + if let Some(parent) = dest_tarball.parent() { + std::fs::create_dir_all(parent) + .map_err(|e| anyhow!("failed to create directory for tarball: {e}"))?; + } + let mut dest_file = File::create(&dest_tarball)?; + io::copy(&mut response, &mut dest_file)?; + + // Extract the tarball using the tar crate + let tar_gz = File::open(&dest_tarball)?; + let tar = flate2::read::GzDecoder::new(tar_gz); + let mut archive = Archive::new(tar); + archive + .unpack(&dest_version) + .map_err(|e| anyhow!("failed to untar compiler binary: {e}"))?; + + let mut dest_binary = dest_version.clone(); + dest_binary.extend(["target", "release"]); + if platform == "windows-x86_64" { + dest_binary.push(&format!("sui-{platform}.exe")); + } else { + dest_binary.push(&format!("sui-{platform}")); + } + let dest_binary_os = OsStr::new(dest_binary.as_path()); + set_executable_permission(dest_binary_os)?; + std::fs::rename(dest_binary_os, dest_canonical_binary.clone())?; + } + + debug!( + "{} move build --default-move-edition {} --default-move-flavor {} -p {} --install-dir {}", + dest_canonical_binary.display(), + edition.to_string().as_str(), + flavor.to_string().as_str(), + root.display(), + install_dir.path().display(), + ); + info!( + "{} {} (compiler @ {})", + "BUILDING".bold().green(), + dep_name.as_str(), + compiler_version.yellow() + ); + Command::new(dest_canonical_binary) + .args([ + OsStr::new("move"), + OsStr::new("build"), + OsStr::new("--default-move-edition"), + OsStr::new(edition.to_string().as_str()), + OsStr::new("--default-move-flavor"), + OsStr::new(flavor.to_string().as_str()), + OsStr::new("-p"), + OsStr::new(root.as_path()), + OsStr::new("--install-dir"), + OsStr::new(install_dir.path()), + ]) + .output() + .map_err(|e| { + anyhow!("failed to build package from compiler binary {compiler_version}: {e}",) + })?; + Ok(()) +} + +fn detect_platform( + package_path: &Path, + compiler_version: &String, + dest_dir: &Path, +) -> anyhow::Result { + let s = match (std::env::consts::OS, std::env::consts::ARCH) { + ("macos", "aarch64") => "macos-arm64", + ("macos", "x86_64") => "macos-x86_64", + ("linux", "x86_64") => "ubuntu-x86_64", + ("windows", "x86_64") => "windows-x86_64", + (os, arch) => { + let mut binary_name = CANONICAL_UNIX_BINARY_NAME; + if os == "windows" { + binary_name = CANONICAL_WIN_BINARY_NAME; + }; + bail!( + "The package {} needs to be built with sui compiler version {compiler_version} but there \ + is no binary release available to download for your platform:\n\ + Operating System: {os}\n\ + Architecture: {arch}\n\ + You can manually put a {binary_name} binary for your platform in {} and rerun your command to continue.", + package_path.display(), + dest_dir.display(), + ) + } + }; + Ok(s.into()) +} + +#[cfg(unix)] +fn set_executable_permission(path: &OsStr) -> anyhow::Result<()> { + use std::fs; + use std::os::unix::prelude::PermissionsExt; + let mut perms = fs::metadata(path)?.permissions(); + perms.set_mode(0o755); + fs::set_permissions(path, perms)?; + Ok(()) +} + +#[cfg(not(unix))] +fn set_executable_permission(path: &OsStr) -> anyhow::Result<()> { + Command::new("icacls") + .args([path, OsStr::new("/grant"), OsStr::new("Everyone:(RX)")]) + .status()?; + Ok(()) +} + +fn decode_bytecode_file( + root_path: PathBuf, + package_name: &Symbol, + bytecode_path_str: &str, +) -> anyhow::Result { + let package_name_opt = Some(*package_name); + let bytecode_path = Path::new(bytecode_path_str); + let path_to_file = CompiledPackageLayout::path_to_file_after_category(bytecode_path); + let bytecode_bytes = std::fs::read(bytecode_path)?; + let source_map = source_map_from_file( + &root_path + .join(CompiledPackageLayout::SourceMaps.path()) + .join(&path_to_file) + .with_extension(SOURCE_MAP_EXTENSION), + )?; + let source_path = &root_path + .join(CompiledPackageLayout::Sources.path()) + .join(path_to_file) + .with_extension(MOVE_EXTENSION); + ensure!( + source_path.is_file(), + "Error decoding package: Unable to find corresponding source file for '{bytecode_path_str}' in package {package_name}" + ); + let module = CompiledModule::deserialize_with_defaults(&bytecode_bytes)?; + let (address_bytes, module_name) = { + let id = module.self_id(); + let parsed_addr = NumericalAddress::new( + id.address().into_bytes(), + move_compiler::shared::NumberFormat::Hex, + ); + let module_name = FileName::from(id.name().as_str()); + (parsed_addr, module_name) + }; + let unit = NamedCompiledModule { + package_name: package_name_opt, + address: address_bytes, + name: module_name, + module, + source_map, + address_name: None, + }; + Ok(CompiledUnitWithSource { + unit, + source_path: source_path.clone(), + }) +} From b4459e73dc4c1c23c8fe980d14bb68c697d6e81b Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Wed, 14 Aug 2024 18:38:47 +0100 Subject: [PATCH 125/232] fix(verify-source): Detect published-at = 0x0 (#18978) ## Description Detect when the `published-at` field in `Move.toml` or `Move.lock` has been explicitly set to `0x0` and treat that as if it was not set at all. This is not commonly done by people, but it happens in our test set-up. This also required converting the field into an `ObjectID` earlier in the pipeline, which introduced some further changes in the codebase. ## Test plan ``` sui-source-validation$ cargo nextest run ``` ## Stack - #18956 - #18959 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [x] CLI: Explicitly setting `published-at = "0x0"` is treated as if the `published-at` field was omitted. - [ ] Rust SDK: - [ ] REST API: --- crates/sui-move-build/src/lib.rs | 2 +- crates/sui-package-management/src/lib.rs | 91 ++++++++++++++++-------- crates/sui/tests/cli_tests.rs | 2 +- 3 files changed, 62 insertions(+), 33 deletions(-) diff --git a/crates/sui-move-build/src/lib.rs b/crates/sui-move-build/src/lib.rs index 9584bcd4998fb..517808b39f104 100644 --- a/crates/sui-move-build/src/lib.rs +++ b/crates/sui-move-build/src/lib.rs @@ -645,7 +645,7 @@ pub struct PackageDependencies { pub invalid: BTreeMap, /// Set of dependencies that have conflicting `published-at` addresses. The key refers to /// the package, and the tuple refers to the address in the (Move.lock, Move.toml) respectively. - pub conflicting: BTreeMap, + pub conflicting: BTreeMap, } /// Partition packages in `resolution_graph` into one of four groups: diff --git a/crates/sui-package-management/src/lib.rs b/crates/sui-package-management/src/lib.rs index e472578c7746a..3cd7ddd025d47 100644 --- a/crates/sui-package-management/src/lib.rs +++ b/crates/sui-package-management/src/lib.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use anyhow::{bail, Context}; +use std::collections::HashMap; use std::fs::File; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -29,8 +30,8 @@ pub enum PublishedAtError { Invalid(String), NotPresent, Conflict { - id_lock: String, - id_manifest: String, + id_lock: ObjectID, + id_manifest: ObjectID, }, } @@ -140,45 +141,42 @@ pub fn resolve_published_id( ) -> Result { // Look up a valid `published-at` in the `Move.toml` first, which we'll // return if the Move.lock does not manage addresses. - let published_id_in_manifest = match published_at_property(package) { - Ok(v) => Some(v), - Err(PublishedAtError::NotPresent) => None, - Err(e) => return Err(e), // An existing but invalid `published-at` in `Move.toml` should fail early. - }; + let published_id_in_manifest = manifest_published_at(package); + + match published_id_in_manifest { + Ok(_) | Err(PublishedAtError::NotPresent) => { /* nop */ } + Err(e) => { + return Err(e); + } + } let lock = package.package_path.join(SourcePackageLayout::Lock.path()); let Ok(mut lock_file) = File::open(lock.clone()) else { - return match published_id_in_manifest { - Some(v) => { - ObjectID::from_str(v.as_str()).map_err(|_| PublishedAtError::Invalid(v.to_owned())) - } - None => Err(PublishedAtError::NotPresent), - }; + return published_id_in_manifest; }; - let managed_packages = ManagedPackage::read(&mut lock_file).ok(); + // Find the environment and ManagedPackage data for this chain_id. - let id_in_lock_for_chain_id = managed_packages.and_then(|m| { - let chain_id = chain_id.as_ref()?; - m.into_iter() - .find_map(|(_, v)| (v.chain_id == *chain_id).then_some(v.latest_published_id)) - }); - - let package_id = match (id_in_lock_for_chain_id, published_id_in_manifest) { - (Some(id_lock), Some(id_manifest)) if id_lock != id_manifest => { - return Err(PublishedAtError::Conflict { + let id_in_lock_for_chain_id = + lock_published_at(ManagedPackage::read(&mut lock_file).ok(), chain_id.as_ref()); + + match (id_in_lock_for_chain_id, published_id_in_manifest) { + (Ok(id_lock), Ok(id_manifest)) if id_lock != id_manifest => { + Err(PublishedAtError::Conflict { id_lock, id_manifest, }) } - (Some(id_lock), _) => id_lock, - (None, Some(id_manifest)) => id_manifest, /* No info in Move.lock: Fall back to manifest */ - _ => return Err(PublishedAtError::NotPresent), /* Neither in Move.toml nor Move.lock */ - }; - ObjectID::from_str(package_id.as_str()) - .map_err(|_| PublishedAtError::Invalid(package_id.to_owned())) + + (Ok(id), _) | (_, Ok(id)) => Ok(id), + + // We return early (above) if we failed to read the ID from the manifest for some reason + // other than it not being present, so at this point, we can defer to whatever error came + // from the lock file (Ok case is handled above). + (from_lock, Err(_)) => from_lock, + } } -fn published_at_property(package: &Package) -> Result { +fn manifest_published_at(package: &Package) -> Result { let Some(value) = package .source_package .package @@ -187,5 +185,36 @@ fn published_at_property(package: &Package) -> Result else { return Err(PublishedAtError::NotPresent); }; - Ok(value.to_string()) + + let id = + ObjectID::from_str(value.as_str()).map_err(|_| PublishedAtError::Invalid(value.clone()))?; + + if id == ObjectID::ZERO { + Err(PublishedAtError::NotPresent) + } else { + Ok(id) + } +} + +fn lock_published_at( + lock: Option>, + chain_id: Option<&String>, +) -> Result { + let (Some(lock), Some(chain_id)) = (lock, chain_id) else { + return Err(PublishedAtError::NotPresent); + }; + + let managed_package = lock + .into_values() + .find(|v| v.chain_id == *chain_id) + .ok_or(PublishedAtError::NotPresent)?; + + let id = ObjectID::from_str(managed_package.latest_published_id.as_str()) + .map_err(|_| PublishedAtError::Invalid(managed_package.latest_published_id.clone()))?; + + if id == ObjectID::ZERO { + Err(PublishedAtError::NotPresent) + } else { + Ok(id) + } } diff --git a/crates/sui/tests/cli_tests.rs b/crates/sui/tests/cli_tests.rs index cc0f05e41e3af..00c2f5034172e 100644 --- a/crates/sui/tests/cli_tests.rs +++ b/crates/sui/tests/cli_tests.rs @@ -2080,7 +2080,7 @@ async fn test_package_management_on_upgrade_command_conflict() -> Result<(), any let err_string = err_string.replace(&package.object_id().to_string(), ""); let expect = expect![[r#" - Conflicting published package address: `Move.toml` contains published-at address 0xbad but `Move.lock` file contains published-at address . You may want to: + Conflicting published package address: `Move.toml` contains published-at address 0x0000000000000000000000000000000000000000000000000000000000000bad but `Move.lock` file contains published-at address . You may want to: - delete the published-at address in the `Move.toml` if the `Move.lock` address is correct; OR - update the `Move.lock` address using the `sui manage-package` command to be the same as the `Move.toml`; OR From 968247ad80a9cbfeec2c5746dd4b10cf76879312 Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Wed, 14 Aug 2024 18:39:15 +0100 Subject: [PATCH 126/232] fix(TransactionBuilder): Publish use storage IDs (#18960) ## Description Fix a bug where publish test transactions always refer to their dependencies at their original IDs. This breaks publishing test packages that depend on upgraded packages. ## Test plan CI -- existing tests using TestTransactionBuilder should still pass. ## Stack - #18956 - #18959 - #18978 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- Cargo.lock | 1 + crates/sui-move-build/src/lib.rs | 6 +++++ crates/sui-single-node-benchmark/Cargo.toml | 1 + .../package_publish_tx_generator.rs | 25 +++++++++++++------ .../sui-test-transaction-builder/src/lib.rs | 4 +-- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 00acf752be673..6cb466474d4bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14406,6 +14406,7 @@ dependencies = [ "move-bytecode-utils", "move-core-types", "move-package", + "move-symbol-pool", "once_cell", "prometheus", "serde", diff --git a/crates/sui-move-build/src/lib.rs b/crates/sui-move-build/src/lib.rs index 517808b39f104..ca071609b4ee6 100644 --- a/crates/sui-move-build/src/lib.rs +++ b/crates/sui-move-build/src/lib.rs @@ -346,6 +346,12 @@ impl CompiledPackage { } } + /// Return the set of Object IDs corresponding to this package's transitive dependencies' + /// storage package IDs (where to load those packages on-chain). + pub fn get_dependency_storage_package_ids(&self) -> Vec { + self.dependency_ids.published.values().cloned().collect() + } + /// Return the set of Object IDs corresponding to this package's transitive dependencies' /// original package IDs. pub fn get_dependency_original_package_ids(&self) -> Vec { diff --git a/crates/sui-single-node-benchmark/Cargo.toml b/crates/sui-single-node-benchmark/Cargo.toml index 8cad1f304ca72..33cdf4cc24138 100644 --- a/crates/sui-single-node-benchmark/Cargo.toml +++ b/crates/sui-single-node-benchmark/Cargo.toml @@ -10,6 +10,7 @@ move-binary-format.workspace = true move-bytecode-utils.workspace = true move-core-types.workspace = true move-package.workspace = true +move-symbol-pool.workspace = true sui-config.workspace = true sui-core = { workspace = true, features = ["test-utils"] } sui-move-build.workspace = true diff --git a/crates/sui-single-node-benchmark/src/tx_generator/package_publish_tx_generator.rs b/crates/sui-single-node-benchmark/src/tx_generator/package_publish_tx_generator.rs index 6912994e75093..fcf15e3a71a52 100644 --- a/crates/sui-single-node-benchmark/src/tx_generator/package_publish_tx_generator.rs +++ b/crates/sui-single-node-benchmark/src/tx_generator/package_publish_tx_generator.rs @@ -5,8 +5,9 @@ use crate::benchmark_context::BenchmarkContext; use crate::mock_account::Account; use crate::tx_generator::TxGenerator; use move_package::source_package::manifest_parser::parse_move_manifest_from_file; +use move_symbol_pool::Symbol; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use std::collections::BTreeMap; use std::fs; use std::path::PathBuf; use sui_move_build::{BuildConfig, CompiledPackage}; @@ -27,13 +28,14 @@ impl PackagePublishTxGenerator { dependencies, root_package, } = manifest; - let mut dep_map = HashMap::new(); + let mut dep_map = BTreeMap::new(); for dependency in dependencies { let Package { name, path, is_source_code, } = dependency; + info!("Publishing dependent package {}", name); let target_path = dir.join(&path); let module_bytes = if is_source_code { @@ -68,23 +70,32 @@ impl PackagePublishTxGenerator { .await .0; info!("Published dependent package {}", package_id); - dep_map.insert(name, package_id); + dep_map.insert(Symbol::from(name), package_id); } + let Package { name, path, is_source_code, } = root_package; + info!("Compiling root package {}", name); assert!( is_source_code, "Only support building root package from source code" ); + let target_path = dir.join(path); - dep_map.insert(name, ObjectID::ZERO); - let compiled_package = BuildConfig::new_for_testing_replace_addresses(dep_map) - .build(&target_path) - .unwrap(); + let published_deps = dep_map.clone(); + + dep_map.insert(Symbol::from(name), ObjectID::ZERO); + let mut compiled_package = BuildConfig::new_for_testing_replace_addresses( + dep_map.into_iter().map(|(k, v)| (k.to_string(), v)), + ) + .build(&target_path) + .unwrap(); + + compiled_package.dependency_ids.published = published_deps; Self { compiled_package } } } diff --git a/crates/sui-test-transaction-builder/src/lib.rs b/crates/sui-test-transaction-builder/src/lib.rs index 6b79fdef33b4f..b0d91e127b5ed 100644 --- a/crates/sui-test-transaction-builder/src/lib.rs +++ b/crates/sui-test-transaction-builder/src/lib.rs @@ -331,13 +331,13 @@ impl TestTransactionBuilder { let compiled_package = BuildConfig::new_for_testing().build(&path).unwrap(); let all_module_bytes = compiled_package.get_package_bytes(with_unpublished_deps); - let dependencies = compiled_package.get_dependency_original_package_ids(); + let dependencies = compiled_package.get_dependency_storage_package_ids(); (all_module_bytes, dependencies) } PublishData::ModuleBytes(bytecode) => (bytecode, vec![]), PublishData::CompiledPackage(compiled_package) => { let all_module_bytes = compiled_package.get_package_bytes(false); - let dependencies = compiled_package.get_dependency_original_package_ids(); + let dependencies = compiled_package.get_dependency_storage_package_ids(); (all_module_bytes, dependencies) } }; From 3160fe15cc21b6dda641f1152a10039ac9e66bdf Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Wed, 14 Aug 2024 18:55:08 +0100 Subject: [PATCH 127/232] fix(test): publish depending on upgrade (#18962) ## Description Fix a bug in tests that publish packages where the publish transaction was always constructed referring to dependencies at their original IDs, and not their storage IDs. This has not caused a problem to date, meaning these tests may not publish a package that depends on an upgrade package, but to avoid confusion `get_dependency_original_package_ids` has been replaced with `get_dependency_storage_package_ids`. Similarly, `get_package_dependencies_hex` has been replaced with an invocation of `get_dependency_storage_package_ids`. ## Test plan CI +: ``` sui$ cargo build --bin sui sui$ cd tmp sui$ ~/sui/target/debug/sui move new test sui$ # make it possible to compile sui$ ~/sui/target/debug/ui move build --dump-bytecode-as-base64 ``` ## Stack - #18956 - #18959 - #18978 - #18960 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .../src/test_case/coin_index_test.rs | 2 +- ...fullnode_build_publish_transaction_test.rs | 2 +- .../src/unit_tests/move_integration_tests.rs | 4 ++-- .../tests/balance_changes_tests.rs | 2 +- .../tests/rpc_server_tests.rs | 6 ++--- crates/sui-move-build/src/lib.rs | 24 ------------------- crates/sui-move/src/build.rs | 3 +-- crates/sui-oracle/tests/integration_tests.rs | 2 +- .../unit_tests/balance_changing_tx_tests.rs | 2 +- 9 files changed, 11 insertions(+), 36 deletions(-) diff --git a/crates/sui-cluster-test/src/test_case/coin_index_test.rs b/crates/sui-cluster-test/src/test_case/coin_index_test.rs index e3977af2602dc..460180d2b8b22 100644 --- a/crates/sui-cluster-test/src/test_case/coin_index_test.rs +++ b/crates/sui-cluster-test/src/test_case/coin_index_test.rs @@ -631,7 +631,7 @@ async fn publish_managed_coin_package( let compiled_package = compile_managed_coin_package(); let all_module_bytes = compiled_package.get_package_base64(/* with_unpublished_deps */ false); - let dependencies = compiled_package.get_dependency_original_package_ids(); + let dependencies = compiled_package.get_dependency_storage_package_ids(); let params = rpc_params![ ctx.get_wallet_address(), diff --git a/crates/sui-cluster-test/src/test_case/fullnode_build_publish_transaction_test.rs b/crates/sui-cluster-test/src/test_case/fullnode_build_publish_transaction_test.rs index a53c685c46c9b..eef1107892b0f 100644 --- a/crates/sui-cluster-test/src/test_case/fullnode_build_publish_transaction_test.rs +++ b/crates/sui-cluster-test/src/test_case/fullnode_build_publish_transaction_test.rs @@ -24,7 +24,7 @@ impl TestCaseImpl for FullNodeBuildPublishTransactionTest { let compiled_package = compile_basics_package(); let all_module_bytes = compiled_package.get_package_base64(/* with_unpublished_deps */ false); - let dependencies = compiled_package.get_dependency_original_package_ids(); + let dependencies = compiled_package.get_dependency_storage_package_ids(); let params = rpc_params![ ctx.get_wallet_address(), diff --git a/crates/sui-core/src/unit_tests/move_integration_tests.rs b/crates/sui-core/src/unit_tests/move_integration_tests.rs index 1df56180e648a..7e43c4f7ac14b 100644 --- a/crates/sui-core/src/unit_tests/move_integration_tests.rs +++ b/crates/sui-core/src/unit_tests/move_integration_tests.rs @@ -2806,7 +2806,7 @@ pub fn build_package( let compiled_package = BuildConfig::new_for_testing().build(&path).unwrap(); let digest = compiled_package.get_package_digest(with_unpublished_deps); let modules = compiled_package.get_package_bytes(with_unpublished_deps); - let dependencies = compiled_package.get_dependency_original_package_ids(); + let dependencies = compiled_package.get_dependency_storage_package_ids(); (digest.to_vec(), modules, dependencies) } @@ -2826,7 +2826,7 @@ pub async fn build_and_try_publish_test_package( let compiled_package = BuildConfig::new_for_testing().build(&path).unwrap(); let all_module_bytes = compiled_package.get_package_bytes(with_unpublished_deps); - let dependencies = compiled_package.get_dependency_original_package_ids(); + let dependencies = compiled_package.get_dependency_storage_package_ids(); let gas_object = authority.get_object(gas_object_id).await.unwrap(); let gas_object_ref = gas_object.unwrap().compute_object_reference(); diff --git a/crates/sui-json-rpc-tests/tests/balance_changes_tests.rs b/crates/sui-json-rpc-tests/tests/balance_changes_tests.rs index bb1dcaa8be1e7..433c8fcddd72d 100644 --- a/crates/sui-json-rpc-tests/tests/balance_changes_tests.rs +++ b/crates/sui-json-rpc-tests/tests/balance_changes_tests.rs @@ -26,7 +26,7 @@ async fn test_dry_run_publish_with_mocked_coin() -> Result<(), anyhow::Error> { .into_iter() .map(|b| b.to_vec().unwrap()) .collect::>(); - let dependencies = compiled_package.get_dependency_original_package_ids(); + let dependencies = compiled_package.get_dependency_storage_package_ids(); let mut builder = ProgrammableTransactionBuilder::new(); builder.publish_immutable(compiled_modules_bytes, dependencies); diff --git a/crates/sui-json-rpc-tests/tests/rpc_server_tests.rs b/crates/sui-json-rpc-tests/tests/rpc_server_tests.rs index e566183954a94..3d648666047e1 100644 --- a/crates/sui-json-rpc-tests/tests/rpc_server_tests.rs +++ b/crates/sui-json-rpc-tests/tests/rpc_server_tests.rs @@ -192,7 +192,7 @@ async fn test_publish() -> Result<(), anyhow::Error> { BuildConfig::new_for_testing().build(Path::new("../../examples/move/basics"))?; let compiled_modules_bytes = compiled_package.get_package_base64(/* with_unpublished_deps */ false); - let dependencies = compiled_package.get_dependency_original_package_ids(); + let dependencies = compiled_package.get_dependency_storage_package_ids(); let transaction_bytes: TransactionBlockBytes = http_client .publish( @@ -453,7 +453,7 @@ async fn test_get_metadata() -> Result<(), anyhow::Error> { let compiled_package = BuildConfig::new_for_testing().build(&path)?; let compiled_modules_bytes = compiled_package.get_package_base64(/* with_unpublished_deps */ false); - let dependencies = compiled_package.get_dependency_original_package_ids(); + let dependencies = compiled_package.get_dependency_storage_package_ids(); let transaction_bytes: TransactionBlockBytes = http_client .publish( @@ -537,7 +537,7 @@ async fn test_get_total_supply() -> Result<(), anyhow::Error> { let compiled_package = BuildConfig::default().build(&path)?; let compiled_modules_bytes = compiled_package.get_package_base64(/* with_unpublished_deps */ false); - let dependencies = compiled_package.get_dependency_original_package_ids(); + let dependencies = compiled_package.get_dependency_storage_package_ids(); let transaction_bytes: TransactionBlockBytes = http_client .publish( diff --git a/crates/sui-move-build/src/lib.rs b/crates/sui-move-build/src/lib.rs index ca071609b4ee6..26ba9a08eb734 100644 --- a/crates/sui-move-build/src/lib.rs +++ b/crates/sui-move-build/src/lib.rs @@ -352,22 +352,6 @@ impl CompiledPackage { self.dependency_ids.published.values().cloned().collect() } - /// Return the set of Object IDs corresponding to this package's transitive dependencies' - /// original package IDs. - pub fn get_dependency_original_package_ids(&self) -> Vec { - let mut ids: BTreeSet<_> = self - .package - .deps_compiled_units - .iter() - .map(|(_, m)| ObjectID::from(*m.unit.module.address())) - .collect(); - - // `0x0` is not a real dependency ID -- it means that the package has unpublished - // dependencies. - ids.remove(&ObjectID::ZERO); - ids.into_iter().collect() - } - pub fn get_package_digest(&self, with_unpublished_deps: bool) -> [u8; 32] { let hash_modules = true; MovePackage::compute_digest_for_modules_and_deps( @@ -397,14 +381,6 @@ impl CompiledPackage { .collect() } - pub fn get_package_dependencies_hex(&self) -> Vec { - self.dependency_ids - .published - .values() - .map(|object_id| object_id.to_hex_uncompressed()) - .collect() - } - /// Get bytecode modules from DeepBook that are used by this package pub fn get_deepbook_modules(&self) -> impl Iterator { self.get_modules_and_deps() diff --git a/crates/sui-move/src/build.rs b/crates/sui-move/src/build.rs index 5baa78625b08b..1d765e49e8120 100644 --- a/crates/sui-move/src/build.rs +++ b/crates/sui-move/src/build.rs @@ -75,12 +75,11 @@ impl Build { check_unpublished_dependencies(&pkg.dependency_ids.unpublished)?; } - let package_dependencies = pkg.get_package_dependencies_hex(); println!( "{}", json!({ "modules": pkg.get_package_base64(with_unpublished_deps), - "dependencies": json!(package_dependencies), + "dependencies": pkg.get_dependency_storage_package_ids(), "digest": pkg.get_package_digest(with_unpublished_deps), }) ) diff --git a/crates/sui-oracle/tests/integration_tests.rs b/crates/sui-oracle/tests/integration_tests.rs index 6800a53461740..23232b87a3591 100644 --- a/crates/sui-oracle/tests/integration_tests.rs +++ b/crates/sui-oracle/tests/integration_tests.rs @@ -466,7 +466,7 @@ async fn publish_package( ) -> ObjectID { let compiled_package = BuildConfig::new_for_testing().build(path).unwrap(); let all_module_bytes = compiled_package.get_package_bytes(false); - let dependencies = compiled_package.get_dependency_original_package_ids(); + let dependencies = compiled_package.get_dependency_storage_package_ids(); let gas = client .coin_read_api() .get_coins(sender, None, None, Some(1)) diff --git a/crates/sui-rosetta/src/unit_tests/balance_changing_tx_tests.rs b/crates/sui-rosetta/src/unit_tests/balance_changing_tx_tests.rs index b0dba64531281..3fcf3044a3ee7 100644 --- a/crates/sui-rosetta/src/unit_tests/balance_changing_tx_tests.rs +++ b/crates/sui-rosetta/src/unit_tests/balance_changing_tx_tests.rs @@ -144,7 +144,7 @@ async fn test_publish_and_move_call() { let compiled_package = BuildConfig::new_for_testing().build(&path).unwrap(); let compiled_modules_bytes = compiled_package.get_package_bytes(/* with_unpublished_deps */ false); - let dependencies = compiled_package.get_dependency_original_package_ids(); + let dependencies = compiled_package.get_dependency_storage_package_ids(); let pt = { let mut builder = ProgrammableTransactionBuilder::new(); From 588ea8f803c461e1d9c55db1a3b925f16b367d8d Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Wed, 14 Aug 2024 18:57:17 +0100 Subject: [PATCH 128/232] feat: [Source Validation] Check linkage (#18964) ## Description Source validation additionally checks that dependencies in the linkage table of the on-chain package match the published dependencies from the source package. Previously it was possible to construct a scenario where source verification would succeed even though a package's representation on-chain did not exactly match, because one of the dependencies mismatched on version but between two versions that were themselves identical. ## Test plan New tests exercising the scenario mentioned above: ``` sui-source-validation$ cargo nextest run -- linkage_differs ``` ## Stack - #18956 - #18959 - #18978 - #18960 - #18962 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [x] CLI: `sui client verify-source` now also confirms a package's linkage table matches its source dependencies. - [ ] Rust SDK: - [ ] REST API: --- Cargo.lock | 2 + crates/sui-package-management/Cargo.toml | 1 + crates/sui-package-management/src/lib.rs | 10 +- .../sui-source-validation-service/src/lib.rs | 8 +- crates/sui-source-validation/Cargo.toml | 3 +- crates/sui-source-validation/src/error.rs | 13 + crates/sui-source-validation/src/lib.rs | 553 ++++++++++-------- crates/sui-source-validation/src/tests.rs | 166 ++++-- crates/sui/src/client_commands.rs | 34 +- 9 files changed, 481 insertions(+), 309 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6cb466474d4bc..4e4adc26ccd99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14037,6 +14037,7 @@ dependencies = [ "sui-json-rpc-types", "sui-sdk 1.32.0", "sui-types", + "thiserror", "tracing", ] @@ -14475,6 +14476,7 @@ dependencies = [ "rand 0.8.5", "sui-json-rpc-types", "sui-move-build", + "sui-package-management", "sui-sdk 1.32.0", "sui-test-transaction-builder", "sui-types", diff --git a/crates/sui-package-management/Cargo.toml b/crates/sui-package-management/Cargo.toml index 9e2149fb36d7e..a4b775c1209f5 100644 --- a/crates/sui-package-management/Cargo.toml +++ b/crates/sui-package-management/Cargo.toml @@ -11,6 +11,7 @@ path = "src/lib.rs" [dependencies] anyhow.workspace = true +thiserror.workspace = true tracing.workspace = true sui-json-rpc-types.workspace = true diff --git a/crates/sui-package-management/src/lib.rs b/crates/sui-package-management/src/lib.rs index 3cd7ddd025d47..43aa2f914711c 100644 --- a/crates/sui-package-management/src/lib.rs +++ b/crates/sui-package-management/src/lib.rs @@ -25,10 +25,18 @@ pub enum LockCommand { Upgrade, } -#[derive(Debug, Clone)] +#[derive(thiserror::Error, Debug, Clone)] pub enum PublishedAtError { + #[error("The 'published-at' field in Move.toml or Move.lock is invalid: {0:?}")] Invalid(String), + + #[error("The 'published-at' field is not present in Move.toml or Move.lock")] NotPresent, + + #[error( + "Conflicting 'published-at' addresses between Move.toml -- {id_manifest} -- and \ + Move.lock -- {id_lock}" + )] Conflict { id_lock: ObjectID, id_manifest: ObjectID, diff --git a/crates/sui-source-validation-service/src/lib.rs b/crates/sui-source-validation-service/src/lib.rs index 2654a92f72373..ff95996921ea5 100644 --- a/crates/sui-source-validation-service/src/lib.rs +++ b/crates/sui-source-validation-service/src/lib.rs @@ -34,7 +34,7 @@ use sui_move_build::{BuildConfig, SuiPackageHooks}; use sui_sdk::rpc_types::SuiTransactionBlockEffects; use sui_sdk::types::base_types::ObjectID; use sui_sdk::SuiClientBuilder; -use sui_source_validation::{BytecodeSourceVerifier, SourceMode}; +use sui_source_validation::{BytecodeSourceVerifier, ValidationMode}; pub const HOST_PORT_ENV: &str = "HOST_PORT"; pub const SUI_SOURCE_VALIDATION_VERSION_HEADER: &str = "x-sui-source-validation-version"; @@ -172,11 +172,7 @@ pub async fn verify_package( let compiled_package = build_config.build(package_path.as_ref())?; BytecodeSourceVerifier::new(client.read_api()) - .verify_package( - &compiled_package, - /* verify_deps */ false, - SourceMode::Verify, - ) + .verify(&compiled_package, ValidationMode::root()) .await .map_err(|e| anyhow!("Network {network}: {e}"))?; diff --git a/crates/sui-source-validation/Cargo.toml b/crates/sui-source-validation/Cargo.toml index a1b1918889e1a..8dc2ef1f07b17 100644 --- a/crates/sui-source-validation/Cargo.toml +++ b/crates/sui-source-validation/Cargo.toml @@ -17,9 +17,10 @@ tracing.workspace = true futures.workspace = true sui-json-rpc-types.workspace = true +sui-move-build.workspace = true +sui-package-management.workspace = true sui-types.workspace = true sui-sdk.workspace = true -sui-move-build.workspace = true move-binary-format.workspace = true move-bytecode-source-map.workspace = true diff --git a/crates/sui-source-validation/src/error.rs b/crates/sui-source-validation/src/error.rs index 9f0be2545c22b..15466b0b67eae 100644 --- a/crates/sui-source-validation/src/error.rs +++ b/crates/sui-source-validation/src/error.rs @@ -6,6 +6,7 @@ use std::fmt; use move_core_types::account_address::AccountAddress; use move_symbol_pool::Symbol; use sui_json_rpc_types::SuiRawMoveObject; +use sui_package_management::PublishedAtError; use sui_sdk::error::Error as SdkError; use sui_types::{base_types::ObjectID, error::SuiObjectResponseError}; @@ -20,6 +21,9 @@ pub enum Error { #[error("Could not read a dependency's on-chain object: {0:?}")] DependencyObjectReadFailure(SdkError), + #[error("On-chain package {0} is empty")] + EmptyOnChainPackage(AccountAddress), + #[error("Invalid module {name} with error: {message}")] InvalidModuleFailure { name: String, message: String }, @@ -29,6 +33,12 @@ pub enum Error { module: Symbol, }, + #[error("Source package depends on {0} which is not in the linkage table.")] + MissingDependencyInLinkageTable(AccountAddress), + + #[error("On-chain package depends on {0} which is not a source dependency.")] + MissingDependencyInSourcePackage(AccountAddress), + #[error( "Local dependency did not match its on-chain version at {address}::{package}::{module}" )] @@ -50,6 +60,9 @@ pub enum Error { #[error("On-chain version of dependency {package}::{module} was not found.")] OnChainDependencyNotFound { package: Symbol, module: Symbol }, + #[error("{0}. Please supply an explicit on-chain address for the package")] + PublishedAt(#[from] PublishedAtError), + #[error("Dependency object does not exist or was deleted: {0:?}")] SuiObjectRefFailure(SuiObjectResponseError), diff --git a/crates/sui-source-validation/src/lib.rs b/crates/sui-source-validation/src/lib.rs index 3b86d7cfba461..a692974c68a3f 100644 --- a/crates/sui-source-validation/src/lib.rs +++ b/crates/sui-source-validation/src/lib.rs @@ -6,9 +6,8 @@ use futures::future; use move_binary_format::CompiledModule; use move_compiler::compiled_unit::NamedCompiledModule; use move_core_types::account_address::AccountAddress; -use move_package::compilation::compiled_package::CompiledPackage as MoveCompiledPackage; use move_symbol_pool::Symbol; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use sui_move_build::CompiledPackage; use sui_sdk::apis::ReadApi; use sui_sdk::error::Error as SdkError; @@ -22,17 +21,21 @@ mod toolchain; #[cfg(test)] mod tests; -/// How to handle package source during bytecode verification. -#[derive(PartialEq, Eq)] -pub enum SourceMode { - /// Don't verify source. - Skip, - - /// Verify source at the address specified in its manifest. - Verify, - - /// Verify source at an overridden address (only works if the package is not published) - VerifyAt(AccountAddress), +/// Details of what to verify +pub enum ValidationMode { + /// Validate only the dependencies + Deps, + + /// Validate the root package, and its linkage. + Root { + /// Additionally validate the dependencies, and make sure the runtime and storage IDs in + /// dependency source code matches the root package's on-chain linkage table. + deps: bool, + + /// Look for the root package on-chain at the specified address, rather than the address in + /// its manifest. + at: Option, + }, } pub struct BytecodeSourceVerifier<'a> { @@ -41,119 +44,332 @@ pub struct BytecodeSourceVerifier<'a> { /// Map package addresses and module names to package names and bytecode. type LocalModules = HashMap<(AccountAddress, Symbol), (Symbol, CompiledModule)>; -/// Map package addresses and modules names to bytecode (package names are gone in the on-chain -/// representation). -type OnChainModules = HashMap<(AccountAddress, Symbol), CompiledModule>; -impl<'a> BytecodeSourceVerifier<'a> { - pub fn new(rpc_client: &'a ReadApi) -> Self { - BytecodeSourceVerifier { rpc_client } +#[derive(Default)] +struct OnChainRepresentation { + /// Storage IDs from the root package's on-chain linkage table. This will only be present if + /// root package verification was requested, in which case the keys from this mapping must + /// match the source package's dependencies. + on_chain_dependencies: Option>, + + /// Map package addresses and module names to bytecode (package names are gone in the on-chain + /// representation). + modules: HashMap<(AccountAddress, Symbol), CompiledModule>, +} + +impl ValidationMode { + /// Only verify that source dependencies match their on-chain versions. + pub fn deps() -> Self { + Self::Deps } - /// Helper wrapper to verify that all local Move package dependencies' and root bytecode matches - /// the bytecode at the address specified on the Sui network we are publishing to. - pub async fn verify_package_root_and_deps( - &self, - compiled_package: &CompiledPackage, - root_on_chain_address: AccountAddress, - ) -> Result<(), AggregateError> { - self.verify_package( - compiled_package, - /* verify_deps */ true, - SourceMode::VerifyAt(root_on_chain_address), - ) - .await + /// Only verify that the root package matches its on-chain version (requires that the root + /// package is published with its address available in the manifest). + pub fn root() -> Self { + Self::Root { + deps: false, + at: None, + } } - /// Helper wrapper to verify that all local Move package root bytecode matches - /// the bytecode at the address specified on the Sui network we are publishing to. - pub async fn verify_package_root( - &self, - compiled_package: &CompiledPackage, - root_on_chain_address: AccountAddress, - ) -> Result<(), AggregateError> { - self.verify_package( - compiled_package, - /* verify_deps */ false, - SourceMode::VerifyAt(root_on_chain_address), - ) - .await + /// Only verify that the root package matches its on-chain version, but override the location + /// to look for the root package to `address`. + pub fn root_at(address: AccountAddress) -> Self { + Self::Root { + deps: false, + at: Some(address), + } + } + + /// Verify both the root package and its dependencies (requires that the root package is + /// published with its address available in the manifest). + pub fn root_and_deps() -> Self { + Self::Root { + deps: true, + at: None, + } + } + + /// Verify both the root package and its dependencies, but override the location to look for + /// the root package to `address`. + pub fn root_and_deps_at(address: AccountAddress) -> Self { + Self::Root { + deps: true, + at: Some(address), + } } - /// Helper wrapper to verify that all local Move package dependencies' matches - /// the bytecode at the address specified on the Sui network we are publishing to. - pub async fn verify_package_deps( + /// Should we verify dependencies? + fn verify_deps(&self) -> bool { + matches!(self, Self::Deps | Self::Root { deps: true, .. }) + } + + /// If the root package needs to be verified, what address should it be fetched from? + fn root_address(&self, package: &CompiledPackage) -> Result, Error> { + match self { + Self::Root { at: Some(addr), .. } => Ok(Some(*addr)), + Self::Root { at: None, .. } => Ok(Some(*package.published_at.clone()?)), + Self::Deps => Ok(None), + } + } + + /// All the on-chain addresses that we need to fetch to build on-chain addresses. + fn on_chain_addresses(&self, package: &CompiledPackage) -> Result, Error> { + let mut addrs = vec![]; + + if let Some(addr) = self.root_address(package)? { + addrs.push(addr); + } + + if self.verify_deps() { + addrs.extend(dependency_addresses(package)); + } + + Ok(addrs) + } + + /// On-chain representation of the package and dependencies compiled to `package`, including + /// linkage information. + async fn on_chain( &self, - compiled_package: &CompiledPackage, - ) -> Result<(), AggregateError> { - self.verify_package( - compiled_package, - /* verify_deps */ true, - SourceMode::Skip, + package: &CompiledPackage, + verifier: &BytecodeSourceVerifier<'_>, + ) -> Result { + let mut on_chain = OnChainRepresentation::default(); + let mut errs: Vec = vec![]; + + let root = self.root_address(package)?; + let addrs = self.on_chain_addresses(package)?; + + let resps = + future::join_all(addrs.iter().copied().map(|a| verifier.pkg_for_address(a))).await; + + for (storage_id, pkg) in addrs.into_iter().zip(resps) { + let SuiRawMovePackage { + module_map, + linkage_table, + .. + } = pkg?; + + let mut modules = module_map + .into_iter() + .map(|(name, bytes)| { + let Ok(module) = CompiledModule::deserialize_with_defaults(&bytes) else { + return Err(Error::OnChainDependencyDeserializationError { + address: storage_id, + module: name.into(), + }); + }; + + Ok::<_, Error>((Symbol::from(name), module)) + }) + .peekable(); + + let runtime_id = match modules.peek() { + Some(Ok((_, module))) => *module.self_id().address(), + + Some(Err(_)) => { + // SAFETY: The error type does not implement `Clone` so we need to take the + // error by value. We do that by calling `next` to take the value we just + // peeked, which we know is an error type. + errs.push(modules.next().unwrap().unwrap_err()); + continue; + } + + None => { + errs.push(Error::EmptyOnChainPackage(storage_id)); + continue; + } + }; + + for module in modules { + match module { + Ok((name, module)) => { + on_chain.modules.insert((runtime_id, name), module); + } + + Err(e) => { + errs.push(e); + continue; + } + } + } + + if root.is_some_and(|r| r == storage_id) { + on_chain.on_chain_dependencies = Some(HashSet::from_iter( + linkage_table.into_values().map(|info| *info.upgraded_id), + )); + } + } + + Ok(on_chain) + } + + /// Local representation of the modules in `package`. If the validation mode requires verifying + /// dependencies, then the dependencies' modules are also included in the output. + /// + /// For the purposes of this function, a module is considered a dependency if it is from a + /// different source package, and that source package has already been published. Conversely, a + /// module that is from a different source package, but that has not been published is + /// considered part of the root package. + /// + /// If the validation mode requires verifying the root package at a specific address, then the + /// modules from the root package will be expected at address `0x0` and this address will be + /// substituted with the specified address. + fn local(&self, package: &CompiledPackage) -> Result { + let package = &package.package; + let root_package = package.compiled_package_info.package_name; + let mut map = LocalModules::new(); + + if self.verify_deps() { + let deps_compiled_units = + units_for_toolchain(&package.deps_compiled_units).map_err(|e| { + Error::CannotCheckLocalModules { + package: package.compiled_package_info.package_name, + message: e.to_string(), + } + })?; + + for (package, local_unit) in deps_compiled_units { + let m = &local_unit.unit; + let module = m.name; + let address = m.address.into_inner(); + + // Skip modules with on 0x0 because they are treated as part of the root package, + // even if they are a source dependency. + if address == AccountAddress::ZERO { + continue; + } + + map.insert((address, module), (package, m.module.clone())); + } + } + + let Self::Root { at, .. } = self else { + return Ok(map); + }; + + // Potentially rebuild according to the toolchain that the package was originally built + // with. + let root_compiled_units = units_for_toolchain( + &package + .root_compiled_units + .iter() + .map(|u| ("root".into(), u.clone())) + .collect(), ) - .await + .map_err(|e| Error::CannotCheckLocalModules { + package: package.compiled_package_info.package_name, + message: e.to_string(), + })?; + + // Add the root modules, potentially remapping 0x0 if we have been supplied an address to + // substitute with. + for (_, local_unit) in root_compiled_units { + let m = &local_unit.unit; + let module = m.name; + let address = m.address.into_inner(); + + let (address, compiled_module) = if let Some(root_address) = at { + (*root_address, substitute_root_address(m, *root_address)?) + } else if address == AccountAddress::ZERO { + return Err(Error::InvalidModuleFailure { + name: module.to_string(), + message: "Can't verify unpublished source".to_string(), + }); + } else { + (address, m.module.clone()) + }; + + map.insert((address, module), (root_package, compiled_module)); + } + + // If we have a root address to substitute, we need to find unpublished dependencies that + // would have gone into the root package as well. + if let Some(root_address) = at { + for (package, local_unit) in &package.deps_compiled_units { + let m = &local_unit.unit; + let module = m.name; + let address = m.address.into_inner(); + + if address != AccountAddress::ZERO { + continue; + } + + map.insert( + (*root_address, module), + (*package, substitute_root_address(m, *root_address)?), + ); + } + } + + Ok(map) } +} - /// Verify that all local Move package dependencies' and/or root bytecode matches the bytecode - /// at the address specified on the Sui network we are publishing to. If `verify_deps` is true, - /// the dependencies are verified. If `root_on_chain_address` is specified, the root is - /// verified against a package at `root_on_chain_address`. - pub async fn verify_package( +impl<'a> BytecodeSourceVerifier<'a> { + pub fn new(rpc_client: &'a ReadApi) -> Self { + BytecodeSourceVerifier { rpc_client } + } + + /// Verify that the `compiled_package` matches its on-chain representation. + /// + /// See [`ValidationMode`] for more details on what is verified. + pub async fn verify( &self, - compiled_package: &CompiledPackage, - verify_deps: bool, - source_mode: SourceMode, + package: &CompiledPackage, + mode: ValidationMode, ) -> Result<(), AggregateError> { - let mut on_chain_pkgs = vec![]; - match &source_mode { - SourceMode::Skip => (), - // On-chain address for matching root package cannot be zero - SourceMode::VerifyAt(AccountAddress::ZERO) => { - return Err(Error::ZeroOnChainAddresSpecifiedFailure.into()) + if matches!( + mode, + ValidationMode::Root { + at: Some(AccountAddress::ZERO), + .. } - SourceMode::VerifyAt(root_address) => on_chain_pkgs.push(*root_address), - SourceMode::Verify => { - on_chain_pkgs.extend(compiled_package.published_at.as_ref().map(|id| **id)) - } - }; + ) { + return Err(Error::ZeroOnChainAddresSpecifiedFailure.into()); + } + + let local = mode.local(package)?; + let mut chain = mode.on_chain(package, self).await?; + let mut errs = vec![]; - if verify_deps { - on_chain_pkgs.extend( - compiled_package - .dependency_ids - .published - .values() - .map(|id| **id), - ); + // Check that the transitive dependencies listed on chain match the dependencies listed in + // source code. Ignore 0x0 becaus this signifies an unpublished dependency. + if let Some(on_chain_deps) = &mut chain.on_chain_dependencies { + for dependency_id in dependency_addresses(package) { + if dependency_id != AccountAddress::ZERO && !on_chain_deps.remove(&dependency_id) { + errs.push(Error::MissingDependencyInLinkageTable(dependency_id)); + } + } } - let local_modules = local_modules(&compiled_package.package, verify_deps, source_mode)?; - let mut on_chain_modules = self.on_chain_modules(on_chain_pkgs.into_iter()).await?; + for on_chain_dep_id in chain.on_chain_dependencies.take().into_iter().flatten() { + errs.push(Error::MissingDependencyInSourcePackage(on_chain_dep_id)); + } - let mut errors = Vec::new(); - for ((address, module), (package, local_module)) in local_modules { - let Some(on_chain_module) = on_chain_modules.remove(&(address, module)) else { - errors.push(Error::OnChainDependencyNotFound { package, module }); + // Check that the contents of bytecode matches between modules. + for ((address, module), (package, local_module)) in local { + let Some(on_chain_module) = chain.modules.remove(&(address, module)) else { + errs.push(Error::OnChainDependencyNotFound { package, module }); continue; }; - // compare local bytecode to on-chain bytecode to ensure integrity of our - // dependencies if local_module != on_chain_module { - errors.push(Error::ModuleBytecodeMismatch { + errs.push(Error::ModuleBytecodeMismatch { address, package, module, - }); + }) } } - if let Some(((address, module), _)) = on_chain_modules.into_iter().next() { - errors.push(Error::LocalDependencyNotFound { address, module }); + for (address, module) in chain.modules.into_keys() { + errs.push(Error::LocalDependencyNotFound { address, module }); } - if !errors.is_empty() { - return Err(AggregateError(errors)); + if !errs.is_empty() { + return Err(AggregateError(errs)); } Ok(()) @@ -190,37 +406,6 @@ impl<'a> BytecodeSourceVerifier<'a> { } } } - - async fn on_chain_modules( - &self, - addresses: impl Iterator + Clone, - ) -> Result { - let resp = future::join_all(addresses.clone().map(|addr| self.pkg_for_address(addr))).await; - let mut map = OnChainModules::new(); - let mut err = vec![]; - - for (storage_id, pkg) in addresses.zip(resp) { - let SuiRawMovePackage { module_map, .. } = pkg?; - for (name, bytes) in module_map { - let Ok(module) = CompiledModule::deserialize_with_defaults(&bytes) else { - err.push(Error::OnChainDependencyDeserializationError { - address: storage_id, - module: name.into(), - }); - continue; - }; - - let runtime_id = *module.self_id().address(); - map.insert((runtime_id, Symbol::from(name)), module); - } - } - - if !err.is_empty() { - return Err(AggregateError(err)); - } - - Ok(map) - } } fn substitute_root_address( @@ -248,115 +433,7 @@ fn substitute_root_address( Ok(module) } -fn local_modules( - compiled_package: &MoveCompiledPackage, - include_deps: bool, - source_mode: SourceMode, -) -> Result { - let mut map = LocalModules::new(); - - if include_deps { - // Compile dependencies with prior compilers if needed. - let deps_compiled_units = units_for_toolchain(&compiled_package.deps_compiled_units) - .map_err(|e| Error::CannotCheckLocalModules { - package: compiled_package.compiled_package_info.package_name, - message: e.to_string(), - })?; - - for (package, local_unit) in deps_compiled_units { - let m = &local_unit.unit; - let module = m.name; - let address = m.address.into_inner(); - if address == AccountAddress::ZERO { - continue; - } - - map.insert((address, module), (package, m.module.clone())); - } - } - - let root_package = compiled_package.compiled_package_info.package_name; - match source_mode { - SourceMode::Skip => { /* nop */ } - - // Include the root compiled units, at their current addresses. - SourceMode::Verify => { - // Compile root modules with prior compiler if needed. - let root_compiled_units = { - let root_compiled_units = compiled_package - .root_compiled_units - .iter() - .map(|u| ("root".into(), u.clone())) - .collect::>(); - - units_for_toolchain(&root_compiled_units).map_err(|e| { - Error::CannotCheckLocalModules { - package: compiled_package.compiled_package_info.package_name, - message: e.to_string(), - } - })? - }; - - for (_, local_unit) in root_compiled_units { - let m = &local_unit.unit; - - let module = m.name; - let address = m.address.into_inner(); - if address == AccountAddress::ZERO { - return Err(Error::InvalidModuleFailure { - name: module.to_string(), - message: "Can't verify unpublished source".to_string(), - }); - } - - map.insert((address, module), (root_package, m.module.clone())); - } - } - - // Include the root compiled units, and any unpublished dependencies with their - // addresses substituted - SourceMode::VerifyAt(root_address) => { - // Compile root modules with prior compiler if needed. - let root_compiled_units = { - let root_compiled_units = compiled_package - .root_compiled_units - .iter() - .map(|u| ("root".into(), u.clone())) - .collect::>(); - - units_for_toolchain(&root_compiled_units).map_err(|e| { - Error::CannotCheckLocalModules { - package: compiled_package.compiled_package_info.package_name, - message: e.to_string(), - } - })? - }; - - for (_, local_unit) in root_compiled_units { - let m = &local_unit.unit; - - let module = m.name; - map.insert( - (root_address, module), - (root_package, substitute_root_address(m, root_address)?), - ); - } - - for (package, local_unit) in &compiled_package.deps_compiled_units { - let m = &local_unit.unit; - let module = m.name; - let address = m.address.into_inner(); - if address != AccountAddress::ZERO { - continue; - } - - map.insert( - (root_address, module), - (*package, substitute_root_address(m, root_address)?), - ); - } - } - } - - Ok(map) +/// The on-chain addresses for a source package's dependencies +fn dependency_addresses(package: &CompiledPackage) -> impl Iterator + '_ { + package.dependency_ids.published.values().map(|id| **id) } diff --git a/crates/sui-source-validation/src/tests.rs b/crates/sui-source-validation/src/tests.rs index abd191b4ce80f..620b209e43d5b 100644 --- a/crates/sui-source-validation/src/tests.rs +++ b/crates/sui-source-validation/src/tests.rs @@ -22,7 +22,7 @@ use sui_types::{ use test_cluster::TestClusterBuilder; use crate::toolchain::CURRENT_COMPILER_VERSION; -use crate::{BytecodeSourceVerifier, SourceMode}; +use crate::{BytecodeSourceVerifier, ValidationMode}; #[tokio::test] async fn successful_verification() -> anyhow::Result<()> { @@ -54,30 +54,27 @@ async fn successful_verification() -> anyhow::Result<()> { let client = context.get_client().await?; let verifier = BytecodeSourceVerifier::new(client.read_api()); - // Skip deps and root + // Verify root without updating the address verifier - .verify_package(&a_pkg, /* verify_deps */ false, SourceMode::Skip) + .verify(&b_pkg, ValidationMode::root()) .await .unwrap(); - // Verify root without updating the address + // Verify deps but skip root verifier - .verify_package(&b_pkg, /* verify_deps */ false, SourceMode::Verify) + .verify(&a_pkg, ValidationMode::deps()) .await .unwrap(); - // Verify deps but skip root - verifier.verify_package_deps(&a_pkg).await.unwrap(); - // Skip deps but verify root verifier - .verify_package_root(&a_pkg, a_ref.0.into()) + .verify(&a_pkg, ValidationMode::root_at(a_ref.0.into())) .await .unwrap(); // Verify both deps and root verifier - .verify_package_root_and_deps(&a_pkg, a_ref.0.into()) + .verify(&a_pkg, ValidationMode::root_and_deps_at(a_ref.0.into())) .await .unwrap(); @@ -103,7 +100,7 @@ async fn successful_verification_unpublished_deps() -> anyhow::Result<()> { // Verify the root package which now includes dependency modules verifier - .verify_package_root(&a_pkg, a_ref.0.into()) + .verify(&a_pkg, ValidationMode::root_at(a_ref.0.into())) .await .unwrap(); @@ -136,11 +133,8 @@ async fn successful_verification_module_ordering() -> anyhow::Result<()> { }; let client = context.get_client().await?; - let verifier = BytecodeSourceVerifier::new(client.read_api()); - - let verify_deps = false; - verifier - .verify_package(&z_pkg, verify_deps, SourceMode::Verify) + BytecodeSourceVerifier::new(client.read_api()) + .verify(&z_pkg, ValidationMode::root()) .await .unwrap(); @@ -176,14 +170,16 @@ async fn successful_verification_upgrades() -> anyhow::Result<()> { let verifier = BytecodeSourceVerifier::new(client.read_api()); // Verify the upgraded package b-v2 as the root. - let verify_deps = false; verifier - .verify_package(&b_pkg, verify_deps, SourceMode::Verify) + .verify(&b_pkg, ValidationMode::root()) .await .unwrap(); // Verify the upgraded package b-v2 as a dep of e. - verifier.verify_package_deps(&e_pkg).await.unwrap(); + verifier + .verify(&e_pkg, ValidationMode::deps()) + .await + .unwrap(); Ok(()) } @@ -208,12 +204,13 @@ async fn fail_verification_bad_address() -> anyhow::Result<()> { }; let client = context.get_client().await?; - let verifier = BytecodeSourceVerifier::new(client.read_api()); - let expected = expect!["On-chain address cannot be zero"]; expected.assert_eq( - &verifier - .verify_package_root_and_deps(&a_pkg, AccountAddress::ZERO) + &BytecodeSourceVerifier::new(client.read_api()) + .verify( + &a_pkg, + ValidationMode::root_and_deps_at(AccountAddress::ZERO), + ) .await .unwrap_err() .to_string(), @@ -234,14 +231,13 @@ async fn fail_to_verify_unpublished_root() -> anyhow::Result<()> { }; let client = context.get_client().await?; - let verifier = BytecodeSourceVerifier::new(client.read_api()); // Trying to verify the root package, which hasn't been published -- this is going to fail // because there is no on-chain package to verify against. let expected = expect!["Invalid module b with error: Can't verify unpublished source"]; expected.assert_eq( - &verifier - .verify_package(&b_pkg, /* verify_deps */ false, SourceMode::Verify) + &BytecodeSourceVerifier::new(client.read_api()) + .verify(&b_pkg, ValidationMode::root()) .await .unwrap_err() .to_string(), @@ -320,7 +316,7 @@ async fn package_not_found() -> anyhow::Result<()> { let client = context.get_client().await?; let verifier = BytecodeSourceVerifier::new(client.read_api()); - let Err(err) = verifier.verify_package_deps(&a_pkg).await else { + let Err(err) = verifier.verify(&a_pkg, ValidationMode::deps()).await else { panic!("Expected verification to fail"); }; @@ -331,7 +327,7 @@ async fn package_not_found() -> anyhow::Result<()> { let package_root = AccountAddress::random(); stable_addrs.insert(SuiAddress::from(package_root), ""); let Err(err) = verifier - .verify_package_root_and_deps(&a_pkg, package_root) + .verify(&a_pkg, ValidationMode::root_and_deps_at(package_root)) .await else { panic!("Expected verification to fail"); @@ -345,7 +341,10 @@ async fn package_not_found() -> anyhow::Result<()> { let package_root = AccountAddress::random(); stable_addrs.insert(SuiAddress::from(package_root), ""); - let Err(err) = verifier.verify_package_root(&a_pkg, package_root).await else { + let Err(err) = verifier + .verify(&a_pkg, ValidationMode::root_at(package_root)) + .await + else { panic!("Expected verification to fail"); }; @@ -368,13 +367,12 @@ async fn dependency_is_an_object() -> anyhow::Result<()> { let a_src = copy_published_package(&a_pkg_fixtures, "a", SuiAddress::ZERO).await?; compile_package(a_src) }; - let client = context.get_client().await?; - let verifier = BytecodeSourceVerifier::new(client.read_api()); + let client = context.get_client().await?; let expected = expect!["Dependency ID contains a Sui object, not a Move package: 0x0000000000000000000000000000000000000000000000000000000000000005"]; expected.assert_eq( - &verifier - .verify_package_deps(&a_pkg) + &BytecodeSourceVerifier::new(client.read_api()) + .verify(&a_pkg, ValidationMode::deps()) .await .unwrap_err() .to_string(), @@ -401,10 +399,12 @@ async fn module_not_found_on_chain() -> anyhow::Result<()> { let a_src = copy_published_package(&a_pkg_fixtures, "a", SuiAddress::ZERO).await?; compile_package(a_src) }; - let client = context.get_client().await?; - let verifier = BytecodeSourceVerifier::new(client.read_api()); - let Err(err) = verifier.verify_package_deps(&a_pkg).await else { + let client = context.get_client().await?; + let Err(err) = BytecodeSourceVerifier::new(client.read_api()) + .verify(&a_pkg, ValidationMode::deps()) + .await + else { panic!("Expected verification to fail"); }; @@ -437,9 +437,10 @@ async fn module_not_found_locally() -> anyhow::Result<()> { }; let client = context.get_client().await?; - let verifier = BytecodeSourceVerifier::new(client.read_api()); - - let Err(err) = verifier.verify_package_deps(&a_pkg).await else { + let Err(err) = BytecodeSourceVerifier::new(client.read_api()) + .verify(&a_pkg, ValidationMode::deps()) + .await + else { panic!("Expected verification to fail"); }; @@ -492,14 +493,17 @@ async fn module_bytecode_mismatch() -> anyhow::Result<()> { let client = context.get_client().await?; let verifier = BytecodeSourceVerifier::new(client.read_api()); - let Err(err) = verifier.verify_package_deps(&a_pkg).await else { + let Err(err) = verifier.verify(&a_pkg, ValidationMode::deps()).await else { panic!("Expected verification to fail"); }; let expected = expect!["Local dependency did not match its on-chain version at ::b::c"]; expected.assert_eq(&sanitize_id(err.to_string(), &stable_addrs)); - let Err(err) = verifier.verify_package_root(&a_pkg, a_addr.into()).await else { + let Err(err) = verifier + .verify(&a_pkg, ValidationMode::root_at(a_addr.into())) + .await + else { panic!("Expected verification to fail"); }; @@ -509,6 +513,73 @@ async fn module_bytecode_mismatch() -> anyhow::Result<()> { Ok(()) } +#[tokio::test] +async fn linkage_differs() -> anyhow::Result<()> { + let mut cluster = TestClusterBuilder::new().build().await; + let context = &mut cluster.wallet; + + let b_v1_fixtures = tempfile::tempdir()?; + let (b_v1, b_cap) = { + let b_src = copy_published_package(&b_v1_fixtures, "b", SuiAddress::ZERO).await?; + publish_package(context, b_src).await + }; + + let b_v2_fixtures = tempfile::tempdir()?; + let b_v2 = { + let b_src = + copy_upgraded_package(&b_v2_fixtures, "b-v2", b_v1.0.into(), SuiAddress::ZERO).await?; + upgrade_package(context, b_v1.0, b_cap.0, b_src).await + }; + + // Publish b-v2 a second time, to create a third version of the package that is othewise + // byte-for-byte identical with the second version; + let b_v3_fixtures = tempfile::tempdir()?; + let b_v3 = { + let b_src = + copy_upgraded_package(&b_v3_fixtures, "b-v2", b_v2.0.into(), SuiAddress::ZERO).await?; + upgrade_package(context, b_v2.0, b_cap.0, b_src).await + }; + + // Publish E pointing at v2 of B. + let e_v1_fixtures = tempfile::tempdir()?; + let (e_v1, _) = { + copy_upgraded_package(&e_v1_fixtures, "b-v2", b_v2.0.into(), b_v1.0.into()).await?; + let e_src = copy_published_package(&e_v1_fixtures, "e", SuiAddress::ZERO).await?; + publish_package(context, e_src).await + }; + + // Compile E pointing at v3 of B, which is byte-for-byte identical with v2, but nevertheless + // has a different address. + let e_v2_fixtures = tempfile::tempdir()?; + let e_pkg = { + copy_upgraded_package(&e_v2_fixtures, "b-v2", b_v3.0.into(), b_v1.0.into()).await?; + let e_src = copy_published_package(&e_v2_fixtures, "e", e_v1.0.into()).await?; + compile_package(e_src) + }; + + let client = context.get_client().await?; + let stable_ids = HashMap::from_iter([ + (b_v1.0.into(), ""), + (b_v2.0.into(), ""), + (b_v3.0.into(), ""), + ]); + + let error = BytecodeSourceVerifier::new(client.read_api()) + .verify(&e_pkg, ValidationMode::root()) + .await + .unwrap_err() + .to_string(); + + let expected = expect![[r#" + Multiple source verification errors found: + + - Source package depends on which is not in the linkage table. + - On-chain package depends on which is not a source dependency."#]]; + expected.assert_eq(&sanitize_id(error, &stable_ids)); + + Ok(()) +} + #[tokio::test] async fn multiple_failures() -> anyhow::Result<()> { let mut cluster = TestClusterBuilder::new().build().await; @@ -547,9 +618,10 @@ async fn multiple_failures() -> anyhow::Result<()> { }; let client = context.get_client().await?; - let verifier = BytecodeSourceVerifier::new(client.read_api()); - - let Err(err) = verifier.verify_package_deps(&d_pkg).await else { + let Err(err) = BytecodeSourceVerifier::new(client.read_api()) + .verify(&d_pkg, ValidationMode::deps()) + .await + else { panic!("Expected verification to fail"); }; @@ -585,10 +657,12 @@ async fn successful_versioned_dependency_verification() -> anyhow::Result<()> { }; let client = context.get_client().await?; - let verifier = BytecodeSourceVerifier::new(client.read_api()); // Verify versioned dependency - verifier.verify_package_deps(&a_pkg).await.unwrap(); + BytecodeSourceVerifier::new(client.read_api()) + .verify(&a_pkg, ValidationMode::deps()) + .await + .unwrap(); Ok(()) } diff --git a/crates/sui/src/client_commands.rs b/crates/sui/src/client_commands.rs index 09907f0941ede..018fcc200177a 100644 --- a/crates/sui/src/client_commands.rs +++ b/crates/sui/src/client_commands.rs @@ -36,7 +36,7 @@ use serde::Serialize; use serde_json::{json, Value}; use sui_move::manage_package::resolve_lock_file_path; use sui_protocol_config::{Chain, ProtocolConfig, ProtocolVersion}; -use sui_source_validation::{BytecodeSourceVerifier, SourceMode}; +use sui_source_validation::{BytecodeSourceVerifier, ValidationMode}; use shared_crypto::intent::Intent; use sui_json::SuiJsonValue; @@ -1605,11 +1605,17 @@ impl SuiClientCommands { skip_source, address_override, } => { - if skip_source && !verify_deps { - return Err(anyhow!( - "Source skipped and not verifying deps: Nothing to verify." - )); - } + let mode = match (!skip_source, verify_deps, address_override) { + (false, false, _) => { + bail!("Source skipped and not verifying deps: Nothing to verify.") + } + + (false, true, _) => ValidationMode::deps(), + (true, false, None) => ValidationMode::root(), + (true, true, None) => ValidationMode::root_and_deps(), + (true, false, Some(at)) => ValidationMode::root_at(*at), + (true, true, Some(at)) => ValidationMode::root_and_deps_at(*at), + }; let build_config = resolve_lock_file_path(build_config, Some(&package_path))?; let chain_id = context @@ -1627,17 +1633,8 @@ impl SuiClientCommands { .build(&package_path)?; let client = context.get_client().await?; - BytecodeSourceVerifier::new(client.read_api()) - .verify_package( - &compiled_package, - verify_deps, - match (skip_source, address_override) { - (true, _) => SourceMode::Skip, - (false, None) => SourceMode::Verify, - (false, Some(addr)) => SourceMode::VerifyAt(addr.into()), - }, - ) + .verify(&compiled_package, mode) .await?; SuiClientCommandResult::VerifySource @@ -1860,7 +1857,10 @@ pub(crate) async fn compile_package( let compiled_modules = compiled_package.get_package_bytes(with_unpublished_dependencies); if !skip_dependency_verification { let verifier = BytecodeSourceVerifier::new(read_api); - if let Err(e) = verifier.verify_package_deps(&compiled_package).await { + if let Err(e) = verifier + .verify(&compiled_package, ValidationMode::deps()) + .await + { return Err(SuiError::ModulePublishFailure { error: format!( "[warning] {e}\n\ From ecbf4b1a0e3a6be8ae56a3023ba66e81558bbc09 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 14 Aug 2024 11:02:07 -0500 Subject: [PATCH 129/232] chore: update quinn-proto --- Cargo.lock | 9 +++++---- Cargo.toml | 3 +-- scripts/simtest/config-patch | 3 +-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e4adc26ccd99..13bac90d114fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9942,7 +9942,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1" dependencies = [ "bytes", - "heck 0.4.1", + "heck 0.5.0", "itertools 0.13.0", "log", "multimap", @@ -10106,8 +10106,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.3" -source = "git+https://github.com/quinn-rs/quinn.git?rev=f0fa66f871b80b9d2d7075d76967c649aecc0b77#f0fa66f871b80b9d2d7075d76967c649aecc0b77" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba92fb39ec7ad06ca2582c0ca834dfeadcaf06ddfc8e635c80aa7e1c05315fdd" dependencies = [ "bytes", "rand 0.8.5", @@ -16300,7 +16301,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", - "rand 0.7.3", + "rand 0.8.5", "static_assertions", ] diff --git a/Cargo.toml b/Cargo.toml index e0532e07df253..b6e34f378a2ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -409,7 +409,7 @@ proptest-derive = "0.3.0" prost = "0.13" prost-build = "0.13" protobuf = { version = "2.28", features = ["with-bytes"] } -quinn-proto = "0.11" +quinn-proto = "0.11.6" quote = "1.0.23" rand = "0.8.5" rayon = "1.5.3" @@ -694,4 +694,3 @@ spinners = "4.1.0" include_dir = "0.7.3" [patch.crates-io] -quinn-proto = { git = "https://github.com/quinn-rs/quinn.git", rev = "f0fa66f871b80b9d2d7075d76967c649aecc0b77" } diff --git a/scripts/simtest/config-patch b/scripts/simtest/config-patch index e30af462b02ad..fe106f225f90e 100644 --- a/scripts/simtest/config-patch +++ b/scripts/simtest/config-patch @@ -12,12 +12,11 @@ diff --git a/Cargo.toml b/Cargo.toml index c0829bc1b6..4007f97d66 100644 --- a/Cargo.toml +++ b/Cargo.toml -@@ -682,6 +682,8 @@ field_names = "0.2.0" +@@ -682,5 +682,7 @@ field_names = "0.2.0" semver = "1.0.16" spinners = "4.1.0" include_dir = "0.7.3" [patch.crates-io] - quinn-proto = { git = "https://github.com/quinn-rs/quinn.git", rev = "f0fa66f871b80b9d2d7075d76967c649aecc0b77" } +tokio = { git = "https://github.com/MystenLabs/mysten-sim.git", rev = "220f52a15804a768610ac0ae3b8da7de4a5c4d2b" } +futures-timer = { git = "https://github.com/MystenLabs/mysten-sim.git", rev = "220f52a15804a768610ac0ae3b8da7de4a5c4d2b" } From e205aca231ba52b29fb51b282ad850fc63b7444e Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 13 Aug 2024 13:58:00 -0500 Subject: [PATCH 130/232] authority_aggregator: sample 5 validators for objects contents --- crates/sui-core/src/authority_aggregator.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/sui-core/src/authority_aggregator.rs b/crates/sui-core/src/authority_aggregator.rs index 6bc31210d6c51..65955171bcbec 100644 --- a/crates/sui-core/src/authority_aggregator.rs +++ b/crates/sui-core/src/authority_aggregator.rs @@ -1501,11 +1501,11 @@ where // create a set of validators that we should sample to request input/output objects from let validators_to_sample = if request.include_input_objects || request.include_output_objects { - // Always at least ask 1 validator - let number_to_sample = std::cmp::max(1, self.committee.num_members() / 2); + // Number of validators to request input/output objects from + const NUMBER_TO_SAMPLE: usize = 5; self.committee - .choose_multiple_weighted_iter(number_to_sample) + .choose_multiple_weighted_iter(NUMBER_TO_SAMPLE) .cloned() .collect() } else { From 65843fd352ef0bb6505d326f84a98e05b42c814d Mon Sep 17 00:00:00 2001 From: Eugene Boguslavsky Date: Thu, 15 Aug 2024 09:21:51 -0700 Subject: [PATCH 131/232] Set `SIMTEST_STATIC_INIT_MOVE` variable for Code Coverage (#18998) ## Description Set `SIMTEST_STATIC_INIT_MOVE` variable for Code Coverage due to https://github.com/MystenLabs/sui/pull/18752 ## Test plan https://github.com/MystenLabs/sui/actions/runs/10406180594/job/28818615076 --- scripts/simtest/codecov.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/simtest/codecov.sh b/scripts/simtest/codecov.sh index afdd148855633..6df7c9ccaef05 100755 --- a/scripts/simtest/codecov.sh +++ b/scripts/simtest/codecov.sh @@ -11,6 +11,9 @@ fi # apply git patch git apply ./scripts/simtest/config-patch +root_dir=$(git rev-parse --show-toplevel) +export SIMTEST_STATIC_INIT_MOVE=$root_dir"/examples/move/basics" + MSIM_WATCHDOG_TIMEOUT_MS=60000 MSIM_TEST_SEED=1 cargo llvm-cov --ignore-run-fail --lcov --output-path lcov-simtest.info nextest --cargo-profile simulator # remove the patch From b4d49448789ae47bc4f3aa17c11bdfa5830452e0 Mon Sep 17 00:00:00 2001 From: Adam Welc Date: Thu, 15 Aug 2024 10:44:13 -0700 Subject: [PATCH 132/232] [move-ide] Fixed a bug preventing pkg build from VSCode (#19003) ## Description This PR fixes a problem recently reported in https://github.com/MystenLabs/sui/issues/18749 and https://github.com/MystenLabs/sui/issues/18983. When creating `git` commands using `Command::new(...).status()` standard input inherited from VSCode was somehow causing the child process to hang, even though the commands created did not in fact require any input. This only happened on Windows and while admittedly I do not understand the root cause (perhaps one of the reviewers can help), it does fix the problem. ## Test plan All existing tests must pass. Additionally, manually verified that the build no longer hangs on Windows. --- .../move/crates/move-analyzer/editors/code/package.json | 2 +- .../move/crates/move-package/src/resolution/dependency_cache.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) 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 69839e35c5720..4a3be17e8b51c 100644 --- a/external-crates/move/crates/move-analyzer/editors/code/package.json +++ b/external-crates/move/crates/move-analyzer/editors/code/package.json @@ -5,7 +5,7 @@ "publisher": "mysten", "icon": "images/move.png", "license": "Apache-2.0", - "version": "1.0.10", + "version": "1.0.11", "preview": true, "repository": { "url": "https://github.com/MystenLabs/sui.git", diff --git a/external-crates/move/crates/move-package/src/resolution/dependency_cache.rs b/external-crates/move/crates/move-package/src/resolution/dependency_cache.rs index 7d2886052d3c8..6752f3f2fe39d 100644 --- a/external-crates/move/crates/move-package/src/resolution/dependency_cache.rs +++ b/external-crates/move/crates/move-package/src/resolution/dependency_cache.rs @@ -165,6 +165,7 @@ impl DependencyCache { OsStr::new("fetch"), OsStr::new("origin"), ]) + .stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::null()) .status() @@ -193,6 +194,7 @@ impl DependencyCache { OsStr::new("--hard"), OsStr::new(&format!("origin/{}", git_rev)), ]) + .stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::null()) .status() From b2108ef219829b573ff1df4e4ea41fdf61776fdd Mon Sep 17 00:00:00 2001 From: Eugene Boguslavsky Date: Thu, 15 Aug 2024 11:08:55 -0700 Subject: [PATCH 133/232] Exclude sui-bridge tests for code coverage (#19002) ## Description Exclude sui-bridge tests for code coverage ## Test plan https://github.com/MystenLabs/sui/actions/runs/10407677341/job/28823468147 --- .github/workflows/cargo-llvm-cov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cargo-llvm-cov.yml b/.github/workflows/cargo-llvm-cov.yml index 73f6cb5057650..dcf9eaf91e6b7 100644 --- a/.github/workflows/cargo-llvm-cov.yml +++ b/.github/workflows/cargo-llvm-cov.yml @@ -54,7 +54,7 @@ jobs: swap-size-gb: 256 - name: Run code coverage for nextest - run: RUSTFLAGS="-C debuginfo=0" SUI_SKIP_SIMTESTS=1 cargo llvm-cov --ignore-run-fail --lcov --output-path lcov.info nextest -vv + run: RUSTFLAGS="-C debuginfo=0" SUI_SKIP_SIMTESTS=1 cargo llvm-cov --ignore-run-fail --lcov --output-path lcov.info nextest -vv -E '!package(sui-bridge)' - name: Upload report to Codecov for nextest uses: codecov/codecov-action@e0b68c6749509c5f83f984dd99a76a1c1a231044 # pin v4.0.1 From 2ac77203bd850222f8082490c84def9f2946c915 Mon Sep 17 00:00:00 2001 From: Ge Gao <106119108+gegaowp@users.noreply.github.com> Date: Thu, 15 Aug 2024 14:22:20 -0400 Subject: [PATCH 134/232] indexer fix: reset db via reverting migrations (#18993) ## Description the issue was that, prev indexer db reset was done via dropping all tables, which is problematic when we change a PG PROCEDURE parameter, see this slack message. https://mysten-labs.slack.com/archives/C03TCGDF45N/p1723507055114959 this caused issues on CI after merging https://github.com/MystenLabs/sui/pull/18899 and it got reverted, this pr changes it to reverting all migrations and cleans up the table dropping codes ## Test plan locally - reset DB before #18899 - cherry-pick this pr - cherry-pick #18899 run cmd below, which was the cmd on CI that ran into issue ``` DB_POOL_SIZE=10 cargo run --bin sui-indexer -- --db-url "postgres://postgres:postgres@localhost/gegao" --reset-db ``` --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-graphql-rpc/src/data/pg.rs | 2 +- .../down.sql | 1 + crates/sui-indexer/src/db.rs | 113 +++--------------- crates/sui-indexer/src/test_utils.rs | 2 +- 4 files changed, 22 insertions(+), 96 deletions(-) diff --git a/crates/sui-graphql-rpc/src/data/pg.rs b/crates/sui-graphql-rpc/src/data/pg.rs index 980bde05b1fda..71b45248a2e2f 100644 --- a/crates/sui-graphql-rpc/src/data/pg.rs +++ b/crates/sui-graphql-rpc/src/data/pg.rs @@ -208,7 +208,7 @@ mod tests { ) .unwrap(); let mut conn = get_pool_connection(&pool).unwrap(); - reset_database(&mut conn, /* drop_all */ true).unwrap(); + reset_database(&mut conn).unwrap(); let objects: Vec = BuiltInFramework::iter_system_packages() .map(|pkg| IndexedObject::from_object(1, pkg.genesis_object(), None).into()) diff --git a/crates/sui-indexer/migrations/pg/2023-11-29-193859_advance_partition/down.sql b/crates/sui-indexer/migrations/pg/2023-11-29-193859_advance_partition/down.sql index 1693f3892a5fa..bab0311186e1d 100644 --- a/crates/sui-indexer/migrations/pg/2023-11-29-193859_advance_partition/down.sql +++ b/crates/sui-indexer/migrations/pg/2023-11-29-193859_advance_partition/down.sql @@ -1 +1,2 @@ DROP PROCEDURE IF EXISTS advance_partition; +DROP PROCEDURE IF EXISTS drop_partition; diff --git a/crates/sui-indexer/src/db.rs b/crates/sui-indexer/src/db.rs index 99b6df729463b..7057dd36eb282 100644 --- a/crates/sui-indexer/src/db.rs +++ b/crates/sui-indexer/src/db.rs @@ -160,7 +160,6 @@ pub fn get_pool_connection( pub fn reset_database( conn: &mut PoolConnection, - drop_all: bool, ) -> Result<(), anyhow::Error> { #[cfg(feature = "postgres-feature")] { @@ -169,7 +168,7 @@ pub fn reset_database( .map_or_else( || Err(anyhow!("Failed to downcast connection to PgConnection")), |pg_conn| { - setup_postgres::reset_database(pg_conn, drop_all)?; + setup_postgres::reset_database(pg_conn)?; Ok(()) }, )?; @@ -182,7 +181,7 @@ pub fn reset_database( .map_or_else( || Err(anyhow!("Failed to downcast connection to PgConnection")), |mysql_conn| { - setup_mysql::reset_database(mysql_conn, drop_all)?; + setup_mysql::reset_database(mysql_conn)?; Ok(()) }, )?; @@ -200,7 +199,7 @@ pub mod setup_postgres { use crate::IndexerConfig; use anyhow::anyhow; use diesel::migration::MigrationSource; - use diesel::{PgConnection, RunQueryDsl}; + use diesel::PgConnection; use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; use prometheus::Registry; use secrecy::ExposeSecret; @@ -208,52 +207,16 @@ pub mod setup_postgres { const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/pg"); - pub fn reset_database( - conn: &mut PoolConnection, - drop_all: bool, - ) -> Result<(), anyhow::Error> { + pub fn reset_database(conn: &mut PoolConnection) -> Result<(), anyhow::Error> { info!("Resetting database ..."); - if drop_all { - drop_all_tables(conn) - .map_err(|e| anyhow!("Encountering error when dropping all tables {e}"))?; - } else { - conn.revert_all_migrations(MIGRATIONS) - .map_err(|e| anyhow!("Error reverting all migrations {e}"))?; - } + conn.revert_all_migrations(MIGRATIONS) + .map_err(|e| anyhow!("Error reverting all migrations {e}"))?; conn.run_migrations(&MIGRATIONS.migrations().unwrap()) .map_err(|e| anyhow!("Failed to run migrations {e}"))?; info!("Reset database complete."); Ok(()) } - fn drop_all_tables(conn: &mut PgConnection) -> Result<(), diesel::result::Error> { - info!("Dropping all tables in the database"); - let table_names: Vec = diesel::dsl::sql::( - " - SELECT tablename FROM pg_tables WHERE schemaname = 'public' - ", - ) - .load(conn)?; - - for table_name in table_names { - let drop_table_query = format!("DROP TABLE IF EXISTS {} CASCADE", table_name); - diesel::sql_query(drop_table_query).execute(conn)?; - } - - // Recreate the __diesel_schema_migrations table - diesel::sql_query( - " - CREATE TABLE __diesel_schema_migrations ( - version VARCHAR(50) PRIMARY KEY, - run_on TIMESTAMP NOT NULL DEFAULT NOW() - ) - ", - ) - .execute(conn)?; - info!("Dropped all tables in the database"); - Ok(()) - } - pub async fn setup( indexer_config: IndexerConfig, registry: Registry, @@ -281,7 +244,7 @@ pub mod setup_postgres { ); e })?; - reset_database(&mut conn, /* drop_all */ true).map_err(|e| { + reset_database(&mut conn).map_err(|e| { let db_err_msg = format!( "Failed resetting database with url: {:?} and error: {:?}", db_url, e @@ -343,7 +306,7 @@ pub mod setup_mysql { use crate::IndexerConfig; use anyhow::anyhow; use diesel::migration::MigrationSource; - use diesel::{MysqlConnection, RunQueryDsl}; + use diesel::MysqlConnection; use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; use prometheus::Registry; use secrecy::ExposeSecret; @@ -351,52 +314,16 @@ pub mod setup_mysql { const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/mysql"); - pub fn reset_database( - conn: &mut PoolConnection, - drop_all: bool, - ) -> Result<(), anyhow::Error> { + pub fn reset_database(conn: &mut PoolConnection) -> Result<(), anyhow::Error> { info!("Resetting database ..."); - if drop_all { - crate::db::setup_mysql::drop_all_tables(conn) - .map_err(|e| anyhow!("Encountering error when dropping all tables {e}"))?; - } else { - conn.revert_all_migrations(MIGRATIONS) - .map_err(|e| anyhow!("Error reverting all migrations {e}"))?; - } + conn.revert_all_migrations(MIGRATIONS) + .map_err(|e| anyhow!("Error reverting all migrations {e}"))?; conn.run_migrations(&MIGRATIONS.migrations().unwrap()) .map_err(|e| anyhow!("Failed to run migrations {e}"))?; info!("Reset database complete."); Ok(()) } - fn drop_all_tables(conn: &mut MysqlConnection) -> Result<(), diesel::result::Error> { - info!("Dropping all tables in the database"); - let table_names: Vec = diesel::dsl::sql::( - " - SELECT TABLE_NAME FROM information_schema.tables WHERE table_schema = DATABASE() - ", - ) - .load(conn)?; - - for table_name in table_names { - let drop_table_query = format!("DROP TABLE IF EXISTS {}", table_name); - diesel::sql_query(drop_table_query).execute(conn)?; - } - - // Recreate the __diesel_schema_migrations table - diesel::sql_query( - " - CREATE TABLE __diesel_schema_migrations ( - version VARCHAR(50) PRIMARY KEY, - run_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP() - ) - ", - ) - .execute(conn)?; - info!("Dropped all tables in the database"); - Ok(()) - } - pub async fn setup( indexer_config: IndexerConfig, registry: Registry, @@ -421,16 +348,14 @@ pub mod setup_mysql { ); e })?; - crate::db::setup_mysql::reset_database(&mut conn, /* drop_all */ true).map_err( - |e| { - let db_err_msg = format!( - "Failed resetting database with url: {:?} and error: {:?}", - db_url, e - ); - error!("{}", db_err_msg); - IndexerError::PostgresResetError(db_err_msg) - }, - )?; + crate::db::setup_mysql::reset_database(&mut conn).map_err(|e| { + let db_err_msg = format!( + "Failed resetting database with url: {:?} and error: {:?}", + db_url, e + ); + error!("{}", db_err_msg); + IndexerError::PostgresResetError(db_err_msg) + })?; info!("Reset MySQL database complete."); } let indexer_metrics = IndexerMetrics::new(®istry); diff --git a/crates/sui-indexer/src/test_utils.rs b/crates/sui-indexer/src/test_utils.rs index dd5c72b485521..486ff1f51c8d2 100644 --- a/crates/sui-indexer/src/test_utils.rs +++ b/crates/sui-indexer/src/test_utils.rs @@ -148,7 +148,7 @@ pub async fn start_test_indexer_impl( } ReaderWriterConfig::Writer { snapshot_config } => { if config.reset_db { - crate::db::reset_database(&mut blocking_pool.get().unwrap(), true).unwrap(); + crate::db::reset_database(&mut blocking_pool.get().unwrap()).unwrap(); } let store_clone = store.clone(); From 8a6959c19113266053adc754a0bc4ed503000e11 Mon Sep 17 00:00:00 2001 From: Patrick Kuo Date: Thu, 15 Aug 2024 19:28:48 +0100 Subject: [PATCH 135/232] [bridge indexer] - bridge indexer unit test (#18973) ## Description added unit test for bridge indexer task creations. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- Cargo.lock | 16 + Cargo.toml | 496 +++++++++--------- crates/sui-bridge-indexer/Cargo.toml | 1 + .../src/eth_bridge_indexer.rs | 11 +- .../sui-bridge-indexer/src/indexer_builder.rs | 429 --------------- crates/sui-bridge-indexer/src/lib.rs | 3 - crates/sui-bridge-indexer/src/main.rs | 4 +- crates/sui-bridge-indexer/src/models.rs | 19 +- .../src/sui_bridge_indexer.rs | 14 +- .../src/sui_checkpoint_ingestion.rs | 38 -- crates/sui-indexer-builder/Cargo.toml | 18 + .../src/indexer_builder.rs | 316 +++++++++++ crates/sui-indexer-builder/src/lib.rs | 35 ++ .../sui-indexer-builder/src/sui_datasource.rs | 146 ++++++ .../tests/indexer_test_utils.rs | 149 ++++++ .../tests/indexer_tests.rs | 156 ++++++ 16 files changed, 1118 insertions(+), 733 deletions(-) delete mode 100644 crates/sui-bridge-indexer/src/indexer_builder.rs delete mode 100644 crates/sui-bridge-indexer/src/sui_checkpoint_ingestion.rs create mode 100644 crates/sui-indexer-builder/Cargo.toml create mode 100644 crates/sui-indexer-builder/src/indexer_builder.rs create mode 100644 crates/sui-indexer-builder/src/lib.rs create mode 100644 crates/sui-indexer-builder/src/sui_datasource.rs create mode 100644 crates/sui-indexer-builder/tests/indexer_test_utils.rs create mode 100644 crates/sui-indexer-builder/tests/indexer_tests.rs diff --git a/Cargo.lock b/Cargo.lock index 13bac90d114fa..c766d515b415e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12796,6 +12796,7 @@ dependencies = [ "sui-bridge", "sui-config", "sui-data-ingestion-core", + "sui-indexer-builder", "sui-json-rpc-types", "sui-sdk 1.32.0", "sui-test-transaction-builder", @@ -13481,6 +13482,21 @@ dependencies = [ "url", ] +[[package]] +name = "sui-indexer-builder" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "mysten-metrics", + "prometheus", + "sui-data-ingestion-core", + "sui-types", + "telemetry-subscribers", + "tokio", + "tracing", +] + [[package]] name = "sui-json" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index b6e34f378a2ed..55f30a7447f6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,199 +2,200 @@ resolver = "2" exclude = [ - "examples/tic-tac-toe/cli", - "external-crates/move/crates/bytecode-interpreter-crypto", - "external-crates/move/crates/bytecode-verifier-libfuzzer", - "external-crates/move/crates/bytecode-verifier-tests", - "external-crates/move/crates/bytecode-verifier-prop-tests", - "external-crates/move/crates/bytecode-verifier-transactional-tests", - "external-crates/move/crates/enum-compat-util", - "external-crates/move/crates/invalid-mutations", - "external-crates/move/crates/language-benchmarks", - "external-crates/move/crates/module-generation", - "external-crates/move/crates/move-abstract-interpreter", - "external-crates/move/crates/move-abstract-stack", - "external-crates/move/crates/move-analyzer", - "external-crates/move/crates/move-binary-format", - "external-crates/move/crates/move-borrow-graph", - "external-crates/move/crates/move-bytecode-source-map", - "external-crates/move/crates/move-bytecode-utils", - "external-crates/move/crates/move-bytecode-verifier", - "external-crates/move/crates/move-bytecode-verifier-meter", - "external-crates/move/crates/move-bytecode-viewer", - "external-crates/move/crates/move-cli", - "external-crates/move/crates/move-command-line-common", - "external-crates/move/crates/move-compiler", - "external-crates/move/crates/move-compiler-transactional-tests", - "external-crates/move/crates/move-core-types", - "external-crates/move/crates/move-coverage", - "external-crates/move/crates/move-disassembler", - "external-crates/move/crates/move-docgen", - "external-crates/move/crates/move-ir-compiler", - "external-crates/move/crates/move-ir-compiler-transactional-tests", - "external-crates/move/crates/move-ir-to-bytecode", - "external-crates/move/crates/move-ir-to-bytecode-syntax", - "external-crates/move/crates/move-ir-types", - "external-crates/move/crates/move-model", - "external-crates/move/crates/move-package", - "external-crates/move/crates/move-proc-macros", - "external-crates/move/crates/move-prover", - "external-crates/move/crates/move-prover-test-utils", - "external-crates/move/crates/move-stackless-bytecode", - "external-crates/move/crates/move-stdlib", - "external-crates/move/crates/move-stdlib-natives", - "external-crates/move/crates/move-symbol-pool", - "external-crates/move/crates/move-transactional-test-runner", - "external-crates/move/crates/move-unit-test", - "external-crates/move/crates/move-vm-config", - "external-crates/move/crates/move-vm-integration-tests", - "external-crates/move/crates/move-vm-profiler", - "external-crates/move/crates/move-vm-runtime", - "external-crates/move/crates/move-vm-test-utils", - "external-crates/move/crates/move-vm-transactional-tests", - "external-crates/move/crates/move-vm-types", - "external-crates/move/crates/serializer-tests", - "external-crates/move/crates/test-generation", - "external-crates/move/move-execution/v0/crates/move-bytecode-verifier", - "external-crates/move/move-execution/v0/crates/move-stdlib-natives", - "external-crates/move/move-execution/v0/crates/move-vm-runtime", - "external-crates/move/move-execution/v1/crates/move-bytecode-verifier", - "external-crates/move/move-execution/v1/crates/move-stdlib-natives", - "external-crates/move/move-execution/v1/crates/move-vm-runtime", - "external-crates/move/move-execution/v2/crates/move-abstract-interpreter", - "external-crates/move/move-execution/v2/crates/move-bytecode-verifier", - "external-crates/move/move-execution/v2/crates/move-stdlib-natives", - "external-crates/move/move-execution/v2/crates/move-vm-runtime", - "sdk/move-bytecode-template", + "examples/tic-tac-toe/cli", + "external-crates/move/crates/bytecode-interpreter-crypto", + "external-crates/move/crates/bytecode-verifier-libfuzzer", + "external-crates/move/crates/bytecode-verifier-tests", + "external-crates/move/crates/bytecode-verifier-prop-tests", + "external-crates/move/crates/bytecode-verifier-transactional-tests", + "external-crates/move/crates/enum-compat-util", + "external-crates/move/crates/invalid-mutations", + "external-crates/move/crates/language-benchmarks", + "external-crates/move/crates/module-generation", + "external-crates/move/crates/move-abstract-interpreter", + "external-crates/move/crates/move-abstract-stack", + "external-crates/move/crates/move-analyzer", + "external-crates/move/crates/move-binary-format", + "external-crates/move/crates/move-borrow-graph", + "external-crates/move/crates/move-bytecode-source-map", + "external-crates/move/crates/move-bytecode-utils", + "external-crates/move/crates/move-bytecode-verifier", + "external-crates/move/crates/move-bytecode-verifier-meter", + "external-crates/move/crates/move-bytecode-viewer", + "external-crates/move/crates/move-cli", + "external-crates/move/crates/move-command-line-common", + "external-crates/move/crates/move-compiler", + "external-crates/move/crates/move-compiler-transactional-tests", + "external-crates/move/crates/move-core-types", + "external-crates/move/crates/move-coverage", + "external-crates/move/crates/move-disassembler", + "external-crates/move/crates/move-docgen", + "external-crates/move/crates/move-ir-compiler", + "external-crates/move/crates/move-ir-compiler-transactional-tests", + "external-crates/move/crates/move-ir-to-bytecode", + "external-crates/move/crates/move-ir-to-bytecode-syntax", + "external-crates/move/crates/move-ir-types", + "external-crates/move/crates/move-model", + "external-crates/move/crates/move-package", + "external-crates/move/crates/move-proc-macros", + "external-crates/move/crates/move-prover", + "external-crates/move/crates/move-prover-test-utils", + "external-crates/move/crates/move-stackless-bytecode", + "external-crates/move/crates/move-stdlib", + "external-crates/move/crates/move-stdlib-natives", + "external-crates/move/crates/move-symbol-pool", + "external-crates/move/crates/move-transactional-test-runner", + "external-crates/move/crates/move-unit-test", + "external-crates/move/crates/move-vm-config", + "external-crates/move/crates/move-vm-integration-tests", + "external-crates/move/crates/move-vm-profiler", + "external-crates/move/crates/move-vm-runtime", + "external-crates/move/crates/move-vm-test-utils", + "external-crates/move/crates/move-vm-transactional-tests", + "external-crates/move/crates/move-vm-types", + "external-crates/move/crates/serializer-tests", + "external-crates/move/crates/test-generation", + "external-crates/move/move-execution/v0/crates/move-bytecode-verifier", + "external-crates/move/move-execution/v0/crates/move-stdlib-natives", + "external-crates/move/move-execution/v0/crates/move-vm-runtime", + "external-crates/move/move-execution/v1/crates/move-bytecode-verifier", + "external-crates/move/move-execution/v1/crates/move-stdlib-natives", + "external-crates/move/move-execution/v1/crates/move-vm-runtime", + "external-crates/move/move-execution/v2/crates/move-abstract-interpreter", + "external-crates/move/move-execution/v2/crates/move-bytecode-verifier", + "external-crates/move/move-execution/v2/crates/move-stdlib-natives", + "external-crates/move/move-execution/v2/crates/move-vm-runtime", + "sdk/move-bytecode-template", ] members = [ - "consensus/config", - "consensus/core", - "crates/anemo-benchmark", - "crates/bin-version", - "crates/mysten-common", - "crates/mysten-metrics", - "crates/mysten-network", - "crates/mysten-service", - "crates/mysten-util-mem", - "crates/mysten-util-mem-derive", - "crates/prometheus-closure-metric", - "crates/shared-crypto", - "crates/simulacrum", - "crates/sui", - "crates/sui-adapter-transactional-tests", - "crates/sui-analytics-indexer", - "crates/sui-analytics-indexer-derive", - "crates/sui-archival", - "crates/sui-authority-aggregation", - "crates/sui-aws-orchestrator", - "crates/sui-benchmark", - "crates/sui-bridge", - "crates/sui-bridge-cli", - "crates/sui-bridge-indexer", - "crates/sui-cluster-test", - "crates/sui-config", - "crates/sui-core", - "crates/sui-cost", - "crates/sui-data-ingestion", - "crates/sui-data-ingestion-core", - "crates/sui-e2e-tests", - "crates/sui-enum-compat-util", - "crates/sui-faucet", - "crates/sui-framework", - "crates/sui-framework-snapshot", - "crates/sui-framework-tests", - "crates/sui-genesis-builder", - "crates/sui-graphql-config", - "crates/sui-graphql-e2e-tests", - "crates/sui-graphql-rpc", - "crates/sui-graphql-rpc-client", - "crates/sui-graphql-rpc-headers", - "crates/sui-indexer", - "crates/sui-json", - "crates/sui-json-rpc", - "crates/sui-json-rpc-api", - "crates/sui-json-rpc-tests", - "crates/sui-json-rpc-types", - "crates/sui-keys", - "crates/sui-light-client", - "crates/sui-macros", - "crates/sui-metric-checker", - "crates/sui-move", - "crates/sui-move-build", - "crates/sui-move-lsp", - "crates/sui-network", - "crates/sui-node", - "crates/sui-open-rpc", - "crates/sui-open-rpc-macros", - "crates/sui-oracle", - "crates/sui-package-management", - "crates/sui-package-resolver", - "crates/sui-proc-macros", - "crates/sui-protocol-config", - "crates/sui-protocol-config-macros", - "crates/sui-proxy", - "crates/sui-replay", - "crates/sui-rest-api", - "crates/sui-rosetta", - "crates/sui-rpc-loadgen", - "crates/sui-sdk", - "crates/sui-security-watchdog", - "crates/sui-simulator", - "crates/sui-single-node-benchmark", - "crates/sui-snapshot", - "crates/sui-source-validation", - "crates/sui-source-validation-service", - "crates/sui-storage", - "crates/sui-surfer", - "crates/sui-swarm", - "crates/sui-swarm-config", - "crates/sui-telemetry", - "crates/sui-test-transaction-builder", - "crates/sui-test-validator", - "crates/sui-tls", - "crates/sui-tool", - "crates/sui-transaction-builder", - "crates/sui-transaction-checks", - "crates/sui-transactional-test-runner", - "crates/sui-types", - "crates/sui-upgrade-compatibility-transactional-tests", - "crates/sui-verifier-transactional-tests", - "crates/suins-indexer", - "crates/suiop-cli", - "crates/telemetry-subscribers", - "crates/test-cluster", - "crates/transaction-fuzzer", - "crates/typed-store", - "crates/typed-store-derive", - "crates/typed-store-error", - "crates/typed-store-workspace-hack", - "crates/x", - "narwhal/config", - "narwhal/crypto", - "narwhal/executor", - "narwhal/network", - "narwhal/node", - "narwhal/primary", - "narwhal/storage", - "narwhal/test-utils", - "narwhal/types", - "narwhal/worker", - "sui-execution", - "sui-execution/cut", - "sui-execution/latest/sui-adapter", - "sui-execution/latest/sui-move-natives", - "sui-execution/latest/sui-verifier", - "sui-execution/v0/sui-adapter", - "sui-execution/v0/sui-move-natives", - "sui-execution/v0/sui-verifier", - "sui-execution/v1/sui-adapter", - "sui-execution/v1/sui-move-natives", - "sui-execution/v1/sui-verifier", - "sui-execution/v2/sui-adapter", - "sui-execution/v2/sui-move-natives", - "sui-execution/v2/sui-verifier", + "consensus/config", + "consensus/core", + "crates/anemo-benchmark", + "crates/bin-version", + "crates/mysten-common", + "crates/mysten-metrics", + "crates/mysten-network", + "crates/mysten-service", + "crates/mysten-util-mem", + "crates/mysten-util-mem-derive", + "crates/prometheus-closure-metric", + "crates/shared-crypto", + "crates/simulacrum", + "crates/sui", + "crates/sui-adapter-transactional-tests", + "crates/sui-analytics-indexer", + "crates/sui-analytics-indexer-derive", + "crates/sui-archival", + "crates/sui-authority-aggregation", + "crates/sui-aws-orchestrator", + "crates/sui-benchmark", + "crates/sui-bridge", + "crates/sui-bridge-cli", + "crates/sui-bridge-indexer", + "crates/sui-cluster-test", + "crates/sui-config", + "crates/sui-core", + "crates/sui-cost", + "crates/sui-data-ingestion", + "crates/sui-data-ingestion-core", + "crates/sui-e2e-tests", + "crates/sui-enum-compat-util", + "crates/sui-faucet", + "crates/sui-framework", + "crates/sui-framework-snapshot", + "crates/sui-framework-tests", + "crates/sui-genesis-builder", + "crates/sui-graphql-config", + "crates/sui-graphql-e2e-tests", + "crates/sui-graphql-rpc", + "crates/sui-graphql-rpc-client", + "crates/sui-graphql-rpc-headers", + "crates/sui-indexer", + "crates/sui-indexer-builder", + "crates/sui-json", + "crates/sui-json-rpc", + "crates/sui-json-rpc-api", + "crates/sui-json-rpc-tests", + "crates/sui-json-rpc-types", + "crates/sui-keys", + "crates/sui-light-client", + "crates/sui-macros", + "crates/sui-metric-checker", + "crates/sui-move", + "crates/sui-move-build", + "crates/sui-move-lsp", + "crates/sui-network", + "crates/sui-node", + "crates/sui-open-rpc", + "crates/sui-open-rpc-macros", + "crates/sui-oracle", + "crates/sui-package-management", + "crates/sui-package-resolver", + "crates/sui-proc-macros", + "crates/sui-protocol-config", + "crates/sui-protocol-config-macros", + "crates/sui-proxy", + "crates/sui-replay", + "crates/sui-rest-api", + "crates/sui-rosetta", + "crates/sui-rpc-loadgen", + "crates/sui-sdk", + "crates/sui-security-watchdog", + "crates/sui-simulator", + "crates/sui-single-node-benchmark", + "crates/sui-snapshot", + "crates/sui-source-validation", + "crates/sui-source-validation-service", + "crates/sui-storage", + "crates/sui-surfer", + "crates/sui-swarm", + "crates/sui-swarm-config", + "crates/sui-telemetry", + "crates/sui-test-transaction-builder", + "crates/sui-test-validator", + "crates/sui-tls", + "crates/sui-tool", + "crates/sui-transaction-builder", + "crates/sui-transaction-checks", + "crates/sui-transactional-test-runner", + "crates/sui-types", + "crates/sui-upgrade-compatibility-transactional-tests", + "crates/sui-verifier-transactional-tests", + "crates/suins-indexer", + "crates/suiop-cli", + "crates/telemetry-subscribers", + "crates/test-cluster", + "crates/transaction-fuzzer", + "crates/typed-store", + "crates/typed-store-derive", + "crates/typed-store-error", + "crates/typed-store-workspace-hack", + "crates/x", + "narwhal/config", + "narwhal/crypto", + "narwhal/executor", + "narwhal/network", + "narwhal/node", + "narwhal/primary", + "narwhal/storage", + "narwhal/test-utils", + "narwhal/types", + "narwhal/worker", + "sui-execution", + "sui-execution/cut", + "sui-execution/latest/sui-adapter", + "sui-execution/latest/sui-move-natives", + "sui-execution/latest/sui-verifier", + "sui-execution/v0/sui-adapter", + "sui-execution/v0/sui-move-natives", + "sui-execution/v0/sui-verifier", + "sui-execution/v1/sui-adapter", + "sui-execution/v1/sui-move-natives", + "sui-execution/v1/sui-verifier", + "sui-execution/v2/sui-adapter", + "sui-execution/v2/sui-move-natives", + "sui-execution/v2/sui-verifier", ] [workspace.package] @@ -255,26 +256,26 @@ 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 = [ - "tokio", - "http1", - "http2", - "json", - "matched-path", - "original-uri", - "form", - "query", - "ws", + "tokio", + "http1", + "http2", + "json", + "matched-path", + "original-uri", + "form", + "query", + "ws", ] } axum-extra = { version = "0.9", features = ["typed-header"] } axum-server = { git = "https://github.com/bmwill/axum-server.git", rev = "f44323e271afdd1365fd0c8b0a4c0bbdf4956cb7", version = "0.6", default-features = false, features = [ - "tls-rustls", + "tls-rustls", ] } backoff = { version = "0.4.0", features = [ - "futures", - "futures-core", - "pin-project-lite", - "tokio", - "tokio_1", + "futures", + "futures-core", + "pin-project-lite", + "tokio", + "tokio_1", ] } base64 = "0.21.2" base64-url = "2" @@ -298,9 +299,9 @@ console-subscriber = "0.2" const-str = "0.5.3" count-min-sketch = "0.1.7" criterion = { version = "0.5.0", features = [ - "async", - "async_tokio", - "html_reports", + "async", + "async_tokio", + "html_reports", ] } crossterm = "0.25.0" csv = "1.2.1" @@ -312,11 +313,11 @@ derive-syn-parse = "0.1.5" derive_builder = "0.12.0" derive_more = "0.99.17" diesel = { version = "2.1.0", features = [ - "chrono", - "r2d2", - "serde_json", - "64-column-tables", - "i-implement-a-third-party-backend-and-opt-into-breaking-changes", + "chrono", + "r2d2", + "serde_json", + "64-column-tables", + "i-implement-a-third-party-backend-and-opt-into-breaking-changes", ] } diesel-derive-enum = { version = "2.0.1" } diesel_migrations = { version = "2.0.0" } @@ -359,11 +360,11 @@ ipnetwork = "0.20.0" itertools = "0.10.5" jemalloc-ctl = "^0.5" jsonrpsee = { git = "https://github.com/wlmyng/jsonrpsee.git", rev = "b1b300784795f6a64d0fcdf8f03081a9bc38bde8", features = [ - "server", - "macros", - "ws-client", - "http-client", - "jsonrpsee-core", + "server", + "macros", + "ws-client", + "http-client", + "jsonrpsee-core", ] } json_to_table = { git = "https://github.com/zhiburt/tabled/", rev = "e449317a1c02eb6b29e409ad6617e5d9eb7b3bd4" } leb128 = "0.2.5" @@ -374,8 +375,8 @@ miette = { version = "7", features = ["fancy"] } mime = "0.3" mockall = "0.11.4" moka = { version = "0.12", default-features = false, features = [ - "sync", - "atomic64", + "sync", + "atomic64", ] } more-asserts = "0.3.1" msim = { git = "https://github.com/MystenLabs/mysten-sim.git", rev = "220f52a15804a768610ac0ae3b8da7de4a5c4d2b", package = "msim" } @@ -401,7 +402,7 @@ prettytable-rs = "0.10.0" proc-macro2 = "1.0.47" prometheus = "0.13.3" prometheus-http-query = { version = "0.8", default_features = false, features = [ - "rustls-tls", + "rustls-tls", ] } prometheus-parse = { git = "https://github.com/asonnino/prometheus-parser.git", rev = "75334db" } proptest = "1.1.0" @@ -416,18 +417,18 @@ rayon = "1.5.3" rcgen = "0.13" regex = "1.7.1" reqwest = { version = "0.12", default_features = false, features = [ - "http2", - "json", - "rustls-tls", + "http2", + "json", + "rustls-tls", ] } roaring = "=0.10.3" ron = "0.8.0" rstest = "0.16.0" rusoto_core = { version = "0.48.0", default_features = false, features = [ - "rustls", + "rustls", ] } rusoto_kms = { version = "0.48.0", default_features = false, features = [ - "rustls", + "rustls", ] } russh = "0.38.0" russh-keys = "0.38.0" @@ -485,32 +486,32 @@ tonic = { version = "0.12", features = ["transport"] } tonic-build = { version = "0.12", features = ["prost", "transport"] } tonic-health = "0.12" tower = { version = "0.4.12", features = [ - "full", - "util", - "timeout", - "load-shed", - "limit", + "full", + "util", + "timeout", + "load-shed", + "limit", ] } tower-http = { version = "0.5", features = [ - "cors", - "full", - "trace", - "set-header", - "propagate-header", + "cors", + "full", + "trace", + "set-header", + "propagate-header", ] } tower-layer = "0.3.2" twox-hash = "1.6.3" tracing = "0.1.37" tracing-appender = "0.2.2" tracing-subscriber = { version = "0.3.15", default-features = false, features = [ - "std", - "smallvec", - "fmt", - "ansi", - "time", - "json", - "registry", - "env-filter", + "std", + "smallvec", + "fmt", + "ansi", + "time", + "json", + "registry", + "env-filter", ] } ttl_cache = "0.5.1" uint = "0.9.4" @@ -519,8 +520,8 @@ ureq = "2.9.1" url = "2.3.1" uuid = { version = "1.1.2", features = ["v4", "fast-rng"] } webpki = { version = "0.102", package = "rustls-webpki", features = [ - "alloc", - "std", + "alloc", + "std", ] } x509-parser = "0.14.0" zstd = "0.12.3" @@ -542,7 +543,7 @@ move-package = { path = "external-crates/move/crates/move-package" } move-unit-test = { path = "external-crates/move/crates/move-unit-test" } move-vm-config = { path = "external-crates/move/crates/move-vm-config" } move-vm-test-utils = { path = "external-crates/move/crates/move-vm-test-utils/", features = [ - "tiered-gas", + "tiered-gas", ] } move-vm-types = { path = "external-crates/move/crates/move-vm-types" } move-vm-profiler = { path = "external-crates/move/crates/move-vm-profiler" } @@ -613,6 +614,7 @@ sui-graphql-rpc-client = { path = "crates/sui-graphql-rpc-client" } sui-graphql-rpc-headers = { path = "crates/sui-graphql-rpc-headers" } sui-genesis-builder = { path = "crates/sui-genesis-builder" } sui-indexer = { path = "crates/sui-indexer" } +sui-indexer-builder = { path = "crates/sui-indexer-builder" } sui-json = { path = "crates/sui-json" } sui-json-rpc = { path = "crates/sui-json-rpc" } sui-json-rpc-api = { path = "crates/sui-json-rpc-api" } diff --git a/crates/sui-bridge-indexer/Cargo.toml b/crates/sui-bridge-indexer/Cargo.toml index 594a8eceba036..7ddb11e1bc5ed 100644 --- a/crates/sui-bridge-indexer/Cargo.toml +++ b/crates/sui-bridge-indexer/Cargo.toml @@ -31,6 +31,7 @@ telemetry-subscribers.workspace = true tracing.workspace = true backoff.workspace = true sui-config.workspace = true +sui-indexer-builder.workspace = true [dev-dependencies] sui-types = { workspace = true, features = ["test-utils"] } diff --git a/crates/sui-bridge-indexer/src/eth_bridge_indexer.rs b/crates/sui-bridge-indexer/src/eth_bridge_indexer.rs index 75577adbf5c8c..f3f951952c6a2 100644 --- a/crates/sui-bridge-indexer/src/eth_bridge_indexer.rs +++ b/crates/sui-bridge-indexer/src/eth_bridge_indexer.rs @@ -18,14 +18,13 @@ use sui_bridge::retry_with_max_elapsed_time; use tokio::task::JoinHandle; use tracing::info; -use mysten_metrics::{metered_channel, spawn_monitored_task}; +use mysten_metrics::spawn_monitored_task; use sui_bridge::abi::{EthBridgeEvent, EthSuiBridgeEvents}; +use crate::metrics::BridgeIndexerMetrics; use sui_bridge::metrics::BridgeMetrics; use sui_bridge::types::{EthEvent, RawEthLog}; - -use crate::indexer_builder::{CheckpointData, DataMapper, Datasource}; -use crate::metrics::BridgeIndexerMetrics; +use sui_indexer_builder::indexer_builder::{DataMapper, DataSender, Datasource}; use crate::{ BridgeDataSource, ProcessedTxnData, TokenTransfer, TokenTransferData, TokenTransferStatus, @@ -59,7 +58,7 @@ impl Datasource for EthSubscriptionDatasource { &self, starting_checkpoint: u64, target_checkpoint: u64, - data_sender: metered_channel::Sender>, + data_sender: DataSender, ) -> Result>, Error> { let filter = Filter::new() .address(self.bridge_address) @@ -160,7 +159,7 @@ impl Datasource for EthSyncDatasource { &self, starting_checkpoint: u64, target_checkpoint: u64, - data_sender: metered_channel::Sender>, + data_sender: DataSender, ) -> Result>, Error> { let client: Arc> = Arc::new( EthClient::::new( diff --git a/crates/sui-bridge-indexer/src/indexer_builder.rs b/crates/sui-bridge-indexer/src/indexer_builder.rs deleted file mode 100644 index ca9f96046bb4e..0000000000000 --- a/crates/sui-bridge-indexer/src/indexer_builder.rs +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use std::cmp::min; -use std::path::PathBuf; -use std::sync::Arc; - -use anyhow::Error; -use async_trait::async_trait; -use tokio::sync::oneshot; -use tokio::sync::oneshot::Sender; -use tokio::task::JoinHandle; -use tracing::info; - -use mysten_metrics::{metered_channel, spawn_monitored_task}; -use sui_data_ingestion_core::{ - DataIngestionMetrics, IndexerExecutor, ProgressStore, ReaderOptions, Worker, WorkerPool, -}; -use sui_types::digests::TransactionDigest; -use sui_types::full_checkpoint_content::{ - CheckpointData as SuiCheckpointData, CheckpointTransaction, -}; -use sui_types::messages_checkpoint::CheckpointSequenceNumber; - -use crate::sui_checkpoint_ingestion::{Task, Tasks}; - -pub type CheckpointData = (u64, Vec); - -pub struct IndexerBuilder { - name: String, - datasource: D, - data_mapper: M, - backfill_strategy: BackfillStrategy, - disable_live_task: bool, -} - -impl IndexerBuilder { - pub fn new(name: &str, datasource: D, data_mapper: M) -> IndexerBuilder { - IndexerBuilder { - name: name.into(), - datasource, - data_mapper, - backfill_strategy: BackfillStrategy::Simple, - disable_live_task: false, - } - } - pub fn build( - self, - start_from_checkpoint: u64, - genesis_checkpoint: u64, - persistent: P, - ) -> Indexer - where - P: Persistent, - { - Indexer { - name: self.name, - storage: persistent, - datasource: self.datasource.into(), - backfill_strategy: self.backfill_strategy, - disable_live_task: self.disable_live_task, - start_from_checkpoint, - data_mapper: self.data_mapper, - genesis_checkpoint, - } - } - - pub fn with_backfill_strategy(mut self, backfill: BackfillStrategy) -> Self { - self.backfill_strategy = backfill; - self - } - - pub fn disable_live_task(mut self) -> Self { - self.disable_live_task = true; - self - } -} - -pub struct Indexer { - name: String, - storage: P, - datasource: Arc, - data_mapper: M, - backfill_strategy: BackfillStrategy, - disable_live_task: bool, - start_from_checkpoint: u64, - genesis_checkpoint: u64, -} - -impl Indexer { - pub async fn start(mut self) -> Result<(), Error> - where - D: Datasource + 'static, - M: DataMapper + 'static, - P: Persistent + 'static, - T: Send, - { - // Update tasks first - let tasks = self.storage.tasks(&self.name)?; - // create checkpoint workers base on backfill config and existing tasks in the db - match tasks.live_task() { - None => { - // if diable_live_task is set, we should not have any live task in the db - if !self.disable_live_task { - // Scenario 1: No task in database, start live task and backfill tasks - self.storage.register_task( - format!("{} - Live", self.name), - self.start_from_checkpoint, - i64::MAX, - )?; - } - - // Create backfill tasks - if self.start_from_checkpoint != self.genesis_checkpoint { - self.create_backfill_tasks(self.genesis_checkpoint)? - } - } - Some(mut live_task) => { - if self.disable_live_task { - // TODO: delete task - // self.storage.delete_task(live_task.task_name.clone())?; - } else if self.start_from_checkpoint > live_task.checkpoint { - // Scenario 2: there are existing tasks in DB and start_from_checkpoint > current checkpoint - // create backfill task to finish at start_from_checkpoint - // update live task to start from start_from_checkpoint and finish at u64::MAX - self.create_backfill_tasks(live_task.checkpoint)?; - live_task.checkpoint = self.start_from_checkpoint; - self.storage.update_task(live_task)?; - } else { - // Scenario 3: start_from_checkpoint < current checkpoint - // ignore start_from_checkpoint, resume all task as it is. - } - } - } - - // get updated tasks from storage and start workers - let updated_tasks = self.storage.tasks(&self.name)?; - // Start latest checkpoint worker - // Tasks are ordered in checkpoint descending order, realtime update task always come first - // tasks won't be empty here, ok to unwrap. - let backfill_tasks; - let live_task_future = if self.disable_live_task { - backfill_tasks = updated_tasks; - None - } else { - let (_live_task, _backfill_tasks) = updated_tasks.split_first().unwrap(); - - backfill_tasks = _backfill_tasks.to_vec(); - let live_task = _live_task; - - Some(self.datasource.start_ingestion_task( - live_task.task_name.clone(), - live_task.checkpoint, - live_task.target_checkpoint, - self.storage.clone(), - self.data_mapper.clone(), - )) - }; - - let backfill_tasks = backfill_tasks.to_vec(); - let storage_clone = self.storage.clone(); - let data_mapper_clone = self.data_mapper.clone(); - let datasource_clone = self.datasource.clone(); - - let handle = spawn_monitored_task!(async { - // Execute task one by one - for backfill_task in backfill_tasks { - datasource_clone - .start_ingestion_task( - backfill_task.task_name.clone(), - backfill_task.checkpoint, - backfill_task.target_checkpoint, - storage_clone.clone(), - data_mapper_clone.clone(), - ) - .await - .expect("Backfill task failed"); - } - }); - - if let Some(live_task_future) = live_task_future { - live_task_future.await?; - } - - tokio::try_join!(handle)?; - - Ok(()) - } - - // Create backfill tasks according to backfill strategy - fn create_backfill_tasks(&mut self, mut current_cp: u64) -> Result<(), Error> - where - P: Persistent, - { - match self.backfill_strategy { - BackfillStrategy::Simple => self.storage.register_task( - format!("{} - backfill - {}", self.name, self.start_from_checkpoint), - current_cp + 1, - self.start_from_checkpoint as i64, - ), - BackfillStrategy::Partitioned { task_size } => { - while current_cp < self.start_from_checkpoint { - let target_cp = min(current_cp + task_size, self.start_from_checkpoint); - self.storage.register_task( - format!("{} - backfill - {target_cp}", self.name), - current_cp + 1, - target_cp as i64, - )?; - current_cp = target_cp; - } - Ok(()) - } - BackfillStrategy::Disabled => Ok(()), - } - } -} - -pub trait Persistent: IndexerProgressStore + Sync + Send + Clone { - fn write(&self, data: Vec) -> Result<(), Error>; -} - -#[async_trait] -pub trait IndexerProgressStore: Send { - async fn load_progress(&self, task_name: String) -> anyhow::Result; - async fn save_progress( - &mut self, - task_name: String, - checkpoint_number: u64, - ) -> anyhow::Result<()>; - - fn tasks(&self, task_prefix: &str) -> Result, Error>; - - fn register_task( - &mut self, - task_name: String, - checkpoint: u64, - target_checkpoint: i64, - ) -> Result<(), anyhow::Error>; - - fn update_task(&mut self, task: Task) -> Result<(), Error>; -} - -#[async_trait] -pub trait Datasource: Sync + Send { - async fn start_ingestion_task( - &self, - task_name: String, - starting_checkpoint: u64, - target_checkpoint: u64, - mut storage: P, - data_mapper: M, - ) -> Result<(), Error> - where - M: DataMapper, - P: Persistent, - { - // todo: add metrics for number of tasks - let (data_sender, mut data_channel) = metered_channel::channel( - 1000, - &mysten_metrics::get_metrics() - .unwrap() - .channel_inflight - .with_label_values(&[&task_name]), - ); - let join_handle = self - .start_data_retrieval(starting_checkpoint, target_checkpoint, data_sender) - .await?; - - while let Some((block_number, data)) = data_channel.recv().await { - if !data.is_empty() { - let processed_data = data.into_iter().try_fold(vec![], |mut result, d| { - result.append(&mut data_mapper.map(d)?); - Ok::, Error>(result) - })?; - // TODO: we might be able to write data and progress in a single transaction. - storage.write(processed_data)?; - } - storage - .save_progress(task_name.clone(), block_number) - .await?; - } - join_handle.abort(); - join_handle.await? - } - - async fn start_data_retrieval( - &self, - starting_checkpoint: u64, - target_checkpoint: u64, - data_sender: metered_channel::Sender>, - ) -> Result>, Error>; -} - -pub struct SuiCheckpointDatasource { - remote_store_url: String, - concurrency: usize, - checkpoint_path: PathBuf, - metrics: DataIngestionMetrics, -} -impl SuiCheckpointDatasource { - pub fn new( - remote_store_url: String, - concurrency: usize, - checkpoint_path: PathBuf, - metrics: DataIngestionMetrics, - ) -> Self { - SuiCheckpointDatasource { - remote_store_url, - concurrency, - checkpoint_path, - metrics, - } - } -} - -#[async_trait] -impl Datasource for SuiCheckpointDatasource { - async fn start_data_retrieval( - &self, - starting_checkpoint: u64, - target_checkpoint: u64, - data_sender: metered_channel::Sender>, - ) -> Result>, Error> { - let (exit_sender, exit_receiver) = oneshot::channel(); - let progress_store = PerTaskInMemProgressStore { - current_checkpoint: starting_checkpoint, - exit_checkpoint: target_checkpoint, - exit_sender: Some(exit_sender), - }; - let mut executor = IndexerExecutor::new(progress_store, 1, self.metrics.clone()); - let worker = IndexerWorker::new(data_sender); - let worker_pool = WorkerPool::new( - worker, - TransactionDigest::random().to_string(), - self.concurrency, - ); - executor.register(worker_pool).await?; - let checkpoint_path = self.checkpoint_path.clone(); - let remote_store_url = self.remote_store_url.clone(); - Ok(spawn_monitored_task!(async { - executor - .run( - checkpoint_path, - Some(remote_store_url), - vec![], // optional remote store access options - ReaderOptions::default(), - exit_receiver, - ) - .await?; - Ok(()) - })) - } -} - -pub enum BackfillStrategy { - Simple, - Partitioned { task_size: u64 }, - Disabled, -} - -pub trait DataMapper: Sync + Send + Clone { - fn map(&self, data: T) -> Result, anyhow::Error>; -} - -struct PerTaskInMemProgressStore { - pub current_checkpoint: u64, - pub exit_checkpoint: u64, - pub exit_sender: Option>, -} - -#[async_trait] -impl ProgressStore for PerTaskInMemProgressStore { - async fn load( - &mut self, - _task_name: String, - ) -> Result { - Ok(self.current_checkpoint) - } - - async fn save( - &mut self, - _task_name: String, - checkpoint_number: CheckpointSequenceNumber, - ) -> anyhow::Result<()> { - if checkpoint_number >= self.exit_checkpoint { - if let Some(sender) = self.exit_sender.take() { - let _ = sender.send(()); - } - } - self.current_checkpoint = checkpoint_number; - Ok(()) - } -} - -pub struct IndexerWorker { - data_sender: metered_channel::Sender<(u64, Vec)>, -} - -impl IndexerWorker { - pub fn new(data_sender: metered_channel::Sender<(u64, Vec)>) -> Self { - Self { data_sender } - } -} - -pub type CheckpointTxnData = (CheckpointTransaction, u64, u64); - -#[async_trait] -impl Worker for IndexerWorker { - async fn process_checkpoint(&self, checkpoint: SuiCheckpointData) -> anyhow::Result<()> { - info!( - "Received checkpoint [{}] {}: {}", - checkpoint.checkpoint_summary.epoch, - checkpoint.checkpoint_summary.sequence_number, - checkpoint.transactions.len(), - ); - let checkpoint_num = checkpoint.checkpoint_summary.sequence_number; - let timestamp_ms = checkpoint.checkpoint_summary.timestamp_ms; - - let transactions = checkpoint - .transactions - .into_iter() - .map(|tx| (tx, checkpoint_num, timestamp_ms)) - .collect(); - Ok(self - .data_sender - .send((checkpoint_num, transactions)) - .await?) - } -} diff --git a/crates/sui-bridge-indexer/src/lib.rs b/crates/sui-bridge-indexer/src/lib.rs index 3a4320e9b316c..afd4b461303f8 100644 --- a/crates/sui-bridge-indexer/src/lib.rs +++ b/crates/sui-bridge-indexer/src/lib.rs @@ -13,13 +13,10 @@ pub mod metrics; pub mod models; pub mod postgres_manager; pub mod schema; -pub mod sui_checkpoint_ingestion; pub mod sui_transaction_handler; pub mod sui_transaction_queries; pub mod types; -pub mod indexer_builder; - pub mod eth_bridge_indexer; pub mod sui_bridge_indexer; diff --git a/crates/sui-bridge-indexer/src/main.rs b/crates/sui-bridge-indexer/src/main.rs index 889383e7f0239..25b845ae4abaf 100644 --- a/crates/sui-bridge-indexer/src/main.rs +++ b/crates/sui-bridge-indexer/src/main.rs @@ -12,7 +12,6 @@ use ethers::providers::Middleware; use ethers::providers::Provider; use sui_bridge_indexer::eth_bridge_indexer::EthSubscriptionDatasource; use sui_bridge_indexer::eth_bridge_indexer::EthSyncDatasource; -use sui_bridge_indexer::indexer_builder::BackfillStrategy; use tokio::task::JoinHandle; use tracing::info; @@ -22,7 +21,6 @@ use mysten_metrics::start_prometheus_server; use sui_bridge::metrics::BridgeMetrics; use sui_bridge_indexer::config::IndexerConfig; use sui_bridge_indexer::eth_bridge_indexer::EthDataMapper; -use sui_bridge_indexer::indexer_builder::{IndexerBuilder, SuiCheckpointDatasource}; use sui_bridge_indexer::metrics::BridgeIndexerMetrics; use sui_bridge_indexer::postgres_manager::{get_connection_pool, read_sui_progress_store}; use sui_bridge_indexer::sui_bridge_indexer::{PgBridgePersistent, SuiBridgeDataMapper}; @@ -30,6 +28,8 @@ use sui_bridge_indexer::sui_transaction_handler::handle_sui_transactions_loop; use sui_bridge_indexer::sui_transaction_queries::start_sui_tx_polling_task; use sui_config::Config; use sui_data_ingestion_core::DataIngestionMetrics; +use sui_indexer_builder::indexer_builder::{BackfillStrategy, IndexerBuilder}; +use sui_indexer_builder::sui_datasource::SuiCheckpointDatasource; use sui_sdk::SuiClientBuilder; #[derive(Parser, Clone, Debug)] diff --git a/crates/sui-bridge-indexer/src/models.rs b/crates/sui-bridge-indexer/src/models.rs index e005dca186d6f..90435cd60c154 100644 --- a/crates/sui-bridge-indexer/src/models.rs +++ b/crates/sui-bridge-indexer/src/models.rs @@ -1,11 +1,14 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use diesel::data_types::PgTimestamp; +use diesel::{Identifiable, Insertable, Queryable, Selectable}; + +use sui_indexer_builder::Task; + use crate::schema::{ progress_store, sui_error_transactions, sui_progress_store, token_transfer, token_transfer_data, }; -use diesel::data_types::PgTimestamp; -use diesel::{Identifiable, Insertable, Queryable, Selectable}; #[derive(Queryable, Selectable, Insertable, Identifiable, Debug)] #[diesel(table_name = progress_store, primary_key(task_name))] @@ -16,6 +19,18 @@ pub struct ProgressStore { pub timestamp: Option, } +impl From for Task { + fn from(value: ProgressStore) -> Self { + Self { + task_name: value.task_name, + checkpoint: value.checkpoint as u64, + target_checkpoint: value.target_checkpoint as u64, + // Ok to unwrap, timestamp is defaulted to now() in database + timestamp: value.timestamp.expect("Timestamp not set").0 as u64, + } + } +} + #[derive(Queryable, Selectable, Insertable, Identifiable, Debug)] #[diesel(table_name = sui_progress_store, primary_key(txn_digest))] pub struct SuiProgressStore { diff --git a/crates/sui-bridge-indexer/src/sui_bridge_indexer.rs b/crates/sui-bridge-indexer/src/sui_bridge_indexer.rs index 41f219beb18f1..453be19c7854a 100644 --- a/crates/sui-bridge-indexer/src/sui_bridge_indexer.rs +++ b/crates/sui-bridge-indexer/src/sui_bridge_indexer.rs @@ -11,18 +11,19 @@ use tracing::info; use sui_bridge::events::{ MoveTokenDepositedEvent, MoveTokenTransferApproved, MoveTokenTransferClaimed, }; +use sui_indexer_builder::indexer_builder::{DataMapper, IndexerProgressStore, Persistent}; +use sui_indexer_builder::sui_datasource::CheckpointTxnData; +use sui_indexer_builder::Task; use sui_types::effects::TransactionEffectsAPI; use sui_types::event::Event; use sui_types::execution_status::ExecutionStatus; use sui_types::full_checkpoint_content::CheckpointTransaction; use sui_types::{BRIDGE_ADDRESS, SUI_BRIDGE_OBJECT_ID}; -use crate::indexer_builder::{CheckpointTxnData, DataMapper, IndexerProgressStore, Persistent}; use crate::metrics::BridgeIndexerMetrics; use crate::postgres_manager::PgPool; use crate::schema::progress_store::{columns, dsl}; use crate::schema::{sui_error_transactions, token_transfer, token_transfer_data}; -use crate::sui_checkpoint_ingestion::Task; use crate::{ models, schema, BridgeDataSource, ProcessedTxnData, SuiTxnError, TokenTransfer, TokenTransferData, TokenTransferStatus, @@ -41,8 +42,9 @@ impl PgBridgePersistent { } // TODO: this is shared between SUI and ETH, move to different file. +#[async_trait] impl Persistent for PgBridgePersistent { - fn write(&self, data: Vec) -> Result<(), Error> { + async fn write(&self, data: Vec) -> Result<(), Error> { if data.is_empty() { return Ok(()); } @@ -115,7 +117,7 @@ impl IndexerProgressStore for PgBridgePersistent { Ok(()) } - fn tasks(&self, prefix: &str) -> Result, anyhow::Error> { + async fn tasks(&self, prefix: &str) -> Result, anyhow::Error> { let mut conn = self.pool.get()?; // get all unfinished tasks let cp: Vec = dsl::progress_store @@ -127,7 +129,7 @@ impl IndexerProgressStore for PgBridgePersistent { Ok(cp.into_iter().map(|d| d.into()).collect()) } - fn register_task( + async fn register_task( &mut self, task_name: String, checkpoint: u64, @@ -146,7 +148,7 @@ impl IndexerProgressStore for PgBridgePersistent { Ok(()) } - fn update_task(&mut self, task: Task) -> Result<(), anyhow::Error> { + async fn update_task(&mut self, task: Task) -> Result<(), anyhow::Error> { let mut conn = self.pool.get()?; diesel::update(dsl::progress_store.filter(columns::task_name.eq(task.task_name))) .set(( diff --git a/crates/sui-bridge-indexer/src/sui_checkpoint_ingestion.rs b/crates/sui-bridge-indexer/src/sui_checkpoint_ingestion.rs deleted file mode 100644 index a0cf1a6b14201..0000000000000 --- a/crates/sui-bridge-indexer/src/sui_checkpoint_ingestion.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crate::models; - -#[derive(Clone)] -pub struct Task { - pub task_name: String, - pub checkpoint: u64, - pub target_checkpoint: u64, - pub timestamp: u64, -} - -impl From for Task { - fn from(value: models::ProgressStore) -> Self { - Self { - task_name: value.task_name, - checkpoint: value.checkpoint as u64, - target_checkpoint: value.target_checkpoint as u64, - // Ok to unwrap, timestamp is defaulted to now() in database - timestamp: value.timestamp.expect("Timestamp not set").0 as u64, - } - } -} - -pub trait Tasks { - fn live_task(&self) -> Option; -} - -impl Tasks for Vec { - fn live_task(&self) -> Option { - self.iter().fold(None, |result, other_task| match &result { - Some(task) if task.checkpoint < other_task.checkpoint => Some(other_task.clone()), - None => Some(other_task.clone()), - _ => result, - }) - } -} diff --git a/crates/sui-indexer-builder/Cargo.toml b/crates/sui-indexer-builder/Cargo.toml new file mode 100644 index 0000000000000..4ae5164d862ce --- /dev/null +++ b/crates/sui-indexer-builder/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "sui-indexer-builder" +version = "0.1.0" +authors = ["Mysten Labs "] +license = "Apache-2.0" +publish = false +edition = "2021" + +[dependencies] +anyhow.workspace = true +tokio = { workspace = true, features = ["full"] } +async-trait.workspace = true +mysten-metrics.workspace = true +sui-types.workspace = true +sui-data-ingestion-core.workspace = true +tracing.workspace = true +prometheus.workspace = true +telemetry-subscribers.workspace = true \ No newline at end of file diff --git a/crates/sui-indexer-builder/src/indexer_builder.rs b/crates/sui-indexer-builder/src/indexer_builder.rs new file mode 100644 index 0000000000000..43006754da381 --- /dev/null +++ b/crates/sui-indexer-builder/src/indexer_builder.rs @@ -0,0 +1,316 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::cmp::{max, min}; +use std::sync::Arc; + +use anyhow::Error; +use async_trait::async_trait; +use tokio::task::JoinHandle; + +use mysten_metrics::{metered_channel, spawn_monitored_task}; + +use crate::{Task, Tasks}; + +type CheckpointData = (u64, Vec); +pub type DataSender = metered_channel::Sender>; + +pub struct IndexerBuilder { + name: String, + datasource: D, + data_mapper: M, + backfill_strategy: BackfillStrategy, + disable_live_task: bool, +} + +impl IndexerBuilder { + pub fn new(name: &str, datasource: D, data_mapper: M) -> IndexerBuilder { + IndexerBuilder { + name: name.into(), + datasource, + data_mapper, + backfill_strategy: BackfillStrategy::Simple, + disable_live_task: false, + } + } + pub fn build( + self, + start_from_checkpoint: u64, + genesis_checkpoint: u64, + persistent: P, + ) -> Indexer + where + P: Persistent, + { + Indexer { + name: self.name, + storage: persistent, + datasource: self.datasource.into(), + backfill_strategy: self.backfill_strategy, + disable_live_task: self.disable_live_task, + start_from_checkpoint, + data_mapper: self.data_mapper, + genesis_checkpoint, + } + } + + pub fn with_backfill_strategy(mut self, backfill: BackfillStrategy) -> Self { + self.backfill_strategy = backfill; + self + } + + pub fn disable_live_task(mut self) -> Self { + self.disable_live_task = true; + self + } +} + +pub struct Indexer { + name: String, + storage: P, + datasource: Arc, + data_mapper: M, + backfill_strategy: BackfillStrategy, + disable_live_task: bool, + start_from_checkpoint: u64, + genesis_checkpoint: u64, +} + +impl Indexer { + pub async fn start(mut self) -> Result<(), Error> + where + D: Datasource + 'static, + M: DataMapper + 'static, + P: Persistent + 'static, + T: Send, + { + // Update tasks first + self.update_tasks().await?; + // get updated tasks from storage and start workers + let updated_tasks = self.storage.tasks(&self.name).await?; + // Start latest checkpoint worker + // Tasks are ordered in checkpoint descending order, realtime update task always come first + // tasks won't be empty here, ok to unwrap. + let live_task_future = match updated_tasks.live_task() { + Some(live_task) if !self.disable_live_task => { + let live_task_future = self.datasource.start_ingestion_task( + live_task.task_name.clone(), + live_task.checkpoint, + live_task.target_checkpoint, + self.storage.clone(), + self.data_mapper.clone(), + ); + Some(live_task_future) + } + _ => None, + }; + + let backfill_tasks = updated_tasks.backfill_tasks(); + let storage_clone = self.storage.clone(); + let data_mapper_clone = self.data_mapper.clone(); + let datasource_clone = self.datasource.clone(); + + let handle = spawn_monitored_task!(async { + // Execute task one by one + for backfill_task in backfill_tasks { + if backfill_task.checkpoint < backfill_task.target_checkpoint { + datasource_clone + .start_ingestion_task( + backfill_task.task_name.clone(), + backfill_task.checkpoint, + backfill_task.target_checkpoint, + storage_clone.clone(), + data_mapper_clone.clone(), + ) + .await + .expect("Backfill task failed"); + } + } + }); + + if let Some(live_task_future) = live_task_future { + live_task_future.await?; + } + + tokio::try_join!(handle)?; + + Ok(()) + } + + async fn update_tasks(&mut self) -> Result<(), Error> + where + P: Persistent, + { + let tasks = self.storage.tasks(&self.name).await?; + let backfill_tasks = tasks.backfill_tasks(); + let latest_task = backfill_tasks.first(); + + // 1, create and update live task if needed + if !self.disable_live_task { + let from_checkpoint = max( + self.start_from_checkpoint, + latest_task + .map(|t| t.target_checkpoint + 1) + .unwrap_or_default(), + ); + + match tasks.live_task() { + None => { + self.storage + .register_task(format!("{} - Live", self.name), from_checkpoint, i64::MAX) + .await?; + } + Some(mut live_task) => { + if self.start_from_checkpoint > live_task.checkpoint { + live_task.checkpoint = self.start_from_checkpoint; + self.storage.update_task(live_task).await?; + } + } + } + } + + // 2, create backfill tasks base on task config and existing tasks in the db + match latest_task { + None => { + // No task in database, create backfill tasks from genesis to `start_from_checkpoint` + if self.start_from_checkpoint != self.genesis_checkpoint { + self.create_backfill_tasks(self.genesis_checkpoint, self.start_from_checkpoint) + .await? + } + } + Some(latest_task) => { + if latest_task.target_checkpoint < self.start_from_checkpoint { + self.create_backfill_tasks( + latest_task.target_checkpoint + 1, + self.start_from_checkpoint, + ) + .await?; + } + } + } + Ok(()) + } + + // Create backfill tasks according to backfill strategy + async fn create_backfill_tasks(&mut self, mut from_cp: u64, to_cp: u64) -> Result<(), Error> + where + P: Persistent, + { + match self.backfill_strategy { + BackfillStrategy::Simple => { + self.storage + .register_task( + format!("{} - backfill - {}", self.name, to_cp), + from_cp, + self.start_from_checkpoint as i64 - 1, + ) + .await + } + BackfillStrategy::Partitioned { task_size } => { + while from_cp < self.start_from_checkpoint { + let target_cp = min(from_cp + task_size, to_cp) - 1; + self.storage + .register_task( + format!("{} - backfill - {target_cp}", self.name), + from_cp, + target_cp as i64, + ) + .await?; + from_cp = target_cp + 1; + } + Ok(()) + } + BackfillStrategy::Disabled => Ok(()), + } + } +} + +#[async_trait] +pub trait Persistent: IndexerProgressStore + Sync + Send + Clone { + async fn write(&self, data: Vec) -> Result<(), Error>; +} + +#[async_trait] +pub trait IndexerProgressStore: Send { + async fn load_progress(&self, task_name: String) -> anyhow::Result; + async fn save_progress( + &mut self, + task_name: String, + checkpoint_number: u64, + ) -> anyhow::Result<()>; + + async fn tasks(&self, task_prefix: &str) -> Result, Error>; + + async fn register_task( + &mut self, + task_name: String, + checkpoint: u64, + target_checkpoint: i64, + ) -> Result<(), anyhow::Error>; + + async fn update_task(&mut self, task: Task) -> Result<(), Error>; +} + +#[async_trait] +pub trait Datasource: Sync + Send { + async fn start_ingestion_task( + &self, + task_name: String, + starting_checkpoint: u64, + target_checkpoint: u64, + mut storage: P, + data_mapper: M, + ) -> Result<(), Error> + where + M: DataMapper, + P: Persistent, + { + // todo: add metrics for number of tasks + let (data_sender, mut data_channel) = metered_channel::channel( + 1000, + &mysten_metrics::get_metrics() + .unwrap() + .channel_inflight + .with_label_values(&[&task_name]), + ); + let join_handle = self + .start_data_retrieval(starting_checkpoint, target_checkpoint, data_sender) + .await?; + + while let Some((block_number, data)) = data_channel.recv().await { + if block_number > target_checkpoint { + break; + } + if !data.is_empty() { + let processed_data = data.into_iter().try_fold(vec![], |mut result, d| { + result.append(&mut data_mapper.map(d)?); + Ok::, Error>(result) + })?; + // TODO: we might be able to write data and progress in a single transaction. + storage.write(processed_data).await?; + } + storage + .save_progress(task_name.clone(), block_number) + .await?; + } + join_handle.abort(); + join_handle.await? + } + + async fn start_data_retrieval( + &self, + starting_checkpoint: u64, + target_checkpoint: u64, + data_sender: DataSender, + ) -> Result>, Error>; +} + +pub enum BackfillStrategy { + Simple, + Partitioned { task_size: u64 }, + Disabled, +} + +pub trait DataMapper: Sync + Send + Clone { + fn map(&self, data: T) -> Result, anyhow::Error>; +} diff --git a/crates/sui-indexer-builder/src/lib.rs b/crates/sui-indexer-builder/src/lib.rs new file mode 100644 index 0000000000000..f4cb0d4d32eda --- /dev/null +++ b/crates/sui-indexer-builder/src/lib.rs @@ -0,0 +1,35 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +pub mod indexer_builder; +pub mod sui_datasource; + +#[derive(Clone, Debug)] +pub struct Task { + pub task_name: String, + pub checkpoint: u64, + pub target_checkpoint: u64, + pub timestamp: u64, +} + +pub trait Tasks { + fn live_task(&self) -> Option; + + fn backfill_tasks(&self) -> Vec; +} + +impl Tasks for Vec { + fn live_task(&self) -> Option { + // TODO: Change the schema to record live task properly. + self.iter() + .find(|t| t.target_checkpoint == i64::MAX as u64) + .cloned() + } + + fn backfill_tasks(&self) -> Vec { + self.iter() + .filter(|t| t.target_checkpoint != i64::MAX as u64) + .cloned() + .collect() + } +} diff --git a/crates/sui-indexer-builder/src/sui_datasource.rs b/crates/sui-indexer-builder/src/sui_datasource.rs new file mode 100644 index 0000000000000..388308bf10c4a --- /dev/null +++ b/crates/sui-indexer-builder/src/sui_datasource.rs @@ -0,0 +1,146 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::indexer_builder::{DataSender, Datasource}; +use anyhow::Error; +use async_trait::async_trait; +use mysten_metrics::{metered_channel, spawn_monitored_task}; +use std::path::PathBuf; +use sui_data_ingestion_core::{ + DataIngestionMetrics, IndexerExecutor, ProgressStore, ReaderOptions, Worker, WorkerPool, +}; +use sui_types::base_types::TransactionDigest; +use sui_types::full_checkpoint_content::CheckpointData as SuiCheckpointData; +use sui_types::full_checkpoint_content::CheckpointTransaction; +use sui_types::messages_checkpoint::CheckpointSequenceNumber; +use tokio::sync::oneshot; +use tokio::sync::oneshot::Sender; +use tokio::task::JoinHandle; +use tracing::info; + +pub struct SuiCheckpointDatasource { + remote_store_url: String, + concurrency: usize, + checkpoint_path: PathBuf, + metrics: DataIngestionMetrics, +} +impl SuiCheckpointDatasource { + pub fn new( + remote_store_url: String, + concurrency: usize, + checkpoint_path: PathBuf, + metrics: DataIngestionMetrics, + ) -> Self { + SuiCheckpointDatasource { + remote_store_url, + concurrency, + checkpoint_path, + metrics, + } + } +} + +#[async_trait] +impl Datasource for SuiCheckpointDatasource { + async fn start_data_retrieval( + &self, + starting_checkpoint: u64, + target_checkpoint: u64, + data_sender: DataSender, + ) -> Result>, Error> { + let (exit_sender, exit_receiver) = oneshot::channel(); + let progress_store = PerTaskInMemProgressStore { + current_checkpoint: starting_checkpoint, + exit_checkpoint: target_checkpoint, + exit_sender: Some(exit_sender), + }; + let mut executor = IndexerExecutor::new(progress_store, 1, self.metrics.clone()); + let worker = IndexerWorker::new(data_sender); + let worker_pool = WorkerPool::new( + worker, + TransactionDigest::random().to_string(), + self.concurrency, + ); + executor.register(worker_pool).await?; + let checkpoint_path = self.checkpoint_path.clone(); + let remote_store_url = self.remote_store_url.clone(); + Ok(spawn_monitored_task!(async { + executor + .run( + checkpoint_path, + Some(remote_store_url), + vec![], // optional remote store access options + ReaderOptions::default(), + exit_receiver, + ) + .await?; + Ok(()) + })) + } +} + +struct PerTaskInMemProgressStore { + pub current_checkpoint: u64, + pub exit_checkpoint: u64, + pub exit_sender: Option>, +} + +#[async_trait] +impl ProgressStore for PerTaskInMemProgressStore { + async fn load( + &mut self, + _task_name: String, + ) -> Result { + Ok(self.current_checkpoint) + } + + async fn save( + &mut self, + _task_name: String, + checkpoint_number: CheckpointSequenceNumber, + ) -> anyhow::Result<()> { + if checkpoint_number >= self.exit_checkpoint { + if let Some(sender) = self.exit_sender.take() { + let _ = sender.send(()); + } + } + self.current_checkpoint = checkpoint_number; + Ok(()) + } +} + +pub struct IndexerWorker { + data_sender: metered_channel::Sender<(u64, Vec)>, +} + +impl IndexerWorker { + pub fn new(data_sender: metered_channel::Sender<(u64, Vec)>) -> Self { + Self { data_sender } + } +} + +pub type CheckpointTxnData = (CheckpointTransaction, u64, u64); + +#[async_trait] +impl Worker for IndexerWorker { + async fn process_checkpoint(&self, checkpoint: SuiCheckpointData) -> anyhow::Result<()> { + info!( + "Received checkpoint [{}] {}: {}", + checkpoint.checkpoint_summary.epoch, + checkpoint.checkpoint_summary.sequence_number, + checkpoint.transactions.len(), + ); + let checkpoint_num = checkpoint.checkpoint_summary.sequence_number; + let timestamp_ms = checkpoint.checkpoint_summary.timestamp_ms; + + let transactions = checkpoint + .transactions + .into_iter() + .map(|tx| (tx, checkpoint_num, timestamp_ms)) + .collect(); + Ok(self + .data_sender + .send((checkpoint_num, transactions)) + .await?) + } +} diff --git a/crates/sui-indexer-builder/tests/indexer_test_utils.rs b/crates/sui-indexer-builder/tests/indexer_test_utils.rs new file mode 100644 index 0000000000000..4b9fed4724407 --- /dev/null +++ b/crates/sui-indexer-builder/tests/indexer_test_utils.rs @@ -0,0 +1,149 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::HashMap; +use std::sync::Arc; +use std::time::{SystemTime, UNIX_EPOCH}; + +use anyhow::Error; +use async_trait::async_trait; +use tokio::sync::Mutex; +use tokio::task::JoinHandle; + +use mysten_metrics::spawn_monitored_task; + +use sui_indexer_builder::indexer_builder::{ + DataMapper, DataSender, Datasource, IndexerProgressStore, Persistent, +}; +use sui_indexer_builder::Task; + +pub struct TestDatasource { + pub data: Vec, +} + +#[async_trait] +impl Datasource for TestDatasource +where + T: Send + Sync + Clone + 'static, +{ + async fn start_data_retrieval( + &self, + starting_checkpoint: u64, + _target_checkpoint: u64, + data_sender: DataSender, + ) -> Result>, Error> { + let data_clone = self.data.clone(); + + Ok(spawn_monitored_task!(async { + let mut cp = starting_checkpoint; + while cp < data_clone.len() as u64 { + data_sender + .send((cp, vec![data_clone[cp as usize].clone()])) + .await?; + cp += 1; + } + Ok(()) + })) + } +} + +#[derive(Clone, Debug, Default)] +pub struct InMemoryPersistent { + pub progress_store: Arc>>, + pub data: Arc>>, +} + +impl InMemoryPersistent { + pub fn new() -> Self { + InMemoryPersistent { + progress_store: Default::default(), + data: Arc::new(Mutex::new(vec![])), + } + } +} + +#[async_trait] +impl IndexerProgressStore for InMemoryPersistent { + async fn load_progress(&self, task_name: String) -> anyhow::Result { + Ok(self + .progress_store + .lock() + .await + .get(&task_name) + .unwrap() + .checkpoint) + } + + async fn save_progress( + &mut self, + task_name: String, + checkpoint_number: u64, + ) -> anyhow::Result<()> { + self.progress_store + .lock() + .await + .get_mut(&task_name) + .unwrap() + .checkpoint = checkpoint_number; + Ok(()) + } + + async fn tasks(&self, task_prefix: &str) -> Result, Error> { + let mut tasks = self + .progress_store + .lock() + .await + .values() + .filter(|task| task.task_name.starts_with(task_prefix)) + .cloned() + .collect::>(); + tasks.sort_by(|t1, t2| t2.checkpoint.cmp(&t1.checkpoint)); + Ok(tasks) + } + + async fn register_task( + &mut self, + task_name: String, + checkpoint: u64, + target_checkpoint: i64, + ) -> Result<(), Error> { + self.progress_store.lock().await.insert( + task_name.clone(), + Task { + task_name, + checkpoint, + target_checkpoint: target_checkpoint as u64, + timestamp: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() as u64, + }, + ); + Ok(()) + } + + async fn update_task(&mut self, task: Task) -> Result<(), Error> { + self.progress_store + .lock() + .await + .insert(task.task_name.clone(), task); + Ok(()) + } +} + +#[async_trait] +impl Persistent for InMemoryPersistent { + async fn write(&self, data: Vec) -> Result<(), Error> { + self.data.lock().await.append(&mut data.clone()); + Ok(()) + } +} + +#[derive(Clone)] +pub struct NoopDataMapper; + +impl DataMapper for NoopDataMapper { + fn map(&self, data: T) -> Result, Error> { + Ok(vec![data]) + } +} diff --git a/crates/sui-indexer-builder/tests/indexer_tests.rs b/crates/sui-indexer-builder/tests/indexer_tests.rs new file mode 100644 index 0000000000000..03db730de3392 --- /dev/null +++ b/crates/sui-indexer-builder/tests/indexer_tests.rs @@ -0,0 +1,156 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::indexer_test_utils::{InMemoryPersistent, NoopDataMapper, TestDatasource}; +use prometheus::Registry; +use sui_indexer_builder::indexer_builder::{ + BackfillStrategy, IndexerBuilder, IndexerProgressStore, +}; +use sui_indexer_builder::Task; + +mod indexer_test_utils; + +#[tokio::test] +async fn indexer_simple_backfill_task_test() { + telemetry_subscribers::init_for_testing(); + let registry = Registry::new(); + mysten_metrics::init_metrics(®istry); + + let data = (0..=10u64).collect::>(); + let datasource = TestDatasource { data: data.clone() }; + let persistent = InMemoryPersistent::new(); + let indexer = IndexerBuilder::new("test_indexer", datasource, NoopDataMapper).build( + 5, + 0, + persistent.clone(), + ); + + indexer.start().await.unwrap(); + + // it should have 2 task created for the indexer - a live task and a backfill task + let tasks = persistent.tasks("test_indexer").await.unwrap(); + assert_eq!(2, tasks.len()); + // the tasks should be ordered by checkpoint number, + // the first one will be the live task and second one will be the backfill + assert_eq!(10, tasks.first().unwrap().checkpoint); + assert_eq!(i64::MAX as u64, tasks.first().unwrap().target_checkpoint); + assert_eq!(4, tasks.last().unwrap().checkpoint); + assert_eq!(4, tasks.last().unwrap().target_checkpoint); + + // the data recorded in storage should be the same as the datasource + let mut recorded_data = persistent.data.lock().await.clone(); + recorded_data.sort(); + assert_eq!(data, recorded_data); +} + +#[tokio::test] +async fn indexer_partitioned_backfill_task_test() { + telemetry_subscribers::init_for_testing(); + let registry = Registry::new(); + mysten_metrics::init_metrics(®istry); + + let data = (0..=50u64).collect::>(); + let datasource = TestDatasource { data: data.clone() }; + let persistent = InMemoryPersistent::new(); + let indexer = IndexerBuilder::new("test_indexer", datasource, NoopDataMapper) + .with_backfill_strategy(BackfillStrategy::Partitioned { task_size: 10 }) + .build(35, 0, persistent.clone()); + indexer.start().await.unwrap(); + + // it should have 5 task created for the indexer - a live task and 4 backfill task + let tasks = persistent.tasks("test_indexer").await.unwrap(); + assert_eq!(5, tasks.len()); + // the tasks should be ordered by checkpoint number, + // the first one will be the live task and rest will be the backfills + assert_eq!(50, tasks.first().unwrap().checkpoint); + assert_eq!(i64::MAX as u64, tasks.first().unwrap().target_checkpoint); + assert_eq!(34, tasks.get(1).unwrap().checkpoint); + assert_eq!(34, tasks.get(1).unwrap().target_checkpoint); + assert_eq!(29, tasks.get(2).unwrap().checkpoint); + assert_eq!(29, tasks.get(2).unwrap().target_checkpoint); + assert_eq!(19, tasks.get(3).unwrap().checkpoint); + assert_eq!(19, tasks.get(3).unwrap().target_checkpoint); + assert_eq!(9, tasks.get(4).unwrap().checkpoint); + assert_eq!(9, tasks.get(4).unwrap().target_checkpoint); + // the data recorded in storage should be the same as the datasource + let mut recorded_data = persistent.data.lock().await.clone(); + recorded_data.sort(); + assert_eq!(data, recorded_data); +} + +#[tokio::test] +async fn indexer_partitioned_task_with_data_already_in_db_test() { + telemetry_subscribers::init_for_testing(); + let registry = Registry::new(); + mysten_metrics::init_metrics(®istry); + + let data = (0..=50u64).collect::>(); + let datasource = TestDatasource { data: data.clone() }; + let persistent = InMemoryPersistent::new(); + persistent.data.lock().await.append(&mut (0..=30).collect()); + persistent.progress_store.lock().await.insert( + "test_indexer - backfill - 1".to_string(), + Task { + task_name: "test_indexer - backfill - 1".to_string(), + checkpoint: 30, + target_checkpoint: 30, + timestamp: 0, + }, + ); + let indexer = IndexerBuilder::new("test_indexer", datasource, NoopDataMapper) + .with_backfill_strategy(BackfillStrategy::Partitioned { task_size: 10 }) + .build(25, 0, persistent.clone()); + indexer.start().await.unwrap(); + + // it should have 2 task created for the indexer, one existing task and one live task + let tasks = persistent.tasks("test_indexer").await.unwrap(); + assert_eq!(2, tasks.len()); + // the first one will be the live task + assert_eq!(50, tasks.first().unwrap().checkpoint); + assert_eq!(i64::MAX as u64, tasks.first().unwrap().target_checkpoint); + // the data recorded in storage should be the same as the datasource + let mut recorded_data = persistent.data.lock().await.clone(); + recorded_data.sort(); + assert_eq!(data, recorded_data); +} + +#[tokio::test] +async fn indexer_partitioned_task_with_data_already_in_db_test2() { + telemetry_subscribers::init_for_testing(); + let registry = Registry::new(); + mysten_metrics::init_metrics(®istry); + + let data = (0..=50u64).collect::>(); + let datasource = TestDatasource { data: data.clone() }; + let persistent = InMemoryPersistent::new(); + persistent.data.lock().await.append(&mut (0..=30).collect()); + persistent.progress_store.lock().await.insert( + "test_indexer - backfill - 1".to_string(), + Task { + task_name: "test_indexer - backfill - 1".to_string(), + checkpoint: 30, + target_checkpoint: 30, + timestamp: 0, + }, + ); + let indexer = IndexerBuilder::new("test_indexer", datasource, NoopDataMapper) + .with_backfill_strategy(BackfillStrategy::Partitioned { task_size: 10 }) + .build(35, 0, persistent.clone()); + indexer.start().await.unwrap(); + + // it should have 3 task created for the indexer, existing task, a backfill task from cp 31 to cp 34, and a live task + let tasks = persistent.tasks("test_indexer").await.unwrap(); + assert_eq!(3, tasks.len()); + // the tasks should be ordered by checkpoint number, + // the first one will be the live task and rest will be the backfills + assert_eq!(50, tasks.first().unwrap().checkpoint); + assert_eq!(i64::MAX as u64, tasks.first().unwrap().target_checkpoint); + assert_eq!(34, tasks.get(1).unwrap().checkpoint); + assert_eq!(34, tasks.get(1).unwrap().target_checkpoint); + assert_eq!(30, tasks.get(2).unwrap().checkpoint); + assert_eq!(30, tasks.get(2).unwrap().target_checkpoint); + // the data recorded in storage should be the same as the datasource + let mut recorded_data = persistent.data.lock().await.clone(); + recorded_data.sort(); + assert_eq!(data, recorded_data); +} From fea58211135790715c57c61c3f837853e8738538 Mon Sep 17 00:00:00 2001 From: Ge Gao <106119108+gegaowp@users.noreply.github.com> Date: Thu, 15 Aug 2024 14:51:42 -0400 Subject: [PATCH 136/232] indexer minor: more efficient total tx query (#19004) ## Description title, per a related slack thread ## Test plan CI --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-indexer/src/store/pg_indexer_store.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/sui-indexer/src/store/pg_indexer_store.rs b/crates/sui-indexer/src/store/pg_indexer_store.rs index c19fede61bbdf..e7fe8ce61d4f9 100644 --- a/crates/sui-indexer/src/store/pg_indexer_store.rs +++ b/crates/sui-indexer/src/store/pg_indexer_store.rs @@ -1224,9 +1224,9 @@ impl PgIndexerStore { read_only_blocking!(&self.blocking_cp, |conn| { checkpoints::table .filter(checkpoints::epoch.eq(epoch as i64)) - .select(max(checkpoints::network_total_transactions)) - .first::>(conn) - .map(|o| o.unwrap_or(0)) + .select(checkpoints::network_total_transactions) + .order_by(checkpoints::sequence_number.desc()) + .first::(conn) }) .context("Failed to get network total transactions in epoch") .map(|v| v as u64) From 1c88f3f229748f47675de284ed36060a3feb0ba5 Mon Sep 17 00:00:00 2001 From: Eugene Boguslavsky Date: Thu, 15 Aug 2024 14:02:19 -0700 Subject: [PATCH 137/232] Fail if publish to chocolatey fails (#19006) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Fail if publish to chocolatey fails ## Test plan 👀 --- .github/workflows/release.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index de9bbf42c8367..87a5b2efe8521 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -162,8 +162,7 @@ jobs: [[ ${{ env.sui_tag }} == *"testnet"* ]] && aws s3 cp ./tmp/sui-${{ env.sui_tag }}-${{ env.os_type }}.tgz s3://sui-releases/releases/sui-${{ env.sui_tag }}-${{ env.os_type }}.tgz || true - name: Publish Windows sui binary to Chocolatey - if: ${{ matrix.os == 'windows-ghcloud' && contains( env.sui_tag, 'testnet') }} - continue-on-error: true + if: ${{ matrix.os == 'windows-ghcloud' && contains(env.sui_tag, 'testnet') }} shell: bash run: | choco install checksum From 667745104e492e8a9a389bedcf4cf63c37f48118 Mon Sep 17 00:00:00 2001 From: Ge Gao <106119108+gegaowp@users.noreply.github.com> Date: Thu, 15 Aug 2024 18:02:42 -0400 Subject: [PATCH 138/232] indexer 2024-08: merge idx-breaking-change-park to main (#19005) ## Description title, this is another attempt of https://github.com/MystenLabs/sui/pull/18899 which got reverted as it triggered some CI issues, the issues have been resolved by #18993 ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --------- Co-authored-by: Emma Zhong Co-authored-by: Ashok Menon Co-authored-by: wlmyng <127570466+wlmyng@users.noreply.github.com> Co-authored-by: Emma Zhong --- .../src/types/transaction_block.rs | 20 +- .../mysql/2024-04-24-180249_packages/up.sql | 13 +- .../2024-05-05-155158_obj_indices/down.sql | 1 + .../2024-05-05-155158_obj_indices/up.sql | 9 + .../pg/2023-08-19-044020_events/up.sql | 5 +- .../2023-08-19-044026_transactions/down.sql | 1 + .../pg/2023-08-19-044026_transactions/up.sql | 8 +- .../pg/2023-08-19-044044_checkpoints/up.sql | 20 +- .../pg/2023-08-19-060729_packages/up.sql | 14 +- .../pg/2023-10-06-204335_tx_indices/down.sql | 5 +- .../pg/2023-10-06-204335_tx_indices/up.sql | 61 ++- .../up.sql | 6 +- .../pg/2024-05-05-155158_obj_indices/down.sql | 1 + .../pg/2024-05-05-155158_obj_indices/up.sql | 31 ++ .../2024-06-14-045801_event_indices/down.sql | 7 + .../pg/2024-06-14-045801_event_indices/up.sql | 74 +++ .../src/handlers/checkpoint_handler.rs | 66 ++- crates/sui-indexer/src/handlers/committer.rs | 8 + crates/sui-indexer/src/handlers/mod.rs | 6 +- crates/sui-indexer/src/indexer_reader.rs | 8 +- crates/sui-indexer/src/metrics.rs | 16 + crates/sui-indexer/src/models/checkpoints.rs | 4 + .../sui-indexer/src/models/event_indices.rs | 145 ++++++ crates/sui-indexer/src/models/mod.rs | 2 + crates/sui-indexer/src/models/obj_indices.rs | 40 ++ crates/sui-indexer/src/models/packages.rs | 6 + crates/sui-indexer/src/models/tx_indices.rs | 117 +++-- crates/sui-indexer/src/schema/mod.rs | 39 +- crates/sui-indexer/src/schema/mysql.rs | 128 ++++- crates/sui-indexer/src/schema/pg.rs | 149 +++++- crates/sui-indexer/src/store/indexer_store.rs | 9 +- crates/sui-indexer/src/store/mod.rs | 30 ++ .../sui-indexer/src/store/pg_indexer_store.rs | 450 ++++++++++++++++-- .../src/store/pg_partition_manager.rs | 78 ++- crates/sui-indexer/src/types.rs | 49 +- 35 files changed, 1448 insertions(+), 178 deletions(-) create mode 100644 crates/sui-indexer/migrations/mysql/2024-05-05-155158_obj_indices/down.sql create mode 100644 crates/sui-indexer/migrations/mysql/2024-05-05-155158_obj_indices/up.sql create mode 100644 crates/sui-indexer/migrations/pg/2024-05-05-155158_obj_indices/down.sql create mode 100644 crates/sui-indexer/migrations/pg/2024-05-05-155158_obj_indices/up.sql create mode 100644 crates/sui-indexer/migrations/pg/2024-06-14-045801_event_indices/down.sql create mode 100644 crates/sui-indexer/migrations/pg/2024-06-14-045801_event_indices/up.sql create mode 100644 crates/sui-indexer/src/models/event_indices.rs create mode 100644 crates/sui-indexer/src/models/obj_indices.rs diff --git a/crates/sui-graphql-rpc/src/types/transaction_block.rs b/crates/sui-graphql-rpc/src/types/transaction_block.rs index 5009d076145d8..c75a34ee2fcda 100644 --- a/crates/sui-graphql-rpc/src/types/transaction_block.rs +++ b/crates/sui-graphql-rpc/src/types/transaction_block.rs @@ -8,14 +8,14 @@ use async_graphql::{ dataloader::Loader, *, }; -use diesel::{BoolExpressionMethods, ExpressionMethods, JoinOnDsl, QueryDsl, SelectableHelper}; +use diesel::{ExpressionMethods, JoinOnDsl, QueryDsl, SelectableHelper}; use fastcrypto::encoding::{Base58, Encoding}; use serde::{Deserialize, Serialize}; use sui_indexer::{ models::transactions::StoredTransaction, schema::{ - transactions, tx_calls, tx_changed_objects, tx_digests, tx_input_objects, tx_recipients, - tx_senders, + transactions, tx_calls_fun, tx_changed_objects, tx_digests, tx_input_objects, + tx_recipients, tx_senders, }, }; use sui_types::{ @@ -318,15 +318,15 @@ impl TransactionBlock { let mut query = tx::dsl::transactions.into_boxed(); if let Some(f) = &filter.function { - let sub_query = tx_calls::dsl::tx_calls - .select(tx_calls::dsl::tx_sequence_number) + let sub_query = tx_calls_fun::dsl::tx_calls_fun + .select(tx_calls_fun::dsl::tx_sequence_number) .into_boxed(); query = query.filter(tx::dsl::tx_sequence_number.eq_any(f.apply( sub_query, - tx_calls::dsl::package, - tx_calls::dsl::module, - tx_calls::dsl::func, + tx_calls_fun::dsl::package, + tx_calls_fun::dsl::module, + tx_calls_fun::dsl::func, ))); } @@ -507,9 +507,7 @@ impl Loader for Db { let transactions: Vec = self .execute(move |conn| { conn.results(move || { - let join = ds::cp_sequence_number - .eq(tx::checkpoint_sequence_number) - .and(ds::tx_sequence_number.eq(tx::tx_sequence_number)); + let join = ds::tx_sequence_number.eq(tx::tx_sequence_number); tx::transactions .inner_join(ds::tx_digests.on(join)) diff --git a/crates/sui-indexer/migrations/mysql/2024-04-24-180249_packages/up.sql b/crates/sui-indexer/migrations/mysql/2024-04-24-180249_packages/up.sql index f3fe2539038fc..7ee89206f254f 100644 --- a/crates/sui-indexer/migrations/mysql/2024-04-24-180249_packages/up.sql +++ b/crates/sui-indexer/migrations/mysql/2024-04-24-180249_packages/up.sql @@ -1,7 +1,14 @@ CREATE TABLE packages ( - package_id blob NOT NULL, + package_id BLOB NOT NULL, + original_id BLOB NOT NULL, + package_version BIGINT NOT NULL, -- bcs serialized MovePackage - move_package MEDIUMBLOB NOT NULL, - CONSTRAINT packages_pk PRIMARY KEY (package_id(255)) + move_package MEDIUMBLOB NOT NULL, + checkpoint_sequence_number BIGINT NOT NULL, + CONSTRAINT packages_pk PRIMARY KEY (package_id(32), original_id(32), package_version), + CONSTRAINT packages_unique_package_id UNIQUE (package_id(32)) ); + +CREATE INDEX packages_cp_id_version ON packages (checkpoint_sequence_number, original_id(32), package_version); +CREATE INDEX packages_id_version_cp ON packages (original_id(32), package_version, checkpoint_sequence_number); diff --git a/crates/sui-indexer/migrations/mysql/2024-05-05-155158_obj_indices/down.sql b/crates/sui-indexer/migrations/mysql/2024-05-05-155158_obj_indices/down.sql new file mode 100644 index 0000000000000..7a3a7670f24c2 --- /dev/null +++ b/crates/sui-indexer/migrations/mysql/2024-05-05-155158_obj_indices/down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS objects_version; diff --git a/crates/sui-indexer/migrations/mysql/2024-05-05-155158_obj_indices/up.sql b/crates/sui-indexer/migrations/mysql/2024-05-05-155158_obj_indices/up.sql new file mode 100644 index 0000000000000..e501b71a073c0 --- /dev/null +++ b/crates/sui-indexer/migrations/mysql/2024-05-05-155158_obj_indices/up.sql @@ -0,0 +1,9 @@ +-- The Postgres version of this table is partitioned by the first byte +-- of object_id, but this kind of partition is not easily supported in +-- MySQL, so this variant is unpartitioned for now. +CREATE TABLE objects_version ( + object_id BLOB NOT NULL, + object_version BIGINT NOT NULL, + cp_sequence_number BIGINT NOT NULL, + PRIMARY KEY (object_id(32), object_version) +) diff --git a/crates/sui-indexer/migrations/pg/2023-08-19-044020_events/up.sql b/crates/sui-indexer/migrations/pg/2023-08-19-044020_events/up.sql index a6c0d70566e7b..dfbfa3ea14495 100644 --- a/crates/sui-indexer/migrations/pg/2023-08-19-044020_events/up.sql +++ b/crates/sui-indexer/migrations/pg/2023-08-19-044020_events/up.sql @@ -1,3 +1,4 @@ +-- TODO: modify queries in indexer reader to take advantage of the new indices CREATE TABLE events ( tx_sequence_number BIGINT NOT NULL, @@ -23,8 +24,8 @@ CREATE TABLE events timestamp_ms BIGINT NOT NULL, -- bcs of the Event contents (Event.contents) bcs BYTEA NOT NULL, - PRIMARY KEY(tx_sequence_number, event_sequence_number, checkpoint_sequence_number) -) PARTITION BY RANGE (checkpoint_sequence_number); + PRIMARY KEY(tx_sequence_number, event_sequence_number) +) PARTITION BY RANGE (tx_sequence_number); CREATE TABLE events_partition_0 PARTITION OF events FOR VALUES FROM (0) TO (MAXVALUE); CREATE INDEX events_package ON events (package, tx_sequence_number, event_sequence_number); CREATE INDEX events_package_module ON events (package, module, tx_sequence_number, event_sequence_number); diff --git a/crates/sui-indexer/migrations/pg/2023-08-19-044026_transactions/down.sql b/crates/sui-indexer/migrations/pg/2023-08-19-044026_transactions/down.sql index e5850457f922e..15e9dc9f1cb82 100644 --- a/crates/sui-indexer/migrations/pg/2023-08-19-044026_transactions/down.sql +++ b/crates/sui-indexer/migrations/pg/2023-08-19-044026_transactions/down.sql @@ -1,2 +1,3 @@ -- This file should undo anything in `up.sql` DROP TABLE IF EXISTS transactions; +DROP TABLE IF EXISTS transactions_partition_0; diff --git a/crates/sui-indexer/migrations/pg/2023-08-19-044026_transactions/up.sql b/crates/sui-indexer/migrations/pg/2023-08-19-044026_transactions/up.sql index ede66ad44798c..f5404e3610751 100644 --- a/crates/sui-indexer/migrations/pg/2023-08-19-044026_transactions/up.sql +++ b/crates/sui-indexer/migrations/pg/2023-08-19-044026_transactions/up.sql @@ -18,10 +18,6 @@ CREATE TABLE transactions ( -- number of successful commands in this transaction, bound by number of command -- in a programmaable transaction. success_command_count smallint NOT NULL, - PRIMARY KEY (tx_sequence_number, checkpoint_sequence_number) -) PARTITION BY RANGE (checkpoint_sequence_number); + PRIMARY KEY (tx_sequence_number) +) PARTITION BY RANGE (tx_sequence_number); CREATE TABLE transactions_partition_0 PARTITION OF transactions FOR VALUES FROM (0) TO (MAXVALUE); -CREATE INDEX transactions_transaction_digest ON transactions (transaction_digest); -CREATE INDEX transactions_checkpoint_sequence_number ON transactions (checkpoint_sequence_number); --- only create index for system transactions (0). See types.rs -CREATE INDEX transactions_transaction_kind ON transactions (transaction_kind) WHERE transaction_kind = 0; diff --git a/crates/sui-indexer/migrations/pg/2023-08-19-044044_checkpoints/up.sql b/crates/sui-indexer/migrations/pg/2023-08-19-044044_checkpoints/up.sql index 5f7281a2e1a1d..ddb63b020de70 100644 --- a/crates/sui-indexer/migrations/pg/2023-08-19-044044_checkpoints/up.sql +++ b/crates/sui-indexer/migrations/pg/2023-08-19-044044_checkpoints/up.sql @@ -1,15 +1,15 @@ CREATE TABLE checkpoints ( - sequence_number bigint PRIMARY KEY, - checkpoint_digest bytea NOT NULL, - epoch bigint NOT NULL, + sequence_number BIGINT PRIMARY KEY, + checkpoint_digest BYTEA NOT NULL, + epoch BIGINT NOT NULL, -- total transactions in the network at the end of this checkpoint (including itself) - network_total_transactions bigint NOT NULL, - previous_checkpoint_digest bytea, + network_total_transactions BIGINT NOT NULL, + previous_checkpoint_digest BYTEA, -- if this checkpoitn is the last checkpoint of an epoch end_of_epoch boolean NOT NULL, -- array of TranscationDigest in bytes included in this checkpoint - tx_digests bytea[] NOT NULL, + tx_digests BYTEA[] NOT NULL, timestamp_ms BIGINT NOT NULL, total_gas_cost BIGINT NOT NULL, computation_cost BIGINT NOT NULL, @@ -17,11 +17,13 @@ CREATE TABLE checkpoints storage_rebate BIGINT NOT NULL, non_refundable_storage_fee BIGINT NOT NULL, -- bcs serialized Vec bytes - checkpoint_commitments bytea NOT NULL, + checkpoint_commitments BYTEA NOT NULL, -- bcs serialized AggregateAuthoritySignature bytes - validator_signature bytea NOT NULL, + validator_signature BYTEA NOT NULL, -- bcs serialzied EndOfEpochData bytes, if the checkpoint marks end of an epoch - end_of_epoch_data bytea + end_of_epoch_data BYTEA, + min_tx_sequence_number BIGINT, + max_tx_sequence_number BIGINT ); CREATE INDEX checkpoints_epoch ON checkpoints (epoch, sequence_number); diff --git a/crates/sui-indexer/migrations/pg/2023-08-19-060729_packages/up.sql b/crates/sui-indexer/migrations/pg/2023-08-19-060729_packages/up.sql index a95489af4dc41..f08a5549608eb 100644 --- a/crates/sui-indexer/migrations/pg/2023-08-19-060729_packages/up.sql +++ b/crates/sui-indexer/migrations/pg/2023-08-19-060729_packages/up.sql @@ -1,6 +1,14 @@ -CREATE TABLE packages +CREATE TABLE packages ( - package_id bytea PRIMARY KEY, + package_id bytea NOT NULL, + original_id bytea NOT NULL, + package_version bigint NOT NULL, -- bcs serialized MovePackage - move_package bytea NOT NULL + move_package bytea NOT NULL, + checkpoint_sequence_number bigint NOT NULL, + CONSTRAINT packages_pkey PRIMARY KEY (package_id, original_id, package_version), + CONSTRAINT packages_unique_package_id UNIQUE (package_id) ); + +CREATE INDEX packages_cp_id_version ON packages (checkpoint_sequence_number, original_id, package_version); +CREATE INDEX packages_id_version_cp ON packages (original_id, package_version, checkpoint_sequence_number); diff --git a/crates/sui-indexer/migrations/pg/2023-10-06-204335_tx_indices/down.sql b/crates/sui-indexer/migrations/pg/2023-10-06-204335_tx_indices/down.sql index 8e4f29f981c22..f5604c0db5357 100644 --- a/crates/sui-indexer/migrations/pg/2023-10-06-204335_tx_indices/down.sql +++ b/crates/sui-indexer/migrations/pg/2023-10-06-204335_tx_indices/down.sql @@ -2,5 +2,8 @@ DROP TABLE IF EXISTS tx_senders; DROP TABLE IF EXISTS tx_recipients; DROP TABLE IF EXISTS tx_input_objects; DROP TABLE IF EXISTS tx_changed_objects; -DROP TABLE IF EXISTS tx_calls; +DROP TABLE IF EXISTS tx_calls_pkg; +DROP TABLE IF EXISTS tx_calls_mod; +DROP TABLE IF EXISTS tx_calls_fun; DROP TABLE IF EXISTS tx_digests; +DROP TABLE IF EXISTS tx_kinds; diff --git a/crates/sui-indexer/migrations/pg/2023-10-06-204335_tx_indices/up.sql b/crates/sui-indexer/migrations/pg/2023-10-06-204335_tx_indices/up.sql index ed81a281f2b0a..0bcd824e31254 100644 --- a/crates/sui-indexer/migrations/pg/2023-10-06-204335_tx_indices/up.sql +++ b/crates/sui-indexer/migrations/pg/2023-10-06-204335_tx_indices/up.sql @@ -1,57 +1,70 @@ CREATE TABLE tx_senders ( - cp_sequence_number BIGINT NOT NULL, tx_sequence_number BIGINT NOT NULL, - -- SuiAddress in bytes. sender BYTEA NOT NULL, - PRIMARY KEY(sender, tx_sequence_number, cp_sequence_number) + PRIMARY KEY(sender, tx_sequence_number) ); -CREATE INDEX tx_senders_tx_sequence_number_index ON tx_senders (tx_sequence_number, cp_sequence_number); CREATE TABLE tx_recipients ( - cp_sequence_number BIGINT NOT NULL, tx_sequence_number BIGINT NOT NULL, - -- SuiAddress in bytes. recipient BYTEA NOT NULL, - PRIMARY KEY(recipient, tx_sequence_number, cp_sequence_number) + sender BYTEA NOT NULL, + PRIMARY KEY(recipient, tx_sequence_number) ); -CREATE INDEX tx_recipients_tx_sequence_number_index ON tx_recipients (tx_sequence_number, cp_sequence_number); +CREATE INDEX tx_recipients_sender ON tx_recipients (sender, recipient, tx_sequence_number); CREATE TABLE tx_input_objects ( - cp_sequence_number BIGINT NOT NULL, tx_sequence_number BIGINT NOT NULL, - -- Object ID in bytes. object_id BYTEA NOT NULL, - PRIMARY KEY(object_id, tx_sequence_number, cp_sequence_number) + sender BYTEA NOT NULL, + PRIMARY KEY(object_id, tx_sequence_number) ); CREATE INDEX tx_input_objects_tx_sequence_number_index ON tx_input_objects (tx_sequence_number); +CREATE INDEX tx_input_objects_sender ON tx_input_objects (sender, object_id, tx_sequence_number); CREATE TABLE tx_changed_objects ( - cp_sequence_number BIGINT NOT NULL, tx_sequence_number BIGINT NOT NULL, - -- Object Id in bytes. object_id BYTEA NOT NULL, - PRIMARY KEY(object_id, tx_sequence_number, cp_sequence_number) + sender BYTEA NOT NULL, + PRIMARY KEY(object_id, tx_sequence_number) ); CREATE INDEX tx_changed_objects_tx_sequence_number_index ON tx_changed_objects (tx_sequence_number); +CREATE INDEX tx_changed_objects_sender ON tx_changed_objects (sender, object_id, tx_sequence_number); + +CREATE TABLE tx_calls_pkg ( + tx_sequence_number BIGINT NOT NULL, + package BYTEA NOT NULL, + sender BYTEA NOT NULL, + PRIMARY KEY(package, tx_sequence_number) +); +CREATE INDEX tx_calls_pkg_sender ON tx_calls_pkg (sender, package, tx_sequence_number); + +CREATE TABLE tx_calls_mod ( + tx_sequence_number BIGINT NOT NULL, + package BYTEA NOT NULL, + module TEXT NOT NULL, + sender BYTEA NOT NULL, + PRIMARY KEY(package, module, tx_sequence_number) +); +CREATE INDEX tx_calls_mod_sender ON tx_calls_mod (sender, package, module, tx_sequence_number); -CREATE TABLE tx_calls ( - cp_sequence_number BIGINT NOT NULL, +CREATE TABLE tx_calls_fun ( tx_sequence_number BIGINT NOT NULL, package BYTEA NOT NULL, module TEXT NOT NULL, func TEXT NOT NULL, - -- 1. Using Primary Key as a unique index. - -- 2. Diesel does not like tables with no primary key. - PRIMARY KEY(package, tx_sequence_number, cp_sequence_number) + sender BYTEA NOT NULL, + PRIMARY KEY(package, module, func, tx_sequence_number) ); -CREATE INDEX tx_calls_module ON tx_calls (package, module, tx_sequence_number, cp_sequence_number); -CREATE INDEX tx_calls_func ON tx_calls (package, module, func, tx_sequence_number, cp_sequence_number); -CREATE INDEX tx_calls_tx_sequence_number ON tx_calls (tx_sequence_number, cp_sequence_number); +CREATE INDEX tx_calls_fun_sender ON tx_calls_fun (sender, package, module, func, tx_sequence_number); --- un-partitioned table for tx_digest -> (cp_sequence_number, tx_sequence_number) lookup. CREATE TABLE tx_digests ( tx_digest BYTEA PRIMARY KEY, - cp_sequence_number BIGINT NOT NULL, tx_sequence_number BIGINT NOT NULL ); CREATE INDEX tx_digests_tx_sequence_number ON tx_digests (tx_sequence_number); + +CREATE TABLE tx_kinds ( + tx_sequence_number BIGINT NOT NULL, + tx_kind SMALLINT NOT NULL, + PRIMARY KEY(tx_kind, tx_sequence_number) +); diff --git a/crates/sui-indexer/migrations/pg/2023-11-29-193859_advance_partition/up.sql b/crates/sui-indexer/migrations/pg/2023-11-29-193859_advance_partition/up.sql index cb24af8e09934..8ca64b86a7081 100644 --- a/crates/sui-indexer/migrations/pg/2023-11-29-193859_advance_partition/up.sql +++ b/crates/sui-indexer/migrations/pg/2023-11-29-193859_advance_partition/up.sql @@ -1,10 +1,10 @@ -CREATE OR REPLACE PROCEDURE advance_partition(table_name TEXT, last_epoch BIGINT, new_epoch BIGINT, last_epoch_start_cp BIGINT, new_epoch_start_cp BIGINT) +CREATE OR REPLACE PROCEDURE advance_partition(table_name TEXT, last_epoch BIGINT, new_epoch BIGINT, last_epoch_start BIGINT, new_epoch_start BIGINT) LANGUAGE plpgsql AS $$ BEGIN EXECUTE format('ALTER TABLE %I DETACH PARTITION %I_partition_%s', table_name, table_name, last_epoch); - EXECUTE format('ALTER TABLE %I ATTACH PARTITION %I_partition_%s FOR VALUES FROM (%L) TO (%L)', table_name, table_name, last_epoch, last_epoch_start_cp, new_epoch_start_cp); - EXECUTE format('CREATE TABLE IF NOT EXISTS %I_partition_%s PARTITION OF %I FOR VALUES FROM (%L) TO (MAXVALUE)', table_name, new_epoch, table_name, new_epoch_start_cp); + EXECUTE format('ALTER TABLE %I ATTACH PARTITION %I_partition_%s FOR VALUES FROM (%L) TO (%L)', table_name, table_name, last_epoch, last_epoch_start, new_epoch_start); + EXECUTE format('CREATE TABLE IF NOT EXISTS %I_partition_%s PARTITION OF %I FOR VALUES FROM (%L) TO (MAXVALUE)', table_name, new_epoch, table_name, new_epoch_start); END; $$; diff --git a/crates/sui-indexer/migrations/pg/2024-05-05-155158_obj_indices/down.sql b/crates/sui-indexer/migrations/pg/2024-05-05-155158_obj_indices/down.sql new file mode 100644 index 0000000000000..7a3a7670f24c2 --- /dev/null +++ b/crates/sui-indexer/migrations/pg/2024-05-05-155158_obj_indices/down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS objects_version; diff --git a/crates/sui-indexer/migrations/pg/2024-05-05-155158_obj_indices/up.sql b/crates/sui-indexer/migrations/pg/2024-05-05-155158_obj_indices/up.sql new file mode 100644 index 0000000000000..666e5a2423319 --- /dev/null +++ b/crates/sui-indexer/migrations/pg/2024-05-05-155158_obj_indices/up.sql @@ -0,0 +1,31 @@ +-- Indexing table mapping an object's ID and version to its checkpoint +-- sequence number, partitioned by the first byte of its Object ID. +CREATE TABLE objects_version ( + object_id bytea NOT NULL, + object_version bigint NOT NULL, + cp_sequence_number bigint NOT NULL, + PRIMARY KEY (object_id, object_version) +) PARTITION BY RANGE (object_id); + +-- Create a partition for each first byte value. +DO $$ +DECLARE + lo text; + hi text; +BEGIN + FOR i IN 0..254 LOOP + lo := LPAD(TO_HEX(i), 2, '0'); + hi := LPAD(TO_HEX(i + 1), 2, '0'); + EXECUTE FORMAT($F$ + CREATE TABLE objects_version_%1$s PARTITION OF objects_version FOR VALUES + FROM (E'\\x%1$s00000000000000000000000000000000000000000000000000000000000000') + TO (E'\\x%2$s00000000000000000000000000000000000000000000000000000000000000'); + $F$, lo, hi); + END LOOP; +END; +$$ LANGUAGE plpgsql; + +-- Special case for the last partition, because of the upper bound. +CREATE TABLE objects_version_ff PARTITION OF objects_version FOR VALUES +FROM (E'\\xff00000000000000000000000000000000000000000000000000000000000000') +TO (MAXVALUE); diff --git a/crates/sui-indexer/migrations/pg/2024-06-14-045801_event_indices/down.sql b/crates/sui-indexer/migrations/pg/2024-06-14-045801_event_indices/down.sql new file mode 100644 index 0000000000000..3583887435168 --- /dev/null +++ b/crates/sui-indexer/migrations/pg/2024-06-14-045801_event_indices/down.sql @@ -0,0 +1,7 @@ +DROP TABLE IF EXISTS event_emit_package; +DROP TABLE IF EXISTS event_emit_module; +DROP TABLE IF EXISTS event_struct_package; +DROP TABLE IF EXISTS event_struct_module; +DROP TABLE IF EXISTS event_struct_name; +DROP TABLE IF EXISTS event_struct_instantiation; +DROP TABLE IF EXISTS event_senders; diff --git a/crates/sui-indexer/migrations/pg/2024-06-14-045801_event_indices/up.sql b/crates/sui-indexer/migrations/pg/2024-06-14-045801_event_indices/up.sql new file mode 100644 index 0000000000000..a89625146a9fd --- /dev/null +++ b/crates/sui-indexer/migrations/pg/2024-06-14-045801_event_indices/up.sql @@ -0,0 +1,74 @@ +CREATE TABLE event_emit_package +( + package BYTEA NOT NULL, + tx_sequence_number BIGINT NOT NULL, + event_sequence_number BIGINT NOT NULL, + sender BYTEA NOT NULL, + PRIMARY KEY(package, tx_sequence_number, event_sequence_number) +); +CREATE INDEX event_emit_package_sender ON event_emit_package (sender, package, tx_sequence_number, event_sequence_number); + +CREATE TABLE event_emit_module +( + package BYTEA NOT NULL, + module TEXT NOT NULL, + tx_sequence_number BIGINT NOT NULL, + event_sequence_number BIGINT NOT NULL, + sender BYTEA NOT NULL, + PRIMARY KEY(package, module, tx_sequence_number, event_sequence_number) +); +CREATE INDEX event_emit_module_sender ON event_emit_module (sender, package, module, tx_sequence_number, event_sequence_number); + +CREATE TABLE event_struct_package +( + package BYTEA NOT NULL, + tx_sequence_number BIGINT NOT NULL, + event_sequence_number BIGINT NOT NULL, + sender BYTEA NOT NULL, + PRIMARY KEY(package, tx_sequence_number, event_sequence_number) +); +CREATE INDEX event_struct_package_sender ON event_struct_package (sender, package, tx_sequence_number, event_sequence_number); + + +CREATE TABLE event_struct_module +( + package BYTEA NOT NULL, + module TEXT NOT NULL, + tx_sequence_number BIGINT NOT NULL, + event_sequence_number BIGINT NOT NULL, + sender BYTEA NOT NULL, + PRIMARY KEY(package, module, tx_sequence_number, event_sequence_number) +); +CREATE INDEX event_struct_module_sender ON event_struct_module (sender, package, module, tx_sequence_number, event_sequence_number); + +CREATE TABLE event_struct_name +( + package BYTEA NOT NULL, + module TEXT NOT NULL, + type_name TEXT NOT NULL, + tx_sequence_number BIGINT NOT NULL, + event_sequence_number BIGINT NOT NULL, + sender BYTEA NOT NULL, + PRIMARY KEY(package, module, type_name, tx_sequence_number, event_sequence_number) +); +CREATE INDEX event_struct_name_sender ON event_struct_name (sender, package, module, type_name, tx_sequence_number, event_sequence_number); + +CREATE TABLE event_struct_instantiation +( + package BYTEA NOT NULL, + module TEXT NOT NULL, + type_instantiation TEXT NOT NULL, + tx_sequence_number BIGINT NOT NULL, + event_sequence_number BIGINT NOT NULL, + sender BYTEA NOT NULL, + PRIMARY KEY(package, module, type_instantiation, tx_sequence_number, event_sequence_number) +); +CREATE INDEX event_struct_instantiation_sender ON event_struct_instantiation (sender, package, module, type_instantiation, tx_sequence_number, event_sequence_number); + +CREATE TABLE event_senders +( + sender BYTEA NOT NULL, + tx_sequence_number BIGINT NOT NULL, + event_sequence_number BIGINT NOT NULL, + PRIMARY KEY(sender, tx_sequence_number, event_sequence_number) +); diff --git a/crates/sui-indexer/src/handlers/checkpoint_handler.rs b/crates/sui-indexer/src/handlers/checkpoint_handler.rs index b3897f2fe8935..7a8f8efe67fa9 100644 --- a/crates/sui-indexer/src/handlers/checkpoint_handler.rs +++ b/crates/sui-indexer/src/handlers/checkpoint_handler.rs @@ -49,8 +49,8 @@ use crate::db::ConnectionPool; use crate::store::package_resolver::{IndexerStorePackageResolver, InterimPackageResolver}; use crate::store::{IndexerStore, PgIndexerStore}; use crate::types::{ - IndexedCheckpoint, IndexedDeletedObject, IndexedEpochInfo, IndexedEvent, IndexedObject, - IndexedPackage, IndexedTransaction, IndexerResult, TransactionKind, TxIndex, + EventIndex, IndexedCheckpoint, IndexedDeletedObject, IndexedEpochInfo, IndexedEvent, + IndexedObject, IndexedPackage, IndexedTransaction, IndexerResult, TransactionKind, TxIndex, }; use super::tx_processor::EpochEndIndexingObjectStore; @@ -215,6 +215,7 @@ where 0, //first_checkpoint_id None, ), + network_total_transactions: 0, })); } @@ -241,7 +242,12 @@ where let event = bcs::from_bytes::(&epoch_event.contents)?; // Now we just entered epoch X, we want to calculate the diff between - // TotalTransactionsByEndOfEpoch(X-1) and TotalTransactionsByEndOfEpoch(X-2) + // TotalTransactionsByEndOfEpoch(X-1) and TotalTransactionsByEndOfEpoch(X-2). Note that on + // the indexer's chain-reading side, this is not guaranteed to have the latest data. Rather + // than impose a wait on the reading side, however, we overwrite this on the persisting + // side, where we can guarantee that the previous epoch's checkpoints have been written to + // db. + let network_tx_count_prev_epoch = match system_state.epoch { // If first epoch change, this number is 0 1 => Ok(0), @@ -265,6 +271,7 @@ where checkpoint_summary.sequence_number + 1, // first_checkpoint_id Some(&event), ), + network_total_transactions: checkpoint_summary.network_total_transactions, })) } @@ -287,20 +294,21 @@ where let object_history_changes: TransactionObjectChangesToCommit = Self::index_objects_history(data.clone(), package_resolver.clone()).await?; - let (checkpoint, db_transactions, db_events, db_indices, db_displays) = { + let (checkpoint, db_transactions, db_events, db_tx_indices, db_event_indices, db_displays) = { let CheckpointData { transactions, checkpoint_summary, checkpoint_contents, } = data; - let (db_transactions, db_events, db_indices, db_displays) = Self::index_transactions( - transactions, - &checkpoint_summary, - &checkpoint_contents, - &metrics, - ) - .await?; + let (db_transactions, db_events, db_tx_indices, db_event_indices, db_displays) = + Self::index_transactions( + transactions, + &checkpoint_summary, + &checkpoint_contents, + &metrics, + ) + .await?; let successful_tx_num: u64 = db_transactions.iter().map(|t| t.successful_tx_num).sum(); ( @@ -311,7 +319,8 @@ where ), db_transactions, db_events, - db_indices, + db_tx_indices, + db_event_indices, db_displays, ) }; @@ -334,7 +343,8 @@ where checkpoint, transactions: db_transactions, events: db_events, - tx_indices: db_indices, + tx_indices: db_tx_indices, + event_indices: db_event_indices, display_updates: db_displays, object_changes, object_history_changes, @@ -352,6 +362,7 @@ where Vec, Vec, Vec, + Vec, BTreeMap, )> { let checkpoint_seq = checkpoint_summary.sequence_number(); @@ -372,7 +383,8 @@ where let mut db_transactions = Vec::new(); let mut db_events = Vec::new(); let mut db_displays = BTreeMap::new(); - let mut db_indices = Vec::new(); + let mut db_tx_indices = Vec::new(); + let mut db_event_indices = Vec::new(); for tx in transactions { let CheckpointTransaction { @@ -390,6 +402,7 @@ where checkpoint_seq, tx_digest, sender_signed_data.digest() ))); } + let tx = sender_signed_data.transaction_data(); let events = events .as_ref() @@ -413,6 +426,12 @@ where ) })); + db_event_indices.extend( + events.iter().enumerate().map(|(idx, event)| { + EventIndex::from_event(tx_sequence_number, idx as u64, event) + }), + ); + db_displays.extend( events .iter() @@ -440,7 +459,7 @@ where object_changes, balance_change, events, - transaction_kind, + transaction_kind: transaction_kind.clone(), successful_tx_num: if fx.status().is_ok() { tx.kind().tx_count() as u64 } else { @@ -468,8 +487,8 @@ where // Payers let payers = vec![tx.gas_owner()]; - // Senders - let senders = vec![tx.sender()]; + // Sender + let sender = tx.sender(); // Recipients let recipients = fx @@ -489,19 +508,26 @@ where .map(|(p, m, f)| (*<&ObjectID>::clone(p), m.to_string(), f.to_string())) .collect(); - db_indices.push(TxIndex { + db_tx_indices.push(TxIndex { tx_sequence_number, transaction_digest: tx_digest, checkpoint_sequence_number: *checkpoint_seq, input_objects, changed_objects, - senders, + sender, payers, recipients, move_calls, + tx_kind: transaction_kind, }); } - Ok((db_transactions, db_events, db_indices, db_displays)) + Ok(( + db_transactions, + db_events, + db_tx_indices, + db_event_indices, + db_displays, + )) } pub(crate) async fn index_objects( diff --git a/crates/sui-indexer/src/handlers/committer.rs b/crates/sui-indexer/src/handlers/committer.rs index eb1dc7365f2b8..58d6fe94e1f7c 100644 --- a/crates/sui-indexer/src/handlers/committer.rs +++ b/crates/sui-indexer/src/handlers/committer.rs @@ -89,6 +89,7 @@ async fn commit_checkpoints( let mut tx_batch = vec![]; let mut events_batch = vec![]; let mut tx_indices_batch = vec![]; + let mut event_indices_batch = vec![]; let mut display_updates_batch = BTreeMap::new(); let mut object_changes_batch = vec![]; let mut object_history_changes_batch = vec![]; @@ -99,6 +100,7 @@ async fn commit_checkpoints( checkpoint, transactions, events, + event_indices, tx_indices, display_updates, object_changes, @@ -110,6 +112,7 @@ async fn commit_checkpoints( tx_batch.push(transactions); events_batch.push(events); tx_indices_batch.push(tx_indices); + event_indices_batch.push(event_indices); display_updates_batch.extend(display_updates.into_iter()); object_changes_batch.push(object_changes); object_history_changes_batch.push(object_history_changes); @@ -123,6 +126,10 @@ async fn commit_checkpoints( let tx_batch = tx_batch.into_iter().flatten().collect::>(); let tx_indices_batch = tx_indices_batch.into_iter().flatten().collect::>(); let events_batch = events_batch.into_iter().flatten().collect::>(); + let event_indices_batch = event_indices_batch + .into_iter() + .flatten() + .collect::>(); let packages_batch = packages_batch.into_iter().flatten().collect::>(); let checkpoint_num = checkpoint_batch.len(); let tx_count = tx_batch.len(); @@ -133,6 +140,7 @@ async fn commit_checkpoints( state.persist_transactions(tx_batch), state.persist_tx_indices(tx_indices_batch), state.persist_events(events_batch), + state.persist_event_indices(event_indices_batch), state.persist_displays(display_updates_batch), state.persist_packages(packages_batch), state.persist_objects(object_changes_batch.clone()), diff --git a/crates/sui-indexer/src/handlers/mod.rs b/crates/sui-indexer/src/handlers/mod.rs index ca27e92a0bf41..2a6578fc18295 100644 --- a/crates/sui-indexer/src/handlers/mod.rs +++ b/crates/sui-indexer/src/handlers/mod.rs @@ -6,8 +6,8 @@ use std::collections::BTreeMap; use crate::{ models::display::StoredDisplay, types::{ - IndexedCheckpoint, IndexedDeletedObject, IndexedEpochInfo, IndexedEvent, IndexedObject, - IndexedPackage, IndexedTransaction, TxIndex, + EventIndex, IndexedCheckpoint, IndexedDeletedObject, IndexedEpochInfo, IndexedEvent, + IndexedObject, IndexedPackage, IndexedTransaction, TxIndex, }, }; @@ -22,6 +22,7 @@ pub struct CheckpointDataToCommit { pub checkpoint: IndexedCheckpoint, pub transactions: Vec, pub events: Vec, + pub event_indices: Vec, pub tx_indices: Vec, pub display_updates: BTreeMap, pub object_changes: TransactionObjectChangesToCommit, @@ -40,4 +41,5 @@ pub struct TransactionObjectChangesToCommit { pub struct EpochToCommit { pub last_epoch: Option, pub new_epoch: IndexedEpochInfo, + pub network_total_transactions: u64, } diff --git a/crates/sui-indexer/src/indexer_reader.rs b/crates/sui-indexer/src/indexer_reader.rs index 00408945140bd..f05098a764185 100644 --- a/crates/sui-indexer/src/indexer_reader.rs +++ b/crates/sui-indexer/src/indexer_reader.rs @@ -777,14 +777,14 @@ impl IndexerReader { let package = Hex::encode(package.to_vec()); match (module, function) { (Some(module), Some(function)) => ( - "tx_calls".into(), + "tx_calls_fun".into(), format!( "package = '\\x{}'::bytea AND module = '{}' AND func = '{}'", package, module, function ), ), (Some(module), None) => ( - "tx_calls".into(), + "tx_calls_mod".into(), format!( "package = '\\x{}'::bytea AND module = '{}'", package, module @@ -792,11 +792,11 @@ impl IndexerReader { ), (None, Some(_)) => { return Err(IndexerError::InvalidArgumentError( - "Function cannot be present wihtout Module.".into(), + "Function cannot be present without Module.".into(), )); } (None, None) => ( - "tx_calls".into(), + "tx_calls_pkg".into(), format!("package = '\\x{}'::bytea", package), ), } diff --git a/crates/sui-indexer/src/metrics.rs b/crates/sui-indexer/src/metrics.rs index 36d788f5fd580..34978836db5d2 100644 --- a/crates/sui-indexer/src/metrics.rs +++ b/crates/sui-indexer/src/metrics.rs @@ -139,6 +139,8 @@ pub struct IndexerMetrics { pub checkpoint_db_commit_latency_objects_history_chunks: Histogram, pub checkpoint_db_commit_latency_events: Histogram, pub checkpoint_db_commit_latency_events_chunks: Histogram, + pub checkpoint_db_commit_latency_event_indices: Histogram, + pub checkpoint_db_commit_latency_event_indices_chunks: Histogram, pub checkpoint_db_commit_latency_packages: Histogram, pub checkpoint_db_commit_latency_tx_indices: Histogram, pub checkpoint_db_commit_latency_tx_indices_chunks: Histogram, @@ -494,6 +496,20 @@ impl IndexerMetrics { registry, ) .unwrap(), + checkpoint_db_commit_latency_event_indices: register_histogram_with_registry!( + "checkpoint_db_commit_latency_event_indices", + "Time spent commiting event indices", + DATA_INGESTION_LATENCY_SEC_BUCKETS.to_vec(), + registry, + ) + .unwrap(), + checkpoint_db_commit_latency_event_indices_chunks: register_histogram_with_registry!( + "checkpoint_db_commit_latency_event_indices_chunks", + "Time spent commiting event indices chunks", + DATA_INGESTION_LATENCY_SEC_BUCKETS.to_vec(), + registry, + ) + .unwrap(), checkpoint_db_commit_latency_packages: register_histogram_with_registry!( "checkpoint_db_commit_latency_packages", "Time spent commiting packages", diff --git a/crates/sui-indexer/src/models/checkpoints.rs b/crates/sui-indexer/src/models/checkpoints.rs index 260fcfb5944f2..69e2618c82297 100644 --- a/crates/sui-indexer/src/models/checkpoints.rs +++ b/crates/sui-indexer/src/models/checkpoints.rs @@ -42,6 +42,8 @@ pub struct StoredCheckpoint { pub checkpoint_commitments: Vec, pub validator_signature: Vec, pub end_of_epoch_data: Option>, + pub min_tx_sequence_number: Option, + pub max_tx_sequence_number: Option, } impl From<&IndexedCheckpoint> for StoredCheckpoint { @@ -83,6 +85,8 @@ impl From<&IndexedCheckpoint> for StoredCheckpoint { .as_ref() .map(|d| bcs::to_bytes(d).unwrap()), end_of_epoch: c.end_of_epoch_data.is_some(), + min_tx_sequence_number: Some(c.min_tx_sequence_number as i64), + max_tx_sequence_number: Some(c.max_tx_sequence_number as i64), } } } diff --git a/crates/sui-indexer/src/models/event_indices.rs b/crates/sui-indexer/src/models/event_indices.rs new file mode 100644 index 0000000000000..08f17cce339d5 --- /dev/null +++ b/crates/sui-indexer/src/models/event_indices.rs @@ -0,0 +1,145 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + schema::{ + event_emit_module, event_emit_package, event_senders, event_struct_instantiation, + event_struct_module, event_struct_name, event_struct_package, + }, + types::EventIndex, +}; +use diesel::prelude::*; + +#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] +#[diesel(table_name = event_emit_package)] +pub struct StoredEventEmitPackage { + pub tx_sequence_number: i64, + pub event_sequence_number: i64, + pub package: Vec, + pub sender: Vec, +} + +#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] +#[diesel(table_name = event_emit_module)] +pub struct StoredEventEmitModule { + pub tx_sequence_number: i64, + pub event_sequence_number: i64, + pub package: Vec, + pub module: String, + pub sender: Vec, +} + +#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] +#[diesel(table_name = event_senders)] +pub struct StoredEventSenders { + pub tx_sequence_number: i64, + pub event_sequence_number: i64, + pub sender: Vec, +} + +#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] +#[diesel(table_name = event_struct_package)] +pub struct StoredEventStructPackage { + pub tx_sequence_number: i64, + pub event_sequence_number: i64, + pub package: Vec, + pub sender: Vec, +} + +#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] +#[diesel(table_name = event_struct_module)] +pub struct StoredEventStructModule { + pub tx_sequence_number: i64, + pub event_sequence_number: i64, + pub package: Vec, + pub module: String, + pub sender: Vec, +} + +#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] +#[diesel(table_name = event_struct_name)] +pub struct StoredEventStructName { + pub tx_sequence_number: i64, + pub event_sequence_number: i64, + pub package: Vec, + pub module: String, + pub type_name: String, + pub sender: Vec, +} + +#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] +#[diesel(table_name = event_struct_instantiation)] +pub struct StoredEventStructInstantiation { + pub tx_sequence_number: i64, + pub event_sequence_number: i64, + pub package: Vec, + pub module: String, + pub type_instantiation: String, + pub sender: Vec, +} + +impl EventIndex { + pub fn split( + self: EventIndex, + ) -> ( + StoredEventEmitPackage, + StoredEventEmitModule, + StoredEventSenders, + StoredEventStructPackage, + StoredEventStructModule, + StoredEventStructName, + StoredEventStructInstantiation, + ) { + let tx_sequence_number = self.tx_sequence_number as i64; + let event_sequence_number = self.event_sequence_number as i64; + ( + StoredEventEmitPackage { + tx_sequence_number, + event_sequence_number, + package: self.emit_package.to_vec(), + sender: self.sender.to_vec(), + }, + StoredEventEmitModule { + tx_sequence_number, + event_sequence_number, + package: self.emit_package.to_vec(), + module: self.emit_module.clone(), + sender: self.sender.to_vec(), + }, + StoredEventSenders { + tx_sequence_number, + event_sequence_number, + sender: self.sender.to_vec(), + }, + StoredEventStructPackage { + tx_sequence_number, + event_sequence_number, + package: self.type_package.to_vec(), + sender: self.sender.to_vec(), + }, + StoredEventStructModule { + tx_sequence_number, + event_sequence_number, + package: self.type_package.to_vec(), + module: self.type_module.clone(), + sender: self.sender.to_vec(), + }, + StoredEventStructName { + tx_sequence_number, + event_sequence_number, + package: self.type_package.to_vec(), + module: self.type_module.clone(), + type_name: self.type_name.clone(), + sender: self.sender.to_vec(), + }, + StoredEventStructInstantiation { + tx_sequence_number, + event_sequence_number, + package: self.type_package.to_vec(), + module: self.type_module.clone(), + type_instantiation: self.type_instantiation.clone(), + sender: self.sender.to_vec(), + }, + ) + } +} diff --git a/crates/sui-indexer/src/models/mod.rs b/crates/sui-indexer/src/models/mod.rs index 3b8233ec45021..b677e09f1aaad 100644 --- a/crates/sui-indexer/src/models/mod.rs +++ b/crates/sui-indexer/src/models/mod.rs @@ -4,7 +4,9 @@ pub mod checkpoints; pub mod display; pub mod epoch; +pub mod event_indices; pub mod events; +pub mod obj_indices; pub mod objects; pub mod packages; pub mod transactions; diff --git a/crates/sui-indexer/src/models/obj_indices.rs b/crates/sui-indexer/src/models/obj_indices.rs new file mode 100644 index 0000000000000..7e5298008834c --- /dev/null +++ b/crates/sui-indexer/src/models/obj_indices.rs @@ -0,0 +1,40 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use diesel::prelude::*; + +use crate::schema::objects_version; + +use super::objects::StoredDeletedObject; +use super::objects::StoredObject; + +/// Model types related to tables that support efficient execution of queries on the `objects`, +/// `objects_history` and `objects_snapshot` tables. + +#[derive(Queryable, Insertable, Debug, Identifiable, Clone, QueryableByName)] +#[diesel(table_name = objects_version, primary_key(object_id, object_version))] +pub struct StoredObjectVersion { + pub object_id: Vec, + pub object_version: i64, + pub cp_sequence_number: i64, +} + +impl From<&StoredObject> for StoredObjectVersion { + fn from(o: &StoredObject) -> Self { + Self { + object_id: o.object_id.clone(), + object_version: o.object_version, + cp_sequence_number: o.checkpoint_sequence_number, + } + } +} + +impl From<&StoredDeletedObject> for StoredObjectVersion { + fn from(o: &StoredDeletedObject) -> Self { + Self { + object_id: o.object_id.clone(), + object_version: o.object_version, + cp_sequence_number: o.checkpoint_sequence_number, + } + } +} diff --git a/crates/sui-indexer/src/models/packages.rs b/crates/sui-indexer/src/models/packages.rs index 63f61f01fc428..97c8e8fc5b459 100644 --- a/crates/sui-indexer/src/models/packages.rs +++ b/crates/sui-indexer/src/models/packages.rs @@ -10,14 +10,20 @@ use diesel::prelude::*; #[diesel(table_name = packages, primary_key(package_id))] pub struct StoredPackage { pub package_id: Vec, + pub original_id: Vec, + pub package_version: i64, pub move_package: Vec, + pub checkpoint_sequence_number: i64, } impl From for StoredPackage { fn from(p: IndexedPackage) -> Self { Self { package_id: p.package_id.to_vec(), + original_id: p.move_package.original_package_id().to_vec(), + package_version: p.move_package.version().value() as i64, move_package: bcs::to_bytes(&p.move_package).unwrap(), + checkpoint_sequence_number: p.checkpoint_sequence_number as i64, } } } diff --git a/crates/sui-indexer/src/models/tx_indices.rs b/crates/sui-indexer/src/models/tx_indices.rs index 86c4ae4c73819..4f942c2e7af0b 100644 --- a/crates/sui-indexer/src/models/tx_indices.rs +++ b/crates/sui-indexer/src/models/tx_indices.rs @@ -3,7 +3,8 @@ use crate::{ schema::{ - tx_calls, tx_changed_objects, tx_digests, tx_input_objects, tx_recipients, tx_senders, + tx_calls_fun, tx_calls_mod, tx_calls_pkg, tx_changed_objects, tx_digests, tx_input_objects, + tx_kinds, tx_recipients, tx_senders, }, types::TxIndex, }; @@ -24,7 +25,6 @@ pub struct TxDigest { #[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] #[diesel(table_name = tx_senders)] pub struct StoredTxSenders { - pub cp_sequence_number: i64, pub tx_sequence_number: i64, pub sender: Vec, } @@ -32,35 +32,52 @@ pub struct StoredTxSenders { #[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] #[diesel(table_name = tx_recipients)] pub struct StoredTxRecipients { - pub cp_sequence_number: i64, 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 { - pub cp_sequence_number: i64, pub tx_sequence_number: i64, pub object_id: Vec, + pub sender: Vec, } #[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] #[diesel(table_name = tx_changed_objects)] pub struct StoredTxChangedObject { - pub cp_sequence_number: i64, pub tx_sequence_number: i64, pub object_id: Vec, + pub sender: Vec, +} + +#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] +#[diesel(table_name = tx_calls_pkg)] +pub struct StoredTxPkg { + pub tx_sequence_number: i64, + pub package: Vec, + pub sender: Vec, +} + +#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] +#[diesel(table_name = tx_calls_mod)] +pub struct StoredTxMod { + pub tx_sequence_number: i64, + pub package: Vec, + pub module: String, + pub sender: Vec, } #[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] -#[diesel(table_name = tx_calls)] -pub struct StoredTxCalls { - pub cp_sequence_number: i64, +#[diesel(table_name = tx_calls_fun)] +pub struct StoredTxFun { pub tx_sequence_number: i64, pub package: Vec, pub module: String, pub func: String, + pub sender: Vec, } #[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] @@ -68,7 +85,13 @@ pub struct StoredTxCalls { pub struct StoredTxDigest { pub tx_digest: Vec, pub tx_sequence_number: i64, - pub cp_sequence_number: i64, +} + +#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)] +#[diesel(table_name = tx_kinds)] +pub struct StoredTxKind { + pub tx_kind: i16, + pub tx_sequence_number: i64, } #[allow(clippy::type_complexity)] @@ -80,71 +103,109 @@ impl TxIndex { Vec, Vec, Vec, - Vec, + Vec, + Vec, + Vec, Vec, + Vec, ) { let tx_sequence_number = self.tx_sequence_number as i64; - let cp_sequence_number = self.checkpoint_sequence_number as i64; - let tx_senders = self - .senders - .iter() - .map(|s| StoredTxSenders { - cp_sequence_number, - tx_sequence_number, - sender: s.to_vec(), - }) - .collect(); + let tx_sender = StoredTxSenders { + tx_sequence_number, + sender: self.sender.to_vec(), + }; let tx_recipients = self .recipients .iter() .map(|s| StoredTxRecipients { - cp_sequence_number, tx_sequence_number, recipient: s.to_vec(), + sender: self.sender.to_vec(), }) .collect(); let tx_input_objects = self .input_objects .iter() .map(|o| StoredTxInputObject { - cp_sequence_number, tx_sequence_number, object_id: bcs::to_bytes(&o).unwrap(), + sender: self.sender.to_vec(), }) .collect(); let tx_changed_objects = self .changed_objects .iter() .map(|o| StoredTxChangedObject { - cp_sequence_number, tx_sequence_number, object_id: bcs::to_bytes(&o).unwrap(), + sender: self.sender.to_vec(), }) .collect(); - let tx_calls = self + + let mut packages = Vec::new(); + let mut packages_modules = Vec::new(); + let mut packages_modules_funcs = Vec::new(); + + for (pkg, pkg_mod, pkg_mod_func) in self .move_calls .iter() - .map(|(p, m, f)| StoredTxCalls { - cp_sequence_number, + .map(|(p, m, f)| (*p, (*p, m.clone()), (*p, m.clone(), f.clone()))) + { + packages.push(pkg); + packages_modules.push(pkg_mod); + packages_modules_funcs.push(pkg_mod_func); + } + + let tx_pkgs = packages + .iter() + .map(|p| StoredTxPkg { + tx_sequence_number, + package: p.to_vec(), + sender: self.sender.to_vec(), + }) + .collect(); + + let tx_mods = packages_modules + .iter() + .map(|(p, m)| StoredTxMod { + tx_sequence_number, + package: p.to_vec(), + module: m.to_string(), + sender: self.sender.to_vec(), + }) + .collect(); + + let tx_calls = packages_modules_funcs + .iter() + .map(|(p, m, f)| StoredTxFun { tx_sequence_number, package: p.to_vec(), module: m.to_string(), func: f.to_string(), + sender: self.sender.to_vec(), }) .collect(); + let stored_tx_digest = StoredTxDigest { tx_digest: self.transaction_digest.into_inner().to_vec(), tx_sequence_number, - cp_sequence_number, + }; + + let tx_kind = StoredTxKind { + tx_kind: self.tx_kind as i16, + tx_sequence_number, }; ( - tx_senders, + vec![tx_sender], tx_recipients, tx_input_objects, tx_changed_objects, + tx_pkgs, + tx_mods, tx_calls, vec![stored_tx_digest], + vec![tx_kind], ) } } diff --git a/crates/sui-indexer/src/schema/mod.rs b/crates/sui-indexer/src/schema/mod.rs index d1d408d76a307..8f98297ac4d80 100644 --- a/crates/sui-indexer/src/schema/mod.rs +++ b/crates/sui-indexer/src/schema/mod.rs @@ -16,17 +16,28 @@ mod inner { pub use crate::schema::pg::checkpoints; pub use crate::schema::pg::display; pub use crate::schema::pg::epochs; + pub use crate::schema::pg::event_emit_module; + pub use crate::schema::pg::event_emit_package; + pub use crate::schema::pg::event_senders; + pub use crate::schema::pg::event_struct_instantiation; + pub use crate::schema::pg::event_struct_module; + pub use crate::schema::pg::event_struct_name; + pub use crate::schema::pg::event_struct_package; pub use crate::schema::pg::events; pub use crate::schema::pg::objects; pub use crate::schema::pg::objects_history; pub use crate::schema::pg::objects_snapshot; + pub use crate::schema::pg::objects_version; pub use crate::schema::pg::packages; pub use crate::schema::pg::pruner_cp_watermark; pub use crate::schema::pg::transactions; - pub use crate::schema::pg::tx_calls; + pub use crate::schema::pg::tx_calls_fun; + pub use crate::schema::pg::tx_calls_mod; + pub use crate::schema::pg::tx_calls_pkg; pub use crate::schema::pg::tx_changed_objects; pub use crate::schema::pg::tx_digests; pub use crate::schema::pg::tx_input_objects; + pub use crate::schema::pg::tx_kinds; pub use crate::schema::pg::tx_recipients; pub use crate::schema::pg::tx_senders; } @@ -38,17 +49,28 @@ mod inner { pub use crate::schema::mysql::checkpoints; pub use crate::schema::mysql::display; pub use crate::schema::mysql::epochs; + pub use crate::schema::mysql::event_emit_module; + pub use crate::schema::mysql::event_emit_package; + pub use crate::schema::mysql::event_senders; + pub use crate::schema::mysql::event_struct_instantiation; + pub use crate::schema::mysql::event_struct_module; + pub use crate::schema::mysql::event_struct_name; + pub use crate::schema::mysql::event_struct_package; pub use crate::schema::mysql::events; pub use crate::schema::mysql::objects; pub use crate::schema::mysql::objects_history; pub use crate::schema::mysql::objects_snapshot; + pub use crate::schema::mysql::objects_version; pub use crate::schema::mysql::packages; pub use crate::schema::mysql::pruner_cp_watermark; pub use crate::schema::mysql::transactions; - pub use crate::schema::mysql::tx_calls; + pub use crate::schema::mysql::tx_calls_fun; + pub use crate::schema::mysql::tx_calls_mod; + pub use crate::schema::mysql::tx_calls_pkg; pub use crate::schema::mysql::tx_changed_objects; pub use crate::schema::mysql::tx_digests; pub use crate::schema::mysql::tx_input_objects; + pub use crate::schema::mysql::tx_kinds; pub use crate::schema::mysql::tx_recipients; pub use crate::schema::mysql::tx_senders; } @@ -57,17 +79,28 @@ pub use inner::chain_identifier; pub use inner::checkpoints; pub use inner::display; pub use inner::epochs; +pub use inner::event_emit_module; +pub use inner::event_emit_package; +pub use inner::event_senders; +pub use inner::event_struct_instantiation; +pub use inner::event_struct_module; +pub use inner::event_struct_name; +pub use inner::event_struct_package; pub use inner::events; pub use inner::objects; pub use inner::objects_history; pub use inner::objects_snapshot; +pub use inner::objects_version; pub use inner::packages; pub use inner::pruner_cp_watermark; pub use inner::transactions; -pub use inner::tx_calls; +pub use inner::tx_calls_fun; +pub use inner::tx_calls_mod; +pub use inner::tx_calls_pkg; pub use inner::tx_changed_objects; pub use inner::tx_digests; pub use inner::tx_input_objects; +pub use inner::tx_kinds; pub use inner::tx_recipients; pub use inner::tx_senders; diff --git a/crates/sui-indexer/src/schema/mysql.rs b/crates/sui-indexer/src/schema/mysql.rs index 10cdc089c8884..e7805ae562be0 100644 --- a/crates/sui-indexer/src/schema/mysql.rs +++ b/crates/sui-indexer/src/schema/mysql.rs @@ -26,6 +26,8 @@ diesel::table! { checkpoint_commitments -> Mediumblob, validator_signature -> Blob, end_of_epoch_data -> Nullable, + min_tx_sequence_number -> Nullable, + max_tx_sequence_number -> Nullable } } @@ -80,6 +82,74 @@ diesel::table! { } } +diesel::table! { + event_emit_module (package, module, tx_sequence_number, event_sequence_number) { + package -> Blob, + module -> Text, + tx_sequence_number -> Bigint, + event_sequence_number -> Bigint, + sender -> Blob, + } +} + +diesel::table! { + event_emit_package (package, tx_sequence_number, event_sequence_number) { + package -> Blob, + tx_sequence_number -> Bigint, + event_sequence_number -> Bigint, + sender -> Blob, + } +} + +diesel::table! { + event_senders (sender, tx_sequence_number, event_sequence_number) { + sender -> Blob, + tx_sequence_number -> Bigint, + event_sequence_number -> Bigint, + } +} + +diesel::table! { + event_struct_instantiation (package, module, type_instantiation, tx_sequence_number, event_sequence_number) { + package -> Blob, + module -> Text, + type_instantiation -> Text, + tx_sequence_number -> Bigint, + event_sequence_number -> Bigint, + sender -> Blob, + } +} + +diesel::table! { + event_struct_module (package, module, tx_sequence_number, event_sequence_number) { + package -> Blob, + module -> Text, + tx_sequence_number -> Bigint, + event_sequence_number -> Bigint, + sender -> Blob, + } +} + +diesel::table! { + event_struct_name (package, module, type_name, tx_sequence_number, event_sequence_number) { + package -> Blob, + module -> Text, + type_name -> Text, + tx_sequence_number -> Bigint, + event_sequence_number -> Bigint, + sender -> Blob, + } +} + +diesel::table! { + event_struct_package (package, tx_sequence_number, event_sequence_number) { + package -> Blob, + tx_sequence_number -> Bigint, + event_sequence_number -> Bigint, + sender -> Blob, + } +} + diesel::table! { objects (object_id) { object_id -> Blob, @@ -148,10 +218,21 @@ diesel::table! { } } +diesel::table! { + objects_version (object_id, object_version) { + object_id -> Blob, + object_version -> Bigint, + cp_sequence_number -> Bigint, + } +} + diesel::table! { packages (package_id) { package_id -> Blob, + original_id -> Blob, + package_version -> Bigint, move_package -> Mediumblob, + checkpoint_sequence_number -> Bigint, } } @@ -180,12 +261,29 @@ diesel::table! { } diesel::table! { - tx_calls (package, tx_sequence_number, cp_sequence_number) { - cp_sequence_number -> Bigint, + tx_calls_fun (package, module, func, tx_sequence_number) { tx_sequence_number -> Bigint, package -> Blob, module -> Text, func -> Text, + sender -> Blob, + } +} + +diesel::table! { + tx_calls_mod (package, module, tx_sequence_number) { + tx_sequence_number -> Bigint, + package -> Blob, + module -> Text, + sender -> Blob, + } +} + +diesel::table! { + tx_calls_pkg (package, tx_sequence_number) { + tx_sequence_number -> Bigint, + package -> Blob, + sender -> Blob, } } @@ -194,6 +292,7 @@ diesel::table! { cp_sequence_number -> Bigint, tx_sequence_number -> Bigint, object_id -> Blob, + sender -> Blob, } } @@ -210,6 +309,14 @@ diesel::table! { cp_sequence_number -> Bigint, tx_sequence_number -> Bigint, object_id -> Blob, + sender -> Blob, + } +} + +diesel::table! { + tx_kinds (tx_kind, tx_sequence_number) { + tx_sequence_number -> Bigint, + tx_kind -> Smallint, } } @@ -218,6 +325,7 @@ diesel::table! { cp_sequence_number -> Bigint, tx_sequence_number -> Bigint, recipient -> Blob, + sender -> Blob, } } @@ -235,18 +343,30 @@ macro_rules! for_all_tables { $action!( chain_identifier, checkpoints, + pruner_cp_watermark, + display, epochs, + event_emit_module, + event_emit_package, + event_senders, + event_struct_instantiation, + event_struct_module, + event_struct_name, + event_struct_package, events, objects, objects_history, objects_snapshot, + objects_version, packages, - pruner_cp_watermark, transactions, - tx_calls, + tx_calls_fun, + tx_calls_mod, + tx_calls_pkg, tx_changed_objects, tx_digests, tx_input_objects, + tx_kinds, tx_recipients, tx_senders ); diff --git a/crates/sui-indexer/src/schema/pg.rs b/crates/sui-indexer/src/schema/pg.rs index 564f7cd721343..1ef8e0aed8e81 100644 --- a/crates/sui-indexer/src/schema/pg.rs +++ b/crates/sui-indexer/src/schema/pg.rs @@ -26,6 +26,8 @@ diesel::table! { checkpoint_commitments -> Bytea, validator_signature -> Bytea, end_of_epoch_data -> Nullable, + min_tx_sequence_number -> Nullable, + max_tx_sequence_number -> Nullable } } @@ -71,7 +73,75 @@ diesel::table! { } diesel::table! { - events (tx_sequence_number, event_sequence_number, checkpoint_sequence_number) { + event_emit_module (package, module, tx_sequence_number, event_sequence_number) { + package -> Bytea, + module -> Text, + tx_sequence_number -> Int8, + event_sequence_number -> Int8, + sender -> Bytea, + } +} + +diesel::table! { + event_emit_package (package, tx_sequence_number, event_sequence_number) { + package -> Bytea, + tx_sequence_number -> Int8, + event_sequence_number -> Int8, + sender -> Bytea, + } +} + +diesel::table! { + event_senders (sender, tx_sequence_number, event_sequence_number) { + sender -> Bytea, + tx_sequence_number -> Int8, + event_sequence_number -> Int8, + } +} + +diesel::table! { + event_struct_instantiation (package, module, type_instantiation, tx_sequence_number, event_sequence_number) { + package -> Bytea, + module -> Text, + type_instantiation -> Text, + tx_sequence_number -> Int8, + event_sequence_number -> Int8, + sender -> Bytea, + } +} + +diesel::table! { + event_struct_module (package, module, tx_sequence_number, event_sequence_number) { + package -> Bytea, + module -> Text, + tx_sequence_number -> Int8, + event_sequence_number -> Int8, + sender -> Bytea, + } +} + +diesel::table! { + event_struct_name (package, module, type_name, tx_sequence_number, event_sequence_number) { + package -> Bytea, + module -> Text, + type_name -> Text, + tx_sequence_number -> Int8, + event_sequence_number -> Int8, + sender -> Bytea, + } +} + +diesel::table! { + event_struct_package (package, tx_sequence_number, event_sequence_number) { + package -> Bytea, + tx_sequence_number -> Int8, + event_sequence_number -> Int8, + sender -> Bytea, + } +} + +diesel::table! { + events (tx_sequence_number, event_sequence_number) { tx_sequence_number -> Int8, event_sequence_number -> Int8, transaction_digest -> Bytea, @@ -89,7 +159,7 @@ diesel::table! { } diesel::table! { - events_partition_0 (tx_sequence_number, event_sequence_number, checkpoint_sequence_number) { + events_partition_0 (tx_sequence_number, event_sequence_number) { tx_sequence_number -> Int8, event_sequence_number -> Int8, transaction_digest -> Bytea, @@ -198,14 +268,25 @@ diesel::table! { } diesel::table! { - packages (package_id) { + objects_version (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + packages (package_id, original_id, package_version) { package_id -> Bytea, + original_id -> Bytea, + package_version -> Int8, move_package -> Bytea, + checkpoint_sequence_number -> Int8, } } diesel::table! { - transactions (tx_sequence_number, checkpoint_sequence_number) { + transactions (tx_sequence_number) { tx_sequence_number -> Int8, transaction_digest -> Bytea, raw_transaction -> Bytea, @@ -221,7 +302,7 @@ diesel::table! { } diesel::table! { - transactions_partition_0 (tx_sequence_number, checkpoint_sequence_number) { + transactions_partition_0 (tx_sequence_number) { tx_sequence_number -> Int8, transaction_digest -> Bytea, raw_transaction -> Bytea, @@ -237,50 +318,72 @@ diesel::table! { } diesel::table! { - tx_calls (package, tx_sequence_number, cp_sequence_number) { - cp_sequence_number -> Int8, + tx_calls_fun (package, module, func, tx_sequence_number) { tx_sequence_number -> Int8, package -> Bytea, module -> Text, func -> Text, + sender -> Bytea, } } diesel::table! { - tx_changed_objects (object_id, tx_sequence_number, cp_sequence_number) { - cp_sequence_number -> Int8, + tx_calls_mod (package, module, tx_sequence_number) { + tx_sequence_number -> Int8, + package -> Bytea, + module -> Text, + sender -> Bytea, + } +} + +diesel::table! { + tx_calls_pkg (package, tx_sequence_number) { + tx_sequence_number -> Int8, + package -> Bytea, + sender -> Bytea, + } +} + +diesel::table! { + tx_changed_objects (object_id, tx_sequence_number) { tx_sequence_number -> Int8, object_id -> Bytea, + sender -> Bytea, } } diesel::table! { tx_digests (tx_digest) { tx_digest -> Bytea, - cp_sequence_number -> Int8, tx_sequence_number -> Int8, } } diesel::table! { - tx_input_objects (object_id, tx_sequence_number, cp_sequence_number) { - cp_sequence_number -> Int8, + tx_input_objects (object_id, tx_sequence_number) { tx_sequence_number -> Int8, object_id -> Bytea, + sender -> Bytea, } } diesel::table! { - tx_recipients (recipient, tx_sequence_number, cp_sequence_number) { - cp_sequence_number -> Int8, + tx_kinds (tx_kind, tx_sequence_number) { + tx_sequence_number -> Int8, + tx_kind -> Int2, + } +} + +diesel::table! { + tx_recipients (recipient, tx_sequence_number) { tx_sequence_number -> Int8, recipient -> Bytea, + sender -> Bytea, } } diesel::table! { - tx_senders (sender, tx_sequence_number, cp_sequence_number) { - cp_sequence_number -> Int8, + tx_senders (sender, tx_sequence_number) { tx_sequence_number -> Int8, sender -> Bytea, } @@ -295,21 +398,33 @@ macro_rules! for_all_tables { pruner_cp_watermark, display, epochs, + event_emit_module, + event_emit_package, + event_senders, + event_struct_instantiation, + event_struct_module, + event_struct_name, + event_struct_package, events, objects, objects_history, objects_snapshot, + objects_version, packages, transactions, - tx_calls, + tx_calls_fun, + tx_calls_mod, + tx_calls_pkg, tx_changed_objects, tx_digests, tx_input_objects, + tx_kinds, tx_recipients, tx_senders ); }; } + pub use for_all_tables; for_all_tables!(diesel::allow_tables_to_appear_in_same_query); diff --git a/crates/sui-indexer/src/store/indexer_store.rs b/crates/sui-indexer/src/store/indexer_store.rs index 868fe31416a6c..97516929ffe24 100644 --- a/crates/sui-indexer/src/store/indexer_store.rs +++ b/crates/sui-indexer/src/store/indexer_store.rs @@ -10,7 +10,9 @@ use crate::errors::IndexerError; use crate::handlers::{EpochToCommit, TransactionObjectChangesToCommit}; use crate::models::display::StoredDisplay; use crate::models::objects::{StoredDeletedObject, StoredObject}; -use crate::types::{IndexedCheckpoint, IndexedEvent, IndexedPackage, IndexedTransaction, TxIndex}; +use crate::types::{ + EventIndex, IndexedCheckpoint, IndexedEvent, IndexedPackage, IndexedTransaction, TxIndex, +}; #[allow(clippy::large_enum_variant)] pub enum ObjectChangeToCommit { @@ -63,6 +65,11 @@ pub trait IndexerStore: Any + Clone + Sync + Send + 'static { async fn persist_tx_indices(&self, indices: Vec) -> Result<(), IndexerError>; async fn persist_events(&self, events: Vec) -> Result<(), IndexerError>; + async fn persist_event_indices( + &self, + event_indices: Vec, + ) -> Result<(), IndexerError>; + async fn persist_displays( &self, display_updates: BTreeMap, diff --git a/crates/sui-indexer/src/store/mod.rs b/crates/sui-indexer/src/store/mod.rs index ae04c2ea1c1c2..920cc5817499c 100644 --- a/crates/sui-indexer/src/store/mod.rs +++ b/crates/sui-indexer/src/store/mod.rs @@ -312,6 +312,36 @@ pub mod diesel_macro { }}; } + #[macro_export] + macro_rules! persist_chunk_into_table { + ($table:expr, $chunk:expr, $pool:expr) => {{ + let now = std::time::Instant::now(); + let chunk_len = $chunk.len(); + transactional_blocking_with_retry!( + $pool, + |conn| { + for chunk in $chunk.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { + insert_or_ignore_into!($table, chunk, conn); + } + Ok::<(), IndexerError>(()) + }, + PG_DB_COMMIT_SLEEP_DURATION + ) + .tap_ok(|_| { + let elapsed = now.elapsed().as_secs_f64(); + info!( + elapsed, + "Persisted {} rows to {}", + chunk_len, + stringify!($table), + ); + }) + .tap_err(|e| { + tracing::error!("Failed to persist {} with error: {}", stringify!($table), e); + }) + }}; + } + pub use blocking_call_is_ok_or_panic; pub use read_only_blocking; pub use read_only_repeatable_blocking; diff --git a/crates/sui-indexer/src/store/pg_indexer_store.rs b/crates/sui-indexer/src/store/pg_indexer_store.rs index e7fe8ce61d4f9..59e6ba3dc07ff 100644 --- a/crates/sui-indexer/src/store/pg_indexer_store.rs +++ b/crates/sui-indexer/src/store/pg_indexer_store.rs @@ -33,6 +33,7 @@ use crate::models::checkpoints::StoredCpTx; use crate::models::display::StoredDisplay; use crate::models::epoch::StoredEpochInfo; use crate::models::events::StoredEvent; +use crate::models::obj_indices::StoredObjectVersion; use crate::models::objects::{ StoredDeletedHistoryObject, StoredDeletedObject, StoredHistoryObject, StoredObject, StoredObjectSnapshot, @@ -40,13 +41,16 @@ use crate::models::objects::{ use crate::models::packages::StoredPackage; use crate::models::transactions::StoredTransaction; use crate::schema::{ - chain_identifier, checkpoints, display, epochs, events, objects, objects_history, - objects_snapshot, packages, pruner_cp_watermark, transactions, tx_calls, tx_changed_objects, - tx_digests, tx_input_objects, tx_recipients, tx_senders, + chain_identifier, checkpoints, display, epochs, event_emit_module, event_emit_package, + event_senders, event_struct_instantiation, event_struct_module, event_struct_name, + event_struct_package, events, objects, objects_history, objects_snapshot, objects_version, + packages, pruner_cp_watermark, transactions, tx_calls_fun, tx_calls_mod, tx_calls_pkg, + tx_changed_objects, tx_digests, tx_input_objects, tx_kinds, tx_recipients, tx_senders, }; +use crate::types::EventIndex; use crate::types::{IndexedCheckpoint, IndexedEvent, IndexedPackage, IndexedTransaction, TxIndex}; use crate::{ - insert_or_ignore_into, on_conflict_do_update, read_only_blocking, + insert_or_ignore_into, on_conflict_do_update, persist_chunk_into_table, read_only_blocking, transactional_blocking_with_retry, }; @@ -69,7 +73,7 @@ macro_rules! chunk { }}; } -macro_rules! prune_tx_indice_table { +macro_rules! prune_tx_or_event_indice_table { ($table:ident, $conn:expr, $min_tx:expr, $max_tx:expr, $context_msg:expr) => { diesel::delete($table::table.filter($table::tx_sequence_number.between($min_tx, $max_tx))) .execute($conn) @@ -462,6 +466,12 @@ impl PgIndexerStore { .eq(excluded(objects_snapshot::checkpoint_sequence_number)), objects_snapshot::owner_type.eq(excluded(objects_snapshot::owner_type)), objects_snapshot::owner_id.eq(excluded(objects_snapshot::owner_id)), + objects_snapshot::object_type_package + .eq(excluded(objects_snapshot::object_type_package)), + objects_snapshot::object_type_module + .eq(excluded(objects_snapshot::object_type_module)), + objects_snapshot::object_type_name + .eq(excluded(objects_snapshot::object_type_name)), objects_snapshot::object_type .eq(excluded(objects_snapshot::object_type)), objects_snapshot::serialized_object @@ -484,6 +494,9 @@ impl PgIndexerStore { .eq(excluded.checkpoint_sequence_number), objects_snapshot::owner_type.eq(excluded.owner_type), objects_snapshot::owner_id.eq(excluded.owner_id), + objects_snapshot::object_type_package.eq(excluded.object_type_package), + objects_snapshot::object_type_module.eq(excluded.object_type_module), + objects_snapshot::object_type_name.eq(excluded.object_type_name), objects_snapshot::object_type.eq(excluded.object_type), objects_snapshot::serialized_object.eq(excluded.serialized_object), objects_snapshot::coin_type.eq(excluded.coin_type), @@ -522,13 +535,16 @@ impl PgIndexerStore { .checkpoint_db_commit_latency_objects_history_chunks .start_timer(); let mut mutated_objects: Vec = vec![]; + let mut object_versions: Vec = vec![]; let mut deleted_object_ids: Vec = vec![]; for object in objects { match object { ObjectChangeToCommit::MutatedObject(stored_object) => { + object_versions.push(StoredObjectVersion::from(&stored_object)); mutated_objects.push(stored_object.into()); } ObjectChangeToCommit::DeletedObject(stored_deleted_object) => { + object_versions.push(StoredObjectVersion::from(&stored_deleted_object)); deleted_object_ids.push(stored_deleted_object.into()); } } @@ -547,6 +563,11 @@ impl PgIndexerStore { ); } + for object_version_chunk in object_versions.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) + { + insert_or_ignore_into!(objects_version::table, object_version_chunk, conn); + } + for deleted_objects_chunk in deleted_object_ids.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { @@ -806,13 +827,144 @@ impl PgIndexerStore { }) } + async fn persist_event_indices_chunk( + &self, + indices: Vec, + ) -> Result<(), IndexerError> { + let guard = self + .metrics + .checkpoint_db_commit_latency_event_indices_chunks + .start_timer(); + let len = indices.len(); + let ( + event_emit_packages, + event_emit_modules, + event_senders, + event_struct_packages, + event_struct_modules, + event_struct_names, + event_struct_instantiations, + ) = indices.into_iter().map(|i| i.split()).fold( + ( + Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), + ), + |( + mut event_emit_packages, + mut event_emit_modules, + mut event_senders, + mut event_struct_packages, + mut event_struct_modules, + mut event_struct_names, + mut event_struct_instantiations, + ), + index| { + event_emit_packages.push(index.0); + event_emit_modules.push(index.1); + event_senders.push(index.2); + event_struct_packages.push(index.3); + event_struct_modules.push(index.4); + event_struct_names.push(index.5); + event_struct_instantiations.push(index.6); + ( + event_emit_packages, + event_emit_modules, + event_senders, + event_struct_packages, + event_struct_modules, + event_struct_names, + event_struct_instantiations, + ) + }, + ); + + // Now persist all the event indices in parallel into their tables. + let mut futures = vec![]; + futures.push(self.spawn_blocking_task(move |this| { + persist_chunk_into_table!( + event_emit_package::table, + event_emit_packages, + &this.blocking_cp + ) + })); + + futures.push(self.spawn_blocking_task(move |this| { + persist_chunk_into_table!( + event_emit_module::table, + event_emit_modules, + &this.blocking_cp + ) + })); + + futures.push(self.spawn_blocking_task(move |this| { + persist_chunk_into_table!(event_senders::table, event_senders, &this.blocking_cp) + })); + + futures.push(self.spawn_blocking_task(move |this| { + persist_chunk_into_table!( + event_struct_package::table, + event_struct_packages, + &this.blocking_cp + ) + })); + + futures.push(self.spawn_blocking_task(move |this| { + persist_chunk_into_table!( + event_struct_module::table, + event_struct_modules, + &this.blocking_cp + ) + })); + + futures.push(self.spawn_blocking_task(move |this| { + persist_chunk_into_table!( + event_struct_name::table, + event_struct_names, + &this.blocking_cp + ) + })); + + futures.push(self.spawn_blocking_task(move |this| { + persist_chunk_into_table!( + event_struct_instantiation::table, + event_struct_instantiations, + &this.blocking_cp + ) + })); + + futures::future::join_all(futures) + .await + .into_iter() + .collect::, _>>() + .map_err(|e| { + tracing::error!("Failed to join event indices futures in a chunk: {}", e); + IndexerError::from(e) + })? + .into_iter() + .collect::, _>>() + .map_err(|e| { + IndexerError::PostgresWriteError(format!( + "Failed to persist all event indices in a chunk: {:?}", + e + )) + })?; + let elapsed = guard.stop_and_record(); + info!(elapsed, "Persisted {} chunked event indices", len); + Ok(()) + } + async fn persist_tx_indices_chunk(&self, indices: Vec) -> Result<(), IndexerError> { let guard = self .metrics .checkpoint_db_commit_latency_tx_indices_chunks .start_timer(); let len = indices.len(); - let (senders, recipients, input_objects, changed_objects, calls, digests) = + let (senders, recipients, input_objects, changed_objects, pkgs, mods, funs, digests, kinds) = indices.into_iter().map(|i| i.split()).fold( ( Vec::new(), @@ -821,29 +973,41 @@ impl PgIndexerStore { Vec::new(), Vec::new(), Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), ), |( mut tx_senders, mut tx_recipients, mut tx_input_objects, mut tx_changed_objects, - mut tx_calls, + mut tx_pkgs, + mut tx_mods, + mut tx_funs, mut tx_digests, + mut tx_kinds, ), index| { tx_senders.extend(index.0); tx_recipients.extend(index.1); tx_input_objects.extend(index.2); tx_changed_objects.extend(index.3); - tx_calls.extend(index.4); - tx_digests.extend(index.5); + 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_senders, tx_recipients, tx_input_objects, tx_changed_objects, - tx_calls, + tx_pkgs, + tx_mods, + tx_funs, tx_digests, + tx_kinds, ) }, ); @@ -932,14 +1096,15 @@ impl PgIndexerStore { tracing::error!("Failed to persist tx_changed_objects with error: {}", e); }) })); + futures.push(self.spawn_blocking_task(move |this| { let now = Instant::now(); - let calls_len = calls.len(); + let rows_len = pkgs.len(); transactional_blocking_with_retry!( &this.blocking_cp, |conn| { - for chunk in calls.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { - insert_or_ignore_into!(tx_calls::table, chunk, conn); + for chunk in pkgs.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { + insert_or_ignore_into!(tx_calls_pkg::table, chunk, conn); } Ok::<(), IndexerError>(()) }, @@ -947,12 +1112,60 @@ impl PgIndexerStore { ) .tap_ok(|_| { let elapsed = now.elapsed().as_secs_f64(); - info!(elapsed, "Persisted {} rows to tx_calls tables", calls_len); + info!( + elapsed, + "Persisted {} rows to tx_calls_pkg tables", rows_len + ); }) .tap_err(|e| { - tracing::error!("Failed to persist tx_calls with error: {}", e); + tracing::error!("Failed to persist tx_calls_pkg with error: {}", e); }) })); + + futures.push(self.spawn_blocking_task(move |this| { + let now = Instant::now(); + let rows_len = mods.len(); + transactional_blocking_with_retry!( + &this.blocking_cp, + |conn| { + for chunk in mods.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { + insert_or_ignore_into!(tx_calls_mod::table, chunk, conn); + } + Ok::<(), IndexerError>(()) + }, + PG_DB_COMMIT_SLEEP_DURATION + ) + .tap_ok(|_| { + let elapsed = now.elapsed().as_secs_f64(); + info!(elapsed, "Persisted {} rows to tx_calls_mod table", rows_len); + }) + .tap_err(|e| { + tracing::error!("Failed to persist tx_calls_mod with error: {}", e); + }) + })); + + futures.push(self.spawn_blocking_task(move |this| { + let now = Instant::now(); + let rows_len = funs.len(); + transactional_blocking_with_retry!( + &this.blocking_cp, + |conn| { + for chunk in funs.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { + insert_or_ignore_into!(tx_calls_fun::table, chunk, conn); + } + Ok::<(), IndexerError>(()) + }, + PG_DB_COMMIT_SLEEP_DURATION + ) + .tap_ok(|_| { + let elapsed = now.elapsed().as_secs_f64(); + info!(elapsed, "Persisted {} rows to tx_calls_fun table", rows_len); + }) + .tap_err(|e| { + tracing::error!("Failed to persist tx_calls_fun with error: {}", e); + }) + })); + futures.push(self.spawn_blocking_task(move |this| { let now = Instant::now(); let calls_len = digests.len(); @@ -960,12 +1173,7 @@ impl PgIndexerStore { &this.blocking_cp, |conn| { for chunk in digests.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { - diesel::insert_into(tx_digests::table) - .values(chunk) - .on_conflict_do_nothing() - .execute(conn) - .map_err(IndexerError::from) - .context("Failed to write tx_digests chunk to PostgresDB")?; + insert_or_ignore_into!(tx_digests::table, chunk, conn); } Ok::<(), IndexerError>(()) }, @@ -980,6 +1188,28 @@ impl PgIndexerStore { }) })); + futures.push(self.spawn_blocking_task(move |this| { + let now = Instant::now(); + let rows_len = kinds.len(); + transactional_blocking_with_retry!( + &this.blocking_cp, + |conn| { + for chunk in kinds.chunks(PG_COMMIT_CHUNK_SIZE_INTRA_DB_TX) { + insert_or_ignore_into!(tx_kinds::table, chunk, conn); + } + Ok::<(), IndexerError>(()) + }, + Duration::from_secs(60) + ) + .tap_ok(|_| { + let elapsed = now.elapsed().as_secs_f64(); + info!(elapsed, "Persisted {} rows to tx_kinds tables", rows_len); + }) + .tap_err(|e| { + tracing::error!("Failed to persist tx_kinds with error: {}", e); + }) + })); + futures::future::join_all(futures) .await .into_iter() @@ -1007,12 +1237,35 @@ impl PgIndexerStore { .checkpoint_db_commit_latency_epoch .start_timer(); let epoch_id = epoch.new_epoch.epoch; + transactional_blocking_with_retry!( &self.blocking_cp, |conn| { if let Some(last_epoch) = &epoch.last_epoch { let last_epoch_id = last_epoch.epoch; - let last_epoch = StoredEpochInfo::from_epoch_end_info(last_epoch); + // Overwrites the `epoch_total_transactions` field on `epoch.last_epoch` because + // we are not guaranteed to have the latest data in db when this is set on + // indexer's chain-reading side. However, when we `persist_epoch`, the + // checkpoints from an epoch ago must have been indexed. + let previous_epoch_network_total_transactions = match epoch_id { + 0 | 1 => 0, + _ => { + let prev_epoch_id = epoch_id - 2; + let result = checkpoints::table + .filter(checkpoints::epoch.eq(prev_epoch_id as i64)) + .select(max(checkpoints::network_total_transactions)) + .first::>(conn) + .map(|o| o.unwrap_or(0))?; + + result as u64 + } + }; + + let epoch_total_transactions = epoch.network_total_transactions + - previous_epoch_network_total_transactions; + + let mut last_epoch = StoredEpochInfo::from_epoch_end_info(last_epoch); + last_epoch.epoch_total_transactions = Some(epoch_total_transactions as i64); info!(last_epoch_id, "Persisting epoch end data."); on_conflict_do_update!( epochs::table, @@ -1094,6 +1347,14 @@ impl PgIndexerStore { EpochPartitionData::compose_data(epoch_to_commit, last_epoch); let table_partitions = self.partition_manager.get_table_partitions()?; for (table, (_, last_partition)) in table_partitions { + // Only advance epoch partition for epoch partitioned tables. + if !self + .partition_manager + .get_strategy(&table) + .is_epoch_partitioned() + { + continue; + } let guard = self.metrics.advance_epoch_latency.start_timer(); self.partition_manager.advance_epoch( table.clone(), @@ -1147,47 +1408,121 @@ impl PgIndexerStore { ) } + fn prune_event_indices_table(&self, min_tx: u64, max_tx: u64) -> Result<(), IndexerError> { + let (min_tx, max_tx) = (min_tx as i64, max_tx as i64); + transactional_blocking_with_retry!( + &self.blocking_cp, + |conn| { + prune_tx_or_event_indice_table!( + event_emit_module, + conn, + min_tx, + max_tx, + "Failed to prune event_emit_module table" + ); + prune_tx_or_event_indice_table!( + event_emit_package, + conn, + min_tx, + max_tx, + "Failed to prune event_emit_package table" + ); + prune_tx_or_event_indice_table![ + event_senders, + conn, + min_tx, + max_tx, + "Failed to prune event_senders table" + ]; + prune_tx_or_event_indice_table![ + event_struct_instantiation, + conn, + min_tx, + max_tx, + "Failed to prune event_struct_instantiation table" + ]; + prune_tx_or_event_indice_table![ + event_struct_module, + conn, + min_tx, + max_tx, + "Failed to prune event_struct_module table" + ]; + prune_tx_or_event_indice_table![ + event_struct_name, + conn, + min_tx, + max_tx, + "Failed to prune event_struct_name table" + ]; + prune_tx_or_event_indice_table![ + event_struct_package, + conn, + min_tx, + max_tx, + "Failed to prune event_struct_package table" + ]; + Ok::<(), IndexerError>(()) + }, + PG_DB_COMMIT_SLEEP_DURATION + ) + } + fn prune_tx_indices_table(&self, min_tx: u64, max_tx: u64) -> Result<(), IndexerError> { let (min_tx, max_tx) = (min_tx as i64, max_tx as i64); transactional_blocking_with_retry!( &self.blocking_cp, |conn| { - prune_tx_indice_table!( + prune_tx_or_event_indice_table!( tx_senders, conn, min_tx, max_tx, "Failed to prune tx_senders table" ); - prune_tx_indice_table!( + prune_tx_or_event_indice_table!( tx_recipients, conn, min_tx, max_tx, "Failed to prune tx_recipients table" ); - prune_tx_indice_table![ + prune_tx_or_event_indice_table![ tx_input_objects, conn, min_tx, max_tx, "Failed to prune tx_input_objects table" ]; - prune_tx_indice_table![ + prune_tx_or_event_indice_table![ tx_changed_objects, conn, min_tx, max_tx, "Failed to prune tx_changed_objects table" ]; - prune_tx_indice_table![ - tx_calls, + prune_tx_or_event_indice_table![ + tx_calls_pkg, conn, min_tx, max_tx, - "Failed to prune tx_calls table" + "Failed to prune tx_calls_pkg table" ]; - prune_tx_indice_table![ + prune_tx_or_event_indice_table![ + tx_calls_mod, + conn, + min_tx, + max_tx, + "Failed to prune tx_calls_mod table" + ]; + prune_tx_or_event_indice_table![ + tx_calls_fun, + conn, + min_tx, + max_tx, + "Failed to prune tx_calls_fun table" + ]; + prune_tx_or_event_indice_table![ tx_digests, conn, min_tx, @@ -1621,6 +1956,46 @@ impl IndexerStore for PgIndexerStore { .await } + async fn persist_event_indices(&self, indices: Vec) -> Result<(), IndexerError> { + if indices.is_empty() { + return Ok(()); + } + let len = indices.len(); + let guard = self + .metrics + .checkpoint_db_commit_latency_event_indices + .start_timer(); + let chunks = chunk!(indices, self.config.parallel_chunk_size); + + let futures = chunks + .into_iter() + .map(|chunk| { + self.spawn_task(move |this: Self| async move { + this.persist_event_indices_chunk(chunk).await + }) + }) + .collect::>(); + futures::future::join_all(futures) + .await + .into_iter() + .collect::, _>>() + .map_err(|e| { + tracing::error!("Failed to join persist_event_indices_chunk futures: {}", e); + IndexerError::from(e) + })? + .into_iter() + .collect::, _>>() + .map_err(|e| { + IndexerError::PostgresWriteError(format!( + "Failed to persist all event_indices chunks: {:?}", + e + )) + })?; + let elapsed = guard.stop_and_record(); + info!(elapsed, "Persisted {} event_indices chunks", len); + Ok(()) + } + async fn persist_tx_indices(&self, indices: Vec) -> Result<(), IndexerError> { if indices.is_empty() { return Ok(()); @@ -1716,6 +2091,21 @@ impl IndexerStore for PgIndexerStore { "Pruned transactions for checkpoint {} from tx {} to tx {}", cp, min_tx, max_tx ); + self.execute_in_blocking_worker(move |this| { + this.prune_event_indices_table(min_tx, max_tx) + }) + .await + .unwrap_or_else(|e| { + tracing::error!( + "Failed to prune events of transactions for cp {}: {}", + cp, + e + ); + }); + info!( + "Pruned events of transactions for checkpoint {} from tx {} to tx {}", + cp, min_tx, max_tx + ); self.metrics.last_pruned_transaction.set(max_tx as i64); self.execute_in_blocking_worker(move |this| this.prune_cp_tx_table(cp)) diff --git a/crates/sui-indexer/src/store/pg_partition_manager.rs b/crates/sui-indexer/src/store/pg_partition_manager.rs index f67f1ed6b4041..f27078ca47048 100644 --- a/crates/sui-indexer/src/store/pg_partition_manager.rs +++ b/crates/sui-indexer/src/store/pg_partition_manager.rs @@ -4,7 +4,7 @@ use diesel::r2d2::R2D2Connection; use diesel::sql_types::{BigInt, VarChar}; use diesel::{QueryableByName, RunQueryDsl}; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use std::time::Duration; use tracing::{error, info}; @@ -44,22 +44,42 @@ GROUP BY table_name; pub struct PgPartitionManager { cp: ConnectionPool, + partition_strategies: HashMap<&'static str, PgPartitionStrategy>, } impl Clone for PgPartitionManager { fn clone(&self) -> PgPartitionManager { Self { cp: self.cp.clone(), + partition_strategies: self.partition_strategies.clone(), } } } +#[derive(Clone, Copy)] +pub enum PgPartitionStrategy { + CheckpointSequenceNumber, + TxSequenceNumber, + ObjectId, +} + +impl PgPartitionStrategy { + pub fn is_epoch_partitioned(&self) -> bool { + matches!( + self, + Self::CheckpointSequenceNumber | Self::TxSequenceNumber + ) + } +} + #[derive(Clone, Debug)] pub struct EpochPartitionData { last_epoch: u64, next_epoch: u64, last_epoch_start_cp: u64, next_epoch_start_cp: u64, + last_epoch_start_tx: u64, + next_epoch_start_tx: u64, } impl EpochPartitionData { @@ -68,18 +88,35 @@ impl EpochPartitionData { let last_epoch_start_cp = last_db_epoch.first_checkpoint_id as u64; let next_epoch = epoch.new_epoch.epoch; let next_epoch_start_cp = epoch.new_epoch.first_checkpoint_id; + + // Determining the tx_sequence_number range for the epoch partition differs from the + // checkpoint_sequence_number range, because the former is a sum of total transactions - + // this sum already addresses the off-by-one. + let next_epoch_start_tx = epoch.network_total_transactions; + let last_epoch_start_tx = + next_epoch_start_tx - last_db_epoch.epoch_total_transactions.unwrap() as u64; + Self { last_epoch, next_epoch, last_epoch_start_cp, next_epoch_start_cp, + last_epoch_start_tx, + next_epoch_start_tx, } } } impl PgPartitionManager { pub fn new(cp: ConnectionPool) -> Result { - let manager = Self { cp }; + let mut partition_strategies = HashMap::new(); + partition_strategies.insert("events", PgPartitionStrategy::TxSequenceNumber); + partition_strategies.insert("transactions", PgPartitionStrategy::TxSequenceNumber); + partition_strategies.insert("objects_version", PgPartitionStrategy::ObjectId); + let manager = Self { + cp, + partition_strategies, + }; let tables = manager.get_table_partitions()?; info!( "Found {} tables with partitions : [{:?}]", @@ -116,12 +153,41 @@ impl PgPartitionManager { ) } + /// Tries to fetch the partitioning strategy for the given partitioned table. Defaults to + /// `CheckpointSequenceNumber` as the majority of our tables are partitioned on an epoch's + /// checkpoints today. + pub fn get_strategy(&self, table_name: &str) -> PgPartitionStrategy { + self.partition_strategies + .get(table_name) + .copied() + .unwrap_or(PgPartitionStrategy::CheckpointSequenceNumber) + } + + pub fn determine_epoch_partition_range( + &self, + table_name: &str, + data: &EpochPartitionData, + ) -> Option<(u64, u64)> { + match self.get_strategy(table_name) { + PgPartitionStrategy::CheckpointSequenceNumber => { + Some((data.last_epoch_start_cp, data.next_epoch_start_cp)) + } + PgPartitionStrategy::TxSequenceNumber => { + Some((data.last_epoch_start_tx, data.next_epoch_start_tx)) + } + PgPartitionStrategy::ObjectId => None, + } + } + pub fn advance_epoch( &self, table: String, last_partition: u64, data: &EpochPartitionData, ) -> Result<(), IndexerError> { + let Some(partition_range) = self.determine_epoch_partition_range(&table, data) else { + return Ok(()); + }; if data.next_epoch == 0 { tracing::info!("Epoch 0 partition has been created in the initial setup."); return Ok(()); @@ -136,8 +202,8 @@ impl PgPartitionManager { .bind::(table.clone()) .bind::(data.last_epoch as i64) .bind::(data.next_epoch as i64) - .bind::(data.last_epoch_start_cp as i64) - .bind::(data.next_epoch_start_cp as i64), + .bind::(partition_range.0 as i64) + .bind::(partition_range.1 as i64), conn, ) }, @@ -148,14 +214,14 @@ impl PgPartitionManager { transactional_blocking_with_retry!( &self.cp, |conn| { - RunQueryDsl::execute(diesel::sql_query(format!("ALTER TABLE {table_name} REORGANIZE PARTITION {table_name}_partition_{last_epoch} INTO (PARTITION {table_name}_partition_{last_epoch} VALUES LESS THAN ({next_epoch_start_cp}), PARTITION {table_name}_partition_{next_epoch} VALUES LESS THAN MAXVALUE)", table_name = table.clone(), last_epoch = data.last_epoch as i64, next_epoch_start_cp = data.next_epoch_start_cp as i64, next_epoch = data.next_epoch as i64)), conn) + RunQueryDsl::execute(diesel::sql_query(format!("ALTER TABLE {table_name} REORGANIZE PARTITION {table_name}_partition_{last_epoch} INTO (PARTITION {table_name}_partition_{last_epoch} VALUES LESS THAN ({next_epoch_start}), PARTITION {table_name}_partition_{next_epoch} VALUES LESS THAN MAXVALUE)", table_name = table.clone(), last_epoch = data.last_epoch as i64, next_epoch_start = partition_range.1 as i64, next_epoch = data.next_epoch as i64)), conn) }, Duration::from_secs(10) )?; info!( "Advanced epoch partition for table {} from {} to {}, prev partition upper bound {}", - table, last_partition, data.next_epoch, data.last_epoch_start_cp + table, last_partition, data.next_epoch, partition_range.0 ); } else if last_partition != data.next_epoch { // skip when the partition is already advanced once, which is possible when indexer diff --git a/crates/sui-indexer/src/types.rs b/crates/sui-indexer/src/types.rs index 0d313250e2359..04409ecb53757 100644 --- a/crates/sui-indexer/src/types.rs +++ b/crates/sui-indexer/src/types.rs @@ -90,6 +90,8 @@ impl IndexedCheckpoint { } } +/// Represents system state and summary info at the start and end of an epoch. Optional fields are +/// populated at epoch boundary, since they cannot be determined at the start of the epoch. #[derive(Clone, Debug, Default)] pub struct IndexedEpochInfo { pub epoch: u64, @@ -135,6 +137,9 @@ impl IndexedEpochInfo { } } + /// Creates `IndexedEpochInfo` for epoch X-1 at the boundary of epoch X-1 to X. + /// `network_total_tx_num_at_last_epoch_end` is needed to determine the number of transactions + /// that occurred in the epoch X-1. pub fn from_end_of_epoch_data( system_state_summary: &SuiSystemStateSummary, last_checkpoint_summary: &CertifiedCheckpointSummary, @@ -218,6 +223,47 @@ impl IndexedEvent { } } +#[derive(Debug, Clone)] +pub struct EventIndex { + pub tx_sequence_number: u64, + pub event_sequence_number: u64, + pub sender: SuiAddress, + pub emit_package: ObjectID, + pub emit_module: String, + pub type_package: ObjectID, + pub type_module: String, + /// Struct name of the event, without type parameters. + pub type_name: String, + /// Type instantiation of the event, with type name and type parameters, if any. + pub type_instantiation: String, +} + +impl EventIndex { + pub fn from_event( + tx_sequence_number: u64, + event_sequence_number: u64, + event: &sui_types::event::Event, + ) -> Self { + let type_instantiation = event + .type_ + .to_canonical_string(/* with_prefix */ true) + .splitn(3, "::") + .collect::>()[2] + .to_string(); + Self { + tx_sequence_number, + event_sequence_number, + sender: event.sender, + emit_package: event.package_id, + emit_module: event.transaction_module.to_string(), + type_package: event.type_.address.into(), + type_module: event.type_.module.to_string(), + type_name: event.type_.name.to_string(), + type_instantiation, + } + } +} + #[derive(Debug, Copy, Clone)] pub enum OwnerType { Immutable = 0, @@ -340,12 +386,13 @@ pub struct IndexedTransaction { #[derive(Debug, Clone)] pub struct TxIndex { pub tx_sequence_number: u64, + pub tx_kind: TransactionKind, pub transaction_digest: TransactionDigest, pub checkpoint_sequence_number: u64, pub input_objects: Vec, pub changed_objects: Vec, pub payers: Vec, - pub senders: Vec, + pub sender: SuiAddress, pub recipients: Vec, pub move_calls: Vec<(ObjectID, String, String)>, } From 9a09b4914be6d228f0c390848b029b1c6405599f Mon Sep 17 00:00:00 2001 From: Eugene Boguslavsky Date: Thu, 15 Aug 2024 16:02:37 -0700 Subject: [PATCH 139/232] Update chocolatey sui content (#19008) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Update chocolatey sui content ## Test plan 👀 --- .github/workflows/release.yml | 6 +++--- chocolatey/sui.nuspec | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 87a5b2efe8521..1ec80c79b9c12 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -171,9 +171,9 @@ jobs: cat <>VERIFICATION.txt Sui Binary verification steps - 1. Go to https://github.com/MystenLabs/sui/releases/download/${{ env.sui_tag }}/sui-${{ env.sui_tag }}-windows-x86_64.tgz - 2. Extract sui-windows-x86_64.exe - 3. checksum.exe -t sha256 sui-windows-x86_64.exe: ${sui_sha} + 1. Download https://github.com/MystenLabs/sui/releases/download/${{ env.sui_tag }}/sui-${{ env.sui_tag }}-windows-x86_64.tgz + 2. Extract sui.exe + 3. Verify binary: checksum.exe -t sha256 sui.exe: ${sui_sha} File 'LICENSE.txt' is obtained from: https://github.com/MystenLabs/sui/blob/main/LICENSE EOF diff --git a/chocolatey/sui.nuspec b/chocolatey/sui.nuspec index e241fb7aff335..843f5bf90f3c9 100644 --- a/chocolatey/sui.nuspec +++ b/chocolatey/sui.nuspec @@ -5,7 +5,7 @@ enclosed in quotation marks, you should use an editor that supports UTF-8, not t sui $version$ - sui + Sui Foundation Main Sui Binary sui https://sui.io/ @@ -15,9 +15,9 @@ enclosed in quotation marks, you should use an editor that supports UTF-8, not t https://github.com/MystenLabs/sui/issues sui https://community.chocolatey.org/packages/sui.portable - Run a local sui binary + Sui delivers the benefits of Web3 with the ease of Web2 Sui is the first internet-scale programmable blockchain platform - https://github.com/MystenLabs/sui/releases/tag/mainnet-v$version$ + See https://github.com/MystenLabs/sui/releases/tag/testnet-v$version$ From 396ecf1565bf76dce046d6b00d3c0ba0221e0210 Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Fri, 16 Aug 2024 10:37:19 +0100 Subject: [PATCH 140/232] feat(rust-sdk): Simulate WaitForLocalExecution (#18996) ## Description If transaction execution requires waiting for local execution, then simulate it by polling, to account for the fact that fullnodes will soon start to ignore this parameter. ## Test plan Run the programmable transaction SDK example: ``` sui-sdk$ cargo run --example programmable_transactions_api ``` Run the tic-tac-toe E2E example: ``` examples/tic-tac-toe/cli$ env $(cat testnet.env) \ cargo run -- new $(sui client active-address) | | ---+---+--- | | ---+---+--- | | -> X:
O:
GAME: examples/tic-tac-toe/cli$ env $(cat testnet.env) \ cargo run -- move -r 0 -c 0 $GAME ... ``` --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [X] Rust SDK: Adds support for simulating `WaitForLocalExecution` in the client, using polling, as the flag will be ignored by fullnodes shortly. - [ ] REST API: --- .../sui-json-rpc-types/src/sui_transaction.rs | 4 + crates/sui-sdk/src/apis.rs | 73 +++++++++++-------- 2 files changed, 47 insertions(+), 30 deletions(-) diff --git a/crates/sui-json-rpc-types/src/sui_transaction.rs b/crates/sui-json-rpc-types/src/sui_transaction.rs index eff57a51290cc..dcd685349ec2a 100644 --- a/crates/sui-json-rpc-types/src/sui_transaction.rs +++ b/crates/sui-json-rpc-types/src/sui_transaction.rs @@ -180,6 +180,10 @@ impl SuiTransactionBlockResponseOptions { } } + #[deprecated( + since = "1.33.0", + note = "Balance and object changes no longer require local execution" + )] pub fn require_local_execution(&self) -> bool { self.show_balance_changes || self.show_object_changes } diff --git a/crates/sui-sdk/src/apis.rs b/crates/sui-sdk/src/apis.rs index 61c4cd5e81ee0..8597d2fb920f9 100644 --- a/crates/sui-sdk/src/apis.rs +++ b/crates/sui-sdk/src/apis.rs @@ -9,6 +9,7 @@ use jsonrpsee::core::client::Subscription; use std::collections::BTreeMap; use std::future; use std::sync::Arc; +use std::time::Duration; use std::time::Instant; use sui_json_rpc_types::DevInspectArgs; use sui_json_rpc_types::SuiData; @@ -39,7 +40,9 @@ use sui_types::sui_serde::BigInt; use sui_types::sui_system_state::sui_system_state_summary::SuiSystemStateSummary; use sui_types::transaction::{Transaction, TransactionData, TransactionKind}; -const WAIT_FOR_LOCAL_EXECUTION_RETRY_COUNT: u8 = 3; +const WAIT_FOR_LOCAL_EXECUTION_TIMEOUT: Duration = Duration::from_secs(60); +const WAIT_FOR_LOCAL_EXECUTION_DELAY: Duration = Duration::from_millis(200); +const WAIT_FOR_LOCAL_EXECUTION_INTERVAL: Duration = Duration::from_secs(2); /// The main read API structure with functions for retrieving data about different objects and transactions #[derive(Debug)] @@ -1133,39 +1136,49 @@ impl QuorumDriverApi { ) -> SuiRpcResult { let (tx_bytes, signatures) = tx.to_tx_bytes_and_signatures(); let request_type = request_type.unwrap_or_else(|| options.default_execution_request_type()); - let mut retry_count = 0; + let start = Instant::now(); - while retry_count < WAIT_FOR_LOCAL_EXECUTION_RETRY_COUNT { - let response: SuiTransactionBlockResponse = self - .api - .http - .execute_transaction_block( - tx_bytes.clone(), - signatures.clone(), - Some(options.clone()), - Some(request_type.clone()), - ) - .await?; + let response = self + .api + .http + .execute_transaction_block( + tx_bytes.clone(), + signatures.clone(), + Some(options.clone()), + Some(request_type.clone()), + ) + .await?; - match request_type { - ExecuteTransactionRequestType::WaitForEffectsCert => { - return Ok(response); - } - ExecuteTransactionRequestType::WaitForLocalExecution => { - if let Some(true) = response.confirmed_local_execution { - return Ok(response); - } else { - // If fullnode executed the cert in the network but did not confirm local - // execution, it must have timed out and hence we could retry. - retry_count += 1; - } + if let ExecuteTransactionRequestType::WaitForEffectsCert = request_type { + return Ok(response); + } + + // JSON-RPC ignores WaitForLocalExecution, so simulate it by polling for the transaction. + let mut poll_response = tokio::time::timeout(WAIT_FOR_LOCAL_EXECUTION_TIMEOUT, async { + // Apply a short delay to give the full node a chance to catch up. + tokio::time::sleep(WAIT_FOR_LOCAL_EXECUTION_DELAY).await; + + let mut interval = tokio::time::interval(WAIT_FOR_LOCAL_EXECUTION_INTERVAL); + loop { + interval.tick().await; + + if let Ok(poll_response) = self + .api + .http + .get_transaction_block(*tx.digest(), Some(options.clone())) + .await + { + break poll_response; } } - } - Err(Error::FailToConfirmTransactionStatus( - *tx.digest(), - start.elapsed().as_secs(), - )) + }) + .await + .map_err(|_| { + Error::FailToConfirmTransactionStatus(*tx.digest(), start.elapsed().as_secs()) + })?; + + poll_response.confirmed_local_execution = Some(true); + Ok(poll_response) } } From c51f186ad5b377d4e69ad43bf66c79bfe682d81b Mon Sep 17 00:00:00 2001 From: Tony Lee Date: Fri, 16 Aug 2024 09:10:45 -0400 Subject: [PATCH 141/232] Deepbook SDK Constants Update (#19007) ## Description Deepbook SDK Constants Update ## Test plan Testnet --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .changeset/unlucky-lamps-design.md | 5 +++++ sdk/deepbook-v3/src/utils/constants.ts | 12 ++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 .changeset/unlucky-lamps-design.md diff --git a/.changeset/unlucky-lamps-design.md b/.changeset/unlucky-lamps-design.md new file mode 100644 index 0000000000000..05605a2db6e18 --- /dev/null +++ b/.changeset/unlucky-lamps-design.md @@ -0,0 +1,5 @@ +--- +'@mysten/deepbook-v3': minor +--- + +New contract constants diff --git a/sdk/deepbook-v3/src/utils/constants.ts b/sdk/deepbook-v3/src/utils/constants.ts index c1838e4cbc815..ee7d44b8d63a5 100644 --- a/sdk/deepbook-v3/src/utils/constants.ts +++ b/sdk/deepbook-v3/src/utils/constants.ts @@ -12,8 +12,8 @@ export interface DeepbookPackageIds { } export const testnetPackageIds = { - DEEPBOOK_PACKAGE_ID: '0x279443bdf339c7beb585fa44bbd9fa40e4ff255b4df6a2eca32ccc469ffc9046', - REGISTRY_ID: '0xe9c839fd0c6469b750b96ebe0fe656eec25466dea2a57f7d6df12c63c71af9ac', + DEEPBOOK_PACKAGE_ID: '0x2c152dba0110d3afb76b659ed3436edd848b37e177c3abfc0296f8aefc2e6cf4', + REGISTRY_ID: '0x9162317a81a9eb66ecd42705529b2a39c7805f98f42312275c2e7a599d518437', DEEP_TREASURY_ID: '0x69fffdae0075f8f71f4fa793549c11079266910e8905169845af1f5d00e09dcb', } satisfies DeepbookPackageIds; @@ -76,22 +76,22 @@ export const mainnetCoins: CoinMap = { export const testnetPools: PoolMap = { DEEP_SUI: { - address: `0x69c220e16a38e6f18073b2ae5d200d45f27449db86d3435a592474686037416e`, + address: `0x2decc59a6f05c5800e5c8a1135f9d133d1746f562bf56673e6e81ef4f7ccd3b7`, baseCoin: 'DEEP', quoteCoin: 'SUI', }, SUI_DBUSDC: { - address: `0x7abf219eda0771e27a4cc1c87946515c3dfc6ef994b4b06aef81830ff3bfad7f`, + address: `0xace543e8239f0c19783e57bacb02c581fd38d52899bdce117e49c91b494c8b10`, baseCoin: 'SUI', quoteCoin: 'DBUSDC', }, DEEP_DBUSDC: { - address: `0x4d48440f70032595f31d1fd39c4891e81e9b3f5d48ae2dde3198f26eebfed55e`, + address: `0x1faaa544a84c16215ef005edb046ddf8e1cfec0792aec3032e86e554b33bd33a`, baseCoin: 'DEEP', quoteCoin: 'DBUSDC', }, DBUSDT_DBUSDC: { - address: `0x2d532573987c46e43b6d4c09d6b702caee045d330b7450300d331e57a11a0308`, + address: `0x83aca040eaeaf061e3d482a44d1a87a5b8b6206ad52edae9d0479b830a38106f`, baseCoin: 'DBUSDT', quoteCoin: 'DBUSDC', }, From 3316ca4eb1bcd2970da32502a677a5f41e26274f Mon Sep 17 00:00:00 2001 From: ronny-mysten <118224482+ronny-mysten@users.noreply.github.com> Date: Fri, 16 Aug 2024 09:54:47 -0600 Subject: [PATCH 142/232] [docs] Limit information for object model (#19010) ## Description In its original form, the Object Model document pointed to a line of code where the ProtocolConfig struct was. Weird, but the code was added to since the link was made so it no longer pointed to the intended spot. Worse, the link never showed what the actual limits were, just the param name and type. These edits fix the code copy plugin to locate Rust structs so the current code can be used in the document instead of creating another soon-to-be outdated link. Also, adds a component to get the protocol configs for each network so the values are available from docs. The Object Model content was also updated to accommodate these features. Result is bottom of this page: https://sui-docs-git-fork-ronny-mysten-docs-87-sui-foundation.vercel.app/concepts/object-model ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- docs/content/concepts/object-model.mdx | 26 +++- docs/content/concepts/object-ownership.mdx | 1 + .../src/components/ProtocolConfig/index.tsx | 117 ++++++++++++++++++ .../src/plugins/inject-code/injectLoader.js | 2 +- 4 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 docs/site/src/components/ProtocolConfig/index.tsx diff --git a/docs/content/concepts/object-model.mdx b/docs/content/concepts/object-model.mdx index 63bb248c8a1bf..4b3b242a9e785 100644 --- a/docs/content/concepts/object-model.mdx +++ b/docs/content/concepts/object-model.mdx @@ -3,6 +3,8 @@ title: Object Model description: Everything on the Sui blockchain is an object, with metadata, type of ownership, and a referencing scheme. --- +import ProtocolConfig from "@site/src/components/ProtocolConfig"; + The basic unit of storage in Sui is the object. In contrast to many other blockchains where storage is centered around accounts containing key-value stores, Sui's storage is centered around objects addressable on-chain by unique IDs. A smart contract is an object (called a Sui Move package), and these smart contracts manipulate objects on the Sui network: - Sui Move Package: a set of Sui Move bytecode modules. Each module has a name that's unique within the containing package. The combination of the package's on-chain ID and the name of a module uniquely identify the module. When you publish smart contracts to Sui, a package is the unit of publishing. After you publish a package object, it is immutable and can never be changed or removed. A package object can depend on other package objects that were previously published to Sui. @@ -13,7 +15,7 @@ The basic unit of storage in Sui is the object. In contrast to many other blockc Each Sui object has the following metadata: - A 32-byte globally unique ID. An object ID is derived from the digest of the transaction that created the object and from a counter encoding the number of IDs generated by the transaction. -- An 8-byte unsigned integer version that monotonically increases with every transaction that modifies it (see [Object and package versioning](./versioning.mdx)). +- An 8-byte unsigned integer version that monotonically increases with every transaction that modifies it (see [Object and Package Versioning](./versioning.mdx)). - A 32-byte transaction digest indicating the last transaction that included this object as an output. - A 32-byte owner field that indicates how this object can be accessed. See [Object Ownership](./object-ownership.mdx) for more information. @@ -44,4 +46,24 @@ When this DAG contains all committed transactions in the system, it forms a comp ## Limits on transactions, objects, and data -Sui has some limits on transactions and data used in transactions, such as a maximum size and number of objects used. +Sui has some limits on transactions and data used in transactions, such as a maximum size and number of objects used. For more information on limits, see [Building against Limits](https://move-book.com/guides/building-against-limits.html) in The Move Book. + +The `ProtocolConfig` struct in the [`sui-protocol-config` crate](https://github.com/MystenLabs/sui/blob/main/crates/sui-protocol-config/src/lib.rs) itemizes these limits. Expand the following code to see the `ProtocolConfig` struct and the comments that explain each parameter. + +
+ +Toggle source code + +{@inject: crates/sui-protocol-config/src/lib.rs#struct=ProtocolConfig} +
+ +Select a network from the following tabs to see the currently configured limits and values. + + + +## Related links + +- [Object and Package Versioning](./versioning.mdx): Versioning provides the ability to upgrade packages and objects on the Sui network. +- [Object Ownership](./object-ownership.mdx): Every object has an owner field that dictates how you can use it in transactions. +- [`sui-protocol-config`](https://github.com/MystenLabs/sui/blob/main/crates/sui-protocol-config/src/lib.rs): Crate that defines the `ProtocolConfig` struct with limit definitions. +- [Building against Limits](https://move-book.com/guides/building-against-limits.html): The Move Book provides a concise overview for limits most projects deal with. \ No newline at end of file diff --git a/docs/content/concepts/object-ownership.mdx b/docs/content/concepts/object-ownership.mdx index 9a1e9e15a88ec..a2ab513c6e644 100644 --- a/docs/content/concepts/object-ownership.mdx +++ b/docs/content/concepts/object-ownership.mdx @@ -1,5 +1,6 @@ --- title: Object Ownership +description: Every object has an owner field that dictates how you can use it in transactions. Each object is either address-owned, dynamic fields, immutable, shared, or wrapped. --- Every object has an owner field that dictates how you can use it in transactions. Objects can have the following types of ownership: diff --git a/docs/site/src/components/ProtocolConfig/index.tsx b/docs/site/src/components/ProtocolConfig/index.tsx new file mode 100644 index 0000000000000..99f660669ddae --- /dev/null +++ b/docs/site/src/components/ProtocolConfig/index.tsx @@ -0,0 +1,117 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +import React, { useState, useEffect } from "react"; +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import axios from "axios"; + +export default function ProtocolConfig() { + const data = { + jsonrpc: "2.0", + id: 1, + method: "sui_getProtocolConfig", + params: [], + }; + const urls = [ + "https://fullnode.mainnet.sui.io:443", + "https://fullnode.testnet.sui.io:443", + "https://fullnode.devnet.sui.io:443", + ]; + const [results, setResults] = useState({ + mainnet: null, + testnet: null, + devnet: null, + }); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const parseResult = (data) => { + let items = Object.entries(data); + + return items.map((item) => { + if (item[1] === null) { + return item; + } + if (typeof item[1] === "object") { + const [k, v] = Object.entries(item[1])[0]; + return [item[0], k, v]; + } + return item; + }); + }; + + const DisplayResults = (props) => { + const { results } = props; + return ( + + + + + + + + + + {results.map((item, index) => ( + + + + + + ))} + +
ParameterTypeValue
{item[0]}{item[1]}{item[2] ? item[2] : "null"}
+ ); + }; + + useEffect(() => { + const fetchData = async () => { + try { + const responses = await Promise.all( + urls.map((url) => + axios.post(url, data, { + headers: { + "Content-Type": "application/json", + }, + }), + ), + ); + + setResults({ + mainnet: parseResult(responses[0].data.result.attributes), + testnet: parseResult(responses[1].data.result.attributes), + devnet: parseResult(responses[2].data.result.attributes), + }); + } catch (err) { + setError(err.message); + } finally { + setLoading(false); + } + }; + + fetchData(); + }, []); + + if (loading) { + return
Loading...
; + } + + if (error) { + return
Error: {error}
; + } + + return ( + + + + + + + + + + + + ); +} diff --git a/docs/site/src/plugins/inject-code/injectLoader.js b/docs/site/src/plugins/inject-code/injectLoader.js index 704a80e22133b..8aee6e91a2fc2 100644 --- a/docs/site/src/plugins/inject-code/injectLoader.js +++ b/docs/site/src/plugins/inject-code/injectLoader.js @@ -117,7 +117,7 @@ const addCodeInject = function (source) { let structContent = []; for (let struct of structs) { struct = struct.trim(); - const structStr = `^(\\s*)*?(public )?struct \\b${struct}\\b.*?}`; + const structStr = `^(\\s*)*?(pub(lic)? )?struct \\b${struct}\\b.*?}`; const structRE = new RegExp(structStr, "msi"); const structMatch = structRE.exec(injectFileContent); if (structMatch) { From 36f1c6f6b33e70a80322c3458c5425e834b29b85 Mon Sep 17 00:00:00 2001 From: Tony Lee Date: Fri, 16 Aug 2024 13:17:49 -0400 Subject: [PATCH 143/232] Export Update (Deepbook SDK) (#19016) ## Description Deebook SDK update, floating point rounding and exports ## Test plan How did you test the new or updated feature? Testnet ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .changeset/stale-lies-check.md | 5 ++ sdk/deepbook-v3/src/client.ts | 52 +++++++++++-------- sdk/deepbook-v3/src/index.ts | 2 + .../src/transactions/balanceManager.ts | 6 ++- sdk/deepbook-v3/src/transactions/deepbook.ts | 31 +++++++---- .../src/transactions/flashLoans.ts | 10 ++-- .../src/transactions/governance.ts | 9 ++-- 7 files changed, 72 insertions(+), 43 deletions(-) create mode 100644 .changeset/stale-lies-check.md diff --git a/.changeset/stale-lies-check.md b/.changeset/stale-lies-check.md new file mode 100644 index 0000000000000..c47e578f90718 --- /dev/null +++ b/.changeset/stale-lies-check.md @@ -0,0 +1,5 @@ +--- +'@mysten/deepbook-v3': minor +--- + +Rounding for numbers, exports update diff --git a/sdk/deepbook-v3/src/client.ts b/sdk/deepbook-v3/src/client.ts index 5269f5b447a51..b27c1aa68d7fd 100644 --- a/sdk/deepbook-v3/src/client.ts +++ b/sdk/deepbook-v3/src/client.ts @@ -93,7 +93,7 @@ export class DeepBookClient { return { coinType: coin.type, - balance: adjusted_balance, + balance: Number(adjusted_balance.toFixed(9)), }; } @@ -142,9 +142,9 @@ export class DeepBookClient { return { baseQuantity, - baseOut: baseOut / baseScalar, - quoteOut: quoteOut / quoteScalar, - deepRequired: deepRequired / DEEP_SCALAR, + baseOut: Number((baseOut / baseScalar).toFixed(9)), + quoteOut: Number((quoteOut / quoteScalar).toFixed(9)), + deepRequired: Number((deepRequired / DEEP_SCALAR).toFixed(9)), }; } @@ -173,9 +173,9 @@ export class DeepBookClient { return { quoteQuantity: quoteQuantity, - baseOut: baseOut / baseScalar, - quoteOut: quoteOut / quoteScalar, - deepRequired: deepRequired / DEEP_SCALAR, + baseOut: Number((baseOut / baseScalar).toFixed(9)), + quoteOut: Number((quoteOut / quoteScalar).toFixed(9)), + deepRequired: Number((deepRequired / DEEP_SCALAR).toFixed(9)), }; } @@ -206,9 +206,9 @@ export class DeepBookClient { return { baseQuantity, quoteQuantity, - baseOut: baseOut / baseScalar, - quoteOut: quoteOut / quoteScalar, - deepRequired: deepRequired / DEEP_SCALAR, + baseOut: Number((baseOut / baseScalar).toFixed(9)), + quoteOut: Number((quoteOut / quoteScalar).toFixed(9)), + deepRequired: Number((deepRequired / DEEP_SCALAR).toFixed(9)), }; } @@ -301,10 +301,12 @@ export class DeepBookClient { const parsed_quantities = bcs.vector(bcs.u64()).parse(new Uint8Array(quantities)); return { - prices: parsed_prices.map( - (price) => (Number(price) / FLOAT_SCALAR / quoteCoin.scalar) * baseCoin.scalar, + prices: parsed_prices.map((price) => + Number(((Number(price) / FLOAT_SCALAR / quoteCoin.scalar) * baseCoin.scalar).toFixed(9)), + ), + quantities: parsed_quantities.map((price) => + Number((Number(price) / baseCoin.scalar).toFixed(9)), ), - quantities: parsed_quantities.map((price) => Number(price) / baseCoin.scalar), }; } @@ -338,14 +340,18 @@ export class DeepBookClient { const ask_parsed_quantities = bcs.vector(bcs.u64()).parse(new Uint8Array(ask_quantities)); return { - bid_prices: bid_parsed_prices.map( - (price) => (Number(price) / FLOAT_SCALAR / quoteCoin.scalar) * baseCoin.scalar, + bid_prices: bid_parsed_prices.map((price) => + Number(((Number(price) / FLOAT_SCALAR / quoteCoin.scalar) * baseCoin.scalar).toFixed(9)), + ), + bid_quantities: bid_parsed_quantities.map((quantity) => + Number((Number(quantity) / baseCoin.scalar).toFixed(9)), + ), + ask_prices: ask_parsed_prices.map((price) => + Number(((Number(price) / FLOAT_SCALAR / quoteCoin.scalar) * baseCoin.scalar).toFixed(9)), ), - bid_quantities: bid_parsed_quantities.map((quantity) => Number(quantity) / baseCoin.scalar), - ask_prices: ask_parsed_prices.map( - (price) => (Number(price) / FLOAT_SCALAR / quoteCoin.scalar) * baseCoin.scalar, + ask_quantities: ask_parsed_quantities.map((quantity) => + Number((Number(quantity) / baseCoin.scalar).toFixed(9)), ), - ask_quantities: ask_parsed_quantities.map((quantity) => Number(quantity) / baseCoin.scalar), }; } @@ -372,9 +378,9 @@ export class DeepBookClient { const deepInVault = Number(bcs.U64.parse(new Uint8Array(res.results![0].returnValues![2][0]))); return { - base: baseInVault / baseScalar, - quote: quoteInVault / quoteScalar, - deep: deepInVault / DEEP_SCALAR, + base: Number((baseInVault / baseScalar).toFixed(9)), + quote: Number((quoteInVault / quoteScalar).toFixed(9)), + deep: Number((deepInVault / DEEP_SCALAR).toFixed(9)), }; } @@ -424,6 +430,6 @@ export class DeepBookClient { const adjusted_mid_price = (parsed_mid_price * baseCoin.scalar) / quoteCoin.scalar / FLOAT_SCALAR; - return adjusted_mid_price; + return Number(adjusted_mid_price.toFixed(9)); } } diff --git a/sdk/deepbook-v3/src/index.ts b/sdk/deepbook-v3/src/index.ts index f8f6f0d28a03e..fad2c28551413 100644 --- a/sdk/deepbook-v3/src/index.ts +++ b/sdk/deepbook-v3/src/index.ts @@ -8,3 +8,5 @@ export { DeepBookAdminContract } from './transactions/deepbookAdmin.js'; export { FlashLoanContract } from './transactions/flashLoans.js'; export { GovernanceContract } from './transactions/governance.js'; export { DeepBookConfig } from './utils/config.js'; +export type { BalanceManager, Coin, Pool } from './types/index.js'; +export type { CoinMap, PoolMap } from './utils/constants.js'; diff --git a/sdk/deepbook-v3/src/transactions/balanceManager.ts b/sdk/deepbook-v3/src/transactions/balanceManager.ts index 550933c0fc44b..55fdfaa527586 100644 --- a/sdk/deepbook-v3/src/transactions/balanceManager.ts +++ b/sdk/deepbook-v3/src/transactions/balanceManager.ts @@ -46,9 +46,10 @@ export class BalanceManagerContract { tx.setSenderIfNotSet(this.#config.address); const managerId = this.#config.getBalanceManager(managerKey).address; const coin = this.#config.getCoin(coinKey); + const depositInput = Math.round(amountToDeposit * coin.scalar); const deposit = coinWithBalance({ type: coin.type, - balance: amountToDeposit * coin.scalar, + balance: depositInput, }); tx.moveCall({ @@ -71,9 +72,10 @@ export class BalanceManagerContract { (tx: Transaction) => { const managerId = this.#config.getBalanceManager(managerKey).address; const coin = this.#config.getCoin(coinKey); + const withdrawInput = Math.round(amountToWithdraw * coin.scalar); const coinObject = tx.moveCall({ target: `${this.#config.DEEPBOOK_PACKAGE_ID}::balance_manager::withdraw`, - arguments: [tx.object(managerId), tx.pure.u64(amountToWithdraw * coin.scalar)], + arguments: [tx.object(managerId), tx.pure.u64(withdrawInput)], typeArguments: [coin.type], }); diff --git a/sdk/deepbook-v3/src/transactions/deepbook.ts b/sdk/deepbook-v3/src/transactions/deepbook.ts index aff618b9a41d6..8dfa2b792e9a1 100644 --- a/sdk/deepbook-v3/src/transactions/deepbook.ts +++ b/sdk/deepbook-v3/src/transactions/deepbook.ts @@ -46,8 +46,8 @@ export class DeepBookContract { const balanceManager = this.#config.getBalanceManager(balanceManagerKey); const baseCoin = this.#config.getCoin(pool.baseCoin); const quoteCoin = this.#config.getCoin(pool.quoteCoin); - const inputPrice = (price * FLOAT_SCALAR * quoteCoin.scalar) / baseCoin.scalar; - const inputQuantity = quantity * baseCoin.scalar; + const inputPrice = Math.round((price * FLOAT_SCALAR * quoteCoin.scalar) / baseCoin.scalar); + const inputQuantity = Math.round(quantity * baseCoin.scalar); const tradeProof = tx.add(this.#config.balanceManager.generateProof(balanceManagerKey)); @@ -93,6 +93,7 @@ export class DeepBookContract { const baseCoin = this.#config.getCoin(pool.baseCoin); const quoteCoin = this.#config.getCoin(pool.quoteCoin); const tradeProof = tx.add(this.#config.balanceManager.generateProof(balanceManagerKey)); + const inputQuantity = Math.round(quantity * baseCoin.scalar); tx.moveCall({ target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::place_market_order`, @@ -102,7 +103,7 @@ export class DeepBookContract { tradeProof, tx.pure.u64(clientOrderId), tx.pure.u8(selfMatchingOption), - tx.pure.u64(quantity * baseCoin.scalar), + tx.pure.u64(inputQuantity), tx.pure.bool(isBid), tx.pure.bool(payWithDeep), tx.object(SUI_CLOCK_OBJECT_ID), @@ -127,6 +128,7 @@ export class DeepBookContract { const baseCoin = this.#config.getCoin(pool.baseCoin); const quoteCoin = this.#config.getCoin(pool.quoteCoin); const tradeProof = tx.add(this.#config.balanceManager.generateProof(balanceManagerKey)); + const inputQuantity = Math.round(newQuantity * baseCoin.scalar); tx.moveCall({ target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::modify_order`, @@ -135,7 +137,7 @@ export class DeepBookContract { tx.object(balanceManager.address), tradeProof, tx.pure.u128(orderId), - tx.pure.u64(newQuantity), + tx.pure.u64(inputQuantity), tx.object(SUI_CLOCK_OBJECT_ID), ], typeArguments: [baseCoin.type, quoteCoin.type], @@ -524,10 +526,13 @@ export class DeepBookContract { const baseCoinInput = params.baseCoin ?? - coinWithBalance({ type: baseCoin.type, balance: baseAmount * baseCoin.scalar }); + coinWithBalance({ type: baseCoin.type, balance: Math.round(baseAmount * baseCoin.scalar) }); const deepCoin = - params.deepCoin ?? coinWithBalance({ type: deepCoinType, balance: deepAmount * DEEP_SCALAR }); + params.deepCoin ?? + coinWithBalance({ type: deepCoinType, balance: Math.round(deepAmount * DEEP_SCALAR) }); + + const minQuoteInput = Math.round(minQuote * quoteCoin.scalar); const [baseCoinResult, quoteCoinResult, deepCoinResult] = tx.moveCall({ target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::swap_exact_base_for_quote`, @@ -535,7 +540,7 @@ export class DeepBookContract { tx.object(pool.address), baseCoinInput, deepCoin, - tx.pure.u64(quoteCoin.scalar * minQuote), + tx.pure.u64(minQuoteInput), tx.object(SUI_CLOCK_OBJECT_ID), ], typeArguments: [baseCoin.type, quoteCoin.type], @@ -565,10 +570,16 @@ export class DeepBookContract { const quoteCoinInput = params.quoteCoin ?? - coinWithBalance({ type: quoteCoin.type, balance: quoteAmount * quoteCoin.scalar }); + coinWithBalance({ + type: quoteCoin.type, + balance: Math.round(quoteAmount * quoteCoin.scalar), + }); const deepCoin = - params.deepCoin ?? coinWithBalance({ type: deepCoinType, balance: deepAmount * DEEP_SCALAR }); + params.deepCoin ?? + coinWithBalance({ type: deepCoinType, balance: Math.round(deepAmount * DEEP_SCALAR) }); + + const minBaseInput = Math.round(minBase * baseCoin.scalar); const [baseCoinResult, quoteCoinResult, deepCoinResult] = tx.moveCall({ target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::swap_exact_quote_for_base`, @@ -576,7 +587,7 @@ export class DeepBookContract { tx.object(pool.address), quoteCoinInput, deepCoin, - tx.pure.u64(baseCoin.scalar * minBase), + tx.pure.u64(minBaseInput), tx.object(SUI_CLOCK_OBJECT_ID), ], typeArguments: [baseCoin.type, quoteCoin.type], diff --git a/sdk/deepbook-v3/src/transactions/flashLoans.ts b/sdk/deepbook-v3/src/transactions/flashLoans.ts index bffdae0c09572..f61c5eb66f523 100644 --- a/sdk/deepbook-v3/src/transactions/flashLoans.ts +++ b/sdk/deepbook-v3/src/transactions/flashLoans.ts @@ -27,9 +27,10 @@ export class FlashLoanContract { const pool = this.#config.getPool(poolKey); const baseCoin = this.#config.getCoin(pool.baseCoin); const quoteCoin = this.#config.getCoin(pool.quoteCoin); + const inputQuantity = Math.round(borrowAmount * baseCoin.scalar); const [baseCoinResult, flashLoan] = tx.moveCall({ target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::borrow_flashloan_base`, - arguments: [tx.object(pool.address), tx.pure.u64(borrowAmount * baseCoin.scalar)], + arguments: [tx.object(pool.address), tx.pure.u64(inputQuantity)], typeArguments: [baseCoin.type, quoteCoin.type], }); return [baseCoinResult, flashLoan] as const; @@ -57,7 +58,7 @@ export class FlashLoanContract { const borrowScalar = baseCoin.scalar; const [baseCoinReturn] = tx.splitCoins(baseCoinInput, [ - tx.pure.u64(borrowAmount * borrowScalar), + tx.pure.u64(Math.round(borrowAmount * borrowScalar)), ]); tx.moveCall({ target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::return_flashloan_base`, @@ -78,9 +79,10 @@ export class FlashLoanContract { const pool = this.#config.getPool(poolKey); const baseCoin = this.#config.getCoin(pool.baseCoin); const quoteCoin = this.#config.getCoin(pool.quoteCoin); + const inputQuantity = Math.round(borrowAmount * quoteCoin.scalar); const [quoteCoinResult, flashLoan] = tx.moveCall({ target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::borrow_flashloan_quote`, - arguments: [tx.object(pool.address), tx.pure.u64(borrowAmount * quoteCoin.scalar)], + arguments: [tx.object(pool.address), tx.pure.u64(inputQuantity)], typeArguments: [baseCoin.type, quoteCoin.type], }); return [quoteCoinResult, flashLoan] as const; @@ -108,7 +110,7 @@ export class FlashLoanContract { const borrowScalar = quoteCoin.scalar; const [quoteCoinReturn] = tx.splitCoins(quoteCoinInput, [ - tx.pure.u64(borrowAmount * borrowScalar), + tx.pure.u64(Math.round(borrowAmount * borrowScalar)), ]); tx.moveCall({ target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::return_flashloan_quote`, diff --git a/sdk/deepbook-v3/src/transactions/governance.ts b/sdk/deepbook-v3/src/transactions/governance.ts index 9428e8df982de..907be66d6ad13 100644 --- a/sdk/deepbook-v3/src/transactions/governance.ts +++ b/sdk/deepbook-v3/src/transactions/governance.ts @@ -33,6 +33,7 @@ export class GovernanceContract { const tradeProof = tx.add(this.#config.balanceManager.generateProof(balanceManagerKey)); const baseCoin = this.#config.getCoin(pool.baseCoin); const quoteCoin = this.#config.getCoin(pool.quoteCoin); + const stakeInput = Math.round(stakeAmount * DEEP_SCALAR); tx.moveCall({ target: `${this.#config.DEEPBOOK_PACKAGE_ID}::pool::stake`, @@ -40,7 +41,7 @@ export class GovernanceContract { tx.object(pool.address), tx.object(balanceManager.address), tradeProof, - tx.pure.u64(stakeAmount * DEEP_SCALAR), + tx.pure.u64(stakeInput), ], typeArguments: [baseCoin.type, quoteCoin.type], }); @@ -86,9 +87,9 @@ export class GovernanceContract { tx.object(pool.address), tx.object(balanceManager.address), tradeProof, - tx.pure.u64(takerFee * FLOAT_SCALAR), - tx.pure.u64(makerFee * FLOAT_SCALAR), - tx.pure.u64(stakeRequired * DEEP_SCALAR), + tx.pure.u64(Math.round(takerFee * FLOAT_SCALAR)), + tx.pure.u64(Math.round(makerFee * FLOAT_SCALAR)), + tx.pure.u64(Math.round(stakeRequired * DEEP_SCALAR)), ], typeArguments: [baseCoin.type, quoteCoin.type], }); From 8574a5bd9e666bd9f60f9bc25763460efe0f97c5 Mon Sep 17 00:00:00 2001 From: Ge Gao <106119108+gegaowp@users.noreply.github.com> Date: Fri, 16 Aug 2024 13:52:32 -0400 Subject: [PATCH 144/232] indexer: DB reset to handle incomplete migration history (#19019) ## Description previous fix did not work well when migration run history in __diesel_schema_migrations table is not complete. I also considered dropping the database as we usually do locally, but a connection cannot drop the database in its own connection and ended up having errors of ``` cannot drop the currently open database ``` thus this pr drops all tables, functions and procedures explicitly for PG and tables for MySQL as we now only have tables in MySQL. ## Test plan 1. diesel reset on latest main 2. cherry-pick this pr to 1.31 and 1.30, run cmd below and make sure that DB are indeed reset ``` DB_POOL_SIZE=10 cargo run --bin sui-indexer -- --db-url =DB_URL --reset-db ``` --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-indexer/src/db.rs | 85 +++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 7 deletions(-) diff --git a/crates/sui-indexer/src/db.rs b/crates/sui-indexer/src/db.rs index 7057dd36eb282..08e6dd3956d38 100644 --- a/crates/sui-indexer/src/db.rs +++ b/crates/sui-indexer/src/db.rs @@ -200,6 +200,7 @@ pub mod setup_postgres { use anyhow::anyhow; use diesel::migration::MigrationSource; use diesel::PgConnection; + use diesel::RunQueryDsl; use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; use prometheus::Registry; use secrecy::ExposeSecret; @@ -208,9 +209,58 @@ pub mod setup_postgres { const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/pg"); pub fn reset_database(conn: &mut PoolConnection) -> Result<(), anyhow::Error> { - info!("Resetting database ..."); - conn.revert_all_migrations(MIGRATIONS) - .map_err(|e| anyhow!("Error reverting all migrations {e}"))?; + info!("Resetting PG database ..."); + + let drop_all_tables = " + DO $$ DECLARE + r RECORD; + BEGIN + FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = 'public') + LOOP + EXECUTE 'DROP TABLE IF EXISTS ' || quote_ident(r.tablename) || ' CASCADE'; + END LOOP; + END $$;"; + diesel::sql_query(drop_all_tables).execute(conn)?; + info!("Dropped all tables."); + + let drop_all_procedures = " + DO $$ DECLARE + r RECORD; + BEGIN + FOR r IN (SELECT proname, oidvectortypes(proargtypes) as argtypes + FROM pg_proc INNER JOIN pg_namespace ns ON (pg_proc.pronamespace = ns.oid) + WHERE ns.nspname = 'public' AND prokind = 'p') + LOOP + EXECUTE 'DROP PROCEDURE IF EXISTS ' || quote_ident(r.proname) || '(' || r.argtypes || ') CASCADE'; + END LOOP; + END $$;"; + diesel::sql_query(drop_all_procedures).execute(conn)?; + info!("Dropped all procedures."); + + let drop_all_functions = " + DO $$ DECLARE + r RECORD; + BEGIN + FOR r IN (SELECT proname, oidvectortypes(proargtypes) as argtypes + FROM pg_proc INNER JOIN pg_namespace ON (pg_proc.pronamespace = pg_namespace.oid) + WHERE pg_namespace.nspname = 'public' AND prokind = 'f') + LOOP + EXECUTE 'DROP FUNCTION IF EXISTS ' || quote_ident(r.proname) || '(' || r.argtypes || ') CASCADE'; + END LOOP; + END $$;"; + diesel::sql_query(drop_all_functions).execute(conn)?; + info!("Dropped all functions."); + + diesel::sql_query( + " + CREATE TABLE IF NOT EXISTS __diesel_schema_migrations ( + version VARCHAR(50) PRIMARY KEY, + run_on TIMESTAMP NOT NULL DEFAULT NOW() + )", + ) + .execute(conn)?; + info!("Created __diesel_schema_migrations table."); + conn.run_migrations(&MIGRATIONS.migrations().unwrap()) .map_err(|e| anyhow!("Failed to run migrations {e}"))?; info!("Reset database complete."); @@ -307,6 +357,7 @@ pub mod setup_mysql { use anyhow::anyhow; use diesel::migration::MigrationSource; use diesel::MysqlConnection; + use diesel::RunQueryDsl; use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; use prometheus::Registry; use secrecy::ExposeSecret; @@ -315,12 +366,32 @@ pub mod setup_mysql { const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/mysql"); pub fn reset_database(conn: &mut PoolConnection) -> Result<(), anyhow::Error> { - info!("Resetting database ..."); - conn.revert_all_migrations(MIGRATIONS) - .map_err(|e| anyhow!("Error reverting all migrations {e}"))?; + info!("Resetting MySQL database ..."); + + let table_names: Vec = diesel::dsl::sql::( + "SELECT TABLE_NAME FROM information_schema.tables WHERE table_schema = DATABASE()", + ) + .load(conn)?; + for table_name in table_names { + let drop_table_query = format!("DROP TABLE IF EXISTS {}", table_name); + diesel::sql_query(drop_table_query).execute(conn)?; + } + info!("Drop tables complete."); + + diesel::sql_query( + " + CREATE TABLE __diesel_schema_migrations ( + version VARCHAR(50) PRIMARY KEY, + run_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP() + ) + ", + ) + .execute(conn)?; + info!("Created __diesel_schema_migrations table."); + conn.run_migrations(&MIGRATIONS.migrations().unwrap()) .map_err(|e| anyhow!("Failed to run migrations {e}"))?; - info!("Reset database complete."); + info!("All migrations complete, reset database complete"); Ok(()) } From 75c5e109555a34618f32265f3fe2edb040abc15a Mon Sep 17 00:00:00 2001 From: "sui-merge-bot[bot]" <114704316+sui-merge-bot[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 16:22:41 -0400 Subject: [PATCH 145/232] Version Packages (#19015) This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @mysten/deepbook-v3@0.3.0 ### Minor Changes - 36f1c6f: Rounding for numbers, exports update - c51f186: New contract constants Co-authored-by: github-actions[bot] --- .changeset/stale-lies-check.md | 5 ----- .changeset/unlucky-lamps-design.md | 5 ----- sdk/deepbook-v3/CHANGELOG.md | 7 +++++++ sdk/deepbook-v3/package.json | 2 +- 4 files changed, 8 insertions(+), 11 deletions(-) delete mode 100644 .changeset/stale-lies-check.md delete mode 100644 .changeset/unlucky-lamps-design.md diff --git a/.changeset/stale-lies-check.md b/.changeset/stale-lies-check.md deleted file mode 100644 index c47e578f90718..0000000000000 --- a/.changeset/stale-lies-check.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@mysten/deepbook-v3': minor ---- - -Rounding for numbers, exports update diff --git a/.changeset/unlucky-lamps-design.md b/.changeset/unlucky-lamps-design.md deleted file mode 100644 index 05605a2db6e18..0000000000000 --- a/.changeset/unlucky-lamps-design.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@mysten/deepbook-v3': minor ---- - -New contract constants diff --git a/sdk/deepbook-v3/CHANGELOG.md b/sdk/deepbook-v3/CHANGELOG.md index a029c48c94577..2564d0eff9d59 100644 --- a/sdk/deepbook-v3/CHANGELOG.md +++ b/sdk/deepbook-v3/CHANGELOG.md @@ -1,5 +1,12 @@ # @mysten/deepbook-v3 +## 0.3.0 + +### Minor Changes + +- 36f1c6f: Rounding for numbers, exports update +- c51f186: New contract constants + ## 0.2.1 ### Patch Changes diff --git a/sdk/deepbook-v3/package.json b/sdk/deepbook-v3/package.json index 7c95c5f0bf1fd..b42520d75bf0c 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.2.1", + "version": "0.3.0", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", From f8f19a3bc19a5e7191c80e7955b7509bda405e15 Mon Sep 17 00:00:00 2001 From: Adam Welc Date: Sat, 17 Aug 2024 10:39:37 -0700 Subject: [PATCH 146/232] [move-ide] Refactored parsing symbolicator into parsing analysis (#18942) ## Description What the title says - the idea was to do a similar thing we did before for typing symbolicator ## Test plan All existing tests must pass --- .../crates/move-analyzer/src/analysis/mod.rs | 1 + .../src/analysis/parsing_analysis.rs | 848 +++++++++++++++++ .../move/crates/move-analyzer/src/symbols.rs | 875 +----------------- 3 files changed, 870 insertions(+), 854 deletions(-) create mode 100644 external-crates/move/crates/move-analyzer/src/analysis/parsing_analysis.rs diff --git a/external-crates/move/crates/move-analyzer/src/analysis/mod.rs b/external-crates/move/crates/move-analyzer/src/analysis/mod.rs index d1e3391e262e1..6f2a94305dea9 100644 --- a/external-crates/move/crates/move-analyzer/src/analysis/mod.rs +++ b/external-crates/move/crates/move-analyzer/src/analysis/mod.rs @@ -1,4 +1,5 @@ // Copyright (c) The Move Contributors // SPDX-License-Identifier: Apache-2.0 +pub mod parsing_analysis; pub mod typing_analysis; diff --git a/external-crates/move/crates/move-analyzer/src/analysis/parsing_analysis.rs b/external-crates/move/crates/move-analyzer/src/analysis/parsing_analysis.rs new file mode 100644 index 0000000000000..710a007dd9d24 --- /dev/null +++ b/external-crates/move/crates/move-analyzer/src/analysis/parsing_analysis.rs @@ -0,0 +1,848 @@ +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + symbols::{ + add_member_use_def, ignored_function, CallInfo, CursorContext, CursorDefinition, + CursorPosition, DefMap, ModuleDefs, References, UseDef, UseDefMap, + }, + utils::loc_start_to_lsp_position_opt, +}; + +use lsp_types::Position; + +use std::collections::BTreeMap; + +use move_compiler::{ + expansion::ast as E, + parser::ast as P, + shared::{files::MappedFiles, Identifier, Name, NamedAddressMap, NamedAddressMaps}, +}; +use move_ir_types::location::*; + +pub struct ParsingAnalysisContext<'a> { + /// Outermost definitions in a module (structs, consts, functions), keyd on a ModuleIdent + /// string so that we can access it regardless of the ModuleIdent representation + /// (e.g., in the parsing AST or in the typing AST) + pub mod_outer_defs: &'a mut BTreeMap, + /// Mapped file information for translating locations into positions + pub files: &'a MappedFiles, + /// Associates uses for a given definition to allow displaying all references + pub references: &'a mut References, + /// Additional information about definitions + pub def_info: &'a mut DefMap, + /// A UseDefMap for a given module (needs to be appropriately set before the module + /// processing starts) + pub use_defs: UseDefMap, + /// Current module identifier string (needs to be appropriately set before the module + /// processing starts) + pub current_mod_ident_str: Option, + /// Module name lengths in access paths for a given module (needs to be appropriately + /// set before the module processing starts) + pub alias_lengths: BTreeMap, + /// A per-package mapping from package names to their addresses (needs to be appropriately set + /// before the package processint starts) + pub pkg_addresses: &'a NamedAddressMap, + /// Cursor contextual information, computed as part of the traversal. + pub cursor: Option<&'a mut CursorContext>, +} + +macro_rules! update_cursor { + ($cursor:expr, $subject:expr, $kind:ident) => { + if let Some(cursor) = &mut $cursor { + if $subject.loc.contains(&cursor.loc) { + cursor.position = CursorPosition::$kind($subject.clone()); + } + }; + }; + (IDENT, $cursor:expr, $subject:expr, $kind:ident) => { + if let Some(cursor) = &mut $cursor { + if $subject.loc().contains(&cursor.loc) { + cursor.position = CursorPosition::$kind($subject.clone()); + } + }; + }; +} + +impl<'a> ParsingAnalysisContext<'a> { + /// Get symbols for the whole program + pub fn prog_symbols( + &mut self, + prog: &'a P::Program, + mod_use_defs: &mut BTreeMap, + mod_to_alias_lengths: &mut BTreeMap>, + ) { + prog.source_definitions.iter().for_each(|pkg_def| { + self.pkg_symbols( + &prog.named_address_maps, + pkg_def, + mod_use_defs, + mod_to_alias_lengths, + ) + }); + prog.lib_definitions.iter().for_each(|pkg_def| { + self.pkg_symbols( + &prog.named_address_maps, + pkg_def, + mod_use_defs, + mod_to_alias_lengths, + ) + }); + } + + /// Get symbols for the whole package + fn pkg_symbols( + &mut self, + pkg_address_maps: &'a NamedAddressMaps, + pkg_def: &P::PackageDefinition, + mod_use_defs: &mut BTreeMap, + mod_to_alias_lengths: &mut BTreeMap>, + ) { + if let P::Definition::Module(mod_def) = &pkg_def.def { + let pkg_addresses = pkg_address_maps.get(pkg_def.named_address_map); + let old_addresses = std::mem::replace(&mut self.pkg_addresses, pkg_addresses); + self.mod_symbols(mod_def, mod_use_defs, mod_to_alias_lengths); + self.current_mod_ident_str = None; + let _ = std::mem::replace(&mut self.pkg_addresses, old_addresses); + } + } + + fn attr_symbols(&mut self, sp!(_, attr): P::Attribute) { + use P::Attribute_ as A; + match attr { + A::Name(_) => (), + A::Assigned(_, v) => { + update_cursor!(self.cursor, *v, Attribute); + } + A::Parameterized(_, sp!(_, attributes)) => { + attributes.iter().for_each(|a| self.attr_symbols(a.clone())) + } + } + } + + /// Get symbols for the whole module + fn mod_symbols( + &mut self, + mod_def: &P::ModuleDefinition, + mod_use_defs: &mut BTreeMap, + mod_to_alias_lengths: &mut BTreeMap>, + ) { + // parsing symbolicator is currently only responsible for processing use declarations + let Some(mod_ident_str) = parsing_mod_def_to_map_key(self.pkg_addresses, mod_def) else { + return; + }; + assert!(self.current_mod_ident_str.is_none()); + self.current_mod_ident_str = Some(mod_ident_str.clone()); + + let use_defs = mod_use_defs.remove(&mod_ident_str).unwrap(); + let old_defs = std::mem::replace(&mut self.use_defs, use_defs); + let alias_lengths: BTreeMap = BTreeMap::new(); + let old_alias_lengths = std::mem::replace(&mut self.alias_lengths, alias_lengths); + + mod_def + .attributes + .iter() + .for_each(|sp!(_, attrs)| attrs.iter().for_each(|a| self.attr_symbols(a.clone()))); + + for m in &mod_def.members { + use P::ModuleMember as MM; + match m { + MM::Function(fun) => { + if ignored_function(fun.name.value()) { + continue; + } + + // Unit returns span the entire function signature, so we process them first + // for cursor ordering. + self.type_symbols(&fun.signature.return_type); + + // If the cursor is in this item, mark that down. + // This may be overridden by the recursion below. + if let Some(cursor) = &mut self.cursor { + if fun.name.loc().contains(&cursor.loc) { + cursor.position = CursorPosition::DefName; + debug_assert!(cursor.defn_name.is_none()); + cursor.defn_name = Some(CursorDefinition::Function(fun.name)); + } else if fun.loc.contains(&cursor.loc) { + cursor.defn_name = Some(CursorDefinition::Function(fun.name)); + } + }; + + fun.attributes.iter().for_each(|sp!(_, attrs)| { + attrs.iter().for_each(|a| self.attr_symbols(a.clone())) + }); + + for (_, x, t) in fun.signature.parameters.iter() { + update_cursor!(IDENT, self.cursor, x, Parameter); + self.type_symbols(t) + } + + if fun.macro_.is_some() { + // we currently do not process macro function bodies + // in the parsing symbolicator (and do very limited + // processing in typing symbolicator) + continue; + } + if let P::FunctionBody_::Defined(seq) = &fun.body.value { + self.seq_symbols(seq); + }; + } + MM::Struct(sdef) => { + // If the cursor is in this item, mark that down. + // This may be overridden by the recursion below. + if let Some(cursor) = &mut self.cursor { + if sdef.name.loc().contains(&cursor.loc) { + cursor.position = CursorPosition::DefName; + debug_assert!(cursor.defn_name.is_none()); + cursor.defn_name = Some(CursorDefinition::Struct(sdef.name)); + } else if sdef.loc.contains(&cursor.loc) { + cursor.defn_name = Some(CursorDefinition::Struct(sdef.name)); + } + }; + + sdef.attributes.iter().for_each(|sp!(_, attrs)| { + attrs.iter().for_each(|a| self.attr_symbols(a.clone())) + }); + + match &sdef.fields { + P::StructFields::Named(v) => v.iter().for_each(|(x, t)| { + self.field_defn(x); + self.type_symbols(t) + }), + P::StructFields::Positional(v) => { + v.iter().for_each(|t| self.type_symbols(t)) + } + P::StructFields::Native(_) => (), + } + } + MM::Enum(edef) => { + // If the cursor is in this item, mark that down. + // This may be overridden by the recursion below. + if let Some(cursor) = &mut self.cursor { + if edef.name.loc().contains(&cursor.loc) { + cursor.position = CursorPosition::DefName; + debug_assert!(cursor.defn_name.is_none()); + cursor.defn_name = Some(CursorDefinition::Enum(edef.name)); + } else if edef.loc.contains(&cursor.loc) { + cursor.defn_name = Some(CursorDefinition::Enum(edef.name)); + } + }; + + edef.attributes.iter().for_each(|sp!(_, attrs)| { + attrs.iter().for_each(|a| self.attr_symbols(a.clone())) + }); + + let P::EnumDefinition { variants, .. } = edef; + for variant in variants { + let P::VariantDefinition { fields, .. } = variant; + match fields { + P::VariantFields::Named(v) => v.iter().for_each(|(x, t)| { + self.field_defn(x); + self.type_symbols(t) + }), + P::VariantFields::Positional(v) => { + v.iter().for_each(|t| self.type_symbols(t)) + } + P::VariantFields::Empty => (), + } + } + } + MM::Use(use_decl) => self.use_decl_symbols(use_decl), + MM::Friend(fdecl) => self.chain_symbols(&fdecl.friend), + MM::Constant(c) => { + // If the cursor is in this item, mark that down. + // This may be overridden by the recursion below. + if let Some(cursor) = &mut self.cursor { + if c.name.loc().contains(&cursor.loc) { + cursor.position = CursorPosition::DefName; + debug_assert!(cursor.defn_name.is_none()); + cursor.defn_name = Some(CursorDefinition::Constant(c.name)); + } else if c.loc.contains(&cursor.loc) { + cursor.defn_name = Some(CursorDefinition::Constant(c.name)); + } + }; + + c.attributes.iter().for_each(|sp!(_, attrs)| { + attrs.iter().for_each(|a| self.attr_symbols(a.clone())) + }); + + self.type_symbols(&c.signature); + self.exp_symbols(&c.value); + } + MM::Spec(_) => (), + } + } + self.current_mod_ident_str = None; + let processed_defs = std::mem::replace(&mut self.use_defs, old_defs); + mod_use_defs.insert(mod_ident_str.clone(), processed_defs); + let processed_alias_lengths = std::mem::replace(&mut self.alias_lengths, old_alias_lengths); + mod_to_alias_lengths.insert(mod_ident_str, processed_alias_lengths); + } + + /// Get symbols for a sequence item + fn seq_item_symbols(&mut self, seq_item: &P::SequenceItem) { + use P::SequenceItem_ as I; + + // If the cursor is in this item, mark that down. + // This may be overridden by the recursion below. + update_cursor!(self.cursor, seq_item, SeqItem); + + match &seq_item.value { + I::Seq(e) => self.exp_symbols(e), + I::Declare(v, to) => { + v.value + .iter() + .for_each(|bind| self.bind_symbols(bind, to.is_some())); + if let Some(t) = to { + self.type_symbols(t); + } + } + I::Bind(v, to, e) => { + v.value + .iter() + .for_each(|bind| self.bind_symbols(bind, to.is_some())); + if let Some(t) = to { + self.type_symbols(t); + } + self.exp_symbols(e); + } + } + } + + fn path_entry_symbols(&mut self, path: &P::PathEntry) { + let P::PathEntry { + name: _, + tyargs, + is_macro: _, + } = path; + if let Some(sp!(_, tyargs)) = tyargs { + tyargs.iter().for_each(|t| self.type_symbols(t)); + } + } + + fn root_path_entry_symbols(&mut self, path: &P::RootPathEntry) { + let P::RootPathEntry { + name: _, + tyargs, + is_macro: _, + } = path; + if let Some(sp!(_, tyargs)) = tyargs { + tyargs.iter().for_each(|t| self.type_symbols(t)); + } + } + + /// Get symbols for an expression + fn exp_symbols(&mut self, exp: &P::Exp) { + use P::Exp_ as E; + fn last_chain_symbol_loc(sp!(_, chain): &P::NameAccessChain) -> Loc { + use P::NameAccessChain_ as NA; + match chain { + NA::Single(entry) => entry.name.loc, + NA::Path(path) => { + if path.entries.is_empty() { + path.root.name.loc + } else { + path.entries.last().unwrap().name.loc + } + } + } + } + + // If the cursor is in this item, mark that down. + // This may be overridden by the recursion below. + update_cursor!(self.cursor, exp, Exp); + + match &exp.value { + E::Move(_, e) => self.exp_symbols(e), + E::Copy(_, e) => self.exp_symbols(e), + E::Name(chain) => self.chain_symbols(chain), + E::Call(chain, v) => { + self.chain_symbols(chain); + v.value.iter().for_each(|e| self.exp_symbols(e)); + assert!(self.current_mod_ident_str.is_some()); + if let Some(mod_defs) = self + .mod_outer_defs + .get_mut(&self.current_mod_ident_str.clone().unwrap()) + { + mod_defs.call_infos.insert( + last_chain_symbol_loc(chain), + CallInfo::new(/* do_call */ false, &v.value), + ); + }; + } + E::Pack(chain, v) => { + self.chain_symbols(chain); + v.iter().for_each(|(_, e)| self.exp_symbols(e)); + } + E::Vector(_, vo, v) => { + if let Some(v) = vo { + v.iter().for_each(|t| self.type_symbols(t)); + } + v.value.iter().for_each(|e| self.exp_symbols(e)); + } + E::IfElse(e1, e2, oe) => { + self.exp_symbols(e1); + self.exp_symbols(e2); + if let Some(e) = oe.as_ref() { + self.exp_symbols(e) + } + } + E::Match(e, sp!(_, v)) => { + self.exp_symbols(e); + v.iter().for_each(|sp!(_, arm)| { + self.match_pattern_symbols(&arm.pattern); + if let Some(g) = &arm.guard { + self.exp_symbols(g); + } + self.exp_symbols(&arm.rhs); + }) + } + E::While(e1, e2) => { + self.exp_symbols(e1); + self.exp_symbols(e2); + } + E::Loop(e) => self.exp_symbols(e), + E::Labeled(_, e) => self.exp_symbols(e), + E::Block(seq) => self.seq_symbols(seq), + E::Lambda(sp!(_, bindings), to, e) => { + for (sp!(_, v), bto) in bindings { + if let Some(bt) = bto { + self.type_symbols(bt); + } + v.iter() + .for_each(|bind| self.bind_symbols(bind, to.is_some())); + } + if let Some(t) = to { + self.type_symbols(t); + } + self.exp_symbols(e); + } + E::ExpList(l) => l.iter().for_each(|e| self.exp_symbols(e)), + E::Parens(e) => self.exp_symbols(e), + E::Assign(e1, e2) => { + self.exp_symbols(e1); + self.exp_symbols(e2); + } + E::Abort(e) => self.exp_symbols(e), + E::Return(_, oe) => { + if let Some(e) = oe.as_ref() { + self.exp_symbols(e) + } + } + E::Break(_, oe) => { + if let Some(e) = oe.as_ref() { + self.exp_symbols(e) + } + } + E::Dereference(e) => self.exp_symbols(e), + E::UnaryExp(_, e) => self.exp_symbols(e), + E::BinopExp(e1, _, e2) => { + self.exp_symbols(e1); + self.exp_symbols(e2); + } + E::Borrow(_, e) => self.exp_symbols(e), + E::Dot(e, _) => self.exp_symbols(e), + E::DotCall(e, name, _, vo, v) => { + self.exp_symbols(e); + if let Some(v) = vo { + v.iter().for_each(|t| self.type_symbols(t)); + } + v.value.iter().for_each(|e| self.exp_symbols(e)); + assert!(self.current_mod_ident_str.is_some()); + if let Some(mod_defs) = self + .mod_outer_defs + .get_mut(&self.current_mod_ident_str.clone().unwrap()) + { + mod_defs + .call_infos + .insert(name.loc, CallInfo::new(/* do_call */ true, &v.value)); + }; + } + E::Index(e, v) => { + self.exp_symbols(e); + v.value.iter().for_each(|e| self.exp_symbols(e)); + } + E::Cast(e, t) => { + self.exp_symbols(e); + self.type_symbols(t); + } + E::Annotate(e, t) => { + self.exp_symbols(e); + self.type_symbols(t); + } + E::DotUnresolved(_, e) => self.exp_symbols(e), + E::Value(_) + | E::Quant(..) + | E::Unit + | E::Continue(_) + | E::Spec(_) + | E::UnresolvedError => (), + } + } + + fn match_pattern_symbols(&mut self, sp!(_, pattern): &P::MatchPattern) { + use P::MatchPattern_ as MP; + match pattern { + MP::PositionalConstructor(chain, sp!(_, v)) => { + self.chain_symbols(chain); + v.iter().for_each(|e| { + if let P::Ellipsis::Binder(m) = e { + self.match_pattern_symbols(m); + } + }) + } + MP::FieldConstructor(chain, sp!(_, v)) => { + self.chain_symbols(chain); + v.iter().for_each(|e| { + if let P::Ellipsis::Binder((_, m)) = e { + self.match_pattern_symbols(m); + } + }) + } + MP::Name(_, chain) => { + self.chain_symbols(chain); + assert!(self.current_mod_ident_str.is_some()); + if let Some(mod_defs) = self + .mod_outer_defs + .get_mut(&self.current_mod_ident_str.clone().unwrap()) + { + mod_defs.untyped_defs.insert(chain.loc); + }; + } + MP::Or(m1, m2) => { + self.match_pattern_symbols(m2); + self.match_pattern_symbols(m1); + } + MP::At(_, m) => self.match_pattern_symbols(m), + MP::Literal(_) => (), + } + } + + /// Get symbols for a sequence + fn seq_symbols(&mut self, (use_decls, seq_items, _, oe): &P::Sequence) { + use_decls + .iter() + .for_each(|use_decl| self.use_decl_symbols(use_decl)); + + seq_items + .iter() + .for_each(|seq_item| self.seq_item_symbols(seq_item)); + if let Some(e) = oe.as_ref().as_ref() { + self.exp_symbols(e) + } + } + + /// Get symbols for a use declaration + fn use_decl_symbols(&mut self, use_decl: &P::UseDecl) { + use_decl + .attributes + .iter() + .for_each(|sp!(_, attrs)| attrs.iter().for_each(|a| self.attr_symbols(a.clone()))); + + update_cursor!(self.cursor, sp(use_decl.loc, use_decl.use_.clone()), Use); + + match &use_decl.use_ { + P::Use::ModuleUse(mod_ident, mod_use) => { + let mod_ident_str = + parsing_mod_ident_to_map_key(self.pkg_addresses, &mod_ident.value); + self.mod_name_symbol(&mod_ident.value.module, &mod_ident_str); + self.mod_use_symbols(mod_use, &mod_ident_str); + } + P::Use::NestedModuleUses(leading_name, uses) => { + for (mod_name, mod_use) in uses { + let mod_ident_str = parsing_leading_and_mod_names_to_map_key( + self.pkg_addresses, + *leading_name, + *mod_name, + ); + + self.mod_name_symbol(mod_name, &mod_ident_str); + self.mod_use_symbols(mod_use, &mod_ident_str); + } + } + P::Use::Fun { + visibility: _, + function, + ty, + method: _, + } => { + self.chain_symbols(function); + self.chain_symbols(ty); + } + P::Use::Partial { .. } => (), + } + } + + /// Get module name symbol + fn mod_name_symbol(&mut self, mod_name: &P::ModuleName, mod_ident_str: &String) { + let Some(mod_defs) = self.mod_outer_defs.get_mut(mod_ident_str) else { + return; + }; + let Some(mod_name_start) = loc_start_to_lsp_position_opt(self.files, &mod_name.loc()) + else { + debug_assert!(false); + return; + }; + self.use_defs.insert( + mod_name_start.line, + UseDef::new( + self.references, + &BTreeMap::new(), + mod_name.loc().file_hash(), + mod_name_start, + mod_defs.name_loc, + &mod_name.value(), + None, + ), + ); + } + + /// Get symbols for a module use + fn mod_use_symbols(&mut self, mod_use: &P::ModuleUse, mod_ident_str: &String) { + match mod_use { + P::ModuleUse::Module(Some(alias_name)) => { + self.mod_name_symbol(alias_name, mod_ident_str); + } + P::ModuleUse::Module(None) => (), // nothing more to do + P::ModuleUse::Members(v) => { + for (name, alias_opt) in v { + self.use_decl_member_symbols(mod_ident_str.clone(), name, alias_opt); + } + } + P::ModuleUse::Partial { .. } => (), + } + } + + /// Get symbols for a module member in the use declaration (can be a struct or a function) + fn use_decl_member_symbols( + &mut self, + mod_ident_str: String, + name: &Name, + alias_opt: &Option, + ) { + let Some(mod_defs) = self.mod_outer_defs.get(&mod_ident_str) else { + return; + }; + if let Some(mut ud) = add_member_use_def( + &name.value, + self.files, + mod_defs, + &name.value, + &name.loc, + self.references, + self.def_info, + &mut self.use_defs, + &BTreeMap::new(), + ) { + // it's a struct - add it for the alias as well + if let Some(alias) = alias_opt { + let Some(alias_start) = loc_start_to_lsp_position_opt(self.files, &alias.loc) + else { + debug_assert!(false); + return; + }; + ud.rename_use( + self.references, + alias.value, + alias_start, + alias.loc.file_hash(), + ); + self.use_defs.insert(alias_start.line, ud); + } + return; + } + if let Some(mut ud) = add_member_use_def( + &name.value, + self.files, + mod_defs, + &name.value, + &name.loc, + self.references, + self.def_info, + &mut self.use_defs, + &BTreeMap::new(), + ) { + // it's a function - add it for the alias as well + if let Some(alias) = alias_opt { + let Some(alias_start) = loc_start_to_lsp_position_opt(self.files, &alias.loc) + else { + debug_assert!(false); + return; + }; + ud.rename_use( + self.references, + alias.value, + alias_start, + alias.loc.file_hash(), + ); + self.use_defs.insert(alias_start.line, ud); + } + } + } + + /// Get symbols for a type + fn type_symbols(&mut self, type_: &P::Type) { + use P::Type_ as T; + + // If the cursor is in this item, mark that down. + // This may be overridden by the recursion below. + update_cursor!(self.cursor, type_, Type); + + match &type_.value { + T::Apply(chain) => { + self.chain_symbols(chain); + } + T::Ref(_, t) => self.type_symbols(t), + T::Fun(v, t) => { + v.iter().for_each(|t| self.type_symbols(t)); + self.type_symbols(t); + } + T::Multiple(v) => v.iter().for_each(|t| self.type_symbols(t)), + T::Unit => (), + T::UnresolvedError => (), + } + } + + /// Get symbols for a bind statement + fn bind_symbols(&mut self, bind: &P::Bind, explicitly_typed: bool) { + use P::Bind_ as B; + + // If the cursor is in this item, mark that down. + // This may be overridden by the recursion below. + update_cursor!(self.cursor, bind, Binding); + + match &bind.value { + B::Unpack(chain, bindings) => { + self.chain_symbols(chain); + match bindings { + P::FieldBindings::Named(v) => { + for symbol in v { + match symbol { + P::Ellipsis::Binder((_, x)) => self.bind_symbols(x, false), + P::Ellipsis::Ellipsis(_) => (), + } + } + } + P::FieldBindings::Positional(v) => { + for symbol in v.iter() { + match symbol { + P::Ellipsis::Binder(x) => self.bind_symbols(x, false), + P::Ellipsis::Ellipsis(_) => (), + } + } + } + } + } + B::Var(_, var) => { + if !explicitly_typed { + assert!(self.current_mod_ident_str.is_some()); + if let Some(mod_defs) = self + .mod_outer_defs + .get_mut(&self.current_mod_ident_str.clone().unwrap()) + { + mod_defs.untyped_defs.insert(var.loc()); + }; + } + } + } + } + + /// Get symbols for a name access chain + fn chain_symbols(&mut self, sp!(_, chain): &P::NameAccessChain) { + use P::NameAccessChain_ as NA; + // Record the length of all identifiers representing a potentially + // aliased module, struct, enum or function name in an access chain. + // We can conservatively record all identifiers as they are only + // accessed by-location so those irrelevant will never be queried. + match chain { + NA::Single(entry) => { + self.path_entry_symbols(entry); + if let Some(loc) = loc_start_to_lsp_position_opt(self.files, &entry.name.loc) { + self.alias_lengths.insert(loc, entry.name.value.len()); + }; + } + NA::Path(path) => { + let P::NamePath { + root, + entries, + is_incomplete: _, + } = path; + self.root_path_entry_symbols(root); + if let Some(root_loc) = loc_start_to_lsp_position_opt(self.files, &root.name.loc) { + if let P::LeadingNameAccess_::Name(n) = root.name.value { + self.alias_lengths.insert(root_loc, n.value.len()); + } + }; + entries.iter().for_each(|entry| { + self.path_entry_symbols(entry); + if let Some(loc) = loc_start_to_lsp_position_opt(self.files, &entry.name.loc) { + self.alias_lengths.insert(loc, entry.name.value.len()); + }; + }); + } + }; + } + + fn field_defn(&mut self, field: &P::Field) { + // If the cursor is in this item, mark that down. + update_cursor!(IDENT, self.cursor, field, FieldDefn); + } +} + +/// Produces module ident string of the form pkg::module to be used as a map key. +/// It's important that these are consistent between parsing AST and typed AST, +fn parsing_mod_ident_to_map_key( + pkg_addresses: &NamedAddressMap, + mod_ident: &P::ModuleIdent_, +) -> String { + format!( + "{}::{}", + parsed_address(mod_ident.address, pkg_addresses), + mod_ident.module + ) + .to_string() +} + +/// Produces module ident string of the form pkg::module to be used as a map key. +/// It's important that these are consistent between parsing AST and typed AST. +fn parsing_mod_def_to_map_key( + pkg_addresses: &NamedAddressMap, + mod_def: &P::ModuleDefinition, +) -> Option { + // we assume that modules are declared using the PkgName::ModName pattern (which seems to be the + // standard practice) and while Move allows other ways of defining modules (i.e., with address + // preceding a sequence of modules), this method is now deprecated. + // + // TODO: make this function simply return String when the other way of defining modules is + // removed + mod_def + .address + .map(|a| parsing_leading_and_mod_names_to_map_key(pkg_addresses, a, mod_def.name)) +} + +/// Produces module ident string of the form pkg::module to be used as a map key. +/// It's important that these are consistent between parsing AST and typed AST. +fn parsing_leading_and_mod_names_to_map_key( + pkg_addresses: &NamedAddressMap, + ln: P::LeadingNameAccess, + name: P::ModuleName, +) -> String { + format!("{}::{}", parsed_address(ln, pkg_addresses), name).to_string() +} + +/// Converts parsing AST's `LeadingNameAccess` to expansion AST's `Address` (similarly to +/// expansion::translate::top_level_address but disregarding the name portion of `Address` as we +/// only care about actual address here if it's available). We need this to be able to reliably +/// compare parsing AST's module identifier with expansion/typing AST's module identifier, even in +/// presence of module renaming (i.e., we cannot rely on module names if addresses are available). +fn parsed_address(ln: P::LeadingNameAccess, pkg_addresses: &NamedAddressMap) -> E::Address { + let sp!(loc, ln_) = ln; + match ln_ { + P::LeadingNameAccess_::AnonymousAddress(bytes) => E::Address::anonymous(loc, bytes), + P::LeadingNameAccess_::GlobalAddress(name) => E::Address::NamedUnassigned(name), + P::LeadingNameAccess_::Name(name) => match pkg_addresses.get(&name.value).copied() { + Some(addr) => E::Address::anonymous(loc, addr), + None => E::Address::NamedUnassigned(name), + }, + } +} diff --git a/external-crates/move/crates/move-analyzer/src/symbols.rs b/external-crates/move/crates/move-analyzer/src/symbols.rs index 46bb538efcee7..aae8c17cbf87b 100644 --- a/external-crates/move/crates/move-analyzer/src/symbols.rs +++ b/external-crates/move/crates/move-analyzer/src/symbols.rs @@ -5,10 +5,14 @@ //! and typed ASTs, in particular identifier definitions to be used for implementing go-to-def, //! go-to-references, and on-hover language server commands. //! -//! There are different structs that are used at different phases of the process, the -//! ParsingSymbolicator and Typing Symbolicator structs are used when building symbolication -//! information and the Symbols struct is summarizes the symbolication results and is used by the -//! language server find definitions and references. +//! The analysis starts with top-level module definitions being processed and then proceeds to +//! process parsed AST (parsing analysis) and typed AST (typing analysis) to gather all the required +//! information which is then summarized in the Symbols struct subsequently used by the language +//! server to find definitions, references, auto-completions, etc. Parsing analysis is largely +//! responsible for processing import statements (no longer available at the level of typed AST) and +//! typing analysis gathers remaining information. In particular, for local definitions, typing +//! analysis builds a scope stack, entering encountered definitions and matching uses to a +//! definition in the innermost scope. //! //! Here is a brief description of how the symbolication information is encoded. Each identifier in //! the source code of a given module is represented by its location (UseLoc struct): line number, @@ -45,17 +49,10 @@ //! We also associate all uses of an identifier with its definition to support //! go-to-references. This is done in a global map from an identifier location (DefLoc) to a set of //! use locations (UseLoc). -//! -//! Symbolication algorithm over typing AST first analyzes all top-level definitions from all -//! modules. ParsingSymbolicator then processes import statements (no longer available at the level -//! of typed AST) and TypingSymbolicator processes function bodies, as well as constant and struct -//! definitions. For local definitions, TypingSymbolicator builds a scope stack, entering -//! encountered definitions and matching uses to a definition in the innermost scope. - #![allow(clippy::non_canonical_partial_ord_impl)] use crate::{ - analysis::typing_analysis, + analysis::{parsing_analysis, typing_analysis}, compiler_info::CompilerInfo, context::Context, diagnostics::{lsp_diagnostics, lsp_empty_diagnostics}, @@ -100,7 +97,7 @@ use move_compiler::{ shared::{ files::{FileId, MappedFiles}, unique_map::UniqueMap, - Identifier, Name, NamedAddressMap, NamedAddressMaps, + Identifier, Name, NamedAddressMap, }, typing::{ ast::{ @@ -511,33 +508,6 @@ pub enum CursorDefinition { Struct(P::DatatypeName), Enum(P::DatatypeName), } -/// Data used during symbolication over parsed AST -pub struct ParsingSymbolicator<'a> { - /// Outermost definitions in a module (structs, consts, functions), keyd on a ModuleIdent - /// string so that we can access it regardless of the ModuleIdent representation - /// (e.g., in the parsing AST or in the typing AST) - mod_outer_defs: &'a mut BTreeMap, - /// Mapped file information for translating locations into positions - files: &'a MappedFiles, - /// Associates uses for a given definition to allow displaying all references - references: &'a mut References, - /// Additional information about definitions - def_info: &'a mut DefMap, - /// A UseDefMap for a given module (needs to be appropriately set before the module - /// processing starts) - use_defs: UseDefMap, - /// Current module identifier string (needs to be appropriately set before the module - /// processing starts) - current_mod_ident_str: Option, - /// Module name lengths in access paths for a given module (needs to be appropriately - /// set before the module processing starts) - alias_lengths: BTreeMap, - /// A per-package mapping from package names to their addresses (needs to be appropriately set - /// before the package processint starts) - pkg_addresses: &'a NamedAddressMap, - /// Cursor contextual information, computed as part of the traversal. - cursor: Option<&'a mut CursorContext>, -} type LineOffset = u32; @@ -1365,7 +1335,7 @@ impl UseDef { } /// Given a UseDef, modify just the use name and location (to make it represent an alias). - fn rename_use( + pub fn rename_use( &mut self, references: &mut References, new_name: Symbol, @@ -1817,7 +1787,7 @@ pub fn get_symbols( let mut file_use_defs = BTreeMap::new(); let mut mod_to_alias_lengths = BTreeMap::new(); - let mut parsing_symbolicator = ParsingSymbolicator { + let mut parsing_symbolicator = parsing_analysis::ParsingAnalysisContext { mod_outer_defs: &mut mod_outer_defs, files: &mapped_files, references: &mut references, @@ -2013,64 +1983,6 @@ fn file_sources( .collect() } -/// Produces module ident string of the form pkg::module to be used as a map key. -/// It's important that these are consistent between parsing AST and typed AST, -fn parsing_mod_ident_to_map_key( - pkg_addresses: &NamedAddressMap, - mod_ident: &P::ModuleIdent_, -) -> String { - format!( - "{}::{}", - parsed_address(mod_ident.address, pkg_addresses), - mod_ident.module - ) - .to_string() -} - -/// Produces module ident string of the form pkg::module to be used as a map key. -/// It's important that these are consistent between parsing AST and typed AST. -fn parsing_mod_def_to_map_key( - pkg_addresses: &NamedAddressMap, - mod_def: &P::ModuleDefinition, -) -> Option { - // we assume that modules are declared using the PkgName::ModName pattern (which seems to be the - // standard practice) and while Move allows other ways of defining modules (i.e., with address - // preceding a sequence of modules), this method is now deprecated. - // - // TODO: make this function simply return String when the other way of defining modules is - // removed - mod_def - .address - .map(|a| parsing_leading_and_mod_names_to_map_key(pkg_addresses, a, mod_def.name)) -} - -/// Produces module ident string of the form pkg::module to be used as a map key. -/// It's important that these are consistent between parsing AST and typed AST. -fn parsing_leading_and_mod_names_to_map_key( - pkg_addresses: &NamedAddressMap, - ln: P::LeadingNameAccess, - name: P::ModuleName, -) -> String { - format!("{}::{}", parsed_address(ln, pkg_addresses), name).to_string() -} - -/// Converts parsing AST's `LeadingNameAccess` to expansion AST's `Address` (similarly to -/// expansion::translate::top_level_address but disregarding the name portion of `Address` as we -/// only care about actual address here if it's available). We need this to be able to reliably -/// compare parsing AST's module identifier with expansion/typing AST's module identifier, even in -/// presence of module renaming (i.e., we cannot rely on module names if addresses are available). -fn parsed_address(ln: P::LeadingNameAccess, pkg_addresses: &NamedAddressMap) -> E::Address { - let sp!(loc, ln_) = ln; - match ln_ { - P::LeadingNameAccess_::AnonymousAddress(bytes) => E::Address::anonymous(loc, bytes), - P::LeadingNameAccess_::GlobalAddress(name) => E::Address::NamedUnassigned(name), - P::LeadingNameAccess_::Name(name) => match pkg_addresses.get(&name.value).copied() { - Some(addr) => E::Address::anonymous(loc, addr), - None => E::Address::NamedUnassigned(name), - }, - } -} - /// Produces module ident string of the form pkg::module to be used as a map key /// It's important that these are consistent between parsing AST and typed AST. pub fn expansion_mod_ident_to_map_key(mod_ident: &E::ModuleIdent_) -> String { @@ -2095,18 +2007,6 @@ pub fn empty_symbols() -> Symbols { } } -/// Some functions defined in a module need to be ignored. -fn ignored_function(name: Symbol) -> bool { - // In test mode (that's how IDE compiles Move source files), - // the compiler inserts an dummy function preventing preventing - // publishing of modules compiled in test mode. We need to - // ignore its definition to avoid spurious on-hover display - // of this function's info whe hovering close to `module` keyword. - name == UNIT_TEST_POISON_FUN_NAME -} - -/// Main AST traversal functions - fn field_defs_and_types( datatype_name: Symbol, datatype_loc: Loc, @@ -2154,6 +2054,15 @@ fn datatype_type_params(data_tparams: &[DatatypeTypeParameter]) -> Vec<(Type, /* .collect() } +/// Some functions defined in a module need to be ignored. +pub fn ignored_function(name: Symbol) -> bool { + // In test mode (that's how IDE compiles Move source files), the compiler inserts an dummy + // function preventing publishing of modules compiled in test mode. We need to ignore its + // definition to avoid spurious on-hover display of this function's info whe hovering close to + // `module` keyword. + name == UNIT_TEST_POISON_FUN_NAME +} + /// Get symbols for outer definitions in the module (functions, structs, and consts) fn get_mod_outer_defs( loc: &Loc, @@ -2405,748 +2314,6 @@ fn get_mod_outer_defs( (mod_defs, use_def_map) } -macro_rules! update_cursor { - ($cursor:expr, $subject:expr, $kind:ident) => { - if let Some(cursor) = &mut $cursor { - if $subject.loc.contains(&cursor.loc) { - cursor.position = CursorPosition::$kind($subject.clone()); - } - }; - }; - (IDENT, $cursor:expr, $subject:expr, $kind:ident) => { - if let Some(cursor) = &mut $cursor { - if $subject.loc().contains(&cursor.loc) { - cursor.position = CursorPosition::$kind($subject.clone()); - } - }; - }; -} - -impl<'a> ParsingSymbolicator<'a> { - /// Get symbols for the whole program - fn prog_symbols( - &mut self, - prog: &'a P::Program, - mod_use_defs: &mut BTreeMap, - mod_to_alias_lengths: &mut BTreeMap>, - ) { - prog.source_definitions.iter().for_each(|pkg_def| { - self.pkg_symbols( - &prog.named_address_maps, - pkg_def, - mod_use_defs, - mod_to_alias_lengths, - ) - }); - prog.lib_definitions.iter().for_each(|pkg_def| { - self.pkg_symbols( - &prog.named_address_maps, - pkg_def, - mod_use_defs, - mod_to_alias_lengths, - ) - }); - } - - /// Get symbols for the whole package - fn pkg_symbols( - &mut self, - pkg_address_maps: &'a NamedAddressMaps, - pkg_def: &P::PackageDefinition, - mod_use_defs: &mut BTreeMap, - mod_to_alias_lengths: &mut BTreeMap>, - ) { - if let P::Definition::Module(mod_def) = &pkg_def.def { - let pkg_addresses = pkg_address_maps.get(pkg_def.named_address_map); - let old_addresses = std::mem::replace(&mut self.pkg_addresses, pkg_addresses); - self.mod_symbols(mod_def, mod_use_defs, mod_to_alias_lengths); - self.current_mod_ident_str = None; - let _ = std::mem::replace(&mut self.pkg_addresses, old_addresses); - } - } - - fn attr_symbols(&mut self, sp!(_, attr): P::Attribute) { - use P::Attribute_ as A; - match attr { - A::Name(_) => (), - A::Assigned(_, v) => { - update_cursor!(self.cursor, *v, Attribute); - } - A::Parameterized(_, sp!(_, attributes)) => { - attributes.iter().for_each(|a| self.attr_symbols(a.clone())) - } - } - } - - /// Get symbols for the whole module - fn mod_symbols( - &mut self, - mod_def: &P::ModuleDefinition, - mod_use_defs: &mut BTreeMap, - mod_to_alias_lengths: &mut BTreeMap>, - ) { - // parsing symbolicator is currently only responsible for processing use declarations - let Some(mod_ident_str) = parsing_mod_def_to_map_key(self.pkg_addresses, mod_def) else { - return; - }; - assert!(self.current_mod_ident_str.is_none()); - self.current_mod_ident_str = Some(mod_ident_str.clone()); - - let use_defs = mod_use_defs.remove(&mod_ident_str).unwrap(); - let old_defs = std::mem::replace(&mut self.use_defs, use_defs); - let alias_lengths: BTreeMap = BTreeMap::new(); - let old_alias_lengths = std::mem::replace(&mut self.alias_lengths, alias_lengths); - - mod_def - .attributes - .iter() - .for_each(|sp!(_, attrs)| attrs.iter().for_each(|a| self.attr_symbols(a.clone()))); - - for m in &mod_def.members { - use P::ModuleMember as MM; - match m { - MM::Function(fun) => { - if ignored_function(fun.name.value()) { - continue; - } - - // Unit returns span the entire function signature, so we process them first - // for cursor ordering. - self.type_symbols(&fun.signature.return_type); - - // If the cursor is in this item, mark that down. - // This may be overridden by the recursion below. - if let Some(cursor) = &mut self.cursor { - if fun.name.loc().contains(&cursor.loc) { - cursor.position = CursorPosition::DefName; - debug_assert!(cursor.defn_name.is_none()); - cursor.defn_name = Some(CursorDefinition::Function(fun.name)); - } else if fun.loc.contains(&cursor.loc) { - cursor.defn_name = Some(CursorDefinition::Function(fun.name)); - } - }; - - fun.attributes.iter().for_each(|sp!(_, attrs)| { - attrs.iter().for_each(|a| self.attr_symbols(a.clone())) - }); - - for (_, x, t) in fun.signature.parameters.iter() { - update_cursor!(IDENT, self.cursor, x, Parameter); - self.type_symbols(t) - } - - if fun.macro_.is_some() { - // we currently do not process macro function bodies - // in the parsing symbolicator (and do very limited - // processing in typing symbolicator) - continue; - } - if let P::FunctionBody_::Defined(seq) = &fun.body.value { - self.seq_symbols(seq); - }; - } - MM::Struct(sdef) => { - // If the cursor is in this item, mark that down. - // This may be overridden by the recursion below. - if let Some(cursor) = &mut self.cursor { - if sdef.name.loc().contains(&cursor.loc) { - cursor.position = CursorPosition::DefName; - debug_assert!(cursor.defn_name.is_none()); - cursor.defn_name = Some(CursorDefinition::Struct(sdef.name)); - } else if sdef.loc.contains(&cursor.loc) { - cursor.defn_name = Some(CursorDefinition::Struct(sdef.name)); - } - }; - - sdef.attributes.iter().for_each(|sp!(_, attrs)| { - attrs.iter().for_each(|a| self.attr_symbols(a.clone())) - }); - - match &sdef.fields { - P::StructFields::Named(v) => v.iter().for_each(|(x, t)| { - self.field_defn(x); - self.type_symbols(t) - }), - P::StructFields::Positional(v) => { - v.iter().for_each(|t| self.type_symbols(t)) - } - P::StructFields::Native(_) => (), - } - } - MM::Enum(edef) => { - // If the cursor is in this item, mark that down. - // This may be overridden by the recursion below. - if let Some(cursor) = &mut self.cursor { - if edef.name.loc().contains(&cursor.loc) { - cursor.position = CursorPosition::DefName; - debug_assert!(cursor.defn_name.is_none()); - cursor.defn_name = Some(CursorDefinition::Enum(edef.name)); - } else if edef.loc.contains(&cursor.loc) { - cursor.defn_name = Some(CursorDefinition::Enum(edef.name)); - } - }; - - edef.attributes.iter().for_each(|sp!(_, attrs)| { - attrs.iter().for_each(|a| self.attr_symbols(a.clone())) - }); - - let P::EnumDefinition { variants, .. } = edef; - for variant in variants { - let P::VariantDefinition { fields, .. } = variant; - match fields { - P::VariantFields::Named(v) => v.iter().for_each(|(x, t)| { - self.field_defn(x); - self.type_symbols(t) - }), - P::VariantFields::Positional(v) => { - v.iter().for_each(|t| self.type_symbols(t)) - } - P::VariantFields::Empty => (), - } - } - } - MM::Use(use_decl) => self.use_decl_symbols(use_decl), - MM::Friend(fdecl) => self.chain_symbols(&fdecl.friend), - MM::Constant(c) => { - // If the cursor is in this item, mark that down. - // This may be overridden by the recursion below. - if let Some(cursor) = &mut self.cursor { - if c.name.loc().contains(&cursor.loc) { - cursor.position = CursorPosition::DefName; - debug_assert!(cursor.defn_name.is_none()); - cursor.defn_name = Some(CursorDefinition::Constant(c.name)); - } else if c.loc.contains(&cursor.loc) { - cursor.defn_name = Some(CursorDefinition::Constant(c.name)); - } - }; - - c.attributes.iter().for_each(|sp!(_, attrs)| { - attrs.iter().for_each(|a| self.attr_symbols(a.clone())) - }); - - self.type_symbols(&c.signature); - self.exp_symbols(&c.value); - } - MM::Spec(_) => (), - } - } - self.current_mod_ident_str = None; - let processed_defs = std::mem::replace(&mut self.use_defs, old_defs); - mod_use_defs.insert(mod_ident_str.clone(), processed_defs); - let processed_alias_lengths = std::mem::replace(&mut self.alias_lengths, old_alias_lengths); - mod_to_alias_lengths.insert(mod_ident_str, processed_alias_lengths); - } - - /// Get symbols for a sequence item - fn seq_item_symbols(&mut self, seq_item: &P::SequenceItem) { - use P::SequenceItem_ as I; - - // If the cursor is in this item, mark that down. - // This may be overridden by the recursion below. - update_cursor!(self.cursor, seq_item, SeqItem); - - match &seq_item.value { - I::Seq(e) => self.exp_symbols(e), - I::Declare(v, to) => { - v.value - .iter() - .for_each(|bind| self.bind_symbols(bind, to.is_some())); - if let Some(t) = to { - self.type_symbols(t); - } - } - I::Bind(v, to, e) => { - v.value - .iter() - .for_each(|bind| self.bind_symbols(bind, to.is_some())); - if let Some(t) = to { - self.type_symbols(t); - } - self.exp_symbols(e); - } - } - } - - fn path_entry_symbols(&mut self, path: &P::PathEntry) { - let P::PathEntry { - name: _, - tyargs, - is_macro: _, - } = path; - if let Some(sp!(_, tyargs)) = tyargs { - tyargs.iter().for_each(|t| self.type_symbols(t)); - } - } - - fn root_path_entry_symbols(&mut self, path: &P::RootPathEntry) { - let P::RootPathEntry { - name: _, - tyargs, - is_macro: _, - } = path; - if let Some(sp!(_, tyargs)) = tyargs { - tyargs.iter().for_each(|t| self.type_symbols(t)); - } - } - - /// Get symbols for an expression - fn exp_symbols(&mut self, exp: &P::Exp) { - use P::Exp_ as E; - fn last_chain_symbol_loc(sp!(_, chain): &P::NameAccessChain) -> Loc { - use P::NameAccessChain_ as NA; - match chain { - NA::Single(entry) => entry.name.loc, - NA::Path(path) => { - if path.entries.is_empty() { - path.root.name.loc - } else { - path.entries.last().unwrap().name.loc - } - } - } - } - - // If the cursor is in this item, mark that down. - // This may be overridden by the recursion below. - update_cursor!(self.cursor, exp, Exp); - - match &exp.value { - E::Move(_, e) => self.exp_symbols(e), - E::Copy(_, e) => self.exp_symbols(e), - E::Name(chain) => self.chain_symbols(chain), - E::Call(chain, v) => { - self.chain_symbols(chain); - v.value.iter().for_each(|e| self.exp_symbols(e)); - assert!(self.current_mod_ident_str.is_some()); - if let Some(mod_defs) = self - .mod_outer_defs - .get_mut(&self.current_mod_ident_str.clone().unwrap()) - { - mod_defs.call_infos.insert( - last_chain_symbol_loc(chain), - CallInfo::new(/* do_call */ false, &v.value), - ); - }; - } - E::Pack(chain, v) => { - self.chain_symbols(chain); - v.iter().for_each(|(_, e)| self.exp_symbols(e)); - } - E::Vector(_, vo, v) => { - if let Some(v) = vo { - v.iter().for_each(|t| self.type_symbols(t)); - } - v.value.iter().for_each(|e| self.exp_symbols(e)); - } - E::IfElse(e1, e2, oe) => { - self.exp_symbols(e1); - self.exp_symbols(e2); - if let Some(e) = oe.as_ref() { - self.exp_symbols(e) - } - } - E::Match(e, sp!(_, v)) => { - self.exp_symbols(e); - v.iter().for_each(|sp!(_, arm)| { - self.match_pattern_symbols(&arm.pattern); - if let Some(g) = &arm.guard { - self.exp_symbols(g); - } - self.exp_symbols(&arm.rhs); - }) - } - E::While(e1, e2) => { - self.exp_symbols(e1); - self.exp_symbols(e2); - } - E::Loop(e) => self.exp_symbols(e), - E::Labeled(_, e) => self.exp_symbols(e), - E::Block(seq) => self.seq_symbols(seq), - E::Lambda(sp!(_, bindings), to, e) => { - for (sp!(_, v), bto) in bindings { - if let Some(bt) = bto { - self.type_symbols(bt); - } - v.iter() - .for_each(|bind| self.bind_symbols(bind, to.is_some())); - } - if let Some(t) = to { - self.type_symbols(t); - } - self.exp_symbols(e); - } - E::ExpList(l) => l.iter().for_each(|e| self.exp_symbols(e)), - E::Parens(e) => self.exp_symbols(e), - E::Assign(e1, e2) => { - self.exp_symbols(e1); - self.exp_symbols(e2); - } - E::Abort(e) => self.exp_symbols(e), - E::Return(_, oe) => { - if let Some(e) = oe.as_ref() { - self.exp_symbols(e) - } - } - E::Break(_, oe) => { - if let Some(e) = oe.as_ref() { - self.exp_symbols(e) - } - } - E::Dereference(e) => self.exp_symbols(e), - E::UnaryExp(_, e) => self.exp_symbols(e), - E::BinopExp(e1, _, e2) => { - self.exp_symbols(e1); - self.exp_symbols(e2); - } - E::Borrow(_, e) => self.exp_symbols(e), - E::Dot(e, _) => self.exp_symbols(e), - E::DotCall(e, name, _, vo, v) => { - self.exp_symbols(e); - if let Some(v) = vo { - v.iter().for_each(|t| self.type_symbols(t)); - } - v.value.iter().for_each(|e| self.exp_symbols(e)); - assert!(self.current_mod_ident_str.is_some()); - if let Some(mod_defs) = self - .mod_outer_defs - .get_mut(&self.current_mod_ident_str.clone().unwrap()) - { - mod_defs - .call_infos - .insert(name.loc, CallInfo::new(/* do_call */ true, &v.value)); - }; - } - E::Index(e, v) => { - self.exp_symbols(e); - v.value.iter().for_each(|e| self.exp_symbols(e)); - } - E::Cast(e, t) => { - self.exp_symbols(e); - self.type_symbols(t); - } - E::Annotate(e, t) => { - self.exp_symbols(e); - self.type_symbols(t); - } - E::DotUnresolved(_, e) => self.exp_symbols(e), - E::Value(_) - | E::Quant(..) - | E::Unit - | E::Continue(_) - | E::Spec(_) - | E::UnresolvedError => (), - } - } - - fn match_pattern_symbols(&mut self, sp!(_, pattern): &P::MatchPattern) { - use P::MatchPattern_ as MP; - match pattern { - MP::PositionalConstructor(chain, sp!(_, v)) => { - self.chain_symbols(chain); - v.iter().for_each(|e| { - if let P::Ellipsis::Binder(m) = e { - self.match_pattern_symbols(m); - } - }) - } - MP::FieldConstructor(chain, sp!(_, v)) => { - self.chain_symbols(chain); - v.iter().for_each(|e| { - if let P::Ellipsis::Binder((_, m)) = e { - self.match_pattern_symbols(m); - } - }) - } - MP::Name(_, chain) => { - self.chain_symbols(chain); - assert!(self.current_mod_ident_str.is_some()); - if let Some(mod_defs) = self - .mod_outer_defs - .get_mut(&self.current_mod_ident_str.clone().unwrap()) - { - mod_defs.untyped_defs.insert(chain.loc); - }; - } - MP::Or(m1, m2) => { - self.match_pattern_symbols(m2); - self.match_pattern_symbols(m1); - } - MP::At(_, m) => self.match_pattern_symbols(m), - MP::Literal(_) => (), - } - } - - /// Get symbols for a sequence - fn seq_symbols(&mut self, (use_decls, seq_items, _, oe): &P::Sequence) { - use_decls - .iter() - .for_each(|use_decl| self.use_decl_symbols(use_decl)); - - seq_items - .iter() - .for_each(|seq_item| self.seq_item_symbols(seq_item)); - if let Some(e) = oe.as_ref().as_ref() { - self.exp_symbols(e) - } - } - - /// Get symbols for a use declaration - fn use_decl_symbols(&mut self, use_decl: &P::UseDecl) { - use_decl - .attributes - .iter() - .for_each(|sp!(_, attrs)| attrs.iter().for_each(|a| self.attr_symbols(a.clone()))); - - update_cursor!(self.cursor, sp(use_decl.loc, use_decl.use_.clone()), Use); - - match &use_decl.use_ { - P::Use::ModuleUse(mod_ident, mod_use) => { - let mod_ident_str = - parsing_mod_ident_to_map_key(self.pkg_addresses, &mod_ident.value); - self.mod_name_symbol(&mod_ident.value.module, &mod_ident_str); - self.mod_use_symbols(mod_use, &mod_ident_str); - } - P::Use::NestedModuleUses(leading_name, uses) => { - for (mod_name, mod_use) in uses { - let mod_ident_str = parsing_leading_and_mod_names_to_map_key( - self.pkg_addresses, - *leading_name, - *mod_name, - ); - - self.mod_name_symbol(mod_name, &mod_ident_str); - self.mod_use_symbols(mod_use, &mod_ident_str); - } - } - P::Use::Fun { - visibility: _, - function, - ty, - method: _, - } => { - self.chain_symbols(function); - self.chain_symbols(ty); - } - P::Use::Partial { .. } => (), - } - } - - /// Get module name symbol - fn mod_name_symbol(&mut self, mod_name: &P::ModuleName, mod_ident_str: &String) { - let Some(mod_defs) = self.mod_outer_defs.get_mut(mod_ident_str) else { - return; - }; - let Some(mod_name_start) = loc_start_to_lsp_position_opt(self.files, &mod_name.loc()) - else { - debug_assert!(false); - return; - }; - self.use_defs.insert( - mod_name_start.line, - UseDef::new( - self.references, - &BTreeMap::new(), - mod_name.loc().file_hash(), - mod_name_start, - mod_defs.name_loc, - &mod_name.value(), - None, - ), - ); - } - - /// Get symbols for a module use - fn mod_use_symbols(&mut self, mod_use: &P::ModuleUse, mod_ident_str: &String) { - match mod_use { - P::ModuleUse::Module(Some(alias_name)) => { - self.mod_name_symbol(alias_name, mod_ident_str); - } - P::ModuleUse::Module(None) => (), // nothing more to do - P::ModuleUse::Members(v) => { - for (name, alias_opt) in v { - self.use_decl_member_symbols(mod_ident_str.clone(), name, alias_opt); - } - } - P::ModuleUse::Partial { .. } => (), - } - } - - /// Get symbols for a module member in the use declaration (can be a struct or a function) - fn use_decl_member_symbols( - &mut self, - mod_ident_str: String, - name: &Name, - alias_opt: &Option, - ) { - let Some(mod_defs) = self.mod_outer_defs.get(&mod_ident_str) else { - return; - }; - if let Some(mut ud) = add_member_use_def( - &name.value, - self.files, - mod_defs, - &name.value, - &name.loc, - self.references, - self.def_info, - &mut self.use_defs, - &BTreeMap::new(), - ) { - // it's a struct - add it for the alias as well - if let Some(alias) = alias_opt { - let Some(alias_start) = loc_start_to_lsp_position_opt(self.files, &alias.loc) - else { - debug_assert!(false); - return; - }; - ud.rename_use( - self.references, - alias.value, - alias_start, - alias.loc.file_hash(), - ); - self.use_defs.insert(alias_start.line, ud); - } - return; - } - if let Some(mut ud) = add_member_use_def( - &name.value, - self.files, - mod_defs, - &name.value, - &name.loc, - self.references, - self.def_info, - &mut self.use_defs, - &BTreeMap::new(), - ) { - // it's a function - add it for the alias as well - if let Some(alias) = alias_opt { - let Some(alias_start) = loc_start_to_lsp_position_opt(self.files, &alias.loc) - else { - debug_assert!(false); - return; - }; - ud.rename_use( - self.references, - alias.value, - alias_start, - alias.loc.file_hash(), - ); - self.use_defs.insert(alias_start.line, ud); - } - } - } - - /// Get symbols for a type - fn type_symbols(&mut self, type_: &P::Type) { - use P::Type_ as T; - - // If the cursor is in this item, mark that down. - // This may be overridden by the recursion below. - update_cursor!(self.cursor, type_, Type); - - match &type_.value { - T::Apply(chain) => { - self.chain_symbols(chain); - } - T::Ref(_, t) => self.type_symbols(t), - T::Fun(v, t) => { - v.iter().for_each(|t| self.type_symbols(t)); - self.type_symbols(t); - } - T::Multiple(v) => v.iter().for_each(|t| self.type_symbols(t)), - T::Unit => (), - T::UnresolvedError => (), - } - } - - /// Get symbols for a bind statement - fn bind_symbols(&mut self, bind: &P::Bind, explicitly_typed: bool) { - use P::Bind_ as B; - - // If the cursor is in this item, mark that down. - // This may be overridden by the recursion below. - update_cursor!(self.cursor, bind, Binding); - - match &bind.value { - B::Unpack(chain, bindings) => { - self.chain_symbols(chain); - match bindings { - P::FieldBindings::Named(v) => { - for symbol in v { - match symbol { - P::Ellipsis::Binder((_, x)) => self.bind_symbols(x, false), - P::Ellipsis::Ellipsis(_) => (), - } - } - } - P::FieldBindings::Positional(v) => { - for symbol in v.iter() { - match symbol { - P::Ellipsis::Binder(x) => self.bind_symbols(x, false), - P::Ellipsis::Ellipsis(_) => (), - } - } - } - } - } - B::Var(_, var) => { - if !explicitly_typed { - assert!(self.current_mod_ident_str.is_some()); - if let Some(mod_defs) = self - .mod_outer_defs - .get_mut(&self.current_mod_ident_str.clone().unwrap()) - { - mod_defs.untyped_defs.insert(var.loc()); - }; - } - } - } - } - - /// Get symbols for a name access chain - fn chain_symbols(&mut self, sp!(_, chain): &P::NameAccessChain) { - use P::NameAccessChain_ as NA; - // Record the length of all identifiers representing a potentially - // aliased module, struct, enum or function name in an access chain. - // We can conservatively record all identifiers as they are only - // accessed by-location so those irrelevant will never be queried. - match chain { - NA::Single(entry) => { - self.path_entry_symbols(entry); - if let Some(loc) = loc_start_to_lsp_position_opt(self.files, &entry.name.loc) { - self.alias_lengths.insert(loc, entry.name.value.len()); - }; - } - NA::Path(path) => { - let P::NamePath { - root, - entries, - is_incomplete: _, - } = path; - self.root_path_entry_symbols(root); - if let Some(root_loc) = loc_start_to_lsp_position_opt(self.files, &root.name.loc) { - if let P::LeadingNameAccess_::Name(n) = root.name.value { - self.alias_lengths.insert(root_loc, n.value.len()); - } - }; - entries.iter().for_each(|entry| { - self.path_entry_symbols(entry); - if let Some(loc) = loc_start_to_lsp_position_opt(self.files, &entry.name.loc) { - self.alias_lengths.insert(loc, entry.name.value.len()); - }; - }); - } - }; - } - - fn field_defn(&mut self, field: &P::Field) { - // If the cursor is in this item, mark that down. - update_cursor!(IDENT, self.cursor, field, FieldDefn); - } -} - /// Add use of a function, method, struct or enum identifier pub fn add_member_use_def( member_def_name: &Symbol, // may be different from use_name for methods From 98c8291018669273995fd5fe1b2ec77965fa330e Mon Sep 17 00:00:00 2001 From: George Danezis <4999882+gdanezis@users.noreply.github.com> Date: Mon, 19 Aug 2024 15:32:29 +0100 Subject: [PATCH 147/232] [light-client] Add standalone verification function (#18971) ## Description Add a standalone light client verification function. It takes a structure that contains some targets to verify and a proof material, and returns Ok() if they verify or an error otherwise. It takes dependencies only on Sui types for the moment. ## Test plan We have new unit tests. --- crates/sui-light-client/Cargo.toml | 8 +- .../example_config/15918264.chk | Bin 0 -> 340915 bytes .../example_config/16005062.chk | Bin 0 -> 341783 bytes .../example_config/20958462.chk | Bin 0 -> 312826 bytes crates/sui-light-client/src/construct.rs | 91 ++++++ crates/sui-light-client/src/lib.rs | 11 + crates/sui-light-client/src/proof.rs | 230 +++++++++++++++ crates/sui-light-client/tests/check_proof.rs | 273 ++++++++++++++++++ 8 files changed, 612 insertions(+), 1 deletion(-) create mode 100644 crates/sui-light-client/example_config/15918264.chk create mode 100644 crates/sui-light-client/example_config/16005062.chk create mode 100644 crates/sui-light-client/example_config/20958462.chk create mode 100644 crates/sui-light-client/src/construct.rs create mode 100644 crates/sui-light-client/src/lib.rs create mode 100644 crates/sui-light-client/src/proof.rs create mode 100644 crates/sui-light-client/tests/check_proof.rs diff --git a/crates/sui-light-client/Cargo.toml b/crates/sui-light-client/Cargo.toml index 1cf90ea7aa48f..a18d7df4c49cd 100644 --- a/crates/sui-light-client/Cargo.toml +++ b/crates/sui-light-client/Cargo.toml @@ -6,6 +6,13 @@ license = "Apache-2.0" publish = false edition = "2021" +[lib] +path = "src/lib.rs" + +[[bin]] +name = "sui-light-client" +path = "src/main.rs" + [dependencies] anyhow.workspace = true async-trait.workspace = true @@ -25,4 +32,3 @@ sui-sdk.workspace = true move-binary-format.workspace = true sui-json-rpc-types.workspace = true sui-package-resolver.workspace = true - diff --git a/crates/sui-light-client/example_config/15918264.chk b/crates/sui-light-client/example_config/15918264.chk new file mode 100644 index 0000000000000000000000000000000000000000..70892f9c226c114b5e44911404ce4af4c501d7cb GIT binary patch literal 340915 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

R$9H8l=8+G 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 Q8&0o?6Iwg6xHHTD2fNA+=Kufz literal 0 HcmV?d00001 diff --git a/crates/sui-light-client/example_config/16005062.chk b/crates/sui-light-client/example_config/16005062.chk new file mode 100644 index 0000000000000000000000000000000000000000..88d1bf8ee7131c23257983bdb89792b5a6670925 GIT binary patch literal 341783 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%0D=dRlpcZ0gFJwwp%d8&%*@+VDe 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

)T2d6o2ThP|Z#qs_J@1ppD0{5XCq_Jxole7e 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 literal 0 HcmV?d00001 diff --git a/crates/sui-light-client/src/construct.rs b/crates/sui-light-client/src/construct.rs new file mode 100644 index 0000000000000..7658236768daa --- /dev/null +++ b/crates/sui-light-client/src/construct.rs @@ -0,0 +1,91 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::proof::{Proof, ProofTarget, TransactionProof}; + +use anyhow::anyhow; +use sui_rest_api::{CheckpointData, CheckpointTransaction}; +use sui_types::effects::TransactionEffectsAPI; + +/// Construct a proof from the given checkpoint data and proof targets. +/// +/// Only minimal cheaper checks are performed to ensure the proof is valid. If you need guaranteed +/// validity consider calling `verify_proof` function on the constructed proof. It either returns +/// `Ok` with a proof, or `Err` with a description of the error. +pub fn construct_proof(targets: ProofTarget, data: &CheckpointData) -> anyhow::Result { + let checkpoint_summary = data.checkpoint_summary.clone(); + let mut this_proof = Proof { + targets, + checkpoint_summary, + contents_proof: None, + }; + + // Do a minimal check that the given checkpoint data is consistent with the committee + if let Some(committee) = &this_proof.targets.committee { + // Check we have the correct epoch + if this_proof.checkpoint_summary.epoch() + 1 != committee.epoch { + return Err(anyhow!("Epoch mismatch between checkpoint and committee")); + } + + // Check its an end of epoch checkpoint + if this_proof.checkpoint_summary.end_of_epoch_data.is_none() { + return Err(anyhow!("Expected end of epoch checkpoint")); + } + } + + // If proof targets include objects or events, we need to include the contents proof + // Need to ensure that all targets refer to the same transaction first of all + let object_tx = this_proof + .targets + .objects + .iter() + .map(|(_, o)| o.previous_transaction); + let event_tx = this_proof + .targets + .events + .iter() + .map(|(eid, _)| eid.tx_digest); + let mut all_tx = object_tx.chain(event_tx); + + // Get the first tx ID + let target_tx_id = if let Some(first_tx) = all_tx.next() { + first_tx + } else { + // Since there is no target we just return the summary proof + return Ok(this_proof); + }; + + // Basic check that all targets refer to the same transaction + if !all_tx.all(|tx| tx == target_tx_id) { + return Err(anyhow!("All targets must refer to the same transaction")); + } + + // Find the transaction in the checkpoint data + let tx = data + .transactions + .iter() + .find(|t| t.effects.transaction_digest() == &target_tx_id) + .ok_or(anyhow!("Transaction not found in checkpoint data"))? + .clone(); + + let CheckpointTransaction { + transaction, + effects, + events, + .. + } = tx; + + // Add all the transaction data in there + this_proof.contents_proof = Some(TransactionProof { + checkpoint_contents: data.checkpoint_contents.clone(), + transaction, + effects, + events, + }); + + // TODO: should we check that the objects & events are in the transaction, to + // avoid constructing invalid proofs? I opt to not check because the check + // is expensive (sequential scan of all objects). + + Ok(this_proof) +} diff --git a/crates/sui-light-client/src/lib.rs b/crates/sui-light-client/src/lib.rs new file mode 100644 index 0000000000000..8f5488b48be9a --- /dev/null +++ b/crates/sui-light-client/src/lib.rs @@ -0,0 +1,11 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +pub mod construct; +pub mod proof; + +#[doc(inline)] +pub use proof::*; + +#[doc(inline)] +pub use construct::*; diff --git a/crates/sui-light-client/src/proof.rs b/crates/sui-light-client/src/proof.rs new file mode 100644 index 0000000000000..e04f080a01a23 --- /dev/null +++ b/crates/sui-light-client/src/proof.rs @@ -0,0 +1,230 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::anyhow; + +use sui_types::{ + base_types::ObjectRef, + committee::Committee, + effects::{TransactionEffects, TransactionEffectsAPI, TransactionEvents}, + event::{Event, EventID}, + messages_checkpoint::{CertifiedCheckpointSummary, CheckpointContents, EndOfEpochData}, + object::Object, + transaction::Transaction, +}; + +/// Define aspect of Sui state that need to be certified in a proof +#[derive(Default)] +pub struct ProofTarget { + /// Objects that need to be certified + pub objects: Vec<(ObjectRef, Object)>, + + /// Events that need to be certified + pub events: Vec<(EventID, Event)>, + + /// The next committee being certified + pub committee: Option, +} + +impl ProofTarget { + /// Create a new empty proof target. An empty proof target still ensures that the + /// checkpoint summary is correct. + pub fn new() -> Self { + Self::default() + } + + /// Add an object to be certified by object reference and content. A verified proof will + /// ensure that both the reference and content are correct. Note that some content is + /// meta-data such as the transaction that created this object. + pub fn add_object(mut self, object_ref: ObjectRef, object: Object) -> Self { + self.objects.push((object_ref, object)); + self + } + + /// Add an event to be certified by event ID and content. A verified proof will ensure that + /// both the ID and content are correct. + pub fn add_event(mut self, event_id: EventID, event: Event) -> Self { + self.events.push((event_id, event)); + self + } + + /// Add the next committee to be certified. A verified proof will ensure that the next + /// committee is correct. + pub fn set_committee(mut self, committee: Committee) -> Self { + self.committee = Some(committee); + self + } +} + +/// Part of a Proof that provides evidence relating to a specific transaction to +/// certify objects and events. +pub struct TransactionProof { + /// Checkpoint contents including this transaction. + pub checkpoint_contents: CheckpointContents, + + /// The transaction being certified. + pub transaction: Transaction, + + /// The effects of the transaction being certified. + pub effects: TransactionEffects, + + /// The events of the transaction being certified. + pub events: Option, +} + +/// A proof for specific targets. It certifies a checkpoint summary and optionally includes +/// transaction evidence to certify objects and events. +pub struct Proof { + /// Targets of the proof are objects, events or a committee that need to be certified + pub targets: ProofTarget, + + /// A summary of the checkpoint being certified. + pub checkpoint_summary: CertifiedCheckpointSummary, + + /// Optional transaction proof to certify objects and events. + pub contents_proof: Option, +} + +/// Verify a proof against a committee. A proof is valid if it certifies the checkpoint summary +/// and optionally includes transaction evidence to certify objects and events. +/// +/// If the result is `Ok(())` then the proof is valid. If Err is returned then the proof is invalid +/// and the error message will describe the reason. Once a proof is verified it can be trusted, +/// and information in `targets` as well as `checkpoint_summary` or `contents_proof` can be +/// trusted as being authentic. +/// +/// The authoritative committee is required to verify the proof. The sequence of committees can be +/// verified through a proof for Committee proof target on the last checkpoint of each epoch, +/// sequentially since the first epoch. +pub fn verify_proof(committee: &Committee, proof: &Proof) -> anyhow::Result<()> { + // Get checkpoint summary and optional contents + let summary = &proof.checkpoint_summary; + let contents_ref = proof + .contents_proof + .as_ref() + .map(|x| &x.checkpoint_contents); + + // Verify the checkpoint summary using the committee + summary.verify_with_contents(committee, contents_ref)?; + + // MILESTONE 1 : summary and contents is correct + // Note: this is unconditional on the proof targets, and always checked. + + // If the proof target is the next committee check it + if let Some(committee) = &proof.targets.committee { + match &summary.end_of_epoch_data { + Some(EndOfEpochData { + next_epoch_committee, + .. + }) => { + // Extract the end of epoch committee + let next_committee_data = next_epoch_committee.iter().cloned().collect(); + let new_committee = + Committee::new(summary.epoch().checked_add(1).unwrap(), next_committee_data); + + if new_committee != *committee { + return Err(anyhow!( + "Given committee does not match the end of epoch committee" + )); + } + } + None => { + return Err(anyhow!( + "No end of epoch committee in the checkpoint summary" + )); + } + } + } + + // MILESTONE 2: committee if requested is correct + + // Non empty object or event targets require the optional contents proof + // If it is not present return an error + + if (!proof.targets.objects.is_empty() || !proof.targets.events.is_empty()) + && proof.contents_proof.is_none() + { + return Err(anyhow!("Contents proof is missing")); + } + + // MILESTONE 3: contents proof is present if required + + if let Some(contents_proof) = &proof.contents_proof { + // Extract Transaction Digests and check they are in contents + let digests = contents_proof.effects.execution_digests(); + if contents_proof.transaction.digest() != &digests.transaction { + return Err(anyhow!( + "Transaction digest does not match the execution digest" + )); + } + + // Ensure the digests are in the checkpoint contents + if !contents_proof + .checkpoint_contents + .enumerate_transactions(summary) + .any(|x| x.1 == &digests) + { + // Could not find the digest in the checkpoint contents + return Err(anyhow!( + "Transaction digest not found in the checkpoint contents" + )); + } + + // MILESTONE 4: Transaction & Effect correct and in contents + + if contents_proof.effects.events_digest() + != contents_proof.events.as_ref().map(|e| e.digest()).as_ref() + { + return Err(anyhow!("Events digest does not match the execution digest")); + } + + // If the target includes any events ensure the events digest is not None + if !proof.targets.events.is_empty() && contents_proof.events.is_none() { + return Err(anyhow!("Events digest is missing")); + } + + // MILESTONE 5: Events digest & Events are correct and present if required + + // Now we verify the content of any target events + + for (event_id, event) in &proof.targets.events { + // Check the event corresponds to the transaction + if event_id.tx_digest != digests.transaction { + return Err(anyhow!("Event does not belong to the transaction")); + } + + // The sequence number must be a valid index + // Note: safe to unwrap as we have checked that its not None above + if event_id.event_seq as usize >= contents_proof.events.as_ref().unwrap().data.len() { + return Err(anyhow!("Event sequence number out of bounds")); + } + + // Now check that the contents of the event are the same + if &contents_proof.events.as_ref().unwrap().data[event_id.event_seq as usize] != event { + return Err(anyhow!("Event contents do not match")); + } + } + + // MILESTONE 6: Event contents are correct + + // Now check all object references are correct and in the effects + let changed_objects = contents_proof.effects.all_changed_objects(); + + for (object_ref, object) in &proof.targets.objects { + // Is the given reference correct? + if object_ref != &object.compute_object_reference() { + return Err(anyhow!("Object reference does not match the object")); + } + + // Has this object been created in these effects? + changed_objects + .iter() + .find(|effects_object_ref| &effects_object_ref.0 == object_ref) + .ok_or(anyhow!("Object not found"))?; + } + + // MILESTONE 7: Object references are correct and in the effects + } + + Ok(()) +} diff --git a/crates/sui-light-client/tests/check_proof.rs b/crates/sui-light-client/tests/check_proof.rs new file mode 100644 index 0000000000000..b02cd3e627398 --- /dev/null +++ b/crates/sui-light-client/tests/check_proof.rs @@ -0,0 +1,273 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::anyhow; + +use sui_light_client::construct::construct_proof; +use sui_light_client::proof::{verify_proof, Proof, ProofTarget}; + +use sui_types::event::{Event, EventID}; + +use sui_types::{committee::Committee, effects::TransactionEffectsAPI, object::Object}; + +use sui_rest_api::CheckpointData; + +use std::io::Read; +use std::{fs, path::PathBuf}; + +async fn read_full_checkpoint(checkpoint_path: &PathBuf) -> anyhow::Result { + println!("Reading checkpoint from {:?}", checkpoint_path); + let mut reader = fs::File::open(checkpoint_path.clone())?; + let mut buffer = Vec::new(); + reader.read_to_end(&mut buffer)?; + let (_, data): (u8, CheckpointData) = + bcs::from_bytes(&buffer).map_err(|e| anyhow!("Unable to parse checkpoint file: {}", e))?; + Ok(data) +} + +async fn read_data(committee_seq: u64, seq: u64) -> (Committee, CheckpointData) { + let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + d.push(format!("example_config/{}.chk", committee_seq)); + + let committee_checkpoint = read_full_checkpoint(&d).await.unwrap(); + + let prev_committee = committee_checkpoint + .checkpoint_summary + .end_of_epoch_data + .as_ref() + .ok_or(anyhow!("Expected checkpoint to be end-of-epoch")) + .unwrap() + .next_epoch_committee + .iter() + .cloned() + .collect(); + + // Make a committee object using this + let committee = Committee::new( + committee_checkpoint + .checkpoint_summary + .epoch() + .checked_add(1) + .unwrap(), + prev_committee, + ); + + let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + d.push(format!("example_config/{}.chk", seq)); + + let full_checkpoint = read_full_checkpoint(&d).await.unwrap(); + + (committee, full_checkpoint) +} + +#[tokio::test] +async fn check_can_read_test_data() { + let (_committee, full_checkpoint) = read_data(15918264, 16005062).await; + assert!(full_checkpoint + .checkpoint_summary + .end_of_epoch_data + .is_some()); +} + +#[tokio::test] +async fn test_new_committee() { + let (committee, full_checkpoint) = read_data(15918264, 16005062).await; + + let new_committee_data = full_checkpoint + .checkpoint_summary + .end_of_epoch_data + .as_ref() + .ok_or(anyhow!("Expected checkpoint to be end-of-epoch")) + .unwrap() + .next_epoch_committee + .iter() + .cloned() + .collect(); + + // Make a committee object using this + let new_committee = Committee::new( + full_checkpoint + .checkpoint_summary + .epoch() + .checked_add(1) + .unwrap(), + new_committee_data, + ); + + let committee_proof = Proof { + checkpoint_summary: full_checkpoint.checkpoint_summary.clone(), + contents_proof: None, + targets: ProofTarget::new().set_committee(new_committee.clone()), + }; + + assert!(verify_proof(&committee, &committee_proof).is_ok()); +} + +// Fail if the new committee does not match the target of the proof +#[tokio::test] +async fn test_incorrect_new_committee() { + let (committee, full_checkpoint) = read_data(15918264, 16005062).await; + + let committee_proof = Proof { + checkpoint_summary: full_checkpoint.checkpoint_summary.clone(), + contents_proof: None, + targets: ProofTarget::new().set_committee(committee.clone()), // WRONG + }; + + assert!(verify_proof(&committee, &committee_proof).is_err()); +} + +// Fail if the certificate is incorrect even if no proof targets are given +#[tokio::test] +async fn test_fail_incorrect_cert() { + let (_committee, full_checkpoint) = read_data(15918264, 16005062).await; + + let new_committee_data = full_checkpoint + .checkpoint_summary + .end_of_epoch_data + .as_ref() + .ok_or(anyhow!("Expected checkpoint to be end-of-epoch")) + .unwrap() + .next_epoch_committee + .iter() + .cloned() + .collect(); + + // Make a committee object using this + let new_committee = Committee::new( + full_checkpoint + .checkpoint_summary + .epoch() + .checked_add(1) + .unwrap(), + new_committee_data, + ); + + let committee_proof = Proof { + checkpoint_summary: full_checkpoint.checkpoint_summary.clone(), + contents_proof: None, + targets: ProofTarget::new(), + }; + + assert!(verify_proof( + &new_committee, // WRONG + &committee_proof + ) + .is_err()); +} + +#[tokio::test] +async fn test_object_target_fail_no_data() { + let (committee, full_checkpoint) = read_data(15918264, 16005062).await; + + let sample_object: Object = full_checkpoint.transactions[0].output_objects[0].clone(); + let sample_ref = sample_object.compute_object_reference(); + + let bad_proof = Proof { + checkpoint_summary: full_checkpoint.checkpoint_summary.clone(), + contents_proof: None, // WRONG + targets: ProofTarget::new().add_object(sample_ref, sample_object), + }; + + assert!(verify_proof(&committee, &bad_proof).is_err()); +} + +#[tokio::test] +async fn test_object_target_success() { + let (committee, full_checkpoint) = read_data(15918264, 16005062).await; + + let sample_object: Object = full_checkpoint.transactions[0].output_objects[0].clone(); + let sample_ref = sample_object.compute_object_reference(); + + let target = ProofTarget::new().add_object(sample_ref, sample_object); + let object_proof = construct_proof(target, &full_checkpoint).unwrap(); + + assert!(verify_proof(&committee, &object_proof).is_ok()); +} + +#[tokio::test] +async fn test_object_target_fail_wrong_object() { + let (committee, full_checkpoint) = read_data(15918264, 16005062).await; + + let sample_object: Object = full_checkpoint.transactions[0].output_objects[0].clone(); + let wrong_object: Object = full_checkpoint.transactions[1].output_objects[1].clone(); + let mut sample_ref = sample_object.compute_object_reference(); + let wrong_ref = wrong_object.compute_object_reference(); + + let target = ProofTarget::new().add_object(wrong_ref, sample_object.clone()); // WRONG + let object_proof = construct_proof(target, &full_checkpoint).unwrap(); + assert!(verify_proof(&committee, &object_proof).is_err()); + + // Does not exist + sample_ref.1 = sample_ref.1.next(); // WRONG + + let target = ProofTarget::new().add_object(sample_ref, sample_object); + let object_proof = construct_proof(target, &full_checkpoint).unwrap(); + assert!(verify_proof(&committee, &object_proof).is_err()); +} + +#[tokio::test] +async fn test_event_target_fail_no_data() { + let (committee, full_checkpoint) = read_data(15918264, 16005062).await; + + let sample_event: Event = full_checkpoint.transactions[1] + .events + .as_ref() + .unwrap() + .data[0] + .clone(); + let sample_eid = EventID::from(( + *full_checkpoint.transactions[1].effects.transaction_digest(), + 0, + )); + + let bad_proof = Proof { + checkpoint_summary: full_checkpoint.checkpoint_summary.clone(), + contents_proof: None, // WRONG + targets: ProofTarget::new().add_event(sample_eid, sample_event), + }; + + assert!(verify_proof(&committee, &bad_proof).is_err()); +} + +#[tokio::test] +async fn test_event_target_success() { + let (committee, full_checkpoint) = read_data(15918264, 16005062).await; + + let sample_event: Event = full_checkpoint.transactions[1] + .events + .as_ref() + .unwrap() + .data[0] + .clone(); + let sample_eid = EventID::from(( + *full_checkpoint.transactions[1].effects.transaction_digest(), + 0, + )); + + let target = ProofTarget::new().add_event(sample_eid, sample_event); + let event_proof = construct_proof(target, &full_checkpoint).unwrap(); + + assert!(verify_proof(&committee, &event_proof).is_ok()); +} + +#[tokio::test] +async fn test_event_target_fail_bad_event() { + let (committee, full_checkpoint) = read_data(15918264, 16005062).await; + + let sample_event: Event = full_checkpoint.transactions[1] + .events + .as_ref() + .unwrap() + .data[0] + .clone(); + let sample_eid = EventID::from(( + *full_checkpoint.transactions[1].effects.transaction_digest(), + 1, // WRONG + )); + + let target = ProofTarget::new().add_event(sample_eid, sample_event); + let event_proof = construct_proof(target, &full_checkpoint).unwrap(); + + assert!(verify_proof(&committee, &event_proof).is_err()); +} From 8a8c6b14d2124832b99ef607b0e5de0a1db90de1 Mon Sep 17 00:00:00 2001 From: George Danezis <4999882+gdanezis@users.noreply.github.com> Date: Mon, 19 Aug 2024 16:59:32 +0100 Subject: [PATCH 148/232] [light client] Fix light client doc spelling (#19030) ## Description Fixed documentation spelling and grammar ## Test plan Proof read. --- crates/sui-light-client/src/proof.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/sui-light-client/src/proof.rs b/crates/sui-light-client/src/proof.rs index e04f080a01a23..8d846384e64bb 100644 --- a/crates/sui-light-client/src/proof.rs +++ b/crates/sui-light-client/src/proof.rs @@ -13,16 +13,16 @@ use sui_types::{ transaction::Transaction, }; -/// Define aspect of Sui state that need to be certified in a proof +/// Define aspect of Sui state that needs to be certified in a proof #[derive(Default)] pub struct ProofTarget { - /// Objects that need to be certified + /// Objects that need to be certified. pub objects: Vec<(ObjectRef, Object)>, - /// Events that need to be certified + /// Events that need to be certified. pub events: Vec<(EventID, Event)>, - /// The next committee being certified + /// The next committee being certified. pub committee: Option, } @@ -35,7 +35,7 @@ impl ProofTarget { /// Add an object to be certified by object reference and content. A verified proof will /// ensure that both the reference and content are correct. Note that some content is - /// meta-data such as the transaction that created this object. + /// metadata such as the transaction that created this object. pub fn add_object(mut self, object_ref: ObjectRef, object: Object) -> Self { self.objects.push((object_ref, object)); self @@ -56,7 +56,7 @@ impl ProofTarget { } } -/// Part of a Proof that provides evidence relating to a specific transaction to +/// Part of a proof that provides evidence relating to a specific transaction to /// certify objects and events. pub struct TransactionProof { /// Checkpoint contents including this transaction. @@ -75,7 +75,7 @@ pub struct TransactionProof { /// A proof for specific targets. It certifies a checkpoint summary and optionally includes /// transaction evidence to certify objects and events. pub struct Proof { - /// Targets of the proof are objects, events or a committee that need to be certified + /// Targets of the proof are a committee, objects, or events that need to be certified. pub targets: ProofTarget, /// A summary of the checkpoint being certified. @@ -94,7 +94,7 @@ pub struct Proof { /// trusted as being authentic. /// /// The authoritative committee is required to verify the proof. The sequence of committees can be -/// verified through a proof for Committee proof target on the last checkpoint of each epoch, +/// verified through a Committee proof target on the last checkpoint of each epoch, /// sequentially since the first epoch. pub fn verify_proof(committee: &Committee, proof: &Proof) -> anyhow::Result<()> { // Get checkpoint summary and optional contents From 226cbc34143a9340fc13a4caa53a6b1e64e92cf5 Mon Sep 17 00:00:00 2001 From: ronny-mysten <118224482+ronny-mysten@users.noreply.github.com> Date: Mon, 19 Aug 2024 10:06:29 -0600 Subject: [PATCH 149/232] [docs] More precise kiosk language (#18997) ## Description Attempts to clear up what is meant by bullet point under **Benefits**. Applies more consistent capitalization. Kiosk is capitalized when specifically referring to the standard or object, concept is lower case. Other minor tweaks. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- docs/content/standards/kiosk.mdx | 52 ++++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/content/standards/kiosk.mdx b/docs/content/standards/kiosk.mdx index b22b65fbdd735..658e1614e1e78 100644 --- a/docs/content/standards/kiosk.mdx +++ b/docs/content/standards/kiosk.mdx @@ -3,11 +3,11 @@ title: Sui Kiosk description: Kiosk is a decentralized system for commerce applications on Sui. Kiosk is a part of the Sui framework, native to the system, and available to everyone. --- -Kiosk is a decentralized system for commerce applications on Sui. It consists of Kiosks - shared objects owned by individual parties that store assets and allow listing them for sale as well as utilize custom trading functionality - for example, an auction. While being highly decentralized, Kiosk provides a set of strong guarantees: +Kiosk is a decentralized system for commerce applications on Sui. It consists of `Kiosk` objects - shared objects owned by individual parties that store assets and allow listing them for sale as well as utilize custom trading functionality - for example, an auction. While being highly decentralized, Sui Kiosk provides a set of strong guarantees: - Kiosk owners retain ownership of their assets to the moment of purchase. - Creators set custom policies - sets of rules applied to every trade (such as pay royalty fee or do some arbitrary action X). -- Marketplaces can index events the Kiosk emits and subscribe to a single feed for on-chain asset trading. +- Marketplaces can index events the `Kiosk` object emits and subscribe to a single feed for on-chain asset trading. Practically, Kiosk is a part of the Sui framework, and it is native to the system and available to everyone out of the box. @@ -23,7 +23,7 @@ Anyone can create a Sui Kiosk. Ownership of a kiosk is determined by the owner o To sell an item, if there is an existing transfer policy for the type (T), you just add your assets to your kiosk and then list them. You specify an offer amount when you list an item. Anyone can then purchase the item for the amount of SUI specified in the listing. The associated transfer policy determines what the buyer can do with the purchased asset. -A Kiosk owner can: +A kiosk owner can: - Place and take items - List items for sale @@ -34,13 +34,13 @@ A Kiosk owner can: ## Sui Kiosk for buyers -A buyer is a party that purchases (or - more generally - receives) items from Kiosks, anyone on the network can be a Buyer (and, for example, a Kiosk Owner at the same time). +A buyer is a party that purchases (or - more generally - receives) items from kiosks, anyone on the network can be a buyer (and, for example, a kiosk owner at the same time). -** Benefits:** +**Benefits:** - Buyers get access to global liquidity and can get the best offer -- Buyers can place bids on collections through their Kiosks -- Most of the actions performed in Kiosks are free (gas-less) for Buyers +- Buyers can place bids on collections through their kiosks +- Most buyer actions performed in kiosks clean up seller objects, which results in free (gas-less) actions **Responsibilities:** @@ -49,7 +49,7 @@ A buyer is a party that purchases (or - more generally - receives) items from Ki **Guarantees:** -- When using a custom trading logic such as an Auction, the items are guaranteed to be unchanged until the trade is complete +- When using a custom trading logic such as an auction, the items are guaranteed to be unchanged until the trade is complete ## Sui Kiosk for marketplaces @@ -59,22 +59,22 @@ As a marketplace operator, you can implement Sui Kiosk to watch for offers made As a creator, Sui Kiosk supports strong enforcement for transfer policies and associated rules to protect assets and enforce asset ownership. Sui Kiosk gives creators more control over their creations, and puts creators and owners in control of how their works can be used. -Creator is a party that creates and controls the TransferPolicy for a single type. For example, the authors of SuiFrens are the Creators of the `SuiFren` type and act as creators in the Kiosk ecosystem. Creators set the policy, but they might also be the first sellers of their assets through a Kiosk. +Creator is a party that creates and controls the TransferPolicy for a single type. For example, the authors of SuiFrens are the Creators of the `SuiFren` type and act as creators in the Kiosk ecosystem. Creators set the policy, but they might also be the first sellers of their assets through a kiosk. **Creators can:** - Set any rules for trades - Set multiple ways ("tracks") of rules - Enable or disable trades at any moment with a policy -- Enforce policies (eg royalties) on all trades -- Perform a primary sale of their assets through a Kiosk +- Enforce policies (like royalties) on all trades +- Perform a primary sale of their assets through a kiosk All of the above is effective immediately and globally. **Creators cannot:** -- Take or modify items stored in someone else's Kiosk -- Restrict taking items from Kiosks if the "locking" rule was not set in the policy +- Take or modify items stored in someone else's kiosk +- Restrict taking items from kiosks if the "locking" rule was not set in the policy ## Sui Kiosk guarantees @@ -82,7 +82,7 @@ Sui Kiosk provides a set of guarantees that Sui enforces through smart contracts These guarantees include: - Every trade in Sui Kiosk requires a `TransferPolicy` resolution. This gives creators control over how their assets can be traded. -- True Ownership, which means that only a kiosk owner can take, list, borrow, or modify the assets added to their kiosk. This is similar to how single-owner objects work on Sui. +- True ownership, which means that only a kiosk owner can take, list, borrow, or modify the assets added to their kiosk. This is similar to how single-owner objects work on Sui. - Strong policy enforcement, for example Royalty policies, that lets creators enable or disable policies at any time that applies to all trades on the platform for objects with that policy attached. - Changes to a `TransferPolicy` apply instantly and globally. @@ -98,10 +98,10 @@ In practice, these guarantees mean that: Sui Kiosk is a shared object that can store heterogeneous values, such as different sets of asset collectibles. When you add an asset to your kiosk, it has one of the following states: -- PLACED - an item placed in the kiosk using the `kiosk::place` function. The Kiosk Owner can withdraw it and use it directly, borrow it (mutably or immutably), or list an item for sale. -- LOCKED - an item placed in the kiosk using the `kiosk::lock` function. You can’t withdraw a Locked item from a kiosk, but you can borrow it mutably and list it for sale. Any item placed in a kiosk that has an associated Kiosk Lock policy have a LOCKED state. +- PLACED - an item placed in the kiosk using the `kiosk::place` function. The kiosk owner can withdraw it and use it directly, borrow it (mutably or immutably), or list an item for sale. +- LOCKED - an item placed in the kiosk using the `kiosk::lock` function. You can’t withdraw a Locked item from a kiosk, but you can borrow it mutably and list it for sale. Any item placed in a kiosk that has an associated kiosk lock policy have a LOCKED state. - LISTED - an item in the kiosk that is listed for sale using the `kiosk::list` or `kiosk::place_and_list` functions. You can’t modify an item while listed, but you can borrow it immutably or delist it, which returns it to its previous state. -- LISTED EXCLUSIVELY - an item placed or locked in the kiosk by an extension that calls the `kiosk::list_with_purchase_cap` function. Only the kiosk owner can approve calling the function. The owner can only borrow it immutably. The extension must provide the functionality to delist / unlock the asset, or it might stay locked forever. Given that this action is explicitly performed by the Owner - it is the responsibility of the Owner to choose verified and audited extensions to use. +- LISTED EXCLUSIVELY - an item placed or locked in the kiosk by an extension that calls the `kiosk::list_with_purchase_cap` function. Only the kiosk owner can approve calling the function. The owner can only borrow it immutably. The extension must provide the functionality to delist / unlock the asset, or it might stay locked forever. Given that this action is explicitly performed by the owner - it is the responsibility of the owner to choose verified and audited extensions to use. When someone purchases an asset from a kiosk, the asset leaves the kiosk and ownership transfers to the buyer’s address. @@ -132,8 +132,8 @@ sui client call \ ### Create a Sui Kiosk with advanced options -For more advanced use cases, when you want to choose the storage model or perform an action right away, you can use the programmable transaction block (PTB) friendly function kiosk::new. -Kiosk is designed to be shared. If you choose a different storage model, such as owned, your kiosk might not function as intended or not be accessible to other users. You can make sure your Kiosk works by testing it on Sui Testnet. +For more advanced use cases, when you want to choose the storage model or perform an action right away, you can use the programmable transaction block (PTB) friendly function `kiosk::new`. +Kiosk is designed to be shared. If you choose a different storage model, such as owned, your kiosk might not function as intended or not be accessible to other users. You can make sure your kiosk works by testing it on Sui Testnet. ### Create a Sui Kiosk with advanced options using programmable transaction blocks @@ -159,11 +159,11 @@ Sui CLI does not support PTBs and transaction chaining yet. You can use the `kio As a kiosk owner, you can place any assets into your Sui Kiosk. You can take any item from your kiosk that is not currently listed for sale. -There's no limitations on which assets you can place in your kiosk. However, you can’t necessarily list and trade all of the items you place in your kiosk. The `TransferPolicy` associated with the type for the item determines whether you can trade it. To learn more, see the [Purchase items from a Kiosk](#purchase) section. +There's no limitations on which assets you can place in your kiosk. However, you can’t necessarily list and trade all of the items you place in your kiosk. The `TransferPolicy` associated with the type for the item determines whether you can trade it. To learn more, see the [Purchase items from a kiosk](#purchase) section. ### Place an item in your kiosk -To place an item to the Kiosk, the owner needs to call the `sui::kiosk::place` function on the `Kiosk` object and pass the `KioskOwnerCap` and the `Item` as arguments. +To place an item to the kiosk, the owner needs to call the `sui::kiosk::place` function on the `Kiosk` object and pass the `KioskOwnerCap` and the `Item` as arguments. `ITEM_TYPE` in the following examples represents the full type of the item. @@ -281,7 +281,7 @@ Anyone on the network can purchase an item listed from a Sui Kiosk. To learn mor ### List an item from a kiosk As a kiosk owner, you can use the `kiosk::list` function to list any asset you added to your kiosk. Include the item to sell and the list price as arguments. All listings on Sui are in SUI tokens. -When you list an item, Sui emits a `kiosk::ItemListed` event that contains the Kiosk ID, Item ID, type of the Item, and the list price. +When you list an item, Sui emits a `kiosk::ItemListed` event that contains the kiosk ID, item ID, type of the item, and the list price. ### List an item using programmable transaction blocks @@ -319,7 +319,7 @@ As a kiosk owner you can use the `kiosk::delist` to delist any currently listed When you delist an item, Sui returns to the kiosk owner the gas fees charged to list the item. -When you delist an item, Sui emits a `kiosk::ItemDelisted` event that contains the Kiosk ID, Item ID, and the type of the item. +When you delist an item, Sui emits a `kiosk::ItemDelisted` event that contains the kiosk ID, item ID, and the type of the item. ### Delist an item using the programmable transaction blocks @@ -351,7 +351,7 @@ sui client call \ ## Purchase an item from a kiosk {#purchase} -Anyone that has an address on the Sui network can purchase an item listed from a Sui Kiosk. To purchase an item, you can use the `kiosk::purchase` function. Specify the item to purchase and pay the list price set by the Kiosk Owner. +Anyone that has an address on the Sui network can purchase an item listed from a Sui Kiosk. To purchase an item, you can use the `kiosk::purchase` function. Specify the item to purchase and pay the list price set by the kiosk owner. You can discover the items listed on the network with the `kiosk::ItemListed` event. @@ -434,7 +434,7 @@ tx.moveCall({ ## Withdraw proceeds from a completed sale -When someone purchases an item, Sui stores the proceeds from the sale in the Kiosk. As the kiosk owner, you can withdraw the proceeds at any time by calling the `kiosk::withdraw` function. The function is simple, but because it is PTB friendly it is not currently supported in the Sui CLI. +When someone purchases an item, Sui stores the proceeds from the sale in the kiosk. As the kiosk owner, you can withdraw the proceeds at any time by calling the `kiosk::withdraw` function. The function is simple, but because it is PTB friendly it is not currently supported in the Sui CLI. ### Withdraw proceeds using programmable transaction blocks @@ -466,4 +466,4 @@ let coin = tx.moveCall({ ### Withdraw proceeds using the Sui CLI -Due to the function being PTB friendly, it is not currently supported in the CLI environment. +This action is not currently supported in the CLI environment. From f82f095956a14110a46d49510d83e1b8aded9192 Mon Sep 17 00:00:00 2001 From: Tim Zakian <2895723+tzakian@users.noreply.github.com> Date: Mon, 19 Aug 2024 09:24:19 -0700 Subject: [PATCH 150/232] [move] Enable enums in mainnet (#19031) ## Description Turns on enums in mainnet. ## Test plan CI --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [X] Protocol: Enable Move enums in mainnet - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-open-rpc/spec/openrpc.json | 2 +- crates/sui-protocol-config/src/lib.rs | 7 +- ...ocol_config__test__Mainnet_version_55.snap | 321 +++++++++++++++++ ...ocol_config__test__Testnet_version_55.snap | 322 +++++++++++++++++ ...sui_protocol_config__test__version_55.snap | 331 ++++++++++++++++++ ...ests__genesis_config_snapshot_matches.snap | 3 +- ..._populated_genesis_snapshot_matches-2.snap | 31 +- 7 files changed, 999 insertions(+), 18 deletions(-) create mode 100644 crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_55.snap create mode 100644 crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_55.snap create mode 100644 crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_55.snap diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index 58be401f20901..986acaf1bd67d 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": "54", + "maxSupportedProtocolVersion": "55", "protocolVersion": "6", "featureFlags": { "accept_zklogin_in_multisig": false, diff --git a/crates/sui-protocol-config/src/lib.rs b/crates/sui-protocol-config/src/lib.rs index c6e4d5de99afa..6f178c1a64455 100644 --- a/crates/sui-protocol-config/src/lib.rs +++ b/crates/sui-protocol-config/src/lib.rs @@ -16,7 +16,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 = 54; +const MAX_PROTOCOL_VERSION: u64 = 55; // Record history of protocol version allocations here: // @@ -167,6 +167,7 @@ const MAX_PROTOCOL_VERSION: u64 = 54; // Update stdlib natives costs // Version 54: Enable random beacon on mainnet. // Enable soft bundle on mainnet. +// Version 55: Enable enums on mainnet. #[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct ProtocolVersion(u64); @@ -2652,6 +2653,10 @@ impl ProtocolConfig { cfg.feature_flags.soft_bundle = true; cfg.max_soft_bundle_size = Some(5); } + 55 => { + // Turn on enums mainnet + cfg.move_binary_format_version = Some(7); + } // Use this template when making changes: // // // modify an existing constant. diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_55.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_55.snap new file mode 100644 index 0000000000000..4797d413342cf --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_55.snap @@ -0,0 +1,321 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 55 +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 + 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: TotalTxCount + 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 +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 +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: 1000 +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: 6291456 +max_accumulated_txn_cost_per_object_in_narwhal_commit: 100 +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: false +max_accumulated_txn_cost_per_object_in_mysticeti_commit: 10 + diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_55.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_55.snap new file mode 100644 index 0000000000000..1255df195a218 --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_55.snap @@ -0,0 +1,322 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 55 +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: TotalTxCount + 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 +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 +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: 1000 +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: 6291456 +max_accumulated_txn_cost_per_object_in_narwhal_commit: 100 +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: 10 + diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_55.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_55.snap new file mode 100644 index 0000000000000..11a1ab15cf20f --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_55.snap @@ -0,0 +1,331 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 55 +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: TotalTxCount + 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 +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 +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: 1000 +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: 6291456 +max_accumulated_txn_cost_per_object_in_narwhal_commit: 100 +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: 10 + 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 c24f52aa519d9..c780c2c68a94a 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: 54 + protocol_version: 55 allow_insertion_of_extra_objects: true epoch_duration_ms: 86400000 stake_subsidy_start_epoch: 0 @@ -49,3 +49,4 @@ accounts: - 30000000000000000 - 30000000000000000 - 30000000000000000 + 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 dd9ade4ff9987..84c19276b05ee 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: 54 +protocol_version: 55 system_state_version: 1 validators: total_stake: 20000000000000000 @@ -240,13 +240,13 @@ validators: next_epoch_worker_address: ~ extra_fields: id: - id: "0x914faf0978a3304bc5eca36ef861f4fb5c8e2fd1d7531fac16fb59c42f2444e0" + id: "0x91cf87189ecc6ba1b20c069d384009a8c716b20b2b8172f6985892ce55d6a69e" size: 0 voting_power: 10000 - operation_cap_id: "0xdd0a37c4cb272a3ef7596ed7a3a3d39e3043ac5694ea85679401afed5a579226" + operation_cap_id: "0x3687778ce9b5fb8e1802c5e49486186ca74cd0e3d440bc4d00e41248bfb4563d" gas_price: 1000 staking_pool: - id: "0x152174c0dc497a9ed5bf38916a6e2a83ed94f7fcac9ad3d2a394d42ac2f1535c" + id: "0x401aa727b457a5bc4471dde614effa9ec542cd5a41f4c1a5d6707d4c2bb57c1d" activation_epoch: 0 deactivation_epoch: ~ sui_balance: 20000000000000000 @@ -254,14 +254,14 @@ validators: value: 0 pool_token_balance: 20000000000000000 exchange_rates: - id: "0x813360f00f57eeb9d7606b042e40cd894600da18cfd2b09c92850429266d27a1" + id: "0x79a609d9ea8838baecedcf8fc23ffe1700a9499b11b43e182d876e77c860f1c8" size: 1 pending_stake: 0 pending_total_sui_withdraw: 0 pending_pool_token_withdraw: 0 extra_fields: id: - id: "0x3ea0a0b3a2ed3e97a41f7a5449dd6cdabc7c352d78060257070a7644df62e0be" + id: "0x6e7d706457a54dad37798cbec8845a00a91ff79ac31ba08150145773cfa8ad5e" size: 0 commission_rate: 200 next_epoch_stake: 20000000000000000 @@ -269,27 +269,27 @@ validators: next_epoch_commission_rate: 200 extra_fields: id: - id: "0x6fe70da3c009c35f7f0775fbb7cf31fee9b791a06b568f9e5b6bdb962df6cd70" + id: "0xf4f6c922cbb8804adf9f57616d174664aa66f9ae2d6ab98c5ca09b04c8f8b790" size: 0 pending_active_validators: contents: - id: "0xa1a5f2a0c949249c21875d68b08c2303afddde080a701a3eb7ed47cd5994a123" + id: "0xc4144367bb1909fca56a014b4f178a86d016bda6d11ee8933ab264d7be63bbb8" size: 0 pending_removals: [] staking_pool_mappings: - id: "0x6d5379cad39050a7e1f6fe202e02729f1b5175ad2382019f05c564099fe0c680" + id: "0xe7222d5ea57578f4c9a9c49a947296cea38fddc619279057e1515d544315a14b" size: 1 inactive_validators: - id: "0x8ec59c4f685c144a0a87b0944cd05aa177402af486357645a4c38b4c9746af88" + id: "0xa67fa0884f4da8278c0c407b3cf93fee1f4d905644e58ac508e5d03aa67ccc10" size: 0 validator_candidates: - id: "0xe77fe73477351803bc9dae400aed9112bbfda929dcdc22e2a14575e40e215a9c" + id: "0x85cb83ef323fc535a0fd268ce1c9d9b5e8a7172934a63a44eca2028b1c7af646" size: 0 at_risk_validators: contents: [] extra_fields: id: - id: "0x7467315914ac8e1c27eb2350a1783cdcea34b6fffe349c31e7f80f7c3543b282" + id: "0x7795c4b8616bb0e1ce32b70b488e47a60c52f9e7b89269697191b6cb66fd69b9" size: 0 storage_fund: total_object_storage_rebates: @@ -306,7 +306,7 @@ parameters: validator_low_stake_grace_period: 7 extra_fields: id: - id: "0x6e650aa8265196ca0fdd9ef3c1c0e2f1d5042c74cee6a76a65316bcb3b6188fc" + id: "0xe4eadbfaffc9fea1f5e08cd4ddc0a98ef16adc465a72eb4899aba35f85769d7c" size: 0 reference_gas_price: 1000 validator_report_records: @@ -320,7 +320,7 @@ stake_subsidy: stake_subsidy_decrease_rate: 1000 extra_fields: id: - id: "0x3786047219840eac174fd6e74faa0602cea08ed146bdc3ac607a6536aff8dc2c" + id: "0x6234f98f2b4a5cb991a4f741b4c1040817ccf12956c9aeb74fb3c9553377dca4" size: 0 safe_mode: false safe_mode_storage_rewards: @@ -332,5 +332,6 @@ safe_mode_non_refundable_storage_fee: 0 epoch_start_timestamp_ms: 10 extra_fields: id: - id: "0x0111121489c309364858942970d14ae6f85473f447a382651fb3a93938538a95" + id: "0x31a90ec6ad9f9584a154ab874f67b395bd6eb3050cf8e1ae40ad5426f8311c3f" size: 0 + From d70e8ff969fce28f5615b3fa09a819b3f2303671 Mon Sep 17 00:00:00 2001 From: Tony Lee Date: Mon, 19 Aug 2024 12:49:17 -0400 Subject: [PATCH 151/232] Upgrade Deepbook SDK Constant (#19027) ## Description Upgrade Deepbook SDK Constant ## Test plan How did you test the new or updated feature? Testnet ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .changeset/small-months-shop.md | 5 +++++ sdk/deepbook-v3/src/utils/constants.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/small-months-shop.md diff --git a/.changeset/small-months-shop.md b/.changeset/small-months-shop.md new file mode 100644 index 0000000000000..21fa4fb6797b3 --- /dev/null +++ b/.changeset/small-months-shop.md @@ -0,0 +1,5 @@ +--- +'@mysten/deepbook-v3': patch +--- + +Upgrade Package diff --git a/sdk/deepbook-v3/src/utils/constants.ts b/sdk/deepbook-v3/src/utils/constants.ts index ee7d44b8d63a5..e906071c1a314 100644 --- a/sdk/deepbook-v3/src/utils/constants.ts +++ b/sdk/deepbook-v3/src/utils/constants.ts @@ -12,7 +12,7 @@ export interface DeepbookPackageIds { } export const testnetPackageIds = { - DEEPBOOK_PACKAGE_ID: '0x2c152dba0110d3afb76b659ed3436edd848b37e177c3abfc0296f8aefc2e6cf4', + DEEPBOOK_PACKAGE_ID: '0x3228ce0225baacb211e230f74891ceaabe3edeae230090ea3a5a176e535af7e9', REGISTRY_ID: '0x9162317a81a9eb66ecd42705529b2a39c7805f98f42312275c2e7a599d518437', DEEP_TREASURY_ID: '0x69fffdae0075f8f71f4fa793549c11079266910e8905169845af1f5d00e09dcb', } satisfies DeepbookPackageIds; From 34628d30ced770114e9fc7c4941c5a4320ea3cb8 Mon Sep 17 00:00:00 2001 From: "sui-merge-bot[bot]" <114704316+sui-merge-bot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 13:18:58 -0400 Subject: [PATCH 152/232] Version Packages (#19033) This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @mysten/deepbook-v3@0.3.1 ### Patch Changes - d70e8ff: Upgrade Package Co-authored-by: github-actions[bot] --- .changeset/small-months-shop.md | 5 ----- sdk/deepbook-v3/CHANGELOG.md | 6 ++++++ sdk/deepbook-v3/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/small-months-shop.md diff --git a/.changeset/small-months-shop.md b/.changeset/small-months-shop.md deleted file mode 100644 index 21fa4fb6797b3..0000000000000 --- a/.changeset/small-months-shop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@mysten/deepbook-v3': patch ---- - -Upgrade Package diff --git a/sdk/deepbook-v3/CHANGELOG.md b/sdk/deepbook-v3/CHANGELOG.md index 2564d0eff9d59..4c9b6efd51710 100644 --- a/sdk/deepbook-v3/CHANGELOG.md +++ b/sdk/deepbook-v3/CHANGELOG.md @@ -1,5 +1,11 @@ # @mysten/deepbook-v3 +## 0.3.1 + +### Patch Changes + +- d70e8ff: Upgrade Package + ## 0.3.0 ### Minor Changes diff --git a/sdk/deepbook-v3/package.json b/sdk/deepbook-v3/package.json index b42520d75bf0c..e522ff4ac4cfb 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.3.0", + "version": "0.3.1", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", From cc6540f867b2ad6899e787a4de4c76f36298024a Mon Sep 17 00:00:00 2001 From: hayes-mysten <135670682+hayes-mysten@users.noreply.github.com> Date: Mon, 19 Aug 2024 11:16:43 -0700 Subject: [PATCH 153/232] [sdk e2e] fix some more flaky e2e tests (#19022) ## Description Describe the changes or additions included in this PR. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- sdk/typescript/test/e2e/id-entry-args.test.ts | 2 ++ sdk/typescript/test/e2e/multisig.test.ts | 1 + sdk/typescript/test/e2e/objects.test.ts | 3 ++- sdk/typescript/test/e2e/txn-builder.test.ts | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/sdk/typescript/test/e2e/id-entry-args.test.ts b/sdk/typescript/test/e2e/id-entry-args.test.ts index afa9d960cd905..25974ac4a1e24 100644 --- a/sdk/typescript/test/e2e/id-entry-args.test.ts +++ b/sdk/typescript/test/e2e/id-entry-args.test.ts @@ -29,6 +29,7 @@ describe('Test ID as args to entry functions', () => { showEffects: true, }, }); + await toolbox.client.waitForTransaction({ digest: result.digest }); expect(result.effects?.status.status).toEqual('success'); }); @@ -45,6 +46,7 @@ describe('Test ID as args to entry functions', () => { showEffects: true, }, }); + await toolbox.client.waitForTransaction({ digest: result.digest }); expect(result.effects?.status.status).toEqual('success'); }); }); diff --git a/sdk/typescript/test/e2e/multisig.test.ts b/sdk/typescript/test/e2e/multisig.test.ts index 205a9ee8cdf3c..53a50966638f0 100644 --- a/sdk/typescript/test/e2e/multisig.test.ts +++ b/sdk/typescript/test/e2e/multisig.test.ts @@ -105,6 +105,7 @@ describe('MultiSig with zklogin signature', () => { signature, options: { showEffects: true }, }); + await client.waitForTransaction({ digest: result.digest }); // check the execution result and digest. const localDigest = await tx.getDigest({ client }); diff --git a/sdk/typescript/test/e2e/objects.test.ts b/sdk/typescript/test/e2e/objects.test.ts index 2a74636be5fcb..7f696b0404c7c 100644 --- a/sdk/typescript/test/e2e/objects.test.ts +++ b/sdk/typescript/test/e2e/objects.test.ts @@ -119,10 +119,11 @@ describe('Object Reading API', () => { // Transfer the entire gas object: tx.transferObjects([tx.gas], normalizeSuiAddress('0x2')); - await toolbox.client.signAndExecuteTransaction({ + const { digest } = await toolbox.client.signAndExecuteTransaction({ signer: toolbox.keypair, transaction: tx, }); + await toolbox.client.waitForTransaction({ digest }); const res = await toolbox.client.tryGetPastObject({ id: data[0].coinObjectId, diff --git a/sdk/typescript/test/e2e/txn-builder.test.ts b/sdk/typescript/test/e2e/txn-builder.test.ts index e92beced30a69..75486345488be 100644 --- a/sdk/typescript/test/e2e/txn-builder.test.ts +++ b/sdk/typescript/test/e2e/txn-builder.test.ts @@ -220,6 +220,7 @@ async function validateTransaction(client: SuiClient, signer: Keypair, tx: Trans showEffects: true, }, }); + await client.waitForTransaction({ digest: result.digest }); expect(localDigest).toEqual(result.digest); expect(result.effects?.status.status).toEqual('success'); } From 4357ac6eda8b6a61d6d401f8768ed77119c3a8c1 Mon Sep 17 00:00:00 2001 From: hayes-mysten <135670682+hayes-mysten@users.noreply.github.com> Date: Mon, 19 Aug 2024 11:19:08 -0700 Subject: [PATCH 154/232] Add docs on zklogin signature verification and using waitForTransaction (#18982) ## Description Describe the changes or additions included in this PR. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .changeset/perfect-peaches-retire.md | 5 ++++ .../typescript/cryptography/keypairs.mdx | 19 +++++++++++++ .../transaction-building/basics.mdx | 27 +++++++++++++++++++ sdk/typescript/src/verify/verify.ts | 10 ++++--- sdk/typescript/src/zklogin/publickey.ts | 1 + 5 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 .changeset/perfect-peaches-retire.md diff --git a/.changeset/perfect-peaches-retire.md b/.changeset/perfect-peaches-retire.md new file mode 100644 index 0000000000000..2e03c18385da0 --- /dev/null +++ b/.changeset/perfect-peaches-retire.md @@ -0,0 +1,5 @@ +--- +'@mysten/sui': patch +--- + +Add options argument to verifyTransactionSignature diff --git a/sdk/docs/pages/typescript/cryptography/keypairs.mdx b/sdk/docs/pages/typescript/cryptography/keypairs.mdx index bdabb9a8cec9f..b779184e20059 100644 --- a/sdk/docs/pages/typescript/cryptography/keypairs.mdx +++ b/sdk/docs/pages/typescript/cryptography/keypairs.mdx @@ -118,6 +118,25 @@ if (publicKey.toSuiAddress() !== keypair.getPublicKey().toSuiAddress()) { } ``` +## Verifying zkLogin signatures + +ZkLogin signatures can't be verified purely on the client. When verifying a zkLogin signature, the +SDK uses the GraphQL API to verify the signature. This will work for mainnet signatures without any +additional configuration. + +For testnet signatures, you will need to provide a testnet GraphQL Client: + +```typescript +import { SuiGraphQLClient } from '@mysten/sui/graphql'; +import { verifyPersonalMessageSignature } from '@mysten/sui/verify'; + +const publicKey = await verifyPersonalMessageSignature(message, zkSignature, { + client: new SuiGraphQLClient({ + url: 'https://sui-testnet.mystenlabs.com/graphql', + }), +}); +``` + ## Deriving a key pair from a mnemonic The Sui TypeScript SDK supports deriving a key pair from a mnemonic phrase. This can be useful when diff --git a/sdk/docs/pages/typescript/transaction-building/basics.mdx b/sdk/docs/pages/typescript/transaction-building/basics.mdx index 4c53a4a5ea2fd..4a3d5bc82ae7d 100644 --- a/sdk/docs/pages/typescript/transaction-building/basics.mdx +++ b/sdk/docs/pages/typescript/transaction-building/basics.mdx @@ -53,6 +53,33 @@ After you have the transaction defined, you can directly execute it with a signe client.signAndExecuteTransaction({ signer: keypair, transaction: tx }); ``` +## Observing the results of a transaction + +When you use `client.signAndExecuteTransaction` or `client.executeTransactionBlock`, the transaction +will be finalized on the blockchain before the function resolves, but the effects of the transaction +may not be immediately observable. + +There are 2 ways to observe the results of a transaction. Methods like +`client.signAndExecuteTransaction` accept an `options` object with options like `showObjectChanges` +and `showBalanceChanges` (see +[the SuiClient docs for more details](/typescript/sui-client#arguments)). These options will cause +the request to contain additional details about the effects of the transaction that can be +immediately displayed to the user, or used for further processing in your application. + +The other way effects of transactions can be observed is by querying other RPC methods like +`client.getBalances` that return objects or balances owned by a specific address. These RPC calls +depend on the RPC node having indexed the effects of the transaction, which may not have happened +immediately after a transaction has been executed. To ensure that effects of a transaction are +represented in future RPC calls, you can use the `waitForTransaction` method on the client: + +```typescript +const result = await client.signAndExecuteTransaction({ signer: keypair, transaction: tx }); +await client.waitForTransaction({ digest: result.digest }); +``` + +Once `waitForTransaction` resolves, any future RPC calls will be guaranteed to reflect the effects +of the transaction. + ## Transactions Programmable Transactions have two key concepts: inputs and transactions. diff --git a/sdk/typescript/src/verify/verify.ts b/sdk/typescript/src/verify/verify.ts index 49cbcba897b64..7ba6fcf63a6ab 100644 --- a/sdk/typescript/src/verify/verify.ts +++ b/sdk/typescript/src/verify/verify.ts @@ -45,8 +45,9 @@ export async function verifyPersonalMessageSignature( export async function verifyTransactionSignature( transaction: Uint8Array, signature: string, + options: { client?: SuiGraphQLClient } = {}, ): Promise { - const parsedSignature = parseSignature(signature); + const parsedSignature = parseSignature(signature, options); if ( !(await parsedSignature.publicKey.verifyTransaction( @@ -102,10 +103,13 @@ export function publicKeyFromRawBytes( } } -export function publicKeyFromSuiBytes(publicKey: string | Uint8Array) { +export function publicKeyFromSuiBytes( + publicKey: string | Uint8Array, + options: { client?: SuiGraphQLClient } = {}, +) { const bytes = typeof publicKey === 'string' ? fromB64(publicKey) : publicKey; const signatureScheme = SIGNATURE_FLAG_TO_SCHEME[bytes[0] as SignatureFlag]; - return publicKeyFromRawBytes(signatureScheme, bytes.slice(1)); + return publicKeyFromRawBytes(signatureScheme, bytes.slice(1), options); } diff --git a/sdk/typescript/src/zklogin/publickey.ts b/sdk/typescript/src/zklogin/publickey.ts index 2e6bf8b3e6a34..d42afefb0559d 100644 --- a/sdk/typescript/src/zklogin/publickey.ts +++ b/sdk/typescript/src/zklogin/publickey.ts @@ -71,6 +71,7 @@ export class ZkLoginPublicIdentifier extends PublicKey { verifyPersonalMessage(message: Uint8Array, signature: Uint8Array | string): Promise { const parsedSignature = parseSerializedZkLoginSignature(signature); const address = new ZkLoginPublicIdentifier(parsedSignature.publicKey).toSuiAddress(); + return graphqlVerifyZkLoginSignature({ address: address, bytes: toB64(message), From 4019dd790d81db0abd2dea6de524a745b9397501 Mon Sep 17 00:00:00 2001 From: hayes-mysten <135670682+hayes-mysten@users.noreply.github.com> Date: Mon, 19 Aug 2024 12:00:44 -0700 Subject: [PATCH 155/232] [sdk] Add default budget to serial transaction executor (#19013) ## Description Describe the changes or additions included in this PR. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .changeset/silly-berries-rush.md | 5 +++++ .changeset/ten-panthers-shave.md | 5 +++++ sdk/docs/pages/typescript/executors.mdx | 7 +++++++ .../src/transactions/executor/parallel.ts | 17 ++++++++++------- .../src/transactions/executor/serial.ts | 14 ++++++++++++-- 5 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 .changeset/silly-berries-rush.md create mode 100644 .changeset/ten-panthers-shave.md diff --git a/.changeset/silly-berries-rush.md b/.changeset/silly-berries-rush.md new file mode 100644 index 0000000000000..271e294f7c416 --- /dev/null +++ b/.changeset/silly-berries-rush.md @@ -0,0 +1,5 @@ +--- +'@mysten/sui': minor +--- + +Add default budget to transactions executed through the SerialTransactionExecutor class diff --git a/.changeset/ten-panthers-shave.md b/.changeset/ten-panthers-shave.md new file mode 100644 index 0000000000000..492770e3a704e --- /dev/null +++ b/.changeset/ten-panthers-shave.md @@ -0,0 +1,5 @@ +--- +'@mysten/sui': minor +--- + +Add options argument to executeTransaction methods on transaction executor classes diff --git a/sdk/docs/pages/typescript/executors.mdx b/sdk/docs/pages/typescript/executors.mdx index 2080f26af6908..05eeed173ea9c 100644 --- a/sdk/docs/pages/typescript/executors.mdx +++ b/sdk/docs/pages/typescript/executors.mdx @@ -25,6 +25,13 @@ when multiple transactions use the same objects. `SerialTransactionExecutor` maintains an internal queue, so you don't need to wait for previous transactions to finish before sending the next one. +`SerialTransactionExecutor` can be configured with a number of options: + +- `client`: An instance of `SuiClient` used to execute transactions. +- `signer`: The signer/keypair used for signed transactions. +- `defaultBudget`: The default budget for transactions, which will be used if the transaction does + not specify a budget (default `50_000_000n`), + ```ts import { getFullnodeUrl, SuiClient } from '@mysten/sui/client'; import { SerialTransactionExecutor } from '@mysten/sui/transactions'; diff --git a/sdk/typescript/src/transactions/executor/parallel.ts b/sdk/typescript/src/transactions/executor/parallel.ts index a74eefc714920..da47dbe1325b2 100644 --- a/sdk/typescript/src/transactions/executor/parallel.ts +++ b/sdk/typescript/src/transactions/executor/parallel.ts @@ -5,7 +5,7 @@ import { toB64 } from '@mysten/bcs'; import { bcs } from '../../bcs/index.js'; import type { SuiObjectRef } from '../../bcs/types.js'; -import type { SuiClient } from '../../client/index.js'; +import type { SuiClient, SuiTransactionBlockResponseOptions } from '../../client/index.js'; import type { Signer } from '../../cryptography/index.js'; import type { ObjectCacheOptions } from '../ObjectCache.js'; import { Transaction } from '../Transaction.js'; @@ -104,7 +104,7 @@ export class ParallelTransactionExecutor { await this.#updateCache(() => this.#waitForLastDigest()); } - async executeTransaction(transaction: Transaction) { + async executeTransaction(transaction: Transaction, options?: SuiTransactionBlockResponseOptions) { const { promise, resolve, reject } = promiseWithResolvers<{ digest: string; effects: string; @@ -113,7 +113,7 @@ export class ParallelTransactionExecutor { const execute = () => { this.#executeQueue.runTask(() => { - const promise = this.#execute(transaction, usedObjects); + const promise = this.#execute(transaction, usedObjects, options); return promise.then(resolve, reject); }); @@ -174,7 +174,11 @@ export class ParallelTransactionExecutor { return usedObjects; } - async #execute(transaction: Transaction, usedObjects: Set) { + async #execute( + transaction: Transaction, + usedObjects: Set, + options?: SuiTransactionBlockResponseOptions, + ) { let gasCoin!: CoinWithBalance; try { transaction.setSenderIfNotSet(this.#signer.toSuiAddress()); @@ -186,9 +190,7 @@ export class ParallelTransactionExecutor { transaction.setGasPrice(await this.#getGasPrice()); } - if (!data.gasData.budget) { - transaction.setGasBudget(this.#defaultGasBudget); - } + transaction.setGasBudgetIfNotSet(this.#defaultGasBudget); await this.#updateCache(); gasCoin = await this.#getGasCoin(); @@ -213,6 +215,7 @@ export class ParallelTransactionExecutor { transaction: bytes, signature, options: { + ...options, showEffects: true, }, }); diff --git a/sdk/typescript/src/transactions/executor/serial.ts b/sdk/typescript/src/transactions/executor/serial.ts index f301825ff887e..603dfeae7bd6e 100644 --- a/sdk/typescript/src/transactions/executor/serial.ts +++ b/sdk/typescript/src/transactions/executor/serial.ts @@ -4,7 +4,7 @@ import { toB64 } from '@mysten/bcs'; import { bcs } from '../../bcs/index.js'; -import type { SuiClient } from '../../client/index.js'; +import type { SuiClient, SuiTransactionBlockResponseOptions } from '../../client/index.js'; import type { Signer } from '../../cryptography/keypair.js'; import type { ObjectCacheOptions } from '../ObjectCache.js'; import { isTransaction, Transaction } from '../Transaction.js'; @@ -15,15 +15,20 @@ export class SerialTransactionExecutor { #queue = new SerialQueue(); #signer: Signer; #cache: CachingTransactionExecutor; + #defaultGasBudget: bigint; constructor({ signer, + defaultGasBudget = 50_000_000n, ...options }: Omit & { client: SuiClient; signer: Signer; + /** The gasBudget to use if the transaction has not defined it's own gasBudget, defaults to `50_000_000n` */ + defaultGasBudget?: bigint; }) { this.#signer = signer; + this.#defaultGasBudget = defaultGasBudget; this.#cache = new CachingTransactionExecutor({ client: options.client, cache: options.cache, @@ -63,6 +68,7 @@ export class SerialTransactionExecutor { copy.setGasPayment([gasCoin]); } + copy.setGasBudgetIfNotSet(this.#defaultGasBudget); copy.setSenderIfNotSet(this.#signer.toSuiAddress()); return this.#cache.buildTransaction({ transaction: copy }); @@ -76,7 +82,10 @@ export class SerialTransactionExecutor { return this.#cache.waitForLastTransaction(); } - executeTransaction(transaction: Transaction | Uint8Array) { + executeTransaction( + transaction: Transaction | Uint8Array, + options?: SuiTransactionBlockResponseOptions, + ) { return this.#queue.runTask(async () => { const bytes = isTransaction(transaction) ? await this.#buildTransaction(transaction) @@ -87,6 +96,7 @@ export class SerialTransactionExecutor { .executeTransaction({ signature, transaction: bytes, + options, }) .catch(async (error) => { await this.resetCache(); From 143cd9dc29896c1e042c4b5bc2c176fa306c5552 Mon Sep 17 00:00:00 2001 From: hayes-mysten <135670682+hayes-mysten@users.noreply.github.com> Date: Mon, 19 Aug 2024 12:04:28 -0700 Subject: [PATCH 156/232] [ts sdk] Add new tx.object methods (#18940) ## Description Describe the changes or additions included in this PR. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .changeset/five-ads-dream.md | 10 + apps/wallet/vitest.config.ts | 1 - sdk/deepbook/vitest.config.ts | 4 +- sdk/graphql-transport/vitest.config.ts | 4 +- sdk/kiosk/vitest.config.ts | 4 +- sdk/suins-toolkit/vitest.config.ts | 4 +- sdk/typescript/src/transactions/Arguments.ts | 5 +- .../src/transactions/Transaction.ts | 59 +++--- sdk/typescript/src/transactions/object.ts | 17 ++ sdk/typescript/test/e2e/object-cache.test.ts | 2 +- .../test/e2e/parallel-executor.test.ts | 2 +- .../test/e2e/serial-executor.test.ts | 2 +- sdk/typescript/test/unit/arguments.test.ts | 48 +++++ .../test/unit/object-inputs.test.ts | 183 ++++++++++++++++++ sdk/typescript/vitest.config.ts | 4 +- 15 files changed, 308 insertions(+), 41 deletions(-) create mode 100644 .changeset/five-ads-dream.md create mode 100644 sdk/typescript/src/transactions/object.ts create mode 100644 sdk/typescript/test/unit/object-inputs.test.ts diff --git a/.changeset/five-ads-dream.md b/.changeset/five-ads-dream.md new file mode 100644 index 0000000000000..0707b4f5b2322 --- /dev/null +++ b/.changeset/five-ads-dream.md @@ -0,0 +1,10 @@ +--- +'@mysten/sui': minor +--- + +Add new tx.object methods for defining inputs for well known object ids: + +- `tx.object.system()`: `0x5` +- `tx.object.clock()`: `0x6` +- `tx.object.random()`: `0x8` +- `tx.object.denyList()`: `0x403` diff --git a/apps/wallet/vitest.config.ts b/apps/wallet/vitest.config.ts index 9623017969816..22ecf350712f4 100644 --- a/apps/wallet/vitest.config.ts +++ b/apps/wallet/vitest.config.ts @@ -10,7 +10,6 @@ export default defineConfig({ exclude: [...configDefaults.exclude, 'tests/**'], // TODO: Create custom extension environment. environment: 'happy-dom', - minThreads: 1, setupFiles: ['./testSetup.ts'], restoreMocks: true, }, diff --git a/sdk/deepbook/vitest.config.ts b/sdk/deepbook/vitest.config.ts index 71d126eaef86c..07452fe1219c2 100644 --- a/sdk/deepbook/vitest.config.ts +++ b/sdk/deepbook/vitest.config.ts @@ -5,8 +5,8 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { - minThreads: 1, - maxThreads: 8, + minWorkers: 1, + maxWorkers: 4, hookTimeout: 1000000, testTimeout: 1000000, env: { diff --git a/sdk/graphql-transport/vitest.config.ts b/sdk/graphql-transport/vitest.config.ts index 2619e62f59c25..c728327ae6aaa 100644 --- a/sdk/graphql-transport/vitest.config.ts +++ b/sdk/graphql-transport/vitest.config.ts @@ -5,8 +5,8 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { - minThreads: 1, - maxThreads: 8, + minWorkers: 1, + maxWorkers: 4, hookTimeout: 1000000, testTimeout: 1000000, env: { diff --git a/sdk/kiosk/vitest.config.ts b/sdk/kiosk/vitest.config.ts index 71d126eaef86c..07452fe1219c2 100644 --- a/sdk/kiosk/vitest.config.ts +++ b/sdk/kiosk/vitest.config.ts @@ -5,8 +5,8 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { - minThreads: 1, - maxThreads: 8, + minWorkers: 1, + maxWorkers: 4, hookTimeout: 1000000, testTimeout: 1000000, env: { diff --git a/sdk/suins-toolkit/vitest.config.ts b/sdk/suins-toolkit/vitest.config.ts index ea622e90c555f..874241689c250 100644 --- a/sdk/suins-toolkit/vitest.config.ts +++ b/sdk/suins-toolkit/vitest.config.ts @@ -5,8 +5,8 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { - minThreads: 1, - maxThreads: 8, + minWorkers: 1, + maxWorkers: 4, hookTimeout: 1000000, testTimeout: 1000000, }, diff --git a/sdk/typescript/src/transactions/Arguments.ts b/sdk/typescript/src/transactions/Arguments.ts index 920b3ff203423..ff384a69c31d1 100644 --- a/sdk/typescript/src/transactions/Arguments.ts +++ b/sdk/typescript/src/transactions/Arguments.ts @@ -2,12 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 import type { Inputs } from './Inputs.js'; +import { createObjectMethods } from './object.js'; import { createPure } from './pure.js'; import type { Transaction, TransactionObjectInput } from './Transaction.js'; export const Arguments = { pure: createPure((value) => (tx: Transaction) => tx.pure(value)), - object: (value: TransactionObjectInput) => (tx: Transaction) => tx.object(value), + object: createObjectMethods( + (value: TransactionObjectInput) => (tx: Transaction) => tx.object(value), + ), sharedObjectRef: (...args: Parameters<(typeof Inputs)['SharedObjectRef']>) => (tx: Transaction) => diff --git a/sdk/typescript/src/transactions/Transaction.ts b/sdk/typescript/src/transactions/Transaction.ts index 75cac56c04735..c8912a00076cc 100644 --- a/sdk/typescript/src/transactions/Transaction.ts +++ b/sdk/typescript/src/transactions/Transaction.ts @@ -22,6 +22,7 @@ import type { TransactionPlugin, } from './json-rpc-resolver.js'; import { resolveTransactionData } from './json-rpc-resolver.js'; +import { createObjectMethods } from './object.js'; import { createPure } from './pure.js'; import { TransactionDataBuilder } from './TransactionData.js'; import { getIdFromCallArg } from './utils.js'; @@ -252,37 +253,43 @@ export class Transaction { /** * Add a new object input to the transaction. */ - object(value: TransactionObjectInput): { $kind: 'Input'; Input: number; type?: 'object' } { - if (typeof value === 'function') { - return this.object(value(this)); - } + object = createObjectMethods( + (value: TransactionObjectInput): { $kind: 'Input'; Input: number; type?: 'object' } => { + if (typeof value === 'function') { + return this.object(value(this)); + } - if (typeof value === 'object' && is(Argument, value)) { - return value as { $kind: 'Input'; Input: number; type?: 'object' }; - } + if (typeof value === 'object' && is(Argument, value)) { + return value as { $kind: 'Input'; Input: number; type?: 'object' }; + } - const id = getIdFromCallArg(value); + const id = getIdFromCallArg(value); - const inserted = this.#data.inputs.find((i) => id === getIdFromCallArg(i)); + const inserted = this.#data.inputs.find((i) => id === getIdFromCallArg(i)); - // Upgrade shared object inputs to mutable if needed: - if (inserted?.Object?.SharedObject && typeof value === 'object' && value.Object?.SharedObject) { - inserted.Object.SharedObject.mutable = - inserted.Object.SharedObject.mutable || value.Object.SharedObject.mutable; - } + // Upgrade shared object inputs to mutable if needed: + if ( + inserted?.Object?.SharedObject && + typeof value === 'object' && + value.Object?.SharedObject + ) { + inserted.Object.SharedObject.mutable = + inserted.Object.SharedObject.mutable || value.Object.SharedObject.mutable; + } - return inserted - ? { $kind: 'Input', Input: this.#data.inputs.indexOf(inserted), type: 'object' } - : this.#data.addInput( - 'object', - typeof value === 'string' - ? { - $kind: 'UnresolvedObject', - UnresolvedObject: { objectId: normalizeSuiAddress(value) }, - } - : value, - ); - } + return inserted + ? { $kind: 'Input', Input: this.#data.inputs.indexOf(inserted), type: 'object' } + : this.#data.addInput( + 'object', + typeof value === 'string' + ? { + $kind: 'UnresolvedObject', + UnresolvedObject: { objectId: normalizeSuiAddress(value) }, + } + : value, + ); + }, + ); /** * Add a new object input to the transaction using the fully-resolved object reference. diff --git a/sdk/typescript/src/transactions/object.ts b/sdk/typescript/src/transactions/object.ts new file mode 100644 index 0000000000000..ff83fac9dc750 --- /dev/null +++ b/sdk/typescript/src/transactions/object.ts @@ -0,0 +1,17 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +import type { TransactionObjectInput } from './Transaction.js'; + +export function createObjectMethods(makeObject: (value: TransactionObjectInput) => T) { + function object(value: TransactionObjectInput) { + return makeObject(value); + } + + object.system = () => object('0x5'); + object.clock = () => object('0x6'); + object.random = () => object('0x8'); + object.denyList = () => object('0x403'); + + return object; +} diff --git a/sdk/typescript/test/e2e/object-cache.test.ts b/sdk/typescript/test/e2e/object-cache.test.ts index 5edb3cea8c630..3577b87389406 100644 --- a/sdk/typescript/test/e2e/object-cache.test.ts +++ b/sdk/typescript/test/e2e/object-cache.test.ts @@ -10,7 +10,7 @@ import { CachingTransactionExecutor } from '../../src/transactions/executor/cach import { normalizeSuiAddress } from '../../src/utils'; import { setup, TestToolbox } from './utils/setup'; -describe('CachingTransactionExecutor', async () => { +describe('CachingTransactionExecutor', { retry: 3 }, async () => { let toolbox: TestToolbox; let packageId: string; let rawPackageId: string; diff --git a/sdk/typescript/test/e2e/parallel-executor.test.ts b/sdk/typescript/test/e2e/parallel-executor.test.ts index 88d7c58b73ea9..7d0d036fd4043 100644 --- a/sdk/typescript/test/e2e/parallel-executor.test.ts +++ b/sdk/typescript/test/e2e/parallel-executor.test.ts @@ -39,7 +39,7 @@ afterAll(() => { vi.restoreAllMocks(); }); -describe('ParallelTransactionExecutor', () => { +describe('ParallelTransactionExecutor', { retry: 3 }, () => { beforeEach(async () => { await executor.resetCache(); vi.clearAllMocks(); diff --git a/sdk/typescript/test/e2e/serial-executor.test.ts b/sdk/typescript/test/e2e/serial-executor.test.ts index 830d32fa8bd5c..d14ba26780dbb 100644 --- a/sdk/typescript/test/e2e/serial-executor.test.ts +++ b/sdk/typescript/test/e2e/serial-executor.test.ts @@ -29,7 +29,7 @@ afterAll(() => { vi.restoreAllMocks(); }); -describe('SerialExecutor', () => { +describe('SerialExecutor', { retry: 3 }, () => { beforeEach(async () => { vi.clearAllMocks(); await executor.resetCache(); diff --git a/sdk/typescript/test/unit/arguments.test.ts b/sdk/typescript/test/unit/arguments.test.ts index d737e5cc51e02..736b0be0eab50 100644 --- a/sdk/typescript/test/unit/arguments.test.ts +++ b/sdk/typescript/test/unit/arguments.test.ts @@ -26,6 +26,10 @@ describe('Arguments helpers', () => { digest: toB58(new Uint8Array(32).fill(0x1)), }), Arguments.pure.address('0x2'), + Arguments.object.system(), + Arguments.object.clock(), + Arguments.object.random(), + Arguments.object.denyList(), ]; const tx = new Transaction(); @@ -67,6 +71,26 @@ describe('Arguments helpers', () => { "Input": 4, "type": "pure", }, + { + "$kind": "Input", + "Input": 5, + "type": "object", + }, + { + "$kind": "Input", + "Input": 6, + "type": "object", + }, + { + "$kind": "Input", + "Input": 7, + "type": "object", + }, + { + "$kind": "Input", + "Input": 8, + "type": "object", + }, ], "function": "bar", "module": "foo", @@ -128,6 +152,30 @@ describe('Arguments helpers', () => { "bytes": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI=", }, }, + { + "$kind": "UnresolvedObject", + "UnresolvedObject": { + "objectId": "0x0000000000000000000000000000000000000000000000000000000000000005", + }, + }, + { + "$kind": "UnresolvedObject", + "UnresolvedObject": { + "objectId": "0x0000000000000000000000000000000000000000000000000000000000000006", + }, + }, + { + "$kind": "UnresolvedObject", + "UnresolvedObject": { + "objectId": "0x0000000000000000000000000000000000000000000000000000000000000008", + }, + }, + { + "$kind": "UnresolvedObject", + "UnresolvedObject": { + "objectId": "0x0000000000000000000000000000000000000000000000000000000000000403", + }, + }, ], "sender": null, "version": 2, diff --git a/sdk/typescript/test/unit/object-inputs.test.ts b/sdk/typescript/test/unit/object-inputs.test.ts new file mode 100644 index 0000000000000..ddde51d40331f --- /dev/null +++ b/sdk/typescript/test/unit/object-inputs.test.ts @@ -0,0 +1,183 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +import { toB58 } from '@mysten/bcs'; +import { describe, expect, it } from 'vitest'; + +import { Transaction } from '../../src/transactions'; + +describe('Transaction inputs', () => { + it('can use tx methods for creating inputs', async () => { + const tx = new Transaction(); + + tx.moveCall({ + target: '0x2::foo::bar', + arguments: [ + tx.object('0x123'), + tx.receivingRef({ + objectId: '1', + version: '123', + digest: toB58(new Uint8Array(32).fill(0x1)), + }), + tx.sharedObjectRef({ + objectId: '2', + mutable: true, + initialSharedVersion: '123', + }), + tx.objectRef({ + objectId: '3', + version: '123', + digest: toB58(new Uint8Array(32).fill(0x1)), + }), + tx.pure.address('0x2'), + tx.object.system(), + tx.object.clock(), + tx.object.random(), + tx.object.denyList(), + ], + }); + + expect(tx.getData()).toMatchInlineSnapshot(` + { + "commands": [ + { + "$kind": "MoveCall", + "MoveCall": { + "arguments": [ + { + "$kind": "Input", + "Input": 0, + "type": "object", + }, + { + "$kind": "Input", + "Input": 1, + "type": "object", + }, + { + "$kind": "Input", + "Input": 2, + "type": "object", + }, + { + "$kind": "Input", + "Input": 3, + "type": "object", + }, + { + "$kind": "Input", + "Input": 4, + "type": "pure", + }, + { + "$kind": "Input", + "Input": 5, + "type": "object", + }, + { + "$kind": "Input", + "Input": 6, + "type": "object", + }, + { + "$kind": "Input", + "Input": 7, + "type": "object", + }, + { + "$kind": "Input", + "Input": 8, + "type": "object", + }, + ], + "function": "bar", + "module": "foo", + "package": "0x0000000000000000000000000000000000000000000000000000000000000002", + "typeArguments": [], + }, + }, + ], + "expiration": null, + "gasData": { + "budget": null, + "owner": null, + "payment": null, + "price": null, + }, + "inputs": [ + { + "$kind": "UnresolvedObject", + "UnresolvedObject": { + "objectId": "0x0000000000000000000000000000000000000000000000000000000000000123", + }, + }, + { + "$kind": "Object", + "Object": { + "$kind": "Receiving", + "Receiving": { + "digest": "4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi", + "objectId": "0x0000000000000000000000000000000000000000000000000000000000000001", + "version": "123", + }, + }, + }, + { + "$kind": "Object", + "Object": { + "$kind": "SharedObject", + "SharedObject": { + "initialSharedVersion": "123", + "mutable": true, + "objectId": "0x0000000000000000000000000000000000000000000000000000000000000002", + }, + }, + }, + { + "$kind": "Object", + "Object": { + "$kind": "ImmOrOwnedObject", + "ImmOrOwnedObject": { + "digest": "4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi", + "objectId": "0x0000000000000000000000000000000000000000000000000000000000000003", + "version": "123", + }, + }, + }, + { + "$kind": "Pure", + "Pure": { + "bytes": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI=", + }, + }, + { + "$kind": "UnresolvedObject", + "UnresolvedObject": { + "objectId": "0x0000000000000000000000000000000000000000000000000000000000000005", + }, + }, + { + "$kind": "UnresolvedObject", + "UnresolvedObject": { + "objectId": "0x0000000000000000000000000000000000000000000000000000000000000006", + }, + }, + { + "$kind": "UnresolvedObject", + "UnresolvedObject": { + "objectId": "0x0000000000000000000000000000000000000000000000000000000000000008", + }, + }, + { + "$kind": "UnresolvedObject", + "UnresolvedObject": { + "objectId": "0x0000000000000000000000000000000000000000000000000000000000000403", + }, + }, + ], + "sender": null, + "version": 2, + } + `); + }); +}); diff --git a/sdk/typescript/vitest.config.ts b/sdk/typescript/vitest.config.ts index e58acd7035af6..231c84d702bbe 100644 --- a/sdk/typescript/vitest.config.ts +++ b/sdk/typescript/vitest.config.ts @@ -5,8 +5,8 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { - minThreads: 1, - maxThreads: 8, + minWorkers: 1, + maxWorkers: 4, hookTimeout: 1000000, testTimeout: 1000000, env: { From 00a974d8cab189c17566d7edb98efd518360cd0c Mon Sep 17 00:00:00 2001 From: hayes-mysten <135670682+hayes-mysten@users.noreply.github.com> Date: Mon, 19 Aug 2024 12:25:40 -0700 Subject: [PATCH 157/232] [ts sdk] Add global registry for transaction plugins (#18928) ## Description Describe the changes or additions included in this PR. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .changeset/witty-wombats-study.md | 5 +++ .../src/transactions/Transaction.ts | 40 ++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 .changeset/witty-wombats-study.md diff --git a/.changeset/witty-wombats-study.md b/.changeset/witty-wombats-study.md new file mode 100644 index 0000000000000..5c65da8c78354 --- /dev/null +++ b/.changeset/witty-wombats-study.md @@ -0,0 +1,5 @@ +--- +'@mysten/sui': minor +--- + +Add global registry for transaction plugins diff --git a/sdk/typescript/src/transactions/Transaction.ts b/sdk/typescript/src/transactions/Transaction.ts index c8912a00076cc..30a7723beeb01 100644 --- a/sdk/typescript/src/transactions/Transaction.ts +++ b/sdk/typescript/src/transactions/Transaction.ts @@ -99,12 +99,37 @@ export function isTransaction(obj: unknown): obj is Transaction { export type TransactionObjectInput = string | CallArg | TransactionObjectArgument; +const modulePluginRegistry = { + buildPlugins: [] as TransactionPlugin[], + serializationPlugins: [] as TransactionPlugin[], +}; + +const TRANSACTION_REGISTRY_KEY = Symbol.for('@mysten/transaction/registry'); +function getGlobalPluginRegistry() { + try { + const target = globalThis as { + [TRANSACTION_REGISTRY_KEY]?: { + buildPlugins: TransactionPlugin[]; + serializationPlugins: TransactionPlugin[]; + }; + }; + + if (!target[TRANSACTION_REGISTRY_KEY]) { + target[TRANSACTION_REGISTRY_KEY] = modulePluginRegistry; + } + + return target[TRANSACTION_REGISTRY_KEY]; + } catch (e) { + return modulePluginRegistry; + } +} + /** * Transaction Builder */ export class Transaction { - #serializationPlugins: TransactionPlugin[] = []; - #buildPlugins: TransactionPlugin[] = []; + #serializationPlugins: TransactionPlugin[]; + #buildPlugins: TransactionPlugin[]; #intentResolvers = new Map(); /** @@ -143,6 +168,14 @@ export class Transaction { return newTransaction; } + static registerGlobalSerializationPlugin(step: TransactionPlugin) { + getGlobalPluginRegistry().serializationPlugins.push(step); + } + + static registerGlobalBuildPlugin(step: TransactionPlugin) { + getGlobalPluginRegistry().buildPlugins.push(step); + } + addSerializationPlugin(step: TransactionPlugin) { this.#serializationPlugins.push(step); } @@ -242,7 +275,10 @@ export class Transaction { } constructor() { + const globalPlugins = getGlobalPluginRegistry(); this.#data = new TransactionDataBuilder(); + this.#buildPlugins = [...globalPlugins.buildPlugins]; + this.#serializationPlugins = [...globalPlugins.serializationPlugins]; } /** Returns an argument for the gas coin, to be used in a transaction. */ From 746ba272f8f9848c2719010f628fb7238f3f861f Mon Sep 17 00:00:00 2001 From: "sui-merge-bot[bot]" <114704316+sui-merge-bot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 12:49:59 -0700 Subject: [PATCH 158/232] Version Packages (#19035) This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @mysten/sui@1.7.0 ### Minor Changes - 143cd9d: Add new tx.object methods for defining inputs for well known object ids: - `tx.object.system()`: `0x5` - `tx.object.clock()`: `0x6` - `tx.object.random()`: `0x8` - `tx.object.denyList()`: `0x403` - 4019dd7: Add default budget to transactions executed through the SerialTransactionExecutor class - 4019dd7: Add options argument to executeTransaction methods on transaction executor classes - 00a974d: Add global registry for transaction plugins ### Patch Changes - 4357ac6: Add options argument to verifyTransactionSignature ## @mysten/create-dapp@0.3.17 ### Patch Changes - Updated dependencies [143cd9d] - Updated dependencies [4357ac6] - Updated dependencies [4019dd7] - Updated dependencies [4019dd7] - Updated dependencies [00a974d] - @mysten/sui@1.7.0 - @mysten/dapp-kit@0.14.17 ## @mysten/dapp-kit@0.14.17 ### Patch Changes - Updated dependencies [143cd9d] - Updated dependencies [4357ac6] - Updated dependencies [4019dd7] - Updated dependencies [4019dd7] - Updated dependencies [00a974d] - @mysten/sui@1.7.0 - @mysten/wallet-standard@0.13.2 - @mysten/zksend@0.10.6 ## @mysten/deepbook@0.8.16 ### Patch Changes - Updated dependencies [143cd9d] - Updated dependencies [4357ac6] - Updated dependencies [4019dd7] - Updated dependencies [4019dd7] - Updated dependencies [00a974d] - @mysten/sui@1.7.0 ## @mysten/deepbook-v3@0.3.2 ### Patch Changes - Updated dependencies [143cd9d] - Updated dependencies [4357ac6] - Updated dependencies [4019dd7] - Updated dependencies [4019dd7] - Updated dependencies [00a974d] - @mysten/sui@1.7.0 ## @mysten/enoki@0.3.17 ### Patch Changes - Updated dependencies [143cd9d] - Updated dependencies [4357ac6] - Updated dependencies [4019dd7] - Updated dependencies [4019dd7] - Updated dependencies [00a974d] - @mysten/sui@1.7.0 - @mysten/zklogin@0.7.17 ## @mysten/graphql-transport@0.2.16 ### Patch Changes - Updated dependencies [143cd9d] - Updated dependencies [4357ac6] - Updated dependencies [4019dd7] - Updated dependencies [4019dd7] - Updated dependencies [00a974d] - @mysten/sui@1.7.0 ## @mysten/kiosk@0.9.16 ### Patch Changes - Updated dependencies [143cd9d] - Updated dependencies [4357ac6] - Updated dependencies [4019dd7] - Updated dependencies [4019dd7] - Updated dependencies [00a974d] - @mysten/sui@1.7.0 ## @mysten/suins-toolkit@0.5.16 ### Patch Changes - Updated dependencies [143cd9d] - Updated dependencies [4357ac6] - Updated dependencies [4019dd7] - Updated dependencies [4019dd7] - Updated dependencies [00a974d] - @mysten/sui@1.7.0 ## @mysten/wallet-standard@0.13.2 ### Patch Changes - Updated dependencies [143cd9d] - Updated dependencies [4357ac6] - Updated dependencies [4019dd7] - Updated dependencies [4019dd7] - Updated dependencies [00a974d] - @mysten/sui@1.7.0 ## @mysten/zklogin@0.7.17 ### Patch Changes - Updated dependencies [143cd9d] - Updated dependencies [4357ac6] - Updated dependencies [4019dd7] - Updated dependencies [4019dd7] - Updated dependencies [00a974d] - @mysten/sui@1.7.0 ## @mysten/zksend@0.10.6 ### Patch Changes - Updated dependencies [143cd9d] - Updated dependencies [4357ac6] - Updated dependencies [4019dd7] - Updated dependencies [4019dd7] - Updated dependencies [00a974d] - @mysten/sui@1.7.0 - @mysten/wallet-standard@0.13.2 Co-authored-by: github-actions[bot] --- .changeset/five-ads-dream.md | 10 ---------- .changeset/perfect-peaches-retire.md | 5 ----- .changeset/silly-berries-rush.md | 5 ----- .changeset/ten-panthers-shave.md | 5 ----- .changeset/witty-wombats-study.md | 5 ----- sdk/create-dapp/CHANGELOG.md | 12 ++++++++++++ sdk/create-dapp/package.json | 2 +- sdk/dapp-kit/CHANGELOG.md | 13 +++++++++++++ sdk/dapp-kit/package.json | 2 +- sdk/deepbook-v3/CHANGELOG.md | 11 +++++++++++ sdk/deepbook-v3/package.json | 2 +- sdk/deepbook/CHANGELOG.md | 11 +++++++++++ sdk/deepbook/package.json | 2 +- sdk/enoki/CHANGELOG.md | 12 ++++++++++++ sdk/enoki/package.json | 2 +- sdk/graphql-transport/CHANGELOG.md | 11 +++++++++++ sdk/graphql-transport/package.json | 2 +- sdk/kiosk/CHANGELOG.md | 11 +++++++++++ sdk/kiosk/package.json | 2 +- sdk/suins-toolkit/CHANGELOG.md | 11 +++++++++++ sdk/suins-toolkit/package.json | 2 +- sdk/typescript/CHANGELOG.md | 19 +++++++++++++++++++ sdk/typescript/package.json | 2 +- sdk/typescript/src/version.ts | 2 +- sdk/wallet-standard/CHANGELOG.md | 11 +++++++++++ sdk/wallet-standard/package.json | 2 +- sdk/zklogin/CHANGELOG.md | 11 +++++++++++ sdk/zklogin/package.json | 2 +- sdk/zksend/CHANGELOG.md | 12 ++++++++++++ sdk/zksend/package.json | 2 +- 30 files changed, 158 insertions(+), 43 deletions(-) delete mode 100644 .changeset/five-ads-dream.md delete mode 100644 .changeset/perfect-peaches-retire.md delete mode 100644 .changeset/silly-berries-rush.md delete mode 100644 .changeset/ten-panthers-shave.md delete mode 100644 .changeset/witty-wombats-study.md diff --git a/.changeset/five-ads-dream.md b/.changeset/five-ads-dream.md deleted file mode 100644 index 0707b4f5b2322..0000000000000 --- a/.changeset/five-ads-dream.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@mysten/sui': minor ---- - -Add new tx.object methods for defining inputs for well known object ids: - -- `tx.object.system()`: `0x5` -- `tx.object.clock()`: `0x6` -- `tx.object.random()`: `0x8` -- `tx.object.denyList()`: `0x403` diff --git a/.changeset/perfect-peaches-retire.md b/.changeset/perfect-peaches-retire.md deleted file mode 100644 index 2e03c18385da0..0000000000000 --- a/.changeset/perfect-peaches-retire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@mysten/sui': patch ---- - -Add options argument to verifyTransactionSignature diff --git a/.changeset/silly-berries-rush.md b/.changeset/silly-berries-rush.md deleted file mode 100644 index 271e294f7c416..0000000000000 --- a/.changeset/silly-berries-rush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@mysten/sui': minor ---- - -Add default budget to transactions executed through the SerialTransactionExecutor class diff --git a/.changeset/ten-panthers-shave.md b/.changeset/ten-panthers-shave.md deleted file mode 100644 index 492770e3a704e..0000000000000 --- a/.changeset/ten-panthers-shave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@mysten/sui': minor ---- - -Add options argument to executeTransaction methods on transaction executor classes diff --git a/.changeset/witty-wombats-study.md b/.changeset/witty-wombats-study.md deleted file mode 100644 index 5c65da8c78354..0000000000000 --- a/.changeset/witty-wombats-study.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@mysten/sui': minor ---- - -Add global registry for transaction plugins diff --git a/sdk/create-dapp/CHANGELOG.md b/sdk/create-dapp/CHANGELOG.md index 2a4eca5756568..8b32d4af32408 100644 --- a/sdk/create-dapp/CHANGELOG.md +++ b/sdk/create-dapp/CHANGELOG.md @@ -1,5 +1,17 @@ # @mysten/create-dapp +## 0.3.17 + +### Patch Changes + +- Updated dependencies [143cd9d] +- Updated dependencies [4357ac6] +- Updated dependencies [4019dd7] +- Updated dependencies [4019dd7] +- Updated dependencies [00a974d] + - @mysten/sui@1.7.0 + - @mysten/dapp-kit@0.14.17 + ## 0.3.16 ### Patch Changes diff --git a/sdk/create-dapp/package.json b/sdk/create-dapp/package.json index fc0aaf9d83d75..ef18887ca7d3e 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.16", + "version": "0.3.17", "license": "Apache-2.0", "files": [ "CHANGELOG.md", diff --git a/sdk/dapp-kit/CHANGELOG.md b/sdk/dapp-kit/CHANGELOG.md index 325c408779cf6..05a307a8bfc72 100644 --- a/sdk/dapp-kit/CHANGELOG.md +++ b/sdk/dapp-kit/CHANGELOG.md @@ -1,5 +1,18 @@ # @mysten/dapp-kit +## 0.14.17 + +### Patch Changes + +- Updated dependencies [143cd9d] +- Updated dependencies [4357ac6] +- Updated dependencies [4019dd7] +- Updated dependencies [4019dd7] +- Updated dependencies [00a974d] + - @mysten/sui@1.7.0 + - @mysten/wallet-standard@0.13.2 + - @mysten/zksend@0.10.6 + ## 0.14.16 ### Patch Changes diff --git a/sdk/dapp-kit/package.json b/sdk/dapp-kit/package.json index c08e0a972f9a0..4547755a41712 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.16", + "version": "0.14.17", "license": "Apache-2.0", "files": [ "CHANGELOG.md", diff --git a/sdk/deepbook-v3/CHANGELOG.md b/sdk/deepbook-v3/CHANGELOG.md index 4c9b6efd51710..7430929925995 100644 --- a/sdk/deepbook-v3/CHANGELOG.md +++ b/sdk/deepbook-v3/CHANGELOG.md @@ -1,5 +1,16 @@ # @mysten/deepbook-v3 +## 0.3.2 + +### Patch Changes + +- Updated dependencies [143cd9d] +- Updated dependencies [4357ac6] +- Updated dependencies [4019dd7] +- Updated dependencies [4019dd7] +- Updated dependencies [00a974d] + - @mysten/sui@1.7.0 + ## 0.3.1 ### Patch Changes diff --git a/sdk/deepbook-v3/package.json b/sdk/deepbook-v3/package.json index e522ff4ac4cfb..fb17602959b0a 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.3.1", + "version": "0.3.2", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", diff --git a/sdk/deepbook/CHANGELOG.md b/sdk/deepbook/CHANGELOG.md index 1d346a039b693..f9fdfffcfd923 100644 --- a/sdk/deepbook/CHANGELOG.md +++ b/sdk/deepbook/CHANGELOG.md @@ -1,5 +1,16 @@ # @mysten/deepbook +## 0.8.16 + +### Patch Changes + +- Updated dependencies [143cd9d] +- Updated dependencies [4357ac6] +- Updated dependencies [4019dd7] +- Updated dependencies [4019dd7] +- Updated dependencies [00a974d] + - @mysten/sui@1.7.0 + ## 0.8.15 ### Patch Changes diff --git a/sdk/deepbook/package.json b/sdk/deepbook/package.json index c11789a3e9f34..dd90fff911719 100644 --- a/sdk/deepbook/package.json +++ b/sdk/deepbook/package.json @@ -2,7 +2,7 @@ "name": "@mysten/deepbook", "author": "Mysten Labs ", "description": "Sui Deepbook SDK", - "version": "0.8.15", + "version": "0.8.16", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", diff --git a/sdk/enoki/CHANGELOG.md b/sdk/enoki/CHANGELOG.md index 50929112b623f..0005f85e4d648 100644 --- a/sdk/enoki/CHANGELOG.md +++ b/sdk/enoki/CHANGELOG.md @@ -1,5 +1,17 @@ # @mysten/enoki +## 0.3.17 + +### Patch Changes + +- Updated dependencies [143cd9d] +- Updated dependencies [4357ac6] +- Updated dependencies [4019dd7] +- Updated dependencies [4019dd7] +- Updated dependencies [00a974d] + - @mysten/sui@1.7.0 + - @mysten/zklogin@0.7.17 + ## 0.3.16 ### Patch Changes diff --git a/sdk/enoki/package.json b/sdk/enoki/package.json index 26543cb1d6966..bd8098049b7ad 100644 --- a/sdk/enoki/package.json +++ b/sdk/enoki/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/enoki", - "version": "0.3.16", + "version": "0.3.17", "description": "TODO: Description", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/graphql-transport/CHANGELOG.md b/sdk/graphql-transport/CHANGELOG.md index f6fa15b284900..fcfa14f6c2b08 100644 --- a/sdk/graphql-transport/CHANGELOG.md +++ b/sdk/graphql-transport/CHANGELOG.md @@ -1,5 +1,16 @@ # @mysten/graphql-transport +## 0.2.16 + +### Patch Changes + +- Updated dependencies [143cd9d] +- Updated dependencies [4357ac6] +- Updated dependencies [4019dd7] +- Updated dependencies [4019dd7] +- Updated dependencies [00a974d] + - @mysten/sui@1.7.0 + ## 0.2.15 ### Patch Changes diff --git a/sdk/graphql-transport/package.json b/sdk/graphql-transport/package.json index 7b396110e747a..08ee1bb6507d3 100644 --- a/sdk/graphql-transport/package.json +++ b/sdk/graphql-transport/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/graphql-transport", - "version": "0.2.15", + "version": "0.2.16", "description": "A GraphQL transport to allow SuiClient to work with RPC 2.0", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/kiosk/CHANGELOG.md b/sdk/kiosk/CHANGELOG.md index 1993ef4851266..b41d2684a66a7 100644 --- a/sdk/kiosk/CHANGELOG.md +++ b/sdk/kiosk/CHANGELOG.md @@ -1,5 +1,16 @@ # @mysten/kiosk +## 0.9.16 + +### Patch Changes + +- Updated dependencies [143cd9d] +- Updated dependencies [4357ac6] +- Updated dependencies [4019dd7] +- Updated dependencies [4019dd7] +- Updated dependencies [00a974d] + - @mysten/sui@1.7.0 + ## 0.9.15 ### Patch Changes diff --git a/sdk/kiosk/package.json b/sdk/kiosk/package.json index 1a529d482e0d7..3f4dcea4135d5 100644 --- a/sdk/kiosk/package.json +++ b/sdk/kiosk/package.json @@ -2,7 +2,7 @@ "name": "@mysten/kiosk", "author": "Mysten Labs ", "description": "Sui Kiosk library", - "version": "0.9.15", + "version": "0.9.16", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", diff --git a/sdk/suins-toolkit/CHANGELOG.md b/sdk/suins-toolkit/CHANGELOG.md index a7e448c820f74..ba5294afb0e87 100644 --- a/sdk/suins-toolkit/CHANGELOG.md +++ b/sdk/suins-toolkit/CHANGELOG.md @@ -1,5 +1,16 @@ # @mysten/suins-toolkit +## 0.5.16 + +### Patch Changes + +- Updated dependencies [143cd9d] +- Updated dependencies [4357ac6] +- Updated dependencies [4019dd7] +- Updated dependencies [4019dd7] +- Updated dependencies [00a974d] + - @mysten/sui@1.7.0 + ## 0.5.15 ### Patch Changes diff --git a/sdk/suins-toolkit/package.json b/sdk/suins-toolkit/package.json index 2330823f46b50..434d8dedf04d7 100644 --- a/sdk/suins-toolkit/package.json +++ b/sdk/suins-toolkit/package.json @@ -2,7 +2,7 @@ "name": "@mysten/suins-toolkit", "author": "Mysten Labs ", "description": "SuiNS TypeScript SDK", - "version": "0.5.15", + "version": "0.5.16", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", diff --git a/sdk/typescript/CHANGELOG.md b/sdk/typescript/CHANGELOG.md index df8b8bcb3e706..1d3e424afca54 100644 --- a/sdk/typescript/CHANGELOG.md +++ b/sdk/typescript/CHANGELOG.md @@ -1,5 +1,24 @@ # @mysten/sui.js +## 1.7.0 + +### Minor Changes + +- 143cd9d: Add new tx.object methods for defining inputs for well known object ids: + + - `tx.object.system()`: `0x5` + - `tx.object.clock()`: `0x6` + - `tx.object.random()`: `0x8` + - `tx.object.denyList()`: `0x403` + +- 4019dd7: Add default budget to transactions executed through the SerialTransactionExecutor class +- 4019dd7: Add options argument to executeTransaction methods on transaction executor classes +- 00a974d: Add global registry for transaction plugins + +### Patch Changes + +- 4357ac6: Add options argument to verifyTransactionSignature + ## 1.6.0 ### Minor Changes diff --git a/sdk/typescript/package.json b/sdk/typescript/package.json index bfe5f446379d0..c544d7a672d78 100644 --- a/sdk/typescript/package.json +++ b/sdk/typescript/package.json @@ -3,7 +3,7 @@ "author": "Mysten Labs ", "description": "Sui TypeScript API(Work in Progress)", "homepage": "https://sdk.mystenlabs.com", - "version": "1.6.0", + "version": "1.7.0", "license": "Apache-2.0", "sideEffects": false, "files": [ diff --git a/sdk/typescript/src/version.ts b/sdk/typescript/src/version.ts index 3145c701257e6..3615ca82466f4 100644 --- a/sdk/typescript/src/version.ts +++ b/sdk/typescript/src/version.ts @@ -3,5 +3,5 @@ // This file is generated by genversion.mjs. Do not edit it directly. -export const PACKAGE_VERSION = '1.6.0'; +export const PACKAGE_VERSION = '1.7.0'; export const TARGETED_RPC_VERSION = '1.32.0'; diff --git a/sdk/wallet-standard/CHANGELOG.md b/sdk/wallet-standard/CHANGELOG.md index 528b98fbf37e3..3c88ee60b92f2 100644 --- a/sdk/wallet-standard/CHANGELOG.md +++ b/sdk/wallet-standard/CHANGELOG.md @@ -1,5 +1,16 @@ # @mysten/wallet-standard +## 0.13.2 + +### Patch Changes + +- Updated dependencies [143cd9d] +- Updated dependencies [4357ac6] +- Updated dependencies [4019dd7] +- Updated dependencies [4019dd7] +- Updated dependencies [00a974d] + - @mysten/sui@1.7.0 + ## 0.13.1 ### Patch Changes diff --git a/sdk/wallet-standard/package.json b/sdk/wallet-standard/package.json index de8f30e2cf204..378c6b92b6e92 100644 --- a/sdk/wallet-standard/package.json +++ b/sdk/wallet-standard/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/wallet-standard", - "version": "0.13.1", + "version": "0.13.2", "description": "A suite of standard utilities for implementing wallets based on the Wallet Standard.", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/zklogin/CHANGELOG.md b/sdk/zklogin/CHANGELOG.md index df730218b15fb..011e1a18c2bc2 100644 --- a/sdk/zklogin/CHANGELOG.md +++ b/sdk/zklogin/CHANGELOG.md @@ -1,5 +1,16 @@ # @mysten/zklogin +## 0.7.17 + +### Patch Changes + +- Updated dependencies [143cd9d] +- Updated dependencies [4357ac6] +- Updated dependencies [4019dd7] +- Updated dependencies [4019dd7] +- Updated dependencies [00a974d] + - @mysten/sui@1.7.0 + ## 0.7.16 ### Patch Changes diff --git a/sdk/zklogin/package.json b/sdk/zklogin/package.json index 0b0a942bcf6fe..e42717b9670c0 100644 --- a/sdk/zklogin/package.json +++ b/sdk/zklogin/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/zklogin", - "version": "0.7.16", + "version": "0.7.17", "description": "Utilities for interacting with zkLogin in Sui", "license": "Apache-2.0", "author": "Mysten Labs ", diff --git a/sdk/zksend/CHANGELOG.md b/sdk/zksend/CHANGELOG.md index f412d4e578f58..7b41cfc38edc4 100644 --- a/sdk/zksend/CHANGELOG.md +++ b/sdk/zksend/CHANGELOG.md @@ -1,5 +1,17 @@ # @mysten/zksend +## 0.10.6 + +### Patch Changes + +- Updated dependencies [143cd9d] +- Updated dependencies [4357ac6] +- Updated dependencies [4019dd7] +- Updated dependencies [4019dd7] +- Updated dependencies [00a974d] + - @mysten/sui@1.7.0 + - @mysten/wallet-standard@0.13.2 + ## 0.10.5 ### Patch Changes diff --git a/sdk/zksend/package.json b/sdk/zksend/package.json index f83f941144117..e105d7deb5bbf 100644 --- a/sdk/zksend/package.json +++ b/sdk/zksend/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/zksend", - "version": "0.10.5", + "version": "0.10.6", "description": "TODO: Write Description", "license": "Apache-2.0", "author": "Mysten Labs ", From a18f77bc932508302856ac2758b6322c6b7aac4e Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Mon, 19 Aug 2024 21:20:38 +0100 Subject: [PATCH 159/232] revert(18170): patch for fetching latest dynamic field state for zkloginverifysignature (#19017) ## Description This reverts commit de8ae3c38c01011ade7e8bc0de70ef7076514ed4. This change was introduced in the absence of optimisations that are going out as part of the next GraphQL release, so reverting so that we get the fully correct behaviour again (consistent lookup). ## Test plan Existing zkLoginVerifySignature E2E tests. --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .../src/types/dynamic_field.rs | 61 +------------------ .../src/types/zklogin_verify_signature.rs | 11 ++-- 2 files changed, 6 insertions(+), 66 deletions(-) diff --git a/crates/sui-graphql-rpc/src/types/dynamic_field.rs b/crates/sui-graphql-rpc/src/types/dynamic_field.rs index 94e1c1cea0017..ebbc86df505a4 100644 --- a/crates/sui-graphql-rpc/src/types/dynamic_field.rs +++ b/crates/sui-graphql-rpc/src/types/dynamic_field.rs @@ -3,17 +3,13 @@ use async_graphql::connection::{Connection, CursorType, Edge}; use async_graphql::*; -use diesel::query_dsl::methods::FilterDsl; -use diesel::{ExpressionMethods, OptionalExtension}; use move_core_types::annotated_value::{self as A, MoveStruct}; -use sui_indexer::models::objects::{StoredHistoryObject, StoredObject}; -use sui_indexer::schema::objects; +use sui_indexer::models::objects::StoredHistoryObject; use sui_indexer::types::OwnerType; use sui_types::dynamic_field::{derive_dynamic_field_id, DynamicFieldInfo, DynamicFieldType}; use super::available_range::AvailableRange; use super::cursor::{Page, Target}; -use super::move_object::MoveObjectDowncastError; use super::object::{self, deserialize_move_struct, Object, ObjectKind, ObjectLookup}; use super::type_filter::ExactTypeFilter; use super::{ @@ -21,7 +17,7 @@ use super::{ }; use crate::consistency::{build_objects_query, View}; use crate::data::package_resolver::PackageResolver; -use crate::data::{Db, DbConnection, QueryExecutor}; +use crate::data::{Db, QueryExecutor}; use crate::error::Error; use crate::filter; use crate::raw_query::RawQuery; @@ -184,59 +180,6 @@ impl DynamicField { super_.map(Self::try_from).transpose() } - /// Due to recent performance degradations, the existing `DynamicField::query` method is now - /// consistently timing out. This impacts features like `verify_zklogin_signature`, which - /// depends on resolving a dynamic field of 0x7 authenticator state. This method is a temporary - /// fix by fetching the data from the live `objects` table, and should only be used by - /// `verify_zklogin_signature`. Once we have fixed `objects_snapshot` table lag and backfilled - /// the `objects_version` table, this will no longer be needed. - pub(crate) async fn query_latest_dynamic_field( - db: &Db, - parent: SuiAddress, - name: DynamicFieldName, - kind: DynamicFieldType, - checkpoint_viewed_at: u64, - ) -> Result, Error> { - let type_ = match kind { - DynamicFieldType::DynamicField => name.type_.0, - DynamicFieldType::DynamicObject => { - DynamicFieldInfo::dynamic_object_field_wrapper(name.type_.0).into() - } - }; - - let field_id = derive_dynamic_field_id(parent, &type_, &name.bcs.0) - .map_err(|e| Error::Internal(format!("Failed to derive dynamic field id: {e}")))?; - - let object_id = SuiAddress::from(field_id); - - let Some(stored_obj): Option = db - .execute(move |conn| { - conn.first(move || { - objects::dsl::objects.filter(objects::dsl::object_id.eq(object_id.into_vec())) - }) - .optional() - }) - .await - .map_err(|e| Error::Internal(format!("Failed to fetch dynamic field: {e}")))? - else { - return Ok(None); - }; - - let history_object = StoredHistoryObject::from(stored_obj); - let gql_object = - Object::try_from_stored_history_object(history_object, checkpoint_viewed_at, None)?; - - let super_ = match MoveObject::try_from(&gql_object) { - Ok(object) => Some(object), - Err(MoveObjectDowncastError::WrappedOrDeleted) => None, - Err(MoveObjectDowncastError::NotAMoveObject) => { - return Err(Error::Internal(format!("{object_id} is not a Move object"))); - } - }; - - super_.map(Self::try_from).transpose() - } - /// Query the `db` for a `page` of dynamic fields attached to object with ID `parent`. The /// returned dynamic fields are bound by the `parent_version` if provided - each field will be /// the latest version at or before the provided version. If `parent_version` is not provided, diff --git a/crates/sui-graphql-rpc/src/types/zklogin_verify_signature.rs b/crates/sui-graphql-rpc/src/types/zklogin_verify_signature.rs index 3f568010b662f..eb51fe116f263 100644 --- a/crates/sui-graphql-rpc/src/types/zklogin_verify_signature.rs +++ b/crates/sui-graphql-rpc/src/types/zklogin_verify_signature.rs @@ -78,14 +78,11 @@ pub(crate) async fn verify_zklogin_signature( )); }; - // fetch on-chain JWKs from dynamic field of system object. Due to recent performance - // degradations, the existing `DynamicField::query` method is now consistently timing out. As a - // workaround, we are using the `query_latest_dynamic_field` method, which fetches object data - // from the live `objects` table. This can be reverted once the `objects_snapshot` lag issue is - // fixed and we've backfilled the `objects_version` table. - let df = DynamicField::query_latest_dynamic_field( - ctx.data_unchecked(), + // fetch on-chain JWKs from dynamic field of system object. + let df = DynamicField::query( + ctx, SUI_AUTHENTICATOR_STATE_ADDRESS.into(), + None, DynamicFieldName { type_: ExactTypeFilter(TypeTag::U64), bcs: Base64(bcs::to_bytes(&1u64).unwrap()), From 3799dcf5f8e79179e145dfaf211336b7fa6a8b25 Mon Sep 17 00:00:00 2001 From: Eugene Boguslavsky Date: Mon, 19 Aug 2024 13:44:05 -0700 Subject: [PATCH 160/232] Create generate release workflow (#19037) ## Description Create generate release workflow ## Test plan Will test it after it lands --- .github/workflows/release-notes-generator.yml | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 .github/workflows/release-notes-generator.yml diff --git a/.github/workflows/release-notes-generator.yml b/.github/workflows/release-notes-generator.yml new file mode 100644 index 0000000000000..818db6ccff249 --- /dev/null +++ b/.github/workflows/release-notes-generator.yml @@ -0,0 +1,52 @@ +name: Release Notes Generator + +on: + workflow_dispatch: + inputs: + release_tag: + description: 'Sui Release Tag' + type: string + required: true + previous_branch: + description: 'Previous Release Branch (Ex: releases/sui-vX.XX.X-release)' + type: string + required: true + current_branch: + description: 'Current Release Branch (Ex: releases/sui-vX.XX.X-release)' + type: string + required: true + +jobs: + get-release-notes: + name: Get Release Notes for ${{ inputs.release_tag }} release + runs-on: ubuntu-latest + + steps: + - name: Checkout sui repo main branch + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # Pin v4.1.1 + with: + fetch-depth: 0 + ref: main + + - name: Generate Release Notes + id: release_notes + shell: bash + working-directory: ./ + run: | + echo "release_notes=$(./scripts/release_notes.py generate ${{ inputs.previous_branch }} ${{ inputs.current_branch }})" >> $GITHUB_ENV + if [[ ${{ inputs.release_tag }} == devnet* ]]; then + echo "pre_release=true" >> $GITHUB_ENV + else + echo "pre_release=false" >> $GITHUB_ENV + fi + + - name: Create Release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token + with: + tag_name: ${{ inputs.release_tag }} + release_name: ${{ inputs.release_tag }} + body: ${{ env.release_notes }} + draft: false + prerelease: ${{ env.pre_release }} From 43c144565114215ea815b6f8626420a6d5624fae Mon Sep 17 00:00:00 2001 From: Andrey Chursin Date: Mon, 19 Aug 2024 21:21:05 +0000 Subject: [PATCH 161/232] Measure RocksDB batch put bytes by cf (#19038) We measure number of bytes by cf for `put`, and measure total written in the batch by DB, but do not have breakdown for batch written bytes by cf. This metrics adds the breakdown by cf for batch writes --- crates/typed-store/src/metrics.rs | 11 +++++++++++ crates/typed-store/src/rocks/mod.rs | 8 +++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/crates/typed-store/src/metrics.rs b/crates/typed-store/src/metrics.rs index 8b92fe983832c..e3da36b8443bb 100644 --- a/crates/typed-store/src/metrics.rs +++ b/crates/typed-store/src/metrics.rs @@ -250,6 +250,7 @@ pub struct OperationMetrics { pub rocksdb_multiget_bytes: HistogramVec, pub rocksdb_put_latency_seconds: HistogramVec, pub rocksdb_put_bytes: HistogramVec, + pub rocksdb_batch_put_bytes: HistogramVec, pub rocksdb_delete_latency_seconds: HistogramVec, pub rocksdb_deletes: IntCounterVec, pub rocksdb_batch_commit_latency_seconds: HistogramVec, @@ -343,6 +344,16 @@ impl OperationMetrics { registry, ) .unwrap(), + rocksdb_batch_put_bytes: register_histogram_vec_with_registry!( + "rocksdb_batch_put_bytes", + "Rocksdb batch put call puts data size in bytes", + &["cf_name"], + prometheus::exponential_buckets(1.0, 4.0, 15) + .unwrap() + .to_vec(), + registry, + ) + .unwrap(), rocksdb_delete_latency_seconds: register_histogram_vec_with_registry!( "rocksdb_delete_latency_seconds", "Rocksdb delete latency in seconds", diff --git a/crates/typed-store/src/rocks/mod.rs b/crates/typed-store/src/rocks/mod.rs index 734e10f4c7269..de33dd7952ca3 100644 --- a/crates/typed-store/src/rocks/mod.rs +++ b/crates/typed-store/src/rocks/mod.rs @@ -1451,15 +1451,21 @@ impl DBBatch { if !Arc::ptr_eq(&db.rocksdb, &self.rocksdb) { return Err(TypedStoreError::CrossDBBatch); } - + let mut total = 0usize; new_vals .into_iter() .try_for_each::<_, Result<_, TypedStoreError>>(|(k, v)| { let k_buf = be_fix_int_ser(k.borrow())?; let v_buf = bcs::to_bytes(v.borrow()).map_err(typed_store_err_from_bcs_err)?; + total += k_buf.len() + v_buf.len(); self.batch.put_cf(&db.cf(), k_buf, v_buf); Ok(()) })?; + self.db_metrics + .op_metrics + .rocksdb_batch_put_bytes + .with_label_values(&[&db.cf]) + .observe(total as f64); Ok(self) } From 66f6d484935110fdf420b4e8b7594a0ec7d7f886 Mon Sep 17 00:00:00 2001 From: Tim Zakian <2895723+tzakian@users.noreply.github.com> Date: Mon, 19 Aug 2024 15:53:24 -0700 Subject: [PATCH 162/232] [replay] Fix issue when replaying transactions taht query existence of dynamic fields before creating it (#19041) ## Description This fixes a bug in the replay tool where if the transaction checked for the existence of the dynamic field before creating it, we would fail to be able to replay the transaction locally since the replay tool was not properly handling the underlying error. ## Test plan Tested manually on a failing transaction. --- crates/sui-replay/src/replay.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/sui-replay/src/replay.rs b/crates/sui-replay/src/replay.rs index 8ccbf693d98f6..b41e0eddf3b0e 100644 --- a/crates/sui-replay/src/replay.rs +++ b/crates/sui-replay/src/replay.rs @@ -635,6 +635,12 @@ impl LocalExec { error!("Object {id} {version} {digest} was deleted on RPC server."); Ok(None) } + // This is a child object which was not found in the store (e.g., due to exists + // check before creating the dynamic field). + Err(ReplayEngineError::ObjectVersionNotFound { id, version }) => { + info!("Object {id} {version} not found on RPC server -- this may have been pruned or never existed."); + Ok(None) + } Err(err) => Err(ReplayEngineError::SuiRpcError { err: err.to_string(), }), From 101221975ac5e3545bec33f02cf9c0a5d5d70668 Mon Sep 17 00:00:00 2001 From: Eugene Boguslavsky Date: Mon, 19 Aug 2024 17:05:56 -0700 Subject: [PATCH 163/232] Release Notes Generator (#19039) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Instead of running`./scripts/release_notes.py generate` locally, we should be doing this via a workflow and also be able to create a new release within the same workflow. ## Test plan https://github.com/MystenLabs/sui/actions/runs/10462421870/job/28972625617 Screenshot 2024-08-19 at 4 31 48 PM --- .github/workflows/release-notes-generator.yml | 29 +++++++++++++++---- release_notes.txt | 0 scripts/release_notes.py | 2 +- 3 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 release_notes.txt diff --git a/.github/workflows/release-notes-generator.yml b/.github/workflows/release-notes-generator.yml index 818db6ccff249..993efe023436f 100644 --- a/.github/workflows/release-notes-generator.yml +++ b/.github/workflows/release-notes-generator.yml @@ -1,4 +1,6 @@ -name: Release Notes Generator +name: Create Sui Release with Release Notes + +concurrency: ${{ github.workflow }}-${{ inputs.release_tag }} on: workflow_dispatch: @@ -16,24 +18,41 @@ on: type: string required: true +env: + RELEASE_NOTES_FILE: "./release_notes.txt" + jobs: get-release-notes: name: Get Release Notes for ${{ inputs.release_tag }} release runs-on: ubuntu-latest steps: - - name: Checkout sui repo main branch + - name: Get commits for branches + shell: bash + working-directory: ./ + run: | + echo "previous_commit=$(curl https://api.github.com/repos/MystenLabs/sui/commits/${{ inputs.previous_branch }} | jq .sha)" >> $GITHUB_ENV + echo "current_commit=$(curl https://api.github.com/repos/MystenLabs/sui/commits/${{ inputs.current_branch }} | jq .sha)" >> $GITHUB_ENV + + - name: Checkout main uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # Pin v4.1.1 with: fetch-depth: 0 ref: main + + - name: Setup Python + uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # pin@v5.0.0 + with: + python-version: 3.10.10 - name: Generate Release Notes - id: release_notes shell: bash working-directory: ./ run: | - echo "release_notes=$(./scripts/release_notes.py generate ${{ inputs.previous_branch }} ${{ inputs.current_branch }})" >> $GITHUB_ENV + GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} python ./scripts/release_notes.py generate ${{ env.previous_commit }} ${{ env.current_commit }} | tee -a ${{ env.RELEASE_NOTES_FILE }} + echo "---" >> ${{ env.RELEASE_NOTES_FILE }} + echo "#### Full Log: https://github.com/MystenLabs/sui/commits/${{ inputs.release_tag }}" >> ${{ env.RELEASE_NOTES_FILE }} + if [[ ${{ inputs.release_tag }} == devnet* ]]; then echo "pre_release=true" >> $GITHUB_ENV else @@ -47,6 +66,6 @@ jobs: with: tag_name: ${{ inputs.release_tag }} release_name: ${{ inputs.release_tag }} - body: ${{ env.release_notes }} + body_path: ${{ env.RELEASE_NOTES_FILE }} draft: false prerelease: ${{ env.pre_release }} diff --git a/release_notes.txt b/release_notes.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/scripts/release_notes.py b/scripts/release_notes.py index 4629a304db631..fe5c4e68f83a0 100755 --- a/scripts/release_notes.py +++ b/scripts/release_notes.py @@ -304,7 +304,7 @@ def do_generate(from_, to): print(f"## {impacted}") if impacted == "Protocol": - print(f"Sui Protocol Version in this release: {protocol_version}") + print(f"#### Sui Protocol Version in this release: `{protocol_version}`") print() for pr, note in reversed(notes): From c26c1217777fe2d16283b5d504bf9966711c79c4 Mon Sep 17 00:00:00 2001 From: Xun Li Date: Mon, 19 Aug 2024 22:08:47 -0700 Subject: [PATCH 164/232] [LocalExecution] Remove local execution loop from FN (#19032) ## Description We are about to deprecate local execution. This PR gets rid of the loop that subscribes effects from quorum driver and execute them locally. This was an optimization and it should be safe to remove at this point. Removing this also allows us to see clear metrics on the number of local execution requests received from the network. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .../sui-core/src/transaction_orchestrator.rs | 57 ++----------------- 1 file changed, 6 insertions(+), 51 deletions(-) diff --git a/crates/sui-core/src/transaction_orchestrator.rs b/crates/sui-core/src/transaction_orchestrator.rs index 1db608f9e7b6c..b4a0d1462097b 100644 --- a/crates/sui-core/src/transaction_orchestrator.rs +++ b/crates/sui-core/src/transaction_orchestrator.rs @@ -111,22 +111,15 @@ where ); let effects_receiver = quorum_driver_handler.subscribe_to_effects(); - let state_clone = validator_state.clone(); let metrics = Arc::new(TransactionOrchestratorMetrics::new(prometheus_registry)); - let metrics_clone = metrics.clone(); let pending_tx_log = Arc::new(WritePathPendingTransactionLog::new( parent_path.join("fullnode_pending_transactions"), )); let pending_tx_log_clone = pending_tx_log.clone(); let _local_executor_handle = { spawn_monitored_task!(async move { - Self::loop_execute_finalized_tx_locally( - state_clone, - effects_receiver, - pending_tx_log_clone, - metrics_clone, - ) - .await; + Self::loop_execute_finalized_tx_locally(effects_receiver, pending_tx_log_clone) + .await; }) }; Self::schedule_txes_in_log(pending_tx_log.clone(), quorum_driver_handler.clone()); @@ -428,57 +421,19 @@ where } async fn loop_execute_finalized_tx_locally( - validator_state: Arc, mut effects_receiver: Receiver, pending_transaction_log: Arc, - metrics: Arc, ) { loop { match effects_receiver.recv().await { - Ok(Ok((transaction, QuorumDriverResponse { effects_cert, .. }))) => { + Ok(Ok((transaction, ..))) => { let tx_digest = transaction.digest(); if let Err(err) = pending_transaction_log.finish_transaction(tx_digest) { - panic!( - "Failed to finish transaction {tx_digest} in pending transaction log: {err}" + error!( + ?tx_digest, + "Failed to finish transaction in pending transaction log: {err}" ); } - - if transaction.contains_shared_object() { - // Do not locally execute transactions with shared objects, as this can - // cause forks until MVCC is merged. - continue; - } - - let epoch_store = validator_state.load_epoch_store_one_call_per_task(); - - // This is a redundant verification, but SignatureVerifier will cache the - // previous result. - let transaction = match epoch_store.verify_transaction(transaction) { - Ok(transaction) => transaction, - Err(err) => { - // This should be impossible, since we verified the transaction - // before sending it to quorum driver. - error!( - ?err, - "Transaction signature failed to verify after quorum driver execution." - ); - continue; - } - }; - - let executable_tx = VerifiedExecutableTransaction::new_from_quorum_execution( - transaction, - effects_cert.executed_epoch(), - ); - - let _ = Self::execute_finalized_tx_locally_with_timeout( - &validator_state, - &epoch_store, - &executable_tx, - &effects_cert, - &metrics, - ) - .await; } Ok(Err((tx_digest, _err))) => { if let Err(err) = pending_transaction_log.finish_transaction(&tx_digest) { From 56a05c87bf795bee57039ec5979c45465ace44dd Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Mon, 6 May 2024 00:57:52 +0100 Subject: [PATCH 165/232] [GraphQL] Leverage `objects_version` table. (#17543) ## Description Use the `objects_version` table to speed up point look-ups (via data loaders) for historical objects (ID + version), and dynamic fields (object look-up bounding version by parent ID). With this change, the restriction of accessing dynamic fields only within the available range is dropped. ## Test plan ``` sui$ cargo nextest run -p sui-graphql-rpc sui$ cargo nextest run -p sui-graphql-e2e-tests --features pg_integration. ``` Perform a query that involves fetching a large number of dynamic fields, which should now be fast. The following example, fetching dynamic fields on a deepbook pool loads 50 dynamic fields in about 5s from cold (which also requires loading packages for resolution), and then 2s from there: ``` query { owner( address: "0x029170bfa0a1677054263424fe4f9960c7cf05d359f6241333994c8830772bdb" ) { dynamicFields { pageInfo { hasNextPage endCursor } nodes { name { type { repr } json } value { ... on MoveValue { type { repr } json } ... on MoveObject { contents { json type { repr } } } } } } } } ``` ## Stack - #17686 - #17687 - #17688 - #17689 - #17691 - #17694 - #17695 - #17542 - #17726 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [x] GraphQL: Dynamic fields can now be looked up on any historical object (not just objects in the available range). - [ ] CLI: - [ ] Rust SDK: --- .../dynamic_fields/dynamic_fields.exp | 12 +- .../src/types/dynamic_field.rs | 9 +- crates/sui-graphql-rpc/src/types/object.rs | 226 +++++++++++++----- crates/sui-graphql-rpc/src/types/owner.rs | 7 +- 4 files changed, 186 insertions(+), 68 deletions(-) diff --git a/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/dynamic_fields.exp b/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/dynamic_fields.exp index afbfbb96ca3a1..51b7b407d25ad 100644 --- a/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/dynamic_fields.exp +++ b/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/dynamic_fields.exp @@ -1166,7 +1166,17 @@ task 34, lines 497-528: Response: { "data": { "parent_version_4": { - "dfAtParentVersion4_outside_range": null + "dfAtParentVersion4_outside_range": { + "name": { + "bcs": "A2RmMQ==", + "type": { + "repr": "0x0000000000000000000000000000000000000000000000000000000000000001::string::String" + } + }, + "value": { + "json": "df1" + } + } }, "parent_version_6": { "dfAtParentVersion6": null diff --git a/crates/sui-graphql-rpc/src/types/dynamic_field.rs b/crates/sui-graphql-rpc/src/types/dynamic_field.rs index ebbc86df505a4..60e05ed610512 100644 --- a/crates/sui-graphql-rpc/src/types/dynamic_field.rs +++ b/crates/sui-graphql-rpc/src/types/dynamic_field.rs @@ -10,7 +10,7 @@ use sui_types::dynamic_field::{derive_dynamic_field_id, DynamicFieldInfo, Dynami use super::available_range::AvailableRange; use super::cursor::{Page, Target}; -use super::object::{self, deserialize_move_struct, Object, ObjectKind, ObjectLookup}; +use super::object::{self, deserialize_move_struct, Object, ObjectKind}; use super::type_filter::ExactTypeFilter; use super::{ base64::Base64, move_object::MoveObject, move_value::MoveValue, sui_address::SuiAddress, @@ -170,9 +170,10 @@ impl DynamicField { let super_ = MoveObject::query( ctx, SuiAddress::from(field_id), - ObjectLookup::LatestAt { - parent_version, - checkpoint_viewed_at, + if let Some(parent_version) = parent_version { + Object::under_parent(parent_version, checkpoint_viewed_at) + } else { + Object::latest_at(checkpoint_viewed_at) }, ) .await?; diff --git a/crates/sui-graphql-rpc/src/types/object.rs b/crates/sui-graphql-rpc/src/types/object.rs index 7243666ef520c..e9177a122057d 100644 --- a/crates/sui-graphql-rpc/src/types/object.rs +++ b/crates/sui-graphql-rpc/src/types/object.rs @@ -34,12 +34,12 @@ use crate::{filter, or_filter}; use async_graphql::connection::{CursorType, Edge}; use async_graphql::dataloader::Loader; use async_graphql::{connection::Connection, *}; -use diesel::{BoolExpressionMethods, ExpressionMethods, QueryDsl}; +use diesel::{BoolExpressionMethods, ExpressionMethods, JoinOnDsl, QueryDsl, SelectableHelper}; use move_core_types::annotated_value::{MoveStruct, MoveTypeLayout}; use move_core_types::language_storage::StructTag; use serde::{Deserialize, Serialize}; use sui_indexer::models::objects::{StoredDeletedHistoryObject, StoredHistoryObject}; -use sui_indexer::schema::objects_history; +use sui_indexer::schema::{objects_history, objects_version}; use sui_indexer::types::ObjectStatus as NativeObjectStatus; use sui_indexer::types::OwnerType; use sui_types::object::bounded_visitor::BoundedVisitor; @@ -183,9 +183,14 @@ pub(crate) struct AddressOwner { pub(crate) enum ObjectLookup { LatestAt { - /// The parent version to be used as an optional upper bound for the query. Look for the - /// latest version of a child object that is less than or equal to this upper bound. - parent_version: Option, + /// The checkpoint sequence number at which this was viewed at + checkpoint_viewed_at: u64, + }, + + UnderParent { + /// The parent version to be used as an upper bound for the query. Look for the latest + /// version of a child object whose version is less than or equal to this upper bound. + parent_version: u64, /// The checkpoint sequence number at which this was viewed at checkpoint_viewed_at: u64, }, @@ -283,13 +288,21 @@ struct HistoricalKey { checkpoint_viewed_at: u64, } -/// DataLoader key for fetching the latest version of an `Object` as of a consistency cursor. The -/// query can optionally be bounded by a `parent_version` which imposes an additional requirement -/// that the object's version is bounded above by the parent version. +/// DataLoader key for fetching the latest version of an object whose parent object has version +/// `parent_version`, as of `checkpoint_viewed_at`. This look-up can fail to find a valid object if +/// the key is not self-consistent, for example if the `parent_version` is set to a higher version +/// than the object's actual parent as of `checkpoint_viewed_at`. +#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] +struct ParentVersionKey { + id: SuiAddress, + parent_version: u64, + checkpoint_viewed_at: u64, +} + +/// DataLoader key for fetching the latest version of an `Object` as of a consistency cursor. #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] struct LatestAtKey { id: SuiAddress, - parent_version: Option, checkpoint_viewed_at: u64, } @@ -807,7 +820,6 @@ impl Object { /// Look-up the latest version of the object as of a given checkpoint. pub(crate) fn latest_at(checkpoint_viewed_at: u64) -> ObjectLookup { ObjectLookup::LatestAt { - parent_version: None, checkpoint_viewed_at, } } @@ -815,8 +827,8 @@ impl Object { /// Look-up the latest version of an object whose version is less than or equal to its parent's /// version, as of a given checkpoint. pub(crate) fn under_parent(parent_version: u64, checkpoint_viewed_at: u64) -> ObjectLookup { - ObjectLookup::LatestAt { - parent_version: Some(parent_version), + ObjectLookup::UnderParent { + parent_version, checkpoint_viewed_at, } } @@ -849,18 +861,30 @@ impl Object { }) .await } - ObjectLookup::LatestAt { + + ObjectLookup::UnderParent { parent_version, checkpoint_viewed_at, } => { loader - .load_one(LatestAtKey { + .load_one(ParentVersionKey { id, parent_version, checkpoint_viewed_at, }) .await } + + ObjectLookup::LatestAt { + checkpoint_viewed_at, + } => { + loader + .load_one(LatestAtKey { + id, + checkpoint_viewed_at, + }) + .await + } } } @@ -1177,7 +1201,8 @@ impl Loader for Db { type Error = Error; async fn load(&self, keys: &[HistoricalKey]) -> Result, Error> { - use objects_history::dsl; + use objects_history::dsl as h; + use objects_version::dsl as v; let id_versions: BTreeSet<_> = keys .iter() @@ -1187,12 +1212,19 @@ impl Loader for Db { let objects: Vec = self .execute(move |conn| { conn.results(move || { - let mut query = dsl::objects_history.into_boxed(); + let mut query = h::objects_history + .inner_join( + v::objects_version.on(v::cp_sequence_number + .eq(h::checkpoint_sequence_number) + .and(v::object_id.eq(h::object_id)) + .and(v::object_version.eq(h::object_version))), + ) + .select(StoredHistoryObject::as_select()) + .into_boxed(); - // TODO: Speed up using an `obj_version` table. for (id, version) in id_versions.iter().cloned() { - query = query - .or_filter(dsl::object_id.eq(id).and(dsl::object_version.eq(version))); + query = + query.or_filter(v::object_id.eq(id).and(v::object_version.eq(version))); } query @@ -1234,17 +1266,20 @@ impl Loader for Db { } #[async_trait::async_trait] -impl Loader for Db { +impl Loader for Db { type Value = Object; type Error = Error; - async fn load(&self, keys: &[LatestAtKey]) -> Result, Error> { + async fn load( + &self, + keys: &[ParentVersionKey], + ) -> Result, Error> { // Group keys by checkpoint viewed at and parent version -- we'll issue a separate query for // each group. #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] struct GroupKey { checkpoint_viewed_at: u64, - parent_version: Option, + parent_version: u64, } let mut keys_by_cursor_and_parent_version: BTreeMap<_, BTreeSet<_>> = BTreeMap::new(); @@ -1257,50 +1292,40 @@ impl Loader for Db { keys_by_cursor_and_parent_version .entry(group_key) .or_default() - .insert(key.id); + .insert(key.id.into_vec()); } // Issue concurrent reads for each group of keys. let futures = keys_by_cursor_and_parent_version .into_iter() .map(|(group_key, ids)| { - self.execute_repeatable(move |conn| { - let Some(range) = AvailableRange::result(conn, group_key.checkpoint_viewed_at)? - else { - return Ok::, diesel::result::Error>( - vec![], - ); - }; - - let filter = ObjectFilter { - object_ids: Some(ids.iter().cloned().collect()), - ..Default::default() - }; - - // TODO: Implement queries that use a parent version bound using an - // `obj_version` table. - let apply_parent_bound = |q: RawQuery| { - if let Some(parent_version) = group_key.parent_version { - filter!(q, format!("object_version <= {parent_version}")) - } else { - q - } - }; - - Ok(conn - .results(move || { - build_objects_query( - View::Consistent, - range, - &Page::bounded(ids.len() as u64), - |q| apply_parent_bound(filter.apply(q)), - apply_parent_bound, + self.execute(move |conn| { + let stored: Vec = conn.results(move || { + use objects_history::dsl as h; + use objects_version::dsl as v; + + h::objects_history + .inner_join( + v::objects_version.on(v::cp_sequence_number + .eq(h::checkpoint_sequence_number) + .and(v::object_id.eq(h::object_id)) + .and(v::object_version.eq(h::object_version))), ) + .select(StoredHistoryObject::as_select()) + .filter(v::object_id.eq_any(ids.iter().cloned())) + .filter(v::object_version.le(group_key.parent_version as i64)) + .distinct_on(v::object_id) + .order_by(v::object_id) + .then_order_by(v::object_version.desc()) .into_boxed() - })? - .into_iter() - .map(|r| (group_key, r)) - .collect()) + })?; + + Ok::<_, diesel::result::Error>( + stored + .into_iter() + .map(|stored| (group_key, stored)) + .collect::>(), + ) }) }); @@ -1312,15 +1337,21 @@ impl Loader for Db { for (group_key, stored) in group.map_err(|e| Error::Internal(format!("Failed to fetch objects: {e}")))? { + // This particular object is invalid -- it didn't exist at the checkpoint we are + // viewing at. + if group_key.checkpoint_viewed_at < stored.checkpoint_sequence_number as u64 { + continue; + } + let object = Object::try_from_stored_history_object( stored, group_key.checkpoint_viewed_at, // If `LatestAtKey::parent_version` is set, it must have been correctly // propagated from the `Object::root_version` of some object. - group_key.parent_version, + Some(group_key.parent_version), )?; - let key = LatestAtKey { + let key = ParentVersionKey { id: object.address, checkpoint_viewed_at: group_key.checkpoint_viewed_at, parent_version: group_key.parent_version, @@ -1334,6 +1365,81 @@ impl Loader for Db { } } +#[async_trait::async_trait] +impl Loader for Db { + type Value = Object; + type Error = Error; + + async fn load(&self, keys: &[LatestAtKey]) -> Result, Error> { + // Group keys by checkpoint viewed at -- we'll issue a separate query for each group. + let mut keys_by_cursor_and_parent_version: BTreeMap<_, BTreeSet<_>> = BTreeMap::new(); + + for key in keys { + keys_by_cursor_and_parent_version + .entry(key.checkpoint_viewed_at) + .or_default() + .insert(key.id); + } + + // Issue concurrent reads for each group of keys. + let futures = + keys_by_cursor_and_parent_version + .into_iter() + .map(|(checkpoint_viewed_at, ids)| { + self.execute_repeatable(move |conn| { + let Some(range) = AvailableRange::result(conn, checkpoint_viewed_at)? + else { + return Ok::, diesel::result::Error>( + vec![], + ); + }; + + let filter = ObjectFilter { + object_ids: Some(ids.iter().cloned().collect()), + ..Default::default() + }; + + Ok(conn + .results(move || { + build_objects_query( + View::Consistent, + range, + &Page::bounded(ids.len() as u64), + |q| filter.apply(q), + |q| q, + ) + .into_boxed() + })? + .into_iter() + .map(|r| (checkpoint_viewed_at, r)) + .collect()) + }) + }); + + // Wait for the reads to all finish, and gather them into the result map. + let groups = futures::future::join_all(futures).await; + + let mut results = HashMap::new(); + for group in groups { + for (checkpoint_viewed_at, stored) in + group.map_err(|e| Error::Internal(format!("Failed to fetch objects: {e}")))? + { + let object = + Object::try_from_stored_history_object(stored, checkpoint_viewed_at, None)?; + + let key = LatestAtKey { + id: object.address, + checkpoint_viewed_at, + }; + + results.insert(key, object); + } + } + + Ok(results) + } +} + impl From<&ObjectKind> for ObjectStatus { fn from(kind: &ObjectKind) -> Self { match kind { diff --git a/crates/sui-graphql-rpc/src/types/owner.rs b/crates/sui-graphql-rpc/src/types/owner.rs index 79525ca9e9921..3986a39f6ad23 100644 --- a/crates/sui-graphql-rpc/src/types/owner.rs +++ b/crates/sui-graphql-rpc/src/types/owner.rs @@ -251,9 +251,10 @@ impl Owner { Object::query( ctx, self.address, - object::ObjectLookup::LatestAt { - parent_version: self.root_version, - checkpoint_viewed_at: self.checkpoint_viewed_at, + if let Some(parent_version) = self.root_version { + Object::under_parent(parent_version, self.checkpoint_viewed_at) + } else { + Object::latest_at(self.checkpoint_viewed_at) }, ) .await From fd0fe51e5c24f84da6944ee63d13fb579aca2f33 Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Mon, 19 Aug 2024 11:35:30 +0100 Subject: [PATCH 166/232] [GraphQL/MovePackage] Query by ID and version (#17692) ## Description Implement `Query.package` and `MovePackage.atVersion` to query a package at a specific version, using the new fields added to the `packages` table, exposed via some new data loaders. ## Test plan New transactional tests: ``` sui$ cargo nextest run -p sui-graphql-e2e-tests \ --features pg_integration \ -- packages/versioning ``` ## Stack - #17686 - #17687 - #17688 - #17689 - #17691 - #17694 - #17695 - #17542 - #17726 - #17543 - #17692 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [x] GraphQL: Introduce `Query.package` and `MovePackage.atVersion` to query packages at specific versions. - [ ] CLI: - [ ] Rust SDK: --- .../tests/packages/versioning.exp | 318 ++++++++++++++++++ .../tests/packages/versioning.move | 171 ++++++++++ .../schema/current_progress_schema.graphql | 18 + .../sui-graphql-rpc/src/types/move_module.rs | 7 +- .../sui-graphql-rpc/src/types/move_package.rs | 157 ++++++++- crates/sui-graphql-rpc/src/types/object.rs | 11 +- crates/sui-graphql-rpc/src/types/query.rs | 41 ++- .../sui-graphql-rpc/src/types/sui_address.rs | 15 +- .../snapshot_tests__schema_sdl_export.snap | 18 + 9 files changed, 726 insertions(+), 30 deletions(-) create mode 100644 crates/sui-graphql-e2e-tests/tests/packages/versioning.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/packages/versioning.move diff --git a/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp b/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp new file mode 100644 index 0000000000000..4c37560181402 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp @@ -0,0 +1,318 @@ +processed 9 tasks + +init: +A: object(0,0) + +task 1, lines 6-9: +//# publish --upgradeable --sender A +created: object(1,0), object(1,1) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 5076800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, lines 11-15: +//# upgrade --package P0 --upgrade-capability 1,1 --sender A +created: object(2,0) +mutated: object(0,0), object(1,1) +gas summary: computation_cost: 1000000, storage_cost: 5251600, storage_rebate: 2595780, non_refundable_storage_fee: 26220 + +task 3, lines 17-22: +//# upgrade --package P1 --upgrade-capability 1,1 --sender A +created: object(3,0) +mutated: object(0,0), object(1,1) +gas summary: computation_cost: 1000000, storage_cost: 5426400, storage_rebate: 2595780, non_refundable_storage_fee: 26220 + +task 4, line 24: +//# create-checkpoint +Checkpoint created: 1 + +task 5, lines 26-45: +//# run-graphql +Response: { + "data": { + "v1": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + } + ] + } + } + }, + "v2": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + } + ] + } + } + }, + "v3": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + }, + { + "name": "h" + } + ] + } + } + } + } +} + +task 6, lines 47-84: +//# run-graphql +Response: { + "data": { + "v1_from_p1": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + } + ] + } + } + }, + "v1_from_p2": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + } + ] + } + } + }, + "v2_from_p0": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + } + ] + } + } + }, + "v2_from_p2": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + } + ] + } + } + }, + "v3_from_p0": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + }, + { + "name": "h" + } + ] + } + } + }, + "v3_from_p1": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + }, + { + "name": "h" + } + ] + } + } + } + } +} + +task 7, lines 86-141: +//# run-graphql +Response: { + "data": { + "v1": { + "v1": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + } + ] + } + } + }, + "v2": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + } + ] + } + } + }, + "v3": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + }, + { + "name": "h" + } + ] + } + } + } + }, + "v2": { + "v1": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + } + ] + } + } + }, + "v2": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + } + ] + } + } + }, + "v3": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + }, + { + "name": "h" + } + ] + } + } + } + }, + "v3": { + "v1": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + } + ] + } + } + }, + "v2": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + } + ] + } + } + }, + "v3": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + }, + { + "name": "h" + } + ] + } + } + } + } + } +} + +task 8, lines 143-171: +//# run-graphql +Response: { + "data": { + "v0": null, + "v1": { + "v0": null, + "v4": null + }, + "v4": null + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/packages/versioning.move b/crates/sui-graphql-e2e-tests/tests/packages/versioning.move new file mode 100644 index 0000000000000..72bdc66db632f --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/packages/versioning.move @@ -0,0 +1,171 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//# init --protocol-version 39 --addresses P0=0x0 P1=0x0 P2=0x0 --accounts A --simulator + +//# publish --upgradeable --sender A +module P0::m { + public fun f(): u64 { 42 } +} + +//# upgrade --package P0 --upgrade-capability 1,1 --sender A +module P1::m { + public fun f(): u64 { 42 } + public fun g(): u64 { 43 } +} + +//# upgrade --package P1 --upgrade-capability 1,1 --sender A +module P2::m { + public fun f(): u64 { 42 } + public fun g(): u64 { 43 } + public fun h(): u64 { 44 } +} + +//# create-checkpoint + +//# run-graphql +{ # Test fetching by ID + v1: package(address: "@{P0}") { + module(name: "m") { + functions { nodes { name } } + } + } + + v2: package(address: "@{P1}") { + module(name: "m") { + functions { nodes { name } } + } + } + + v3: package(address: "@{P2}") { + module(name: "m") { + functions { nodes { name } } + } + } +} + +//# run-graphql +{ # Test fetching by version + v1_from_p1: package(address: "@{P1}", version: 1) { + module(name: "m") { + functions { nodes { name } } + } + } + + v1_from_p2: package(address: "@{P2}", version: 1) { + module(name: "m") { + functions { nodes { name } } + } + } + + v2_from_p0: package(address: "@{P0}", version: 2) { + module(name: "m") { + functions { nodes { name } } + } + } + + v2_from_p2: package(address: "@{P2}", version: 2) { + module(name: "m") { + functions { nodes { name } } + } + } + + v3_from_p0: package(address: "@{P0}", version: 3) { + module(name: "m") { + functions { nodes { name } } + } + } + + v3_from_p1: package(address: "@{P1}", version: 3) { + module(name: "m") { + functions { nodes { name } } + } + } +} + +//# run-graphql +{ # Go from one version to another using packageAtVersion + v1: package(address: "@{P1}") { + v1: packageAtVersion(version: 1) { + module(name: "m") { + functions { nodes { name } } + } + } + v2: packageAtVersion(version: 2) { + module(name: "m") { + functions { nodes { name } } + } + } + v3: packageAtVersion(version: 3) { + module(name: "m") { + functions { nodes { name } } + } + } + } + + v2: package(address: "@{P2}") { + v1: packageAtVersion(version: 1) { + module(name: "m") { + functions { nodes { name } } + } + } + v2: packageAtVersion(version: 2) { + module(name: "m") { + functions { nodes { name } } + } + } + v3: packageAtVersion(version: 3) { + module(name: "m") { + functions { nodes { name } } + } + } + } + + v3: package(address: "@{P2}") { + v1: packageAtVersion(version: 1) { + module(name: "m") { + functions { nodes { name } } + } + } + v2: packageAtVersion(version: 2) { + module(name: "m") { + functions { nodes { name } } + } + } + v3: packageAtVersion(version: 3) { + module(name: "m") { + functions { nodes { name } } + } + } + } +} + +//# run-graphql +{ # Fetch out of range versions (should return null) + v0: package(address: "@{P0}", version: 0) { + module(name: "m") { + functions { nodes { name } } + } + } + + # This won't return null, but its inner queries will + v1: package(address: "@{P0}") { + v0: packageAtVersion(version: 0) { + module(name: "m") { + functions { nodes { name } } + } + } + + v4: packageAtVersion(version: 4) { + module(name: "m") { + functions { nodes { name } } + } + } + } + + v4: package(address: "@{P0}", version: 4) { + module(name: "m") { + functions { nodes { name } } + } + } +} diff --git a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql b/crates/sui-graphql-rpc/schema/current_progress_schema.graphql index cbde89c2c6fc5..201b62290eabe 100644 --- a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql +++ b/crates/sui-graphql-rpc/schema/current_progress_schema.graphql @@ -2168,6 +2168,11 @@ type MovePackage implements IObject & IOwner { """ bcs: Base64 """ + Fetch another version of this package (the package that shares this package's original ID, + but has the specified `version`). + """ + packageAtVersion(version: Int!): MovePackage + """ A representation of the module called `name` in this package, including the structs and functions it defines. """ @@ -3040,6 +3045,19 @@ type Query { """ object(address: SuiAddress!, version: UInt53): Object """ + The package corresponding to the given address at the (optionally) given version. + + When no version is given, the package is loaded directly from the address given. Otherwise, + the address is translated before loading to point to the package whose original ID matches + the package at `address`, but whose version is `version`. For non-system packages, this may + result in a different address than `address` because different versions of a package, + introduced by upgrades, exist at distinct addresses. + + Note that this interpretation of `version` is different from a historical object read (the + interpretation of `version` for the `object` query). + """ + package(address: SuiAddress!, version: UInt53): MovePackage + """ Look-up an Account by its SuiAddress. """ address(address: SuiAddress!): Address diff --git a/crates/sui-graphql-rpc/src/types/move_module.rs b/crates/sui-graphql-rpc/src/types/move_module.rs index e34ad6c46a8bc..f85d6fe558abc 100644 --- a/crates/sui-graphql-rpc/src/types/move_module.rs +++ b/crates/sui-graphql-rpc/src/types/move_module.rs @@ -15,7 +15,6 @@ use super::datatype::MoveDatatype; use super::move_enum::MoveEnum; use super::move_function::MoveFunction; use super::move_struct::MoveStruct; -use super::object::Object; use super::{base64::Base64, move_package::MovePackage, sui_address::SuiAddress}; #[derive(Clone)] @@ -40,7 +39,7 @@ impl MoveModule { MovePackage::query( ctx, self.storage_id, - Object::latest_at(self.checkpoint_viewed_at), + MovePackage::by_id_at(self.checkpoint_viewed_at), ) .await .extend()? @@ -91,7 +90,7 @@ impl MoveModule { let Some(package) = MovePackage::query( ctx, self.storage_id, - Object::latest_at(checkpoint_viewed_at), + MovePackage::by_id_at(checkpoint_viewed_at), ) .await .extend()? @@ -482,7 +481,7 @@ impl MoveModule { checkpoint_viewed_at: u64, ) -> Result, Error> { let Some(package) = - MovePackage::query(ctx, address, Object::latest_at(checkpoint_viewed_at)).await? + MovePackage::query(ctx, address, MovePackage::by_id_at(checkpoint_viewed_at)).await? else { return Ok(None); }; diff --git a/crates/sui-graphql-rpc/src/types/move_package.rs b/crates/sui-graphql-rpc/src/types/move_package.rs index 1791b0bde32ca..a9dbeb0188f15 100644 --- a/crates/sui-graphql-rpc/src/types/move_package.rs +++ b/crates/sui-graphql-rpc/src/types/move_package.rs @@ -1,6 +1,8 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use std::collections::{BTreeSet, HashMap}; + use super::balance::{self, Balance}; use super::base64::Base64; use super::big_int::BigInt; @@ -8,9 +10,7 @@ use super::coin::Coin; use super::cursor::{JsonCursor, Page}; use super::move_module::MoveModule; use super::move_object::MoveObject; -use super::object::{ - self, Object, ObjectFilter, ObjectImpl, ObjectLookup, ObjectOwner, ObjectStatus, -}; +use super::object::{self, Object, ObjectFilter, ObjectImpl, ObjectOwner, ObjectStatus}; use super::owner::OwnerImpl; use super::stake::StakedSui; use super::sui_address::SuiAddress; @@ -19,10 +19,16 @@ use super::transaction_block::{self, TransactionBlock, TransactionBlockFilter}; use super::type_filter::ExactTypeFilter; use super::uint53::UInt53; use crate::consistency::ConsistentNamedCursor; +use crate::data::{DataLoader, Db, DbConnection, QueryExecutor}; use crate::error::Error; +use crate::types::sui_address::addr; use async_graphql::connection::{Connection, CursorType, Edge}; +use async_graphql::dataloader::Loader; use async_graphql::*; +use diesel::{BoolExpressionMethods, ExpressionMethods, JoinOnDsl, QueryDsl}; +use sui_indexer::schema::packages; use sui_package_resolver::{error::Error as PackageCacheError, Package as ParsedMovePackage}; +use sui_types::is_system_package; use sui_types::{move_package::MovePackage as NativeMovePackage, object::Data}; #[derive(Clone)] @@ -35,6 +41,21 @@ pub(crate) struct MovePackage { pub native: NativeMovePackage, } +/// Filter for a point query of a MovePackage, supporting querying different versions of a package +/// by their version. Note that different versions of the same user package exist at different IDs +/// to each other, so this is different from looking up the historical version of an object. +pub(crate) enum PackageLookup { + /// Get the package at the given address, if it was created before the given checkpoint. + ById { checkpoint_viewed_at: u64 }, + + /// Get the package whose original ID matches the storage ID of the package at the given + /// address, but whose version is `version`. + Versioned { + version: u64, + checkpoint_viewed_at: u64, + }, +} + /// Information used by a package to link to a specific version of its dependency. #[derive(SimpleObject)] struct Linkage { @@ -66,6 +87,18 @@ pub(crate) struct MovePackageDowncastError; pub(crate) type CModule = JsonCursor; +/// DataLoader key for fetching the storage ID of the (user) package that shares an original (aka +/// runtime) ID with the package stored at `package_id`, and whose version is `version`. +/// +/// Note that this is different from looking up the historical version of an object -- the query +/// returns the ID of the package (each version of a user package is at a different ID) -- and it +/// does not work for system packages (whose versions do all reside under the same ID). +#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] +struct PackageVersionKey { + address: SuiAddress, + version: u64, +} + /// A MovePackage is a kind of Move object that represents code that has been published on chain. /// It exposes information about its modules, type definitions, functions, and dependencies. #[Object] @@ -255,6 +288,22 @@ impl MovePackage { ObjectImpl(&self.super_).bcs().await } + /// Fetch another version of this package (the package that shares this package's original ID, + /// but has the specified `version`). + async fn package_at_version( + &self, + ctx: &Context<'_>, + version: u64, + ) -> Result> { + MovePackage::query( + ctx, + self.super_.address, + MovePackage::by_version(version, self.checkpoint_viewed_at_impl()), + ) + .await + .extend() + } + /// A representation of the module called `name` in this package, including the /// structs and functions it defines. async fn module(&self, name: String) -> Result> { @@ -416,11 +465,53 @@ impl MovePackage { } } + /// Look-up the package by its Storage ID, as of a given checkpoint. + pub(crate) fn by_id_at(checkpoint_viewed_at: u64) -> PackageLookup { + PackageLookup::ById { + checkpoint_viewed_at, + } + } + + /// Look-up a specific version of the package, identified by the storage ID of any version of + /// the package, and the desired version (the actual object loaded might be at a different + /// object ID). + pub(crate) fn by_version(version: u64, checkpoint_viewed_at: u64) -> PackageLookup { + PackageLookup::Versioned { + version, + checkpoint_viewed_at, + } + } + pub(crate) async fn query( ctx: &Context<'_>, address: SuiAddress, - key: ObjectLookup, + key: PackageLookup, ) -> Result, Error> { + let (address, key) = match key { + PackageLookup::ById { + checkpoint_viewed_at, + } => (address, Object::latest_at(checkpoint_viewed_at)), + + PackageLookup::Versioned { + version, + checkpoint_viewed_at, + } => { + if is_system_package(address) { + (address, Object::at_version(version, checkpoint_viewed_at)) + } else { + let DataLoader(loader) = &ctx.data_unchecked(); + let Some(translation) = loader + .load_one(PackageVersionKey { address, version }) + .await? + else { + return Ok(None); + }; + + (translation, Object::latest_at(checkpoint_viewed_at)) + } + } + }; + let Some(object) = Object::query(ctx, address, key).await? else { return Ok(None); }; @@ -431,6 +522,64 @@ impl MovePackage { } } +#[async_trait::async_trait] +impl Loader for Db { + type Value = SuiAddress; + type Error = Error; + + async fn load( + &self, + keys: &[PackageVersionKey], + ) -> Result, Error> { + use packages::dsl; + let other = diesel::alias!(packages as other); + + let id_versions: BTreeSet<_> = keys + .iter() + .map(|k| (k.address.into_vec(), k.version as i64)) + .collect(); + + let stored_packages: Vec<(Vec, i64, Vec)> = self + .execute(move |conn| { + conn.results(|| { + let mut query = dsl::packages + .inner_join(other.on(dsl::original_id.eq(other.field(dsl::original_id)))) + .select(( + dsl::package_id, + other.field(dsl::package_version), + other.field(dsl::package_id), + )) + .into_boxed(); + + for (id, version) in id_versions.iter().cloned() { + query = query.or_filter( + dsl::package_id + .eq(id) + .and(other.field(dsl::package_version).eq(version)), + ); + } + + query + }) + }) + .await + .map_err(|e| Error::Internal(format!("Failed to load packages: {e}")))?; + + let mut result = HashMap::new(); + for (id, version, other_id) in stored_packages { + result.insert( + PackageVersionKey { + address: addr(&id)?, + version: version as u64, + }, + addr(&other_id)?, + ); + } + + Ok(result) + } +} + impl TryFrom<&Object> for MovePackage { type Error = MovePackageDowncastError; diff --git a/crates/sui-graphql-rpc/src/types/object.rs b/crates/sui-graphql-rpc/src/types/object.rs index e9177a122057d..cd3e18709760c 100644 --- a/crates/sui-graphql-rpc/src/types/object.rs +++ b/crates/sui-graphql-rpc/src/types/object.rs @@ -17,6 +17,7 @@ use super::move_object::MoveObject; use super::move_package::MovePackage; use super::owner::OwnerImpl; use super::stake::StakedSui; +use super::sui_address::addr; use super::suins_registration::{DomainFormat, SuinsRegistration}; use super::transaction_block; use super::transaction_block::TransactionBlockFilter; @@ -181,6 +182,7 @@ pub(crate) struct AddressOwner { owner: Option, } +/// Filter for a point query of an Object. pub(crate) enum ObjectLookup { LatestAt { /// The checkpoint sequence number at which this was viewed at @@ -1459,15 +1461,6 @@ impl From<&Object> for OwnerImpl { } } -/// Parse a `SuiAddress` from its stored representation. Failure is an internal error: the -/// database should never contain a malformed address (containing the wrong number of bytes). -fn addr(bytes: impl AsRef<[u8]>) -> Result { - SuiAddress::from_bytes(bytes.as_ref()).map_err(|e| { - let bytes = bytes.as_ref().to_vec(); - Error::Internal(format!("Error deserializing address: {bytes:?}: {e}")) - }) -} - pub(crate) async fn deserialize_move_struct( move_object: &NativeMoveObject, resolver: &PackageResolver, diff --git a/crates/sui-graphql-rpc/src/types/query.rs b/crates/sui-graphql-rpc/src/types/query.rs index 10a6f4c9a8464..46334df30b1fd 100644 --- a/crates/sui-graphql-rpc/src/types/query.rs +++ b/crates/sui-graphql-rpc/src/types/query.rs @@ -12,6 +12,7 @@ use sui_sdk::SuiClient; use sui_types::transaction::{TransactionData, TransactionKind}; use sui_types::{gas_coin::GAS, transaction::TransactionDataAPI, TypeTag}; +use super::move_package::MovePackage; use super::suins_registration::NameService; use super::uint53::UInt53; use super::{ @@ -214,17 +215,37 @@ impl Query { version: Option, ) -> Result> { let Watermark { checkpoint, .. } = *ctx.data()?; + let key = match version { + Some(version) => Object::at_version(version.into(), checkpoint), + None => Object::latest_at(checkpoint), + }; + + Object::query(ctx, address, key).await.extend() + } + + /// The package corresponding to the given address at the (optionally) given version. + /// + /// When no version is given, the package is loaded directly from the address given. Otherwise, + /// the address is translated before loading to point to the package whose original ID matches + /// the package at `address`, but whose version is `version`. For non-system packages, this may + /// result in a different address than `address` because different versions of a package, + /// introduced by upgrades, exist at distinct addresses. + /// + /// Note that this interpretation of `version` is different from a historical object read (the + /// interpretation of `version` for the `object` query). + async fn package( + &self, + ctx: &Context<'_>, + address: SuiAddress, + version: Option, + ) -> Result> { + let Watermark { checkpoint, .. } = *ctx.data()?; + let key = match version { + Some(version) => MovePackage::by_version(version.into(), checkpoint), + None => MovePackage::by_id_at(checkpoint), + }; - match version { - Some(version) => { - Object::query(ctx, address, Object::at_version(version.into(), checkpoint)) - .await - .extend() - } - None => Object::query(ctx, address, Object::latest_at(checkpoint)) - .await - .extend(), - } + MovePackage::query(ctx, address, key).await.extend() } /// Look-up an Account by its SuiAddress. diff --git a/crates/sui-graphql-rpc/src/types/sui_address.rs b/crates/sui-graphql-rpc/src/types/sui_address.rs index 287bf0540e887..3a775e15064d4 100644 --- a/crates/sui-graphql-rpc/src/types/sui_address.rs +++ b/crates/sui-graphql-rpc/src/types/sui_address.rs @@ -3,18 +3,18 @@ use std::str::FromStr; +use crate::error::Error; use async_graphql::*; use move_core_types::account_address::AccountAddress; use serde::{Deserialize, Serialize}; use sui_types::base_types::{ObjectID, SuiAddress as NativeSuiAddress}; -use thiserror::Error; const SUI_ADDRESS_LENGTH: usize = 32; #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Copy)] pub(crate) struct SuiAddress([u8; SUI_ADDRESS_LENGTH]); -#[derive(Error, Debug, Eq, PartialEq)] +#[derive(thiserror::Error, Debug, Eq, PartialEq)] pub(crate) enum FromStrError { #[error("Invalid SuiAddress. Missing 0x prefix.")] NoPrefix, @@ -30,7 +30,7 @@ pub(crate) enum FromStrError { BadHex(char, usize), } -#[derive(Error, Debug, Eq, PartialEq)] +#[derive(thiserror::Error, Debug, Eq, PartialEq)] pub(crate) enum FromVecError { #[error("Expected SuiAddress with {} bytes, received {0}", SUI_ADDRESS_LENGTH)] WrongLength(usize), @@ -161,6 +161,15 @@ impl std::fmt::Display for SuiAddress { } } +/// Parse a `SuiAddress` from its stored representation. Failure is an internal error: the +/// database should never contain a malformed address (containing the wrong number of bytes). +pub(crate) fn addr(bytes: impl AsRef<[u8]>) -> Result { + SuiAddress::from_bytes(bytes.as_ref()).map_err(|e| { + let bytes = bytes.as_ref().to_vec(); + Error::Internal(format!("Error deserializing address: {bytes:?}: {e}")) + }) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap index c82e1181d7f6f..809354db88972 100644 --- a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap +++ b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap @@ -2172,6 +2172,11 @@ type MovePackage implements IObject & IOwner { """ bcs: Base64 """ + Fetch another version of this package (the package that shares this package's original ID, + but has the specified `version`). + """ + packageAtVersion(version: Int!): MovePackage + """ A representation of the module called `name` in this package, including the structs and functions it defines. """ @@ -3044,6 +3049,19 @@ type Query { """ object(address: SuiAddress!, version: UInt53): Object """ + The package corresponding to the given address at the (optionally) given version. + + When no version is given, the package is loaded directly from the address given. Otherwise, + the address is translated before loading to point to the package whose original ID matches + the package at `address`, but whose version is `version`. For non-system packages, this may + result in a different address than `address` because different versions of a package, + introduced by upgrades, exist at distinct addresses. + + Note that this interpretation of `version` is different from a historical object read (the + interpretation of `version` for the `object` query). + """ + package(address: SuiAddress!, version: UInt53): MovePackage + """ Look-up an Account by its SuiAddress. """ address(address: SuiAddress!): Address From e22cd030099cfd86a57e3015b58775558161ed77 Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Mon, 19 Aug 2024 11:38:54 +0100 Subject: [PATCH 167/232] [GraphQL/MovePackage] Query for latest version (#17693) ## Description Add a new kind of package point look-up to get the latest version of the package at a given ID (or from another `MovePackage`). For system packages, this is analogous to getting the latest version of the object at that ID, but the versions of other packages all exist at different IDs. ## Test plan New transactional tests: ``` sui$ cargo nextest run -p sui-graphql-e2e-tests \ --features pg_integration \ -- packages/versioning ``` ## Stack - #17686 - #17687 - #17688 - #17689 - #17691 - #17694 - #17695 - #17542 - #17726 - #17543 - #17692 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [x] GraphQL: Add `Query.latestPackage` and `MovePackage.latest` for fetching the latest version of a package. - [ ] CLI: - [ ] Rust SDK: --- .../tests/packages/versioning.exp | 147 ++++++++++++++++-- .../tests/packages/versioning.move | 52 +++++++ .../schema/current_progress_schema.graphql | 12 ++ .../sui-graphql-rpc/src/types/move_package.rs | 125 ++++++++++++++- crates/sui-graphql-rpc/src/types/query.rs | 15 ++ .../snapshot_tests__schema_sdl_export.snap | 12 ++ 6 files changed, 351 insertions(+), 12 deletions(-) diff --git a/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp b/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp index 4c37560181402..7b8ad9c4c32cc 100644 --- a/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp +++ b/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp @@ -1,4 +1,4 @@ -processed 9 tasks +processed 14 tasks init: A: object(0,0) @@ -9,23 +9,97 @@ created: object(1,0), object(1,1) mutated: object(0,0) gas summary: computation_cost: 1000000, storage_cost: 5076800, storage_rebate: 0, non_refundable_storage_fee: 0 -task 2, lines 11-15: +task 2, line 11: +//# create-checkpoint +Checkpoint created: 1 + +task 3, lines 13-21: +//# run-graphql +Response: { + "data": { + "latestPackage": { + "version": 1, + "module": { + "functions": { + "nodes": [ + { + "name": "f" + } + ] + } + } + } + } +} + +task 4, lines 23-27: //# upgrade --package P0 --upgrade-capability 1,1 --sender A -created: object(2,0) +created: object(4,0) mutated: object(0,0), object(1,1) gas summary: computation_cost: 1000000, storage_cost: 5251600, storage_rebate: 2595780, non_refundable_storage_fee: 26220 -task 3, lines 17-22: +task 5, line 29: +//# create-checkpoint +Checkpoint created: 2 + +task 6, lines 31-39: +//# run-graphql +Response: { + "data": { + "latestPackage": { + "version": 2, + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + } + ] + } + } + } + } +} + +task 7, lines 41-46: //# upgrade --package P1 --upgrade-capability 1,1 --sender A -created: object(3,0) +created: object(7,0) mutated: object(0,0), object(1,1) gas summary: computation_cost: 1000000, storage_cost: 5426400, storage_rebate: 2595780, non_refundable_storage_fee: 26220 -task 4, line 24: +task 8, line 48: //# create-checkpoint -Checkpoint created: 1 +Checkpoint created: 3 -task 5, lines 26-45: +task 9, lines 50-58: +//# run-graphql +Response: { + "data": { + "latestPackage": { + "version": 3, + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + }, + { + "name": "h" + } + ] + } + } + } + } +} + +task 10, lines 60-97: //# run-graphql Response: { "data": { @@ -38,6 +112,23 @@ Response: { } ] } + }, + "latestPackage": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + }, + { + "name": "h" + } + ] + } + } } }, "v2": { @@ -52,6 +143,23 @@ Response: { } ] } + }, + "latestPackage": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + }, + { + "name": "h" + } + ] + } + } } }, "v3": { @@ -69,12 +177,29 @@ Response: { } ] } + }, + "latestPackage": { + "module": { + "functions": { + "nodes": [ + { + "name": "f" + }, + { + "name": "g" + }, + { + "name": "h" + } + ] + } + } } } } } -task 6, lines 47-84: +task 11, lines 99-136: //# run-graphql Response: { "data": { @@ -165,7 +290,7 @@ Response: { } } -task 7, lines 86-141: +task 12, lines 138-193: //# run-graphql Response: { "data": { @@ -304,7 +429,7 @@ Response: { } } -task 8, lines 143-171: +task 13, lines 195-223: //# run-graphql Response: { "data": { diff --git a/crates/sui-graphql-e2e-tests/tests/packages/versioning.move b/crates/sui-graphql-e2e-tests/tests/packages/versioning.move index 72bdc66db632f..f4646294722b7 100644 --- a/crates/sui-graphql-e2e-tests/tests/packages/versioning.move +++ b/crates/sui-graphql-e2e-tests/tests/packages/versioning.move @@ -8,12 +8,36 @@ module P0::m { public fun f(): u64 { 42 } } +//# create-checkpoint + +//# run-graphql +{ + latestPackage(address: "@{P0}") { + version + module(name: "m") { + functions { nodes { name } } + } + } +} + //# upgrade --package P0 --upgrade-capability 1,1 --sender A module P1::m { public fun f(): u64 { 42 } public fun g(): u64 { 43 } } +//# create-checkpoint + +//# run-graphql +{ + latestPackage(address: "@{P0}") { + version + module(name: "m") { + functions { nodes { name } } + } + } +} + //# upgrade --package P1 --upgrade-capability 1,1 --sender A module P2::m { public fun f(): u64 { 42 } @@ -23,24 +47,52 @@ module P2::m { //# create-checkpoint +//# run-graphql +{ + latestPackage(address: "@{P0}") { + version + module(name: "m") { + functions { nodes { name } } + } + } +} + //# run-graphql { # Test fetching by ID v1: package(address: "@{P0}") { module(name: "m") { functions { nodes { name } } } + + latestPackage { + module(name: "m") { + functions { nodes { name } } + } + } } v2: package(address: "@{P1}") { module(name: "m") { functions { nodes { name } } } + + latestPackage { + module(name: "m") { + functions { nodes { name } } + } + } } v3: package(address: "@{P2}") { module(name: "m") { functions { nodes { name } } } + + latestPackage { + module(name: "m") { + functions { nodes { name } } + } + } } } diff --git a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql b/crates/sui-graphql-rpc/schema/current_progress_schema.graphql index 201b62290eabe..69472d0578045 100644 --- a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql +++ b/crates/sui-graphql-rpc/schema/current_progress_schema.graphql @@ -2173,6 +2173,11 @@ type MovePackage implements IObject & IOwner { """ packageAtVersion(version: Int!): MovePackage """ + Fetch the latest version of this package (the package with the highest `version` that shares + this packages's original ID) + """ + latestPackage: MovePackage! + """ A representation of the module called `name` in this package, including the structs and functions it defines. """ @@ -3058,6 +3063,13 @@ type Query { """ package(address: SuiAddress!, version: UInt53): MovePackage """ + The latest version of the package at `address`. + + This corresponds to the package with the highest `version` that shares its original ID with + the package at `address`. + """ + latestPackage(address: SuiAddress!): MovePackage + """ Look-up an Account by its SuiAddress. """ address(address: SuiAddress!): Address diff --git a/crates/sui-graphql-rpc/src/types/move_package.rs b/crates/sui-graphql-rpc/src/types/move_package.rs index a9dbeb0188f15..1e8490be2465b 100644 --- a/crates/sui-graphql-rpc/src/types/move_package.rs +++ b/crates/sui-graphql-rpc/src/types/move_package.rs @@ -1,7 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::collections::{BTreeSet, HashMap}; +use std::collections::{BTreeMap, BTreeSet, HashMap}; use super::balance::{self, Balance}; use super::base64::Base64; @@ -54,6 +54,10 @@ pub(crate) enum PackageLookup { version: u64, checkpoint_viewed_at: u64, }, + + /// Get the package whose original ID matches the storage ID of the package at the given + /// address, but that has the max version at the given checkpoint. + Latest { checkpoint_viewed_at: u64 }, } /// Information used by a package to link to a specific version of its dependency. @@ -99,6 +103,14 @@ struct PackageVersionKey { version: u64, } +/// DataLoader key for fetching the latest version of a user package: The package with the largest +/// version whose original ID matches the original ID of the package at `address`. +#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] +struct LatestKey { + address: SuiAddress, + checkpoint_viewed_at: u64, +} + /// A MovePackage is a kind of Move object that represents code that has been published on chain. /// It exposes information about its modules, type definitions, functions, and dependencies. #[Object] @@ -304,6 +316,19 @@ impl MovePackage { .extend() } + /// Fetch the latest version of this package (the package with the highest `version` that shares + /// this packages's original ID) + async fn latest_package(&self, ctx: &Context<'_>) -> Result { + Ok(MovePackage::query( + ctx, + self.super_.address, + MovePackage::latest_at(self.checkpoint_viewed_at_impl()), + ) + .await + .extend()? + .ok_or_else(|| Error::Internal("No latest version found".to_string()))?) + } + /// A representation of the module called `name` in this package, including the /// structs and functions it defines. async fn module(&self, name: String) -> Result> { @@ -482,6 +507,14 @@ impl MovePackage { } } + /// Look-up the package that shares the same original ID as the package at `address`, but has + /// the latest version, as of the given checkpoint. + pub(crate) fn latest_at(checkpoint_viewed_at: u64) -> PackageLookup { + PackageLookup::Latest { + checkpoint_viewed_at, + } + } + pub(crate) async fn query( ctx: &Context<'_>, address: SuiAddress, @@ -510,6 +543,27 @@ impl MovePackage { (translation, Object::latest_at(checkpoint_viewed_at)) } } + + PackageLookup::Latest { + checkpoint_viewed_at, + } => { + if is_system_package(address) { + (address, Object::latest_at(checkpoint_viewed_at)) + } else { + let DataLoader(loader) = &ctx.data_unchecked(); + let Some(translation) = loader + .load_one(LatestKey { + address, + checkpoint_viewed_at, + }) + .await? + else { + return Ok(None); + }; + + (translation, Object::latest_at(checkpoint_viewed_at)) + } + } }; let Some(object) = Object::query(ctx, address, key).await? else { @@ -580,6 +634,75 @@ impl Loader for Db { } } +#[async_trait::async_trait] +impl Loader for Db { + type Value = SuiAddress; + type Error = Error; + + async fn load(&self, keys: &[LatestKey]) -> Result, Error> { + use packages::dsl; + let other = diesel::alias!(packages as other); + + let mut ids_by_cursor: BTreeMap<_, BTreeSet<_>> = BTreeMap::new(); + for key in keys { + ids_by_cursor + .entry(key.checkpoint_viewed_at) + .or_default() + .insert(key.address.into_vec()); + } + + // Issue concurrent reads for each group of IDs + let futures = ids_by_cursor + .into_iter() + .map(|(checkpoint_viewed_at, ids)| { + self.execute(move |conn| { + let results: Vec<(Vec, Vec)> = conn.results(|| { + let o_original_id = other.field(dsl::original_id); + let o_package_id = other.field(dsl::package_id); + let o_cp_seq_num = other.field(dsl::checkpoint_sequence_number); + let o_version = other.field(dsl::package_version); + + let query = dsl::packages + .inner_join(other.on(dsl::original_id.eq(o_original_id))) + .select((dsl::package_id, o_package_id)) + .filter(dsl::package_id.eq_any(ids.iter().cloned())) + .filter(o_cp_seq_num.le(checkpoint_viewed_at as i64)) + .order_by((dsl::package_id, dsl::original_id, o_version.desc())) + .distinct_on((dsl::package_id, dsl::original_id)); + query + })?; + + Ok::<_, diesel::result::Error>( + results + .into_iter() + .map(|(p, latest)| (checkpoint_viewed_at, p, latest)) + .collect::>(), + ) + }) + }); + + // Wait for the reads to all finish, and gather them into the result map. + let groups = futures::future::join_all(futures).await; + + let mut results = HashMap::new(); + for group in groups { + for (checkpoint_viewed_at, address, latest) in + group.map_err(|e| Error::Internal(format!("Failed to fetch packages: {e}")))? + { + results.insert( + LatestKey { + address: addr(&address)?, + checkpoint_viewed_at, + }, + addr(&latest)?, + ); + } + } + + Ok(results) + } +} + impl TryFrom<&Object> for MovePackage { type Error = MovePackageDowncastError; diff --git a/crates/sui-graphql-rpc/src/types/query.rs b/crates/sui-graphql-rpc/src/types/query.rs index 46334df30b1fd..5aa55e7334773 100644 --- a/crates/sui-graphql-rpc/src/types/query.rs +++ b/crates/sui-graphql-rpc/src/types/query.rs @@ -248,6 +248,21 @@ impl Query { MovePackage::query(ctx, address, key).await.extend() } + /// The latest version of the package at `address`. + /// + /// This corresponds to the package with the highest `version` that shares its original ID with + /// the package at `address`. + async fn latest_package( + &self, + ctx: &Context<'_>, + address: SuiAddress, + ) -> Result> { + let Watermark { checkpoint, .. } = *ctx.data()?; + MovePackage::query(ctx, address, MovePackage::latest_at(checkpoint)) + .await + .extend() + } + /// Look-up an Account by its SuiAddress. async fn address(&self, ctx: &Context<'_>, address: SuiAddress) -> Result> { let Watermark { checkpoint, .. } = *ctx.data()?; diff --git a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap index 809354db88972..61cabeeb1de13 100644 --- a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap +++ b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap @@ -2177,6 +2177,11 @@ type MovePackage implements IObject & IOwner { """ packageAtVersion(version: Int!): MovePackage """ + Fetch the latest version of this package (the package with the highest `version` that shares + this packages's original ID) + """ + latestPackage: MovePackage! + """ A representation of the module called `name` in this package, including the structs and functions it defines. """ @@ -3062,6 +3067,13 @@ type Query { """ package(address: SuiAddress!, version: UInt53): MovePackage """ + The latest version of the package at `address`. + + This corresponds to the package with the highest `version` that shares its original ID with + the package at `address`. + """ + latestPackage(address: SuiAddress!): MovePackage + """ Look-up an Account by its SuiAddress. """ address(address: SuiAddress!): Address From 01bbabe976d61f016d33dcd2375679f3958793eb Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Mon, 19 Aug 2024 11:40:25 +0100 Subject: [PATCH 168/232] [GraphQL/MovePackage] Paginate by checkpoint (#17696) ## Description Adds a query, `Query.packages` for fetching all packages that were introduced within a given checkpoint range. Useful for fetching package contents in bulk, to do local analyses. ## Test plan New E2E tests: ``` sui$ cargo nextest run -p sui-graphql-e2e-tests \ --features pg_integration \ -- packages/versioning ``` Also tested for performance against a large read replica (the query planner quotes a high estimate for the query but the actual results do not take very long to run because queries on many sub-partitions are eliminated). ## Stack - #17686 - #17687 - #17688 - #17689 - #17691 - #17694 - #17695 - #17542 - #17726 - #17543 - #17692 - #17693 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [x] GraphQL: Introduces `Query.packages` for paginating through all packages (optionally bounding by the checkpoint the package was introduced in). - [ ] CLI: - [ ] Rust SDK: --- .../tests/packages/versioning.exp | 204 ++++++++++++++++-- .../tests/packages/versioning.move | 54 +++++ .../schema/current_progress_schema.graphql | 24 +++ crates/sui-graphql-rpc/src/types/event.rs | 12 +- .../sui-graphql-rpc/src/types/move_package.rs | 203 ++++++++++++++++- crates/sui-graphql-rpc/src/types/query.rs | 24 ++- .../snapshot_tests__schema_sdl_export.snap | 24 +++ 7 files changed, 523 insertions(+), 22 deletions(-) diff --git a/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp b/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp index 7b8ad9c4c32cc..7a0e6ec3faeed 100644 --- a/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp +++ b/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp @@ -1,4 +1,4 @@ -processed 14 tasks +processed 15 tasks init: A: object(0,0) @@ -13,7 +13,7 @@ task 2, line 11: //# create-checkpoint Checkpoint created: 1 -task 3, lines 13-21: +task 3, lines 13-28: //# run-graphql Response: { "data": { @@ -28,21 +28,45 @@ Response: { ] } } + }, + "packages": { + "nodes": [ + { + "address": "0x0000000000000000000000000000000000000000000000000000000000000001", + "version": 1 + }, + { + "address": "0x0000000000000000000000000000000000000000000000000000000000000002", + "version": 1 + }, + { + "address": "0x0000000000000000000000000000000000000000000000000000000000000003", + "version": 1 + }, + { + "address": "0x000000000000000000000000000000000000000000000000000000000000dee9", + "version": 1 + }, + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + } + ] } } } -task 4, lines 23-27: +task 4, lines 30-34: //# upgrade --package P0 --upgrade-capability 1,1 --sender A created: object(4,0) mutated: object(0,0), object(1,1) gas summary: computation_cost: 1000000, storage_cost: 5251600, storage_rebate: 2595780, non_refundable_storage_fee: 26220 -task 5, line 29: +task 5, line 36: //# create-checkpoint Checkpoint created: 2 -task 6, lines 31-39: +task 6, lines 38-53: //# run-graphql Response: { "data": { @@ -60,21 +84,49 @@ Response: { ] } } + }, + "packages": { + "nodes": [ + { + "address": "0x0000000000000000000000000000000000000000000000000000000000000001", + "version": 1 + }, + { + "address": "0x0000000000000000000000000000000000000000000000000000000000000002", + "version": 1 + }, + { + "address": "0x0000000000000000000000000000000000000000000000000000000000000003", + "version": 1 + }, + { + "address": "0x000000000000000000000000000000000000000000000000000000000000dee9", + "version": 1 + }, + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + }, + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + } + ] } } } -task 7, lines 41-46: +task 7, lines 55-60: //# upgrade --package P1 --upgrade-capability 1,1 --sender A created: object(7,0) mutated: object(0,0), object(1,1) gas summary: computation_cost: 1000000, storage_cost: 5426400, storage_rebate: 2595780, non_refundable_storage_fee: 26220 -task 8, line 48: +task 8, line 62: //# create-checkpoint Checkpoint created: 3 -task 9, lines 50-58: +task 9, lines 64-79: //# run-graphql Response: { "data": { @@ -95,11 +147,43 @@ Response: { ] } } + }, + "packages": { + "nodes": [ + { + "address": "0x0000000000000000000000000000000000000000000000000000000000000001", + "version": 1 + }, + { + "address": "0x0000000000000000000000000000000000000000000000000000000000000002", + "version": 1 + }, + { + "address": "0x0000000000000000000000000000000000000000000000000000000000000003", + "version": 1 + }, + { + "address": "0x000000000000000000000000000000000000000000000000000000000000dee9", + "version": 1 + }, + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + }, + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + }, + { + "address": "0x0eae57b7a07b0548b1f6b0c309f0692828ff994e9159b541334b25582980631c", + "version": 3 + } + ] } } } -task 10, lines 60-97: +task 10, lines 81-118: //# run-graphql Response: { "data": { @@ -199,7 +283,7 @@ Response: { } } -task 11, lines 99-136: +task 11, lines 120-157: //# run-graphql Response: { "data": { @@ -290,7 +374,7 @@ Response: { } } -task 12, lines 138-193: +task 12, lines 159-214: //# run-graphql Response: { "data": { @@ -429,7 +513,7 @@ Response: { } } -task 13, lines 195-223: +task 13, lines 216-244: //# run-graphql Response: { "data": { @@ -441,3 +525,99 @@ Response: { "v4": null } } + +task 14, lines 246-277: +//# run-graphql +Response: { + "data": { + "before": { + "nodes": [ + { + "address": "0x0000000000000000000000000000000000000000000000000000000000000001", + "version": 1, + "previousTransactionBlock": { + "effects": { + "checkpoint": { + "sequenceNumber": 0 + } + } + } + }, + { + "address": "0x0000000000000000000000000000000000000000000000000000000000000002", + "version": 1, + "previousTransactionBlock": { + "effects": { + "checkpoint": { + "sequenceNumber": 0 + } + } + } + }, + { + "address": "0x0000000000000000000000000000000000000000000000000000000000000003", + "version": 1, + "previousTransactionBlock": { + "effects": { + "checkpoint": { + "sequenceNumber": 0 + } + } + } + }, + { + "address": "0x000000000000000000000000000000000000000000000000000000000000dee9", + "version": 1, + "previousTransactionBlock": { + "effects": { + "checkpoint": { + "sequenceNumber": 0 + } + } + } + } + ] + }, + "after": { + "nodes": [ + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2, + "previousTransactionBlock": { + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "address": "0x0eae57b7a07b0548b1f6b0c309f0692828ff994e9159b541334b25582980631c", + "version": 3, + "previousTransactionBlock": { + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + }, + "between": { + "nodes": [ + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2, + "previousTransactionBlock": { + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/packages/versioning.move b/crates/sui-graphql-e2e-tests/tests/packages/versioning.move index f4646294722b7..694072fb9c445 100644 --- a/crates/sui-graphql-e2e-tests/tests/packages/versioning.move +++ b/crates/sui-graphql-e2e-tests/tests/packages/versioning.move @@ -18,6 +18,13 @@ module P0::m { functions { nodes { name } } } } + + packages(first: 10) { + nodes { + address + version + } + } } //# upgrade --package P0 --upgrade-capability 1,1 --sender A @@ -36,6 +43,13 @@ module P1::m { functions { nodes { name } } } } + + packages(first: 10) { + nodes { + address + version + } + } } //# upgrade --package P1 --upgrade-capability 1,1 --sender A @@ -55,6 +69,13 @@ module P2::m { functions { nodes { name } } } } + + packages(first: 10) { + nodes { + address + version + } + } } //# run-graphql @@ -221,3 +242,36 @@ module P2::m { } } } + +//# run-graphql +{ # Querying packages with checkpoint bounds + before: packages(first: 10, filter: { beforeCheckpoint: 1 }) { + nodes { + address + version + previousTransactionBlock { + effects { checkpoint { sequenceNumber } } + } + } + } + + after: packages(first: 10, filter: { afterCheckpoint: 1 }) { + nodes { + address + version + previousTransactionBlock { + effects { checkpoint { sequenceNumber } } + } + } + } + + between: packages(first: 10, filter: { afterCheckpoint: 1, beforeCheckpoint: 3 }) { + nodes { + address + version + previousTransactionBlock { + effects { checkpoint { sequenceNumber } } + } + } + } +} diff --git a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql b/crates/sui-graphql-rpc/schema/current_progress_schema.graphql index 69472d0578045..95fca9ce535b8 100644 --- a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql +++ b/crates/sui-graphql-rpc/schema/current_progress_schema.graphql @@ -2201,6 +2201,22 @@ type MovePackage implements IObject & IOwner { moduleBcs: Base64 } +""" +Filter for paginating `MovePackage`s that were created within a range of checkpoints. +""" +input MovePackageCheckpointFilter { + """ + Fetch packages that were published strictly after this checkpoint. Omitting this fetches + packages published since genesis. + """ + afterCheckpoint: UInt53 + """ + Fetch packages that were published strictly before this checkpoint. Omitting this fetches + packages published up to the latest checkpoint (inclusive). + """ + beforeCheckpoint: UInt53 +} + type MovePackageConnection { """ Information to aid in pagination. @@ -3115,6 +3131,14 @@ type Query { """ objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): ObjectConnection! """ + The Move packages that exist in the network, optionally filtered to be strictly before + `beforeCheckpoint` and/or strictly after `afterCheckpoint`. + + This query will return all versions of a given user package that appear between the + specified checkpoints, but only records the latest versions of system packages. + """ + packages(first: Int, after: String, last: Int, before: String, filter: MovePackageCheckpointFilter): MovePackageConnection! + """ Fetch the protocol config by protocol version (defaults to the latest protocol version known to the GraphQL service). """ diff --git a/crates/sui-graphql-rpc/src/types/event.rs b/crates/sui-graphql-rpc/src/types/event.rs index 16284f01618cd..6b7ba8ee8b3c2 100644 --- a/crates/sui-graphql-rpc/src/types/event.rs +++ b/crates/sui-graphql-rpc/src/types/event.rs @@ -143,13 +143,13 @@ impl Event { /// checkpoint sequence numbers as the cursor to determine the correct page of results. The /// query can optionally be further `filter`-ed by the `EventFilter`. /// - /// The `checkpoint_viewed_at` parameter is represents the checkpoint sequence number at which - /// this page was queried for. Each entity returned in the connection will inherit this - /// checkpoint, so that when viewing that entity's state, it will be from the reference of this - /// checkpoint_viewed_at parameter. + /// The `checkpoint_viewed_at` parameter represents the checkpoint sequence number at which this + /// page was queried. Each entity returned in the connection will inherit this checkpoint, so + /// that when viewing that entity's state, it will be as if it is being viewed at this + /// checkpoint. /// - /// If the `Page` is set, then this function will defer to the `checkpoint_viewed_at` in - /// the cursor if they are consistent. + /// The cursors in `page` may also include checkpoint viewed at fields. If these are set, they + /// take precedence over the checkpoint that pagination is being conducted in. pub(crate) async fn paginate( db: &Db, page: Page, diff --git a/crates/sui-graphql-rpc/src/types/move_package.rs b/crates/sui-graphql-rpc/src/types/move_package.rs index 1e8490be2465b..d29612246c305 100644 --- a/crates/sui-graphql-rpc/src/types/move_package.rs +++ b/crates/sui-graphql-rpc/src/types/move_package.rs @@ -7,7 +7,7 @@ use super::balance::{self, Balance}; use super::base64::Base64; use super::big_int::BigInt; use super::coin::Coin; -use super::cursor::{JsonCursor, Page}; +use super::cursor::{BcsCursor, JsonCursor, Page, RawPaginated, Target}; use super::move_module::MoveModule; use super::move_object::MoveObject; use super::object::{self, Object, ObjectFilter, ObjectImpl, ObjectOwner, ObjectStatus}; @@ -18,14 +18,19 @@ use super::suins_registration::{DomainFormat, SuinsRegistration}; use super::transaction_block::{self, TransactionBlock, TransactionBlockFilter}; use super::type_filter::ExactTypeFilter; use super::uint53::UInt53; -use crate::consistency::ConsistentNamedCursor; +use crate::consistency::{Checkpointed, ConsistentNamedCursor}; use crate::data::{DataLoader, Db, DbConnection, QueryExecutor}; use crate::error::Error; +use crate::raw_query::RawQuery; use crate::types::sui_address::addr; +use crate::{filter, query}; use async_graphql::connection::{Connection, CursorType, Edge}; use async_graphql::dataloader::Loader; use async_graphql::*; -use diesel::{BoolExpressionMethods, ExpressionMethods, JoinOnDsl, QueryDsl}; +use diesel::prelude::QueryableByName; +use diesel::{BoolExpressionMethods, ExpressionMethods, JoinOnDsl, QueryDsl, Selectable}; +use serde::{Deserialize, Serialize}; +use sui_indexer::models::objects::StoredHistoryObject; use sui_indexer::schema::packages; use sui_package_resolver::{error::Error as PackageCacheError, Package as ParsedMovePackage}; use sui_types::is_system_package; @@ -41,6 +46,18 @@ pub(crate) struct MovePackage { pub native: NativeMovePackage, } +/// Filter for paginating `MovePackage`s that were created within a range of checkpoints. +#[derive(InputObject, Debug, Default, Clone)] +pub(crate) struct MovePackageCheckpointFilter { + /// Fetch packages that were published strictly after this checkpoint. Omitting this fetches + /// packages published since genesis. + pub after_checkpoint: Option, + + /// Fetch packages that were published strictly before this checkpoint. Omitting this fetches + /// packages published up to the latest checkpoint (inclusive). + pub before_checkpoint: Option, +} + /// Filter for a point query of a MovePackage, supporting querying different versions of a package /// by their version. Note that different versions of the same user package exist at different IDs /// to each other, so this is different from looking up the historical version of an object. @@ -87,9 +104,31 @@ struct TypeOrigin { defining_id: SuiAddress, } +/// A wrapper around the stored representation of a package, used to implement pagination-related +/// traits. +#[derive(Selectable, QueryableByName)] +#[diesel(table_name = packages)] +struct StoredHistoryPackage { + original_id: Vec, + #[diesel(embed)] + object: StoredHistoryObject, +} + pub(crate) struct MovePackageDowncastError; pub(crate) type CModule = JsonCursor; +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. +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] +pub(crate) struct PackageCursor { + pub checkpoint_sequence_number: u64, + pub original_id: Vec, + pub package_version: u64, + pub checkpoint_viewed_at: u64, +} /// DataLoader key for fetching the storage ID of the (user) package that shares an original (aka /// runtime) ID with the package stored at `package_id`, and whose version is `version`. @@ -574,6 +613,164 @@ impl MovePackage { Error::Internal(format!("{address} is not a package")) })?)) } + + /// Query the database for a `page` of Move packages. The Page uses the checkpoint sequence + /// number the package was created at, its original ID, and its version as the cursor. The query + /// can optionally be filtered by a bound on the checkpoints the packages were created in. + /// + /// The `checkpoint_viewed_at` parameter represents the checkpoint sequence number at which this + /// page was queried. Each entity returned in the connection will inherit this checkpoint, so + /// that when viewing that entity's state, it will be as if it is being viewed at this + /// checkpoint. + /// + /// The cursors in `page` may also include checkpoint viewed at fields. If these are set, they + /// take precedence over the checkpoint that pagination is being conducted in. + pub(crate) async fn paginate_by_checkpoint( + db: &Db, + page: Page, + filter: Option, + checkpoint_viewed_at: u64, + ) -> Result, Error> { + let cursor_viewed_at = page.validate_cursor_consistency()?; + let checkpoint_viewed_at = cursor_viewed_at.unwrap_or(checkpoint_viewed_at); + + let after_checkpoint: Option = filter + .as_ref() + .and_then(|f| f.after_checkpoint) + .map(|v| v.into()); + + // Clamp the "before checkpoint" bound by "checkpoint viewed at". + let before_checkpoint = filter + .as_ref() + .and_then(|f| f.before_checkpoint) + .map(|v| v.into()) + .unwrap_or(u64::MAX) + .min(checkpoint_viewed_at + 1); + + let (prev, next, results) = db + .execute(move |conn| { + 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 + "# + ); + + q = filter!( + q, + format!("o.checkpoint_sequence_number < {before_checkpoint}") + ); + if let Some(after) = after_checkpoint { + q = filter!(q, format!("{after} < o.checkpoint_sequence_number")); + } + + page.paginate_raw_query::(conn, checkpoint_viewed_at, q) + }) + .await?; + + let mut conn = Connection::new(prev, next); + + // 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)?; + conn.edges.push(Edge::new(cursor, package)); + } + + Ok(conn) + } + + /// `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, + checkpoint_viewed_at: u64, + ) -> Result { + let object = Object::try_from_stored_history_object( + history_object, + checkpoint_viewed_at, + /* root_version */ None, + )?; + Self::try_from(&object).map_err(|_| Error::Internal("Not a package!".to_string())) + } +} + +impl Checkpointed for Cursor { + fn checkpoint_viewed_at(&self) -> u64 { + self.checkpoint_viewed_at + } +} + +impl RawPaginated for StoredHistoryPackage { + fn filter_ge(cursor: &Cursor, query: RawQuery) -> RawQuery { + filter!( + query, + format!( + "o.checkpoint_sequence_number > {cp} OR (\ + o.checkpoint_sequence_number = {cp} AND + p.original_id > '\\x{id}'::bytea OR (\ + p.original_id = '\\x{id}'::bytea AND \ + p.package_version >= {pv}\ + ))", + cp = cursor.checkpoint_sequence_number, + id = hex::encode(&cursor.original_id), + pv = cursor.package_version, + ) + ) + } + + fn filter_le(cursor: &Cursor, query: RawQuery) -> RawQuery { + filter!( + query, + format!( + "o.checkpoint_sequence_number < {cp} OR (\ + o.checkpoint_sequence_number = {cp} AND + p.original_id < '\\x{id}'::bytea OR (\ + p.original_id = '\\x{id}'::bytea AND \ + p.package_version <= {pv}\ + ))", + cp = cursor.checkpoint_sequence_number, + id = hex::encode(&cursor.original_id), + pv = cursor.package_version, + ) + ) + } + + fn order(asc: bool, query: RawQuery) -> RawQuery { + if asc { + query + .order_by("o.checkpoint_sequence_number ASC") + .order_by("p.original_id ASC") + .order_by("p.package_version ASC") + } else { + query + .order_by("o.checkpoint_sequence_number DESC") + .order_by("p.original_id DESC") + .order_by("p.package_version DESC") + } + } +} + +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, + original_id: self.original_id.clone(), + package_version: self.object.object_version as u64, + checkpoint_viewed_at, + }) + } } #[async_trait::async_trait] diff --git a/crates/sui-graphql-rpc/src/types/query.rs b/crates/sui-graphql-rpc/src/types/query.rs index 5aa55e7334773..0970a046749c3 100644 --- a/crates/sui-graphql-rpc/src/types/query.rs +++ b/crates/sui-graphql-rpc/src/types/query.rs @@ -12,7 +12,7 @@ use sui_sdk::SuiClient; use sui_types::transaction::{TransactionData, TransactionKind}; use sui_types::{gas_coin::GAS, transaction::TransactionDataAPI, TypeTag}; -use super::move_package::MovePackage; +use super::move_package::{self, MovePackage, MovePackageCheckpointFilter}; use super::suins_registration::NameService; use super::uint53::UInt53; use super::{ @@ -435,6 +435,28 @@ impl Query { .extend() } + /// The Move packages that exist in the network, optionally filtered to be strictly before + /// `beforeCheckpoint` and/or strictly after `afterCheckpoint`. + /// + /// This query will return all versions of a given user package that appear between the + /// specified checkpoints, but only records the latest versions of system packages. + async fn packages( + &self, + ctx: &Context<'_>, + first: Option, + after: Option, + last: Option, + before: Option, + filter: Option, + ) -> Result> { + let Watermark { checkpoint, .. } = *ctx.data()?; + + let page = Page::from_params(ctx.data_unchecked(), first, after, last, before)?; + MovePackage::paginate_by_checkpoint(ctx.data_unchecked(), page, filter, checkpoint) + .await + .extend() + } + /// Fetch the protocol config by protocol version (defaults to the latest protocol /// version known to the GraphQL service). async fn protocol_config( diff --git a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap index 61cabeeb1de13..3499ea88329cc 100644 --- a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap +++ b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap @@ -2205,6 +2205,22 @@ type MovePackage implements IObject & IOwner { moduleBcs: Base64 } +""" +Filter for paginating `MovePackage`s that were created within a range of checkpoints. +""" +input MovePackageCheckpointFilter { + """ + Fetch packages that were published strictly after this checkpoint. Omitting this fetches + packages published since genesis. + """ + afterCheckpoint: UInt53 + """ + Fetch packages that were published strictly before this checkpoint. Omitting this fetches + packages published up to the latest checkpoint (inclusive). + """ + beforeCheckpoint: UInt53 +} + type MovePackageConnection { """ Information to aid in pagination. @@ -3119,6 +3135,14 @@ type Query { """ objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): ObjectConnection! """ + The Move packages that exist in the network, optionally filtered to be strictly before + `beforeCheckpoint` and/or strictly after `afterCheckpoint`. + + This query will return all versions of a given user package that appear between the + specified checkpoints, but only records the latest versions of system packages. + """ + packages(first: Int, after: String, last: Int, before: String, filter: MovePackageCheckpointFilter): MovePackageConnection! + """ Fetch the protocol config by protocol version (defaults to the latest protocol version known to the GraphQL service). """ From c90c28f174288ffec3eaf44e021b2cceddd6a320 Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Fri, 10 May 2024 18:29:57 +0100 Subject: [PATCH 169/232] [GraphQL/MovePackage] Paginate by version (#17697) ## Description Introduce two new queries: `Query.packageVersions` and `MovePackage.versions` for iterating over all the different versions of a given package. This kind of query is useful for understanding package history. These were introduced as a separate query, instead of having a single query for iterating over packages that could optionally take a checkpoint bounds or version bounds because of how system packages interact with the `packages` table: Because system packages are updated in-place, they only have one row in the `packages` table. This makes sense for paginating packages in bulk (e.g. by checkpoint) where the primary aim is to get a snapshot of the packages available at a certain point in time, but doesn't work for answering package version queries for system packages, and it prevents us from creating a combined query. A combined query would also allow someone to create a filter that bounds checkpoints and versions, but doesn't bound the package itself (or would require us to prevent that combination), which is complicated to implement efficiently and not particularly useful. ## Test plan New E2E tests: ``` sui$ cargo nextest run -p sui-graphql-e2e-tests \ --features pg_integration \ -- packages/versioning ``` & Testing against a read replica to make sure system package tests work well, and performance is reasonable. ## Stack - #17686 - #17687 - #17688 - #17689 - #17691 - #17694 - #17695 - #17542 - #17690 - #17543 - #17692 - #17693 - #17696 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [x] GraphQL: Introduces `Query.packageVersions` and `MovePackage.versions` for paginating over the versions of a particular package. - [ ] CLI: - [ ] Rust SDK: --- .../tests/packages/versioning.exp | 217 ++++++++++++++++-- .../tests/packages/versioning.move | 123 ++++++++++ .../schema/current_progress_schema.graphql | 28 +++ .../sui-graphql-rpc/src/types/move_package.rs | 197 +++++++++++++++- crates/sui-graphql-rpc/src/types/query.rs | 25 +- .../snapshot_tests__schema_sdl_export.snap | 28 +++ 6 files changed, 594 insertions(+), 24 deletions(-) diff --git a/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp b/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp index 7a0e6ec3faeed..7f0e8a7153b98 100644 --- a/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp +++ b/crates/sui-graphql-e2e-tests/tests/packages/versioning.exp @@ -1,4 +1,4 @@ -processed 15 tasks +processed 17 tasks init: A: object(0,0) @@ -13,7 +13,7 @@ task 2, line 11: //# create-checkpoint Checkpoint created: 1 -task 3, lines 13-28: +task 3, lines 13-50: //# run-graphql Response: { "data": { @@ -27,6 +27,35 @@ Response: { } ] } + }, + "packageVersions": { + "nodes": [ + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + } + ] + } + }, + "firstPackage": { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1, + "module": { + "functions": { + "nodes": [ + { + "name": "f" + } + ] + } + }, + "packageVersions": { + "nodes": [ + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + } + ] } }, "packages": { @@ -56,17 +85,17 @@ Response: { } } -task 4, lines 30-34: +task 4, lines 52-56: //# upgrade --package P0 --upgrade-capability 1,1 --sender A created: object(4,0) mutated: object(0,0), object(1,1) gas summary: computation_cost: 1000000, storage_cost: 5251600, storage_rebate: 2595780, non_refundable_storage_fee: 26220 -task 5, line 36: +task 5, line 58: //# create-checkpoint Checkpoint created: 2 -task 6, lines 38-53: +task 6, lines 60-97: //# run-graphql Response: { "data": { @@ -83,6 +112,43 @@ Response: { } ] } + }, + "packageVersions": { + "nodes": [ + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + }, + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + } + ] + } + }, + "firstPackage": { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1, + "module": { + "functions": { + "nodes": [ + { + "name": "f" + } + ] + } + }, + "packageVersions": { + "nodes": [ + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + }, + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + } + ] } }, "packages": { @@ -116,17 +182,17 @@ Response: { } } -task 7, lines 55-60: +task 7, lines 99-104: //# upgrade --package P1 --upgrade-capability 1,1 --sender A created: object(7,0) mutated: object(0,0), object(1,1) gas summary: computation_cost: 1000000, storage_cost: 5426400, storage_rebate: 2595780, non_refundable_storage_fee: 26220 -task 8, line 62: +task 8, line 106: //# create-checkpoint Checkpoint created: 3 -task 9, lines 64-79: +task 9, lines 108-145: //# run-graphql Response: { "data": { @@ -146,6 +212,51 @@ Response: { } ] } + }, + "packageVersions": { + "nodes": [ + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + }, + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + }, + { + "address": "0x0eae57b7a07b0548b1f6b0c309f0692828ff994e9159b541334b25582980631c", + "version": 3 + } + ] + } + }, + "firstPackage": { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1, + "module": { + "functions": { + "nodes": [ + { + "name": "f" + } + ] + } + }, + "packageVersions": { + "nodes": [ + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + }, + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + }, + { + "address": "0x0eae57b7a07b0548b1f6b0c309f0692828ff994e9159b541334b25582980631c", + "version": 3 + } + ] } }, "packages": { @@ -183,7 +294,7 @@ Response: { } } -task 10, lines 81-118: +task 10, lines 147-184: //# run-graphql Response: { "data": { @@ -283,7 +394,7 @@ Response: { } } -task 11, lines 120-157: +task 11, lines 186-223: //# run-graphql Response: { "data": { @@ -374,7 +485,7 @@ Response: { } } -task 12, lines 159-214: +task 12, lines 225-280: //# run-graphql Response: { "data": { @@ -513,7 +624,7 @@ Response: { } } -task 13, lines 216-244: +task 13, lines 282-310: //# run-graphql Response: { "data": { @@ -526,7 +637,7 @@ Response: { } } -task 14, lines 246-277: +task 14, lines 312-343: //# run-graphql Response: { "data": { @@ -621,3 +732,83 @@ Response: { } } } + +task 15, lines 345-380: +//# run-graphql +Response: { + "data": { + "packageVersions": { + "nodes": [ + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + }, + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + }, + { + "address": "0x0eae57b7a07b0548b1f6b0c309f0692828ff994e9159b541334b25582980631c", + "version": 3 + } + ] + }, + "after": { + "nodes": [ + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + }, + { + "address": "0x0eae57b7a07b0548b1f6b0c309f0692828ff994e9159b541334b25582980631c", + "version": 3 + } + ] + }, + "before": { + "nodes": [ + { + "address": "0x175ae86f2df1eb652d57fbe9e44c7f2d67870d2b6776a4356f30930221b63b88", + "version": 1 + }, + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + } + ] + }, + "between": { + "nodes": [ + { + "address": "0x351bc614b36f0f522a64334e4c278d4bfe200234958870c084e0a005f041d681", + "version": 2 + } + ] + } + } +} + +task 16, lines 382-400: +//# run-graphql +Response: { + "data": { + "packageVersions": { + "nodes": [ + { + "address": "0x0000000000000000000000000000000000000000000000000000000000000001", + "version": 1 + } + ] + }, + "package": { + "packageVersions": { + "nodes": [ + { + "address": "0x0000000000000000000000000000000000000000000000000000000000000001", + "version": 1 + } + ] + } + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/packages/versioning.move b/crates/sui-graphql-e2e-tests/tests/packages/versioning.move index 694072fb9c445..b0e0900bbcb59 100644 --- a/crates/sui-graphql-e2e-tests/tests/packages/versioning.move +++ b/crates/sui-graphql-e2e-tests/tests/packages/versioning.move @@ -17,6 +17,28 @@ module P0::m { module(name: "m") { functions { nodes { name } } } + + packageVersions { + nodes { + address + version + } + } + } + + firstPackage: package(address: "@{P0}", version: 1) { + address + version + module(name: "m") { + functions { nodes { name } } + } + + packageVersions { + nodes { + address + version + } + } } packages(first: 10) { @@ -42,6 +64,28 @@ module P1::m { module(name: "m") { functions { nodes { name } } } + + packageVersions { + nodes { + address + version + } + } + } + + firstPackage: package(address: "@{P1}", version: 1) { + address + version + module(name: "m") { + functions { nodes { name } } + } + + packageVersions { + nodes { + address + version + } + } } packages(first: 10) { @@ -68,6 +112,28 @@ module P2::m { module(name: "m") { functions { nodes { name } } } + + packageVersions { + nodes { + address + version + } + } + } + + firstPackage: package(address: "@{P2}", version: 1) { + address + version + module(name: "m") { + functions { nodes { name } } + } + + packageVersions { + nodes { + address + version + } + } } packages(first: 10) { @@ -275,3 +341,60 @@ module P2::m { } } } + +//# run-graphql +{ # Query for versions of a user package + packageVersions(address: "@{P0}") { + nodes { + address + version + } + } + + after: packageVersions(address: "@{P0}", filter: { afterVersion: 1 }) { + nodes { + address + version + } + } + + before: packageVersions(address: "@{P0}", filter: { beforeVersion: 3 }) { + nodes { + address + version + } + } + + between: packageVersions( + address: "@{P0}", + filter: { + afterVersion: 1, + beforeVersion: 3, + }, + ) { + nodes { + address + version + } + } +} + +//# run-graphql +{ # Query for versions of a system package (there will be only one because we + # don't have a way to upgrade system packages in these tests.) + packageVersions(address: "0x1") { + nodes { + address + version + } + } + + package(address: "0x1") { + packageVersions { + nodes { + address + version + } + } + } +} diff --git a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql b/crates/sui-graphql-rpc/schema/current_progress_schema.graphql index 95fca9ce535b8..9a134382a7a70 100644 --- a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql +++ b/crates/sui-graphql-rpc/schema/current_progress_schema.graphql @@ -2173,6 +2173,12 @@ type MovePackage implements IObject & IOwner { """ packageAtVersion(version: Int!): MovePackage """ + Fetch all versions of this package (packages that share this package's original ID), + optionally bounding the versions exclusively from below with `afterVersion`, or from above + with `beforeVersion`. + """ + packageVersions(first: Int, after: String, last: Int, before: String, filter: MovePackageVersionFilter): MovePackageConnection! + """ Fetch the latest version of this package (the package with the highest `version` that shares this packages's original ID) """ @@ -2246,6 +2252,22 @@ type MovePackageEdge { cursor: String! } +""" +Filter for paginating versions of a given `MovePackage`. +""" +input MovePackageVersionFilter { + """ + Fetch versions of this package that are strictly newer than this version. Omitting this + fetches versions since the original version. + """ + afterVersion: UInt53 + """ + Fetch versions of this package that are strictly older than this version. Omitting this + fetches versions up to the latest version (inclusive). + """ + beforeVersion: UInt53 +} + """ Description of a struct type, defined in a Move module. """ @@ -3139,6 +3161,12 @@ type Query { """ packages(first: Int, after: String, last: Int, before: String, filter: MovePackageCheckpointFilter): MovePackageConnection! """ + Fetch all versions of package at `address` (packages that share this package's original ID), + optionally bounding the versions exclusively from below with `afterVersion`, or from above + with `beforeVersion`. + """ + packageVersions(first: Int, after: String, last: Int, before: String, address: SuiAddress!, filter: MovePackageVersionFilter): MovePackageConnection! + """ Fetch the protocol config by protocol version (defaults to the latest protocol version known to the GraphQL service). """ diff --git a/crates/sui-graphql-rpc/src/types/move_package.rs b/crates/sui-graphql-rpc/src/types/move_package.rs index d29612246c305..aba7259ff1a22 100644 --- a/crates/sui-graphql-rpc/src/types/move_package.rs +++ b/crates/sui-graphql-rpc/src/types/move_package.rs @@ -58,6 +58,18 @@ pub(crate) struct MovePackageCheckpointFilter { pub before_checkpoint: Option, } +/// Filter for paginating versions of a given `MovePackage`. +#[derive(InputObject, Debug, Default, Clone)] +pub(crate) struct MovePackageVersionFilter { + /// Fetch versions of this package that are strictly newer than this version. Omitting this + /// fetches versions since the original version. + pub after_version: Option, + + /// Fetch versions of this package that are strictly older than this version. Omitting this + /// fetches versions up to the latest version (inclusive). + pub before_version: Option, +} + /// Filter for a point query of a MovePackage, supporting querying different versions of a package /// by their version. Note that different versions of the same user package exist at different IDs /// to each other, so this is different from looking up the historical version of an object. @@ -355,6 +367,31 @@ impl MovePackage { .extend() } + /// Fetch all versions of this package (packages that share this package's original ID), + /// optionally bounding the versions exclusively from below with `afterVersion`, or from above + /// with `beforeVersion`. + async fn package_versions( + &self, + ctx: &Context<'_>, + first: Option, + after: Option, + last: Option, + before: Option, + filter: Option, + ) -> Result> { + let page = Page::from_params(ctx.data_unchecked(), first, after, last, before)?; + + MovePackage::paginate_by_version( + ctx.data_unchecked(), + page, + self.super_.address, + filter, + self.checkpoint_viewed_at_impl(), + ) + .await + .extend() + } + /// Fetch the latest version of this package (the package with the highest `version` that shares /// this packages's original ID) async fn latest_package(&self, ctx: &Context<'_>) -> Result { @@ -690,6 +727,55 @@ impl MovePackage { Ok(conn) } + /// Query the database for a `page` of Move packages. The Page uses the checkpoint sequence + /// number the package was created at, its original ID, and its version as the cursor. The query + /// is filtered by the ID of a package and will only return packages from the same family + /// (sharing the same original ID as the package whose ID was given), and can optionally be + /// filtered by an upper and lower bound on package version. + /// + /// The `checkpoint_viewed_at` parameter represents the checkpoint sequence number at which this + /// page was queried. Each entity returned in the connection will inherit this checkpoint, so + /// that when viewing that entity's state, it will be as if it is being viewed at this + /// checkpoint. + /// + /// The cursors in `page` may also include checkpoint viewed at fields. If these are set, they + /// take precedence over the checkpoint that pagination is being conducted in. + pub(crate) async fn paginate_by_version( + db: &Db, + page: Page, + package: SuiAddress, + filter: Option, + checkpoint_viewed_at: u64, + ) -> Result, Error> { + 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| { + page.paginate_raw_query::( + conn, + checkpoint_viewed_at, + if is_system_package(package) { + system_package_version_query(package, filter) + } else { + user_package_version_query(package, filter) + }, + ) + }) + .await?; + + let mut conn = Connection::new(prev, next); + + // 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)?; + conn.edges.push(Edge::new(cursor, package)); + } + + Ok(conn) + } + /// `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). @@ -719,9 +805,9 @@ impl RawPaginated for StoredHistoryPackage { format!( "o.checkpoint_sequence_number > {cp} OR (\ o.checkpoint_sequence_number = {cp} AND - p.original_id > '\\x{id}'::bytea OR (\ - p.original_id = '\\x{id}'::bytea AND \ - p.package_version >= {pv}\ + original_id > '\\x{id}'::bytea OR (\ + original_id = '\\x{id}'::bytea AND \ + o.object_version >= {pv}\ ))", cp = cursor.checkpoint_sequence_number, id = hex::encode(&cursor.original_id), @@ -736,9 +822,9 @@ impl RawPaginated for StoredHistoryPackage { format!( "o.checkpoint_sequence_number < {cp} OR (\ o.checkpoint_sequence_number = {cp} AND - p.original_id < '\\x{id}'::bytea OR (\ - p.original_id = '\\x{id}'::bytea AND \ - p.package_version <= {pv}\ + original_id < '\\x{id}'::bytea OR (\ + original_id = '\\x{id}'::bytea AND \ + o.object_version <= {pv}\ ))", cp = cursor.checkpoint_sequence_number, id = hex::encode(&cursor.original_id), @@ -751,13 +837,13 @@ impl RawPaginated for StoredHistoryPackage { if asc { query .order_by("o.checkpoint_sequence_number ASC") - .order_by("p.original_id ASC") - .order_by("p.package_version ASC") + .order_by("original_id ASC") + .order_by("o.object_version ASC") } else { query .order_by("o.checkpoint_sequence_number DESC") - .order_by("p.original_id DESC") - .order_by("p.package_version DESC") + .order_by("original_id DESC") + .order_by("o.object_version DESC") } } } @@ -918,3 +1004,94 @@ 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. +fn system_package_version_query( + package: SuiAddress, + filter: Option, +) -> RawQuery { + // Query uses a left join to force the query planner to use `objects_version` in the outer loop. + let mut q = query!( + r#" + SELECT + o.object_id AS original_id, + o.* + FROM + objects_version v + LEFT JOIN + 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 + "# + ); + + q = filter!( + q, + format!( + "v.object_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}")); + } + + 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 +} + +/// Query for fetching all the versions of a non-system package (assumes that `package` has already +/// been verified as a system package) +fn user_package_version_query( + package: SuiAddress, + filter: Option, +) -> RawQuery { + let mut q = query!( + r#" + SELECT + p.original_id, + o.* + FROM + packages q + INNER JOIN + packages p + ON + q.original_id = p.original_id + 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 + "# + ); + + q = filter!( + q, + format!( + "q.package_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!("p.package_version > {a}")); + } + + if let Some(before) = filter.as_ref().and_then(|f| f.before_version) { + let b: u64 = before.into(); + q = filter!(q, format!("p.package_version < {b}")); + } + + q +} diff --git a/crates/sui-graphql-rpc/src/types/query.rs b/crates/sui-graphql-rpc/src/types/query.rs index 0970a046749c3..1ff209d351a55 100644 --- a/crates/sui-graphql-rpc/src/types/query.rs +++ b/crates/sui-graphql-rpc/src/types/query.rs @@ -12,7 +12,9 @@ use sui_sdk::SuiClient; use sui_types::transaction::{TransactionData, TransactionKind}; use sui_types::{gas_coin::GAS, transaction::TransactionDataAPI, TypeTag}; -use super::move_package::{self, MovePackage, MovePackageCheckpointFilter}; +use super::move_package::{ + self, MovePackage, MovePackageCheckpointFilter, MovePackageVersionFilter, +}; use super::suins_registration::NameService; use super::uint53::UInt53; use super::{ @@ -457,6 +459,27 @@ impl Query { .extend() } + /// Fetch all versions of package at `address` (packages that share this package's original ID), + /// optionally bounding the versions exclusively from below with `afterVersion`, or from above + /// with `beforeVersion`. + async fn package_versions( + &self, + ctx: &Context<'_>, + first: Option, + after: Option, + last: Option, + before: Option, + address: SuiAddress, + filter: Option, + ) -> Result> { + let Watermark { checkpoint, .. } = *ctx.data()?; + + let page = Page::from_params(ctx.data_unchecked(), first, after, last, before)?; + MovePackage::paginate_by_version(ctx.data_unchecked(), page, address, filter, checkpoint) + .await + .extend() + } + /// Fetch the protocol config by protocol version (defaults to the latest protocol /// version known to the GraphQL service). async fn protocol_config( diff --git a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap index 3499ea88329cc..87f5a27061f06 100644 --- a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap +++ b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap @@ -2177,6 +2177,12 @@ type MovePackage implements IObject & IOwner { """ packageAtVersion(version: Int!): MovePackage """ + Fetch all versions of this package (packages that share this package's original ID), + optionally bounding the versions exclusively from below with `afterVersion`, or from above + with `beforeVersion`. + """ + packageVersions(first: Int, after: String, last: Int, before: String, filter: MovePackageVersionFilter): MovePackageConnection! + """ Fetch the latest version of this package (the package with the highest `version` that shares this packages's original ID) """ @@ -2250,6 +2256,22 @@ type MovePackageEdge { cursor: String! } +""" +Filter for paginating versions of a given `MovePackage`. +""" +input MovePackageVersionFilter { + """ + Fetch versions of this package that are strictly newer than this version. Omitting this + fetches versions since the original version. + """ + afterVersion: UInt53 + """ + Fetch versions of this package that are strictly older than this version. Omitting this + fetches versions up to the latest version (inclusive). + """ + beforeVersion: UInt53 +} + """ Description of a struct type, defined in a Move module. """ @@ -3143,6 +3165,12 @@ type Query { """ packages(first: Int, after: String, last: Int, before: String, filter: MovePackageCheckpointFilter): MovePackageConnection! """ + Fetch all versions of package at `address` (packages that share this package's original ID), + optionally bounding the versions exclusively from below with `afterVersion`, or from above + with `beforeVersion`. + """ + packageVersions(first: Int, after: String, last: Int, before: String, address: SuiAddress!, filter: MovePackageVersionFilter): MovePackageConnection! + """ Fetch the protocol config by protocol version (defaults to the latest protocol version known to the GraphQL service). """ From 4110e6eaae9fcd26d5284b319b2f88dda9aa4984 Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Mon, 19 Aug 2024 11:40:43 +0100 Subject: [PATCH 170/232] [chore][GraphQL] Clean-up unused commands (#18287) ## Description Remove commands to generate examples, markdown and schema from the main binary as we do not use them: - Instead of generating examples, we have hand-crafted examples in our docs. Removing this code also removes a test that forces regeneration of a markdown file from these docs (which we also were not using). - We also never used the output from the `generate-schema` sub-command, because the schema was always available as a file, or via introspection commands from the running service. - Logic for gathering examples to test has been moved into the test file, to avoid including test-only code in the main library. ## Test plan CI ## Description Describe the changes or additions included in this PR. ## Test plan How did you test the new or updated feature? ## Stack - #17543 - #17692 - #17693 - #17696 - #17697 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [x] GraphQL: The GraphQL binary no longer supports generating examples, or exporting its own schema as these commands have been unused for some time. - [ ] CLI: - [ ] Rust SDK: --- Cargo.lock | 7 - Cargo.toml | 1 - crates/sui-graphql-rpc/Cargo.toml | 1 - crates/sui-graphql-rpc/docs/examples.md | 1700 ----------------- crates/sui-graphql-rpc/src/commands.rs | 11 - crates/sui-graphql-rpc/src/examples.rs | 240 --- crates/sui-graphql-rpc/src/lib.rs | 1 - crates/sui-graphql-rpc/src/main.rs | 34 - .../tests/examples_validation_tests.rs | 228 ++- 9 files changed, 134 insertions(+), 2089 deletions(-) delete mode 100644 crates/sui-graphql-rpc/docs/examples.md delete mode 100644 crates/sui-graphql-rpc/src/examples.rs diff --git a/Cargo.lock b/Cargo.lock index c766d515b415e..4449deae1a1d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6716,12 +6716,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" -[[package]] -name = "markdown-gen" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034621d7f1258317ca1dfb9205e3925d27ee4aa2a46620a09c567daf0310562" - [[package]] name = "match_opt" version = "0.1.2" @@ -13353,7 +13347,6 @@ dependencies = [ "insta", "itertools 0.10.5", "lru 0.10.0", - "markdown-gen", "move-binary-format", "move-bytecode-utils", "move-core-types", diff --git a/Cargo.toml b/Cargo.toml index 55f30a7447f6b..78b95b57b41e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -369,7 +369,6 @@ jsonrpsee = { git = "https://github.com/wlmyng/jsonrpsee.git", rev = "b1b3007847 json_to_table = { git = "https://github.com/zhiburt/tabled/", rev = "e449317a1c02eb6b29e409ad6617e5d9eb7b3bd4" } leb128 = "0.2.5" lru = "0.10" -markdown-gen = "1.2.1" match_opt = "0.1.2" miette = { version = "7", features = ["fancy"] } mime = "0.3" diff --git a/crates/sui-graphql-rpc/Cargo.toml b/crates/sui-graphql-rpc/Cargo.toml index 87c8a1571a9ef..12414fd4888c7 100644 --- a/crates/sui-graphql-rpc/Cargo.toml +++ b/crates/sui-graphql-rpc/Cargo.toml @@ -32,7 +32,6 @@ lru.workspace = true move-binary-format.workspace = true move-disassembler.workspace = true move-ir-types.workspace = true -markdown-gen.workspace = true mysten-metrics.workspace = true mysten-network.workspace = true move-core-types.workspace = true diff --git a/crates/sui-graphql-rpc/docs/examples.md b/crates/sui-graphql-rpc/docs/examples.md deleted file mode 100644 index b227665131112..0000000000000 --- a/crates/sui-graphql-rpc/docs/examples.md +++ /dev/null @@ -1,1700 +0,0 @@ -# Sui GraphQL Examples -### [Address](#0) -####   [Address](#0) -####   [Transaction Block Connection](#1) -### [Balance Connection](#1) -####   [Balance Connection](#65535) -### [Chain Id](#2) -####   [Chain Id](#131070) -### [Checkpoint](#3) -####   [At Digest](#196605) -####   [At Seq Num](#196606) -####   [First Two Tx Blocks For Checkpoint](#196607) -####   [Latest Checkpoint](#196608) -####   [Multiple Selections](#196609) -####   [With Timestamp Tx Block Live Objects](#196610) -####   [With Tx Sent Addr Filter](#196611) -### [Checkpoint Connection](#4) -####   [Ascending Fetch](#262140) -####   [First Ten After Checkpoint](#262141) -####   [Last Ten After Checkpoint](#262142) -### [Coin Connection](#5) -####   [Coin Connection](#327675) -### [Coin Metadata](#6) -####   [Coin Metadata](#393210) -### [Epoch](#7) -####   [Latest Epoch](#458745) -####   [Specific Epoch](#458746) -####   [With Checkpoint Connection](#458747) -####   [With Tx Block Connection](#458748) -####   [With Tx Block Connection Latest Epoch](#458749) -### [Event Connection](#8) -####   [Event Connection](#524280) -####   [Filter By Emitting Package Module And Event Type](#524281) -####   [Filter By Sender](#524282) -### [Name Service](#9) -####   [Name Service](#589815) -### [Object](#10) -####   [Object](#655350) -### [Object Connection](#11) -####   [Filter Object Ids](#720885) -####   [Filter On Generic Type](#720886) -####   [Filter On Type](#720887) -####   [Filter Owner](#720888) -####   [Object Connection](#720889) -### [Owner](#12) -####   [Dynamic Field](#786420) -####   [Dynamic Field Connection](#786421) -####   [Dynamic Object Field](#786422) -####   [Owner](#786423) -### [Protocol Configs](#13) -####   [Key Value](#851955) -####   [Key Value Feature Flag](#851956) -####   [Specific Config](#851957) -####   [Specific Feature Flag](#851958) -### [Service Config](#14) -####   [Service Config](#917490) -### [Stake Connection](#15) -####   [Stake Connection](#983025) -### [Sui System State Summary](#16) -####   [Sui System State Summary](#1048560) -### [Transaction Block](#17) -####   [Transaction Block](#1114095) -####   [Transaction Block Kind](#1114096) -### [Transaction Block Connection](#18) -####   [Before After Checkpoint](#1179630) -####   [Changed Object Filter](#1179631) -####   [Input Object Filter](#1179632) -####   [Input Object Sign Addr Filter](#1179633) -####   [Package Filter](#1179634) -####   [Package Module Filter](#1179635) -####   [Package Module Func Filter](#1179636) -####   [Recv Addr Filter](#1179637) -####   [Sign Addr Filter](#1179638) -####   [Tx Ids Filter](#1179639) -####   [Tx Kind Filter](#1179640) -####   [With Defaults Ascending](#1179641) -### [Transaction Block Effects](#19) -####   [Transaction Block Effects](#1245165) -## -## Address -### -### Address -#### Get the address' balance and its coins' id and type - ->

{
->  address(
->    address: "0x5094652429957619e6efa79a404a6714d1126e63f551f4b6c7fb76440f8118c9"
->  ) {
->    address
->    balance {
->      coinType {
->        repr
->      }
->      coinObjectCount
->      totalBalance
->    }
->    coins {
->      nodes {
->        contents {
->          type {
->            repr
->          }
->        }
->      }
->    }
->  }
->}
- -### -### Transaction Block Connection -#### See examples in Query::transactionBlocks as this is similar behavior -#### to the `transactionBlocks` in Query but supports additional -#### `AddressTransactionBlockRelationship` filter -#### Filtering on package where the signer of the TX is the current -#### address and displaying the transaction's sender and the gas price -#### and budget. - ->
# See examples in Query::transactionBlocks as this is similar behavior
-># to the `transactionBlocks` in Query but supports additional
-># `AddressTransactionBlockRelationship` filter
->
-># Filtering on package where the signer of the TX is the current
-># address and displaying the transaction's sender and the gas price
-># and budget.
->query transaction_block_with_relation_filter {
->  address(address: "0x2") {
->    transactionBlocks(relation: SIGN, filter: { function: "0x2" }) {
->      nodes {
->        sender {
->          address
->        }
->        gasInput {
->          gasPrice
->          gasBudget
->        }
->      }
->    }
->  }
->}
- -## -## Balance Connection -### -### Balance Connection -#### Query the balance for objects of type COIN and then for each coin -#### get the coin type, the number of objects, and the total balance - ->
{
->  address(
->    address: "0x5094652429957619e6efa79a404a6714d1126e63f551f4b6c7fb76440f8118c9"
->  ) {
->    balance(
->      type: "0xc060006111016b8a020ad5b33834984a437aaa7d3c74c18e09a95d48aceab08c::coin::COIN"
->    ) {
->      coinObjectCount
->      totalBalance
->    }
->    balances {
->      nodes {
->        coinType {
->          repr
->        }
->        coinObjectCount
->        totalBalance
->      }
->      pageInfo {
->        endCursor
->      }
->    }
->  }
->}
- -## -## Chain Id -### -### Chain Id -#### Returns the chain identifier for the chain that the server is tracking - ->
{
->  chainIdentifier
->}
- -## -## Checkpoint -### -### At Digest -#### Get the checkpoint's information at a particular digest - ->
{
->  checkpoint(id: { digest: "GaDeWEfbSQCQ8FBQHUHVdm4KjrnbgMqEZPuhStoq5njU" }) {
->    digest
->    sequenceNumber
->    validatorSignatures
->    previousCheckpointDigest
->    networkTotalTransactions
->    rollingGasSummary {
->      computationCost
->      storageCost
->      storageRebate
->      nonRefundableStorageFee
->    }
->    epoch {
->      epochId
->      referenceGasPrice
->      startTimestamp
->      endTimestamp
->    }
->  }
->}
- -### -### At Seq Num -#### Get the checkpoint's information at a particular sequence number - ->
{
->  checkpoint(id: { sequenceNumber: 10 }) {
->    digest
->    sequenceNumber
->    validatorSignatures
->    previousCheckpointDigest
->    networkTotalTransactions
->    rollingGasSummary {
->      computationCost
->      storageCost
->      storageRebate
->      nonRefundableStorageFee
->    }
->    epoch {
->      epochId
->      referenceGasPrice
->      startTimestamp
->      endTimestamp
->    }
->  }
->}
- -### -### First Two Tx Blocks For Checkpoint -#### Get data for the first two transaction blocks of checkpoint at sequence number 10 - ->
{
->  checkpoint(id: { sequenceNumber: 10 }) {
->    transactionBlocks(first: 2) {
->      edges {
->        node {
->          kind {
->            __typename
->          }
->          digest
->          sender {
->            address
->          }
->          expiration {
->            epochId
->          }
->        }
->      }
->      pageInfo {
->        startCursor
->        hasNextPage
->        hasPreviousPage
->        endCursor
->      }
->    }
->  }
->}
- -### -### Latest Checkpoint -#### Latest checkpoint's data - ->
{
->  checkpoint {
->    digest
->    sequenceNumber
->    validatorSignatures
->    previousCheckpointDigest
->    networkTotalTransactions
->    rollingGasSummary {
->      computationCost
->      storageCost
->      storageRebate
->      nonRefundableStorageFee
->    }
->    epoch {
->      epochId
->      referenceGasPrice
->      startTimestamp
->      endTimestamp
->    }
->  }
->}
- -### -### Multiple Selections -#### Get the checkpoint at sequence 9769 and show -#### its transactions - ->
{
->  checkpoint(id: { sequenceNumber: 9769 }) {
->    digest
->    sequenceNumber
->    timestamp
->    validatorSignatures
->    previousCheckpointDigest
->    networkTotalTransactions
->    rollingGasSummary {
->      computationCost
->      storageCost
->      storageRebate
->      nonRefundableStorageFee
->    }
->    epoch {
->      epochId
->      liveObjectSetDigest
->    }
->    transactionBlocks {
->      edges {
->        node {
->          digest
->          sender {
->            address
->          }
->          expiration {
->            epochId
->          }
->        }
->      }
->    }
->  }
->}
- -### -### With Timestamp Tx Block Live Objects -#### Latest checkpoint's timestamp, and transaction block data - ->
{
->  checkpoint {
->    digest
->    sequenceNumber
->    timestamp
->    transactionBlocks {
->      edges {
->        node {
->          digest
->          sender {
->            address
->          }
->          expiration {
->            epochId
->          }
->        }
->      }
->    }
->  }
->}
- -### -### With Tx Sent Addr Filter -#### Select checkpoint at sequence number 14830285 for transactions from signAddress - ->
{
->  checkpoint(id: { sequenceNumber: 14830285 }) {
->    digest
->    sequenceNumber
->    timestamp
->    transactionBlocks(
->      filter: {
->        signAddress: "0x0000000000000000000000000000000000000000000000000000000000000000"
->      }
->    ) {
->      edges {
->        node {
->          digest
->          sender {
->            address
->          }
->          expiration {
->            epochId
->          }
->        }
->      }
->    }
->  }
->}
- -## -## Checkpoint Connection -### -### Ascending Fetch -#### Use the checkpoint connection to fetch some default amount of checkpoints in an ascending order - ->
{
->  checkpoints {
->    nodes {
->      digest
->      sequenceNumber
->      validatorSignatures
->      previousCheckpointDigest
->      networkTotalTransactions
->      rollingGasSummary {
->        computationCost
->        storageCost
->        storageRebate
->        nonRefundableStorageFee
->      }
->      epoch {
->        epochId
->        referenceGasPrice
->        startTimestamp
->        endTimestamp
->      }
->    }
->  }
->}
- -### -### First Ten After Checkpoint -#### Fetch the digest and sequence number of the first 10 checkpoints after the cursor, which in this example is set to be checkpoint 0. Note that the cursor is opaque. - ->
{
->  checkpoints(first: 10, after: "eyJjIjoyMjgwMDU4MCwicyI6MH0") {
->    nodes {
->      sequenceNumber
->      digest
->    }
->  }
->}
- -### -### Last Ten After Checkpoint -#### Fetch the digest and the sequence number of the last 20 checkpoints before the cursor - ->
{
->  checkpoints(last: 20, before: "eyJjIjoyMjgwMDY1MSwicyI6MjI4MDA2MzJ9") {
->    nodes {
->      sequenceNumber
->      digest
->    }
->  }
->}
- -## -## Coin Connection -### -### Coin Connection -#### Get last 3 coins owned by `0x0`. - ->
{
->  address(
->    address: "0x0000000000000000000000000000000000000000000000000000000000000000"
->  ) {
->    coins(last: 3) {
->      nodes {
->        coinBalance
->      }
->      pageInfo {
->        endCursor
->        hasNextPage
->      }
->    }
->  }
->}
- -## -## Coin Metadata -### -### Coin Metadata - ->
query CoinMetadata {
->  coinMetadata(coinType: "0x2::sui::SUI") {
->    decimals
->    name
->    symbol
->    description
->    iconUrl
->    supply
->    hasPublicTransfer
->  }
->}
- -## -## Epoch -### -### Latest Epoch -#### Latest epoch, since epoch omitted - ->
{
->  epoch {
->    protocolConfigs {
->      protocolVersion
->    }
->    epochId
->    referenceGasPrice
->    startTimestamp
->    endTimestamp
->  }
->}
- -### -### Specific Epoch -#### Selecting all fields for epoch 100 - ->
{
->  epoch(id: 100) {
->    protocolConfigs {
->      protocolVersion
->    }
->    epochId
->    referenceGasPrice
->    startTimestamp
->    endTimestamp
->    validatorSet {
->      totalStake
->      pendingActiveValidatorsSize
->      stakingPoolMappingsSize
->      inactivePoolsSize
->      validatorCandidatesSize
->      activeValidators {
->        nodes {
->          name
->          description
->          imageUrl
->          projectUrl
->          exchangeRates {
->            storageRebate
->            bcs
->            hasPublicTransfer
->          }
->          exchangeRatesSize
->          stakingPoolActivationEpoch
->          stakingPoolSuiBalance
->          rewardsPool
->          poolTokenBalance
->          pendingStake
->          pendingTotalSuiWithdraw
->          pendingPoolTokenWithdraw
->          votingPower
->          gasPrice
->          commissionRate
->          nextEpochStake
->          nextEpochGasPrice
->          nextEpochCommissionRate
->          atRisk
->        }
->      }
->    }
->  }
->}
- -### -### With Checkpoint Connection - ->
{
->  epoch {
->    checkpoints {
->      nodes {
->        transactionBlocks(first: 10) {
->          pageInfo {
->            hasNextPage
->            endCursor
->          }
->          edges {
->            cursor
->            node {
->              sender {
->                address
->              }
->              effects {
->                gasEffects {
->                  gasObject {
->                    address
->                  }
->                }
->              }
->              gasInput {
->                gasPrice
->                gasBudget
->              }
->            }
->          }
->        }
->      }
->    }
->  }
->}
- -### -### With Tx Block Connection -#### Fetch the first 20 transactions after tx 231220153 (encoded as a -#### cursor) in epoch 97. - ->
{
->  epoch(id: 97) {
->    transactionBlocks(first: 20, after:"eyJjIjoyNjkzMzc3OCwidCI6MjMxMjIwMTUzLCJ0YyI6ODAxMDg4NH0") {
->      pageInfo {
->        hasNextPage
->        endCursor
->      }
->      edges {
->        cursor
->        node {
->          digest
->          sender {
->            address
->          }
->          effects {
->            gasEffects {
->              gasObject {
->                address
->              }
->            }
->          }
->          gasInput {
->            gasPrice
->            gasBudget
->          }
->        }
->      }
->    }
->  }
->}
- -### -### With Tx Block Connection Latest Epoch - ->
{
->  epoch {
->    transactionBlocks(first: 20, after: "eyJjIjoyNjkzMzMyNCwidCI6MTEwMTYxMDQ4MywidGMiOjI2ODUxMjQ4fQ") {
->      pageInfo {
->        hasNextPage
->        endCursor
->      }
->      edges {
->        cursor
->        node {
->          sender {
->            address
->          }
->          effects {
->            gasEffects {
->              gasObject {
->                address
->              }
->            }
->          }
->          gasInput {
->            gasPrice
->            gasBudget
->          }
->        }
->      }
->    }
->  }
->}
- -## -## Event Connection -### -### Event Connection - ->
{
->  events(
->    filter: {
->      eventType: "0x3164fcf73eb6b41ff3d2129346141bd68469964c2d95a5b1533e8d16e6ea6e13::Market::ChangePriceEvent<0x2::sui::SUI>"
->    }
->  ) {
->    nodes {
->      sendingModule {
->        name
->        package { digest }
->      }
->      type {
->        repr
->      }
->      sender {
->        address
->      }
->      timestamp
->      json
->      bcs
->    }
->  }
->}
- -### -### Filter By Emitting Package Module And Event Type - ->
query ByEmittingPackageModuleAndEventType {
->  events(
->    first: 1
->    after: "eyJ0eCI6Njc2MywiZSI6MCwiYyI6MjI4MDA3NDJ9"
->    filter: {
->      emittingModule: "0x3::sui_system",
->      eventType: "0x3::validator::StakingRequestEvent"
->    }
->  ) {
->    pageInfo {
->      hasNextPage
->      endCursor
->    }
->    nodes {
->      sendingModule {
->        name
->      }
->      type {
->        repr
->      }
->      sender {
->        address
->      }
->      timestamp
->      json
->      bcs
->    }
->  }
->}
- -### -### Filter By Sender - ->
query ByTxSender {
->  events(
->    first: 1
->    filter: {
->      sender: "0xdff57c401e125a7e0e06606380560b459a179aacd08ed396d0162d57dbbdadfb"
->    }
->  ) {
->    pageInfo {
->      hasNextPage
->      endCursor
->    }
->    nodes {
->      sendingModule {
->        name
->      }
->      type {
->        repr
->      }
->      sender {
->        address
->      }
->      timestamp
->      json
->      bcs
->    }
->  }
->}
- -## -## Name Service -### -### Name Service - ->
{
->  resolveSuinsAddress(domain: "example.sui") {
->    address
->  }
->  address(
->    address: "0x0b86be5d779fac217b41d484b8040ad5145dc9ba0cba099d083c6cbda50d983e"
->  ) {
->    address
->    balance(type: "0x2::sui::SUI") {
->      coinType {
->        repr
->      }
->      coinObjectCount
->      totalBalance
->    }
->    defaultSuinsName
->  }
->}
- -## -## Object -### -### Object - ->
{
->  object(
->    address: "0x04e20ddf36af412a4096f9014f4a565af9e812db9a05cc40254846cf6ed0ad91"
->  ) {
->    address
->    version
->    digest
->    storageRebate
->    owner {
->      __typename
->      ... on Shared {
->        initialSharedVersion
->      }
->      __typename
->      ... on Parent {
->        parent {
->          address
->        }
->      }
->      __typename
->      ... on AddressOwner {
->        owner {
->          address
->        }
->      }
->    }
->    previousTransactionBlock {
->      digest
->    }
->  }
->}
- -## -## Object Connection -### -### Filter Object Ids -#### Filter on objectIds - ->
{
->  objects(filter: { objectIds: [
->    "0x4bba2c7b9574129c272bca8f58594eba933af8001257aa6e0821ad716030f149"
->  ]}) {
->    edges {
->      node {
->        storageRebate
->        owner {
->          __typename
->          ... on Shared {
->            initialSharedVersion
->          }
->          __typename
->          ... on Parent {
->            parent {
->              address
->            }
->          }
->          __typename
->          ... on AddressOwner {
->            owner {
->              address
->            }
->          }
->        }
->      }
->    }
->  }
->}
- -### -### Filter On Generic Type - ->
{
->  objects(filter: {type: "0x2::coin::Coin"}) {
->    edges {
->      node {
->        asMoveObject {
->          contents {
->            type { repr }
->          }
->        }
->      }
->    }
->  }
->}
- -### -### Filter On Type - ->
{
->  objects(filter: {type: "0x3::staking_pool::StakedSui"}) {
->    edges {
->      node {
->        asMoveObject {
->          contents {
->            type {
->              repr
->            }
->          }
->        }
->      }
->    }
->  }
->}
- -### -### Filter Owner -#### Filter on owner - ->
{
->  objects(filter: {
->    owner: "0x23b7b0e2badb01581ba9b3ab55587d8d9fdae087e0cfc79f2c72af36f5059439"
->  }) {
->    edges {
->      node {
->        storageRebate
->        owner {
->          __typename
->          ... on Shared {
->            initialSharedVersion
->          }
->          __typename
->          ... on Parent {
->            parent {
->              address
->            }
->          }
->          __typename
->          ... on AddressOwner {
->            owner {
->              address
->            }
->          }
->        }
->      }
->    }
->  }
->}
- -### -### Object Connection - ->
{
->  objects {
->    nodes {
->      version
->      digest
->      storageRebate
->      previousTransactionBlock {
->        digest
->        sender { defaultSuinsName }
->        gasInput {
->          gasPrice
->          gasBudget
->        }
->      }
->    }
->    pageInfo {
->      endCursor
->    }
->  }
->}
- -## -## Owner -### -### Dynamic Field - ->
fragment DynamicFieldValueSelection on DynamicFieldValue {
->  ... on MoveValue {
->    type {
->      repr
->    }
->    data
->    __typename
->  }
->  ... on MoveObject {
->    hasPublicTransfer
->    contents {
->      type {
->        repr
->      }
->      data
->    }
->    __typename
->  }
->}
->
->fragment DynamicFieldNameSelection on MoveValue {
->  type {
->    repr
->  }
->  data
->  bcs
->}
->
->fragment DynamicFieldSelect on DynamicField {
->  name {
->    ...DynamicFieldNameSelection
->  }
->  value {
->    ...DynamicFieldValueSelection
->  }
->}
->
->query DynamicField {
->  object(
->    address: "0xb57fba584a700a5bcb40991e1b2e6bf68b0f3896d767a0da92e69de73de226ac"
->  ) {
->    dynamicField(
->      name: {
->        type: "0x2::kiosk::Listing",
->        bcs: "NLArx1UJguOUYmXgNG8Pv8KbKXLjWtCi6i0Yeq1VhfwA",
->      }
->    ) {
->      ...DynamicFieldSelect
->    }
->  }
->}
- -### -### Dynamic Field Connection - ->
fragment DynamicFieldValueSelection on DynamicFieldValue {
->  ... on MoveValue {
->    type {
->      repr
->    }
->    data
->  }
->  ... on MoveObject {
->    hasPublicTransfer
->    contents {
->      type {
->        repr
->      }
->      data
->    }
->  }
->}
->
->fragment DynamicFieldNameSelection on MoveValue {
->  type {
->    repr
->  }
->  data
->  bcs
->}
->
->fragment DynamicFieldSelect on DynamicField {
->  name {
->    ...DynamicFieldNameSelection
->  }
->  value {
->    ...DynamicFieldValueSelection
->  }
->}
->
->query DynamicFields {
->  object(
->    address: "0xb57fba584a700a5bcb40991e1b2e6bf68b0f3896d767a0da92e69de73de226ac"
->  ) {
->    dynamicFields {
->      pageInfo {
->        hasNextPage
->        endCursor
->      }
->      edges {
->        cursor
->        node {
->          ...DynamicFieldSelect
->        }
->      }
->    }
->  }
->}
- -### -### Dynamic Object Field - ->
fragment DynamicFieldValueSelection on DynamicFieldValue {
->  ... on MoveValue {
->    type {
->      repr
->    }
->    data
->    __typename
->  }
->  ... on MoveObject {
->    hasPublicTransfer
->    contents {
->      type {
->        repr
->      }
->      data
->    }
->    __typename
->  }
->}
->
->fragment DynamicFieldNameSelection on MoveValue {
->  type {
->    repr
->  }
->  data
->  bcs
->}
->
->fragment DynamicFieldSelect on DynamicField {
->  name {
->    ...DynamicFieldNameSelection
->  }
->  value {
->    ...DynamicFieldValueSelection
->  }
->}
->
->query DynamicObjectField {
->  object(
->    address: "0xb57fba584a700a5bcb40991e1b2e6bf68b0f3896d767a0da92e69de73de226ac"
->  ) {
->    dynamicObjectField(
->      name: {type: "0x2::kiosk::Item", bcs: "NLArx1UJguOUYmXgNG8Pv8KbKXLjWtCi6i0Yeq1Vhfw="}
->    ) {
->      ...DynamicFieldSelect
->    }
->  }
->}
- -### -### Owner - ->
{
->  owner(
->    address: "0x931f293ce7f65fd5ebe9542653e1fd92fafa03dda563e13b83be35da8a2eecbe"
->  ) {
->    address
->  }
->}
- -## -## Protocol Configs -### -### Key Value -#### Select the key and value of the protocol configuration - ->
{
->  protocolConfig {
->    configs {
->      key
->      value
->    }
->  }
->}
- -### -### Key Value Feature Flag -#### Select the key and value of the feature flag - ->
{
->  protocolConfig {
->    featureFlags {
->      key
->      value
->    }
->  }
->}
- -### -### Specific Config -#### Select the key and value of the specific protocol configuration, in this case `max_move_identifier_len` - ->
{
->  protocolConfig {
->    config(key: "max_move_identifier_len") {
->      key
->      value
->    }
->  }
->}
- -### -### Specific Feature Flag - ->
{
->  protocolConfig {
->    protocolVersion
->    featureFlag(key: "advance_epoch_start_time_in_safe_mode") {
->      value
->    }
->  }
->}
- -## -## Service Config -### -### Service Config -#### Get the configuration of the running service - ->
{
->  serviceConfig {
->    isEnabled(feature: ANALYTICS)
->    enabledFeatures
->    maxQueryDepth
->    maxQueryNodes
->    maxDbQueryCost
->    defaultPageSize
->    maxPageSize
->    requestTimeoutMs
->    maxQueryPayloadSize
->  }
->}
- -## -## Stake Connection -### -### Stake Connection -#### Get all the staked objects for this address and all the active validators at the epoch when the stake became active - ->
{
->  address(
->    address: "0xc0a5b916d0e406ddde11a29558cd91b29c49e644eef597b7424a622955280e1e"
->  ) {
->    address
->    balance(type: "0x2::sui::SUI") {
->      coinType {
->        repr
->      }
->      totalBalance
->    }
->    stakedSuis {
->      nodes {
->        status
->        principal
->        estimatedReward
->        activatedEpoch {
->          epochId
->          referenceGasPrice
->          validatorSet {
->            activeValidators {
->              nodes {
->                name
->                description
->                exchangeRatesSize
->              }
->            }
->            totalStake
->          }
->        }
->        requestedEpoch {
->          epochId
->        }
->      }
->    }
->  }
->}
- -## -## Sui System State Summary -### -### Sui System State Summary -#### Get the latest sui system state data - ->
{
->  epoch {
->    storageFund {
->      totalObjectStorageRebates
->      nonRefundableBalance
->    }
->    safeMode {
->      enabled
->      gasSummary {
->         computationCost
->         storageCost
->         storageRebate
->         nonRefundableStorageFee
->      }
->    }
->    systemStateVersion
->    systemParameters {
->      durationMs
->      stakeSubsidyStartEpoch
->      minValidatorCount
->      maxValidatorCount
->      minValidatorJoiningStake
->      validatorLowStakeThreshold
->      validatorVeryLowStakeThreshold
->      validatorLowStakeGracePeriod
->    }
->    systemStakeSubsidy {
->      balance
->      distributionCounter
->      currentDistributionAmount
->      periodLength
->      decreaseRate
->
->    }
->  }
->}
- -## -## Transaction Block -### -### Transaction Block -#### Get the data for a TransactionBlock by its digest - ->
{
->  transactionBlock(digest: "HvTjk3ELg8gRofmB1GgrpLHBFeA53QKmUKGEuhuypezg") {
->    sender {
->      address
->    }
->    gasInput {
->      gasSponsor {
->        address
->      }
->      gasPayment {
->        nodes {
->          address
->        }
->      }
->      gasPrice
->      gasBudget
->    }
->    kind {
->      __typename
->    }
->    signatures
->    digest
->    expiration {
->      epochId
->    }
->    effects {
->      timestamp
->    }
->  }
->}
- -### -### Transaction Block Kind - ->
{
->  object(
->    address: "0xd6b9c261ab53d636760a104e4ab5f46c2a3e9cda58bd392488fc4efa6e43728c"
->  ) {
->    previousTransactionBlock {
->      sender {
->        address
->      }
->      kind {
->        __typename
->        ... on ConsensusCommitPrologueTransaction {
->          epoch {
->            epochId
->            referenceGasPrice
->          }
->          round
->          commitTimestamp
->          consensusCommitDigest
->        }
->        ... on ChangeEpochTransaction {
->          computationCharge
->          storageCharge
->          startTimestamp
->          storageRebate
->        }
->        ... on GenesisTransaction {
->          objects {
->            nodes { address }
->          }
->        }
->      }
->    }
->  }
->}
- -## -## Transaction Block Connection -### -### Before After Checkpoint -#### Filter on before_ and after_checkpoint. If both are provided, before must be greater than after - ->
{
->  transactionBlocks(
->    filter: { afterCheckpoint: 10, beforeCheckpoint: 20 }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Changed Object Filter -#### Filter on changedObject - ->
{
->  transactionBlocks(
->    filter: {
->      changedObject: "0x0000000000000000000000000000000000000000000000000000000000000006"
->    }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Input Object Filter -#### Filter on inputObject - ->
{
->  transactionBlocks(
->    filter: {
->      inputObject: "0x0000000000000000000000000000000000000000000000000000000000000006"
->    }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Input Object Sign Addr Filter -#### multiple filters - ->
{
->  transactionBlocks(
->    filter: {
->      inputObject: "0x0000000000000000000000000000000000000000000000000000000000000006"
->      signAddress: "0x0000000000000000000000000000000000000000000000000000000000000000"
->    }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      effects {
->        gasEffects {
->          gasObject {
->            address
->          }
->        }
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Package Filter -#### Filtering on package - ->
{
->  transactionBlocks(filter: { function: "0x3" }) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Package Module Filter -#### Filtering on package and module - ->
{
->  transactionBlocks(
->    filter: {
->      function: "0x3::sui_system"
->    }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Package Module Func Filter -#### Filtering on package, module and function - ->
{
->  transactionBlocks(
->    filter: {
->      function: "0x3::sui_system::request_withdraw_stake"
->    }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Recv Addr Filter -#### Filter on recvAddress - ->
{
->  transactionBlocks(
->    filter: {
->      recvAddress: "0x0000000000000000000000000000000000000000000000000000000000000000"
->    }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Sign Addr Filter -#### Filter on signing address - ->
{
->  transactionBlocks(
->    filter: {
->      signAddress: "0x0000000000000000000000000000000000000000000000000000000000000000"
->    }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Tx Ids Filter -#### Filter on transactionIds - ->
{
->  transactionBlocks(
->    filter: { transactionIds: ["DtQ6v6iJW4wMLgadENPUCEUS5t8AP7qvdG5jX84T1akR"] }
->  ) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### Tx Kind Filter -#### Filter on TransactionKind (only SYSTEM_TX or PROGRAMMABLE_TX) - ->
{
->  transactionBlocks(filter: { kind: SYSTEM_TX }) {
->    nodes {
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->  }
->}
- -### -### With Defaults Ascending -#### Fetch some default amount of transactions, ascending - ->
{
->  transactionBlocks {
->    nodes {
->      digest
->      effects {
->        gasEffects {
->          gasObject {
->            version
->            digest
->          }
->          gasSummary {
->            computationCost
->            storageCost
->            storageRebate
->            nonRefundableStorageFee
->          }
->        }
->        errors
->      }
->      sender {
->        address
->      }
->      gasInput {
->        gasPrice
->        gasBudget
->      }
->    }
->    pageInfo {
->      endCursor
->    }
->  }
->}
- -## -## Transaction Block Effects -### -### Transaction Block Effects - ->
{
->  object(
->    address: "0x0bba1e7d907dc2832edfc3bf4468b6deacd9a2df435a35b17e640e135d2d5ddc"
->  ) {
->    version
->    owner {
->      __typename
->      ... on Shared {
->        initialSharedVersion
->      }
->      __typename
->      ... on Parent {
->        parent {
->          address
->        }
->      }
->      __typename
->      ... on AddressOwner {
->        owner {
->          address
->        }
->      }
->    }
->    previousTransactionBlock {
->      effects {
->        status
->        checkpoint {
->          sequenceNumber
->        }
->        lamportVersion
->        gasEffects {
->          gasSummary {
->            computationCost
->            storageCost
->            storageRebate
->            nonRefundableStorageFee
->          }
->        }
->        balanceChanges {
->          nodes {
->            owner {
->              address
->              balance(type: "0x2::sui::SUI") {
->                totalBalance
->              }
->            }
->            amount
->            coinType {
->              repr
->              signature
->              layout
->            }
->          }
->        }
->        dependencies {
->          nodes {
->            sender {
->              address
->            }
->          }
->        }
->      }
->    }
->  }
->}
- diff --git a/crates/sui-graphql-rpc/src/commands.rs b/crates/sui-graphql-rpc/src/commands.rs index f605efd735946..bda0c2f561fab 100644 --- a/crates/sui-graphql-rpc/src/commands.rs +++ b/crates/sui-graphql-rpc/src/commands.rs @@ -13,17 +13,6 @@ use std::path::PathBuf; version )] pub enum Command { - GenerateDocsExamples, - GenerateSchema { - /// Path to output GraphQL schema to, in SDL format. - #[clap(short, long)] - file: Option, - }, - GenerateExamples { - /// Path to output examples docs. - #[clap(short, long)] - file: Option, - }, StartServer { /// The title to display at the top of the page #[clap(short, long)] diff --git a/crates/sui-graphql-rpc/src/examples.rs b/crates/sui-graphql-rpc/src/examples.rs deleted file mode 100644 index 56f86ca428180..0000000000000 --- a/crates/sui-graphql-rpc/src/examples.rs +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::anyhow; -use markdown_gen::markdown::{AsMarkdown, Markdown}; -use std::io::{BufWriter, Read}; -use std::path::PathBuf; - -#[derive(Debug)] -pub struct ExampleQuery { - pub name: String, - pub contents: String, - pub path: PathBuf, -} - -#[derive(Debug)] -pub struct ExampleQueryGroup { - pub name: String, - pub queries: Vec, - pub _path: PathBuf, -} - -const QUERY_EXT: &str = "graphql"; - -fn regularize_string(s: &str) -> String { - // Replace underscore with space and make every word first letter uppercase - s.replace('_', " ") - .split_whitespace() - .map(|word| { - let mut chars = word.chars(); - match chars.next() { - None => String::new(), - Some(f) => f.to_uppercase().chain(chars).collect(), - } - }) - .collect::>() - .join(" ") -} - -pub fn load_examples() -> anyhow::Result> { - let mut buf: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - buf.push("examples"); - - let mut groups = vec![]; - for entry in std::fs::read_dir(buf).map_err(|e| anyhow::anyhow!(e))? { - let entry = entry.map_err(|e| anyhow::anyhow!(e))?; - let path = entry.path(); - let group_name = path - .file_stem() - .ok_or(anyhow::anyhow!("File stem cannot be read"))? - .to_str() - .ok_or(anyhow::anyhow!("File stem cannot be read"))? - .to_string(); - - let mut group = ExampleQueryGroup { - name: group_name.clone(), - queries: vec![], - _path: path.clone(), - }; - - for file in std::fs::read_dir(path).map_err(|e| anyhow::anyhow!(e))? { - assert!(file.is_ok()); - let file = file.map_err(|e| anyhow::anyhow!(e))?; - assert!(file.path().extension().is_some()); - let ext = file - .path() - .extension() - .ok_or(anyhow!("File extension cannot be read"))? - .to_str() - .ok_or(anyhow!("File extension cannot be read to string"))? - .to_string(); - assert_eq!(ext, QUERY_EXT, "wrong file extension for example"); - - let file_path = file.path(); - let query_name = file_path - .file_stem() - .ok_or(anyhow!("File stem cannot be read"))? - .to_str() - .ok_or(anyhow!("File extension cannot be read to string"))? - .to_string(); - - let mut contents = String::new(); - let mut fp = std::fs::File::open(file_path.clone()).map_err(|e| anyhow!(e))?; - fp.read_to_string(&mut contents).map_err(|e| anyhow!(e))?; - group.queries.push(ExampleQuery { - name: query_name, - contents, - path: file_path, - }); - } - group.queries.sort_by(|x, y| x.name.cmp(&y.name)); - - groups.push(group); - } - - groups.sort_by(|x, y| x.name.cmp(&y.name)); - Ok(groups) -} - -/// This generates a markdown page with all the examples, to be used in the docs site -pub fn generate_examples_for_docs() -> anyhow::Result { - let groups = load_examples()?; - - let mut output = BufWriter::new(Vec::new()); - let mut md = Markdown::new(&mut output); - md.write( - r#"--- -title: Examples -description: Query examples for working with the Sui GraphQL RPC. ---- -"#, - )?; - md.write("This page showcases a number of queries to interact with the network. These examples can also be found in the [repository](https://github.com/MystenLabs/sui/tree/main/crates/sui-graphql-rpc/examples). You can use the [interactive online IDE](https://mainnet.sui.io/rpc/graphql) to run these examples.")?; - for group in groups.iter() { - let group_name = regularize_string(&group.name); - md.write(group_name.heading(2)) - .map_err(|e| anyhow::anyhow!(e))?; - for query in group.queries.iter() { - let name = regularize_string(&query.name); - md.write(name.heading(3)).map_err(|e| anyhow::anyhow!(e))?; - let query = query.contents.lines().collect::>().join("\n"); - let content = format!("```graphql\n{}\n```", query); - md.write(content.as_str()).map_err(|e| anyhow::anyhow!(e))?; - } - } - let bytes = output.into_inner().map_err(|e| anyhow::anyhow!(e))?; - Ok(String::from_utf8(bytes) - .map_err(|e| anyhow::anyhow!(e))? - .replace('\\', "")) -} - -pub fn generate_markdown() -> anyhow::Result { - let groups = load_examples()?; - - let mut output = BufWriter::new(Vec::new()); - let mut md = Markdown::new(&mut output); - - md.write("Sui GraphQL Examples".heading(1)) - .map_err(|e| anyhow!(e))?; - - // TODO: reduce multiple loops - // Generate the table of contents - for (id, group) in groups.iter().enumerate() { - let group_name = regularize_string(&group.name); - let group_name_toc = format!("[{}](#{})", group_name, id); - md.write(group_name_toc.heading(3)) - .map_err(|e| anyhow!(e))?; - - for (inner, query) in group.queries.iter().enumerate() { - let inner_id = inner + 0xFFFF * id; - let inner_name = regularize_string(&query.name); - let inner_name_toc = format!("  [{}](#{})", inner_name, inner_id); - md.write(inner_name_toc.heading(4)) - .map_err(|e| anyhow!(e))?; - } - } - - for (id, group) in groups.iter().enumerate() { - let group_name = regularize_string(&group.name); - - let id_tag = format!("", id); - md.write(id_tag.heading(2)) - .map_err(|e| anyhow::anyhow!(e))?; - md.write(group_name.heading(2)) - .map_err(|e| anyhow::anyhow!(e))?; - for (inner, query) in group.queries.iter().enumerate() { - let inner_id = inner + 0xFFFF * id; - let name = regularize_string(&query.name); - - let id_tag = format!("", inner_id); - md.write(id_tag.heading(3)) - .map_err(|e| anyhow::anyhow!(e))?; - md.write(name.heading(3)).map_err(|e| anyhow::anyhow!(e))?; - - // Extract all lines that start with `#` and use them as headers - let mut headers = vec![]; - let mut query_start = 0; - for (idx, line) in query.contents.lines().enumerate() { - let line = line.trim(); - if line.starts_with('#') { - headers.push(line.trim_start_matches('#')); - } else if line.starts_with('{') { - query_start = idx; - break; - } - } - - // Remove headers from query - let query = query - .contents - .lines() - .skip(query_start) - .collect::>() - .join("\n"); - - let content = format!("
{}
", query); - for header in headers { - md.write(header.heading(4)) - .map_err(|e| anyhow::anyhow!(e))?; - } - md.write(content.quote()).map_err(|e| anyhow::anyhow!(e))?; - } - } - let bytes = output.into_inner().map_err(|e| anyhow::anyhow!(e))?; - Ok(String::from_utf8(bytes) - .map_err(|e| anyhow::anyhow!(e))? - .replace('\\', "")) -} - -#[test] -fn test_generate_markdown() { - use similar::*; - use std::fs::File; - - let mut buf: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - buf.push("docs"); - buf.push("examples.md"); - let mut out_file: File = File::open(buf).expect("Could not open examples.md"); - - // Read the current content of `out_file` - let mut current_content = String::new(); - out_file - .read_to_string(&mut current_content) - .expect("Could not read examples.md"); - let new_content: String = generate_markdown().expect("Generating examples markdown failed"); - - if current_content != new_content { - let mut res = vec![]; - let diff = TextDiff::from_lines(¤t_content, &new_content); - for change in diff.iter_all_changes() { - let sign = match change.tag() { - ChangeTag::Delete => "---", - ChangeTag::Insert => "+++", - ChangeTag::Equal => " ", - }; - res.push(format!("{}{}", sign, change)); - } - panic!("Doc examples have changed. Please run `sui-graphql-rpc generate-examples` to update the docs. Diff: {}", res.join("")); - } -} diff --git a/crates/sui-graphql-rpc/src/lib.rs b/crates/sui-graphql-rpc/src/lib.rs index baea0d2ce2ce8..c2f7cd3f8687b 100644 --- a/crates/sui-graphql-rpc/src/lib.rs +++ b/crates/sui-graphql-rpc/src/lib.rs @@ -8,7 +8,6 @@ pub(crate) mod consistency; pub mod context_data; pub(crate) mod data; mod error; -pub mod examples; pub mod extensions; pub(crate) mod functional_group; mod metrics; diff --git a/crates/sui-graphql-rpc/src/main.rs b/crates/sui-graphql-rpc/src/main.rs index 6e552a09e92e8..349ef0f0f74a4 100644 --- a/crates/sui-graphql-rpc/src/main.rs +++ b/crates/sui-graphql-rpc/src/main.rs @@ -9,7 +9,6 @@ use sui_graphql_rpc::commands::Command; use sui_graphql_rpc::config::{ ConnectionConfig, Ide, ServerConfig, ServiceConfig, TxExecFullNodeConfig, Version, }; -use sui_graphql_rpc::server::builder::export_schema; use sui_graphql_rpc::server::graphiql_server::start_graphiql_server; use tokio_util::sync::CancellationToken; use tokio_util::task::TaskTracker; @@ -38,39 +37,6 @@ static VERSION: Version = Version { async fn main() { let cmd: Command = Command::parse(); match cmd { - Command::GenerateDocsExamples => { - let mut buf: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - // we are looking to put examples content in - // sui/docs/content/references/sui-graphql/examples.mdx - let filename = "docs/content/references/sui-graphql/examples.mdx"; - buf.pop(); - buf.pop(); - buf.push(filename); - let content = sui_graphql_rpc::examples::generate_examples_for_docs() - .expect("Generating examples markdown file for docs failed"); - std::fs::write(buf, content).expect("Writing examples markdown failed"); - println!("Generated the docs example.mdx file and copied it to {filename}."); - } - Command::GenerateSchema { file } => { - let out = export_schema(); - if let Some(file) = file { - println!("Write schema to file: {:?}", file); - std::fs::write(file, &out).unwrap(); - } else { - println!("{}", &out); - } - } - Command::GenerateExamples { file } => { - let new_content: String = sui_graphql_rpc::examples::generate_markdown() - .expect("Generating examples markdown failed"); - let mut buf: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - buf.push("docs"); - buf.push("examples.md"); - let file = file.unwrap_or(buf); - - std::fs::write(file.clone(), new_content).expect("Writing examples markdown failed"); - println!("Written examples to file: {:?}", file); - } Command::StartServer { ide_title, db_url, diff --git a/crates/sui-graphql-rpc/tests/examples_validation_tests.rs b/crates/sui-graphql-rpc/tests/examples_validation_tests.rs index fc2a95d21c90b..205c0e1407b5d 100644 --- a/crates/sui-graphql-rpc/tests/examples_validation_tests.rs +++ b/crates/sui-graphql-rpc/tests/examples_validation_tests.rs @@ -3,105 +3,147 @@ #[cfg(feature = "pg_integration")] mod tests { + use anyhow::{anyhow, Context, Result}; use rand::rngs::StdRng; use rand::SeedableRng; use serial_test::serial; use simulacrum::Simulacrum; use std::cmp::max; + use std::collections::BTreeMap; + use std::fs; use std::path::PathBuf; use std::sync::Arc; use sui_graphql_rpc::config::{ConnectionConfig, Limits}; - use sui_graphql_rpc::examples::{load_examples, ExampleQuery, ExampleQueryGroup}; use sui_graphql_rpc::test_infra::cluster::ExecutorCluster; use sui_graphql_rpc::test_infra::cluster::DEFAULT_INTERNAL_DATA_SOURCE_PORT; use tempfile::tempdir; - fn bad_examples() -> ExampleQueryGroup { - ExampleQueryGroup { - name: "bad_examples".to_string(), - queries: vec![ - ExampleQuery { - name: "multiple_queries".to_string(), + struct Example { + contents: String, + path: Option, + } + + fn good_examples() -> Result> { + let examples = PathBuf::from(&env!("CARGO_MANIFEST_DIR")).join("examples"); + + let mut dirs = vec![examples.clone()]; + let mut queries = BTreeMap::new(); + while let Some(dir) = dirs.pop() { + let entries = + fs::read_dir(&dir).with_context(|| format!("Looking in {}", dir.display()))?; + + for entry in entries { + let entry = entry.with_context(|| format!("Entry in {}", dir.display()))?; + let path = entry.path(); + let typ_ = entry + .file_type() + .with_context(|| format!("Metadata for {}", path.display()))?; + + if typ_.is_dir() { + dirs.push(entry.path()); + continue; + } + + if path.ends_with(".graphql") { + let contents = fs::read_to_string(&path) + .with_context(|| format!("Reading {}", path.display()))?; + + let rel_path = path + .strip_prefix(&examples) + .with_context(|| format!("Generating name from {}", path.display()))? + .with_extension(""); + + let name = rel_path + .to_str() + .ok_or_else(|| anyhow!("Generating name from {}", path.display()))?; + + queries.insert( + name.to_string(), + Example { + contents, + path: Some(path), + }, + ); + } + } + } + + Ok(queries) + } + + fn bad_examples() -> BTreeMap { + BTreeMap::from_iter([ + ( + "multiple_queries".to_string(), + Example { contents: "{ chainIdentifier } { chainIdentifier }".to_string(), - path: PathBuf::from("multiple_queries.graphql"), + path: None, }, - ExampleQuery { - name: "malformed".to_string(), + ), + ( + "malformed".to_string(), + Example { contents: "query { }}".to_string(), - path: PathBuf::from("malformed.graphql"), + path: None, }, - ExampleQuery { - name: "invalid".to_string(), + ), + ( + "invalid".to_string(), + Example { contents: "djewfbfo".to_string(), - path: PathBuf::from("invalid.graphql"), + path: None, }, - ExampleQuery { - name: "empty".to_string(), + ), + ( + "empty".to_string(), + Example { contents: " ".to_string(), - path: PathBuf::from("empty.graphql"), + path: None, }, - ], - _path: PathBuf::from("bad_examples"), - } + ), + ]) } - async fn validate_example_query_group( + async fn test_query( cluster: &ExecutorCluster, - group: &ExampleQueryGroup, + name: &str, + query: &Example, max_nodes: &mut u64, max_output_nodes: &mut u64, max_depth: &mut u64, max_payload: &mut u64, ) -> Vec { - let mut errors = vec![]; - for query in &group.queries { - let resp = cluster - .graphql_client - .execute_to_graphql(query.contents.clone(), true, vec![], vec![]) - .await - .unwrap(); - resp.errors().iter().for_each(|err| { - errors.push(format!( - "Query failed: {}: {} at: {}\nError: {}", - group.name, - query.name, - query.path.display(), - err - )) - }); - if resp.errors().is_empty() { - let usage = resp - .usage() - .expect("Usage fetch should succeed") - .unwrap_or_else(|| panic!("Usage should be present for query: {}", query.name)); - - let nodes = *usage.get("inputNodes").unwrap_or_else(|| { - panic!("Node usage should be present for query: {}", query.name) - }); - let output_nodes = *usage.get("outputNodes").unwrap_or_else(|| { - panic!( - "Output node usage should be present for query: {}", - query.name - ) - }); - let depth = *usage.get("depth").unwrap_or_else(|| { - panic!("Depth usage should be present for query: {}", query.name) - }); - let payload = *usage.get("queryPayload").unwrap_or_else(|| { - panic!("Payload usage should be present for query: {}", query.name) - }); - *max_nodes = max(*max_nodes, nodes); - *max_output_nodes = max(*max_output_nodes, output_nodes); - *max_depth = max(*max_depth, depth); - *max_payload = max(*max_payload, payload); - } + let resp = cluster + .graphql_client + .execute_to_graphql(query.contents.clone(), true, vec![], vec![]) + .await + .unwrap(); + + let errors = resp.errors(); + if errors.is_empty() { + let usage = resp + .usage() + .expect("Usage not found") + .expect("Usage not found"); + *max_nodes = max(*max_nodes, usage["inputNodes"]); + *max_output_nodes = max(*max_output_nodes, usage["outputNodes"]); + *max_depth = max(*max_depth, usage["depth"]); + *max_payload = max(*max_payload, usage["queryPayload"]); + return vec![]; } + errors + .into_iter() + .map(|e| match &query.path { + Some(p) => format!("Query {name:?} at {} failed: {e}", p.display()), + None => format!("Query {name:?} failed: {e}"), + }) + .collect() } #[tokio::test] #[serial] - async fn test_single_all_examples_structure_valid() { + async fn good_examples_within_limits() { let rng = StdRng::from_seed([12; 32]); let data_ingestion_path = tempdir().unwrap().into_path(); let mut sim = Simulacrum::new_with_rng(rng); @@ -119,20 +161,20 @@ mod tests { ) .await; - let groups = load_examples().expect("Could not load examples"); - let mut errors = vec![]; - for group in groups { - let group_errors = validate_example_query_group( - &cluster, - &group, - &mut max_nodes, - &mut max_output_nodes, - &mut max_depth, - &mut max_payload, - ) - .await; - errors.extend(group_errors); + for (name, example) in good_examples().expect("Could not load examples") { + errors.extend( + test_query( + &cluster, + &name, + &example, + &mut max_nodes, + &mut max_output_nodes, + &mut max_depth, + &mut max_payload, + ) + .await, + ); } // Check that our examples can run with our usage limits @@ -167,7 +209,7 @@ mod tests { #[tokio::test] #[serial] - async fn test_bad_examples_fail() { + async fn bad_examples_fail() { let rng = StdRng::from_seed([12; 32]); let data_ingestion_path = tempdir().unwrap().into_path(); let mut sim = Simulacrum::new_with_rng(rng); @@ -185,21 +227,19 @@ mod tests { ) .await; - let bad_examples = bad_examples(); - let errors = validate_example_query_group( - &cluster, - &bad_examples, - &mut max_nodes, - &mut max_output_nodes, - &mut max_depth, - &mut max_payload, - ) - .await; + for (name, example) in bad_examples() { + let errors = test_query( + &cluster, + &name, + &example, + &mut max_nodes, + &mut max_output_nodes, + &mut max_depth, + &mut max_payload, + ) + .await; - assert_eq!( - errors.len(), - bad_examples.queries.len(), - "all examples should fail" - ); + assert!(!errors.is_empty(), "Query {name:?} should have failed"); + } } } From b3d3d7ad680fa993c03ec959046632ec0a32500e Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Mon, 19 Aug 2024 11:41:40 +0100 Subject: [PATCH 171/232] [chore][GraphQL] Declutter schemas (#18288) ## Description Remove `draft_target_schema.graphql` and promote `current_progress_schema.graphql` to be the canonical schema for the service -- move it to the top-level of the `sui-graphql-rpc` crate to make it easier to find. This is to avoid confusion about source of truth for the GraphQL schema. Because the TS SDK references the schema at multiple GraphQL versions, we will need to cherry-pick this change to release branches when it lands. ## Test plan CI ## Stack - #17543 - #17692 - #17693 - #17696 - #18287 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [x] GraphQL: The schema file has been moved from `crates/sui-graphql-rpc/schemas/current_progress_schema.graphql` to `crates/sui-graphql-rpc/schema.graphql`. - [ ] CLI: - [ ] Rust SDK: --- ...progress_schema.graphql => schema.graphql} | 0 .../schema/draft_target_schema.graphql | 1588 ----------------- .../sui-graphql-rpc/tests/snapshot_tests.rs | 7 +- docs/site/docusaurus.config.js | 2 +- .../scripts/update-graphql-schemas.ts | 2 +- 5 files changed, 5 insertions(+), 1594 deletions(-) rename crates/sui-graphql-rpc/{schema/current_progress_schema.graphql => schema.graphql} (100%) delete mode 100644 crates/sui-graphql-rpc/schema/draft_target_schema.graphql diff --git a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql b/crates/sui-graphql-rpc/schema.graphql similarity index 100% rename from crates/sui-graphql-rpc/schema/current_progress_schema.graphql rename to crates/sui-graphql-rpc/schema.graphql diff --git a/crates/sui-graphql-rpc/schema/draft_target_schema.graphql b/crates/sui-graphql-rpc/schema/draft_target_schema.graphql deleted file mode 100644 index 733f13eb57bf7..0000000000000 --- a/crates/sui-graphql-rpc/schema/draft_target_schema.graphql +++ /dev/null @@ -1,1588 +0,0 @@ -# Copyright (c) Mysten Labs, Inc. -# SPDX-License-Identifier: Apache-2.0 - -# GraphQL Schema Draft -# -------------------- -# -# This is a draft design of the schema used by the second iteration of -# the RPC service. Note that some elements may not be complete, and -# others may exist in this schema but may not appear in the production -# design initially, or ever. -# -# The source of truth for the actual schema is accessed by querying -# the GraphQL server for its `__schema`. - -schema { - query: Query - subscription: Subscription - mutation: Mutation -} - -type Query { - # First four bytes of the network's genesis checkpoint digest - # (uniquely identifies the network) - chainIdentifier: String! - - # Range of checkpoints that the RPC has data available for (for data - # that can be tied to a particular checkpoint). - availableRange: AvailableRange! - - # Configuration for this RPC service - serviceConfig: ServiceConfig! - - # Simulate running a transaction to inspect its effects without - # committing to them on-chain. - # - # `txBytes` either a `TransactionData` struct or a `TransactionKind` - # struct, BCS-encoded and then Base64-encoded. The expected - # type is controlled by the presence or absence of `txMeta`: If - # present, `txBytes` is assumed to be a `TransactionKind`, if - # absent, then `TransactionData`. - # - # `txMeta` the data that is missing from a `TransactionKind` to make - # a `TransactionData` (sender address and gas information). All - # its fields are nullable: `sender` defaults to `0x0`, if - # `gasObjects` is not present, or is an empty list, it is - # substituted with a mock Coin object, and `gasPrice` defaults to - # the reference gas price. - # - # `skipChecks` optional flag to disable the usual verification - # checks that prevent access to objects that are owned by - # addresses other than the sender, and calling non-public, - # non-entry functions. Defaults to false. - dryRunTransactionBlock( - txBytes: Base64!, - txMeta: TransactionMetadata, - skipChecks: Boolean, - ): DryRunResult - - owner(address: SuiAddress!): Owner - object(address: SuiAddress!, version: Int): Object - address(address: SuiAddress!): Address - type(type: String!): MoveType! - - # Fetch epoch information by ID (defaults to the latest epoch). - epoch(id: Int): Epoch - - # `protocolVersion` defaults to the latest protocol version. - protocolConfig(protocolVersion: Int): ProtocolConfigs - - # Fetch checkpoint information by sequence number or digest - # (defaults to the latest available checkpoint). - checkpoint(id: CheckpointId): Checkpoint - - # Fetch a transaction block by its transaction digest - transactionBlock(digest: String!): TransactionBlock - - coinMetadata(coinType: String!): CoinMetadata - - checkpoints( - first: Int, - after: String, - last: Int, - before: String, - ): CheckpointConnection! - - coins( - first: Int, - after: String, - last: Int, - before: String, - type: String, - ): CoinConnection! - - transactionBlocks( - first: Int, - after: String, - last: Int, - before: String, - filter: TransactionBlockFilter, - ): TransactionBlockConnection! - - events( - first: Int, - after: String, - last: Int, - before: String, - filter: EventFilter, - ): EventConnection! - - objects( - first: Int, - after: String, - last: Int, - before: String, - filter: ObjectFilter, - ): ObjectConnection! - - resolveSuinsAddress(name: String!): Address - - # NB. Will be moved into a private, explorer-specific extension. - networkMetrics: NetworkMetrics - moveCallMetrics: MoveCallMetrics - - allEpochAddressMetrics( - first: Int, - after: String, - last: Int, - before: String, - ): AddressMetricsConnection! -} - -# NB. Add after MVP has stabilised. -# -# Subscriptions use a "push-pull" system: Subscribers are notified -# when there is new data by being sent the cursor pointing after that -# new data. To actually fetch the data, a call must be made to the -# equivalent Connection API: -# -# e.g. When subscription `subscribe { events(filter: F) }` pushes -# cursor `E`. Then -# -# query { events(before: E, filter: F) } -# -# Will start paginating events up to the new data (multiple calls may -# be required if there are multiple pages of information between the -# start and the latest). If the client has already processed some -# prefix, up to cursor `P`, then they can resume with: -# -# query { events(after: P, before: E, filter: F) } -# -# The API for transactions is similar. -type Subscription { - events(filter: EventFilter): String! - transactions(filter: TransactionBlockFilter): String! -} - -type Mutation { - # Execute a transaction, committing its effects on chain. - # - # `txBytes` is a `TransactionData` struct that has been BCS-encoded - # and then Base64-encoded. - # `signatures` are a list of `flag || signature || pubkey` bytes, - # Base64-encoded. - # - # Waits until the transaction has been finalized on chain to return - # its transaction digest. If the transaction could not be - # finalized, returns the errors that prevented it, instead. - executeTransactionBlock( - txBytes: Base64!, - signatures: [Base64!]!, - ): ExecutionResult -} - -# String containing 32B hex-encoded address, with a leading "0x". -# Leading zeroes can be omitted on input but will always appear in -# outputs (SuiAddress in output is guaranteed to be 66 characters -# long). -scalar SuiAddress - -# String representation of an arbitrary width, possibly signed integer -scalar BigInt - -# String containing Base64-encoded binary data. -scalar Base64 - -# ISO-8601 Date and Time -scalar DateTime - -# Arbitrary JSON data -scalar JSON - -# Scalar representing the contents of a Move Value, corresponding to -# the following recursive type: -# -# type MoveData = -# { Number: BigInt } -# | { Bool: bool } -# | { Address: SuiAddress } -# | { UID: SuiAddress } -# | { ID: SuiAddress } -# | { String: string } -# | { Vector: [MoveData] } -# | { Option: MoveData? } -# | { Struct: [{ name: string, value: MoveData }] } -scalar MoveData - -# The signature of a concrete Move Type (a type with all its type -# parameters instantiated with concrete types, that contains no -# references), corresponding to the following recursive type: -# -# type MoveTypeSignature = -# "address" -# | "bool" -# | "u8" | "u16" | ... | "u256" -# | { vector: MoveTypeSignature } -# | { -# struct: { -# package: string, -# module: string, -# type: string, -# typeParameters: [MoveTypeSignature], -# } -# } -scalar MoveTypeSignature - -# The shape of a concrete Move Type (a type with all its type -# parameters instantiated with concrete types), corresponding to the -# following recursive type: -# -# type MoveTypeLayout = -# "address" -# | "bool" -# | "u8" | "u16" | ... | "u256" -# | { vector: MoveTypeLayout } -# | { -# struct: { -# type: string, -# fields: [{ name: string, layout: MoveTypeLayout }], -# } -# } -scalar MoveTypeLayout - -# The shape of an abstract Move Type (a type that can contain free -# type parameters, and can optionally be taken by reference), -# corresponding to the following recursive type: -# -# type OpenMoveTypeSignature = { -# ref: ("&" | "&mut")?, -# body: OpenMoveTypeSignatureBody, -# } -# -# type OpenMoveTypeSignatureBody = -# "address" -# | "bool" -# | "u8" | "u16" | ... | "u256" -# | { vector: OpenMoveTypeSignatureBody } -# | { -# struct: { -# package: string, -# module: string, -# type: string, -# typeParameters: [OpenMoveTypeSignatureBody]? -# } -# } -# | { typeParameter: number } -scalar OpenMoveTypeSignature - -# The extra data required to turn a `TransactionKind` into a -# `TransactionData` in a dry-run. -input TransactionMetadata { - sender: SuiAddress - gasPrice: Int - gasBudget: Int - gasObjects: [ObjectRef!] - gasSponsor: SuiAddress -} - -# A reference to a particular version of an object. -input ObjectRef { - address: SuiAddress! - version: Int! - digest: String! -} - -# Filter either by the digest, or the sequence number, or neither, to -# get the latest checkpoint. -input CheckpointId { - digest: String - sequenceNumber: Int -} - -input ObjectFilter { - # This field is used to specify the type of objects that should be - # include in the query results. - # - # Objects can be filtered by their type's package, package::module, - # or their fully qualified type name. - # - # Generic types can be queried by either the generic type name, e.g. - # `0x2::coin::Coin`, or by the full type name, such as - # `0x2::coin::Coin<0x2::sui::SUI>`. - type: String - - # Filter for live objects by their current owners. - owner: SuiAddress - - # Filter for live objects by their IDs. - objectIds: [SuiAddress!] - - # Filter for live or potentially historical objects by their ID and version. - objectKeys: [ObjectKey!] - - # Enhancement (post-MVP), compound filters. Compound filters are - # exclusive (must be the only filter set if they are used). - any: [ObjectFilter] - all: [ObjectFilter] - not: ObjectFilter -} - -input ObjectKey { - objectId: SuiAddress! - version: Int! -} - -input EventFilter { - sender: SuiAddress - transactionDigest: String - # Enhancement (post-MVP), requires compound filters to be useful. - afterCheckpoint: Int - beforeCheckpoint: Int - - # Events emitted by a particular module. An event is emitted by a - # particular module if some function in the module is called by a - # PTB and emits an event. - # - # Modules can be filtered by their package, or package::module. - emittingModule: String - - # This field is used to specify the type of event emitted. - # - # Events can be filtered by their type's package, package::module, - # or their fully qualified type name. - # - # Generic types can be queried by either the generic type name, e.g. - # `0x2::coin::Coin`, or by the full type name, such as - # `0x2::coin::Coin<0x2::sui::SUI>`. - eventType: String - - # Enhancement (post-MVP), requires compound filters to be useful. - startTime: DateTime - endTime: DateTime - - # Enhancement (post-MVP), compound filters. Compound filters are - # exclusive (must be the only filter set if they are used). - any: [EventFilter] - all: [EventFilter] - not: EventFilter -} - -input TransactionBlockFilter { - # Filter by the function called. Limited to an individual package, - # package::module, or package::module::function. - function: String - - kind: TransactionBlockKindInput - afterCheckpoint: Int - beforeCheckpoint: Int - - signAddress: SuiAddress - sentAddress: SuiAddress - recvAddress: SuiAddress - paidAddress: SuiAddress - - inputObject: SuiAddress - changedObject: SuiAddress - - transactionIDs: [String!] - - # Enhancement (post-MVP), consistency with EventFilter -- timestamp - # comes from checkpoint timestamp. - startTime: DateTime - endTime: DateTime - - # Enhancement (post-MVP), compound filters. Compound filters are - # exclusive (must be the only filter set if they are used). - any: [TransactionBlockFilter] - all: [TransactionBlockFilter] - not: TransactionBlockFilter -} - -input DynamicFieldFilter { - # Filter the type of dynamic field name. - # - # Names can be filtered by their type's package, package::module, or - # their fully qualified type name. - # - # Generic types can be queried by either the generic type name, e.g. - # `0x2::coin::Coin`, or by the full type name, such as - # `0x2::coin::Coin<0x2::sui::SUI>`. - nameType: String - - # Filter the type of dynamic field value. - # - # Values can be filtered by their type's package, package::module, - # or their fully qualified type name. - # - # Generic types can be queried by either the generic type name, e.g. - # `0x2::coin::Coin`, or by the full type name, such as - # `0x2::coin::Coin<0x2::sui::SUI>`. - valueType: String -} - -type AvailableRange { - first: Checkpoint - last: Checkpoint -} - -type ServiceConfig { - availableVersions: [String!] - enabledFeatures: [Feature!] - isEnabled(feature: Feature!): Boolean! - - maxQueryDepth: Int! - maxQueryNodes: Int! - maxOutputNodes: Int! - defaultPageSize: Int! - maxPageSize: Int! - requestTimeoutMs: Int! - maxQueryPayloadSize: Int! -} - -enum Feature { - ANALYTICS - COINS - DYNAMIC_FIELDS - NAME_SERVICE - SUBSCRIPTIONS - SYSTEM_STATE -} - -interface IOwner { - address: SuiAddress! - - objects( - first: Int, - after: String, - last: Int, - before: String, - # Enhancement (post-MVP) relies on compound filters. - filter: ObjectFilter, - ): MoveObjectConnection! - - balance(type: String!): Balance - balances( - first: Int, - after: String, - last: Int, - before: String, - ): BalanceConnection! - - # `type` defaults to `0x2::sui::SUI`. - coins( - first: Int, - after: String, - last: Int, - before: String, - type: String, - ): CoinConnection! - - stakedSuis( - first: Int, - after: String, - last: Int, - before: String, - ): StakedSuiConnection! - - dynamicField(dynamicFieldName: DynamicFieldName!): DynamicField - dynamicObjectField(dynamicFieldName: DynamicFieldName!): DynamicField - dynamicFields( - first: Int, - after: String, - last: Int, - before: String, - # Enhancement (post-MVP) to filter dynamic fields by type. - filter: DynamicFieldFilter, - ): DynamicFieldConnection! - - defaultSuinsName: String - suinsRegistrations( - first: Int, - after: String, - last: Int, - before: String, - ): SuinsRegistrationConnection! -} - -union ObjectOwner = Immutable | Shared | Parent | AddressOwner - -type Immutable { - # Dummy field - _: Boolean -} - -type Shared { - initialSharedVersion: Int! -} - -type Parent { - # Child objects are an implementation-detail of dynamic fields. Only - # another object can be a parent of a child object (not an address). - parent: Object -} - -type AddressOwner { - # The address that owns an object could be an Address, or an Object. - owner: Owner -} - -interface IObject { - version: Int! - digest: String! - owner: ObjectOwner - - previousTransactionBlock: TransactionBlock - storageRebate: BigInt - - display: [DisplayEntry!] - - # Transaction Blocks that sent objects to this object - receivedTransactionBlocks( - first: Int, - after: String, - last: Int, - before: String, - # Enhancement (post-MVP) relies on compound filters. - filter: TransactionBlockFilter, - ): TransactionBlockConnection! - - bcs: Base64 -} - -interface IMoveObject { - contents: MoveValue -} - -# Returned by Object.owner, where we can't disambiguate between -# Address and Object. -type Owner implements IOwner { - asAddress: Address - asObject: Object -} - -type Address implements IOwner { - transactionBlocks( - first: Int, - after: String, - last: Int, - before: String, - relation: AddressTransactionBlockRelationship, - # Enhancement (post-MVP) relies on compound filters. - filter: TransactionBlockFilter, - ): TransactionBlockConnection! -} - -enum AddressTransactionBlockRelationship { - SIGN # Transactions this address has signed - SENT # Transactions that transferred objects from this address - RECV # Transactions that received objects into this address - PAID # Transactions that were paid for by this address -} - -type Object implements IOwner & IObject { - asMoveObject: MoveObject - asMovePackage: MovePackage -} - -type DisplayEntry { - key: String! - value: String - error: String -} - -type Epoch { - epochId: Int! - protocolConfigs: ProtocolConfigs - referenceGasPrice: BigInt - validatorSet: ValidatorSet - - startTimestamp: DateTime! - endTimestamp: DateTime - - totalCheckpoints: BigInt - totalGasFees: BigInt - totalStakeRewards: BigInt - totalStakeSubsidies: BigInt - fundSize: BigInt - netInflow: BigInt - fundInflow: BigInt - fundOutflow: BigInt - - # SystemState fields - storageFund: StorageFund - safeMode: SafeMode - systemStateVersion: BigInt - systemParameters: SystemParameters - systemStakeSubsidy: StakeSubsidy - - checkpoints( - first: Int, - after: String, - last: Int, - before: String, - ): CheckpointConnection! - - transactionBlocks( - first: Int, - after: String, - last: Int, - before: String, - # Enhancement (post-MVP) relies on compound filters. - filter: TransactionBlockFilter, - ): TransactionBlockConnection! -} - -type ProtocolConfigs { - protocolVersion: Int! - featureFlags: [ProtocolConfigFeatureFlag!]! - configs: [ProtocolConfigAttr!]! - config(key: String!): ProtocolConfigAttr - featureFlag(key: String!): ProtocolConfigFeatureFlag -} - -type ProtocolConfigAttr { - key: String! - value: String! -} - -type ProtocolConfigFeatureFlag { - key: String! - value: Boolean! -} - -type SystemParameters { - durationMs: BigInt - stakeSubsidyStartEpoch: Int - - minValidatorCount: Int - maxValidatorCount: Int - - minValidatorJoiningStake: BigInt - validatorLowStakeThreshold: BigInt - validatorVeryLowStakeThreshold: BigInt - validatorLowStakeGracePeriod: Int -} - -type StakeSubsidy { - balance: BigInt - distributionCounter: Int - currentDistributionAmount: BigInt - periodLength: Int - decreaseRate: Int -} - -type ValidatorSet { - totalStake: BigInt - - activeValidators( - first: Int, - after: String, - last: Int, - before: String - ): ValidatorConnection! - - # Indices into `activeValidators` - pendingRemovals: [Int] - - pendingActiveValidators: MoveObject - pendingActiveValidatorsSize: Int - - stakePoolMappings: MoveObject - stakePoolMappingsSize: Int - - inactivePools: MoveObject - inactivePoolsSize: Int - - validatorCandidates: MoveObject - validatorCandidatesSize: Int -} - -type Validator { - address: Address! - - credentials: ValidatorCredentials - nextEpochCredentials: ValidatorCredentials - - name: String - description: String - imageUrl: String - projectUrl: String - - operationCap: MoveObject - stakingPool: MoveObject - - exchangeRates: MoveObject - exchangeRatesSize: Int - - stakingPoolActivationEpoch: Int - stakingPoolSuiBalance: BigInt - rewardsPool: BigInt - poolTokenBalance: BigInt - pendingStake: BigInt - pendingTotalSuiWithdraw: BigInt - pendingPoolTokenWithdraw: BigInt - - votingPower: Int - stakeUnits: Int - gasPrice: BigInt - commissionRate: Int - nextEpochStake: BigInt - nextEpochGasPrice: BigInt - nextEpochCommissionRate: Int - - # The number of epochs for which this validator has been below the - # low stake threshold. - atRisk: Int - - # The other validators this validator has reported - reportRecords: [SuiAddress!] - - apy: Int -} - -type ValidatorCredentials { - protocolPubKey: Base64 - networkPubKey: Base64 - workerPubKey: Base64 - proofOfPossession: Base64 - - netAddress: String - p2pAddreess: String - primaryAddress: String - workerAddress: String -} - -type StorageFund { - totalObjectStorageRebates: BigInt - nonRefundableBalance: BigInt -} - -type SafeMode { - enabled: Boolean - gasSummary: GasCostSummary -} - -type Checkpoint { - digest: String! - sequenceNumber: Int! - - timestamp: DateTime! - validatorSignatures: Base64 - - # Commitments - previousCheckpointDigest: String - liveObjectSetDigest: String - - networkTotalTransactions: Int - rollingGasSummary: GasCostSummary - - epoch: Epoch - - transactionBlocks( - first: Int, - after: String, - last: Int, - before: String, - # Enhancement (post-MVP) relies on compound filters. - filter: TransactionBlockFilter, - ): TransactionBlockConnection! - - # NB. Will be moved into a private, explorer-specific extension. - addressMetrics: AddressMetrics -} - -type TransactionBlock { - digest: String - - sender: Address - gasInput: GasInput - kind: TransactionBlockKind - signatures: [Base64!] - effects: TransactionBlockEffects - - expiration: Epoch - - bcs: Base64 -} - -enum TransactionBlockKindInput { - PROGRAMMABLE_TX - SYSTEM_TX -} - -union TransactionBlockKind = - ConsensusCommitPrologueTransaction - | GenesisTransaction - | ChangeEpochTransaction - | ProgrammableTransactionBlock - | AuthenticatorStateUpdateTransaction - | RandomnessStateUpdateTransaction - | EndOfEpochTransaction - -type ConsensusCommitPrologueTransaction { - epoch: Epoch! - round: Int! - commitTimestamp: DateTime! - consensusCommitDigest: String -} - -type GenesisTransaction { - objects( - first: Int, - after: String, - last: Int, - before: String, - ): ObjectConnection! -} - -type ChangeEpochTransaction { - epoch: Epoch - protocolVersion: Int! - startTimestamp: DateTime! - - storageCharge: BigInt! - computationCharge: BigInt! - storageRebate: BigInt! - nonRefundableStorageFee: BigInt! - - systemPackages( - first: Int, - after: String, - last: Int, - before: String, - ): MovePackageConnection! -} - -type ProgrammableTransactionBlock { - inputs( - first: Int, - after: String, - last: Int, - before: String, - ): TransactionInputConnection! - - transactions( - first: Int, - after: String, - last: Int, - before: String, - ): ProgrammableTransactionConnection! -} - -union TransactionInput = OwnedOrImmutable | SharedInput | Receiving | Pure - -type OwnedOrImmutable { - address: SuiAddress! - version: Int! - digest: String! - object: Object -} - -type SharedInput { - address: SuiAddress! - initialSharedVersion: Int! - mutable: Boolean! -} - -type Receiving { - address: SuiAddress! - version: Int! - digest: String! - object: Object -} - -type Pure { - bytes: Base64! -} - -union TransactionArgument = GasCoin | Input | Result - -type GasCoin { _: Boolean } -type Input { ix: Int! } -type Result { cmd: Int!, ix: Int } - -union ProgrammableTransaction = - MoveCallTransaction - | TransferObjectsTransaction - | SplitCoinTransaction - | MergeCoinsTransaction - | PublishTransaction - | UpgradeTransaction - | MakeMoveVecTransaction - -type MoveCallTransaction { - package: SuiAddress! - module: String! - functionName: String! - function: MoveFunction - typeArguments: [MoveType!]! - arguments: [TransactionArgument!]! -} - -type TransferObjectsTransaction { - objects: [TransactionArgument!]! - address: TransactionArgument! -} - -type SplitCoinsTransaction { - coin: TransactionArgument! - amounts: [TransactionArgument!]! -} - -type MergeCoinsTransaction { - coin: TransactionArgument! - coins: [TransactionArgument!]! -} - -type PublishTransaction { - modules: [Base64!]! - dependencies: [SuiAddress!]! -} - -type UpgradeTransaction { - modules: [Base64!]! - dependencies: [SuiAddress!]! - currentPackage: SuiAddress! - upgradeTicket: TransactionArgument! -} - -type MakeMoveVecTransaction { - type: MoveType - elements: [TransactionArgument!]! -} - -type TransactionBlockEffects { - transactionBlock: TransactionBlock! - status: ExecutionStatus - - errors: String - dependencies( - first: Int, - after: String, - last: Int, - before: String, - ): TransactionBlockConnection! - - lamportVersion: Int - gasEffects: GasEffects - - unchangedSharedObjects( - first: Int, - after: String, - last: Int, - before: String, - ): UnchangedSharedObjectConnection! - - objectChanges( - first: Int, - after: String, - last: Int, - before: String, - ): ObjectChangeConnection! - - balanceChanges( - first: Int, - after: String, - last: Int, - before: String, - ): BalanceChangeConnection! - - timestamp: DateTime - epoch: Epoch - checkpoint: Checkpoint - - events( - first: Int, - after: String, - last: Int, - before: String, - # Extension (post-MVP) relies on compound filters - filter: EventFilter, - ): EventConnection! - - bcs: Base64 -} - -enum ExecutionStatus { - SUCCESS - FAILURE -} - -type GasInput { - gasSponsor: Address - gasPayment( - first: Int, - after: String, - last: Int, - before: String, - ): ObjectConnection! - - gasPrice: BigInt - gasBudget: BigInt -} - -type GasEffects { - gasObject: Coin - gasSummary: GasCostSummary -} - -type GasCostSummary { - computationCost: BigInt - storageCost: BigInt - storageRebate: BigInt - nonRefundableStorageFee: BigInt -} - -union UnchangedSharedObject = SharedObjectRead | SharedObjectDelete - -type SharedObjectRead { - address: SuiAddress! - version: u64! - digest: String! - object: Object -} - -type SharedObjectDelete { - address: SuiAddress! - version: u64! - - # Whether this transaction intended to use this shared object - # mutably or not. - mutable: Boolean! -} - -type ObjectChange { - address: SuiAddress! - - inputState: Object - outputState: Object - - idCreated: Boolean - idDeleted: Boolean -} - -type BalanceChange { - owner: Owner - coinType: MoveType - amount: BigInt -} - -type Event { - # Module that the event was emitted by - sendingModule: MoveModule - - sender: Address - timestamp: DateTime - - type: MoveType! - bcs: Base64! - data: MoveData! - json: JSON! -} - -type Balance { - coinType: MoveType - coinObjectCount: Int - totalBalance: BigInt -} - -type Coin implements IOwner & IObject { - coinBalance: BigInt -} - -type StakedSui implements IOwner & IObject { - stakeStatus: StakeStatus! - requestEpoch: Epoch - activeEpoch: Epoch - principal: BigInt - - # Only available if status is `ACTIVE`. - estimatedReward: BigInt -} - -enum StakeStatus { - PENDING - ACTIVE - UNSTAKED -} - -type CoinMetadata implements IOwner & IObject { - decimals: Int - name: String - symbol: String - description: String - iconUrl: String - supply: BigInt -} - -input DynamicFieldName { - type: String! - bcs: Base64! -} - -type DynamicField { - name: MoveValue - value: DynamicFieldValue -} - -union DynamicFieldValue = MoveObject | MoveValue - -type MoveObject implements IOwner & IObject & IMoveObject { - asCoin: Coin - asStakedSui: StakedSui - asCoinMetadata: CoinMetadata - asSuinsRegistration: SuinsRegistration -} - -type MovePackage implements IOwner & IObject { - module(name: String!): MoveModule - modules( - first: Int, - after: String, - last: Int, - before: String, - ): MoveModuleConnection! - - linkage: [Linkage!] - typeOrigins: [TypeOrigin!] - - moduleBcs: Base64 -} - -type Linkage { - originalId: SuiAddress! - upgradedId: SuiAddress! - version: Int! -} - -type TypeOrigin { - module: String! - struct: String! - definingId: SuiAddress! -} - -enum MoveAbility { - COPY - DROP - STORE - KEY -} - -enum MoveVisibility { - PUBLIC - PRIVATE - FRIEND -} - -type MoveStructTypeParameter { - constraints: [MoveAbility!]! - isPhantom: Boolean! -} - -type MoveFunctionTypeParameter { - constraints: [MoveAbility!]! -} - -type MoveModule { - package: SuiAddress! - name: String! - - fileFormatVersion: Int! - - friends( - first: Int, - after: String, - last: Int, - before: String - ): MoveModuleConnection! - - struct(name: String!): MoveStruct - structs( - first: Int, - after: String, - last: Int, - before: String, - ): MoveStructConnection! - - function(name: String!): MoveFunction - functions( - first: Int, - after: String, - last: Int, - before: String, - ): MoveFunctionConnection! - - bytes: Base64 - disassembly: String -} - -type MoveStruct { - module: MoveModule! - name: String! - abilities: [MoveAbility!] - typeParameters: [MoveStructTypeParameter!] - fields: [MoveField!] -} - -type MoveField { - name: String! - type: OpenMoveType -} - -type MoveFunction { - module: MoveModule! - name: String! - - visibility: MoveVisibility - isEntry: Boolean - - typeParameters: [MoveFunctionTypeParameter!] - parameters: [OpenMoveType!] - return: [OpenMoveType!] -} - -type MoveValue { - type: MoveType! - data: MoveData! - json: JSON! - - bcs: Base64! -} - -# Represents concrete types (no type parameters, no references) -type MoveType { - # Flat representation of the type signature, as a displayable string. - repr: String! - # Structured representation of the type signature. - signature: MoveTypeSignature! - # Structured representation of the "shape" of values that match this type. - layout: MoveTypeLayout! - # The abilities this concrete type has. - abilities: [MoveAbility!]! -} - -# Represents types that could contain references or free type -# parameters. Such types can appear as function parameters, or fields -# in structs. -type OpenMoveType { - # Flat representation of the type signature, as a displayable string. - repr: String! - # Structured representation of the type signature. - signature: OpenMoveTypeSignature! -} - -# Metrics (omitted for brevity) -type NetworkMetrics -type MoveCallMetrics -type AddressMetrics - -# Execution - -# Either TransactionBlockEffects on success, or error on failure. -type ExecutionResult { - effects: TransactionBlockEffects - errors: [String!] -} - -type DryRunResult { - transaction: TransactionBlock - error: String - results: [DryRunEffect!] -} - -type DryRunEffect { - # Changes made to arguments that were mutably borrowed by this - # transaction - mutatedReferences: [DryRunMutation!] - - # Results of this transaction - returnValues: [DryRunReturn!] -} - -type DryRunMutation { - input: TransactionArgument - type: MoveType - bcs: Base64 -} - -type DryRunReturn { - type: MoveType - bcs: Base64 -} - -# Connections - -# Pagination -type PageInfo { - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - endCursor: String -} - -# Checkpoints -type CheckpointConnection { - edges: [CheckpointEdge!]! - nodes: [Checkpoint!]! - pageInfo: PageInfo! -} - -type CheckpointEdge { - cursor: String - node: Checkpoint! -} - -# Balance -type BalanceConnection { - edges: [BalanceEdge!]! - nodes: [Balance!]! - pageInfo: PageInfo! -} - -type BalanceEdge { - cursor: String - node: Balance! -} - -# Coin -type CoinConnection { - edges: [CoinEdge!]! - nodes: [Coin!]! - pageInfo: PageInfo! -} - -type CoinEdge { - cursor: String - node: Coin! -} - -# DynamicField -type DynamicFieldConnection { - edges: [DynamicFieldEdge!]! - nodes: [DynamicField!]! - pageInfo: PageInfo! -} - -type DynamicFieldEdge { - cursor: String - node: DynamicField! -} - -# Object -type ObjectConnection { - edges: [ObjectEdge!]! - nodes: [Object!]! - pageInfo: PageInfo! -} - -type ObjectEdge { - cursor: String - node: Object! -} - -# MoveObject -type MoveObjectConnection { - edges: [MoveObjectEdge!]! - nodes: [MoveObject!]! - pageInfo: PageInfo! -} - -type MoveObjectEdge { - cursor: String - node: MoveObject! -} - -# MovePackage -type MovePackageConnection { - edges: [MovePackageEdge!]! - nodes: [MovePackage!]! - pageInfo: PageInfo! -} - -type MovePackageEdge { - cursor: String - node: MovePackage! -} - -# Event -type EventConnection { - edges: [EventEdge!]! - nodes: [Event!]! - pageInfo: PageInfo! -} - -type EventEdge { - cursor: String - node: Event! -} - -# MoveFunction -type MoveFunctionConnection { - edges: [MoveFunctionEdge!]! - nodes: [MoveFunction!]! - pageInfo: PageInfo! -} - -type MoveFunctionEdge { - cursor: String - node: MoveFunction! -} - -# MoveModuleConnection -type MoveModuleConnection { - edges: [MoveModuleEdge] - nodes: [MoveModule] - pageInfo: PageInfo! -} - -type MoveModuleEdge { - cursor: String - node: MoveModule -} - -# MoveStructConnection -type MoveStructConnection { - edges: [MoveStructEdge!]! - nodes: [MoveStruct!]! - pageInfo: PageInfo! -} - -type MoveStructEdge { - cursor: String - node: MoveStruct! -} - -# TransactionBlockConnection -type TransactionBlockConnection { - totalTransactionBlocks: Int - edges: [TransactionBlockEdge!]! - nodes: [TransactionBlock!]! - pageInfo: PageInfo! -} - -type TransactionBlockEdge { - cursor: String - node: TransactionBlock! -} - -# TransactionInputConnection -type TransactionInputConnection { - edges: [TransactionInputEdge!]! - nodes: [TransactionInput!]! - pageInfo: PageInfo! -} - -type TransactionInputEdge { - cursor: String - node: TransactionInput! -} - -# ProgrammableTransactionConnection -type ProgrammableTransactionConnection { - edges: [ProgrammableTransactionEdge!]! - nodes: [ProgrammableTransaction!]! - pageInfo: PageInfo! -} - -type ProgrammableTransactionEdge { - cursor: String - node: ProgrammableTransaction! -} - -# UnchangedSharedObjectConnection - -type UnchangedSharedObjectConnection { - edges: [UnchangedSharedObjectEdge!]! - nodes: [UnchangedSharedObject!]! - pageInfo: PageInfo! -} - -type UnchangedSharedObjectEdge { - cursor: String - node: UnchangedSharedObject! -} - -# ObjectChangeConnection -type ObjectChangeConnection { - edges: [ObjectChangeEdge!]! - nodes: [ObjectChange!]! - pageInfo: PageInfo! -} - -type ObjectChangeEdge { - cursor: String - node: ObjectChange -} - -# BalanceChangeConnection -type BalanceChangeConnection { - edges: [BalanceChangeEdge!]! - nodes: [BalanceChange!]! - pageInfo: PageInfo! -} - -type BalanceChangeEdge { - cursor: String - node: BalanceChange -} - -# MoveModuleConnection -type MoveModuleConnection { - edges: [MoveModuleEdge!]! - nodes: [MoveModule!]! - pageInfo: PageInfo! -} - -type MoveModuleEdge { - cursor: String - node: MoveModule! -} - -# SuinsRegistrationConnection -type SuinsRegistrationConnection { - edges: [SuinsRegistrationEdge!]! - nodes: [SuinsRegistration!]! - pageInfo: PageInfo! -} - -type SuinsRegistrationEdge { - cursor: String - node: SuinsRegistration -} - -type SuinsRegistration { - """ - Domain name of the SuinsRegistration object - """ - domain: String! - """ - Convert the SuinsRegistration object into a Move object - """ - asMoveObject: MoveObject! -} - -# AddressMetricsConnection -type AddressMetricsConnection { - edges: [AddressMetricEdge!]! - nodes: [AddressMetric!]! - pageInfo: PageInfo! -} - -type AddressMetricEdge { - cursor: String - node: AddressMetrics! -} - -# StakedSuiConnection -type StakedSuiConnection { - edges: [StakedSuiEdge!]! - nodes: [StakedSui!]! - pageInfo: PageInfo! -} - -type StakedSuiEdge { - cursor: String - node: StakedSui! -} - -# ValidatorConnection -type ValidatorConnection { - edges: [ValidatorEdge!]! - nodes: [Validator!]! - pageInfo: PageInfo! -} - -type ValidatorEdge { - cursor: String - node: Validator! -} diff --git a/crates/sui-graphql-rpc/tests/snapshot_tests.rs b/crates/sui-graphql-rpc/tests/snapshot_tests.rs index 30a66934b5deb..dcefef844375d 100644 --- a/crates/sui-graphql-rpc/tests/snapshot_tests.rs +++ b/crates/sui-graphql-rpc/tests/snapshot_tests.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use insta::assert_snapshot; -use std::fs::write; +use std::fs; use std::path::PathBuf; use sui_graphql_rpc::server::builder::export_schema; @@ -11,9 +11,8 @@ fn test_schema_sdl_export() { let sdl = export_schema(); // update the current schema file - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.extend(["schema", "current_progress_schema.graphql"]); - write(path, &sdl).unwrap(); + let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("schema.graphql"); + fs::write(path, &sdl).unwrap(); assert_snapshot!(sdl); } diff --git a/docs/site/docusaurus.config.js b/docs/site/docusaurus.config.js index e784d38744f15..5760ff48882b1 100644 --- a/docs/site/docusaurus.config.js +++ b/docs/site/docusaurus.config.js @@ -62,7 +62,7 @@ const config = { "@graphql-markdown/docusaurus", { schema: - "../../crates/sui-graphql-rpc/schema/current_progress_schema.graphql", + "../../crates/sui-graphql-rpc/schema.graphql", rootPath: "../content", // docs will be generated under rootPath/baseURL baseURL: "references/sui-api/sui-graphql/reference", loaders: { diff --git a/sdk/typescript/scripts/update-graphql-schemas.ts b/sdk/typescript/scripts/update-graphql-schemas.ts index a129e7dfe1024..4a3843ede5cbc 100644 --- a/sdk/typescript/scripts/update-graphql-schemas.ts +++ b/sdk/typescript/scripts/update-graphql-schemas.ts @@ -29,7 +29,7 @@ const result = execSync(`git branch --remote --list "origin/releases/sui-graphql minor, patch, branch, - schema: `https://raw.githubusercontent.com/MystenLabs/sui/${branch}/crates/sui-graphql-rpc/schema/current_progress_schema.graphql`, + schema: `https://raw.githubusercontent.com/MystenLabs/sui/${branch}/crates/sui-graphql-rpc/schema.graphql`, } : null; }) From d068f8f21301767915cd2a2e0fc3598ced0ea63f Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Mon, 19 Aug 2024 11:52:52 +0100 Subject: [PATCH 172/232] [GraphQL] `generate-config` sub-command (#18336) ## Description Add a command for generating a config TOML file for the GraphQL service with all its parameters set to their default values. (We used to have a similar command for the YAML file which we weren't using, but we still use the TOML file). ## Test plan ``` cargo run --bin sui-graphql-rpc -- generate-config /tmp/config.toml ``` ## Stack - #17543 - #17692 - #17693 - #17696 - #18287 - #18288 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [x] GraphQL: New sub-command for `sui-graphql-rpc`, `generate-config` for creating a TOML config with all default values set. - [ ] CLI: - [ ] Rust SDK: --- crates/sui-graphql-rpc/src/commands.rs | 7 +++++++ crates/sui-graphql-rpc/src/main.rs | 13 +++++++++++++ .../sui-graphql-rpc/src/server/graphiql_server.rs | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/crates/sui-graphql-rpc/src/commands.rs b/crates/sui-graphql-rpc/src/commands.rs index bda0c2f561fab..e5166def39f50 100644 --- a/crates/sui-graphql-rpc/src/commands.rs +++ b/crates/sui-graphql-rpc/src/commands.rs @@ -13,6 +13,13 @@ use std::path::PathBuf; version )] pub enum Command { + /// Output a TOML config (suitable for passing into the --config parameter of the start-server + /// command) with all values set to their defaults. + GenerateConfig { + /// Optional path to a file to output to. Prints to stdout if none is provided. + output: Option, + }, + StartServer { /// The title to display at the top of the page #[clap(short, long)] diff --git a/crates/sui-graphql-rpc/src/main.rs b/crates/sui-graphql-rpc/src/main.rs index 349ef0f0f74a4..cedc55b39e72a 100644 --- a/crates/sui-graphql-rpc/src/main.rs +++ b/crates/sui-graphql-rpc/src/main.rs @@ -37,6 +37,19 @@ static VERSION: Version = Version { async fn main() { let cmd: Command = Command::parse(); match cmd { + Command::GenerateConfig { output } => { + let config = ServiceConfig::default(); + let toml = toml::to_string_pretty(&config).expect("Failed to serialize configuration"); + + if let Some(path) = output { + fs::write(&path, toml).unwrap_or_else(|e| { + panic!("Failed to write configuration to {}: {e}", path.display()) + }); + } else { + println!("{}", toml); + } + } + Command::StartServer { ide_title, db_url, diff --git a/crates/sui-graphql-rpc/src/server/graphiql_server.rs b/crates/sui-graphql-rpc/src/server/graphiql_server.rs index d5c2f329ecf7f..7a809c01d85be 100644 --- a/crates/sui-graphql-rpc/src/server/graphiql_server.rs +++ b/crates/sui-graphql-rpc/src/server/graphiql_server.rs @@ -31,7 +31,7 @@ pub async fn start_graphiql_server( version: &Version, cancellation_token: CancellationToken, ) -> Result<(), Error> { - info!("Starting server with config: {:?}", server_config); + info!("Starting server with config: {:#?}", server_config); info!("Server version: {}", version); start_graphiql_server_impl( ServerBuilder::from_config(server_config, version, cancellation_token).await?, From 7802581c5ace6150c5bb59e501854dc9cb2b423a Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Mon, 19 Aug 2024 11:53:22 +0100 Subject: [PATCH 173/232] [sui-tool] dump-packages uses GraphQL (#18337) ## Description Replace the original implementation of the dump-packages command (which requires access to an indexer database) with an implementation that reads from a GraphQL service. The former is not readily accessible, but the latter should be. The new tool is also able to run incrementally: Fetching only packages created before a certain checkpoint, or pick up where a previous invocation took off to fetch new packages that were introduced since. ## Test plan Ran a test invocation, on our experimental read replica. With a max page size of 200, I was able to fetch 17000 packages (all the packages at the time the read replica was created) in 3 minutes. ## Stack - #17543 - #17692 - #17693 - #17696 - #18287 - #18288 - #18336 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: --- Cargo.lock | 182 +++++++++++++++++-- Cargo.toml | 4 + crates/sui-package-dump/Cargo.toml | 22 +++ crates/sui-package-dump/build.rs | 10 + crates/sui-package-dump/src/client.rs | 39 ++++ crates/sui-package-dump/src/lib.rs | 251 ++++++++++++++++++++++++++ crates/sui-package-dump/src/query.rs | 105 +++++++++++ crates/sui-tool/Cargo.toml | 3 +- crates/sui-tool/src/commands.rs | 26 ++- crates/sui-tool/src/lib.rs | 1 - crates/sui-tool/src/pkg_dump.rs | 122 ------------- 11 files changed, 618 insertions(+), 147 deletions(-) create mode 100644 crates/sui-package-dump/Cargo.toml create mode 100644 crates/sui-package-dump/build.rs create mode 100644 crates/sui-package-dump/src/client.rs create mode 100644 crates/sui-package-dump/src/lib.rs create mode 100644 crates/sui-package-dump/src/query.rs delete mode 100644 crates/sui-tool/src/pkg_dump.rs diff --git a/Cargo.lock b/Cargo.lock index 4449deae1a1d5..37af0287befea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2947,6 +2947,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "counter" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d458e66999348f56fd3ffcfbb7f7951542075ca8359687c703de6500c1ddccd" +dependencies = [ + "num-traits", +] + [[package]] name = "cpp_demangle" version = "0.4.0" @@ -3288,6 +3297,62 @@ dependencies = [ "syn 1.0.107", ] +[[package]] +name = "cynic" +version = "3.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c02b53607e3f21c374f024c2cfc2154e554905bba478e8e09409f10ce3726" +dependencies = [ + "cynic-proc-macros", + "ref-cast", + "reqwest 0.12.5", + "serde", + "serde_json", + "static_assertions", + "thiserror", +] + +[[package]] +name = "cynic-codegen" +version = "3.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c0ec86f960a00ce087e96ff6f073f6ff28b6876d69ce8caa06c03fb4143981c" +dependencies = [ + "counter", + "cynic-parser", + "darling 0.20.3", + "once_cell", + "ouroboros 0.18.4", + "proc-macro2 1.0.78", + "quote 1.0.35", + "strsim 0.10.0", + "syn 2.0.48", + "thiserror", +] + +[[package]] +name = "cynic-parser" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718f6cd8c54ae5249fd42b0c86639df0100b8a86eea2e5f1b915cde2e1481453" +dependencies = [ + "indexmap 2.2.6", + "lalrpop-util", + "logos", +] + +[[package]] +name = "cynic-proc-macros" +version = "3.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a69ecdf4aa110fed1c0c8de290bc8ccb2835388733cf2f418f0abdf6ff3899" +dependencies = [ + "cynic-codegen", + "darling 0.20.3", + "quote 1.0.35", + "syn 2.0.48", +] + [[package]] name = "darling" version = "0.13.4" @@ -4496,7 +4561,7 @@ dependencies = [ "tokio", "tracing", "walkdir", - "yansi", + "yansi 0.5.1", ] [[package]] @@ -6630,6 +6695,39 @@ dependencies = [ "serde", ] +[[package]] +name = "logos" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1ceb190eb9bdeecdd8f1ad6a71d6d632a50905948771718741b5461fb01e13" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-codegen" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90be66cb7bd40cb5cc2e9cfaf2d1133b04a3d93b72344267715010a466e0915a" +dependencies = [ + "beef", + "fnv", + "lazy_static", + "proc-macro2 1.0.78", + "quote 1.0.35", + "regex-syntax 0.8.2", + "syn 2.0.48", +] + +[[package]] +name = "logos-derive" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45154231e8e96586b39494029e58f12f8ffcb5ecf80333a603a13aa205ea8cbd" +dependencies = [ + "logos-codegen", +] + [[package]] name = "lru" version = "0.7.8" @@ -7308,7 +7406,7 @@ dependencies = [ "move-ir-to-bytecode-syntax", "move-ir-types", "move-symbol-pool", - "ouroboros", + "ouroboros 0.17.2", ] [[package]] @@ -8956,7 +9054,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2ba07320d39dfea882faa70554b4bd342a5f273ed59ba7c1c6b4c840492c954" dependencies = [ "aliasable", - "ouroboros_macro", + "ouroboros_macro 0.17.2", + "static_assertions", +] + +[[package]] +name = "ouroboros" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "944fa20996a25aded6b4795c6d63f10014a7a83f8be9828a11860b08c5fc4a67" +dependencies = [ + "aliasable", + "ouroboros_macro 0.18.4", "static_assertions", ] @@ -8973,6 +9082,20 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "ouroboros_macro" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd" +dependencies = [ + "heck 0.4.1", + "itertools 0.12.0", + "proc-macro2 1.0.78", + "proc-macro2-diagnostics", + "quote 1.0.35", + "syn 2.0.48", +] + [[package]] name = "output_vt100" version = "0.1.3" @@ -9698,7 +9821,7 @@ dependencies = [ "ctor", "diff", "output_vt100", - "yansi", + "yansi 0.5.1", ] [[package]] @@ -9818,6 +9941,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2 1.0.78", + "quote 1.0.35", + "syn 2.0.48", + "version_check", + "yansi 1.0.1", +] + [[package]] name = "prometheus" version = "0.13.3" @@ -10378,22 +10514,22 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.14" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c78fb8c9293bcd48ef6fce7b4ca950ceaf21210de6e105a883ee280c0f7b9ed" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.14" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f9c0c92af03644e4806106281fe2e068ac5bc0ae74a707266d06ea27bccee5f" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2 1.0.78", "quote 1.0.35", - "syn 1.0.107", + "syn 2.0.48", ] [[package]] @@ -14036,6 +14172,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "sui-package-dump" +version = "1.32.0" +dependencies = [ + "anyhow", + "bcs", + "cynic", + "cynic-codegen", + "fastcrypto", + "move-core-types", + "reqwest 0.12.5", + "serde", + "serde_json", + "sui-types", + "tracing", +] + [[package]] name = "sui-package-management" version = "1.32.0" @@ -14733,7 +14886,6 @@ dependencies = [ "clap", "colored", "comfy-table", - "diesel", "eyre", "fastcrypto", "futures", @@ -14754,8 +14906,8 @@ dependencies = [ "sui-archival", "sui-config", "sui-core", - "sui-indexer", "sui-network", + "sui-package-dump", "sui-protocol-config", "sui-replay", "sui-sdk 1.32.0", @@ -16328,7 +16480,7 @@ dependencies = [ "itertools 0.10.5", "msim", "once_cell", - "ouroboros", + "ouroboros 0.17.2", "proc-macro2 1.0.78", "prometheus", "quote 1.0.35", @@ -17275,6 +17427,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yasna" version = "0.5.2" diff --git a/Cargo.toml b/Cargo.toml index 78b95b57b41e3..3b3d1836ba1c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -130,6 +130,7 @@ members = [ "crates/sui-open-rpc", "crates/sui-open-rpc-macros", "crates/sui-oracle", + "crates/sui-package-dump", "crates/sui-package-management", "crates/sui-package-resolver", "crates/sui-proc-macros", @@ -305,6 +306,8 @@ criterion = { version = "0.5.0", features = [ ] } crossterm = "0.25.0" csv = "1.2.1" +cynic = { version = "3.7.3", features = ["http-reqwest"] } +cynic-codegen = "= 3.7.3" dashmap = "5.5.3" # datatest-stable = "0.1.2" datatest-stable = { git = "https://github.com/nextest-rs/datatest-stable.git", rev = "72db7f6d1bbe36a5407e96b9488a581f763e106f" } @@ -628,6 +631,7 @@ sui-network = { path = "crates/sui-network" } sui-node = { path = "crates/sui-node" } sui-open-rpc = { path = "crates/sui-open-rpc" } sui-open-rpc-macros = { path = "crates/sui-open-rpc-macros" } +sui-package-dump = { path = "crates/sui-package-dump" } sui-package-management = { path = "crates/sui-package-management" } sui-package-resolver = { path = "crates/sui-package-resolver" } sui-proc-macros = { path = "crates/sui-proc-macros" } diff --git a/crates/sui-package-dump/Cargo.toml b/crates/sui-package-dump/Cargo.toml new file mode 100644 index 0000000000000..92632519a9877 --- /dev/null +++ b/crates/sui-package-dump/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "sui-package-dump" +version.workspace = true +authors = ["Mysten Labs Result { + Ok(Self { + inner: reqwest::Client::builder() + .user_agent(concat!("sui-package-dump/", env!("CARGO_PKG_VERSION"))) + .build() + .context("Failed to create GraphQL client")?, + url: url.into_url().context("Invalid RPC URL")?, + }) + } + + pub(crate) async fn query(&self, query: Operation) -> Result + where + V: Serialize, + Q: DeserializeOwned + QueryBuilder + 'static, + { + self.inner + .post(self.url.clone()) + .run_graphql(query) + .await + .context("Failed to send GraphQL query")? + .data + .ok_or_else(|| anyhow!("Empty response to query")) + } +} diff --git a/crates/sui-package-dump/src/lib.rs b/crates/sui-package-dump/src/lib.rs new file mode 100644 index 0000000000000..8438a8bec7f88 --- /dev/null +++ b/crates/sui-package-dump/src/lib.rs @@ -0,0 +1,251 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::{ + collections::BTreeMap, + fs, + path::{Path, PathBuf}, +}; + +use anyhow::{bail, ensure, Context, Result}; +use client::Client; +use fastcrypto::encoding::{Base64, Encoding}; +use query::{limits, packages, SuiAddress, UInt53}; +use sui_types::object::Object; +use tracing::info; + +mod client; +mod query; + +/// Ensure all packages created before `before_checkpoint` are written to the `output_dir`ectory, +/// from the GraphQL service at `rpc_url`. +/// +/// `output_dir` can be a path to a non-existent directory, in which case it will be created, or an +/// existing empty directory (in which case it will be filled), or an existing directory that has +/// been written to in the past (in which case this invocation will pick back up from where the +/// previous invocation left off). +pub async fn dump( + rpc_url: String, + output_dir: PathBuf, + before_checkpoint: Option, +) -> Result<()> { + ensure_output_directory(&output_dir)?; + + let client = Client::new(rpc_url)?; + let after_checkpoint = read_last_checkpoint(&output_dir)?; + let limit = max_page_size(&client).await?; + let (last_checkpoint, packages) = + fetch_packages(&client, limit, after_checkpoint, before_checkpoint).await?; + + for package in &packages { + let SuiAddress(address) = &package.address; + dump_package(&output_dir, package) + .with_context(|| format!("Failed to dump package {address}"))?; + } + + if let Some(last_checkpoint) = last_checkpoint { + write_last_checkpoint(&output_dir, last_checkpoint)?; + } + + Ok(()) +} + +/// Ensure the output directory exists, either because it already exists as a writable directory, or +/// by creating a new directory. +fn ensure_output_directory(path: impl Into) -> Result<()> { + let path: PathBuf = path.into(); + if !path.exists() { + fs::create_dir_all(&path).context("Making output directory")?; + return Ok(()); + } + + ensure!( + path.is_dir(), + "Output path is not a directory: {}", + path.display() + ); + + let metadata = fs::metadata(&path).context("Getting metadata for output path")?; + + ensure!( + !metadata.permissions().readonly(), + "Output directory is not writable: {}", + path.display() + ); + + Ok(()) +} + +/// Load the last checkpoint that was loaded by a previous run of the tool, if there is a previous +/// run. +fn read_last_checkpoint(output: &Path) -> Result> { + let path = output.join("last-checkpoint"); + if !path.exists() { + return Ok(None); + } + + let content = fs::read_to_string(&path).context("Failed to read last checkpoint")?; + let checkpoint: u64 = + serde_json::from_str(&content).context("Failed to parse last checkpoint")?; + + info!("Resuming download after checkpoint {checkpoint}"); + + Ok(Some(checkpoint)) +} + +/// Write the max checkpoint that we have seen a package from back to the output directory. +fn write_last_checkpoint(output: &Path, checkpoint: u64) -> Result<()> { + let path = output.join("last-checkpoint"); + let content = + serde_json::to_string(&checkpoint).context("Failed to serialize last checkpoint")?; + + fs::write(path, content).context("Failed to write last checkpoint")?; + Ok(()) +} + +/// Read the max page size supported by the GraphQL service. +async fn max_page_size(client: &Client) -> Result { + Ok(client + .query(limits::build()) + .await + .context("Failed to fetch max page size")? + .service_config + .max_page_size) +} + +/// Read all the packages between `after_checkpoint` and `before_checkpoint`, in batches of +/// `page_size` from the `client` connected to a GraphQL service. +/// +/// If `after_checkpoint` is not provided, packages will be read from genesis. If +/// `before_checkpoint` is not provided, packages will be read until the latest checkpoint. +/// +/// Returns the latest checkpoint that was read from in this fetch, and a list of all the packages +/// that were read. +async fn fetch_packages( + client: &Client, + page_size: i32, + after_checkpoint: Option, + before_checkpoint: Option, +) -> Result<(Option, Vec)> { + let packages::Query { + checkpoint: checkpoint_viewed_at, + packages: + packages::MovePackageConnection { + mut page_info, + mut nodes, + }, + } = client + .query(packages::build( + page_size, + None, + after_checkpoint.map(UInt53), + before_checkpoint.map(UInt53), + )) + .await + .with_context(|| "Failed to fetch page 1 of packages.")?; + + for i in 2.. { + if !page_info.has_next_page { + break; + } + + let packages = client + .query(packages::build( + page_size, + page_info.end_cursor, + after_checkpoint.map(UInt53), + before_checkpoint.map(UInt53), + )) + .await + .with_context(|| format!("Failed to fetch page {i} of packages."))? + .packages; + + nodes.extend(packages.nodes); + page_info = packages.page_info; + + info!( + "Fetched page {i} ({} package{} so far).", + nodes.len(), + if nodes.len() == 1 { "" } else { "s" }, + ); + } + + use packages::Checkpoint as C; + let last_checkpoint = match (checkpoint_viewed_at, before_checkpoint) { + ( + Some(C { + sequence_number: UInt53(v), + }), + Some(b), + ) if b > 0 => Some(v.min(b - 1)), + ( + Some(C { + sequence_number: UInt53(c), + }), + _, + ) + | (_, Some(c)) => Some(c), + _ => None, + }; + + Ok((last_checkpoint, nodes)) +} + +/// Write out `pkg` to the `output_dir`ectory, using the package's address and name as the directory +/// name. The following files are written for each directory: +/// +/// - `object.bcs` -- the BCS serialized form of the `Object` type containing the package. +/// +/// - `linkage.json` -- a JSON serialization of the package's linkage table, mapping dependency +/// original IDs to the version of the dependency being depended on and the ID of the object +/// on-chain that contains that version. +/// +/// - `origins.json` -- a JSON serialize of the type origin table, mapping type names contained in +/// this package to the version of the package that first introduced that type. +/// +/// - `*.mv` -- a BCS serialization of each compiled module in the package. +fn dump_package(output_dir: &Path, pkg: &packages::MovePackage) -> Result<()> { + let Some(query::Base64(bcs)) = &pkg.bcs else { + bail!("Missing BCS"); + }; + + let bytes = Base64::decode(bcs).context("Failed to decode BCS")?; + + let object = bcs::from_bytes::(&bytes).context("Failed to deserialize")?; + let id = object.id(); + let Some(package) = object.data.try_as_package() else { + bail!("Not a package"); + }; + + let origins: BTreeMap<_, _> = package + .type_origin_table() + .iter() + .map(|o| { + ( + format!("{}::{}", o.module_name, o.datatype_name), + o.package.to_string(), + ) + }) + .collect(); + + let package_dir = output_dir.join(format!("{}.{}", id, package.version().value())); + fs::create_dir(&package_dir).context("Failed to make output directory")?; + + let linkage_json = serde_json::to_string_pretty(package.linkage_table()) + .context("Failed to serialize linkage")?; + let origins_json = + serde_json::to_string_pretty(&origins).context("Failed to serialize type origins")?; + + fs::write(package_dir.join("object.bcs"), bytes).context("Failed to write object BCS")?; + fs::write(package_dir.join("linkage.json"), linkage_json).context("Failed to write linkage")?; + fs::write(package_dir.join("origins.json"), origins_json) + .context("Failed to write type origins")?; + + for (module_name, module_bytes) in package.serialized_module_map() { + let module_path = package_dir.join(format!("{module_name}.mv")); + fs::write(module_path, module_bytes) + .with_context(|| format!("Failed to write module: {module_name}"))? + } + + Ok(()) +} diff --git a/crates/sui-package-dump/src/query.rs b/crates/sui-package-dump/src/query.rs new file mode 100644 index 0000000000000..a0d2c0ae391d5 --- /dev/null +++ b/crates/sui-package-dump/src/query.rs @@ -0,0 +1,105 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use cynic::Operation; +use cynic::QueryBuilder; + +#[cynic::schema("sui")] +mod schema {} + +#[derive(cynic::Scalar, Debug)] +pub(crate) struct SuiAddress(pub String); + +#[derive(cynic::Scalar, Debug)] +pub(crate) struct Base64(pub String); + +#[derive(cynic::Scalar, Debug)] +pub(crate) struct UInt53(pub u64); + +/// Query types related to GraphQL service limits. +pub(crate) mod limits { + use super::*; + + pub(crate) fn build() -> Operation { + Query::build(()) + } + + #[derive(cynic::QueryFragment, Debug)] + pub(crate) struct Query { + pub(crate) service_config: ServiceConfig, + } + + #[derive(cynic::QueryFragment, Debug)] + pub(crate) struct ServiceConfig { + pub(crate) max_page_size: i32, + } +} + +/// Query types related to fetching packages. +pub(crate) mod packages { + use super::*; + + pub(crate) fn build( + first: i32, + after: Option, + after_checkpoint: Option, + before_checkpoint: Option, + ) -> Operation { + Query::build(Vars { + first, + after, + filter: Some(MovePackageCheckpointFilter { + after_checkpoint, + before_checkpoint, + }), + }) + } + + #[derive(cynic::QueryVariables, Debug)] + pub(crate) struct Vars { + pub(crate) first: i32, + pub(crate) after: Option, + pub(crate) filter: Option, + } + + #[derive(cynic::InputObject, Debug)] + pub(crate) struct MovePackageCheckpointFilter { + pub(crate) after_checkpoint: Option, + pub(crate) before_checkpoint: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + #[cynic(variables = "Vars")] + pub(crate) struct Query { + pub(crate) checkpoint: Option, + #[arguments( + first: $first, + after: $after, + filter: $filter, + )] + pub(crate) packages: MovePackageConnection, + } + + #[derive(cynic::QueryFragment, Debug)] + pub(crate) struct Checkpoint { + pub(crate) sequence_number: UInt53, + } + + #[derive(cynic::QueryFragment, Debug)] + pub(crate) struct MovePackageConnection { + pub(crate) page_info: PageInfo, + pub(crate) nodes: Vec, + } + + #[derive(cynic::QueryFragment, Debug)] + pub(crate) struct PageInfo { + pub(crate) has_next_page: bool, + pub(crate) end_cursor: Option, + } + + #[derive(cynic::QueryFragment, Debug)] + pub(crate) struct MovePackage { + pub(crate) address: SuiAddress, + pub(crate) bcs: Option, + } +} diff --git a/crates/sui-tool/Cargo.toml b/crates/sui-tool/Cargo.toml index 2db2c5395e241..a19bfaa194b94 100644 --- a/crates/sui-tool/Cargo.toml +++ b/crates/sui-tool/Cargo.toml @@ -13,7 +13,6 @@ bcs.workspace = true clap = { version = "4.1.4", features = ["derive"] } colored.workspace = true comfy-table.workspace = true -diesel.workspace = true eyre.workspace = true futures.workspace = true hex.workspace = true @@ -41,7 +40,6 @@ narwhal-storage.workspace = true narwhal-types.workspace = true sui-config.workspace = true sui-core.workspace = true -sui-indexer.workspace = true sui-network.workspace = true sui-snapshot.workspace = true sui-protocol-config.workspace = true @@ -50,4 +48,5 @@ sui-sdk.workspace = true sui-storage.workspace = true sui-types.workspace = true sui-archival.workspace = true +sui-package-dump.workspace = true bin-version.workspace = true diff --git a/crates/sui-tool/src/commands.rs b/crates/sui-tool/src/commands.rs index 95b39183c96f6..3871143671e3e 100644 --- a/crates/sui-tool/src/commands.rs +++ b/crates/sui-tool/src/commands.rs @@ -5,7 +5,7 @@ use crate::{ check_completed_snapshot, db_tool::{execute_db_tool_command, print_db_all_tables, DbToolCommand}, download_db_snapshot, download_formal_snapshot, dump_checkpoints_from_archive, - get_latest_available_epoch, get_object, get_transaction_block, make_clients, pkg_dump, + get_latest_available_epoch, get_object, get_transaction_block, make_clients, restore_from_db_checkpoint, verify_archive, verify_archive_by_checksum, ConciseObjectOutput, GroupedObjectOutput, SnapshotVerifyMode, VerboseObjectOutput, }; @@ -177,21 +177,26 @@ pub enum ToolCommand { max_content_length: usize, }, - /// Download all packages to the local filesystem from an indexer database. Each package gets - /// its own sub-directory, named for its ID on-chain, containing two metadata files - /// (linkage.json and origins.json) as well as a file for every module it contains. Each module - /// file is named for its module name, with a .mv suffix, and contains Move bytecode (suitable - /// for passing into a disassembler). + /// Download all packages to the local filesystem from a GraphQL service. Each package gets its + /// own sub-directory, named for its ID on-chain and version containing two metadata files + /// (linkage.json and origins.json), a file containing the overall object and a file for every + /// module it contains. Each module file is named for its module name, with a .mv suffix, and + /// contains Move bytecode (suitable for passing into a disassembler). #[command(name = "dump-packages")] DumpPackages { - /// Connection information for the Indexer's Postgres DB. + /// Connection information for a GraphQL service. #[clap(long, short)] - db_url: String, + rpc_url: String, /// Path to a non-existent directory that can be created and filled with package information. #[clap(long, short)] output_dir: PathBuf, + /// Only fetch packages that were created before this checkpoint (given by its sequence + /// number). + #[clap(long)] + before_checkpoint: Option, + /// If false (default), log level will be overridden to "off", and output will be reduced to /// necessary status information. #[clap(short, long = "verbose")] @@ -633,8 +638,9 @@ impl ToolCommand { } } ToolCommand::DumpPackages { - db_url, + rpc_url, output_dir, + before_checkpoint, verbose, } => { if !verbose { @@ -643,7 +649,7 @@ impl ToolCommand { .expect("Failed to update log level"); } - pkg_dump::dump(db_url, output_dir).await?; + sui_package_dump::dump(rpc_url, output_dir, before_checkpoint).await?; } ToolCommand::DumpValidators { genesis, concise } => { let genesis = Genesis::load(genesis).unwrap(); diff --git a/crates/sui-tool/src/lib.rs b/crates/sui-tool/src/lib.rs index 574eb87a64f1b..b4290dc48b510 100644 --- a/crates/sui-tool/src/lib.rs +++ b/crates/sui-tool/src/lib.rs @@ -71,7 +71,6 @@ use typed_store::rocks::MetricConf; pub mod commands; pub mod db_tool; -pub mod pkg_dump; #[derive( Clone, Serialize, Deserialize, Debug, PartialEq, Copy, PartialOrd, Ord, Eq, ValueEnum, Default, diff --git a/crates/sui-tool/src/pkg_dump.rs b/crates/sui-tool/src/pkg_dump.rs deleted file mode 100644 index bd78cbf2b4b87..0000000000000 --- a/crates/sui-tool/src/pkg_dump.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use std::{ - collections::BTreeMap, - fs, - path::{Path, PathBuf}, - time::Duration, -}; - -use anyhow::{anyhow, ensure, Context, Result}; -use diesel::{ - r2d2::{ConnectionManager, Pool}, - PgConnection, RunQueryDsl, -}; -use sui_indexer::{models::packages::StoredPackage, schema::packages}; -use sui_types::{base_types::SuiAddress, move_package::MovePackage}; -use tracing::info; - -type PgPool = Pool>; - -pub(crate) async fn dump(db_url: String, output_dir: PathBuf) -> Result<()> { - ensure_output_directory(&output_dir)?; - - let conn = ConnectionManager::::new(db_url); - let pool = Pool::builder() - .max_size(1) - .connection_timeout(Duration::from_secs(30)) - .build(conn) - .context("Failed to create connection pool.")?; - - info!("Querying Indexer..."); - let pkgs = query_packages(&pool)?; - let total = pkgs.len(); - - let mut progress = 0; - for (i, pkg) in pkgs.into_iter().enumerate() { - let pct = (100 * i) / total; - if pct % 5 == 0 && pct > progress { - info!("Dumping packages ({total}): {pct: >3}%"); - progress = pct; - } - - let id = SuiAddress::from_bytes(&pkg.package_id).context("Parsing package ID")?; - dump_package(&output_dir, id, &pkg.move_package) - .with_context(|| format!("Dumping package: {id}"))?; - } - - info!("Dumping packages ({total}): 100%, Done."); - Ok(()) -} - -/// Ensure the output directory exists, either because it already exists as an empty, writable -/// directory, or by creating a new directory. -fn ensure_output_directory(path: impl Into) -> Result<()> { - let path: PathBuf = path.into(); - if path.exists() { - ensure!( - path.is_dir(), - "Output path is not a directory: {}", - path.display() - ); - ensure!( - path.read_dir().is_ok_and(|mut d| d.next().is_none()), - "Output directory is not empty: {}", - path.display(), - ); - - let metadata = fs::metadata(&path).context("Getting metadata for output path")?; - - ensure!( - !metadata.permissions().readonly(), - "Output directory is not writable: {}", - path.display() - ) - } else { - fs::create_dir_all(&path).context("Making output directory")?; - } - - Ok(()) -} - -fn query_packages(pool: &PgPool) -> Result> { - let mut conn = pool - .get() - .map_err(|e| anyhow!("Failed to get connection: {e}"))?; - Ok(packages::dsl::packages.load::(&mut conn)?) -} - -fn dump_package(output_dir: &Path, id: SuiAddress, pkg: &[u8]) -> Result<()> { - let package = bcs::from_bytes::(pkg).context("Deserializing")?; - let origins: BTreeMap<_, _> = package - .type_origin_table() - .iter() - .map(|o| { - ( - format!("{}::{}", o.module_name, o.datatype_name), - o.package.to_string(), - ) - }) - .collect(); - - let package_dir = output_dir.join(format!("{}.{}", id, package.version().value())); - fs::create_dir(&package_dir).context("Making output directory")?; - - let linkage_json = - serde_json::to_string_pretty(package.linkage_table()).context("Serializing linkage")?; - let origins_json = - serde_json::to_string_pretty(&origins).context("Serializing type origins")?; - - fs::write(package_dir.join("package.bcs"), pkg).context("Writing package BCS")?; - fs::write(package_dir.join("linkage.json"), linkage_json).context("Writing linkage")?; - fs::write(package_dir.join("origins.json"), origins_json).context("Writing type origins")?; - - for (module_name, module_bytes) in package.serialized_module_map() { - let module_path = package_dir.join(format!("{module_name}.mv")); - fs::write(module_path, module_bytes) - .with_context(|| format!("Writing module: {module_name}"))? - } - - Ok(()) -} From bd887c6bbb4666d36ac0833d09552ad31d2d1f6d Mon Sep 17 00:00:00 2001 From: Godwin JIbs <126525197+Jibz-Mysten@users.noreply.github.com> Date: Tue, 20 Aug 2024 10:42:48 -0400 Subject: [PATCH 174/232] Add expiry time to cached SuiNS name validation (#19048) ## Description Describe the changes or additions included in this PR. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .../app/components/address-input/validation.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/wallet/src/ui/app/components/address-input/validation.ts b/apps/wallet/src/ui/app/components/address-input/validation.ts index 4c4a133a79d28..26b28ef6b9b30 100644 --- a/apps/wallet/src/ui/app/components/address-input/validation.ts +++ b/apps/wallet/src/ui/app/components/address-input/validation.ts @@ -8,9 +8,12 @@ import { isValidSuiAddress, isValidSuiNSName } from '@mysten/sui/utils'; import { useMemo } from 'react'; import * as Yup from 'yup'; +const CACHE_EXPIRY_TIME = 60 * 1000; // 1 minute in milliseconds + export function createSuiAddressValidation(client: SuiClient, suiNSEnabled: boolean) { - const resolveCache = new Map(); + const resolveCache = new Map(); + const currentTime = Date.now(); return Yup.string() .ensure() .trim() @@ -18,14 +21,22 @@ export function createSuiAddressValidation(client: SuiClient, suiNSEnabled: bool .test('is-sui-address', 'Invalid address. Please check again.', async (value) => { if (suiNSEnabled && isValidSuiNSName(value)) { if (resolveCache.has(value)) { - return resolveCache.get(value)!; + const cachedEntry = resolveCache.get(value)!; + if (currentTime < cachedEntry.expiry) { + return cachedEntry.valid; + } else { + resolveCache.delete(value); // Remove expired entry + } } const address = await client.resolveNameServiceAddress({ name: value, }); - resolveCache.set(value, !!address); + resolveCache.set(value, { + valid: !!address, + expiry: currentTime + CACHE_EXPIRY_TIME, + }); return !!address; } From c68131bc41c3d88a950caa1228693a63ee51d29e Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Tue, 20 Aug 2024 16:41:36 +0100 Subject: [PATCH 175/232] fix(jsonrpc): Flaky fullnode JSON-RPC test (#19047) `transaction_tests::test_get_fullnode_transaction` included a flaky assertion that if you fetch 5 transactions, then the rest of the transactions, and then fetched the latest 10 transactions, the latter would match up with the suffix of the former two queries concatenated together. This does not work in general (at least not with this test set-up), because of consensus commit transactions. I have updated the test to check a similar property: That if you fetch 5 transactions, then the rest, and then fetch the first 10 transactions, then the prefixes match (this tests that transaction order is stable). There are other existing tests for what happens when you query transactions in descending order. This seed previously exhibited the failure, and now it succeeds. ``` sui-json-rpc-tests$ MSIM_TEST_SEED=968774516445189525 cargo simtest -- test_get_fullnode_transaction ``` --- .../sui-json-rpc-tests/tests/transaction_tests.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/sui-json-rpc-tests/tests/transaction_tests.rs b/crates/sui-json-rpc-tests/tests/transaction_tests.rs index f1036cbf00d7d..0ab96b4ac0210 100644 --- a/crates/sui-json-rpc-tests/tests/transaction_tests.rs +++ b/crates/sui-json-rpc-tests/tests/transaction_tests.rs @@ -259,24 +259,23 @@ async fn test_get_fullnode_transaction() -> Result<(), anyhow::Error> { assert!(second_page.data.len() > 5); assert!(!second_page.has_next_page); - let mut all_txs_rev = first_page.data.clone(); - all_txs_rev.extend(second_page.data); - all_txs_rev.reverse(); + let mut all_txs = first_page.data.clone(); + all_txs.extend(second_page.data); - // test get 10 latest transactions paged + // test get 10 transactions paged let latest = client .read_api() .query_transaction_blocks( SuiTransactionBlockResponseQuery::default(), None, Some(10), - true, + false, ) .await .unwrap(); assert_eq!(10, latest.data.len()); - assert_eq!(Some(all_txs_rev[9].digest), latest.next_cursor); - assert_eq!(all_txs_rev[0..10], latest.data); + assert_eq!(Some(all_txs[9].digest), latest.next_cursor); + assert_eq!(all_txs[0..10], latest.data); assert!(latest.has_next_page); // test get from address txs in ascending order From 91f8de171a193b6866e097897ffc518870b65c0b Mon Sep 17 00:00:00 2001 From: Dario Russi <113150618+dariorussi@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:19:41 +0200 Subject: [PATCH 176/232] unit test (#18659) ## Description Unit test infra for the bridge. Not sure why `bridge-env.move` is not showing as a rename. Also I run the latest formatter on the test files and that may add to the change. In any case @patrickkuo @Bridgerz we will talk about this given that is the easiest way to understand what is going on. Overall it is a purely additive change. ## Test plan This is it. Old tests run and new test increase coverage (~90%). We need to write few tests to cover for some assertion and that is coming How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: --- .../packages/bridge/sources/bridge.move | 61 + .../packages/bridge/sources/limiter.move | 7 + .../packages/bridge/sources/message.move | 5 + .../packages/bridge/sources/treasury.move | 15 + .../packages/bridge/tests/bridge_env.move | 1429 +++++++++++++++++ .../packages/bridge/tests/bridge_setup.move | 800 --------- .../packages/bridge/tests/bridge_tests.move | 1345 +++++++++------- .../packages/bridge/tests/bridge_txns.move | 329 ++++ 8 files changed, 2573 insertions(+), 1418 deletions(-) create mode 100644 crates/sui-framework/packages/bridge/tests/bridge_env.move delete mode 100644 crates/sui-framework/packages/bridge/tests/bridge_setup.move create mode 100644 crates/sui-framework/packages/bridge/tests/bridge_txns.move diff --git a/crates/sui-framework/packages/bridge/sources/bridge.move b/crates/sui-framework/packages/bridge/sources/bridge.move index 86b0b5ba08df4..f0dbc8d10788b 100644 --- a/crates/sui-framework/packages/bridge/sources/bridge.move +++ b/crates/sui-framework/packages/bridge/sources/bridge.move @@ -843,4 +843,65 @@ module bridge::bridge { let inner = load_inner_mut(bridge); inner.execute_add_tokens_on_sui(payload); } + + #[test_only] + public fun get_seq_num_for(bridge: &mut Bridge, message_type: u8): u64 { + let inner = load_inner_mut(bridge); + let seq_num = if (inner.sequence_nums.contains(&message_type)) { + inner.sequence_nums[&message_type] + } else { + inner.sequence_nums.insert(message_type, 0); + 0 + }; + seq_num + } + + #[test_only] + public fun get_seq_num_inc_for(bridge: &mut Bridge, message_type: u8): u64 { + let inner = load_inner_mut(bridge); + inner.get_current_seq_num_and_increment(message_type) + } + + #[test_only] + public fun transfer_approve_key(event: TokenTransferApproved): BridgeMessageKey { + event.message_key + } + + #[test_only] + public fun transfer_claimed_key(event: TokenTransferClaimed): BridgeMessageKey { + event.message_key + } + + #[test_only] + public fun transfer_already_approved_key(event: TokenTransferAlreadyApproved): BridgeMessageKey { + event.message_key + } + + #[test_only] + public fun transfer_already_claimed_key(event: TokenTransferAlreadyClaimed): BridgeMessageKey { + event.message_key + } + + #[test_only] + public fun transfer_limit_exceed_key(event: TokenTransferLimitExceed): BridgeMessageKey { + event.message_key + } + + #[test_only] + public fun unwrap_deposited_event(event: TokenDepositedEvent): (u64, u8, vector, u8, vector, u8, u64) { + ( + event.seq_num, + event.source_chain, + event.sender_address, + event.target_chain, + event.target_address, + event.token_type, + event.amount, + ) + } + + #[test_only] + public fun unwrap_emergency_op_event(event: EmergencyOpEvent): bool { + event.frozen + } } diff --git a/crates/sui-framework/packages/bridge/sources/limiter.move b/crates/sui-framework/packages/bridge/sources/limiter.move index 391476f3921b8..55f01477f68a7 100644 --- a/crates/sui-framework/packages/bridge/sources/limiter.move +++ b/crates/sui-framework/packages/bridge/sources/limiter.move @@ -276,4 +276,11 @@ module bridge::limiter { public(package) fun hour_tail(record: &TransferRecord): u64 { record.hour_tail } + + #[test_only] + public(package) fun unpack_route_limit_event(event: UpdateRouteLimitEvent): + (u8, u8, u64) + { + (event.sending_chain, event.receiving_chain, event.new_limit) + } } diff --git a/crates/sui-framework/packages/bridge/sources/message.move b/crates/sui-framework/packages/bridge/sources/message.move index e46d30b0e1aa9..769e57f3577a9 100644 --- a/crates/sui-framework/packages/bridge/sources/message.move +++ b/crates/sui-framework/packages/bridge/sources/message.move @@ -663,4 +663,9 @@ module bridge::message { token_prices, } } + + #[test_only] + public(package) fun unpack_message(msg: BridgeMessageKey): (u8, u8, u64) { + (msg.source_chain, msg.message_type, msg.bridge_seq_num) + } } diff --git a/crates/sui-framework/packages/bridge/sources/treasury.move b/crates/sui-framework/packages/bridge/sources/treasury.move index 27898d3a6e3b0..245189fa583f6 100644 --- a/crates/sui-framework/packages/bridge/sources/treasury.move +++ b/crates/sui-framework/packages/bridge/sources/treasury.move @@ -280,4 +280,19 @@ module bridge::treasury { public fun treasuries(treasury: &BridgeTreasury): &ObjectBag { &treasury.treasuries } + + #[test_only] + public fun unwrap_update_event(event: UpdateTokenPriceEvent): (u8, u64) { + (event.token_id, event.new_price) + } + + #[test_only] + public fun unwrap_new_token_event(event: NewTokenEvent): (u8, TypeName, bool, u64, u64) { + (event.token_id, event.type_name, event.native_token, event.decimal_multiplier, event.notional_value) + } + + #[test_only] + public fun unwrap_registration_event(event: TokenRegistrationEvent): (TypeName, u8, bool) { + (event.type_name, event.decimal, event.native_token) + } } diff --git a/crates/sui-framework/packages/bridge/tests/bridge_env.move b/crates/sui-framework/packages/bridge/tests/bridge_env.move new file mode 100644 index 0000000000000..c2ef819b3c7cb --- /dev/null +++ b/crates/sui-framework/packages/bridge/tests/bridge_env.move @@ -0,0 +1,1429 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[test_only] +module bridge::bridge_env { + use bridge::bridge::{ + assert_not_paused, + assert_paused, + create_bridge_for_testing, + inner_token_transfer_records, + test_init_bridge_committee, + test_load_inner_mut, + Bridge, + EmergencyOpEvent, + TokenDepositedEvent, + TokenTransferAlreadyApproved, + TokenTransferAlreadyClaimed, + TokenTransferApproved, + TokenTransferClaimed, + TokenTransferLimitExceed + }; + use bridge::btc::{Self, BTC}; + use bridge::chain_ids; + use bridge::committee::BlocklistValidatorEvent; + use bridge::eth::{Self, ETH}; + use bridge::limiter::UpdateRouteLimitEvent; + use bridge::message::{ + Self, + BridgeMessage, + create_add_tokens_on_sui_message, + create_blocklist_message, + emergency_op_pause, + emergency_op_unpause + }; + use bridge::message_types; + use bridge::test_token::{Self, TEST_TOKEN}; + use bridge::treasury::{ + TokenRegistrationEvent, + NewTokenEvent, + UpdateTokenPriceEvent + }; + use bridge::usdc::{Self, USDC}; + use bridge::usdt::{Self, USDT}; + use std::ascii::String; + use std::type_name; + use sui::address; + use sui::clock::Clock; + use sui::coin::{Self, Coin, CoinMetadata, TreasuryCap}; + use sui::ecdsa_k1::{KeyPair, secp256k1_keypair_from_seed, secp256k1_sign}; + use sui::event; + use sui::package::UpgradeCap; + use sui::test_scenario::{Self, Scenario}; + use sui::test_utils::destroy; + use sui_system::governance_test_utils::{ + advance_epoch_with_reward_amounts, + create_sui_system_state_for_testing, + create_validator_for_testing + }; + use sui_system::sui_system::{ + validator_voting_powers_for_testing, + SuiSystemState + }; + + // + // Token IDs + // + const BTC_ID: u8 = 1; + const ETH_ID: u8 = 2; + const USDC_ID: u8 = 3; + const USDT_ID: u8 = 4; + const TEST_TOKEN_ID: u8 = 5; + + public fun btc_id(): u8 { + BTC_ID + } + + public fun eth_id(): u8 { + ETH_ID + } + + public fun usdc_id(): u8 { + USDC_ID + } + + public fun usdt_id(): u8 { + USDT_ID + } + + public fun test_token_id(): u8 { + TEST_TOKEN_ID + } + + // + // Claim status + // + const CLAIMED: u8 = 1; + const ALREADY_CLAIMED: u8 = 2; + const LIMIT_EXCEEDED: u8 = 3; + + public fun claimed(): u8 { + CLAIMED + } + + public fun already_claimed(): u8 { + ALREADY_CLAIMED + } + + public fun limit_exceeded(): u8 { + LIMIT_EXCEEDED + } + + // + // Approve status + // + const APPROVED: u8 = 1; + const ALREADY_APPROVED: u8 = 2; + + public fun approved(): u8 { + APPROVED + } + + public fun already_approved(): u8 { + ALREADY_APPROVED + } + + // + // Validators setup and info + // + + // Validator info + public struct ValidatorInfo has drop { + validator: address, + key_pair: KeyPair, + stake_amount: u64, + } + + public fun addr(validator: &ValidatorInfo): address { + validator.validator + } + + public fun public_key(validator: &ValidatorInfo): &vector { + validator.key_pair.public_key() + } + + public fun create_validator( + validator: address, + stake_amount: u64, + seed: &vector, + ): ValidatorInfo { + ValidatorInfo { + validator, + key_pair: secp256k1_keypair_from_seed(seed), + stake_amount, + } + } + + // Bridge environemnt + public struct BridgeEnv { + scenario: Scenario, + validators: vector, + chain_id: u8, + vault: Vault, + clock: Clock, + } + + // Holds coins for different bridged tokens + public struct Vault { + btc_coins: Coin, + eth_coins: Coin, + usdc_coins: Coin, + usdt_coins: Coin, + test_coins: Coin, + } + + // HotPotato to access shared state + // TODO: if the bridge is the only shared state we could remvove this + public struct BridgeWrapper { + bridge: Bridge, + } + + public fun bridge(env: &mut BridgeEnv, sender: address): BridgeWrapper { + let scenario = &mut env.scenario; + scenario.next_tx(sender); + let bridge = scenario.take_shared(); + BridgeWrapper { bridge } + } + + public fun bridge_ref(wrapper: &BridgeWrapper): &Bridge { + &wrapper.bridge + } + + public fun bridge_ref_mut(wrapper: &mut BridgeWrapper): &mut Bridge { + &mut wrapper.bridge + } + + public fun return_bridge(bridge: BridgeWrapper) { + let BridgeWrapper { bridge } = bridge; + test_scenario::return_shared(bridge); + } + + // + // Public functions + // + + // + // Environment creation and destruction + // + + public fun create_env(chain_id: u8): BridgeEnv { + let mut scenario = test_scenario::begin(@0x0); + let ctx = scenario.ctx(); + let mut clock = sui::clock::create_for_testing(ctx); + clock.set_for_testing(1_000_000_000); + let btc_coins = coin::zero(ctx); + let eth_coins = coin::zero(ctx); + let usdc_coins = coin::zero(ctx); + let usdt_coins = coin::zero(ctx); + let test_coins = coin::zero(ctx); + let vault = Vault { + btc_coins, + eth_coins, + usdc_coins, + usdt_coins, + test_coins, + }; + BridgeEnv { + scenario, + chain_id, + vault, + validators: vector::empty(), + clock, + } + } + + public fun destroy_env(env: BridgeEnv) { + let BridgeEnv { + scenario, + chain_id: _, + vault, + validators: _, + clock, + } = env; + destroy_valut(vault); + clock.destroy_for_testing(); + scenario.end(); + } + + // + // Add a set of validators to the chain. + // Call only once in a test scenario. + public fun setup_validators( + env: &mut BridgeEnv, + validators_info: vector, + ) { + let scenario = &mut env.scenario; + scenario.next_tx(@0x0); + let ctx = scenario.ctx(); + let validators = validators_info.map_ref!( + |validator| { + create_validator_for_testing( + validator.validator, + validator.stake_amount, + ctx, + ) + }, + ); + env.validators = validators_info; + create_sui_system_state_for_testing(validators, 0, 0, ctx); + advance_epoch_with_reward_amounts(0, 0, scenario); + } + + // + // Bridge creation and setup + // + + // Set up an environment with 3 validators, a bridge with + // a treasury and a committee with all 3 validators. + // The treasury will contain 4 tokens: ETH, BTC, USDT, USDC. + // Save the Bridge as a shared object. + public fun create_bridge_default(env: &mut BridgeEnv) { + let validators = vector[ + create_validator( + @0xAAAA, + 100, + &b"1234567890_1234567890_1234567890", + ), + create_validator( + @0xBBBB, + 100, + &b"234567890_1234567890_1234567890_", + ), + create_validator( + @0xCCCC, + 100, + &b"34567890_1234567890_1234567890_1", + ), + ]; + env.setup_validators(validators); + + let sender = @0x0; + env.create_bridge(sender); + env.register_committee(); + env.init_committee(sender); + env.setup_treasury(sender); + } + + // Create a bridge and set up a treasury. + // The treasury will contain 4 tokens: ETH, BTC, USDT, USDC. + // Save the Bridge as a shared object. + // No operation on the validators. + public fun create_bridge(env: &mut BridgeEnv, sender: address) { + env.scenario.next_tx(sender); + let ctx = env.scenario.ctx(); + create_bridge_for_testing(object::new(ctx), env.chain_id, ctx); + } + + // Register 3 committee members (validators `@0xA`, `@0xB`, `@0xC`) + public fun register_committee(env: &mut BridgeEnv) { + let scenario = &mut env.scenario; + scenario.next_tx(@0x0); + let mut bridge = scenario.take_shared(); + let mut system_state = test_scenario::take_shared( + scenario, + ); + + env + .validators + .do_ref!( + |validator| { + scenario.next_tx(validator.validator); + bridge.committee_registration( + &mut system_state, + *validator.key_pair.public_key(), + b"", + scenario.ctx(), + ); + }, + ); + + test_scenario::return_shared(bridge); + test_scenario::return_shared(system_state); + } + + // Init the bridge committee + public fun init_committee(env: &mut BridgeEnv, sender: address) { + let scenario = &mut env.scenario; + scenario.next_tx(sender); + let mut bridge = scenario.take_shared(); + let mut system_state = test_scenario::take_shared( + scenario, + ); + let voting_powers = validator_voting_powers_for_testing( + &mut system_state, + ); + bridge.test_init_bridge_committee( + voting_powers, + 50, + scenario.ctx(), + ); + test_scenario::return_shared(bridge); + test_scenario::return_shared(system_state); + } + + // Set up a treasury with 4 tokens: ETH, BTC, USDT, USDC. + public fun setup_treasury(env: &mut BridgeEnv, sender: address) { + env.register_default_tokens(sender); + env.add_default_tokens(sender); + env.load_vault(sender); + } + + // Register 4 tokens with the Bridge: ETH, BTC, USDT, USDC. + fun register_default_tokens(env: &mut BridgeEnv, sender: address) { + env.scenario.next_tx(sender); + let mut bridge = env.scenario.take_shared(); + + // BTC + let (upgrade_cap, treasury_cap, metadata) = btc::create_bridge_token(env + .scenario + .ctx()); + bridge.register_foreign_token( + treasury_cap, + upgrade_cap, + &metadata, + ); + destroy(metadata); + // ETH + let (upgrade_cap, treasury_cap, metadata) = eth::create_bridge_token(env + .scenario + .ctx()); + bridge.register_foreign_token( + treasury_cap, + upgrade_cap, + &metadata, + ); + destroy(metadata); + // USDC + let ( + upgrade_cap, + treasury_cap, + metadata, + ) = usdc::create_bridge_token(env.scenario.ctx()); + bridge.register_foreign_token( + treasury_cap, + upgrade_cap, + &metadata, + ); + destroy(metadata); + // USDT + let ( + upgrade_cap, + treasury_cap, + metadata, + ) = usdt::create_bridge_token(env.scenario.ctx()); + bridge.register_foreign_token( + treasury_cap, + upgrade_cap, + &metadata, + ); + destroy(metadata); + + test_scenario::return_shared(bridge); + } + + // Add the 4 tokens previously registered: ETH, BTC, USDT, USDC. + fun add_default_tokens(env: &mut BridgeEnv, sender: address) { + let scenario = &mut env.scenario; + scenario.next_tx(sender); + let mut bridge = scenario.take_shared(); + + let add_token_message = create_add_tokens_on_sui_message( + env.chain_id, + bridge.get_seq_num_for(message_types::add_tokens_on_sui()), + false, + vector[BTC_ID, ETH_ID, USDC_ID, USDT_ID], + vector[ + type_name::get().into_string(), + type_name::get().into_string(), + type_name::get().into_string(), + type_name::get().into_string(), + ], + vector[1000, 100, 1, 1], + ); + let signatures = env.sign_message(add_token_message); + bridge.execute_system_message(add_token_message, signatures); + + test_scenario::return_shared(bridge); + } + + // + // Utility functions for custom behavior + // + + public fun token_type(env: &mut BridgeEnv): u8 { + env.scenario.next_tx(@0x0); + let bridge = env.scenario.take_shared(); + let inner = bridge.test_load_inner(); + let token_id = inner.inner_treasury().token_id(); + test_scenario::return_shared(bridge); + token_id + } + + const SUI_MESSAGE_PREFIX: vector = b"SUI_BRIDGE_MESSAGE"; + + fun sign_message( + env: &BridgeEnv, + message: BridgeMessage, + ): vector> { + let mut message_bytes = SUI_MESSAGE_PREFIX; + message_bytes.append(message.serialize_message()); + let mut message_bytes = SUI_MESSAGE_PREFIX; + message_bytes.append(message.serialize_message()); + env + .validators + .map_ref!( + |validator| { + secp256k1_sign( + validator.key_pair.private_key(), + &message_bytes, + 0, + true, + ) + }, + ) + } + + public fun sign_message_with( + env: &BridgeEnv, + message: BridgeMessage, + validator_idxs: vector, + ): vector> { + let mut message_bytes = SUI_MESSAGE_PREFIX; + message_bytes.append(message.serialize_message()); + validator_idxs.map!( + |idx| { + secp256k1_sign( + env.validators[idx].key_pair.private_key(), + &message_bytes, + 0, + true, + ) + }, + ) + } + + public fun bridge_in_message( + env: &mut BridgeEnv, + source_chain: u8, + source_address: vector, + target_address: address, + amount: u64, + ): BridgeMessage { + let token_type = env.token_type(); + + let scenario = &mut env.scenario; + scenario.next_tx(@0x0); + let mut bridge = scenario.take_shared(); + + let message = message::create_token_bridge_message( + source_chain, + bridge.get_seq_num_inc_for(message_types::token()), + source_address, + env.chain_id, + address::to_bytes(target_address), + token_type, + amount, + ); + test_scenario::return_shared(bridge); + message + } + + public fun bridge_out_message( + env: &mut BridgeEnv, + target_chain: u8, + target_address: vector, + source_address: address, + amount: u64, + transfer_id: u64, + ): BridgeMessage { + let token_type = env.token_type(); + + let scenario = &mut env.scenario; + scenario.next_tx(@0x0); + let bridge = scenario.take_shared(); + + let message = message::create_token_bridge_message( + env.chain_id, + transfer_id, + address::to_bytes(source_address), + target_chain, + target_address, + token_type, + amount, + ); + test_scenario::return_shared(bridge); + message + } + + public fun bridge_token_signed_message( + env: &mut BridgeEnv, + source_chain: u8, + source_address: vector, + target_address: address, + amount: u64, + ): (BridgeMessage, vector>) { + let token_type = env.token_type(); + let scenario = &mut env.scenario; + scenario.next_tx(@0x0); + let mut bridge = scenario.take_shared(); + let seq_num = bridge.get_seq_num_inc_for(message_types::token()); + test_scenario::return_shared(bridge); + let message = message::create_token_bridge_message( + source_chain, + seq_num, + source_address, + env.chain_id, + address::to_bytes(target_address), + token_type, + amount, + ); + let signatures = env.sign_message(message); + (message, signatures) + } + + // Bridge the `amount` of the given `Token` from the `source_chain`. + public fun bridge_to_sui( + env: &mut BridgeEnv, + source_chain: u8, + source_address: vector, + target_address: address, + amount: u64, + ): u64 { + let token_type = env.token_type(); + + // setup + let scenario = &mut env.scenario; + scenario.next_tx(@0x0); + let mut bridge = scenario.take_shared(); + + // sign message + let seq_num = bridge.get_seq_num_inc_for(message_types::token()); + let message = message::create_token_bridge_message( + source_chain, + seq_num, + source_address, + env.chain_id, + address::to_bytes(target_address), + token_type, + amount, + ); + let signatures = env.sign_message(message); + + // run approval + bridge.approve_token_transfer(message, signatures); + + // verify approval events + let approved_events = event::events_by_type(); + let already_approved_events = event::events_by_type< + TokenTransferAlreadyApproved, + >(); + assert!( + approved_events.length() == 1 || + already_approved_events.length() == 1, + ); + let key = if (approved_events.length() == 1) { + approved_events[0].transfer_approve_key() + } else { + already_approved_events[0].transfer_already_approved_key() + }; + let (sc, mt, sn) = key.unpack_message(); + assert!(source_chain == sc); + assert!(mt == message_types::token()); + assert!(sn == seq_num); + + // tear down + test_scenario::return_shared(bridge); + seq_num + } + + // Approves a token transer + public fun approve_token_transfer( + env: &mut BridgeEnv, + message: BridgeMessage, + signatures: vector>, + ): u8 { + let msg_key = message.key(); + + // set up + let scenario = &mut env.scenario; + scenario.next_tx(@0x0); + let mut bridge = scenario.take_shared(); + + // run approval + bridge.approve_token_transfer(message, signatures); + + // verify approval events + let approved = event::events_by_type(); + let already_approved = event::events_by_type< + TokenTransferAlreadyApproved, + >(); + assert!(approved.length() == 1 || already_approved.length() == 1); + let (key, approve_status) = if (approved.length() == 1) { + (approved[0].transfer_approve_key(), APPROVED) + } else { + ( + already_approved[0].transfer_already_approved_key(), + ALREADY_APPROVED, + ) + }; + assert!(msg_key == key); + + // tear down + test_scenario::return_shared(bridge); + approve_status + } + + // Clain a token transfer and returns the coin + public fun claim_token( + env: &mut BridgeEnv, + sender: address, + source_chain: u8, + bridge_seq_num: u64, + ): Coin { + // set up + let scenario = &mut env.scenario; + scenario.next_tx(sender); + let clock = &env.clock; + let mut bridge = scenario.take_shared(); + let ctx = scenario.ctx(); + let total_supply_before = get_total_supply(&bridge); + + // run claim + let token = bridge.claim_token( + clock, + source_chain, + bridge_seq_num, + ctx, + ); + + // verify value change and claim events + let token_value = token.value(); + assert!( + total_supply_before + token_value == get_total_supply(&bridge), + ); + let claimed = event::events_by_type(); + let already_claimed = event::events_by_type< + TokenTransferAlreadyClaimed, + >(); + let limit_exceeded = event::events_by_type(); + assert!( + claimed.length() == 1 || already_claimed.length() == 1 || + limit_exceeded.length() == 1, + ); + let key = if (claimed.length() == 1) { + claimed[0].transfer_claimed_key() + } else if (already_claimed.length() == 1) { + already_claimed[0].transfer_already_claimed_key() + } else { + limit_exceeded[0].transfer_limit_exceed_key() + }; + let (sc, mt, sn) = key.unpack_message(); + assert!(source_chain == sc); + assert!(mt == message_types::token()); + assert!(sn == bridge_seq_num); + + // tear down + test_scenario::return_shared(bridge); + token + } + + // Claim a token and transfer to the receiver in the bridge message + public fun claim_and_transfer_token( + env: &mut BridgeEnv, + source_chain: u8, + bridge_seq_num: u64, + ): u8 { + // set up + let sender = @0xA1B2C3; // random sender + let scenario = &mut env.scenario; + scenario.next_tx(sender); + let clock = &env.clock; + let mut bridge = scenario.take_shared(); + let ctx = scenario.ctx(); + let total_supply_before = get_total_supply(&bridge); + + // run claim and transfer + bridge.claim_and_transfer_token( + clock, + source_chain, + bridge_seq_num, + ctx, + ); + + // verify claim events + let claimed = event::events_by_type(); + let already_claimed = event::events_by_type< + TokenTransferAlreadyClaimed, + >(); + let limit_exceeded = event::events_by_type(); + assert!( + claimed.length() == 1 || already_claimed.length() == 1 || + limit_exceeded.length() == 1, + ); + let (key, claim_status) = if (claimed.length() == 1) { + (claimed[0].transfer_claimed_key(), CLAIMED) + } else if (already_claimed.length() == 1) { + (already_claimed[0].transfer_already_claimed_key(), ALREADY_CLAIMED) + } else { + (limit_exceeded[0].transfer_limit_exceed_key(), LIMIT_EXCEEDED) + }; + let (sc, mt, sn) = key.unpack_message(); + assert!(source_chain == sc); + assert!(mt == message_types::token()); + assert!(sn == bridge_seq_num); + + // verify effects + let effects = scenario.next_tx(@0xABCDEF); + let created = effects.created(); + if (!created.is_empty()) { + let token_id = effects.created()[0]; + let token = scenario.take_from_sender_by_id>(token_id); + let token_value = token.value(); + assert!( + total_supply_before + token_value == + get_total_supply(&bridge), + ); + scenario.return_to_sender(token); + }; + + // tear down + test_scenario::return_shared(bridge); + claim_status + } + + // Send a coin (token) to the target chain + public fun send_token( + env: &mut BridgeEnv, + sender: address, + target_chain_id: u8, + eth_address: vector, + coin: Coin, + ): u64 { + // set up + let chain_id = env.chain_id; + let scenario = env.scenario(); + scenario.next_tx(sender); + let mut bridge = scenario.take_shared(); + let coin_value = coin.value(); + let total_supply_before = get_total_supply(&bridge); + let seq_num = bridge.get_seq_num_for(message_types::token()); + + // run send + bridge.send_token(target_chain_id, eth_address, coin, scenario.ctx()); + + // verify send events + assert!( + total_supply_before - coin_value == get_total_supply(&bridge), + ); + let deposited_events = event::events_by_type(); + assert!(deposited_events.length() == 1); + let ( + event_seq_num, + _event_source_chain, + _event_sender_address, + _event_target_chain, + _event_target_address, + _event_token_type, + event_amount, + ) = deposited_events[0].unwrap_deposited_event(); + assert!(event_seq_num == seq_num); + assert!(event_amount == coin_value); + assert_key(chain_id, &bridge); + + // tear down + test_scenario::return_shared(bridge); + seq_num + } + + // Update the limit for a given route + public fun update_bridge_limit( + env: &mut BridgeEnv, + sender: address, + receiving_chain: u8, + sending_chain: u8, + limit: u64, + ): u64 { + // set up + let scenario = env.scenario(); + scenario.next_tx(sender); + let mut bridge = scenario.take_shared(); + + // message signed + let msg = message::create_update_bridge_limit_message( + receiving_chain, + bridge.get_seq_num_for(message_types::update_bridge_limit()), + sending_chain, + limit, + ); + let signatures = env.sign_message(msg); + + // run limit update + bridge.execute_system_message(msg, signatures); + + // verify limit events + let limit_events = event::events_by_type(); + assert!(limit_events.length() == 1); + let event = limit_events[0]; + let (sc, rc, new_limit) = event.unpack_route_limit_event(); + assert!(sc == sending_chain); + assert!(rc == receiving_chain); + assert!(new_limit == limit); + + // tear down + test_scenario::return_shared(bridge); + new_limit + } + + // Update a given asset price (notional value) + public fun update_asset_price( + env: &mut BridgeEnv, + sender: address, + token_id: u8, + value: u64, + ) { + // set up + let scenario = &mut env.scenario; + scenario.next_tx(sender); + let mut bridge = scenario.take_shared(); + + // message signed + let message = message::create_update_asset_price_message( + token_id, + env.chain_id, + bridge.get_seq_num_for(message_types::update_asset_price()), + value, + ); + let signatures = env.sign_message(message); + + // run price update + bridge.execute_system_message(message, signatures); + + // verify price events + let update_events = event::events_by_type(); + assert!(update_events.length() == 1); + let (event_token_id, event_new_price) = update_events[ + 0 + ].unwrap_update_event(); + assert!(event_token_id == token_id); + assert!(event_new_price == value); + + // tear down + test_scenario::return_shared(bridge); + } + + // Register the `TEST_TOKEN` token + public fun register_test_token(env: &mut BridgeEnv) { + // set up + let scenario = &mut env.scenario; + scenario.next_tx(@0x0); + let mut bridge = scenario.take_shared(); + + // "create" the `Coin` + let ( + upgrade_cap, + treasury_cap, + metadata, + ) = test_token::create_bridge_token(scenario.ctx()); + // register the coin/token with the bridge + bridge.register_foreign_token( + treasury_cap, + upgrade_cap, + &metadata, + ); + + // verify registration events + let register_events = event::events_by_type(); + assert!(register_events.length() == 1); + let (type_name, decimal, nat) = register_events[ + 0 + ].unwrap_registration_event(); + assert!(type_name == type_name::get()); + assert!(decimal == 8); + assert!(nat == false); + + // tear down + destroy(metadata); + test_scenario::return_shared(bridge); + } + + // Add a list of tokens to the bridge. + public fun add_tokens( + env: &mut BridgeEnv, + sender: address, + native_token: bool, + token_ids: vector, + type_names: vector, + token_prices: vector, + ) { + // set up + let scenario = &mut env.scenario; + scenario.next_tx(sender); + let mut bridge = scenario.take_shared(); + + // message signed + let message = create_add_tokens_on_sui_message( + env.chain_id, + bridge.get_seq_num_for(message_types::add_tokens_on_sui()), + native_token, + token_ids, + type_names, + token_prices, + ); + let signatures = env.sign_message(message); + + // run token addition + bridge.execute_system_message(message, signatures); + + // verify token addition events + let new_tokens_events = event::events_by_type(); + assert!(new_tokens_events.length() <= token_ids.length()); + + // tear down + test_scenario::return_shared(bridge); + } + + // Blocklist a list of bridge nodes + public fun execute_blocklist( + env: &mut BridgeEnv, + sender: address, + chain_id: u8, + blocklist_type: u8, + validator_ecdsa_addresses: vector>, + ) { + // set up + let scenario = env.scenario(); + scenario.next_tx(sender); + let mut bridge = scenario.take_shared(); + + // message signed + let blocklist = create_blocklist_message( + chain_id, + bridge.get_seq_num_for(message_types::committee_blocklist()), + blocklist_type, + validator_ecdsa_addresses, + ); + let signatures = env.sign_message(blocklist); + + // run blocklist + bridge.execute_system_message(blocklist, signatures); + + // verify blocklist events + let block_list_events = event::events_by_type< + BlocklistValidatorEvent, + >(); + assert!( + block_list_events.length() == validator_ecdsa_addresses.length(), + ); + + // tear down + test_scenario::return_shared(bridge); + } + + // Register new token + public fun register_foreign_token( + env: &mut BridgeEnv, + treasury_cap: TreasuryCap, + upgrade_cap: UpgradeCap, + metadata: CoinMetadata, + sender: address, + ) { + // set up + let scenario = env.scenario(); + scenario.next_tx(sender); + let mut bridge = scenario.take_shared(); + + // run registration + bridge.register_foreign_token(treasury_cap, upgrade_cap, &metadata); + + // verify registration events + let register_events = event::events_by_type(); + assert!(register_events.length() == 1); + + // verify changes in bridge + let type_name = type_name::get(); + let inner = bridge.test_load_inner(); + let treasury = inner.inner_treasury(); + let waiting_room = treasury.waiting_room(); + assert!(waiting_room.contains(type_name::into_string(type_name))); + let treasuries = treasury.treasuries(); + assert!(treasuries.contains(type_name)); + + // tear down + test_scenario::return_shared(bridge); + destroy(metadata); + } + + // Freeze the bridge + public fun freeze_bridge(env: &mut BridgeEnv, sender: address, error: u64) { + // set up + let scenario = env.scenario(); + scenario.next_tx(sender); + let mut bridge = scenario.take_shared(); + let seq_num = bridge.get_seq_num_for(message_types::emergency_op()); + + // message signed + let msg = message::create_emergency_op_message( + env.chain_id, + seq_num, + emergency_op_pause(), + ); + let signatures = env.sign_message(msg); + + // run freeze + bridge.execute_system_message(msg, signatures); + + // verify freeze events + let register_events = event::events_by_type(); + assert!(register_events.length() == 1); + assert!(register_events[0].unwrap_emergency_op_event() == true); + + // verify freeze + let inner = bridge.test_load_inner_mut(); + inner.assert_paused(error); + + // tear down + test_scenario::return_shared(bridge); + } + + // Unfreeze the bridge + public fun unfreeze_bridge( + env: &mut BridgeEnv, + sender: address, + error: u64, + ) { + // set up + let scenario = env.scenario(); + scenario.next_tx(sender); + let mut bridge = scenario.take_shared(); + let seq_num = bridge.get_seq_num_for(message_types::emergency_op()); + + // message signed + let msg = message::create_emergency_op_message( + env.chain_id, + seq_num, + emergency_op_unpause(), + ); + let signatures = env.sign_message(msg); + + // run unfreeze + bridge.execute_system_message(msg, signatures); + let register_events = event::events_by_type(); + assert!(register_events.length() == 1); + assert!(register_events[0].unwrap_emergency_op_event() == false); + + // verify unfreeze events + + // verify unfreeze + let inner = bridge.test_load_inner_mut(); + inner.assert_not_paused(error); + + // tear down + test_scenario::return_shared(bridge); + } + + // + // Getters + // + + public fun ctx(env: &mut BridgeEnv): &mut TxContext { + env.scenario.ctx() + } + + public fun scenario(env: &mut BridgeEnv): &mut Scenario { + &mut env.scenario + } + + public fun chain_id(env: &mut BridgeEnv): u8 { + env.chain_id + } + + public fun validators(env: &BridgeEnv): &vector { + &env.validators + } + + public fun get_btc(env: &mut BridgeEnv, amount: u64): Coin { + let scenario = &mut env.scenario; + let ctx = scenario.ctx(); + env.vault.btc_coins.split(amount, ctx) + } + + public fun get_eth(env: &mut BridgeEnv, amount: u64): Coin { + let scenario = &mut env.scenario; + let ctx = scenario.ctx(); + env.vault.eth_coins.split(amount, ctx) + } + + public fun get_usdc(env: &mut BridgeEnv, amount: u64): Coin { + let scenario = &mut env.scenario; + let ctx = scenario.ctx(); + env.vault.usdc_coins.split(amount, ctx) + } + + public fun get_usdt(env: &mut BridgeEnv, amount: u64): Coin { + let scenario = &mut env.scenario; + let ctx = scenario.ctx(); + env.vault.usdt_coins.split(amount, ctx) + } + + public fun limits(env: &mut BridgeEnv, dest: u8): u64 { + let scenario = env.scenario(); + scenario.next_tx(@0x0); + let bridge = scenario.take_shared(); + let route = chain_ids::get_route(dest, env.chain_id); + let limits = bridge + .test_load_inner() + .inner_limiter() + .get_route_limit(&route); + test_scenario::return_shared(bridge); + limits + } + + fun assert_key(chain_id: u8, bridge: &Bridge) { + let inner = bridge.test_load_inner(); + let transfer_record = inner.inner_token_transfer_records(); + let seq_num = inner.sequence_nums()[&message_types::token()] - 1; + let key = message::create_key( + chain_id, + message_types::token(), + seq_num, + ); + assert!(transfer_record.contains(key)); + } + + // + // Internal functions + // + + // Destroy the vault + fun destroy_valut(vault: Vault) { + let Vault { + btc_coins, + eth_coins, + usdc_coins, + usdt_coins, + test_coins, + } = vault; + btc_coins.burn_for_testing(); + eth_coins.burn_for_testing(); + usdc_coins.burn_for_testing(); + usdt_coins.burn_for_testing(); + test_coins.burn_for_testing(); + } + + // Load the vault with some coins + fun load_vault(env: &mut BridgeEnv, sender: address) { + let scenario = &mut env.scenario; + scenario.next_tx(sender); + let mut bridge = scenario.take_shared(); + let vault = &mut env.vault; + vault.btc_coins.join(mint_some(&mut bridge, scenario.ctx())); + vault.eth_coins.join(mint_some(&mut bridge, scenario.ctx())); + vault.usdc_coins.join(mint_some(&mut bridge, scenario.ctx())); + vault.usdt_coins.join(mint_some(&mut bridge, scenario.ctx())); + test_scenario::return_shared(bridge); + } + + // Mint some coins + fun mint_some(bridge: &mut Bridge, ctx: &mut TxContext): Coin { + let treasury = bridge.test_load_inner_mut().inner_treasury_mut(); + let coin = treasury.mint(1_000_000, ctx); + coin + } + + fun get_total_supply(bridge: &Bridge): u64 { + let inner = bridge.test_load_inner(); + let treasury = inner.inner_treasury(); + let treasuries = treasury.treasuries(); + let tc: &TreasuryCap = &treasuries[type_name::get()]; + tc.total_supply() + } +} + +// +// Test Coins +// + +#[test_only] +module bridge::test_token { + use std::ascii; + use std::type_name; + use sui::address; + use sui::coin::{CoinMetadata, TreasuryCap, create_currency}; + use sui::hex; + use sui::package::{UpgradeCap, test_publish}; + use sui::test_utils::create_one_time_witness; + + public struct TEST_TOKEN has drop {} + + public fun create_bridge_token( + ctx: &mut TxContext, + ): (UpgradeCap, TreasuryCap, CoinMetadata) { + let otw = create_one_time_witness(); + let (treasury_cap, metadata) = create_currency( + otw, + 8, + b"tst", + b"test", + b"bridge test token", + option::none(), + ctx, + ); + + let type_name = type_name::get(); + let address_bytes = hex::decode( + ascii::into_bytes(type_name::get_address(&type_name)), + ); + let coin_id = address::from_bytes(address_bytes).to_id(); + let upgrade_cap = test_publish(coin_id, ctx); + + (upgrade_cap, treasury_cap, metadata) + } +} + +#[test_only] +module bridge::btc { + use std::ascii; + use std::type_name; + use sui::address; + use sui::coin::{CoinMetadata, TreasuryCap, create_currency}; + use sui::hex; + use sui::package::{UpgradeCap, test_publish}; + use sui::test_utils::create_one_time_witness; + + public struct BTC has drop {} + + public fun create_bridge_token( + ctx: &mut TxContext, + ): (UpgradeCap, TreasuryCap, CoinMetadata) { + let otw = create_one_time_witness(); + let (treasury_cap, metadata) = create_currency( + otw, + 8, + b"btc", + b"bitcoin", + b"bridge bitcoin token", + option::none(), + ctx, + ); + + let type_name = type_name::get(); + let address_bytes = hex::decode( + ascii::into_bytes(type_name::get_address(&type_name)), + ); + let coin_id = address::from_bytes(address_bytes).to_id(); + let upgrade_cap = test_publish(coin_id, ctx); + + (upgrade_cap, treasury_cap, metadata) + } +} + +#[test_only] +module bridge::eth { + use std::ascii; + use std::type_name; + use sui::address; + use sui::coin::{CoinMetadata, TreasuryCap, create_currency}; + use sui::hex; + use sui::package::{UpgradeCap, test_publish}; + use sui::test_utils::create_one_time_witness; + + public struct ETH has drop {} + + public fun create_bridge_token( + ctx: &mut TxContext, + ): (UpgradeCap, TreasuryCap, CoinMetadata) { + let otw = create_one_time_witness(); + let (treasury_cap, metadata) = create_currency( + otw, + 8, + b"eth", + b"eth", + b"bridge ethereum token", + option::none(), + ctx, + ); + + let type_name = type_name::get(); + let address_bytes = hex::decode( + ascii::into_bytes(type_name::get_address(&type_name)), + ); + let coin_id = address::from_bytes(address_bytes).to_id(); + let upgrade_cap = test_publish(coin_id, ctx); + + (upgrade_cap, treasury_cap, metadata) + } +} + +#[test_only] +module bridge::usdc { + use std::ascii; + use std::type_name; + use sui::address; + use sui::coin::{CoinMetadata, TreasuryCap, create_currency}; + use sui::hex; + use sui::package::{UpgradeCap, test_publish}; + use sui::test_utils::create_one_time_witness; + + public struct USDC has drop {} + + public fun create_bridge_token( + ctx: &mut TxContext, + ): (UpgradeCap, TreasuryCap, CoinMetadata) { + let otw = create_one_time_witness(); + let (treasury_cap, metadata) = create_currency( + otw, + 6, + b"usdc", + b"usdc", + b"bridge usdc token", + option::none(), + ctx, + ); + + let type_name = type_name::get(); + let address_bytes = hex::decode( + ascii::into_bytes(type_name::get_address(&type_name)), + ); + let coin_id = address::from_bytes(address_bytes).to_id(); + let upgrade_cap = test_publish(coin_id, ctx); + + (upgrade_cap, treasury_cap, metadata) + } +} + +#[test_only] +module bridge::usdt { + use std::ascii; + use std::type_name; + use sui::address; + use sui::coin::{CoinMetadata, TreasuryCap, create_currency}; + use sui::hex; + use sui::package::{UpgradeCap, test_publish}; + use sui::test_utils::create_one_time_witness; + + public struct USDT has drop {} + + public fun create_bridge_token( + ctx: &mut TxContext, + ): (UpgradeCap, TreasuryCap, CoinMetadata) { + let otw = create_one_time_witness(); + let (treasury_cap, metadata) = create_currency( + otw, + 6, + b"usdt", + b"usdt", + b"bridge usdt token", + option::none(), + ctx, + ); + + let type_name = type_name::get(); + let address_bytes = hex::decode( + ascii::into_bytes(type_name::get_address(&type_name)), + ); + let coin_id = address::from_bytes(address_bytes).to_id(); + let upgrade_cap = test_publish(coin_id, ctx); + + (upgrade_cap, treasury_cap, metadata) + } +} diff --git a/crates/sui-framework/packages/bridge/tests/bridge_setup.move b/crates/sui-framework/packages/bridge/tests/bridge_setup.move deleted file mode 100644 index 4652bb058bbd9..0000000000000 --- a/crates/sui-framework/packages/bridge/tests/bridge_setup.move +++ /dev/null @@ -1,800 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -#[test_only] -module bridge::bridge_setup { - use bridge::bridge::{ - assert_not_paused, assert_paused, create_bridge_for_testing, inner_token_transfer_records, - test_execute_add_tokens_on_sui, test_execute_emergency_op, test_init_bridge_committee, - test_load_inner_mut, Bridge, - }; - use bridge::btc::{Self, BTC}; - use bridge::eth::{Self, ETH}; - use bridge::message::{ - Self, create_add_tokens_on_sui_message, create_blocklist_message, emergency_op_pause, - emergency_op_unpause, - }; - use bridge::message_types; - use bridge::test_token::TEST_TOKEN; - use bridge::usdc::{Self, USDC}; - use bridge::usdt::{Self, USDT}; - use std::{ascii::String, type_name}; - use sui::coin::{Self, Coin, CoinMetadata, TreasuryCap}; - use sui::hex; - use sui::package::UpgradeCap; - use sui::test_scenario::{Self, Scenario}; - use sui::test_utils::destroy; - use sui_system::{ - governance_test_utils::{ - advance_epoch_with_reward_amounts, - create_sui_system_state_for_testing, - create_validator_for_testing, - }, - sui_system::{ - validator_voting_powers_for_testing, - SuiSystemState, - }, - }; - - // - // Token IDs - // - const BTC_ID: u8 = 1; - const ETH_ID: u8 = 2; - const USDC_ID: u8 = 3; - const USDT_ID: u8 = 4; - const TEST_TOKEN_ID: u8 = 5; - - public fun btc_id(): u8 { - BTC_ID - } - - public fun eth_id(): u8 { - ETH_ID - } - - public fun usdc_id(): u8 { - USDC_ID - } - - public fun usdt_id(): u8 { - USDT_ID - } - - public fun test_token_id(): u8 { - TEST_TOKEN_ID - } - - // - // Validators setup and info - // - - const VALIDATOR1_PUBKEY: vector = b"029bef8d556d80e43ae7e0becb3a7e6838b95defe45896ed6075bb9035d06c9964"; - const VALIDATOR2_PUBKEY: vector = b"033e99a541db69bd32040dfe5037fbf5210dafa8151a71e21c5204b05d95ce0a62"; - const VALIDATOR3_PUBKEY: vector = b"033e99a541db69bd32040dfe5037fbf5210dafa8151a71e21c5204b05d95ce0a63"; - - // Bridge environemnt - public struct BridgeEnv { - scenario: Scenario, - chain_id: u8, - seq_num: u64, - vault: Vault, - } - - public struct Vault { - btc_coins: Coin, - eth_coins: Coin, - usdc_coins: Coin, - usdt_coins: Coin, - test_coins: Coin, - } - - // Info to set up a validator - public struct ValidatorInfo has copy, drop { - validator: address, - stake_amount: u64, - } - - // HotPotato to access the Bridge - public struct BridgeWrapper { - bridge: Bridge, - } - - // - // Public functions - // - - // - // Environment creation and destruction - // - - public fun create_env(chain_id: u8, start_addr: address): BridgeEnv { - let mut scenario = test_scenario::begin(start_addr); - let ctx = scenario.ctx(); - let btc_coins = coin::zero(ctx); - let eth_coins = coin::zero(ctx); - let usdc_coins = coin::zero(ctx); - let usdt_coins = coin::zero(ctx); - let test_coins = coin::zero(ctx); - let vault = Vault { - btc_coins, - eth_coins, - usdc_coins, - usdt_coins, - test_coins, - }; - BridgeEnv { - scenario, - chain_id, - seq_num: 0, - vault, - } - } - - public fun destroy_env(env: BridgeEnv) { - let BridgeEnv {scenario, chain_id: _, seq_num: _, vault} = env; - destroy_valut(vault); - scenario.end(); - } - - public fun create_validator_info(validator: address, stake_amount: u64): ValidatorInfo { - ValidatorInfo { - validator, - stake_amount, - } - } - - // - // Add a set of validators to the chain. - // Call only once in a test scenario. - public fun setup_validators( - env: &mut BridgeEnv, - validators_info: vector, - sender: address, - ) { - let scenario = &mut env.scenario; - scenario.next_tx(sender); - let ctx = scenario.ctx(); - let mut validators = vector::empty(); - let mut count = validators_info.length(); - while (count > 0) { - count = count - 1; - validators.push_back(create_validator_for_testing( - validators_info[count].validator, - validators_info[count].stake_amount, - ctx, - )); - }; - create_sui_system_state_for_testing(validators, 0, 0, ctx); - advance_epoch_with_reward_amounts(0, 0, scenario); - } - - // - // Bridge creation and setup - // - - // Set up an environment with 3 validators, a bridge with - // a treasury and a committee with all 3 validators. - // The treasury will contain 4 tokens: ETH, BTC, USDT, USDC. - // Save the Bridge as a shared object. - public fun create_bridge_default(env: &mut BridgeEnv) { - let validators = vector[ - ValidatorInfo { validator: @0xA, stake_amount: 100 }, - ValidatorInfo { validator: @0xB, stake_amount: 100 }, - ValidatorInfo { validator: @0xC, stake_amount: 100 }, - ]; - let sender = @0x0; - env.setup_validators(validators, sender); - env.create_bridge(sender); - env.register_committee(); - env.init_committee(sender); - } - - // Create a bridge and set up a treasury. - // The treasury will contain 4 tokens: ETH, BTC, USDT, USDC. - // Save the Bridge as a shared object. - // No operation on the validators. - public fun create_bridge(env: &mut BridgeEnv, sender: address) { - env.scenario.next_tx(sender); - let ctx = env.scenario.ctx(); - create_bridge_for_testing(object::new(ctx), env.chain_id, ctx); - env.setup_treasury(sender); - } - - // Register 3 committee members (validators `@0xA`, `@0xB`, `@0xC`) - public fun register_committee(env: &mut BridgeEnv) { - let scenario = &mut env.scenario; - scenario.next_tx(@0x0); - let mut bridge = scenario.take_shared(); - let mut system_state = test_scenario::take_shared(scenario); - - // register committee member `@0xA` - scenario.next_tx(@0xA); - bridge.committee_registration( - &mut system_state, - hex::decode(VALIDATOR1_PUBKEY), - b"", - scenario.ctx(), - ); - - // register committee member `@0xB` - scenario.next_tx(@0xB); - bridge.committee_registration( - &mut system_state, - hex::decode(VALIDATOR2_PUBKEY), - b"", - scenario.ctx(), - ); - - // register committee member `@0xC` - scenario.next_tx(@0xC); - bridge.committee_registration( - &mut system_state, - hex::decode(VALIDATOR3_PUBKEY), - b"", - scenario.ctx(), - ); - - test_scenario::return_shared(bridge); - test_scenario::return_shared(system_state); - } - - // Init the bridge committee - public fun init_committee(env: &mut BridgeEnv, sender: address) { - let scenario = &mut env.scenario; - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - let mut system_state = test_scenario::take_shared(scenario); - let voting_powers = validator_voting_powers_for_testing(&mut system_state); - bridge.test_init_bridge_committee( - voting_powers, - 50, - scenario.ctx(), - ); - test_scenario::return_shared(bridge); - test_scenario::return_shared(system_state); - } - - // Set up a treasury with 4 tokens: ETH, BTC, USDT, USDC. - public fun setup_treasury(env: &mut BridgeEnv, sender: address) { - env.register_default_tokens(sender); - env.add_default_tokens(sender); - env.load_vault(sender); - } - - // Register 4 tokens with the Bridge: ETH, BTC, USDT, USDC. - public fun register_default_tokens(env: &mut BridgeEnv, sender: address) { - env.scenario.next_tx(sender); - let mut bridge = env.scenario.take_shared(); - - // BTC - let (upgrade_cap, treasury_cap, metadata) = - btc::create_bridge_token(env.scenario.ctx()); - bridge.register_foreign_token( - treasury_cap, - upgrade_cap, - &metadata, - ); - destroy(metadata); - // ETH - let (upgrade_cap, treasury_cap, metadata) = - eth::create_bridge_token(env.scenario.ctx()); - bridge.register_foreign_token( - treasury_cap, - upgrade_cap, - &metadata, - ); - destroy(metadata); - // USDC - let (upgrade_cap, treasury_cap, metadata) = - usdc::create_bridge_token(env.scenario.ctx()); - bridge.register_foreign_token( - treasury_cap, - upgrade_cap, - &metadata, - ); - destroy(metadata); - // USDT - let (upgrade_cap, treasury_cap, metadata) = - usdt::create_bridge_token(env.scenario.ctx()); - bridge.register_foreign_token( - treasury_cap, - upgrade_cap, - &metadata, - ); - destroy(metadata); - - test_scenario::return_shared(bridge); - } - - // Add the 4 tokens previously registered: ETH, BTC, USDT, USDC. - public fun add_default_tokens(env: &mut BridgeEnv, sender: address) { - let scenario = &mut env.scenario; - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - - let add_token_message = create_add_tokens_on_sui_message( - env.chain_id, - env.seq_num(), - false, // native_token - vector[BTC_ID, ETH_ID, USDC_ID, USDT_ID], - vector[ - type_name::get().into_string(), - type_name::get().into_string(), - type_name::get().into_string(), - type_name::get().into_string(), - ], - vector[1000, 100, 1, 1], - ); - let payload = add_token_message.extract_add_tokens_on_sui(); - bridge.test_execute_add_tokens_on_sui(payload); - - test_scenario::return_shared(bridge); - } - - // Add the 4 tokens previously registered: ETH, BTC, USDT, USDC. - public fun add_tokens( - env: &mut BridgeEnv, - sender: address, - native_token: bool, - token_ids: vector, - type_names: vector, - token_prices: vector, - ) { - let scenario = &mut env.scenario; - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - - let add_token_message = create_add_tokens_on_sui_message( - env.chain_id, - env.seq_num(), - native_token, - token_ids, - type_names, - token_prices, - ); - let payload = add_token_message.extract_add_tokens_on_sui(); - bridge.test_execute_add_tokens_on_sui(payload); - - test_scenario::return_shared(bridge); - } - - public fun update_asset_price( - env: &mut BridgeEnv, - sender: address, - token_id: u8, - value:u64, - ) { - let scenario = &mut env.scenario; - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - let inner = bridge.test_load_inner_mut(); - - let msg = message::create_update_asset_price_message( - token_id, - env.chain_id, - env.seq_num(), - value, - ); - let payload = msg.extract_update_asset_price(); - inner.test_execute_update_asset_price(payload); - - test_scenario::return_shared(bridge); - } - - // - // Getters - // - - public fun validator_pubkeys(): vector> { - vector[ - VALIDATOR1_PUBKEY, - VALIDATOR2_PUBKEY, - VALIDATOR3_PUBKEY, - ] - } - - public fun ctx(env: &mut BridgeEnv): &mut TxContext { - env.scenario.ctx() - } - - public fun scenario(env: &mut BridgeEnv): &mut Scenario { - &mut env.scenario - } - - public fun bridge(env: &mut BridgeEnv, sender: address): BridgeWrapper { - let scenario = &mut env.scenario; - scenario.next_tx(sender); - let bridge = scenario.take_shared(); - BridgeWrapper { bridge } - } - - public fun bridge_ref(wrapper: &BridgeWrapper): &Bridge { - &wrapper.bridge - } - - public fun return_bridge(bridge: BridgeWrapper) { - let BridgeWrapper { bridge } = bridge; - test_scenario::return_shared(bridge); - } - - public fun get_btc(env: &mut BridgeEnv, amount: u64): Coin { - let scenario = &mut env.scenario; - let ctx = scenario.ctx(); - env.vault.btc_coins.split(amount, ctx) - } - - public fun get_eth(env: &mut BridgeEnv, amount: u64): Coin { - let scenario = &mut env.scenario; - let ctx = scenario.ctx(); - env.vault.eth_coins.split(amount, ctx) - } - - public fun get_usdc(env: &mut BridgeEnv, amount: u64): Coin { - let scenario = &mut env.scenario; - let ctx = scenario.ctx(); - env.vault.usdc_coins.split(amount, ctx) - } - - public fun get_usdt(env: &mut BridgeEnv, amount: u64): Coin { - let scenario = &mut env.scenario; - let ctx = scenario.ctx(); - env.vault.usdt_coins.split(amount, ctx) - } - - // - // Bridge commands - // - - // Register new tokens - public fun register_foreign_token( - env: &mut BridgeEnv, - treasury_cap: TreasuryCap, - upgrade_cap: UpgradeCap, - metadata: CoinMetadata, - sender: address, - ) { - let scenario = env.scenario(); - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - bridge.register_foreign_token(treasury_cap, upgrade_cap, &metadata); - - // assert changes in bridge - let type_name = type_name::get(); - let inner = bridge.test_load_inner(); - let treasury = inner.inner_treasury(); - let waiting_room = treasury.waiting_room(); - assert!(waiting_room.contains(type_name::into_string(type_name))); - let treasuries = treasury.treasuries(); - assert!(treasuries.contains(type_name)); - - test_scenario::return_shared(bridge); - destroy(metadata); - } - - // Freeze the bridge - public fun freeze_bridge(env: &mut BridgeEnv, sender: address, error: u64) { - let scenario = env.scenario(); - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - let inner = bridge.test_load_inner_mut(); - let msg = message::create_emergency_op_message(env.chain_id, 0, emergency_op_pause()); - let payload = msg.extract_emergency_op_payload(); - inner.test_execute_emergency_op(payload); - inner.assert_paused(error); - test_scenario::return_shared(bridge); - } - - // Unfreeze the bridge - public fun unfreeze_bridge(env: &mut BridgeEnv, sender: address, error: u64) { - let scenario = env.scenario(); - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - let inner = bridge.test_load_inner_mut(); - let msg = message::create_emergency_op_message(env.chain_id, 1, emergency_op_unpause()); - let payload = msg.extract_emergency_op_payload(); - inner.test_execute_emergency_op(payload); - inner.assert_not_paused(error); - test_scenario::return_shared(bridge); - } - - public fun send_token( - env: &mut BridgeEnv, - target_chain_id: u8, - eth_address: vector, - coin: Coin, - ) { - let scenario = env.scenario(); - scenario.next_tx(@0xAAAA); - let mut bridge = scenario.take_shared(); - let coin_value = coin.value(); - let total_supply_before = get_total_supply(&bridge); - - bridge.send_token(target_chain_id, eth_address, coin, scenario.ctx()); - - assert!(total_supply_before - coin_value == get_total_supply(&bridge)); - - let inner = bridge.test_load_inner(); - let transfer_record = inner.inner_token_transfer_records(); - let seq_num = inner.sequence_nums()[&message_types::token()] - 1; - let key = message::create_key(env.chain_id, message_types::token(), seq_num); - assert!(transfer_record.contains(key)); - - test_scenario::return_shared(bridge); - } - - public fun execute_blocklist( - env: &mut BridgeEnv, - sender: address, - chain_id: u8, - blocklist_type: u8, - validator_ecdsa_addresses: vector>, - signatures: vector>, - ) { - let scenario = env.scenario(); - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - let blocklist = create_blocklist_message( - chain_id, - env.seq_num(), - blocklist_type, - validator_ecdsa_addresses, - ); - bridge.execute_system_message(blocklist, signatures); - test_scenario::return_shared(bridge); - } - - public fun update_bridge_limit( - env: &mut BridgeEnv, - sender: address, - receiving_chain: u8, - sending_chain: u8, - limit: u64, - ) { - let scenario = env.scenario(); - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - let msg = message::create_update_bridge_limit_message( - receiving_chain, - env.seq_num(), - sending_chain, - limit, - ); - let payload = msg.extract_update_bridge_limit(); - bridge.test_load_inner_mut().test_execute_update_bridge_limit(payload); - test_scenario::return_shared(bridge); - } - - // - // Internal functions - // - - fun seq_num(env: &mut BridgeEnv): u64 { - let seq_num = env.seq_num; - env.seq_num = seq_num + 1; - seq_num - } - - // Destroy the vault - fun destroy_valut(vault: Vault) { - let Vault { - btc_coins, - eth_coins, - usdc_coins, - usdt_coins, - test_coins, - } = vault; - btc_coins.burn_for_testing(); - eth_coins.burn_for_testing(); - usdc_coins.burn_for_testing(); - usdt_coins.burn_for_testing(); - test_coins.burn_for_testing(); - } - - // Load the vault with some coins - fun load_vault(env: &mut BridgeEnv, sender: address) { - let scenario = &mut env.scenario; - scenario.next_tx(sender); - let mut bridge = scenario.take_shared(); - let vault = &mut env.vault; - vault.btc_coins.join(mint_some(&mut bridge, scenario.ctx())); - vault.eth_coins.join(mint_some(&mut bridge, scenario.ctx())); - vault.usdc_coins.join(mint_some(&mut bridge, scenario.ctx())); - vault.usdt_coins.join(mint_some(&mut bridge, scenario.ctx())); - test_scenario::return_shared(bridge); - } - - // Mint some coins - fun mint_some(bridge: &mut Bridge, ctx: &mut TxContext): Coin { - let treasury = bridge.test_load_inner_mut().inner_treasury_mut(); - let coin = treasury.mint(1_000_000, ctx); - coin - } - - fun get_total_supply(bridge: &Bridge): u64 { - let inner = bridge.test_load_inner(); - let treasury = inner.inner_treasury(); - let treasuries = treasury.treasuries(); - let tc: &TreasuryCap = &treasuries[type_name::get()]; - tc.total_supply() - } -} - -// -// Test Coins -// - -#[test_only] -module bridge::test_token { - use std::{ascii, type_name}; - use sui::address; - use sui::coin::{CoinMetadata, TreasuryCap, create_currency}; - use sui::hex; - use sui::package::{UpgradeCap, test_publish}; - use sui::test_utils::create_one_time_witness; - - - public struct TEST_TOKEN has drop {} - - public fun create_bridge_token( - ctx: &mut TxContext, - ): (UpgradeCap, TreasuryCap, CoinMetadata) { - let otw = create_one_time_witness(); - let (treasury_cap, metadata) = create_currency( - otw, - 8, - b"tst", - b"test", - b"bridge test token", - option::none(), - ctx, - ); - - let type_name = type_name::get(); - let address_bytes = hex::decode(ascii::into_bytes(type_name::get_address(&type_name))); - let coin_id = address::from_bytes(address_bytes).to_id(); - let upgrade_cap = test_publish(coin_id, ctx); - - (upgrade_cap, treasury_cap, metadata) - } -} - -#[test_only] -module bridge::btc { - use std::{ascii, type_name}; - use sui::address; - use sui::coin::{CoinMetadata, TreasuryCap, create_currency}; - use sui::hex; - use sui::package::{UpgradeCap, test_publish}; - use sui::test_utils::create_one_time_witness; - - - public struct BTC has drop {} - - public fun create_bridge_token( - ctx: &mut TxContext, - ): (UpgradeCap, TreasuryCap, CoinMetadata) { - let otw = create_one_time_witness(); - let (treasury_cap, metadata) = create_currency( - otw, - 8, - b"btc", - b"bitcoin", - b"bridge bitcoin token", - option::none(), - ctx, - ); - - let type_name = type_name::get(); - let address_bytes = hex::decode(ascii::into_bytes(type_name::get_address(&type_name))); - let coin_id = address::from_bytes(address_bytes).to_id(); - let upgrade_cap = test_publish(coin_id, ctx); - - (upgrade_cap, treasury_cap, metadata) - } -} - -#[test_only] -module bridge::eth { - use std::{ascii, type_name}; - use sui::address; - use sui::coin::{CoinMetadata, TreasuryCap, create_currency}; - use sui::hex; - use sui::package::{UpgradeCap, test_publish}; - use sui::test_utils::create_one_time_witness; - - - public struct ETH has drop {} - - public fun create_bridge_token( - ctx: &mut TxContext, - ): (UpgradeCap, TreasuryCap, CoinMetadata) { - let otw = create_one_time_witness(); - let (treasury_cap, metadata) = create_currency( - otw, - 8, - b"eth", - b"eth", - b"bridge ethereum token", - option::none(), - ctx, - ); - - let type_name = type_name::get(); - let address_bytes = hex::decode(ascii::into_bytes(type_name::get_address(&type_name))); - let coin_id = address::from_bytes(address_bytes).to_id(); - let upgrade_cap = test_publish(coin_id, ctx); - - (upgrade_cap, treasury_cap, metadata) - } -} - -#[test_only] -module bridge::usdc { - use std::{ascii, type_name}; - use sui::address; - use sui::coin::{CoinMetadata, TreasuryCap, create_currency}; - use sui::hex; - use sui::package::{UpgradeCap, test_publish}; - use sui::test_utils::create_one_time_witness; - - - public struct USDC has drop {} - - public fun create_bridge_token( - ctx: &mut TxContext, - ): (UpgradeCap, TreasuryCap, CoinMetadata) { - let otw = create_one_time_witness(); - let (treasury_cap, metadata) = create_currency( - otw, - 6, - b"usdc", - b"usdc", - b"bridge usdc token", - option::none(), - ctx, - ); - - let type_name = type_name::get(); - let address_bytes = hex::decode(ascii::into_bytes(type_name::get_address(&type_name))); - let coin_id = address::from_bytes(address_bytes).to_id(); - let upgrade_cap = test_publish(coin_id, ctx); - - (upgrade_cap, treasury_cap, metadata) - } -} - -#[test_only] -module bridge::usdt { - use std::{ascii, type_name}; - use sui::address; - use sui::coin::{CoinMetadata, TreasuryCap, create_currency}; - use sui::hex; - use sui::package::{UpgradeCap, test_publish}; - use sui::test_utils::create_one_time_witness; - - - public struct USDT has drop {} - - public fun create_bridge_token( - ctx: &mut TxContext, - ): (UpgradeCap, TreasuryCap, CoinMetadata) { - let otw = create_one_time_witness(); - let (treasury_cap, metadata) = create_currency( - otw, - 6, - b"usdt", - b"usdt", - b"bridge usdt token", - option::none(), - ctx, - ); - - let type_name = type_name::get(); - let address_bytes = hex::decode(ascii::into_bytes(type_name::get_address(&type_name))); - let coin_id = address::from_bytes(address_bytes).to_id(); - let upgrade_cap = test_publish(coin_id, ctx); - - (upgrade_cap, treasury_cap, metadata) - } -} diff --git a/crates/sui-framework/packages/bridge/tests/bridge_tests.move b/crates/sui-framework/packages/bridge/tests/bridge_tests.move index 3c416c33fb24f..d26b5c919a9a2 100644 --- a/crates/sui-framework/packages/bridge/tests/bridge_tests.move +++ b/crates/sui-framework/packages/bridge/tests/bridge_tests.move @@ -2,652 +2,761 @@ // SPDX-License-Identifier: Apache-2.0 #[test_only] -module bridge::bridge_tests { - use bridge::bridge::{ - inner_limiter, inner_paused, inner_treasury, inner_token_transfer_records_mut, - new_bridge_record_for_testing, new_for_testing, test_get_current_seq_num_and_increment, - test_execute_update_asset_price, test_get_token_transfer_action_signatures, - test_load_inner, test_load_inner_mut, test_get_token_transfer_action_status, - transfer_status_approved, transfer_status_claimed, transfer_status_not_found, - transfer_status_pending, Bridge, - }; - use bridge::bridge_setup::{ - btc_id, create_bridge, create_bridge_default, create_env, create_validator_info, - eth_id, freeze_bridge, init_committee, register_committee, unfreeze_bridge, test_token_id, - }; - use bridge::btc::BTC; - use bridge::chain_ids; - use bridge::eth::ETH; - use bridge::message::{Self, to_parsed_token_transfer_message}; - use bridge::message_types; - use bridge::test_token::{TEST_TOKEN, create_bridge_token as create_test_token}; - use bridge::usdc::USDC; - use std::type_name; - use sui::address; - use sui::balance; - use sui::coin::{Self, Coin}; - use sui::hex; - use sui::package::test_publish; - use sui::test_scenario::Self; - use sui::test_utils::destroy; - - // common error start code for unexpected errors in tests (assertions). - // If more than one assert in a test needs to use an unexpected error code, - // use this as the starting error and add 1 to subsequent errors - const UNEXPECTED_ERROR: u64 = 10293847; - // use on tests that fail to save cleanup - const TEST_DONE: u64 = 74839201; - - #[test] - fun test_bridge_create() { - let mut env = create_env(chain_ids::sui_testnet(), @0x0); - env.create_bridge(@0x0); - - let bridge = env.bridge(@0x0); - let inner = bridge.bridge_ref().test_load_inner(); - inner.assert_not_paused(UNEXPECTED_ERROR); - assert!(inner.inner_token_transfer_records().length() == 0); - bridge.return_bridge(); - - env.destroy_env(); - } - - #[test] - #[expected_failure(abort_code = bridge::bridge::ENotSystemAddress)] - fun test_bridge_create_non_system_addr() { - let mut env = create_env(chain_ids::sui_mainnet(), @0x1); - env.create_bridge(@0x1); - - abort TEST_DONE - } - - #[test] - fun test_init_committee() { - let mut env = create_env(chain_ids::sui_custom(), @0x0); - env.create_bridge_default(); - env.destroy_env(); - } - - #[test] - fun test_init_committee_twice() { - let mut env = create_env(chain_ids::sui_testnet(), @0x0); - env.create_bridge_default(); - env.init_committee(@0x0); // second time is a no-op - - env.destroy_env(); - } - - #[test] - #[expected_failure(abort_code = bridge::bridge::ENotSystemAddress)] - fun test_init_committee_non_system_addr() { - let mut env = create_env(chain_ids::sui_mainnet(), @0x0); - // TODO: this is too brittle, fix it - env.setup_validators( - vector[ - create_validator_info(@0xA, 100), - create_validator_info(@0xB, 100), - create_validator_info(@0xC, 100), - ], - @0x0, - ); - env.create_bridge(@0x0); - env.register_committee(); - env.init_committee(@0xA); - - abort TEST_DONE - } - - #[test] - #[expected_failure(abort_code = bridge::committee::ECommitteeAlreadyInitiated)] - fun test_register_committee_after_init() { - let mut env = create_env(chain_ids::sui_custom(), @0x0); - env.create_bridge_default(); - env.register_committee(); - - abort TEST_DONE - } - - #[test] - fun test_register_foreign_token() { - let addr = @0x0; - let mut env = create_env(chain_ids::sui_testnet(), addr); - env.create_bridge_default(); - let (upgrade_cap, treasury_cap, metadata) = create_test_token(env.scenario().ctx()); - env.register_foreign_token(treasury_cap, upgrade_cap, metadata, addr); - env.destroy_env(); - } - - #[test] - #[expected_failure(abort_code = bridge::treasury::ETokenSupplyNonZero)] - fun test_register_foreign_token_non_zero_supply() { - let addr = @0x0; - let mut env = create_env(chain_ids::sui_testnet(), addr); - env.create_bridge_default(); - let (upgrade_cap, mut treasury_cap, metadata) = create_test_token(env.scenario().ctx()); - let _coin = treasury_cap.mint(1, env.scenario().ctx()); - env.register_foreign_token(treasury_cap, upgrade_cap, metadata, addr); - - abort 0 - } - - #[test] - #[expected_failure(abort_code = bridge::treasury::EInvalidNotionalValue)] - fun test_add_token_price_zero_value() { - let addr = @0x0; - let mut env = create_env(chain_ids::sui_testnet(), addr); - env.create_bridge_default(); - env.add_tokens( - addr, - false, - vector[test_token_id()], - vector[type_name::get().into_string()], - vector[0], - ); +module bridge::bridge_tests; +use bridge::bridge::{ + inner_limiter, + inner_paused, + inner_treasury, + inner_token_transfer_records_mut, + new_bridge_record_for_testing, + new_for_testing, + test_get_current_seq_num_and_increment, + test_execute_update_asset_price, + test_get_token_transfer_action_signatures, + test_load_inner, + test_load_inner_mut, + test_get_token_transfer_action_status, + transfer_status_approved, + transfer_status_claimed, + transfer_status_not_found, + transfer_status_pending, + Bridge +}; +use bridge::bridge_env::{ + btc_id, + create_bridge, + create_bridge_default, + create_env, + create_validator, + eth_id, + freeze_bridge, + init_committee, + register_committee, + unfreeze_bridge, + test_token_id +}; +use bridge::btc::BTC; +use bridge::chain_ids; +use bridge::eth::ETH; +use bridge::message::{Self, to_parsed_token_transfer_message}; +use bridge::message_types; +use bridge::test_token::{TEST_TOKEN, create_bridge_token as create_test_token}; +use bridge::usdc::USDC; +use std::type_name; +use sui::address; +use sui::balance; +use sui::coin::{Self, Coin}; +use sui::hex; +use sui::package::test_publish; +use sui::test_scenario; +use sui::test_utils::destroy; + +// common error start code for unexpected errors in tests (assertions). +// If more than one assert in a test needs to use an unexpected error code, +// use this as the starting error and add 1 to subsequent errors +const UNEXPECTED_ERROR: u64 = 10293847; +// use on tests that fail to save cleanup +const TEST_DONE: u64 = 74839201; + +#[test] +fun test_bridge_create() { + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge(@0x0); + + let bridge = env.bridge(@0x0); + let inner = bridge.bridge_ref().test_load_inner(); + inner.assert_not_paused(UNEXPECTED_ERROR); + assert!(inner.inner_token_transfer_records().length() == 0); + bridge.return_bridge(); + + env.destroy_env(); +} - abort 0 - } - - #[test] - #[expected_failure(abort_code = bridge::bridge::EMalformedMessageError)] - fun test_add_token_malformed_1() { - let addr = @0x0; - let mut env = create_env(chain_ids::sui_testnet(), addr); - env.create_bridge_default(); - env.add_tokens( - addr, - false, - vector[test_token_id(), eth_id()], - vector[type_name::get().into_string()], - vector[10], - ); +#[test] +#[expected_failure(abort_code = bridge::bridge::ENotSystemAddress)] +fun test_bridge_create_non_system_addr() { + let mut env = create_env(chain_ids::sui_mainnet()); + env.create_bridge(@0x1); - abort 0 - } - - #[test] - #[expected_failure(abort_code = bridge::bridge::EMalformedMessageError)] - fun test_add_token_malformed_2() { - let addr = @0x0; - let mut env = create_env(chain_ids::sui_testnet(), addr); - env.create_bridge_default(); - env.add_tokens( - addr, - false, - vector[test_token_id()], - vector[type_name::get().into_string(), type_name::get().into_string()], - vector[10], - ); + abort TEST_DONE +} - abort 0 - } - - #[test] - #[expected_failure(abort_code = bridge::bridge::EMalformedMessageError)] - fun test_add_token_malformed_3() { - let addr = @0x0; - let mut env = create_env(chain_ids::sui_testnet(), addr); - env.create_bridge_default(); - env.add_tokens( - addr, - false, - vector[test_token_id()], - vector[type_name::get().into_string()], - vector[10, 20], - ); +#[test] +fun test_create_bridge_default() { + let mut env = create_env(chain_ids::sui_custom()); + env.create_bridge_default(); + env.destroy_env(); +} - abort 0 - } - - #[test] - fun test_add_native_token_nop() { - // adding a native token is simply a NO-OP at the moment - let addr = @0x0; - let mut env = create_env(chain_ids::sui_testnet(), addr); - env.create_bridge_default(); - env.add_tokens( - addr, - true, - vector[test_token_id()], - vector[type_name::get().into_string()], - vector[100], - ); - env.destroy_env(); - } - - #[test] - #[expected_failure(abort_code = bridge::treasury::EInvalidUpgradeCap)] - fun test_register_foreign_token_bad_upgrade_cap() { - let addr = @0x0; - let mut env = create_env(chain_ids::sui_testnet(), addr); - env.create_bridge_default(); - let (_upgrade_cap, treasury_cap, metadata) = create_test_token(env.scenario().ctx()); - let upgrade_cap = test_publish(@0x42.to_id(), env.scenario().ctx()); - env.register_foreign_token(treasury_cap, upgrade_cap, metadata, addr); - - abort 0 - } - - #[test] - fun test_execute_send_token() { - let mut env = create_env(chain_ids::sui_testnet(), @0x0); - env.create_bridge_default(); - let btc: Coin = env.get_btc(1); - let eth_address = x"0000000000000000000000000000000000000000"; - env.send_token(chain_ids::eth_sepolia(), eth_address, btc); - env.destroy_env(); - } - - #[test] - #[expected_failure(abort_code = bridge::bridge::ETokenValueIsZero)] - fun test_execute_send_token_zero_value() { - let mut env = create_env(chain_ids::sui_testnet(), @0x0); - env.create_bridge_default(); - let btc: Coin = env.get_btc(0); - let eth_address = x"0000000000000000000000000000000000000000"; - env.send_token(chain_ids::eth_sepolia(), eth_address, btc); - - abort TEST_DONE - } - - #[test] - #[expected_failure(abort_code = bridge::bridge::EInvalidEvmAddress)] - fun test_execute_send_token_invalid_evem_address() { - let mut env = create_env(chain_ids::sui_testnet(), @0x0); - env.create_bridge_default(); - let btc: Coin = env.get_btc(1); - let eth_address = x"1234"; - env.send_token(chain_ids::eth_sepolia(), eth_address, btc); - - abort TEST_DONE - } - - #[test] - #[expected_failure(abort_code = bridge::bridge::EBridgeUnavailable)] - fun test_execute_send_token_frozen() { - let chain_id = chain_ids::sui_testnet(); - let mut env = create_env(chain_id, @0x0); - env.create_bridge_default(); - let eth: Coin = env.get_eth(1); - let eth_address = x"0000000000000000000000000000000000000000"; - env.freeze_bridge(@0x0, UNEXPECTED_ERROR); - env.send_token(chain_ids::eth_sepolia(), eth_address, eth); - - abort TEST_DONE - } - - #[test] - #[expected_failure(abort_code = bridge::bridge::EInvalidBridgeRoute)] - fun test_execute_send_token_invalid_route() { - let mut env = create_env(chain_ids::sui_testnet(), @0x0); - env.create_bridge_default(); - let usdc: Coin = env.get_usdc(100); - let eth_address = x"0000000000000000000000000000000000000000"; - env.send_token(chain_ids::eth_mainnet(), eth_address, usdc); - - abort TEST_DONE - } - - #[test] - #[expected_failure(abort_code = bridge::bridge::EUnexpectedChainID)] - fun test_system_msg_incorrect_chain_id() { - let sender = @0x0; - let mut env = create_env(chain_ids::sui_testnet(), sender); - env.create_bridge_default(); - env.execute_blocklist(sender, chain_ids::sui_mainnet(), 0, vector[], vector[]); - - abort TEST_DONE - } - - #[test] - fun test_get_seq_num_and_increment() { - let mut scenario = test_scenario::begin(@0x0); - let ctx = scenario.ctx(); - let chain_id = chain_ids::sui_testnet(); - let mut bridge = new_for_testing(chain_id, ctx); - - let inner = bridge.test_load_inner_mut(); - assert!( - inner.test_get_current_seq_num_and_increment( - message_types::committee_blocklist(), - ) == 0, - ); - assert!( - inner.sequence_nums()[&message_types::committee_blocklist()] == 1, - ); - assert!( - inner.test_get_current_seq_num_and_increment( - message_types::committee_blocklist(), - ) == 1, - ); - // other message type nonce does not change - assert!( - !inner.sequence_nums().contains(&message_types::token()), - ); - assert!( - !inner.sequence_nums().contains(&message_types::emergency_op()), - ); - assert!( - !inner.sequence_nums().contains(&message_types::update_bridge_limit()), - ); - assert!( - !inner.sequence_nums().contains(&message_types::update_asset_price()), - ); - assert!( - inner.test_get_current_seq_num_and_increment(message_types::token()) == 0, - ); - assert!( - inner.test_get_current_seq_num_and_increment( - message_types::emergency_op(), - ) == 0, - ); - assert!( - inner.test_get_current_seq_num_and_increment( - message_types::update_bridge_limit(), - ) == 0, - ); - assert!( - inner.test_get_current_seq_num_and_increment( - message_types::update_asset_price(), - ) == 0, - ); +#[test] +fun test_init_committee_twice() { + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + env.init_committee(@0x0); // second time is a no-op + + env.destroy_env(); +} + +#[test] +#[expected_failure(abort_code = bridge::bridge::ENotSystemAddress)] +fun test_init_committee_non_system_addr() { + let mut env = create_env(chain_ids::sui_mainnet()); + env.setup_validators(vector[ + create_validator(@0xA, 100, &b"12345678901234567890123456789012"), + ]); + env.create_bridge(@0x0); + env.register_committee(); + env.init_committee(@0xA); + + abort TEST_DONE +} + +#[test] +#[expected_failure(abort_code = bridge::committee::ECommitteeAlreadyInitiated)] +fun test_register_committee_after_init() { + let mut env = create_env(chain_ids::sui_custom()); + env.create_bridge_default(); + env.register_committee(); + + abort TEST_DONE +} + +#[test] +fun test_register_foreign_token() { + let addr = @0x0; + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + let (upgrade_cap, treasury_cap, metadata) = create_test_token(env + .scenario() + .ctx()); + env.register_foreign_token( + treasury_cap, + upgrade_cap, + metadata, + addr, + ); + env.destroy_env(); +} + +#[test] +#[expected_failure(abort_code = bridge::treasury::ETokenSupplyNonZero)] +fun test_register_foreign_token_non_zero_supply() { + let addr = @0x0; + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + let (upgrade_cap, mut treasury_cap, metadata) = create_test_token(env + .scenario() + .ctx()); + let _coin = treasury_cap.mint(1, env.scenario().ctx()); + env.register_foreign_token( + treasury_cap, + upgrade_cap, + metadata, + addr, + ); + + abort 0 +} + +#[test] +#[expected_failure(abort_code = bridge::treasury::EInvalidNotionalValue)] +fun test_add_token_price_zero_value() { + let addr = @0x0; + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + env.add_tokens( + addr, + false, + vector[test_token_id()], + vector[type_name::get().into_string()], + vector[0], + ); + + abort 0 +} + +#[test] +#[expected_failure(abort_code = bridge::bridge::EMalformedMessageError)] +fun test_add_token_malformed_1() { + let addr = @0x0; + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + env.add_tokens( + addr, + false, + vector[test_token_id(), eth_id()], + vector[type_name::get().into_string()], + vector[10], + ); + + abort 0 +} + +#[test] +#[expected_failure(abort_code = bridge::bridge::EMalformedMessageError)] +fun test_add_token_malformed_2() { + let addr = @0x0; + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + env.add_tokens( + addr, + false, + vector[test_token_id()], + vector[ + type_name::get().into_string(), + type_name::get().into_string(), + ], + vector[10], + ); + + abort 0 +} + +#[test] +#[expected_failure(abort_code = bridge::bridge::EMalformedMessageError)] +fun test_add_token_malformed_3() { + let addr = @0x0; + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + env.add_tokens( + addr, + false, + vector[test_token_id()], + vector[type_name::get().into_string()], + vector[10, 20], + ); + + abort 0 +} + +#[test] +fun test_add_native_token_nop() { + // adding a native token is simply a NO-OP at the moment + let addr = @0x0; + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + env.add_tokens( + addr, + true, + vector[test_token_id()], + vector[type_name::get().into_string()], + vector[100], + ); + env.destroy_env(); +} + +#[test] +#[expected_failure(abort_code = bridge::treasury::EInvalidUpgradeCap)] +fun test_register_foreign_token_bad_upgrade_cap() { + let addr = @0x0; + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + let (_upgrade_cap, treasury_cap, metadata) = create_test_token(env + .scenario() + .ctx()); + let upgrade_cap = test_publish(@0x42.to_id(), env.scenario().ctx()); + env.register_foreign_token( + treasury_cap, + upgrade_cap, + metadata, + addr, + ); + + abort 0 +} + +#[test] +fun test_execute_send_token() { + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + let btc: Coin = env.get_btc(1); + let eth_address = x"0000000000000000000000000000000000000000"; + env.send_token(@0xABCD, chain_ids::eth_sepolia(), eth_address, btc); + env.destroy_env(); +} + +#[test] +#[expected_failure(abort_code = bridge::bridge::ETokenValueIsZero)] +fun test_execute_send_token_zero_value() { + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + let btc: Coin = env.get_btc(0); + let eth_address = x"0000000000000000000000000000000000000000"; + env.send_token(@0x0, chain_ids::eth_sepolia(), eth_address, btc); + + abort TEST_DONE +} + +#[test] +#[expected_failure(abort_code = bridge::bridge::EInvalidEvmAddress)] +fun test_execute_send_token_invalid_evem_address() { + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + let btc: Coin = env.get_btc(1); + let eth_address = x"1234"; + let val_addr = env.validators()[0].addr(); + env.send_token(val_addr, chain_ids::eth_sepolia(), eth_address, btc); + + abort TEST_DONE +} + +#[test] +#[expected_failure(abort_code = bridge::bridge::EBridgeUnavailable)] +fun test_execute_send_token_frozen() { + let chain_id = chain_ids::sui_testnet(); + let mut env = create_env(chain_id); + env.create_bridge_default(); + let eth: Coin = env.get_eth(1); + let eth_address = x"0000000000000000000000000000000000000000"; + env.freeze_bridge(@0x0, UNEXPECTED_ERROR); + env.send_token(@0xAAAA, chain_ids::eth_sepolia(), eth_address, eth); + + abort TEST_DONE +} - destroy(bridge); - scenario.end(); - } - - #[test] - fun test_update_limit() { - let chain_id = chain_ids::sui_mainnet(); - let mut env = create_env(chain_id, @0x0); - env.create_bridge_default(); - - let bridge = env.bridge(@0x0); - let inner = bridge.bridge_ref().test_load_inner(); - // Assert the starting limit is a different value - assert!( - inner.inner_limiter().get_route_limit( +#[test] +#[expected_failure(abort_code = bridge::bridge::EInvalidBridgeRoute)] +fun test_execute_send_token_invalid_route() { + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + let usdc: Coin = env.get_usdc(100); + let eth_address = x"0000000000000000000000000000000000000000"; + env.send_token(@0xABCDEF, chain_ids::eth_mainnet(), eth_address, usdc); + + abort TEST_DONE +} + +#[test] +#[expected_failure(abort_code = bridge::bridge::EUnexpectedChainID)] +fun test_system_msg_incorrect_chain_id() { + let sender = @0x0; + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + env.execute_blocklist(sender, chain_ids::sui_mainnet(), 0, vector[]); + + abort TEST_DONE +} + +#[test] +fun test_get_seq_num_and_increment() { + let mut scenario = test_scenario::begin(@0x0); + let ctx = scenario.ctx(); + let chain_id = chain_ids::sui_testnet(); + let mut bridge = new_for_testing(chain_id, ctx); + + let inner = bridge.test_load_inner_mut(); + assert!( + inner.test_get_current_seq_num_and_increment( + message_types::committee_blocklist(), + ) == + 0, + ); + assert!( + inner.sequence_nums()[&message_types::committee_blocklist()] == 1, + ); + assert!( + inner.test_get_current_seq_num_and_increment( + message_types::committee_blocklist(), + ) == + 1, + ); + // other message type nonce does not change + assert!( + !inner.sequence_nums().contains(&message_types::token()), + ); + assert!( + !inner.sequence_nums().contains(&message_types::emergency_op()), + ); + assert!( + !inner.sequence_nums().contains(&message_types::update_bridge_limit()), + ); + assert!( + !inner.sequence_nums().contains(&message_types::update_asset_price()), + ); + assert!( + inner.test_get_current_seq_num_and_increment(message_types::token()) == + 0, + ); + assert!( + inner.test_get_current_seq_num_and_increment( + message_types::emergency_op(), + ) == + 0, + ); + assert!( + inner.test_get_current_seq_num_and_increment( + message_types::update_bridge_limit(), + ) == + 0, + ); + assert!( + inner.test_get_current_seq_num_and_increment( + message_types::update_asset_price(), + ) == + 0, + ); + + destroy(bridge); + scenario.end(); +} + +#[test] +fun test_update_limit() { + let chain_id = chain_ids::sui_mainnet(); + let mut env = create_env(chain_id); + env.create_bridge_default(); + + let bridge = env.bridge(@0x0); + let inner = bridge.bridge_ref().test_load_inner(); + // Assert the starting limit is a different value + assert!( + inner + .inner_limiter() + .get_route_limit( &chain_ids::get_route( chain_ids::eth_mainnet(), chain_ids::sui_mainnet(), ), - ) != 1, - ); - bridge.return_bridge(); - - // update limit - env.update_bridge_limit( - @0x0, - chain_ids::sui_mainnet(), - chain_ids::eth_mainnet(), - 1, - ); - - let bridge = env.bridge(@0x0); - let inner = bridge.bridge_ref().test_load_inner(); - // Assert the starting limit is a different value - assert!( - inner.inner_limiter().get_route_limit( + ) != + 1, + ); + bridge.return_bridge(); + + // update limit + env.update_bridge_limit( + @0x0, + chain_ids::sui_mainnet(), + chain_ids::eth_mainnet(), + 1, + ); + + let bridge = env.bridge(@0x0); + let inner = bridge.bridge_ref().test_load_inner(); + // Assert the starting limit is a different value + assert!( + inner + .inner_limiter() + .get_route_limit( &chain_ids::get_route( chain_ids::eth_mainnet(), chain_ids::sui_mainnet(), ), - ) == 1, - ); - // other routes are not impacted - assert!( - inner.inner_limiter().get_route_limit( + ) == + 1, + ); + // other routes are not impacted + assert!( + inner + .inner_limiter() + .get_route_limit( &chain_ids::get_route( chain_ids::eth_sepolia(), chain_ids::sui_testnet(), ), - ) != 1, - ); - bridge.return_bridge(); - - env.destroy_env(); - } - - #[test] - #[expected_failure(abort_code = bridge::bridge::EUnexpectedChainID)] - fun test_execute_update_bridge_limit_abort_with_unexpected_chain_id() { - let mut env = create_env(chain_ids::sui_testnet(), @0x0); - env.create_bridge_default(); - - // This abort because the receiving_chain (sui_mainnet) is not the same as - // the bridge's chain_id (sui_devnet) - env.update_bridge_limit( - @0x0, - chain_ids::sui_mainnet(), - chain_ids::eth_mainnet(), - 1, - ); + ) != + 1, + ); + bridge.return_bridge(); - abort TEST_DONE - } - - #[test] - fun test_update_asset_price() { - let mut env = create_env(chain_ids::sui_testnet(), @0x0); - env.create_bridge_default(); - let scenario = env.scenario(); - scenario.next_tx(@0x0); - let mut bridge = scenario.take_shared(); - let inner = bridge.test_load_inner_mut(); - - // Assert the starting limit is a different value - assert!( - inner.inner_treasury().notional_value() != 1_001_000_000, - ); - // now change it to 100_001_000 - let msg = message::create_update_asset_price_message( - inner.inner_treasury().token_id(), - chain_ids::sui_mainnet(), - 0, - 1_001_000_000, - ); - let payload = msg.extract_update_asset_price(); - inner.test_execute_update_asset_price(payload); - - // should be 1_001_000_000 now - assert!(inner.inner_treasury().notional_value() == 1_001_000_000); - // other assets are not impacted - assert!(inner.inner_treasury().notional_value() != 1_001_000_000); - - destroy(bridge); - env.destroy_env(); - } - - #[test] - #[expected_failure(abort_code = bridge::treasury::EInvalidNotionalValue)] - fun test_invalid_price_update() { - let mut env = create_env(chain_ids::sui_testnet(), @0x0); - env.create_bridge_default(); - env.update_asset_price(@0x0, btc_id(), 0); - - abort 0 - } - - #[test] - #[expected_failure(abort_code = bridge::treasury::EUnsupportedTokenType)] - fun test_unsupported_token_type() { - let mut env = create_env(chain_ids::sui_testnet(), @0x0); - env.create_bridge_default(); - env.update_asset_price(@0x0, 42, 100); - - abort 0 - } - - #[test] - fun test_execute_freeze_unfreeze() { - let chain_id = chain_ids::sui_testnet(); - let mut env = create_env(chain_id, @0x0); - env.create_bridge_default(); - env.freeze_bridge(@0x0, UNEXPECTED_ERROR + 1); - let bridge = env.bridge(@0x0); - assert!(bridge.bridge_ref().test_load_inner().inner_paused()); - bridge.return_bridge(); - env.unfreeze_bridge(@0x0, UNEXPECTED_ERROR + 2); - let bridge = env.bridge(@0x0); - assert!(!bridge.bridge_ref().test_load_inner().inner_paused()); - bridge.return_bridge(); - env.destroy_env(); - } - - #[test] - #[expected_failure(abort_code = bridge::bridge::EBridgeNotPaused)] - fun test_execute_unfreeze_err() { - let chain_id = chain_ids::sui_testnet(); - let mut env = create_env(chain_id, @0x0); - env.create_bridge_default(); - let bridge = env.bridge(@0x0); - assert!(!bridge.bridge_ref().test_load_inner().inner_paused()); - bridge.return_bridge(); - env.unfreeze_bridge(@0x0, UNEXPECTED_ERROR + 2); - - abort TEST_DONE - } - - #[test] - #[expected_failure(abort_code = bridge::bridge::EBridgeAlreadyPaused)] - fun test_execute_emergency_op_abort_when_already_frozen() { - let chain_id = chain_ids::sui_testnet(); - let mut env = create_env(chain_id, @0x0); - env.create_bridge_default(); - - // initially it's unfrozen - let bridge = env.bridge(@0x0); - assert!(!bridge.bridge_ref().test_load_inner().inner_paused()); - bridge.return_bridge(); - // freeze it - env.freeze_bridge(@0x0, UNEXPECTED_ERROR); - let bridge = env.bridge(@0x0); - assert!(bridge.bridge_ref().test_load_inner().inner_paused()); - bridge.return_bridge(); - // freeze it again, should abort - env.freeze_bridge(@0x0, UNEXPECTED_ERROR); - - abort TEST_DONE - } - - #[test] - fun test_get_token_transfer_action_data() { - let mut scenario = test_scenario::begin(@0x0); - let ctx = scenario.ctx(); - let chain_id = chain_ids::sui_testnet(); - let mut bridge = new_for_testing(chain_id, ctx); - let coin = coin::mint_for_testing(12345, ctx); - - // Test when pending - let message = message::create_token_bridge_message( - chain_ids::sui_testnet(), // source chain - 10, // seq_num - address::to_bytes(ctx.sender()), // sender address - chain_ids::eth_sepolia(), // target_chain - hex::decode(b"00000000000000000000000000000000000000c8"), // target_address - 1u8, // token_type - coin.balance().value(), - ); + env.destroy_env(); +} + +#[test] +#[expected_failure(abort_code = bridge::bridge::EUnexpectedChainID)] +fun test_execute_update_bridge_limit_abort_with_unexpected_chain_id() { + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + + // This abort because the receiving_chain (sui_mainnet) is not the same as + // the bridge's chain_id (sui_devnet) + env.update_bridge_limit( + @0x0, + chain_ids::sui_mainnet(), + chain_ids::eth_mainnet(), + 1, + ); + + abort TEST_DONE +} + +#[test] +fun test_update_asset_price() { + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + let scenario = env.scenario(); + scenario.next_tx(@0x0); + let mut bridge = scenario.take_shared(); + let inner = bridge.test_load_inner_mut(); + + // Assert the starting limit is a different value + assert!( + inner.inner_treasury().notional_value() != 1_001_000_000, + ); + // now change it to 100_001_000 + let msg = message::create_update_asset_price_message( + inner.inner_treasury().token_id(), + chain_ids::sui_mainnet(), + 0, + 1_001_000_000, + ); + let payload = msg.extract_update_asset_price(); + inner.test_execute_update_asset_price(payload); + + // should be 1_001_000_000 now + assert!(inner.inner_treasury().notional_value() == 1_001_000_000); + // other assets are not impacted + assert!(inner.inner_treasury().notional_value() != 1_001_000_000); + + destroy(bridge); + env.destroy_env(); +} + +#[test] +#[expected_failure(abort_code = bridge::treasury::EInvalidNotionalValue)] +fun test_invalid_price_update() { + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + env.update_asset_price(@0x0, btc_id(), 0); + + abort 0 +} + +#[test] +#[expected_failure(abort_code = bridge::treasury::EUnsupportedTokenType)] +fun test_unsupported_token_type() { + let mut env = create_env(chain_ids::sui_testnet()); + env.create_bridge_default(); + env.update_asset_price(@0x0, 42, 100); - let key = message.key(); - bridge.test_load_inner_mut().inner_token_transfer_records_mut().push_back( + abort 0 +} + +#[test] +fun test_execute_freeze_unfreeze() { + let chain_id = chain_ids::sui_testnet(); + let mut env = create_env(chain_id); + env.create_bridge_default(); + env.freeze_bridge(@0x0, UNEXPECTED_ERROR + 1); + let bridge = env.bridge(@0x0); + assert!(bridge.bridge_ref().test_load_inner().inner_paused()); + bridge.return_bridge(); + env.unfreeze_bridge(@0x0, UNEXPECTED_ERROR + 2); + let bridge = env.bridge(@0x0); + assert!(!bridge.bridge_ref().test_load_inner().inner_paused()); + bridge.return_bridge(); + env.destroy_env(); +} + +#[test] +#[expected_failure(abort_code = bridge::bridge::EBridgeNotPaused)] +fun test_execute_unfreeze_err() { + let chain_id = chain_ids::sui_testnet(); + let mut env = create_env(chain_id); + env.create_bridge_default(); + let bridge = env.bridge(@0x0); + assert!(!bridge.bridge_ref().test_load_inner().inner_paused()); + bridge.return_bridge(); + env.unfreeze_bridge(@0x0, UNEXPECTED_ERROR + 2); + + abort TEST_DONE +} + +#[test] +#[expected_failure(abort_code = bridge::bridge::EBridgeAlreadyPaused)] +fun test_execute_emergency_op_abort_when_already_frozen() { + let chain_id = chain_ids::sui_testnet(); + let mut env = create_env(chain_id); + env.create_bridge_default(); + + // initially it's unfrozen + let bridge = env.bridge(@0x0); + assert!(!bridge.bridge_ref().test_load_inner().inner_paused()); + bridge.return_bridge(); + // freeze it + env.freeze_bridge(@0x0, UNEXPECTED_ERROR); + let bridge = env.bridge(@0x0); + assert!(bridge.bridge_ref().test_load_inner().inner_paused()); + bridge.return_bridge(); + // freeze it again, should abort + env.freeze_bridge(@0x0, UNEXPECTED_ERROR); + + abort TEST_DONE +} + +#[test] +fun test_get_token_transfer_action_data() { + let mut scenario = test_scenario::begin(@0x0); + let ctx = scenario.ctx(); + let chain_id = chain_ids::sui_testnet(); + let mut bridge = new_for_testing(chain_id, ctx); + let coin = coin::mint_for_testing(12345, ctx); + + // Test when pending + let message = message::create_token_bridge_message( + chain_ids::sui_testnet(), // source chain + 10, // seq_num + address::to_bytes(ctx.sender()), // sender address + chain_ids::eth_sepolia(), // target_chain + hex::decode( + b"00000000000000000000000000000000000000c8", + ), // target_address + 1u8, // token_type + coin.balance().value(), + ); + + let key = message.key(); + bridge + .test_load_inner_mut() + .inner_token_transfer_records_mut() + .push_back( key, new_bridge_record_for_testing(message, option::none(), false), ); - assert!( - bridge.test_get_token_transfer_action_status(chain_id, 10) - == transfer_status_pending(), - ); - assert!(bridge.test_get_token_transfer_action_signatures(chain_id, 10) == option::none()); - - // Test when ready for claim - let message = message::create_token_bridge_message( - chain_ids::sui_testnet(), // source chain - 11, // seq_num - address::to_bytes(ctx.sender()), // sender address - chain_ids::eth_sepolia(), // target_chain - hex::decode(b"00000000000000000000000000000000000000c8"), // target_address - 1u8, // token_type - balance::value(coin::balance(&coin)) - ); - let key = message.key(); - bridge.test_load_inner_mut().inner_token_transfer_records_mut().push_back( + assert!( + bridge.test_get_token_transfer_action_status(chain_id, 10) == + transfer_status_pending(), + ); + assert!( + bridge.test_get_token_transfer_action_signatures(chain_id, 10) == + option::none(), + ); + + // Test when ready for claim + let message = message::create_token_bridge_message( + chain_ids::sui_testnet(), // source chain + 11, // seq_num + address::to_bytes(ctx.sender()), // sender address + chain_ids::eth_sepolia(), // target_chain + hex::decode( + b"00000000000000000000000000000000000000c8", + ), // target_address + 1u8, // token_type + balance::value(coin::balance(&coin)), + ); + let key = message.key(); + bridge + .test_load_inner_mut() + .inner_token_transfer_records_mut() + .push_back( key, - new_bridge_record_for_testing(message, option::some(vector[]), false), - ); - assert!( - bridge.test_get_token_transfer_action_status(chain_id, 11) - == transfer_status_approved(), - ); - assert!( - bridge.test_get_token_transfer_action_signatures(chain_id, 11) - == option::some(vector[]), - ); - assert!( - bridge.test_get_parsed_token_transfer_message(chain_id, 11) - == option::some( - to_parsed_token_transfer_message(&message) + new_bridge_record_for_testing( + message, + option::some(vector[]), + false, ), ); - - // Test when already claimed - let message = message::create_token_bridge_message( - chain_ids::sui_testnet(), // source chain - 12, // seq_num - address::to_bytes(ctx.sender()), // sender address - chain_ids::eth_sepolia(), // target_chain - hex::decode(b"00000000000000000000000000000000000000c8"), // target_address - 1u8, // token_type - balance::value(coin::balance(&coin)) - ); - let key = message.key(); - bridge.test_load_inner_mut().inner_token_transfer_records_mut().push_back( + assert!( + bridge.test_get_token_transfer_action_status(chain_id, 11) == + transfer_status_approved(), + ); + assert!( + bridge.test_get_token_transfer_action_signatures(chain_id, 11) == + option::some(vector[]), + ); + assert!( + bridge.test_get_parsed_token_transfer_message(chain_id, 11) == + option::some( + to_parsed_token_transfer_message(&message), + ), + ); + + // Test when already claimed + let message = message::create_token_bridge_message( + chain_ids::sui_testnet(), // source chain + 12, // seq_num + address::to_bytes(ctx.sender()), // sender address + chain_ids::eth_sepolia(), // target_chain + hex::decode( + b"00000000000000000000000000000000000000c8", + ), // target_address + 1u8, // token_type + balance::value(coin::balance(&coin)), + ); + let key = message.key(); + bridge + .test_load_inner_mut() + .inner_token_transfer_records_mut() + .push_back( key, - new_bridge_record_for_testing(message, option::some(vector[b"1234"]), true), - ); - assert!( - bridge.test_get_token_transfer_action_status(chain_id, 12) - == transfer_status_claimed(), - ); - assert!( - bridge.test_get_token_transfer_action_signatures(chain_id, 12) - == option::some(vector[b"1234"]), - ); - assert!( - bridge.test_get_parsed_token_transfer_message(chain_id, 12) - == option::some( - to_parsed_token_transfer_message(&message) + new_bridge_record_for_testing( + message, + option::some(vector[b"1234"]), + true, ), ); + assert!( + bridge.test_get_token_transfer_action_status(chain_id, 12) == + transfer_status_claimed(), + ); + assert!( + bridge.test_get_token_transfer_action_signatures(chain_id, 12) == + option::some(vector[b"1234"]), + ); + assert!( + bridge.test_get_parsed_token_transfer_message(chain_id, 12) == + option::some( + to_parsed_token_transfer_message(&message), + ), + ); + + // Test when message not found + assert!( + bridge.test_get_token_transfer_action_status(chain_id, 13) == + transfer_status_not_found(), + ); + assert!( + bridge.test_get_token_transfer_action_signatures(chain_id, 13) == + option::none(), + ); + assert!( + bridge.test_get_parsed_token_transfer_message(chain_id, 13) == + option::none(), + ); + + destroy(bridge); + coin.burn_for_testing(); + scenario.end(); +} - // Test when message not found - assert!( - bridge.test_get_token_transfer_action_status(chain_id, 13) - == transfer_status_not_found(), - ); - assert!( - bridge.test_get_token_transfer_action_signatures(chain_id, 13) - == option::none(), - ); - assert!( - bridge.test_get_parsed_token_transfer_message(chain_id, 13) - == option::none(), - ); +#[test] +#[expected_failure(abort_code = bridge::treasury::EUnsupportedTokenType)] +fun test_get_metadata_no_token() { + let chain_id = chain_ids::sui_testnet(); + let mut env = create_env(chain_id); + env.create_bridge_default(); + let bridge = env.bridge(@0x0); + let treasury = bridge.bridge_ref().test_load_inner().inner_treasury(); + treasury.notional_value(); + + abort 0 +} + +#[test] +fun change_url() { + let chain_id = chain_ids::sui_testnet(); + let mut env = create_env(chain_id); + env.create_bridge_default(); + let mut bridge = env.bridge(@0xAAAA); + bridge + .bridge_ref_mut() + .update_node_url(b"", env.scenario().ctx()); + bridge.return_bridge(); + env.destroy_env(); +} - destroy(bridge); - coin.burn_for_testing(); - scenario.end(); - } - - #[test] - #[expected_failure(abort_code = bridge::treasury::EUnsupportedTokenType)] - fun test_get_metadata_no_token() { - let chain_id = chain_ids::sui_testnet(); - let mut env = create_env(chain_id, @0x0); - env.create_bridge_default(); - let bridge = env.bridge(@0x0); - let treasury = bridge.bridge_ref().test_load_inner().inner_treasury(); - treasury.notional_value(); - - abort 0 - } +#[test] +#[ + expected_failure( + abort_code = bridge::committee::ESenderIsNotInBridgeCommittee, + ), +] +fun change_url_bad_sender() { + let chain_id = chain_ids::sui_testnet(); + let mut env = create_env(chain_id); + env.create_bridge_default(); + let mut bridge = env.bridge(@0x0); + bridge + .bridge_ref_mut() + .update_node_url(b"", env.scenario().ctx()); + abort 0 } diff --git a/crates/sui-framework/packages/bridge/tests/bridge_txns.move b/crates/sui-framework/packages/bridge/tests/bridge_txns.move new file mode 100644 index 0000000000000..8e2a082f57faa --- /dev/null +++ b/crates/sui-framework/packages/bridge/tests/bridge_txns.move @@ -0,0 +1,329 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[test_only] +module bridge::bridge_txns; +use bridge::bridge_env::{ + already_approved, + already_claimed, + approved, + claimed, + create_bridge_default, + create_env, + create_validator, + eth_id, + limit_exceeded, + register_test_token, + test_token_id +}; +use bridge::chain_ids; +use bridge::crypto::ecdsa_pub_key_to_eth_address; +use bridge::eth::ETH; +use bridge::test_token::TEST_TOKEN; +use std::type_name; + +#[test] +fun test_limits() { + let mut env = create_env(chain_ids::sui_custom()); + env.create_bridge_default(); + + let source_chain = chain_ids::eth_custom(); + let sui_address = @0xABCDEF; + let eth_address = x"0000000000000000000000000000000000001234"; + + // lower limits + let chain_id = env.chain_id(); + env.update_bridge_limit(@0x0, chain_id, source_chain, 3000); + let transfer_id1 = env.bridge_to_sui( + source_chain, + eth_address, + sui_address, + 4000000000, + ); + let transfer_id2 = env.bridge_to_sui( + source_chain, + eth_address, + sui_address, + 1000, + ); + assert!( + env.claim_and_transfer_token(source_chain, transfer_id1) == + limit_exceeded(), + ); + assert!( + env.claim_and_transfer_token(source_chain, transfer_id2) == + claimed(), + ); + // double claim is ok and it is a no-op + assert!( + env.claim_and_transfer_token(source_chain, transfer_id2) == + already_claimed(), + ); + + // up limits to allow claim + env.update_bridge_limit(@0x0, chain_id, source_chain, 4000); + assert!( + env.claim_and_transfer_token(source_chain, transfer_id1) == + claimed(), + ); + + env.destroy_env(); +} + +#[test] +fun test_bridge_and_claim() { + let mut env = create_env(chain_ids::sui_custom()); + env.create_bridge_default(); + + let source_chain = chain_ids::eth_custom(); + let sui_address = @0xABCDEF; + let eth_address = x"0000000000000000000000000000000000001234"; + let amount = 1000; + + // + // move from eth and transfer to sui account + let transfer_id1 = env.bridge_to_sui( + source_chain, + eth_address, + sui_address, + amount, + ); + assert!( + env.claim_and_transfer_token(source_chain, transfer_id1) == + claimed(), + ); + let transfer_id2 = env.bridge_to_sui( + source_chain, + eth_address, + sui_address, + amount, + ); + assert!( + env.claim_and_transfer_token(source_chain, transfer_id2) == + claimed(), + ); + // double claim is ok and it is a no-op + assert!( + env.claim_and_transfer_token(source_chain, transfer_id2) == + already_claimed(), + ); + + // + // change order + let transfer_id1 = env.bridge_to_sui( + source_chain, + eth_address, + sui_address, + amount, + ); + let transfer_id2 = env.bridge_to_sui( + source_chain, + eth_address, + sui_address, + amount, + ); + assert!( + env.claim_and_transfer_token(source_chain, transfer_id1) == + claimed(), + ); + assert!( + env.claim_and_transfer_token(source_chain, transfer_id2) == + claimed(), + ); + + // + // move from eth and send it back + let transfer_id = env.bridge_to_sui( + source_chain, + eth_address, + sui_address, + amount, + ); + let token = env.claim_token(sui_address, source_chain, transfer_id); + env.send_token( + sui_address, + source_chain, + eth_address, + token, + ); + + // + // approve with subset of signatures + let message = env.bridge_in_message( + source_chain, + eth_address, + sui_address, + amount, + ); + let signatures = env.sign_message_with(message, vector[0, 2]); + let transfer_id = message.seq_num(); + assert!(env.approve_token_transfer(message, signatures) == approved()); + assert!( + env.claim_and_transfer_token(source_chain, transfer_id) == + claimed(), + ); + + // + // multiple approve with subset of signatures + let message = env.bridge_in_message( + source_chain, + eth_address, + sui_address, + amount, + ); + let signatures = env.sign_message_with(message, vector[0, 2]); + let transfer_id = message.seq_num(); + assert!(env.approve_token_transfer(message, signatures) == approved()); + assert!( + env.approve_token_transfer(message, signatures) == already_approved(), + ); + assert!( + env.approve_token_transfer(message, signatures) == already_approved(), + ); + let token = env.claim_token(sui_address, source_chain, transfer_id); + let send_token_id = env.send_token( + sui_address, + source_chain, + eth_address, + token, + ); + let message = env.bridge_out_message( + source_chain, + eth_address, + sui_address, + amount, + send_token_id, + ); + let signatures = env.sign_message_with(message, vector[1, 2]); + assert!(env.approve_token_transfer(message, signatures) == approved()); + let signatures = env.sign_message_with(message, vector[0, 2]); + assert!( + env.approve_token_transfer(message, signatures) == already_approved(), + ); + + // + // multiple approve with different subset of signatures + let message = env.bridge_in_message( + source_chain, + eth_address, + sui_address, + amount, + ); + let transfer_id = message.seq_num(); + let signatures = env.sign_message_with(message, vector[0, 2]); + assert!(env.approve_token_transfer(message, signatures) == approved()); + let signatures = env.sign_message_with(message, vector[0, 1]); + assert!( + env.approve_token_transfer(message, signatures) == already_approved(), + ); + let signatures = env.sign_message_with(message, vector[1, 2]); + assert!( + env.approve_token_transfer(message, signatures) == already_approved(), + ); + let token = env.claim_token(sui_address, source_chain, transfer_id); + env.send_token( + sui_address, + source_chain, + eth_address, + token, + ); + + env.destroy_env(); +} + +#[test] +#[expected_failure(abort_code = bridge::committee::ESignatureBelowThreshold)] +fun test_blocklist() { + let mut env = create_env(chain_ids::sui_custom()); + let validators = vector[ + create_validator(@0xAAAA, 100, &b"1234567890_1234567890_1234567890"), + create_validator(@0xBBBB, 100, &b"234567890_1234567890_1234567890_"), + create_validator(@0xCCCC, 100, &b"34567890_1234567890_1234567890_1"), + create_validator(@0xDDDD, 100, &b"4567890_1234567890_1234567890_12"), + ]; + env.setup_validators(validators); + + let sender = @0x0; + env.create_bridge(sender); + env.register_committee(); + env.init_committee(sender); + env.setup_treasury(sender); + + let source_chain = chain_ids::eth_custom(); + let sui_address = @0xABCDEF; + let eth_address = x"0000000000000000000000000000000000001234"; + let amount = 1000; + + // bridging in and out works + let message = env.bridge_in_message( + source_chain, + eth_address, + sui_address, + amount, + ); + let signatures = env.sign_message_with(message, vector[0, 2]); + let transfer_id = message.seq_num(); + assert!(env.approve_token_transfer(message, signatures) == approved()); + assert!( + env.claim_and_transfer_token(source_chain, transfer_id) == + claimed(), + ); + + // block bridge node 0 + let chain_id = env.chain_id(); + let node_key = ecdsa_pub_key_to_eth_address(env + .validators()[0] + .public_key()); + env.execute_blocklist(@0x0, chain_id, 0, vector[node_key]); + + // signing with 2 valid bridge nodes works + let message = env.bridge_in_message( + source_chain, + eth_address, + sui_address, + amount, + ); + let signatures = env.sign_message_with(message, vector[1, 2]); + assert!(env.approve_token_transfer(message, signatures) == approved()); + assert!( + env.approve_token_transfer(message, signatures) == already_approved(), + ); + + // signing with blocked node fails + let message = env.bridge_in_message( + source_chain, + eth_address, + sui_address, + amount, + ); + let signatures = env.sign_message_with(message, vector[0, 2]); + env.approve_token_transfer(message, signatures); + + env.destroy_env(); +} + +#[test] +fun test_system_messages() { + let addr = @0xABCDEF0123; // random address + let mut env = create_env(chain_ids::sui_custom()); + env.create_bridge_default(); + + env.update_asset_price(addr, eth_id(), 735); + + env.register_test_token(); + env.add_tokens( + addr, + false, + vector[test_token_id()], + vector[type_name::get().into_string()], + vector[333], + ); + + let chain_id = env.chain_id(); + let node_key = ecdsa_pub_key_to_eth_address(env + .validators()[0] + .public_key()); + env.execute_blocklist(@0x0, chain_id, 0, vector[node_key]); + + env.destroy_env(); +} From dab9bad6187e0ba342aa6fca4586e6f276583db9 Mon Sep 17 00:00:00 2001 From: mwtian <81660174+mwtian@users.noreply.github.com> Date: Tue, 20 Aug 2024 17:27:15 +0100 Subject: [PATCH 177/232] [Consensus] move transaction count limit to protocol config (#19042) ## Description 1. move transaction count per block limit to protocol config. 2. reduce the transaction bytes per block limit to 512KB, based on the transaction count limit. 3. verify the limits in `BlockVerifier` ## Test plan Unit test --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- consensus/core/src/authority_node.rs | 2 +- consensus/core/src/block_verifier.rs | 80 ++++++++++++++++++- consensus/core/src/core.rs | 14 ++-- consensus/core/src/core_thread.rs | 2 +- consensus/core/src/error.rs | 9 +++ consensus/core/src/transaction.rs | 22 ++--- crates/sui-open-rpc/spec/openrpc.json | 1 + crates/sui-protocol-config/src/lib.rs | 17 +++- ...ocol_config__test__Mainnet_version_55.snap | 4 +- ...ocol_config__test__Testnet_version_55.snap | 4 +- ...sui_protocol_config__test__version_55.snap | 4 +- 11 files changed, 127 insertions(+), 32 deletions(-) diff --git a/consensus/core/src/authority_node.rs b/consensus/core/src/authority_node.rs index ffd2b421cda35..f684d05bcfdcd 100644 --- a/consensus/core/src/authority_node.rs +++ b/consensus/core/src/authority_node.rs @@ -164,7 +164,7 @@ where let start_time = Instant::now(); let (tx_client, tx_receiver) = TransactionClient::new(context.clone()); - let tx_consumer = TransactionConsumer::new(tx_receiver, context.clone(), None); + let tx_consumer = TransactionConsumer::new(tx_receiver, context.clone()); let (core_signals, signals_receivers) = CoreSignals::new(context.clone()); diff --git a/consensus/core/src/block_verifier.rs b/consensus/core/src/block_verifier.rs index 7c04605ddb0f5..4d68f476455fe 100644 --- a/consensus/core/src/block_verifier.rs +++ b/consensus/core/src/block_verifier.rs @@ -142,8 +142,43 @@ impl BlockVerifier for SignedBlockVerifier { }); } - // TODO: check transaction size, total size and count. let batch: Vec<_> = block.transactions().iter().map(|t| t.data()).collect(); + + let max_transaction_size_limit = + self.context + .protocol_config + .consensus_max_transaction_size_bytes() as usize; + for t in &batch { + if t.len() > max_transaction_size_limit && max_transaction_size_limit > 0 { + return Err(ConsensusError::TransactionTooLarge { + size: t.len(), + limit: max_transaction_size_limit, + }); + } + } + + let max_num_transactions_limit = + self.context.protocol_config.max_num_transactions_in_block() as usize; + if batch.len() > max_num_transactions_limit && max_num_transactions_limit > 0 { + return Err(ConsensusError::TooManyTransactions { + count: batch.len(), + limit: max_num_transactions_limit, + }); + } + + let total_transactions_size_limit = + self.context + .protocol_config + .consensus_max_transactions_in_block_bytes() as usize; + if batch.iter().map(|t| t.len()).sum::() > total_transactions_size_limit + && total_transactions_size_limit > 0 + { + return Err(ConsensusError::TooManyTransactionBytes { + size: batch.len(), + limit: total_transactions_size_limit, + }); + } + self.transaction_verifier .verify_batch(&self.context.protocol_config, &batch) .map_err(|e| ConsensusError::InvalidTransaction(format!("{e:?}"))) @@ -445,6 +480,49 @@ mod test { Err(ConsensusError::InvalidTransaction(_)) )); } + + // Block with transaction too large. + { + let block = test_block + .clone() + .set_transactions(vec![Transaction::new(vec![4; 257 * 1024])]) + .build(); + let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap(); + assert!(matches!( + verifier.verify(&signed_block), + Err(ConsensusError::TransactionTooLarge { size: _, limit: _ }) + )); + } + + // Block with too many transactions. + { + let block = test_block + .clone() + .set_transactions((0..1000).map(|_| Transaction::new(vec![4; 8])).collect()) + .build(); + let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap(); + assert!(matches!( + verifier.verify(&signed_block), + Err(ConsensusError::TooManyTransactions { count: _, limit: _ }) + )); + } + + // Block with too many transaction bytes. + { + let block = test_block + .clone() + .set_transactions( + (0..100) + .map(|_| Transaction::new(vec![4; 8 * 1024])) + .collect(), + ) + .build(); + let signed_block = SignedBlock::new(block, authority_2_protocol_keypair).unwrap(); + assert!(matches!( + verifier.verify(&signed_block), + Err(ConsensusError::TooManyTransactionBytes { size: _, limit: _ }) + )); + } } #[tokio::test] diff --git a/consensus/core/src/core.rs b/consensus/core/src/core.rs index d0a8f89169afb..3c1636bd4e3ff 100644 --- a/consensus/core/src/core.rs +++ b/consensus/core/src/core.rs @@ -868,7 +868,7 @@ impl CoreTextFixture { .with_num_commits_per_schedule(10), ); let (_transaction_client, tx_receiver) = TransactionClient::new(context.clone()); - let transaction_consumer = TransactionConsumer::new(tx_receiver, context.clone(), None); + let transaction_consumer = TransactionConsumer::new(tx_receiver, context.clone()); let (signals, signal_receivers) = CoreSignals::new(context.clone()); // Need at least one subscriber to the block broadcast channel. let block_receiver = signal_receivers.block_broadcast_receiver(); @@ -935,7 +935,7 @@ mod test { let context = Arc::new(context); let store = Arc::new(MemStore::new()); let (_transaction_client, tx_receiver) = TransactionClient::new(context.clone()); - let transaction_consumer = TransactionConsumer::new(tx_receiver, context.clone(), None); + let transaction_consumer = TransactionConsumer::new(tx_receiver, context.clone()); // Create test blocks for all the authorities for 4 rounds and populate them in store let mut last_round_blocks = genesis_blocks(context.clone()); @@ -1045,7 +1045,7 @@ mod test { let context = Arc::new(context); let store = Arc::new(MemStore::new()); let (_transaction_client, tx_receiver) = TransactionClient::new(context.clone()); - let transaction_consumer = TransactionConsumer::new(tx_receiver, context.clone(), None); + let transaction_consumer = TransactionConsumer::new(tx_receiver, context.clone()); // Create test blocks for all authorities except our's (index = 0). let mut last_round_blocks = genesis_blocks(context.clone()); @@ -1175,7 +1175,7 @@ mod test { Arc::new(NoopBlockVerifier), ); let (transaction_client, tx_receiver) = TransactionClient::new(context.clone()); - let transaction_consumer = TransactionConsumer::new(tx_receiver, context.clone(), None); + let transaction_consumer = TransactionConsumer::new(tx_receiver, context.clone()); let (signals, signal_receivers) = CoreSignals::new(context.clone()); // Need at least one subscriber to the block broadcast channel. let mut block_receiver = signal_receivers.block_broadcast_receiver(); @@ -1288,7 +1288,7 @@ mod test { )); let (_transaction_client, tx_receiver) = TransactionClient::new(context.clone()); - let transaction_consumer = TransactionConsumer::new(tx_receiver, context.clone(), None); + let transaction_consumer = TransactionConsumer::new(tx_receiver, context.clone()); let (signals, signal_receivers) = CoreSignals::new(context.clone()); // Need at least one subscriber to the block broadcast channel. let _block_receiver = signal_receivers.block_broadcast_receiver(); @@ -1376,7 +1376,7 @@ mod test { )); let (_transaction_client, tx_receiver) = TransactionClient::new(context.clone()); - let transaction_consumer = TransactionConsumer::new(tx_receiver, context.clone(), None); + let transaction_consumer = TransactionConsumer::new(tx_receiver, context.clone()); let (signals, signal_receivers) = CoreSignals::new(context.clone()); // Need at least one subscriber to the block broadcast channel. let _block_receiver = signal_receivers.block_broadcast_receiver(); @@ -1563,7 +1563,7 @@ mod test { )); let (_transaction_client, tx_receiver) = TransactionClient::new(context.clone()); - let transaction_consumer = TransactionConsumer::new(tx_receiver, context.clone(), None); + let transaction_consumer = TransactionConsumer::new(tx_receiver, context.clone()); let (signals, signal_receivers) = CoreSignals::new(context.clone()); // Need at least one subscriber to the block broadcast channel. let _block_receiver = signal_receivers.block_broadcast_receiver(); diff --git a/consensus/core/src/core_thread.rs b/consensus/core/src/core_thread.rs index 8b9c6a9b89783..eeeeb06d26959 100644 --- a/consensus/core/src/core_thread.rs +++ b/consensus/core/src/core_thread.rs @@ -325,7 +325,7 @@ mod test { Arc::new(NoopBlockVerifier), ); let (_transaction_client, tx_receiver) = TransactionClient::new(context.clone()); - let transaction_consumer = TransactionConsumer::new(tx_receiver, context.clone(), None); + 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"); diff --git a/consensus/core/src/error.rs b/consensus/core/src/error.rs index d9fdb99e37ed0..9dadc6c84da06 100644 --- a/consensus/core/src/error.rs +++ b/consensus/core/src/error.rs @@ -24,6 +24,15 @@ pub(crate) enum ConsensusError { #[error("Error serializing: {0}")] SerializationFailure(bcs::Error), + #[error("Block contains a transaction that is too large: {size} > {limit}")] + TransactionTooLarge { size: usize, limit: usize }, + + #[error("Block contains too many transactions: {count} > {limit}")] + TooManyTransactions { count: usize, limit: usize }, + + #[error("Block contains too many transaction bytes: {size} > {limit}")] + TooManyTransactionBytes { size: usize, limit: usize }, + #[error("Unexpected block authority {0} from peer {1}")] UnexpectedAuthority(AuthorityIndex, AuthorityIndex), diff --git a/consensus/core/src/transaction.rs b/consensus/core/src/transaction.rs index 8ae6eb1c4caba..fb9eda5ffaa3d 100644 --- a/consensus/core/src/transaction.rs +++ b/consensus/core/src/transaction.rs @@ -18,10 +18,6 @@ use crate::{ /// The maximum number of transactions pending to the queue to be pulled for block proposal const MAX_PENDING_TRANSACTIONS: usize = 2_000; -/// Assume 20_000 TPS * 5% max stake per validator / (minimum) 4 blocks per round = 250 transactions per block maximum -/// Using a higher limit that is 250 * 2 = 500, to account for bursty traffic and system transactions. -const MAX_CONSUMED_TRANSACTIONS_PER_REQUEST: u64 = 500; - /// The guard acts as an acknowledgment mechanism for the inclusion of the transactions to a block. /// When its last transaction is included to a block then `included_in_block_ack` will be signalled. /// If the guard is dropped without getting acknowledged that means the transactions have not been @@ -45,18 +41,15 @@ pub(crate) struct TransactionConsumer { } impl TransactionConsumer { - pub(crate) fn new( - tx_receiver: Receiver, - context: Arc, - max_consumed_transactions_per_request: Option, - ) -> Self { + pub(crate) fn new(tx_receiver: Receiver, context: Arc) -> Self { Self { tx_receiver, max_consumed_bytes_per_request: context .protocol_config .consensus_max_transactions_in_block_bytes(), - max_consumed_transactions_per_request: max_consumed_transactions_per_request - .unwrap_or(MAX_CONSUMED_TRANSACTIONS_PER_REQUEST), + max_consumed_transactions_per_request: context + .protocol_config + .max_num_transactions_in_block(), pending_transactions: None, } } @@ -74,7 +67,6 @@ impl TransactionConsumer { // Handle one batch of incoming transactions from TransactionGuard. // Returns the remaining txs as a new TransactionGuard, if the batch breaks any limit. let mut handle_txs = |t: TransactionsGuard| -> Option { - // Here we assume that a transaction can always fit in `max_fetched_bytes_per_request` let remaining_txs: Vec<_> = t .transactions .into_iter() @@ -273,7 +265,7 @@ mod tests { let context = Arc::new(Context::new_for_test(4).0); let (client, tx_receiver) = TransactionClient::new(context.clone()); - let mut consumer = TransactionConsumer::new(tx_receiver, context.clone(), None); + let mut consumer = TransactionConsumer::new(tx_receiver, context.clone()); // submit asynchronously the transactions and keep the waiters let mut included_in_block_waiters = FuturesUnordered::new(); @@ -325,7 +317,7 @@ mod tests { let context = Arc::new(Context::new_for_test(4).0); let (client, tx_receiver) = TransactionClient::new(context.clone()); - let mut consumer = TransactionConsumer::new(tx_receiver, context.clone(), None); + let mut consumer = TransactionConsumer::new(tx_receiver, context.clone()); // submit some transactions for i in 0..10 { @@ -393,7 +385,7 @@ mod tests { let context = Arc::new(Context::new_for_test(4).0); let (client, tx_receiver) = TransactionClient::new(context.clone()); - let mut consumer = TransactionConsumer::new(tx_receiver, context.clone(), None); + let mut consumer = TransactionConsumer::new(tx_receiver, context.clone()); let mut all_receivers = Vec::new(); // submit a few transactions individually. for i in 0..10 { diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index 986acaf1bd67d..8abc1b3548a64 100644 --- a/crates/sui-open-rpc/spec/openrpc.json +++ b/crates/sui-open-rpc/spec/openrpc.json @@ -1417,6 +1417,7 @@ "config_read_setting_impl_cost_base": null, "config_read_setting_impl_cost_per_byte": null, "consensus_bad_nodes_stake_threshold": null, + "consensus_max_num_transactions_in_block": null, "consensus_max_transaction_size_bytes": null, "consensus_max_transactions_in_block_bytes": null, "crypto_invalid_arguments_cost": { diff --git a/crates/sui-protocol-config/src/lib.rs b/crates/sui-protocol-config/src/lib.rs index 6f178c1a64455..58b9bed05183a 100644 --- a/crates/sui-protocol-config/src/lib.rs +++ b/crates/sui-protocol-config/src/lib.rs @@ -1184,8 +1184,10 @@ pub struct ProtocolConfig { /// The maximum serialised transaction size (in bytes) accepted by consensus. That should be bigger than the /// `max_tx_size_bytes` with some additional headroom. consensus_max_transaction_size_bytes: Option, - /// The maximum size of transactions included in a consensus proposed block + /// The maximum size of transactions included in a consensus block. consensus_max_transactions_in_block_bytes: Option, + /// The maximum number of transactions included in a consensus block. + consensus_max_num_transactions_in_block: Option, /// The max accumulated txn execution cost per object in a Narwhal commit. Transactions /// in a checkpoint will be deferred once their touch shared objects hit this limit. @@ -1529,6 +1531,11 @@ impl ProtocolConfig { pub fn authority_capabilities_v2(&self) -> bool { self.feature_flags.authority_capabilities_v2 } + + pub fn max_num_transactions_in_block(&self) -> u64 { + // 500 is the value used before this field is introduced. + self.consensus_max_num_transactions_in_block.unwrap_or(500) + } } #[cfg(not(msim))] @@ -2016,6 +2023,8 @@ impl ProtocolConfig { consensus_max_transactions_in_block_bytes: None, + consensus_max_num_transactions_in_block: None, + max_accumulated_txn_cost_per_object_in_narwhal_commit: None, max_deferral_rounds_for_congestion_control: None, @@ -2656,6 +2665,12 @@ impl ProtocolConfig { 55 => { // Turn on enums mainnet cfg.move_binary_format_version = Some(7); + + // Assume 1KB per transaction and 500 transactions per block. + cfg.consensus_max_transactions_in_block_bytes = Some(512 * 1024); + // Assume 20_000 TPS * 5% max stake per validator / (minimum) 4 blocks per round = 250 transactions per block maximum + // Using a higher limit that is 512, to account for bursty traffic and system transactions. + cfg.consensus_max_num_transactions_in_block = Some(512); } // Use this template when making changes: // diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_55.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_55.snap index 4797d413342cf..3a9ecd48e722f 100644 --- a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_55.snap +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_55.snap @@ -310,7 +310,8 @@ 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: 6291456 +consensus_max_transactions_in_block_bytes: 524288 +consensus_max_num_transactions_in_block: 512 max_accumulated_txn_cost_per_object_in_narwhal_commit: 100 max_deferral_rounds_for_congestion_control: 10 min_checkpoint_interval_ms: 200 @@ -318,4 +319,3 @@ checkpoint_summary_version_specific_data: 1 max_soft_bundle_size: 5 bridge_should_try_to_finalize_committee: false max_accumulated_txn_cost_per_object_in_mysticeti_commit: 10 - diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_55.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_55.snap index 1255df195a218..13f98870266dd 100644 --- a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_55.snap +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_55.snap @@ -311,7 +311,8 @@ 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: 6291456 +consensus_max_transactions_in_block_bytes: 524288 +consensus_max_num_transactions_in_block: 512 max_accumulated_txn_cost_per_object_in_narwhal_commit: 100 max_deferral_rounds_for_congestion_control: 10 min_checkpoint_interval_ms: 200 @@ -319,4 +320,3 @@ 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: 10 - diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_55.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_55.snap index 11a1ab15cf20f..13317be055abe 100644 --- a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_55.snap +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_55.snap @@ -320,7 +320,8 @@ 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: 6291456 +consensus_max_transactions_in_block_bytes: 524288 +consensus_max_num_transactions_in_block: 512 max_accumulated_txn_cost_per_object_in_narwhal_commit: 100 max_deferral_rounds_for_congestion_control: 10 min_checkpoint_interval_ms: 200 @@ -328,4 +329,3 @@ 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: 10 - From 2682497286249dd495aa15bd526d18eac6213f44 Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Tue, 20 Aug 2024 18:15:55 +0100 Subject: [PATCH 178/232] fix(graphql): Docs fixes from #17543 (#19046) ## Description Gathering all the suggested docs fixes across the stack in #17543 into one PR. ## Test plan :eyes: ## Stack - #19047 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-graphql-rpc/schema.graphql | 10 +++++----- crates/sui-graphql-rpc/src/commands.rs | 2 +- .../src/data/package_resolver.rs | 2 +- .../sui-graphql-rpc/src/types/checkpoint.rs | 4 ++-- crates/sui-graphql-rpc/src/types/epoch.rs | 2 +- crates/sui-graphql-rpc/src/types/event.rs | 11 +++++------ .../sui-graphql-rpc/src/types/move_package.rs | 4 ++-- crates/sui-graphql-rpc/src/types/object.rs | 10 +++++----- crates/sui-graphql-rpc/src/types/query.rs | 10 +++++----- .../src/types/transaction_block.rs | 2 +- .../snapshot_tests__schema_sdl_export.snap | 10 +++++----- crates/sui-package-dump/src/lib.rs | 19 ++++++++++--------- crates/sui-tool/src/commands.rs | 2 +- 13 files changed, 44 insertions(+), 44 deletions(-) diff --git a/crates/sui-graphql-rpc/schema.graphql b/crates/sui-graphql-rpc/schema.graphql index 9a134382a7a70..db2f967fa4085 100644 --- a/crates/sui-graphql-rpc/schema.graphql +++ b/crates/sui-graphql-rpc/schema.graphql @@ -3088,12 +3088,12 @@ type Query { """ object(address: SuiAddress!, version: UInt53): Object """ - The package corresponding to the given address at the (optionally) given version. + The package corresponding to the given address (at the optionally given version). When no version is given, the package is loaded directly from the address given. Otherwise, the address is translated before loading to point to the package whose original ID matches - the package at `address`, but whose version is `version`. For non-system packages, this may - result in a different address than `address` because different versions of a package, + the package at `address`, but whose version is `version`. For non-system packages, this + might result in a different address than `address` because different versions of a package, introduced by upgrades, exist at distinct addresses. Note that this interpretation of `version` is different from a historical object read (the @@ -3156,8 +3156,8 @@ type Query { The Move packages that exist in the network, optionally filtered to be strictly before `beforeCheckpoint` and/or strictly after `afterCheckpoint`. - This query will return all versions of a given user package that appear between the - specified checkpoints, but only records the latest versions of system packages. + This query returns all versions of a given user package that appear between the specified + checkpoints, but only records the latest versions of system packages. """ packages(first: Int, after: String, last: Int, before: String, filter: MovePackageCheckpointFilter): MovePackageConnection! """ diff --git a/crates/sui-graphql-rpc/src/commands.rs b/crates/sui-graphql-rpc/src/commands.rs index e5166def39f50..4b0eca46c11cb 100644 --- a/crates/sui-graphql-rpc/src/commands.rs +++ b/crates/sui-graphql-rpc/src/commands.rs @@ -16,7 +16,7 @@ pub enum Command { /// Output a TOML config (suitable for passing into the --config parameter of the start-server /// command) with all values set to their defaults. GenerateConfig { - /// Optional path to a file to output to. Prints to stdout if none is provided. + /// Optional path to an output file. Prints to `stdout` if not provided. output: Option, }, diff --git a/crates/sui-graphql-rpc/src/data/package_resolver.rs b/crates/sui-graphql-rpc/src/data/package_resolver.rs index 467911753d266..f10067fd007b9 100644 --- a/crates/sui-graphql-rpc/src/data/package_resolver.rs +++ b/crates/sui-graphql-rpc/src/data/package_resolver.rs @@ -26,7 +26,7 @@ pub(crate) type PackageResolver = Arc>; /// to `fetch` pub struct DbPackageStore(DataLoader); -/// DataLoader key for fetching the latest version of a `Package` by its ID. +/// `DataLoader` key for fetching the latest version of a `Package` by its ID. #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] struct PackageKey(AccountAddress); diff --git a/crates/sui-graphql-rpc/src/types/checkpoint.rs b/crates/sui-graphql-rpc/src/types/checkpoint.rs index cbebb9935f491..f59ad8c46ae17 100644 --- a/crates/sui-graphql-rpc/src/types/checkpoint.rs +++ b/crates/sui-graphql-rpc/src/types/checkpoint.rs @@ -36,7 +36,7 @@ pub(crate) struct CheckpointId { pub sequence_number: Option, } -/// DataLoader key for fetching a `Checkpoint` by its sequence number, constrained by a consistency +/// `DataLoader` key for fetching a `Checkpoint` by its sequence number, constrained by a consistency /// cursor. #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] struct SeqNumKey { @@ -47,7 +47,7 @@ struct SeqNumKey { pub checkpoint_viewed_at: u64, } -/// DataLoader key for fetching a `Checkpoint` by its digest, constrained by a consistency cursor. +/// `DataLoader` key for fetching a `Checkpoint` by its digest, constrained by a consistency cursor. #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] struct DigestKey { pub digest: Digest, diff --git a/crates/sui-graphql-rpc/src/types/epoch.rs b/crates/sui-graphql-rpc/src/types/epoch.rs index 6ca312ca3ba37..7ecfa9c7d773e 100644 --- a/crates/sui-graphql-rpc/src/types/epoch.rs +++ b/crates/sui-graphql-rpc/src/types/epoch.rs @@ -32,7 +32,7 @@ pub(crate) struct Epoch { pub checkpoint_viewed_at: u64, } -/// DataLoader key for fetching an `Epoch` by its ID, optionally constrained by a consistency +/// `DataLoader` key for fetching an `Epoch` by its ID, optionally constrained by a consistency /// cursor. #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] struct EpochKey { diff --git a/crates/sui-graphql-rpc/src/types/event.rs b/crates/sui-graphql-rpc/src/types/event.rs index 6b7ba8ee8b3c2..e195b10844c26 100644 --- a/crates/sui-graphql-rpc/src/types/event.rs +++ b/crates/sui-graphql-rpc/src/types/event.rs @@ -143,13 +143,12 @@ impl Event { /// checkpoint sequence numbers as the cursor to determine the correct page of results. The /// query can optionally be further `filter`-ed by the `EventFilter`. /// - /// The `checkpoint_viewed_at` parameter represents the checkpoint sequence number at which this - /// page was queried. Each entity returned in the connection will inherit this checkpoint, so - /// that when viewing that entity's state, it will be as if it is being viewed at this - /// checkpoint. + /// The `checkpoint_viewed_at` parameter represents the checkpoint sequence number at which + /// this page was queried. Each entity returned in the connection inherits this checkpoint, so + /// that when viewing that entity's state, it's as if it's being viewed at this checkpoint. /// - /// The cursors in `page` may also include checkpoint viewed at fields. If these are set, they - /// take precedence over the checkpoint that pagination is being conducted in. + /// The cursors in `page` might also include checkpoint viewed at fields. If these are set, + /// they take precedence over the checkpoint that pagination is being conducted in. pub(crate) async fn paginate( db: &Db, page: Page, diff --git a/crates/sui-graphql-rpc/src/types/move_package.rs b/crates/sui-graphql-rpc/src/types/move_package.rs index aba7259ff1a22..bb92ba1e599a3 100644 --- a/crates/sui-graphql-rpc/src/types/move_package.rs +++ b/crates/sui-graphql-rpc/src/types/move_package.rs @@ -142,7 +142,7 @@ pub(crate) struct PackageCursor { pub checkpoint_viewed_at: u64, } -/// DataLoader key for fetching the storage ID of the (user) package that shares an original (aka +/// `DataLoader` key for fetching the storage ID of the (user) package that shares an original (aka /// runtime) ID with the package stored at `package_id`, and whose version is `version`. /// /// Note that this is different from looking up the historical version of an object -- the query @@ -154,7 +154,7 @@ struct PackageVersionKey { version: u64, } -/// DataLoader key for fetching the latest version of a user package: The package with the largest +/// `DataLoader` key for fetching the latest version of a user package: The package with the largest /// version whose original ID matches the original ID of the package at `address`. #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] struct LatestKey { diff --git a/crates/sui-graphql-rpc/src/types/object.rs b/crates/sui-graphql-rpc/src/types/object.rs index cd3e18709760c..66887aa7be54c 100644 --- a/crates/sui-graphql-rpc/src/types/object.rs +++ b/crates/sui-graphql-rpc/src/types/object.rs @@ -185,7 +185,7 @@ pub(crate) struct AddressOwner { /// Filter for a point query of an Object. pub(crate) enum ObjectLookup { LatestAt { - /// The checkpoint sequence number at which this was viewed at + /// The checkpoint sequence number at which this was viewed at. checkpoint_viewed_at: u64, }, @@ -193,7 +193,7 @@ pub(crate) enum ObjectLookup { /// The parent version to be used as an upper bound for the query. Look for the latest /// version of a child object whose version is less than or equal to this upper bound. parent_version: u64, - /// The checkpoint sequence number at which this was viewed at + /// The checkpoint sequence number at which this was viewed at. checkpoint_viewed_at: u64, }, @@ -280,7 +280,7 @@ pub(crate) enum IObject { SuinsRegistration(SuinsRegistration), } -/// DataLoader key for fetching an `Object` at a specific version, constrained by a consistency +/// `DataLoader` key for fetching an `Object` at a specific version, constrained by a consistency /// cursor (if that version was created after the checkpoint the query is viewing at, then it will /// fail). #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] @@ -290,7 +290,7 @@ struct HistoricalKey { checkpoint_viewed_at: u64, } -/// DataLoader key for fetching the latest version of an object whose parent object has version +/// `DataLoader` key for fetching the latest version of an object whose parent object has version /// `parent_version`, as of `checkpoint_viewed_at`. This look-up can fail to find a valid object if /// the key is not self-consistent, for example if the `parent_version` is set to a higher version /// than the object's actual parent as of `checkpoint_viewed_at`. @@ -301,7 +301,7 @@ struct ParentVersionKey { checkpoint_viewed_at: u64, } -/// DataLoader key for fetching the latest version of an `Object` as of a consistency cursor. +/// `DataLoader` key for fetching the latest version of an object as of a given checkpoint. #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] struct LatestAtKey { id: SuiAddress, diff --git a/crates/sui-graphql-rpc/src/types/query.rs b/crates/sui-graphql-rpc/src/types/query.rs index 1ff209d351a55..07b51ef649d88 100644 --- a/crates/sui-graphql-rpc/src/types/query.rs +++ b/crates/sui-graphql-rpc/src/types/query.rs @@ -225,12 +225,12 @@ impl Query { Object::query(ctx, address, key).await.extend() } - /// The package corresponding to the given address at the (optionally) given version. + /// The package corresponding to the given address (at the optionally given version). /// /// When no version is given, the package is loaded directly from the address given. Otherwise, /// the address is translated before loading to point to the package whose original ID matches - /// the package at `address`, but whose version is `version`. For non-system packages, this may - /// result in a different address than `address` because different versions of a package, + /// the package at `address`, but whose version is `version`. For non-system packages, this + /// might result in a different address than `address` because different versions of a package, /// introduced by upgrades, exist at distinct addresses. /// /// Note that this interpretation of `version` is different from a historical object read (the @@ -440,8 +440,8 @@ impl Query { /// The Move packages that exist in the network, optionally filtered to be strictly before /// `beforeCheckpoint` and/or strictly after `afterCheckpoint`. /// - /// This query will return all versions of a given user package that appear between the - /// specified checkpoints, but only records the latest versions of system packages. + /// This query returns all versions of a given user package that appear between the specified + /// checkpoints, but only records the latest versions of system packages. async fn packages( &self, ctx: &Context<'_>, diff --git a/crates/sui-graphql-rpc/src/types/transaction_block.rs b/crates/sui-graphql-rpc/src/types/transaction_block.rs index c75a34ee2fcda..c33da8133001a 100644 --- a/crates/sui-graphql-rpc/src/types/transaction_block.rs +++ b/crates/sui-graphql-rpc/src/types/transaction_block.rs @@ -131,7 +131,7 @@ pub(crate) struct TransactionBlockCursor { pub tx_checkpoint_number: u64, } -/// DataLoader key for fetching a `TransactionBlock` by its digest, optionally constrained by a +/// `DataLoader` key for fetching a `TransactionBlock` by its digest, optionally constrained by a /// consistency cursor. #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] struct DigestKey { diff --git a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap index 87f5a27061f06..93eadb6691d92 100644 --- a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap +++ b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap @@ -3092,12 +3092,12 @@ type Query { """ object(address: SuiAddress!, version: UInt53): Object """ - The package corresponding to the given address at the (optionally) given version. + The package corresponding to the given address (at the optionally given version). When no version is given, the package is loaded directly from the address given. Otherwise, the address is translated before loading to point to the package whose original ID matches - the package at `address`, but whose version is `version`. For non-system packages, this may - result in a different address than `address` because different versions of a package, + the package at `address`, but whose version is `version`. For non-system packages, this + might result in a different address than `address` because different versions of a package, introduced by upgrades, exist at distinct addresses. Note that this interpretation of `version` is different from a historical object read (the @@ -3160,8 +3160,8 @@ type Query { The Move packages that exist in the network, optionally filtered to be strictly before `beforeCheckpoint` and/or strictly after `afterCheckpoint`. - This query will return all versions of a given user package that appear between the - specified checkpoints, but only records the latest versions of system packages. + This query returns all versions of a given user package that appear between the specified + checkpoints, but only records the latest versions of system packages. """ packages(first: Int, after: String, last: Int, before: String, filter: MovePackageCheckpointFilter): MovePackageConnection! """ diff --git a/crates/sui-package-dump/src/lib.rs b/crates/sui-package-dump/src/lib.rs index 8438a8bec7f88..ea70db5fd927a 100644 --- a/crates/sui-package-dump/src/lib.rs +++ b/crates/sui-package-dump/src/lib.rs @@ -20,10 +20,11 @@ mod query; /// Ensure all packages created before `before_checkpoint` are written to the `output_dir`ectory, /// from the GraphQL service at `rpc_url`. /// -/// `output_dir` can be a path to a non-existent directory, in which case it will be created, or an -/// existing empty directory (in which case it will be filled), or an existing directory that has -/// been written to in the past (in which case this invocation will pick back up from where the -/// previous invocation left off). +/// `output_dir` can be a path to a non-existent directory, an existing empty directory, or an +/// existing directory written to in the past. If the path is non-existent, the invocation creates +/// it. If the path exists but is empty, the invocation writes to the directory. If the directory +/// has been written to in the past, the invocation picks back up where the previous invocation +/// left off. pub async fn dump( rpc_url: String, output_dir: PathBuf, @@ -116,8 +117,8 @@ async fn max_page_size(client: &Client) -> Result { /// Read all the packages between `after_checkpoint` and `before_checkpoint`, in batches of /// `page_size` from the `client` connected to a GraphQL service. /// -/// If `after_checkpoint` is not provided, packages will be read from genesis. If -/// `before_checkpoint` is not provided, packages will be read until the latest checkpoint. +/// If `after_checkpoint` is not provided, packages are read from genesis. If `before_checkpoint` +/// is not provided, packages are read until the latest checkpoint. /// /// Returns the latest checkpoint that was read from in this fetch, and a list of all the packages /// that were read. @@ -198,10 +199,10 @@ async fn fetch_packages( /// /// - `linkage.json` -- a JSON serialization of the package's linkage table, mapping dependency /// original IDs to the version of the dependency being depended on and the ID of the object -/// on-chain that contains that version. +/// on chain that contains that version. /// -/// - `origins.json` -- a JSON serialize of the type origin table, mapping type names contained in -/// this package to the version of the package that first introduced that type. +/// - `origins.json` -- a JSON serialization of the type origin table, mapping type names contained +/// in this package to the version of the package that first introduced that type. /// /// - `*.mv` -- a BCS serialization of each compiled module in the package. fn dump_package(output_dir: &Path, pkg: &packages::MovePackage) -> Result<()> { diff --git a/crates/sui-tool/src/commands.rs b/crates/sui-tool/src/commands.rs index 3871143671e3e..5c352104c8aaa 100644 --- a/crates/sui-tool/src/commands.rs +++ b/crates/sui-tool/src/commands.rs @@ -178,7 +178,7 @@ pub enum ToolCommand { }, /// Download all packages to the local filesystem from a GraphQL service. Each package gets its - /// own sub-directory, named for its ID on-chain and version containing two metadata files + /// own sub-directory, named for its ID on chain and version containing two metadata files /// (linkage.json and origins.json), a file containing the overall object and a file for every /// module it contains. Each module file is named for its module name, with a .mv suffix, and /// contains Move bytecode (suitable for passing into a disassembler). From 91bf75e62884d507254f6a7e209d99bbb9c0dafb Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 20 Aug 2024 12:27:05 -0500 Subject: [PATCH 179/232] chore: update rust-sdk --- Cargo.lock | 74 +++++++-------- Cargo.toml | 4 +- crates/sui-rest-api/openapi/openapi.json | 110 +++++++++++++++++++++++ 3 files changed, 149 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 37af0287befea..2278c9ee9a033 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2105,9 +2105,9 @@ dependencies = [ [[package]] name = "bnum" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56953345e39537a3e18bdaeba4cb0c58a78c1f61f361dc0fa7c5c7340ae87c5f" +checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790" [[package]] name = "brotli" @@ -2162,9 +2162,9 @@ dependencies = [ [[package]] name = "bs58" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ "sha2 0.10.8", "tinyvec", @@ -2567,7 +2567,7 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3" dependencies = [ - "bs58 0.5.0", + "bs58 0.5.1", "coins-core", "digest 0.10.7", "hmac 0.12.1", @@ -2601,7 +2601,7 @@ checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" dependencies = [ "base64 0.21.7", "bech32", - "bs58 0.5.0", + "bs58 0.5.1", "digest 0.10.7", "generic-array", "hex", @@ -4659,7 +4659,7 @@ dependencies = [ "secp256k1", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.9.0", "sha2 0.10.8", "sha3 0.10.6", "signature 2.0.0", @@ -8321,7 +8321,7 @@ dependencies = [ "roaring", "serde", "serde_test", - "serde_with 3.8.1", + "serde_with 3.9.0", "sui-protocol-config", "thiserror", "tokio", @@ -10800,9 +10800,9 @@ dependencies = [ [[package]] name = "roaring" -version = "0.10.3" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1c77081a55300e016cb86f2864415b7518741879db925b8d488a0ee0d2da6bf" +checksum = "8f4b84ba6e838ceb47b41de5194a60244fac43d9fe03b71dbe8c5a201081d6d1" dependencies = [ "bytemuck", "byteorder", @@ -11713,9 +11713,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" +checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" dependencies = [ "base64 0.22.1", "chrono", @@ -11725,7 +11725,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "serde_with_macros 3.8.1", + "serde_with_macros 3.9.0", "time", ] @@ -11743,9 +11743,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" +checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" dependencies = [ "darling 0.20.3", "proc-macro2 1.0.78", @@ -12858,7 +12858,7 @@ dependencies = [ "reqwest 0.12.5", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.9.0", "shared-crypto", "sui-authority-aggregation", "sui-config", @@ -12891,7 +12891,7 @@ dependencies = [ "reqwest 0.12.5", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.9.0", "shared-crypto", "sui-bridge", "sui-config", @@ -12997,7 +12997,7 @@ dependencies = [ "rand 0.8.5", "reqwest 0.12.5", "serde", - "serde_with 3.8.1", + "serde_with 3.9.0", "serde_yaml 0.8.26", "sui-keys", "sui-protocol-config", @@ -13076,7 +13076,7 @@ dependencies = [ "serde", "serde-reflection", "serde_json", - "serde_with 3.8.1", + "serde_with 3.9.0", "serde_yaml 0.8.26", "shared-crypto", "signature 1.6.4", @@ -13420,7 +13420,7 @@ dependencies = [ "prometheus", "rand 0.8.5", "serde", - "serde_with 3.8.1", + "serde_with 3.9.0", "serde_yaml 0.8.26", "shared-crypto", "sui-config", @@ -13497,7 +13497,7 @@ dependencies = [ "reqwest 0.12.5", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.9.0", "serde_yaml 0.8.26", "serial_test", "shared-crypto", @@ -13583,7 +13583,7 @@ dependencies = [ "secrecy", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.9.0", "simulacrum", "sui-data-ingestion-core", "sui-json", @@ -13774,7 +13774,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.9.0", "sui-enum-compat-util", "sui-json", "sui-macros", @@ -14248,7 +14248,7 @@ dependencies = [ "move-vm-config", "schemars", "serde", - "serde_with 3.8.1", + "serde_with 3.9.0", "sui-protocol-config-macros", "tracing", ] @@ -14293,7 +14293,7 @@ dependencies = [ "rustls-pemfile 2.1.2", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.9.0", "serde_yaml 0.8.26", "snap", "sui-tls", @@ -14328,7 +14328,7 @@ dependencies = [ "regex", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.9.0", "serde_yaml 0.8.26", "shared-crypto", "shellexpand", @@ -14373,7 +14373,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.9.0", "serde_yaml 0.8.26", "sui-protocol-config", "sui-sdk 0.0.0", @@ -14457,21 +14457,21 @@ dependencies = [ [[package]] name = "sui-sdk" version = "0.0.0" -source = "git+https://github.com/mystenlabs/sui-rust-sdk.git?rev=9a125ed5764fb5bcc1acb6074064bc8f9ea85b38#9a125ed5764fb5bcc1acb6074064bc8f9ea85b38" +source = "git+https://github.com/mystenlabs/sui-rust-sdk.git?rev=bd233b6879b917fb95e17f21927c198e7a60c924#bd233b6879b917fb95e17f21927c198e7a60c924" dependencies = [ "base64ct", "bcs", "blake2", "bnum", - "bs58 0.5.0", + "bs58 0.5.1", "hex", "roaring", "schemars", "serde", "serde_derive", "serde_json", - "serde_with 3.8.1", - "winnow 0.6.9", + "serde_with 3.9.0", + "winnow 0.6.18", ] [[package]] @@ -14495,7 +14495,7 @@ dependencies = [ "reqwest 0.12.5", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.9.0", "shared-crypto", "sui-config", "sui-json", @@ -14812,7 +14812,7 @@ dependencies = [ "prometheus", "rand 0.8.5", "serde", - "serde_with 3.8.1", + "serde_with 3.9.0", "serde_yaml 0.8.26", "shared-crypto", "sui-config", @@ -15058,7 +15058,7 @@ dependencies = [ "serde", "serde-name", "serde_json", - "serde_with 3.8.1", + "serde_with 3.9.0", "serde_yaml 0.8.26", "shared-crypto", "signature 1.6.4", @@ -16032,7 +16032,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime 0.6.8", - "winnow 0.6.9", + "winnow 0.6.18", ] [[package]] @@ -17299,9 +17299,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.9" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 3b3d1836ba1c8..7e815dd578d33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -423,7 +423,7 @@ reqwest = { version = "0.12", default_features = false, features = [ "json", "rustls-tls", ] } -roaring = "=0.10.3" +roaring = "0.10.6" ron = "0.8.0" rstest = "0.16.0" rusoto_core = { version = "0.48.0", default_features = false, features = [ @@ -576,7 +576,7 @@ anemo-cli = { git = "https://github.com/mystenlabs/anemo.git", rev = "dbb5a074c2 anemo-tower = { git = "https://github.com/mystenlabs/anemo.git", rev = "dbb5a074c2d25660525ab5d36d65ff0cb8051949" } # core-types with json format for REST api -sui-sdk2 = { package = "sui-sdk", git = "https://github.com/mystenlabs/sui-rust-sdk.git", rev = "9a125ed5764fb5bcc1acb6074064bc8f9ea85b38", features = ["hash", "serde", "schemars"] } +sui-sdk2 = { package = "sui-sdk", git = "https://github.com/mystenlabs/sui-rust-sdk.git", rev = "bd233b6879b917fb95e17f21927c198e7a60c924", features = ["hash", "serde", "schemars"] } ### Workspace Members ### anemo-benchmark = { path = "crates/anemo-benchmark" } diff --git a/crates/sui-rest-api/openapi/openapi.json b/crates/sui-rest-api/openapi/openapi.json index 2e4ff81d8f3b2..a25feeb9ef023 100644 --- a/crates/sui-rest-api/openapi/openapi.json +++ b/crates/sui-rest-api/openapi/openapi.json @@ -2654,6 +2654,85 @@ ] } } + }, + { + "description": "Certificate is cancelled due to congestion on shared objects", + "type": "object", + "required": [ + "congested_objects", + "error" + ], + "properties": { + "congested_objects": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ObjectId" + } + }, + "error": { + "type": "string", + "enum": [ + "execution_cancelled_due_to_shared_object_congestion" + ] + } + } + }, + { + "description": "Address is denied for this coin type", + "type": "object", + "required": [ + "address", + "coin_type", + "error" + ], + "properties": { + "address": { + "$ref": "#/components/schemas/Address" + }, + "coin_type": { + "type": "string" + }, + "error": { + "type": "string", + "enum": [ + "address_denied_for_coin" + ] + } + } + }, + { + "description": "Coin type is globally paused for use", + "type": "object", + "required": [ + "coin_type", + "error" + ], + "properties": { + "coin_type": { + "type": "string" + }, + "error": { + "type": "string", + "enum": [ + "coin_type_global_pause" + ] + } + } + }, + { + "description": "Certificate is cancelled because randomness could not be generated this epoch", + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string", + "enum": [ + "execution_cancelled_due_to_randomness_unavailable" + ] + } + } } ] }, @@ -5195,6 +5274,37 @@ "$ref": "#/components/schemas/SimpleSignature" } } + }, + { + "type": "object", + "required": [ + "authenticator_data", + "client_data_json", + "scheme", + "signature" + ], + "properties": { + "authenticator_data": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, + "client_data_json": { + "type": "string" + }, + "scheme": { + "type": "string", + "enum": [ + "passkey" + ] + }, + "signature": { + "$ref": "#/components/schemas/SimpleSignature" + } + } } ] }, From ed69f7cce7ac200a849647c5d8efa7113969d44a Mon Sep 17 00:00:00 2001 From: Andrew Schran Date: Tue, 20 Aug 2024 20:00:27 +0100 Subject: [PATCH 180/232] Migrate users of mysten_metrics Histogram to prometheus Histogram (#19043) mysten_metrics variant is difficult to use in Grafana because its precomputation of percentiles makes it impossible to aggregate streams from multiple hosts in a statistically valid way. --- crates/sui-core/src/authority_aggregator.rs | 18 +-- crates/sui-core/src/authority_server.rs | 69 ++++++----- .../checkpoint_executor/metrics.rs | 52 ++++---- .../checkpoints/checkpoint_executor/mod.rs | 16 +-- .../src/checkpoints/checkpoint_output.rs | 4 +- crates/sui-core/src/checkpoints/metrics.rs | 35 +++--- crates/sui-core/src/checkpoints/mod.rs | 4 +- crates/sui-core/src/consensus_adapter.rs | 9 +- crates/sui-core/src/quorum_driver/metrics.rs | 17 ++- crates/sui-core/src/quorum_driver/mod.rs | 4 +- crates/sui-core/src/safe_client.rs | 12 +- .../sui-core/src/transaction_orchestrator.rs | 24 ++-- crates/sui-json-rpc-api/src/lib.rs | 113 +++++++++++------- crates/sui-json-rpc/src/coin_api.rs | 6 +- crates/sui-json-rpc/src/governance_api.rs | 2 +- crates/sui-json-rpc/src/indexer_api.rs | 16 +-- crates/sui-json-rpc/src/read_api.rs | 12 +- crates/sui-network/src/state_sync/metrics.rs | 12 +- crates/sui-network/src/state_sync/mod.rs | 2 +- crates/sui-oracle/src/lib.rs | 8 +- crates/sui-oracle/src/metrics.rs | 22 ++-- crates/sui-types/src/messages_checkpoint.rs | 7 +- narwhal/primary/src/consensus/bullshark.rs | 2 +- narwhal/primary/src/consensus/metrics.rs | 7 +- 24 files changed, 271 insertions(+), 202 deletions(-) diff --git a/crates/sui-core/src/authority_aggregator.rs b/crates/sui-core/src/authority_aggregator.rs index 65955171bcbec..80201ae3530e8 100644 --- a/crates/sui-core/src/authority_aggregator.rs +++ b/crates/sui-core/src/authority_aggregator.rs @@ -8,7 +8,6 @@ use crate::authority_client::{ }; use crate::safe_client::{SafeClient, SafeClientMetrics, SafeClientMetricsBase}; use futures::{future::BoxFuture, stream::FuturesUnordered, StreamExt}; -use mysten_metrics::histogram::Histogram; use mysten_metrics::{monitored_future, spawn_monitored_task, GaugeGuard, MonitorCancellation}; use mysten_network::config::Config; use std::convert::AsRef; @@ -40,8 +39,9 @@ use tracing::{debug, error, info, instrument, trace, trace_span, warn, Instrumen use crate::epoch::committee_store::CommitteeStore; use crate::stake_aggregator::{InsertResult, MultiStakeAggregator, StakeAggregator}; use prometheus::{ - register_int_counter_vec_with_registry, register_int_counter_with_registry, - register_int_gauge_with_registry, IntCounter, IntCounterVec, IntGauge, Registry, + register_histogram_with_registry, register_int_counter_vec_with_registry, + register_int_counter_with_registry, register_int_gauge_with_registry, Histogram, IntCounter, + IntCounterVec, IntGauge, Registry, }; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::string::ToString; @@ -179,16 +179,16 @@ impl AuthAggMetrics { registry, ) .unwrap(), - remaining_tasks_when_reaching_cert_quorum: mysten_metrics::histogram::Histogram::new_in_registry( + remaining_tasks_when_reaching_cert_quorum: register_histogram_with_registry!( "auth_agg_remaining_tasks_when_reaching_cert_quorum", "Number of remaining tasks when reaching certificate quorum", registry, - ), - remaining_tasks_when_cert_broadcasting_post_quorum_timeout: mysten_metrics::histogram::Histogram::new_in_registry( + ).unwrap(), + remaining_tasks_when_cert_broadcasting_post_quorum_timeout: register_histogram_with_registry!( "auth_agg_remaining_tasks_when_cert_broadcasting_post_quorum_timeout", "Number of remaining tasks when post quorum certificate broadcasting times out", registry, - ) + ).unwrap() } } @@ -1667,7 +1667,7 @@ where let metrics = self.metrics.clone(); metrics .remaining_tasks_when_reaching_cert_quorum - .report(remaining_tasks.len() as u64); + .observe(remaining_tasks.len() as f64); if !remaining_tasks.is_empty() { // Use best efforts to send the cert to remaining validators. spawn_monitored_task!(async move { @@ -1677,7 +1677,7 @@ where _ = &mut timeout => { debug!(?tx_digest, "Timed out in post quorum cert broadcasting: {:?}. Remaining tasks: {:?}", timeout_after_quorum, remaining_tasks.len()); metrics.cert_broadcasting_post_quorum_timeout.inc(); - metrics.remaining_tasks_when_cert_broadcasting_post_quorum_timeout.report(remaining_tasks.len() as u64); + metrics.remaining_tasks_when_cert_broadcasting_post_quorum_timeout.observe(remaining_tasks.len() as f64); break; } res = remaining_tasks.next() => { diff --git a/crates/sui-core/src/authority_server.rs b/crates/sui-core/src/authority_server.rs index cb819f18a1a16..872659a522800 100644 --- a/crates/sui-core/src/authority_server.rs +++ b/crates/sui-core/src/authority_server.rs @@ -4,12 +4,11 @@ use anyhow::Result; use async_trait::async_trait; -use mysten_metrics::histogram::Histogram as MystenHistogram; use mysten_metrics::spawn_monitored_task; use narwhal_worker::LazyNarwhalClient; use prometheus::{ - register_int_counter_vec_with_registry, register_int_counter_with_registry, IntCounter, - IntCounterVec, Registry, + register_histogram_with_registry, register_int_counter_vec_with_registry, + register_int_counter_with_registry, Histogram, IntCounter, IntCounterVec, Registry, }; use std::{ io, @@ -166,14 +165,14 @@ impl AuthorityServer { pub struct ValidatorServiceMetrics { pub signature_errors: IntCounter, - pub tx_verification_latency: MystenHistogram, - pub cert_verification_latency: MystenHistogram, - pub consensus_latency: MystenHistogram, - pub handle_transaction_latency: MystenHistogram, - pub submit_certificate_consensus_latency: MystenHistogram, - pub handle_certificate_consensus_latency: MystenHistogram, - pub handle_certificate_non_consensus_latency: MystenHistogram, - pub handle_soft_bundle_certificates_consensus_latency: MystenHistogram, + pub tx_verification_latency: Histogram, + pub cert_verification_latency: Histogram, + pub consensus_latency: Histogram, + pub handle_transaction_latency: Histogram, + pub submit_certificate_consensus_latency: Histogram, + pub handle_certificate_consensus_latency: Histogram, + pub handle_certificate_non_consensus_latency: Histogram, + pub handle_soft_bundle_certificates_consensus_latency: Histogram, num_rejected_tx_in_epoch_boundary: IntCounter, num_rejected_cert_in_epoch_boundary: IntCounter, @@ -194,46 +193,62 @@ impl ValidatorServiceMetrics { registry, ) .unwrap(), - tx_verification_latency: MystenHistogram::new_in_registry( + tx_verification_latency: register_histogram_with_registry!( "validator_service_tx_verification_latency", "Latency of verifying a transaction", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ), - cert_verification_latency: MystenHistogram::new_in_registry( + ) + .unwrap(), + cert_verification_latency: register_histogram_with_registry!( "validator_service_cert_verification_latency", "Latency of verifying a certificate", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ), - consensus_latency: MystenHistogram::new_in_registry( + ) + .unwrap(), + consensus_latency: register_histogram_with_registry!( "validator_service_consensus_latency", "Time spent between submitting a shared obj txn to consensus and getting result", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ), - handle_transaction_latency: MystenHistogram::new_in_registry( + ) + .unwrap(), + handle_transaction_latency: register_histogram_with_registry!( "validator_service_handle_transaction_latency", "Latency of handling a transaction", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ), - handle_certificate_consensus_latency: MystenHistogram::new_in_registry( + ) + .unwrap(), + handle_certificate_consensus_latency: register_histogram_with_registry!( "validator_service_handle_certificate_consensus_latency", "Latency of handling a consensus transaction certificate", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ), - submit_certificate_consensus_latency: MystenHistogram::new_in_registry( + ) + .unwrap(), + submit_certificate_consensus_latency: register_histogram_with_registry!( "validator_service_submit_certificate_consensus_latency", "Latency of submit_certificate RPC handler", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ), - handle_certificate_non_consensus_latency: MystenHistogram::new_in_registry( + ) + .unwrap(), + handle_certificate_non_consensus_latency: register_histogram_with_registry!( "validator_service_handle_certificate_non_consensus_latency", "Latency of handling a non-consensus transaction certificate", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ), - handle_soft_bundle_certificates_consensus_latency: MystenHistogram::new_in_registry( + ) + .unwrap(), + handle_soft_bundle_certificates_consensus_latency: register_histogram_with_registry!( "validator_service_handle_soft_bundle_certificates_consensus_latency", "Latency of handling a consensus soft bundle", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ), + ) + .unwrap(), num_rejected_tx_in_epoch_boundary: register_int_counter_with_registry!( "validator_service_num_rejected_tx_in_epoch_boundary", "Number of rejected transaction during epoch transitioning", diff --git a/crates/sui-core/src/checkpoints/checkpoint_executor/metrics.rs b/crates/sui-core/src/checkpoints/checkpoint_executor/metrics.rs index c51c4c6e61a8f..f81e44e9887cc 100644 --- a/crates/sui-core/src/checkpoints/checkpoint_executor/metrics.rs +++ b/crates/sui-core/src/checkpoints/checkpoint_executor/metrics.rs @@ -1,10 +1,9 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use mysten_metrics::histogram::Histogram; use prometheus::{ - register_int_counter_with_registry, register_int_gauge_with_registry, IntCounter, IntGauge, - Registry, + register_histogram_with_registry, register_int_counter_with_registry, + register_int_gauge_with_registry, Histogram, IntCounter, IntGauge, Registry, }; use std::sync::Arc; @@ -15,11 +14,11 @@ pub struct CheckpointExecutorMetrics { pub checkpoint_exec_errors: IntCounter, pub checkpoint_exec_epoch: IntGauge, pub checkpoint_exec_inflight: IntGauge, - pub checkpoint_exec_latency_us: Histogram, - pub checkpoint_prepare_latency_us: Histogram, + pub checkpoint_exec_latency: Histogram, + pub checkpoint_prepare_latency: Histogram, pub checkpoint_transaction_count: Histogram, - pub checkpoint_contents_age_ms: Histogram, - pub last_executed_checkpoint_age_ms: Histogram, + pub checkpoint_contents_age: Histogram, + pub last_executed_checkpoint_age: Histogram, } impl CheckpointExecutorMetrics { @@ -61,31 +60,38 @@ impl CheckpointExecutorMetrics { registry ) .unwrap(), - checkpoint_exec_latency_us: Histogram::new_in_registry( - "checkpoint_exec_latency_us", - "Latency of executing a checkpoint from enqueue to all effects available, in microseconds", + checkpoint_exec_latency: register_histogram_with_registry!( + "checkpoint_exec_latency", + "Latency of executing a checkpoint from enqueue to all effects available", registry, - ), - checkpoint_prepare_latency_us: Histogram::new_in_registry( - "checkpoint_prepare_latency_us", - "Latency of preparing a checkpoint to enqueue for execution, in microseconds", + ) + .unwrap(), + checkpoint_prepare_latency: register_histogram_with_registry!( + "checkpoint_prepare_latency", + "Latency of preparing a checkpoint to enqueue for execution", registry, - ), - checkpoint_transaction_count: Histogram::new_in_registry( + ) + .unwrap(), + checkpoint_transaction_count: register_histogram_with_registry!( "checkpoint_transaction_count", "Number of transactions in the checkpoint", registry, - ), - checkpoint_contents_age_ms: Histogram::new_in_registry( - "checkpoint_contents_age_ms", + ) + .unwrap(), + checkpoint_contents_age: register_histogram_with_registry!( + "checkpoint_contents_age", "Age of checkpoints when they arrive for execution", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ), - last_executed_checkpoint_age_ms: Histogram::new_in_registry( - "last_executed_checkpoint_age_ms", + ) + .unwrap(), + last_executed_checkpoint_age: register_histogram_with_registry!( + "last_executed_checkpoint_age", "Age of the last executed checkpoint", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry - ), + ) + .unwrap(), }; Arc::new(this) } diff --git a/crates/sui-core/src/checkpoints/checkpoint_executor/mod.rs b/crates/sui-core/src/checkpoints/checkpoint_executor/mod.rs index b1872a9f21723..885904151d1ed 100644 --- a/crates/sui-core/src/checkpoints/checkpoint_executor/mod.rs +++ b/crates/sui-core/src/checkpoints/checkpoint_executor/mod.rs @@ -327,7 +327,7 @@ impl CheckpointExecutor { sequence_number = ?checkpoint.sequence_number, "Received checkpoint summary from state sync" ); - checkpoint.report_checkpoint_age_ms(&self.metrics.checkpoint_contents_age_ms); + checkpoint.report_checkpoint_age(&self.metrics.checkpoint_contents_age); }, Err(RecvError::Lagged(num_skipped)) => { debug!( @@ -411,7 +411,7 @@ impl CheckpointExecutor { self.metrics .last_executed_checkpoint_timestamp_ms .set(checkpoint.timestamp_ms as i64); - checkpoint.report_checkpoint_age_ms(&self.metrics.last_executed_checkpoint_age_ms); + checkpoint.report_checkpoint_age(&self.metrics.last_executed_checkpoint_age); } /// Post processing and plumbing after we executed a checkpoint. This function is guaranteed @@ -741,7 +741,9 @@ async fn execute_checkpoint( let tx_count = execution_digests.len(); debug!("Number of transactions in the checkpoint: {:?}", tx_count); - metrics.checkpoint_transaction_count.report(tx_count as u64); + metrics + .checkpoint_transaction_count + .observe(tx_count as f64); let checkpoint_acc = execute_transactions( execution_digests, @@ -1244,8 +1246,8 @@ async fn execute_transactions( let prepare_elapsed = prepare_start.elapsed(); metrics - .checkpoint_prepare_latency_us - .report(prepare_elapsed.as_micros() as u64); + .checkpoint_prepare_latency + .observe(prepare_elapsed.as_secs_f64()); if checkpoint.sequence_number % CHECKPOINT_PROGRESS_LOG_COUNT_INTERVAL == 0 { info!( "Checkpoint preparation for execution took {:?}", @@ -1274,8 +1276,8 @@ async fn execute_transactions( let exec_elapsed = exec_start.elapsed(); metrics - .checkpoint_exec_latency_us - .report(exec_elapsed.as_micros() as u64); + .checkpoint_exec_latency + .observe(exec_elapsed.as_secs_f64()); if checkpoint.sequence_number % CHECKPOINT_PROGRESS_LOG_COUNT_INTERVAL == 0 { info!("Checkpoint execution took {:?}", exec_elapsed); } diff --git a/crates/sui-core/src/checkpoints/checkpoint_output.rs b/crates/sui-core/src/checkpoints/checkpoint_output.rs index dcf51b801ab4e..4db01df368895 100644 --- a/crates/sui-core/src/checkpoints/checkpoint_output.rs +++ b/crates/sui-core/src/checkpoints/checkpoint_output.rs @@ -74,12 +74,12 @@ impl CheckpointOutput let checkpoint_timestamp = summary.timestamp_ms; let checkpoint_seq = summary.sequence_number; - self.metrics.checkpoint_creation_latency_ms.observe( + self.metrics.checkpoint_creation_latency.observe( summary .timestamp() .elapsed() .unwrap_or_default() - .as_millis() as u64, + .as_secs_f64(), ); let highest_verified_checkpoint = checkpoint_store diff --git a/crates/sui-core/src/checkpoints/metrics.rs b/crates/sui-core/src/checkpoints/metrics.rs index 8c3ad94121f72..36b639f9d2d6a 100644 --- a/crates/sui-core/src/checkpoints/metrics.rs +++ b/crates/sui-core/src/checkpoints/metrics.rs @@ -1,11 +1,11 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use mysten_metrics::histogram::Histogram; use prometheus::{ - register_int_counter_vec_with_registry, register_int_counter_with_registry, - register_int_gauge_vec_with_registry, register_int_gauge_with_registry, IntCounter, - IntCounterVec, IntGauge, IntGaugeVec, Registry, + register_histogram_with_registry, register_int_counter_vec_with_registry, + register_int_counter_with_registry, register_int_gauge_vec_with_registry, + register_int_gauge_with_registry, Histogram, IntCounter, IntCounterVec, IntGauge, IntGaugeVec, + Registry, }; use std::sync::Arc; @@ -21,11 +21,11 @@ pub struct CheckpointMetrics { pub last_skipped_checkpoint_signature_submission: IntGauge, pub last_ignored_checkpoint_signature_received: IntGauge, pub highest_accumulated_epoch: IntGauge, - pub checkpoint_creation_latency_ms: Histogram, + pub checkpoint_creation_latency: Histogram, pub remote_checkpoint_forks: IntCounter, pub split_brain_checkpoint_forks: IntCounter, - pub last_created_checkpoint_age_ms: Histogram, - pub last_certified_checkpoint_age_ms: Histogram, + pub last_created_checkpoint_age: Histogram, + pub last_certified_checkpoint_age: Histogram, } impl CheckpointMetrics { @@ -43,16 +43,18 @@ impl CheckpointMetrics { registry ) .unwrap(), - last_created_checkpoint_age_ms: Histogram::new_in_registry( - "last_created_checkpoint_age_ms", + last_created_checkpoint_age: register_histogram_with_registry!( + "last_created_checkpoint_age", "Age of the last created checkpoint", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry - ), - last_certified_checkpoint_age_ms: Histogram::new_in_registry( - "last_certified_checkpoint_age_ms", + ).unwrap(), + last_certified_checkpoint_age: register_histogram_with_registry!( + "last_certified_checkpoint_age", "Age of the last certified checkpoint", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry - ), + ).unwrap(), checkpoint_errors: register_int_counter_with_registry!( "checkpoint_errors", "Checkpoints errors count", @@ -109,11 +111,12 @@ impl CheckpointMetrics { registry ) .unwrap(), - checkpoint_creation_latency_ms: Histogram::new_in_registry( - "checkpoint_creation_latency_ms", + checkpoint_creation_latency: register_histogram_with_registry!( + "checkpoint_creation_latency", "Latency from consensus commit timstamp to local checkpoint creation in milliseconds", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ), + ).unwrap(), remote_checkpoint_forks: register_int_counter_with_registry!( "remote_checkpoint_forks", "Number of remote checkpoints that forked from local checkpoints", diff --git a/crates/sui-core/src/checkpoints/mod.rs b/crates/sui-core/src/checkpoints/mod.rs index 6b64df4e7877c..bd3387b7fa299 100644 --- a/crates/sui-core/src/checkpoints/mod.rs +++ b/crates/sui-core/src/checkpoints/mod.rs @@ -1511,7 +1511,7 @@ impl CheckpointBuilder { timestamp_ms, matching_randomness_rounds, ); - summary.report_checkpoint_age_ms(&self.metrics.last_created_checkpoint_age_ms); + summary.report_checkpoint_age(&self.metrics.last_created_checkpoint_age); if last_checkpoint_of_epoch { info!( checkpoint_seq = sequence_number, @@ -1894,7 +1894,7 @@ impl CheckpointAggregator { .set(current.summary.sequence_number as i64); current .summary - .report_checkpoint_age_ms(&self.metrics.last_certified_checkpoint_age_ms); + .report_checkpoint_age(&self.metrics.last_certified_checkpoint_age); result.push(summary.into_inner()); self.current = None; continue 'outer; diff --git a/crates/sui-core/src/consensus_adapter.rs b/crates/sui-core/src/consensus_adapter.rs index 8b33d8031fac7..9ddb0a406a223 100644 --- a/crates/sui-core/src/consensus_adapter.rs +++ b/crates/sui-core/src/consensus_adapter.rs @@ -74,7 +74,7 @@ pub struct ConsensusAdapterMetrics { pub sequencing_certificate_success: IntCounterVec, pub sequencing_certificate_failures: IntCounterVec, pub sequencing_certificate_inflight: IntGaugeVec, - pub sequencing_acknowledge_latency: mysten_metrics::histogram::HistogramVec, + pub sequencing_acknowledge_latency: HistogramVec, pub sequencing_certificate_latency: HistogramVec, pub sequencing_certificate_authority_position: Histogram, pub sequencing_certificate_positions_moved: Histogram, @@ -116,12 +116,13 @@ impl ConsensusAdapterMetrics { registry, ) .unwrap(), - sequencing_acknowledge_latency: mysten_metrics::histogram::HistogramVec::new_in_registry( + sequencing_acknowledge_latency: register_histogram_vec_with_registry!( "sequencing_acknowledge_latency", "The latency for acknowledgement from sequencing engine. The overall sequencing latency is measured by the sequencing_certificate_latency metric", &["retry", "tx_type"], + SEQUENCING_CERTIFICATE_LATENCY_SEC_BUCKETS.to_vec(), registry, - ), + ).unwrap(), sequencing_certificate_latency: register_histogram_vec_with_registry!( "sequencing_certificate_latency", "The latency for sequencing a certificate.", @@ -835,7 +836,7 @@ impl ConsensusAdapter { self.metrics .sequencing_acknowledge_latency .with_label_values(&[&bucket, tx_type]) - .report(ack_start.elapsed().as_millis() as u64); + .observe(ack_start.elapsed().as_secs_f64()); }; match select(processed_waiter, submit_inner.boxed()).await { Either::Left((processed, _submit_inner)) => processed, diff --git a/crates/sui-core/src/quorum_driver/metrics.rs b/crates/sui-core/src/quorum_driver/metrics.rs index 0f08b7b110605..a556fac444ec9 100644 --- a/crates/sui-core/src/quorum_driver/metrics.rs +++ b/crates/sui-core/src/quorum_driver/metrics.rs @@ -3,13 +3,12 @@ // SPDX-License-Identifier: Apache-2.0 use prometheus::{ - register_histogram_vec_with_registry, register_int_counter_vec_with_registry, - register_int_counter_with_registry, register_int_gauge_with_registry, HistogramVec, IntCounter, - IntCounterVec, IntGauge, Registry, + register_histogram_vec_with_registry, register_histogram_with_registry, + register_int_counter_vec_with_registry, register_int_counter_with_registry, + register_int_gauge_with_registry, Histogram, HistogramVec, IntCounter, IntCounterVec, IntGauge, + Registry, }; -use mysten_metrics::histogram::Histogram; - const FINALITY_LATENCY_SEC_BUCKETS: &[f64] = &[ 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, @@ -68,11 +67,11 @@ impl QuorumDriverMetrics { registry, ) .unwrap(), - attempt_times_ok_response: Histogram::new_in_registry( + attempt_times_ok_response: register_histogram_with_registry!( "quorum_driver_attempt_times_ok_response", "Total attempt times of ok response", registry, - ), + ).unwrap(), current_requests_in_flight: register_int_gauge_with_registry!( "current_requests_in_flight", "Current number of requests being processed in QuorumDriver", @@ -109,11 +108,11 @@ impl QuorumDriverMetrics { registry, ) .unwrap(), - transaction_retry_count: Histogram::new_in_registry( + transaction_retry_count: register_histogram_with_registry!( "quorum_driver_transaction_retry_count", "Histogram of transaction retry count", registry, - ), + ).unwrap(), current_transactions_in_retry: register_int_gauge_with_registry!( "current_transactions_in_retry", "Current number of transactions in retry loop in QuorumDriver", diff --git a/crates/sui-core/src/quorum_driver/mod.rs b/crates/sui-core/src/quorum_driver/mod.rs index a01afe41206b5..94ffdfdbf6771 100644 --- a/crates/sui-core/src/quorum_driver/mod.rs +++ b/crates/sui-core/src/quorum_driver/mod.rs @@ -127,7 +127,7 @@ impl QuorumDriver { } self.metrics .transaction_retry_count - .report(task.retry_times as u64); + .observe(task.retry_times as f64); } }) .map_err(|e| SuiError::QuorumDriverCommunicationError { @@ -210,7 +210,7 @@ impl QuorumDriver { self.metrics.total_ok_responses.inc(); self.metrics .attempt_times_ok_response - .report(total_attempts as u64); + .observe(total_attempts as f64); Ok((transaction.clone(), resp.clone())) } Err(err) => { diff --git a/crates/sui-core/src/safe_client.rs b/crates/sui-core/src/safe_client.rs index 74c80cd42afd4..750ce1fabcc6f 100644 --- a/crates/sui-core/src/safe_client.rs +++ b/crates/sui-core/src/safe_client.rs @@ -4,9 +4,11 @@ use crate::authority_client::AuthorityAPI; use crate::epoch::committee_store::CommitteeStore; -use mysten_metrics::histogram::{Histogram, HistogramVec}; use prometheus::core::GenericCounter; -use prometheus::{register_int_counter_vec_with_registry, IntCounterVec, Registry}; +use prometheus::{ + register_histogram_vec_with_registry, register_int_counter_vec_with_registry, Histogram, + HistogramVec, IntCounterVec, Registry, +}; use std::collections::HashMap; use std::net::SocketAddr; use std::sync::Arc; @@ -66,12 +68,14 @@ impl SafeClientMetricsBase { registry, ) .unwrap(), - latency: HistogramVec::new_in_registry( + latency: register_histogram_vec_with_registry!( "safe_client_latency", "RPC latency observed by safe client aggregator, group by address and method", &["address", "method"], + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ), + ) + .unwrap(), } } } diff --git a/crates/sui-core/src/transaction_orchestrator.rs b/crates/sui-core/src/transaction_orchestrator.rs index b4a0d1462097b..84993d34e540a 100644 --- a/crates/sui-core/src/transaction_orchestrator.rs +++ b/crates/sui-core/src/transaction_orchestrator.rs @@ -16,13 +16,13 @@ use crate::quorum_driver::{QuorumDriverHandler, QuorumDriverHandlerBuilder, Quor use futures::future::{select, Either, Future}; use futures::FutureExt; use mysten_common::sync::notify_read::NotifyRead; -use mysten_metrics::histogram::{Histogram, HistogramVec}; use mysten_metrics::{spawn_logged_monitored_task, spawn_monitored_task}; use mysten_metrics::{TX_TYPE_SHARED_OBJ_TX, TX_TYPE_SINGLE_WRITER_TX}; use prometheus::core::{AtomicI64, AtomicU64, GenericCounter, GenericGauge}; use prometheus::{ - register_int_counter_vec_with_registry, register_int_counter_with_registry, - register_int_gauge_vec_with_registry, register_int_gauge_with_registry, Registry, + register_histogram_vec_with_registry, register_int_counter_vec_with_registry, + register_int_counter_with_registry, register_int_gauge_vec_with_registry, + register_int_gauge_with_registry, Histogram, Registry, }; use std::net::SocketAddr; use std::ops::Deref; @@ -622,24 +622,30 @@ impl TransactionOrchestratorMetrics { req_in_flight.with_label_values(&[TX_TYPE_SINGLE_WRITER_TX]); let req_in_flight_shared_object = req_in_flight.with_label_values(&[TX_TYPE_SHARED_OBJ_TX]); - let request_latency = HistogramVec::new_in_registry( + let request_latency = register_histogram_vec_with_registry!( "tx_orchestrator_request_latency", "Time spent in processing one Transaction Orchestrator request", &["tx_type"], + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ); - let wait_for_finality_latency = HistogramVec::new_in_registry( + ) + .unwrap(); + let wait_for_finality_latency = register_histogram_vec_with_registry!( "tx_orchestrator_wait_for_finality_latency", "Time spent in waiting for one Transaction Orchestrator request gets finalized", &["tx_type"], + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ); - let local_execution_latency = HistogramVec::new_in_registry( + ) + .unwrap(); + let local_execution_latency = register_histogram_vec_with_registry!( "tx_orchestrator_local_execution_latency", "Time spent in waiting for one Transaction Orchestrator gets locally executed", &["tx_type"], + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ); + ) + .unwrap(); Self { total_req_received_single_writer, diff --git a/crates/sui-json-rpc-api/src/lib.rs b/crates/sui-json-rpc-api/src/lib.rs index 104963812e31f..1f98a9573fd20 100644 --- a/crates/sui-json-rpc-api/src/lib.rs +++ b/crates/sui-json-rpc-api/src/lib.rs @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use anyhow::anyhow; -use mysten_metrics::histogram::Histogram; - pub use bridge::BridgeReadApiClient; pub use bridge::BridgeReadApiOpenRpc; pub use bridge::BridgeReadApiServer; @@ -23,6 +21,8 @@ pub use move_utils::MoveUtilsClient; pub use move_utils::MoveUtilsOpenRpc; pub use move_utils::MoveUtilsServer; use once_cell::sync::Lazy; +use prometheus::register_histogram_with_registry; +use prometheus::Histogram; use prometheus::{register_int_counter_with_registry, IntCounter}; pub use read::ReadApiClient; pub use read::ReadApiOpenRpc; @@ -114,165 +114,190 @@ pub struct JsonRpcMetrics { impl JsonRpcMetrics { pub fn new(registry: &prometheus::Registry) -> Self { Self { - get_objects_limit: Histogram::new_in_registry( + get_objects_limit: register_histogram_with_registry!( "json_rpc_get_objects_limit", "The input limit for multi_get_objects, after applying the cap", registry, - ), - get_objects_result_size: Histogram::new_in_registry( + ) + .unwrap(), + get_objects_result_size: register_histogram_with_registry!( "json_rpc_get_objects_result_size", "The return size for multi_get_objects", registry, - ), + ) + .unwrap(), get_objects_result_size_total: register_int_counter_with_registry!( "json_rpc_get_objects_result_size_total", "The total return size for multi_get_objects", registry ) .unwrap(), - get_tx_blocks_limit: Histogram::new_in_registry( + get_tx_blocks_limit: register_histogram_with_registry!( "json_rpc_get_tx_blocks_limit", "The input limit for get_tx_blocks, after applying the cap", registry, - ), - get_tx_blocks_result_size: Histogram::new_in_registry( + ) + .unwrap(), + get_tx_blocks_result_size: register_histogram_with_registry!( "json_rpc_get_tx_blocks_result_size", "The return size for get_tx_blocks", registry, - ), + ) + .unwrap(), get_tx_blocks_result_size_total: register_int_counter_with_registry!( "json_rpc_get_tx_blocks_result_size_total", "The total return size for get_tx_blocks", registry ) .unwrap(), - get_checkpoints_limit: Histogram::new_in_registry( + get_checkpoints_limit: register_histogram_with_registry!( "json_rpc_get_checkpoints_limit", "The input limit for get_checkpoints, after applying the cap", registry, - ), - get_checkpoints_result_size: Histogram::new_in_registry( + ) + .unwrap(), + get_checkpoints_result_size: register_histogram_with_registry!( "json_rpc_get_checkpoints_result_size", "The return size for get_checkpoints", registry, - ), + ) + .unwrap(), get_checkpoints_result_size_total: register_int_counter_with_registry!( "json_rpc_get_checkpoints_result_size_total", "The total return size for get_checkpoints", registry ) .unwrap(), - get_owned_objects_limit: Histogram::new_in_registry( + get_owned_objects_limit: register_histogram_with_registry!( "json_rpc_get_owned_objects_limit", "The input limit for get_owned_objects, after applying the cap", registry, - ), - get_owned_objects_result_size: Histogram::new_in_registry( + ) + .unwrap(), + get_owned_objects_result_size: register_histogram_with_registry!( "json_rpc_get_owned_objects_result_size", "The return size for get_owned_objects", registry, - ), + ) + .unwrap(), get_owned_objects_result_size_total: register_int_counter_with_registry!( "json_rpc_get_owned_objects_result_size_total", "The total return size for get_owned_objects", registry ) .unwrap(), - get_coins_limit: Histogram::new_in_registry( + get_coins_limit: register_histogram_with_registry!( "json_rpc_get_coins_limit", "The input limit for get_coins, after applying the cap", registry, - ), - get_coins_result_size: Histogram::new_in_registry( + ) + .unwrap(), + get_coins_result_size: register_histogram_with_registry!( "json_rpc_get_coins_result_size", "The return size for get_coins", registry, - ), + ) + .unwrap(), get_coins_result_size_total: register_int_counter_with_registry!( "json_rpc_get_coins_result_size_total", "The total return size for get_coins", registry ) .unwrap(), - get_dynamic_fields_limit: Histogram::new_in_registry( + get_dynamic_fields_limit: register_histogram_with_registry!( "json_rpc_get_dynamic_fields_limit", "The input limit for get_dynamic_fields, after applying the cap", registry, - ), - get_dynamic_fields_result_size: Histogram::new_in_registry( + ) + .unwrap(), + get_dynamic_fields_result_size: register_histogram_with_registry!( "json_rpc_get_dynamic_fields_result_size", "The return size for get_dynamic_fields", registry, - ), + ) + .unwrap(), get_dynamic_fields_result_size_total: register_int_counter_with_registry!( "json_rpc_get_dynamic_fields_result_size_total", "The total return size for get_dynamic_fields", registry ) .unwrap(), - query_tx_blocks_limit: Histogram::new_in_registry( + query_tx_blocks_limit: register_histogram_with_registry!( "json_rpc_query_tx_blocks_limit", "The input limit for query_tx_blocks, after applying the cap", registry, - ), - query_tx_blocks_result_size: Histogram::new_in_registry( + ) + .unwrap(), + query_tx_blocks_result_size: register_histogram_with_registry!( "json_rpc_query_tx_blocks_result_size", "The return size for query_tx_blocks", registry, - ), + ) + .unwrap(), query_tx_blocks_result_size_total: register_int_counter_with_registry!( "json_rpc_query_tx_blocks_result_size_total", "The total return size for query_tx_blocks", registry ) .unwrap(), - query_events_limit: Histogram::new_in_registry( + query_events_limit: register_histogram_with_registry!( "json_rpc_query_events_limit", "The input limit for query_events, after applying the cap", registry, - ), - query_events_result_size: Histogram::new_in_registry( + ) + .unwrap(), + query_events_result_size: register_histogram_with_registry!( "json_rpc_query_events_result_size", "The return size for query_events", registry, - ), + ) + .unwrap(), query_events_result_size_total: register_int_counter_with_registry!( "json_rpc_query_events_result_size_total", "The total return size for query_events", registry ) .unwrap(), - get_stake_sui_result_size: Histogram::new_in_registry( + get_stake_sui_result_size: register_histogram_with_registry!( "json_rpc_get_stake_sui_result_size", "The return size for get_stake_sui", registry, - ), + ) + .unwrap(), get_stake_sui_result_size_total: register_int_counter_with_registry!( "json_rpc_get_stake_sui_result_size_total", "The total return size for get_stake_sui", registry ) .unwrap(), - get_stake_sui_latency: Histogram::new_in_registry( + get_stake_sui_latency: register_histogram_with_registry!( "get_stake_sui_latency", "The latency of get stake sui, in ms", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ), - get_delegated_sui_latency: Histogram::new_in_registry( + ) + .unwrap(), + get_delegated_sui_latency: register_histogram_with_registry!( "get_delegated_sui_latency", "The latency of get delegated sui, in ms", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ), - orchestrator_latency_ms: Histogram::new_in_registry( + ) + .unwrap(), + orchestrator_latency_ms: register_histogram_with_registry!( "json_rpc_orchestrator_latency", "The latency of submitting transaction via transaction orchestrator, in ms", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ), - post_orchestrator_latency_ms: Histogram::new_in_registry( + ) + .unwrap(), + post_orchestrator_latency_ms: register_histogram_with_registry!( "json_rpc_post_orchestrator_latency", "The latency of response processing after transaction orchestrator, in ms", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ), + ) + .unwrap(), } } diff --git a/crates/sui-json-rpc/src/coin_api.rs b/crates/sui-json-rpc/src/coin_api.rs index fafb06d4bfd89..99d7613d7ce61 100644 --- a/crates/sui-json-rpc/src/coin_api.rs +++ b/crates/sui-json-rpc/src/coin_api.rs @@ -373,7 +373,7 @@ impl CoinReadInternal for CoinReadInternalImpl { one_coin_type_only: bool, ) -> RpcInterimResult { let limit = cap_page_limit(limit); - self.metrics.get_coins_limit.report(limit as u64); + self.metrics.get_coins_limit.observe(limit as f64); let state = self.get_state(); let mut data = spawn_monitored_task!(async move { state.get_owned_coins(owner, cursor, limit + 1, one_coin_type_only) @@ -383,7 +383,9 @@ impl CoinReadInternal for CoinReadInternalImpl { let has_next_page = data.len() > limit; data.truncate(limit); - self.metrics.get_coins_result_size.report(data.len() as u64); + self.metrics + .get_coins_result_size + .observe(data.len() as f64); self.metrics .get_coins_result_size_total .inc_by(data.len() as u64); diff --git a/crates/sui-json-rpc/src/governance_api.rs b/crates/sui-json-rpc/src/governance_api.rs index 5f8e514d1d04a..a8a23a41952ed 100644 --- a/crates/sui-json-rpc/src/governance_api.rs +++ b/crates/sui-json-rpc/src/governance_api.rs @@ -54,7 +54,7 @@ impl GovernanceReadApi { self.metrics .get_stake_sui_result_size - .report(result.len() as u64); + .observe(result.len() as f64); self.metrics .get_stake_sui_result_size_total .inc_by(result.len() as u64); diff --git a/crates/sui-json-rpc/src/indexer_api.rs b/crates/sui-json-rpc/src/indexer_api.rs index e29e373dd2872..53709116966b4 100644 --- a/crates/sui-json-rpc/src/indexer_api.rs +++ b/crates/sui-json-rpc/src/indexer_api.rs @@ -148,7 +148,7 @@ impl IndexerApiServer for IndexerApi { with_tracing!(async move { let limit = validate_limit(limit, *QUERY_MAX_RESULT_LIMIT).map_err(SuiRpcInputError::from)?; - self.metrics.get_owned_objects_limit.report(limit as u64); + self.metrics.get_owned_objects_limit.observe(limit as f64); let SuiObjectResponseQuery { filter, options } = query.unwrap_or_default(); let options = options.unwrap_or_default(); let mut objects = self @@ -179,7 +179,7 @@ impl IndexerApiServer for IndexerApi { self.metrics .get_owned_objects_result_size - .report(data.len() as u64); + .observe(data.len() as f64); self.metrics .get_owned_objects_result_size_total .inc_by(data.len() as u64); @@ -202,7 +202,7 @@ impl IndexerApiServer for IndexerApi { ) -> RpcResult { with_tracing!(async move { let limit = cap_page_limit(limit); - self.metrics.query_tx_blocks_limit.report(limit as u64); + self.metrics.query_tx_blocks_limit.observe(limit as f64); let descending = descending_order.unwrap_or_default(); let opts = query.options.unwrap_or_default(); @@ -241,7 +241,7 @@ impl IndexerApiServer for IndexerApi { self.metrics .query_tx_blocks_result_size - .report(data.len() as u64); + .observe(data.len() as f64); self.metrics .query_tx_blocks_result_size_total .inc_by(data.len() as u64); @@ -264,7 +264,7 @@ impl IndexerApiServer for IndexerApi { with_tracing!(async move { let descending = descending_order.unwrap_or_default(); let limit = cap_page_limit(limit); - self.metrics.query_events_limit.report(limit as u64); + self.metrics.query_events_limit.observe(limit as f64); // Retrieve 1 extra item for next cursor let mut data = self .state @@ -282,7 +282,7 @@ impl IndexerApiServer for IndexerApi { let next_cursor = data.last().map_or(cursor, |e| Some(e.id)); self.metrics .query_events_result_size - .report(data.len() as u64); + .observe(data.len() as f64); self.metrics .query_events_result_size_total .inc_by(data.len() as u64); @@ -333,7 +333,7 @@ impl IndexerApiServer for IndexerApi { ) -> RpcResult { with_tracing!(async move { let limit = cap_page_limit(limit); - self.metrics.get_dynamic_fields_limit.report(limit as u64); + self.metrics.get_dynamic_fields_limit.observe(limit as f64); let mut data = self .state .get_dynamic_fields(parent_object_id, cursor, limit + 1) @@ -343,7 +343,7 @@ impl IndexerApiServer for IndexerApi { let next_cursor = data.last().cloned().map_or(cursor, |c| Some(c.0)); self.metrics .get_dynamic_fields_result_size - .report(data.len() as u64); + .observe(data.len() as f64); self.metrics .get_dynamic_fields_result_size_total .inc_by(data.len() as u64); diff --git a/crates/sui-json-rpc/src/read_api.rs b/crates/sui-json-rpc/src/read_api.rs index 6de1c04324208..aa9c96747c13e 100644 --- a/crates/sui-json-rpc/src/read_api.rs +++ b/crates/sui-json-rpc/src/read_api.rs @@ -209,7 +209,7 @@ impl ReadApi { } self.metrics .get_tx_blocks_limit - .report(digests.len() as u64); + .observe(digests.len() as f64); let opts = opts.unwrap_or_default(); @@ -465,7 +465,7 @@ impl ReadApi { self.metrics .get_tx_blocks_result_size - .report(converted_tx_block_resps.len() as u64); + .observe(converted_tx_block_resps.len() as f64); self.metrics .get_tx_blocks_result_size_total .inc_by(converted_tx_block_resps.len() as u64); @@ -542,7 +542,7 @@ impl ReadApiServer for ReadApi { if object_ids.len() <= *QUERY_MAX_RESULT_LIMIT { self.metrics .get_objects_limit - .report(object_ids.len() as u64); + .observe(object_ids.len() as f64); let mut futures = vec![]; for object_id in object_ids { futures.push(self.get_object(object_id, options.clone())); @@ -566,7 +566,7 @@ impl ReadApiServer for ReadApi { self.metrics .get_objects_result_size - .report(objects.len() as u64); + .observe(objects.len() as f64); self.metrics .get_objects_result_size_total .inc_by(objects.len() as u64); @@ -961,7 +961,7 @@ impl ReadApiServer for ReadApi { let state = self.state.clone(); let kv_store = self.transaction_kv_store.clone(); - self.metrics.get_checkpoints_limit.report(limit as u64); + self.metrics.get_checkpoints_limit.observe(limit as f64); let mut data = spawn_monitored_task!(Self::get_checkpoints_internal( state, @@ -985,7 +985,7 @@ impl ReadApiServer for ReadApi { self.metrics .get_checkpoints_result_size - .report(data.len() as u64); + .observe(data.len() as f64); self.metrics .get_checkpoints_result_size_total .inc_by(data.len() as u64); diff --git a/crates/sui-network/src/state_sync/metrics.rs b/crates/sui-network/src/state_sync/metrics.rs index 3d3e5b0ccd53c..f88c1592a66af 100644 --- a/crates/sui-network/src/state_sync/metrics.rs +++ b/crates/sui-network/src/state_sync/metrics.rs @@ -1,8 +1,10 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use mysten_metrics::histogram::Histogram; -use prometheus::{register_int_gauge_with_registry, IntGauge, Registry}; +use prometheus::{ + register_histogram_with_registry, register_int_gauge_with_registry, Histogram, IntGauge, + Registry, +}; use std::sync::Arc; use sui_types::messages_checkpoint::CheckpointSequenceNumber; use tap::Pipe; @@ -84,11 +86,13 @@ impl Inner { ) .unwrap(), - checkpoint_summary_age_ms: Histogram::new_in_registry( + checkpoint_summary_age_ms: register_histogram_with_registry!( "checkpoint_summary_age_ms", "Age of checkpoints summaries when they arrive and are verified.", + mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ), + ) + .unwrap(), } .pipe(Arc::new) } diff --git a/crates/sui-network/src/state_sync/mod.rs b/crates/sui-network/src/state_sync/mod.rs index 0a29e219ebf5e..a4d9bb87ec7c5 100644 --- a/crates/sui-network/src/state_sync/mod.rs +++ b/crates/sui-network/src/state_sync/mod.rs @@ -1089,7 +1089,7 @@ where debug!(checkpoint_seq = ?checkpoint.sequence_number(), "verified checkpoint summary"); if let Some(checkpoint_summary_age_metric) = metrics.checkpoint_summary_age_metric() { - checkpoint.report_checkpoint_age_ms(checkpoint_summary_age_metric); + checkpoint.report_checkpoint_age(checkpoint_summary_age_metric); } current = checkpoint.clone(); diff --git a/crates/sui-oracle/src/lib.rs b/crates/sui-oracle/src/lib.rs index 3f649466280d0..8fbdb85e0a385 100644 --- a/crates/sui-oracle/src/lib.rs +++ b/crates/sui-oracle/src/lib.rs @@ -525,7 +525,7 @@ impl OnChainDataUploader { self.metrics .uploaded_values .with_label_values(&[&data_point.feed_name]) - .observe(data_point.value); + .observe(data_point.value as f64); } else { self.metrics .upload_data_errors @@ -538,7 +538,9 @@ impl OnChainDataUploader { let storage_rebate = effects.gas_cost_summary().storage_rebate; let computation_cost = effects.gas_cost_summary().computation_cost; let net_gas_usage = effects.gas_cost_summary().net_gas_usage(); - self.metrics.computation_gas_used.observe(computation_cost); + self.metrics + .computation_gas_used + .observe(computation_cost as f64); self.metrics.total_gas_cost.inc_by(gas_usage); self.metrics.total_gas_rebate.inc_by(storage_rebate); @@ -660,7 +662,7 @@ impl OnChainDataReader { self.metrics .downloaded_values .with_label_values(&[feed_name]) - .observe((value * METRICS_MULTIPLIER) as u64); + .observe(value * METRICS_MULTIPLIER); self.metrics .download_successes .with_label_values(&[feed_name, &object_id.to_string()]) diff --git a/crates/sui-oracle/src/metrics.rs b/crates/sui-oracle/src/metrics.rs index 552e68ae6da0b..365c677ce45fe 100644 --- a/crates/sui-oracle/src/metrics.rs +++ b/crates/sui-oracle/src/metrics.rs @@ -2,12 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 use prometheus::{ - register_int_counter_vec_with_registry, register_int_counter_with_registry, IntCounter, - IntCounterVec, Registry, + register_histogram_vec_with_registry, register_histogram_with_registry, + register_int_counter_vec_with_registry, register_int_counter_with_registry, Histogram, + HistogramVec, IntCounter, IntCounterVec, Registry, }; -use mysten_metrics::histogram::{Histogram, HistogramVec}; - #[derive(Clone)] pub struct OracleMetrics { pub(crate) data_source_successes: IntCounterVec, @@ -78,18 +77,20 @@ impl OracleMetrics { registry, ) .unwrap(), - uploaded_values: HistogramVec::new_in_registry( + uploaded_values: register_histogram_vec_with_registry!( "oracle_uploaded_values", "Values uploaded on chain", &["feed"], registry, - ), - downloaded_values: HistogramVec::new_in_registry( + ) + .unwrap(), + downloaded_values: register_histogram_vec_with_registry!( "oracle_downloaded_values", "Values downloaded on chain", &["feed"], registry, - ), + ) + .unwrap(), total_gas_cost: register_int_counter_with_registry!( "oracle_total_gas_cost", "Total number of gas used, before gas rebate", @@ -102,11 +103,12 @@ impl OracleMetrics { registry, ) .unwrap(), - computation_gas_used: Histogram::new_in_registry( + computation_gas_used: register_histogram_with_registry!( "oracle_computation_gas_used", "computation gas used", registry, - ), + ) + .unwrap(), total_data_points_uploaded: register_int_counter_with_registry!( "oracle_total_data_points_uploaded", "Total number of data points uploaded", diff --git a/crates/sui-types/src/messages_checkpoint.rs b/crates/sui-types/src/messages_checkpoint.rs index ceea05079d484..163c68ef37630 100644 --- a/crates/sui-types/src/messages_checkpoint.rs +++ b/crates/sui-types/src/messages_checkpoint.rs @@ -25,6 +25,7 @@ use crate::{base_types::AuthorityName, committee::Committee, error::SuiError}; use anyhow::Result; use fastcrypto::hash::MultisetHash; use once_cell::sync::OnceCell; +use prometheus::Histogram; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_with::serde_as; @@ -42,8 +43,6 @@ pub use crate::digests::CheckpointDigest; pub type CheckpointSequenceNumber = u64; pub type CheckpointTimestamp = u64; -use mysten_metrics::histogram::Histogram; - #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CheckpointRequest { /// if a sequence number is specified, return the checkpoint with that sequence number; @@ -265,10 +264,10 @@ impl CheckpointSummary { .map(|e| e.next_epoch_committee.as_slice()) } - pub fn report_checkpoint_age_ms(&self, metrics: &Histogram) { + pub fn report_checkpoint_age(&self, metrics: &Histogram) { SystemTime::now() .duration_since(self.timestamp()) - .map(|latency| metrics.report(latency.as_millis() as u64)) + .map(|latency| metrics.observe(latency.as_secs_f64())) .tap_err(|err| { warn!( checkpoint_seq = self.sequence_number, diff --git a/narwhal/primary/src/consensus/bullshark.rs b/narwhal/primary/src/consensus/bullshark.rs index 267b63012767e..2d8f43cfd1f04 100644 --- a/narwhal/primary/src/consensus/bullshark.rs +++ b/narwhal/primary/src/consensus/bullshark.rs @@ -204,7 +204,7 @@ impl Bullshark { self.metrics .committed_certificates - .report(total_committed_certificates); + .observe(total_committed_certificates as f64); Ok((Outcome::Commit, committed_sub_dags)) } diff --git a/narwhal/primary/src/consensus/metrics.rs b/narwhal/primary/src/consensus/metrics.rs index d855a249327bf..a880d92d50b94 100644 --- a/narwhal/primary/src/consensus/metrics.rs +++ b/narwhal/primary/src/consensus/metrics.rs @@ -1,7 +1,6 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use mysten_metrics::histogram::Histogram as MystenHistogram; use prometheus::{ default_registry, register_histogram_with_registry, register_int_counter_vec_with_registry, register_int_counter_with_registry, register_int_gauge_vec_with_registry, @@ -28,7 +27,7 @@ pub struct ConsensusMetrics { /// The latency between two successful commit rounds pub commit_rounds_latency: Histogram, /// The number of certificates committed per commit round - pub committed_certificates: MystenHistogram, + pub committed_certificates: Histogram, /// The time it takes for a certificate from the moment it gets created /// up to the moment it gets committed. pub certificate_commit_latency: Histogram, @@ -86,11 +85,11 @@ impl ConsensusMetrics { LATENCY_SEC_BUCKETS.to_vec(), registry ).unwrap(), - committed_certificates: MystenHistogram::new_in_registry( + committed_certificates: register_histogram_with_registry!( "committed_certificates", "The number of certificates committed on a commit round", registry - ), + ).unwrap(), certificate_commit_latency: register_histogram_with_registry!( "certificate_commit_latency", "The time it takes for a certificate from the moment it gets created up to the moment it gets committed.", From 2cf1eeb35027fd9c1037d26267e6d23ecd9a0e8d Mon Sep 17 00:00:00 2001 From: Jean-Pierre Smith Date: Tue, 20 Aug 2024 21:10:01 +0200 Subject: [PATCH 181/232] feat: allow specifying OTEL service name with OTEL_SERVICE_NAME (#19044) ## Description This changes allows specifying the open-telemetry service name to the `telemetry-subscribers` crate, by setting the `OTEL_SERVICE_NAME` environment variable. This defaults to sui-node, so there is no change in behaviour when unset. As `OTEL_SERVICE_NAME` is not automatically consulted by the otel rust implementation, this uses the environment variable to perform the update in `telemetry-subscribers`. ## Test plan The change is minor, the code compiles, and the environment variable is spelled correctly. --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/telemetry-subscribers/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/telemetry-subscribers/src/lib.rs b/crates/telemetry-subscribers/src/lib.rs index c320ad39d5b97..ac032093bb193 100644 --- a/crates/telemetry-subscribers/src/lib.rs +++ b/crates/telemetry-subscribers/src/lib.rs @@ -380,6 +380,7 @@ impl TelemetryConfig { let mut file_output = CachedOpenFile::new::<&str>(None).unwrap(); let mut provider = None; let sampler = SamplingFilter::new(config.sample_rate); + let service_name = env::var("OTEL_SERVICE_NAME").unwrap_or("sui-node".to_owned()); if config.enable_otlp_tracing { let trace_file = env::var("TRACE_FILE").ok(); @@ -387,7 +388,7 @@ impl TelemetryConfig { let config = sdk::trace::config() .with_resource(Resource::new(vec![opentelemetry::KeyValue::new( "service.name", - "sui-node", + service_name.clone(), )])) .with_sampler(Sampler::ParentBased(Box::new(sampler.clone()))); @@ -404,7 +405,7 @@ impl TelemetryConfig { .with_span_processor(processor) .build(); - let tracer = p.tracer("sui-node"); + let tracer = p.tracer(service_name); provider = Some(p); tracing_opentelemetry::layer().with_tracer(tracer) From f5898858eedd547dec1484068b41d409085f48d4 Mon Sep 17 00:00:00 2001 From: hayes-mysten <135670682+hayes-mysten@users.noreply.github.com> Date: Tue, 20 Aug 2024 13:00:10 -0700 Subject: [PATCH 182/232] [enoki-sdk] Add sdk methods for managing enoki subnames (#18846) ## Description Describe the changes or additions included in this PR. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --------- Co-authored-by: Pavlos Chrysochoidis <10210143+pchrysochoidis@users.noreply.github.com> --- .changeset/twelve-games-shake.md | 5 +++ sdk/enoki/src/EnokiClient/index.ts | 57 ++++++++++++++++++++++++++++++ sdk/enoki/src/EnokiClient/type.ts | 47 ++++++++++++++++++++++++ sdk/enoki/src/index.ts | 2 +- 4 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 .changeset/twelve-games-shake.md diff --git a/.changeset/twelve-games-shake.md b/.changeset/twelve-games-shake.md new file mode 100644 index 0000000000000..308b6c1c90442 --- /dev/null +++ b/.changeset/twelve-games-shake.md @@ -0,0 +1,5 @@ +--- +'@mysten/enoki': minor +--- + +Add sdk methods for managing enoki subnames diff --git a/sdk/enoki/src/EnokiClient/index.ts b/sdk/enoki/src/EnokiClient/index.ts index cc7f08421b84b..f9f5be737edeb 100644 --- a/sdk/enoki/src/EnokiClient/index.ts +++ b/sdk/enoki/src/EnokiClient/index.ts @@ -4,14 +4,20 @@ import type { CreateSponsoredTransactionApiInput, CreateSponsoredTransactionApiResponse, + CreateSubnameApiInput, + CreateSubnameApiResponse, CreateZkLoginNonceApiInput, CreateZkLoginNonceApiResponse, CreateZkLoginZkpApiInput, CreateZkLoginZkpApiResponse, + DeleteSubnameApiInput, + DeleteSubnameApiResponse, ExecuteSponsoredTransactionApiInput, ExecuteSponsoredTransactionApiResponse, GetAppApiInput, GetAppApiResponse, + GetSubnamesApiInput, + GetSubnamesApiResponse, GetZkLoginApiInput, GetZkLoginApiResponse, } from './type.js'; @@ -29,6 +35,8 @@ export interface EnokiClientConfig { export class EnokiClientError extends Error { errors: { code: string; message: string; data: unknown }[] = []; + status: number; + code: string; constructor(status: number, response: string) { let errors; @@ -46,6 +54,8 @@ export class EnokiClientError extends Error { }); this.errors = errors ?? []; this.name = 'EnokiClientError'; + this.status = status; + this.code = errors?.[0]?.code ?? 'unknown_error'; } } @@ -134,6 +144,53 @@ export class EnokiClient { ); } + getSubnames(input: GetSubnamesApiInput) { + const query = new URLSearchParams(); + if (input.address) { + query.set('address', input.address); + } + if (input.network) { + query.set('network', input.network); + } + if (input.domain) { + query.set('domain', input.domain); + } + return this.#fetch( + 'subnames' + (query.size > 0 ? `?${query.toString()}` : ''), + { + method: 'GET', + }, + ); + } + + createSubname(input: CreateSubnameApiInput) { + return this.#fetch('subnames', { + method: 'POST', + headers: input.jwt + ? { + [ZKLOGIN_HEADER]: input.jwt, + } + : {}, + body: JSON.stringify({ + network: input.network, + domain: input.domain, + subname: input.subname, + targetAddress: input.targetAddress, + }), + }); + } + + deleteSubname(input: DeleteSubnameApiInput) { + this.#fetch('subnames', { + method: 'DELETE', + body: JSON.stringify({ + network: input.network, + domain: input.domain, + subname: input.subname, + }), + }); + } + async #fetch(path: string, init: RequestInit): Promise { const res = await fetch(`${this.#apiUrl}/${this.#version}/${path}`, { ...init, diff --git a/sdk/enoki/src/EnokiClient/type.ts b/sdk/enoki/src/EnokiClient/type.ts index b5fb56ed7f133..77599cdc1ff49 100644 --- a/sdk/enoki/src/EnokiClient/type.ts +++ b/sdk/enoki/src/EnokiClient/type.ts @@ -7,13 +7,21 @@ import type { ZkLoginSignatureInputs } from '@mysten/sui/zklogin'; import type { AuthProvider } from '../EnokiFlow.js'; export type EnokiNetwork = 'mainnet' | 'testnet' | 'devnet'; +export type EnokiDomainNetwork = 'mainnet' | 'testnet'; +export type EnokiSubanameStatus = 'PENDING' | 'ACTIVE'; export interface GetAppApiInput {} export interface GetAppApiResponse { + allowedOrigins: string[]; authenticationProviders: { providerType: AuthProvider; clientId: string; }[]; + domains: { + nftId: string; + name: string; + network: EnokiDomainNetwork; + }[]; } export interface GetZkLoginApiInput { @@ -77,3 +85,42 @@ export interface ExecuteSponsoredTransactionApiInput { export interface ExecuteSponsoredTransactionApiResponse { digest: string; } + +export interface GetSubnamesApiInput { + address?: string; + network?: EnokiDomainNetwork; + domain?: string; +} +export interface GetSubnamesApiResponse { + subnames: { + name: string; + status: EnokiSubanameStatus; + }[]; +} + +export type CreateSubnameApiInput = { + domain: string; + network?: EnokiDomainNetwork; + subname: string; +} & ( + | { + jwt: string; + targetAddress?: never; + } + | { + targetAddress: string; + jwt?: never; + } +); +export interface CreateSubnameApiResponse { + name: string; +} + +export interface DeleteSubnameApiInput { + domain: string; + network?: EnokiDomainNetwork; + subname: string; +} +export interface DeleteSubnameApiResponse { + name: string; +} diff --git a/sdk/enoki/src/index.ts b/sdk/enoki/src/index.ts index 3b713d74a6604..591fbfd8fb30f 100644 --- a/sdk/enoki/src/index.ts +++ b/sdk/enoki/src/index.ts @@ -1,7 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -export { EnokiClient, type EnokiClientConfig } from './EnokiClient/index.js'; +export { EnokiClient, type EnokiClientConfig, EnokiClientError } from './EnokiClient/index.js'; export { EnokiFlow, type AuthProvider, type EnokiFlowConfig } from './EnokiFlow.js'; export { createLocalStorage, From 643b5840c9dc14b549555005b657c4579a7a2bda Mon Sep 17 00:00:00 2001 From: "sui-merge-bot[bot]" <114704316+sui-merge-bot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 20:08:19 +0000 Subject: [PATCH 183/232] Version Packages (#19052) This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @mysten/enoki@0.4.0 ### Minor Changes - f589885: Add sdk methods for managing enoki subnames Co-authored-by: github-actions[bot] --- .changeset/twelve-games-shake.md | 5 ----- sdk/enoki/CHANGELOG.md | 6 ++++++ sdk/enoki/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/twelve-games-shake.md diff --git a/.changeset/twelve-games-shake.md b/.changeset/twelve-games-shake.md deleted file mode 100644 index 308b6c1c90442..0000000000000 --- a/.changeset/twelve-games-shake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@mysten/enoki': minor ---- - -Add sdk methods for managing enoki subnames diff --git a/sdk/enoki/CHANGELOG.md b/sdk/enoki/CHANGELOG.md index 0005f85e4d648..99127ce42ddea 100644 --- a/sdk/enoki/CHANGELOG.md +++ b/sdk/enoki/CHANGELOG.md @@ -1,5 +1,11 @@ # @mysten/enoki +## 0.4.0 + +### Minor Changes + +- f589885: Add sdk methods for managing enoki subnames + ## 0.3.17 ### Patch Changes diff --git a/sdk/enoki/package.json b/sdk/enoki/package.json index bd8098049b7ad..73e5a1d800774 100644 --- a/sdk/enoki/package.json +++ b/sdk/enoki/package.json @@ -1,6 +1,6 @@ { "name": "@mysten/enoki", - "version": "0.3.17", + "version": "0.4.0", "description": "TODO: Description", "license": "Apache-2.0", "author": "Mysten Labs ", From d950fa67a5593596e3e447efd97d050b1b552389 Mon Sep 17 00:00:00 2001 From: Sadhan Sood <106645797+sadhansood@users.noreply.github.com> Date: Tue, 20 Aug 2024 13:46:50 -0700 Subject: [PATCH 184/232] Fix empty result set in wallet monitoring service (#18350) ## Description Handle empty result set. Ran locally, works --- crates/sui-security-watchdog/src/query_runner.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/sui-security-watchdog/src/query_runner.rs b/crates/sui-security-watchdog/src/query_runner.rs index a0ab4ec1054e5..37e1fd95a9815 100644 --- a/crates/sui-security-watchdog/src/query_runner.rs +++ b/crates/sui-security-watchdog/src/query_runner.rs @@ -214,7 +214,8 @@ impl QueryRunner for SnowflakeQueryRunner { let res = self.make_snowflake_api()?.exec(query).await?; match res { QueryResult::Arrow(records) => self.parse_record_batches(records), - // Handle other result types (Json, Empty) with a unified error message + QueryResult::Empty => Ok(Vec::new()), + // Handle other result types Json with a unified error message _ => Err(anyhow!("Unexpected query result type")), } } From 7a030228606e14e3a57f8edf75ffbc6694f3e59d Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 31 Jul 2024 12:46:38 -0500 Subject: [PATCH 185/232] cluster: remove unused ws data --- crates/test-cluster/src/lib.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/crates/test-cluster/src/lib.rs b/crates/test-cluster/src/lib.rs index 8740c39577349..0e730eadfda0e 100644 --- a/crates/test-cluster/src/lib.rs +++ b/crates/test-cluster/src/lib.rs @@ -5,8 +5,6 @@ use futures::Future; use futures::{future::join_all, StreamExt}; use jsonrpsee::core::RpcResult; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; -use jsonrpsee::ws_client::WsClient; -use jsonrpsee::ws_client::WsClientBuilder; use rand::{distributions::*, rngs::OsRng, seq::SliceRandom}; use std::collections::BTreeMap; use std::collections::HashMap; @@ -88,7 +86,6 @@ pub struct FullNodeHandle { pub sui_client: SuiClient, pub rpc_client: HttpClient, pub rpc_url: String, - pub ws_url: String, } impl FullNodeHandle { @@ -96,7 +93,6 @@ impl FullNodeHandle { let rpc_url = format!("http://{}", json_rpc_address); let rpc_client = HttpClientBuilder::default().build(&rpc_url).unwrap(); - let ws_url = format!("ws://{}", json_rpc_address); let sui_client = SuiClientBuilder::default().build(&rpc_url).await.unwrap(); Self { @@ -104,16 +100,8 @@ impl FullNodeHandle { sui_client, rpc_client, rpc_url, - ws_url, } } - - pub async fn ws_client(&self) -> WsClient { - WsClientBuilder::default() - .build(&self.ws_url) - .await - .unwrap() - } } pub struct TestCluster { From 728a4d0271fa6db3ca90681d6dda326d64351fc1 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 31 Jul 2024 16:08:37 -0500 Subject: [PATCH 186/232] keys: convert noisy message to doc comment --- crates/sui-keys/src/keystore.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/sui-keys/src/keystore.rs b/crates/sui-keys/src/keystore.rs index 199e0fe6f4a7f..db622a9535718 100644 --- a/crates/sui-keys/src/keystore.rs +++ b/crates/sui-keys/src/keystore.rs @@ -421,13 +421,10 @@ impl FileBasedKeystore { Ok(()) } + /// Keys saved as Base64 with 33 bytes `flag || privkey` ($BASE64_STR). + /// To see Bech32 format encoding, use `sui keytool export $SUI_ADDRESS` where + /// $SUI_ADDRESS can be found with `sui keytool list`. Or use `sui keytool convert $BASE64_STR` pub fn save_keystore(&self) -> Result<(), anyhow::Error> { - eprintln!( - "Keys saved as Base64 with 33 bytes `flag || privkey` ($BASE64_STR). - To see Bech32 format encoding, use `sui keytool export $SUI_ADDRESS` where - $SUI_ADDRESS can be found with `sui keytool list`. Or use `sui keytool convert $BASE64_STR`." - ); - if let Some(path) = &self.path { let store = serde_json::to_string_pretty( &self From fa9f0972cfa89bf16565510243051cfc5d5841ba Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 31 Jul 2024 16:47:02 -0500 Subject: [PATCH 187/232] rest: redirect /rest -> /v2 One polish item before the rest service can be stabilized is to have route based api versioning. This is done by hosting the main service off of `/v2` and for compatibility for old clients we redirect from the old `/rest` route to the new `/v2` ones. --- .../src/package_store.rs | 3 +-- crates/sui-e2e-tests/tests/rest.rs | 4 +-- .../sui-graphql-rpc/src/test_infra/cluster.rs | 2 +- crates/sui-indexer/tests/ingestion_tests.rs | 2 +- crates/sui-light-client/src/main.rs | 4 +-- crates/sui-node/src/lib.rs | 5 +--- crates/sui-rest-api/Cargo.toml | 1 - crates/sui-rest-api/src/lib.rs | 26 +++++++++++-------- 8 files changed, 22 insertions(+), 25 deletions(-) diff --git a/crates/sui-analytics-indexer/src/package_store.rs b/crates/sui-analytics-indexer/src/package_store.rs index 2878b9852c4d8..94199a6353009 100644 --- a/crates/sui-analytics-indexer/src/package_store.rs +++ b/crates/sui-analytics-indexer/src/package_store.rs @@ -74,10 +74,9 @@ pub struct LocalDBPackageStore { impl LocalDBPackageStore { pub fn new(path: &Path, rest_url: &str) -> Self { - let rest_api_url = format!("{}/rest", rest_url); Self { package_store_tables: PackageStoreTables::new(path), - fallback_client: Client::new(rest_api_url), + fallback_client: Client::new(rest_url), } } diff --git a/crates/sui-e2e-tests/tests/rest.rs b/crates/sui-e2e-tests/tests/rest.rs index 7795e04599008..ff408edee6549 100644 --- a/crates/sui-e2e-tests/tests/rest.rs +++ b/crates/sui-e2e-tests/tests/rest.rs @@ -15,9 +15,7 @@ use test_cluster::TestClusterBuilder; async fn execute_transaction_transfer() { let test_cluster = TestClusterBuilder::new().build().await; - let rest_url = format!("{}/v2", test_cluster.rpc_url()); - - let client = Client::new(rest_url); + let client = Client::new(test_cluster.rpc_url()); let address = SuiAddress::random_for_testing_only(); let amount = 9; diff --git a/crates/sui-graphql-rpc/src/test_infra/cluster.rs b/crates/sui-graphql-rpc/src/test_infra/cluster.rs index 036984929df2b..1625192757f26 100644 --- a/crates/sui-graphql-rpc/src/test_infra/cluster.rs +++ b/crates/sui-graphql-rpc/src/test_infra/cluster.rs @@ -127,7 +127,7 @@ pub async fn serve_executor( let executor_server_handle = tokio::spawn(async move { sui_rest_api::RestService::new_without_version(executor) - .start_service(executor_server_url, Some("/rest".to_owned())) + .start_service(executor_server_url) .await; }); diff --git a/crates/sui-indexer/tests/ingestion_tests.rs b/crates/sui-indexer/tests/ingestion_tests.rs index 06914eeff6471..af67a061bbde3 100644 --- a/crates/sui-indexer/tests/ingestion_tests.rs +++ b/crates/sui-indexer/tests/ingestion_tests.rs @@ -58,7 +58,7 @@ mod ingestion_tests { let server_handle = tokio::spawn(async move { sui_rest_api::RestService::new_without_version(sim) - .start_service(server_url, Some("/rest".to_owned())) + .start_service(server_url) .await; }); // Starts indexer diff --git a/crates/sui-light-client/src/main.rs b/crates/sui-light-client/src/main.rs index f62f5a45e6f4a..555a73448dd25 100644 --- a/crates/sui-light-client/src/main.rs +++ b/crates/sui-light-client/src/main.rs @@ -96,8 +96,8 @@ struct Config { } impl Config { - pub fn rest_url(&self) -> String { - format!("{}/rest", self.full_node_url) + pub fn rest_url(&self) -> &str { + &self.full_node_url } } diff --git a/crates/sui-node/src/lib.rs b/crates/sui-node/src/lib.rs index fe26295dd4c25..db0f848b33209 100644 --- a/crates/sui-node/src/lib.rs +++ b/crates/sui-node/src/lib.rs @@ -2041,10 +2041,7 @@ pub async fn build_http_server( rest_service.with_executor(transaction_orchestrator.clone()) } - let rest_router = rest_service.into_router(); - router = router - .nest("/rest", rest_router.clone()) - .nest("/v2", rest_router); + router = router.merge(rest_service.into_router()); } let listener = tokio::net::TcpListener::bind(&config.json_rpc_address) diff --git a/crates/sui-rest-api/Cargo.toml b/crates/sui-rest-api/Cargo.toml index 95e375770bd2a..b93b8a4497508 100644 --- a/crates/sui-rest-api/Cargo.toml +++ b/crates/sui-rest-api/Cargo.toml @@ -31,6 +31,5 @@ sui-types.workspace = true mysten-network.workspace = true sui-protocol-config.workspace = true - [dev-dependencies] diffy = "0.3" diff --git a/crates/sui-rest-api/src/lib.rs b/crates/sui-rest-api/src/lib.rs index 9efd7f35325a1..59c0b5ffcaac2 100644 --- a/crates/sui-rest-api/src/lib.rs +++ b/crates/sui-rest-api/src/lib.rs @@ -1,7 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use axum::Router; +use axum::{response::Redirect, routing::get, Router}; use mysten_network::callback::CallbackLayer; use openapi::ApiEndpoint; use reader::StateReader; @@ -145,8 +145,14 @@ impl RestService { api.register_endpoints(ENDPOINTS.to_owned()); - api.to_router() - .with_state(self.clone()) + Router::new() + .nest("/v2/", api.to_router().with_state(self.clone())) + .route("/v2", get(|| async { Redirect::permanent("/v2/") })) + // Previously the service used to be hosted at `/rest`. In an effort to migrate folks + // to the new versioned route, we'll issue redirects from `/rest` -> `/v2`. + .route("/rest/*path", axum::routing::method_routing::any(redirect)) + .route("/rest", get(|| async { Redirect::permanent("/v2/") })) + .route("/rest/", get(|| async { Redirect::permanent("/v2/") })) .layer(axum::middleware::map_response_with_state( self, response::append_info_headers, @@ -162,15 +168,9 @@ impl RestService { }) } - pub async fn start_service(self, socket_address: std::net::SocketAddr, base: Option) { - let mut app = self.into_router(); - - if let Some(base) = base { - app = Router::new().nest(&base, app); - } - + pub async fn start_service(self, socket_address: std::net::SocketAddr) { let listener = tokio::net::TcpListener::bind(socket_address).await.unwrap(); - axum::serve(listener, app).await.unwrap(); + axum::serve(listener, self.into_router()).await.unwrap(); } } @@ -196,6 +196,10 @@ fn info() -> openapiv3::v3_1::Info { } } +async fn redirect(axum::extract::Path(path): axum::extract::Path) -> Redirect { + Redirect::permanent(&format!("/v2/{path}")) +} + mod _schemars { use schemars::schema::InstanceType; use schemars::schema::Metadata; From 824b6b6cb4d754a447e5e4c1914322c35d52ae0a Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Thu, 1 Aug 2024 08:52:42 -0500 Subject: [PATCH 188/232] read-store: properly return the highest executed checkpoint Fix the implementation of ReadStore::get_latest_checkpoint for RocksDbStore to properly return the highest executed checkpoint instead of the highest certified checkpoint given this api is explicitly used to signal checkpoint availability. --- crates/sui-core/src/storage.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/sui-core/src/storage.rs b/crates/sui-core/src/storage.rs index d2f6606b9fb95..80e06efb7029a 100644 --- a/crates/sui-core/src/storage.rs +++ b/crates/sui-core/src/storage.rs @@ -233,7 +233,8 @@ impl ReadStore for RocksDbStore { fn get_latest_checkpoint(&self) -> sui_types::storage::error::Result { self.checkpoint_store - .get_latest_certified_checkpoint() + .get_highest_executed_checkpoint() + .map_err(sui_types::storage::error::Error::custom)? .ok_or_else(|| { sui_types::storage::error::Error::missing("unable to get latest checkpoint") }) From b6199ed83d9bcdd01a5ed42c3fb3d114c9f38816 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Thu, 1 Aug 2024 09:00:13 -0500 Subject: [PATCH 189/232] rest: add Debug impl for Page and ResponseContent --- crates/sui-rest-api/src/lib.rs | 1 + crates/sui-rest-api/src/response.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/sui-rest-api/src/lib.rs b/crates/sui-rest-api/src/lib.rs index 59c0b5ffcaac2..550d71f67f69d 100644 --- a/crates/sui-rest-api/src/lib.rs +++ b/crates/sui-rest-api/src/lib.rs @@ -45,6 +45,7 @@ pub enum Direction { Descending, } +#[derive(Debug)] pub struct Page { pub entries: response::ResponseContent>, pub cursor: Option, diff --git a/crates/sui-rest-api/src/response.rs b/crates/sui-rest-api/src/response.rs index 5b3866d68db18..3d2bda9df3a5f 100644 --- a/crates/sui-rest-api/src/response.rs +++ b/crates/sui-rest-api/src/response.rs @@ -19,6 +19,7 @@ use crate::{ pub struct Bcs(pub T); +#[derive(Debug)] pub enum ResponseContent { Bcs(T), Json(J), From b454eb967c8d5e36ef2262809e6aad0b51f214cc Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 12 Aug 2024 09:07:25 -0500 Subject: [PATCH 190/232] rest: add 410 responses to checkpoint apis --- crates/sui-rest-api/openapi/openapi.json | 3 +++ crates/sui-rest-api/src/checkpoints.rs | 28 ++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/crates/sui-rest-api/openapi/openapi.json b/crates/sui-rest-api/openapi/openapi.json index a25feeb9ef023..ced704ee4d2c5 100644 --- a/crates/sui-rest-api/openapi/openapi.json +++ b/crates/sui-rest-api/openapi/openapi.json @@ -368,6 +368,9 @@ }, "404": { "description": "" + }, + "410": { + "description": "" } } } diff --git a/crates/sui-rest-api/src/checkpoints.rs b/crates/sui-rest-api/src/checkpoints.rs index ff11549529862..f6939b76c8b5f 100644 --- a/crates/sui-rest-api/src/checkpoints.rs +++ b/crates/sui-rest-api/src/checkpoints.rs @@ -46,6 +46,7 @@ impl ApiEndpoint for GetCheckpointFull { .build(), ) .response(404, ResponseBuilder::new().build()) + .response(410, ResponseBuilder::new().build()) .build() } @@ -60,7 +61,19 @@ async fn get_checkpoint_full( State(state): State, ) -> Result> { let verified_summary = match checkpoint_id { - CheckpointId::SequenceNumber(s) => state.inner().get_checkpoint_by_sequence_number(s), + CheckpointId::SequenceNumber(s) => { + // Since we need object contents we need to check for the lowest available checkpoint + // with objects that hasn't been pruned + let oldest_checkpoint = state.inner().get_lowest_available_checkpoint_objects()?; + if s < oldest_checkpoint { + return Err(crate::RestError::new( + axum::http::StatusCode::GONE, + "Old checkpoints have been pruned", + )); + } + + state.inner().get_checkpoint_by_sequence_number(s) + } CheckpointId::Digest(d) => state.inner().get_checkpoint_by_digest(&d.into()), }? .ok_or(CheckpointNotFoundError(checkpoint_id))?; @@ -109,6 +122,7 @@ impl ApiEndpoint for GetCheckpoint { .build(), ) .response(404, ResponseBuilder::new().build()) + .response(410, ResponseBuilder::new().build()) .build() } @@ -123,7 +137,17 @@ async fn get_checkpoint( State(state): State, ) -> Result> { let summary = match checkpoint_id { - CheckpointId::SequenceNumber(s) => state.inner().get_checkpoint_by_sequence_number(s), + CheckpointId::SequenceNumber(s) => { + let oldest_checkpoint = state.inner().get_lowest_available_checkpoint()?; + if s < oldest_checkpoint { + return Err(crate::RestError::new( + axum::http::StatusCode::GONE, + "Old checkpoints have been pruned", + )); + } + + state.inner().get_checkpoint_by_sequence_number(s) + } CheckpointId::Digest(d) => state.inner().get_checkpoint_by_digest(&d.into()), }? .ok_or(CheckpointNotFoundError(checkpoint_id))? From d955f7e2ac1e505e17476f01c43aecf233d81fa3 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 16 Aug 2024 22:22:31 -0500 Subject: [PATCH 191/232] rest: introduce a structure error type for client --- Cargo.lock | 1 + crates/sui-light-client/src/main.rs | 2 +- crates/sui-rest-api/Cargo.toml | 1 + crates/sui-rest-api/src/client/mod.rs | 4 +- crates/sui-rest-api/src/client/sdk.rs | 183 +++++++++++++++++++++----- 5 files changed, 154 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2278c9ee9a033..5731c9f3acd34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14381,6 +14381,7 @@ dependencies = [ "tap", "thiserror", "tokio", + "url", ] [[package]] diff --git a/crates/sui-light-client/src/main.rs b/crates/sui-light-client/src/main.rs index 555a73448dd25..0dc3f586f4ee5 100644 --- a/crates/sui-light-client/src/main.rs +++ b/crates/sui-light-client/src/main.rs @@ -186,7 +186,7 @@ async fn download_checkpoint_summary( ) -> anyhow::Result { // Download the checkpoint from the server let client = Client::new(config.rest_url()); - client.get_checkpoint_summary(seq).await + client.get_checkpoint_summary(seq).await.map_err(Into::into) } /// Run binary search to for each end of epoch checkpoint that is missing diff --git a/crates/sui-rest-api/Cargo.toml b/crates/sui-rest-api/Cargo.toml index b93b8a4497508..aa046b7c70b2a 100644 --- a/crates/sui-rest-api/Cargo.toml +++ b/crates/sui-rest-api/Cargo.toml @@ -12,6 +12,7 @@ axum = { workspace = true, features = ["matched-path"] } bcs.workspace = true rand.workspace = true reqwest.workspace = true +url.workspace = true serde.workspace = true serde_json.workspace = true serde_yaml.workspace = true diff --git a/crates/sui-rest-api/src/client/mod.rs b/crates/sui-rest-api/src/client/mod.rs index 15000c16e971c..915563b0798bc 100644 --- a/crates/sui-rest-api/src/client/mod.rs +++ b/crates/sui-rest-api/src/client/mod.rs @@ -2,9 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 pub mod sdk; +use sdk::Result; + +pub use reqwest; use crate::transactions::ExecuteTransactionQueryParameters; -use anyhow::Result; use sui_types::base_types::{ObjectID, SequenceNumber, SuiAddress}; use sui_types::crypto::AuthorityStrongQuorumSignInfo; use sui_types::effects::{TransactionEffects, TransactionEvents}; diff --git a/crates/sui-rest-api/src/client/sdk.rs b/crates/sui-rest-api/src/client/sdk.rs index 9db18f9d8cc61..892c0d2c4835e 100644 --- a/crates/sui-rest-api/src/client/sdk.rs +++ b/crates/sui-rest-api/src/client/sdk.rs @@ -1,8 +1,6 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use anyhow::anyhow; -use anyhow::Result; use reqwest::header::HeaderValue; use reqwest::StatusCode; use reqwest::Url; @@ -57,10 +55,12 @@ pub struct Client { impl Client { pub fn new(url: &str) -> Result { - let mut url = Url::parse(url)?; + let mut url = Url::parse(url).map_err(Error::from_error)?; if url.cannot_be_a_base() { - return Err(anyhow!("provided url '{url}' cannot be used as a base")); + return Err(Error::new_message(format!( + "provided url '{url}' cannot be used as a base" + ))); } url.set_path("/v2/"); @@ -81,12 +81,12 @@ impl Client { &self.inner } - pub(super) fn url(&self) -> &Url { + pub fn url(&self) -> &Url { &self.url } pub async fn node_info(&self) -> Result> { - let url = self.url.join("")?; + let url = self.url().join("")?; let response = self .inner @@ -99,7 +99,7 @@ impl Client { } pub async fn health_check(&self, threshold_seconds: Option) -> Result> { - let url = self.url.join("health")?; + let url = self.url().join("health")?; let query = Threshold { threshold_seconds }; let response = self.inner.get(url).query(&query).send().await?; @@ -108,7 +108,7 @@ impl Client { } pub async fn get_coin_info(&self, coin_type: &StructTag) -> Result> { - let url = self.url.join(&format!("coins/{coin_type}"))?; + let url = self.url().join(&format!("coins/{coin_type}"))?; let response = self .inner @@ -125,7 +125,7 @@ impl Client { account: Address, parameters: &ListAccountOwnedObjectsQueryParameters, ) -> Result>> { - let url = self.url.join(&format!("account/{account}/objects"))?; + let url = self.url().join(&format!("account/{account}/objects"))?; let response = self .inner @@ -139,7 +139,7 @@ impl Client { } pub async fn get_object(&self, object_id: ObjectId) -> Result> { - let url = self.url.join(&format!("objects/{object_id}"))?; + let url = self.url().join(&format!("objects/{object_id}"))?; let response = self .inner @@ -157,7 +157,7 @@ impl Client { version: Version, ) -> Result> { let url = self - .url + .url() .join(&format!("objects/{object_id}/version/{version}"))?; let response = self @@ -175,7 +175,7 @@ impl Client { object_id: ObjectId, parameters: &ListDynamicFieldsQueryParameters, ) -> Result>> { - let url = self.url.join(&format!("objects/{object_id}"))?; + let url = self.url().join(&format!("objects/{object_id}"))?; let response = self .inner @@ -189,7 +189,7 @@ impl Client { } pub async fn get_gas_info(&self) -> Result> { - let url = self.url.join("system/gas")?; + let url = self.url().join("system/gas")?; let response = self .inner @@ -209,7 +209,7 @@ impl Client { } pub async fn get_current_protocol_config(&self) -> Result> { - let url = self.url.join("system/protocol")?; + let url = self.url().join("system/protocol")?; let response = self .inner @@ -225,7 +225,7 @@ impl Client { &self, version: u64, ) -> Result> { - let url = self.url.join(&format!("system/protocol/{version}"))?; + let url = self.url().join(&format!("system/protocol/{version}"))?; let response = self .inner @@ -238,7 +238,7 @@ impl Client { } pub async fn get_system_state_summary(&self) -> Result> { - let url = self.url.join("system")?; + let url = self.url().join("system")?; let response = self .inner @@ -251,7 +251,7 @@ impl Client { } pub async fn get_current_committee(&self) -> Result> { - let url = self.url.join("system/committee")?; + let url = self.url().join("system/committee")?; let response = self .inner @@ -264,7 +264,7 @@ impl Client { } pub async fn get_committee(&self, epoch: EpochId) -> Result> { - let url = self.url.join(&format!("system/committee/{epoch}"))?; + let url = self.url().join(&format!("system/committee/{epoch}"))?; let response = self .inner @@ -281,7 +281,7 @@ impl Client { checkpoint_sequence_number: CheckpointSequenceNumber, ) -> Result> { let url = self - .url + .url() .join(&format!("checkpoints/{checkpoint_sequence_number}"))?; let response = self @@ -305,7 +305,7 @@ impl Client { let checkpoint = page .pop() - .ok_or_else(|| anyhow!("server returned empty checkpoint list"))?; + .ok_or_else(|| Error::new_message("server returned empty checkpoint list"))?; Ok(Response::new(checkpoint, parts)) } @@ -314,7 +314,7 @@ impl Client { &self, parameters: &ListCheckpointsQueryParameters, ) -> Result>> { - let url = self.url.join("checkpoints")?; + let url = self.url().join("checkpoints")?; let response = self .inner @@ -332,7 +332,7 @@ impl Client { checkpoint_sequence_number: CheckpointSequenceNumber, ) -> Result> { let url = self - .url + .url() .join(&format!("checkpoints/{checkpoint_sequence_number}/full"))?; let response = self @@ -349,7 +349,7 @@ impl Client { &self, transaction: &TransactionDigest, ) -> Result> { - let url = self.url.join(&format!("transactions/{transaction}"))?; + let url = self.url().join(&format!("transactions/{transaction}"))?; let response = self .inner @@ -365,7 +365,7 @@ impl Client { &self, parameters: &ListTransactionsQueryParameters, ) -> Result>> { - let url = self.url.join("transactions")?; + let url = self.url().join("transactions")?; let response = self .inner @@ -383,7 +383,7 @@ impl Client { parameters: &ExecuteTransactionQueryParameters, transaction: &SignedTransaction, ) -> Result> { - let url = self.url.join("transactions")?; + let url = self.url().join("transactions")?; let body = bcs::to_bytes(transaction)?; @@ -400,22 +400,27 @@ impl Client { self.bcs(response).await } - fn check_response( + async fn check_response( &self, response: reqwest::Response, ) -> Result<(reqwest::Response, ResponseParts)> { + let parts = ResponseParts::from_reqwest_response(&response); + if !response.status().is_success() { - let status = response.status(); - return Err(anyhow::anyhow!("request failed with status {status}")); - } + let error = match response.text().await { + Ok(body) => Error::new_message(body), + Err(e) => Error::from_error(e), + } + .pipe(|e| e.with_parts(parts)); - let parts = ResponseParts::from_reqwest_response(&response); + return Err(error); + } Ok((response, parts)) } async fn empty(&self, response: reqwest::Response) -> Result> { - let (_response, parts) = self.check_response(response)?; + let (_response, parts) = self.check_response(response).await?; Ok(Response::new((), parts)) } @@ -423,7 +428,7 @@ impl Client { &self, response: reqwest::Response, ) -> Result> { - let (response, parts) = self.check_response(response)?; + let (response, parts) = self.check_response(response).await?; let json = response.json().await?; Ok(Response::new(json, parts)) @@ -433,11 +438,13 @@ impl Client { &self, response: reqwest::Response, ) -> Result> { - let (response, parts) = self.check_response(response)?; + let (response, parts) = self.check_response(response).await?; let bytes = response.bytes().await?; - let bcs = bcs::from_bytes(&bytes)?; - Ok(Response::new(bcs, parts)) + match bcs::from_bytes(&bytes) { + Ok(bcs) => Ok(Response::new(bcs, parts)), + Err(e) => Err(Error::from_error(e).with_parts(parts)), + } } } @@ -553,3 +560,109 @@ impl Response { Response::new(f(inner), state) } } + +pub type Result = std::result::Result; + +type BoxError = Box; + +#[derive(Debug)] +pub struct Error { + inner: Box, +} + +#[derive(Debug)] +struct InnerError { + parts: Option, + message: Option, + source: Option, +} + +impl Error { + fn empty() -> Self { + Self { + inner: Box::new(InnerError { + parts: None, + message: None, + source: None, + }), + } + } + + fn from_error>(error: E) -> Self { + Self::empty().with_error(error.into()) + } + + fn new_message>(message: M) -> Self { + Self::empty().with_message(message.into()) + } + + fn with_parts(mut self, parts: ResponseParts) -> Self { + self.inner.parts.replace(parts); + self + } + + fn with_message(mut self, message: String) -> Self { + self.inner.message.replace(message); + self + } + + fn with_error(mut self, error: BoxError) -> Self { + self.inner.source.replace(error); + self + } + + pub fn status(&self) -> Option { + self.parts().map(|parts| parts.status) + } + + pub fn parts(&self) -> Option<&ResponseParts> { + self.inner.parts.as_ref() + } + + pub fn message(&self) -> Option<&str> { + self.inner.message.as_deref() + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Rest Client Error:")?; + if let Some(status) = self.status() { + write!(f, " {status}")?; + } + + if let Some(message) = self.message() { + write!(f, " '{message}'")?; + } + + if let Some(source) = &self.inner.source { + write!(f, " '{source}'")?; + } + + Ok(()) + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.inner.source.as_deref().map(|e| e as _) + } +} + +impl From for Error { + fn from(error: reqwest::Error) -> Self { + Self::from_error(error) + } +} + +impl From for Error { + fn from(error: bcs::Error) -> Self { + Self::from_error(error) + } +} + +impl From for Error { + fn from(error: url::ParseError) -> Self { + Self::from_error(error) + } +} From d41d7bdb1cdca15587ec85d5fc8c4d3eb54f47c0 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 19 Aug 2024 08:43:38 -0500 Subject: [PATCH 192/232] move-core: add Borrow for Identifier --- .../move/crates/move-core-types/src/identifier.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/external-crates/move/crates/move-core-types/src/identifier.rs b/external-crates/move/crates/move-core-types/src/identifier.rs index 019adcc274eaa..b45a25dbbf459 100644 --- a/external-crates/move/crates/move-core-types/src/identifier.rs +++ b/external-crates/move/crates/move-core-types/src/identifier.rs @@ -234,6 +234,12 @@ impl Borrow for Identifier { } } +impl Borrow for Identifier { + fn borrow(&self) -> &str { + &self.0 + } +} + impl ToOwned for IdentStr { type Owned = Identifier; From 6166edcf2ba14f8ddb11ba68e2536f9947625bf2 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 19 Aug 2024 08:44:25 -0500 Subject: [PATCH 193/232] types: add From impls for TransactionExpiration and Command --- crates/sui-types/src/sui_sdk2_conversions.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/crates/sui-types/src/sui_sdk2_conversions.rs b/crates/sui-types/src/sui_sdk2_conversions.rs index 6b64a26fb5110..88a73019cd19d 100644 --- a/crates/sui-types/src/sui_sdk2_conversions.rs +++ b/crates/sui-types/src/sui_sdk2_conversions.rs @@ -50,6 +50,7 @@ bcs_convert_impl!( ); bcs_convert_impl!(crate::signature::GenericSignature, UserSignature); bcs_convert_impl!(crate::effects::TransactionEvents, TransactionEvents); +bcs_convert_impl!(crate::transaction::Command, Command); impl From> for ValidatorAggregatedSignature @@ -384,3 +385,21 @@ impl From for UnchangedSharedKind { } } } + +impl From for TransactionExpiration { + fn from(value: crate::transaction::TransactionExpiration) -> Self { + match value { + crate::transaction::TransactionExpiration::None => Self::None, + crate::transaction::TransactionExpiration::Epoch(epoch) => Self::Epoch(epoch), + } + } +} + +impl From for crate::transaction::TransactionExpiration { + fn from(value: TransactionExpiration) -> Self { + match value { + TransactionExpiration::None => Self::None, + TransactionExpiration::Epoch(epoch) => Self::Epoch(epoch), + } + } +} From 8c44a8e9c264461317c4d836423734be92332a06 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 19 Aug 2024 08:54:00 -0500 Subject: [PATCH 194/232] types: move TransactionExecutor from sui-rest-api to sui-types Move the TransactionExecutor trait from sui-rest-api to sui-types in order to avoid needing to have sui-core depend on sui-rest-api. --- Cargo.lock | 1 - crates/sui-core/Cargo.toml | 1 - crates/sui-core/src/rest_index.rs | 2 +- crates/sui-core/src/transaction_orchestrator.rs | 9 +++------ crates/sui-rest-api/src/lib.rs | 3 ++- .../sui-rest-api/src/transactions/execution.rs | 17 +---------------- crates/sui-rest-api/src/transactions/mod.rs | 1 - crates/sui-types/Cargo.toml | 1 + crates/sui-types/src/lib.rs | 1 + crates/sui-types/src/transaction_executor.rs | 17 +++++++++++++++++ 10 files changed, 26 insertions(+), 27 deletions(-) create mode 100644 crates/sui-types/src/transaction_executor.rs diff --git a/Cargo.lock b/Cargo.lock index 5731c9f3acd34..0a0a3e0de15c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13093,7 +13093,6 @@ dependencies = [ "sui-move-build", "sui-network", "sui-protocol-config", - "sui-rest-api", "sui-simulator", "sui-storage", "sui-swarm-config", diff --git a/crates/sui-core/Cargo.toml b/crates/sui-core/Cargo.toml index 4afe2e8be8a0b..a2103140851db 100644 --- a/crates/sui-core/Cargo.toml +++ b/crates/sui-core/Cargo.toml @@ -85,7 +85,6 @@ sui-framework.workspace = true sui-swarm-config.workspace = true sui-genesis-builder.workspace = true sui-json-rpc-types.workspace = true -sui-rest-api.workspace = true sui-macros.workspace = true sui-move-build.workspace = true sui-network.workspace = true diff --git a/crates/sui-core/src/rest_index.rs b/crates/sui-core/src/rest_index.rs index 67d9de2ed69ee..beb762c6d6a7f 100644 --- a/crates/sui-core/src/rest_index.rs +++ b/crates/sui-core/src/rest_index.rs @@ -15,13 +15,13 @@ use std::path::PathBuf; use std::sync::Arc; use std::sync::Mutex; use std::time::Instant; -use sui_rest_api::CheckpointData; use sui_types::base_types::MoveObjectType; use sui_types::base_types::ObjectID; use sui_types::base_types::SequenceNumber; use sui_types::base_types::SuiAddress; use sui_types::digests::TransactionDigest; use sui_types::dynamic_field::{DynamicFieldInfo, DynamicFieldType}; +use sui_types::full_checkpoint_content::CheckpointData; use sui_types::layout_resolver::LayoutResolver; use sui_types::messages_checkpoint::CheckpointContents; use sui_types::object::Object; diff --git a/crates/sui-core/src/transaction_orchestrator.rs b/crates/sui-core/src/transaction_orchestrator.rs index 84993d34e540a..42bf5158cef13 100644 --- a/crates/sui-core/src/transaction_orchestrator.rs +++ b/crates/sui-core/src/transaction_orchestrator.rs @@ -717,18 +717,15 @@ impl TransactionOrchestratorMetrics { } #[async_trait::async_trait] -impl sui_rest_api::TransactionExecutor for TransactiondOrchestrator +impl sui_types::transaction_executor::TransactionExecutor for TransactiondOrchestrator where A: AuthorityAPI + Send + Sync + 'static + Clone, { async fn execute_transaction( &self, - request: sui_types::quorum_driver_types::ExecuteTransactionRequestV3, + request: ExecuteTransactionRequestV3, client_addr: Option, - ) -> Result< - sui_types::quorum_driver_types::ExecuteTransactionResponseV3, - sui_types::quorum_driver_types::QuorumDriverError, - > { + ) -> Result { self.execute_transaction_v3(request, client_addr).await } } diff --git a/crates/sui-rest-api/src/lib.rs b/crates/sui-rest-api/src/lib.rs index 550d71f67f69d..18993abb1b23e 100644 --- a/crates/sui-rest-api/src/lib.rs +++ b/crates/sui-rest-api/src/lib.rs @@ -7,6 +7,7 @@ use openapi::ApiEndpoint; use reader::StateReader; use std::sync::Arc; use sui_types::storage::RestStateReader; +use sui_types::transaction_executor::TransactionExecutor; use tap::Pipe; pub mod accept; @@ -32,7 +33,7 @@ pub use client::Client; pub use error::{RestError, Result}; pub use metrics::RestMetrics; pub use sui_types::full_checkpoint_content::{CheckpointData, CheckpointTransaction}; -pub use transactions::{ExecuteTransactionQueryParameters, TransactionExecutor}; +pub use transactions::ExecuteTransactionQueryParameters; pub const TEXT_PLAIN_UTF_8: &str = "text/plain; charset=utf-8"; pub const APPLICATION_BCS: &str = "application/bcs"; diff --git a/crates/sui-rest-api/src/transactions/execution.rs b/crates/sui-rest-api/src/transactions/execution.rs index c075ad6a3de99..9611b82c4bf15 100644 --- a/crates/sui-rest-api/src/transactions/execution.rs +++ b/crates/sui-rest-api/src/transactions/execution.rs @@ -11,6 +11,7 @@ use sui_sdk2::types::{ Address, BalanceChange, CheckpointSequenceNumber, Object, Owner, SignedTransaction, TransactionEffects, TransactionEvents, ValidatorAggregatedSignature, }; +use sui_types::transaction_executor::TransactionExecutor; use tap::Pipe; use crate::openapi::{ @@ -20,22 +21,6 @@ use crate::response::Bcs; use crate::{accept::AcceptFormat, response::ResponseContent}; use crate::{RestService, Result}; -/// Trait to define the interface for how the REST service interacts with a a QuorumDriver or a -/// simulated transaction executor. -#[async_trait::async_trait] -pub trait TransactionExecutor: Send + Sync { - async fn execute_transaction( - &self, - request: sui_types::quorum_driver_types::ExecuteTransactionRequestV3, - client_addr: Option, - ) -> Result< - sui_types::quorum_driver_types::ExecuteTransactionResponseV3, - sui_types::quorum_driver_types::QuorumDriverError, - >; - - //TODO include Simulate functionality -} - pub struct ExecuteTransaction; impl ApiEndpoint for ExecuteTransaction { diff --git a/crates/sui-rest-api/src/transactions/mod.rs b/crates/sui-rest-api/src/transactions/mod.rs index 26921104d8c58..6e9ffed21492d 100644 --- a/crates/sui-rest-api/src/transactions/mod.rs +++ b/crates/sui-rest-api/src/transactions/mod.rs @@ -6,7 +6,6 @@ pub use execution::EffectsFinality; pub use execution::ExecuteTransaction; pub use execution::ExecuteTransactionQueryParameters; pub use execution::TransactionExecutionResponse; -pub use execution::TransactionExecutor; use axum::extract::{Path, Query, State}; use axum::http::StatusCode; diff --git a/crates/sui-types/Cargo.toml b/crates/sui-types/Cargo.toml index 5d2f6876ab28b..90c35908f1b0a 100644 --- a/crates/sui-types/Cargo.toml +++ b/crates/sui-types/Cargo.toml @@ -7,6 +7,7 @@ publish = false edition = "2021" [dependencies] +async-trait.workspace = true anemo.workspace = true anyhow.workspace = true bincode.workspace = true diff --git a/crates/sui-types/src/lib.rs b/crates/sui-types/src/lib.rs index 2c029c52cead5..ea47b7e082ef8 100644 --- a/crates/sui-types/src/lib.rs +++ b/crates/sui-types/src/lib.rs @@ -82,6 +82,7 @@ pub mod sui_system_state; pub mod supported_protocol_versions; pub mod traffic_control; pub mod transaction; +pub mod transaction_executor; pub mod transfer; pub mod versioned; pub mod zk_login_authenticator; diff --git a/crates/sui-types/src/transaction_executor.rs b/crates/sui-types/src/transaction_executor.rs new file mode 100644 index 0000000000000..8fd2ebc742a28 --- /dev/null +++ b/crates/sui-types/src/transaction_executor.rs @@ -0,0 +1,17 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::quorum_driver_types::ExecuteTransactionRequestV3; +use crate::quorum_driver_types::ExecuteTransactionResponseV3; +use crate::quorum_driver_types::QuorumDriverError; + +/// Trait to define the interface for how the REST service interacts with a a QuorumDriver or a +/// simulated transaction executor. +#[async_trait::async_trait] +pub trait TransactionExecutor: Send + Sync { + async fn execute_transaction( + &self, + request: ExecuteTransactionRequestV3, + client_addr: Option, + ) -> Result; +} From 3ab3cef3adc65a272047213c5b005774bad309c8 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 19 Aug 2024 08:55:05 -0500 Subject: [PATCH 195/232] types: add a digest method to TransactionData --- crates/sui-types/src/transaction.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/sui-types/src/transaction.rs b/crates/sui-types/src/transaction.rs index a2e37eddbd138..be40bcd989d8b 100644 --- a/crates/sui-types/src/transaction.rs +++ b/crates/sui-types/src/transaction.rs @@ -1902,6 +1902,10 @@ impl TransactionData { .iter() .any(|obj| obj.id() == SUI_RANDOMNESS_STATE_OBJECT_ID) } + + pub fn digest(&self) -> TransactionDigest { + TransactionDigest::new(default_hash(self)) + } } #[enum_dispatch] @@ -2378,7 +2382,7 @@ impl Message for SenderSignedData { /// Computes the tx digest that encodes the Rust type prefix from Signable trait. fn digest(&self) -> Self::DigestType { - TransactionDigest::new(default_hash(&self.intent_message().value)) + self.intent_message().value.digest() } } From a2e3e99ae4e49dc688bbd7b1c0e4bad05ba0b189 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 19 Aug 2024 08:58:38 -0500 Subject: [PATCH 196/232] sui-core: remove unnecessary async from a few functions --- crates/sui-core/src/authority.rs | 73 ++++++++----------- .../sui-core/src/transaction_input_loader.rs | 4 +- .../src/unit_tests/authority_tests.rs | 1 - .../sui-transactional-test-runner/src/lib.rs | 4 +- 4 files changed, 32 insertions(+), 50 deletions(-) diff --git a/crates/sui-core/src/authority.rs b/crates/sui-core/src/authority.rs index c6ec89b460ff7..409a9f67f8de4 100644 --- a/crates/sui-core/src/authority.rs +++ b/crates/sui-core/src/authority.rs @@ -865,15 +865,12 @@ impl AuthorityState { self.get_backing_package_store().as_ref(), )?; - let (input_objects, receiving_objects) = self - .input_loader - .read_objects_for_signing( - Some(tx_digest), - &input_object_kinds, - &receiving_objects_refs, - epoch_store.epoch(), - ) - .await?; + let (input_objects, receiving_objects) = self.input_loader.read_objects_for_signing( + Some(tx_digest), + &input_object_kinds, + &receiving_objects_refs, + epoch_store.epoch(), + )?; let (_gas_status, checked_input_objects) = sui_transaction_checks::check_transaction_input( epoch_store.protocol_config(), @@ -1162,9 +1159,7 @@ impl AuthorityState { debug!("execute_certificate_internal"); let tx_digest = certificate.digest(); - let input_objects = self - .read_objects_for_execution(certificate, epoch_store) - .await?; + let input_objects = self.read_objects_for_execution(certificate, epoch_store)?; if expected_effects_digest.is_none() { // We could be re-executing a previously executed but uncommitted transaction, perhaps after @@ -1191,7 +1186,7 @@ impl AuthorityState { .tap_err(|e| info!(?tx_digest, "process_certificate failed: {e}")) } - pub async fn read_objects_for_execution( + pub fn read_objects_for_execution( &self, certificate: &VerifiedExecutableTransaction, epoch_store: &Arc, @@ -1202,14 +1197,12 @@ impl AuthorityState { .execution_load_input_objects_latency .start_timer(); let input_objects = &certificate.data().transaction_data().input_objects()?; - self.input_loader - .read_objects_for_execution( - epoch_store.as_ref(), - &certificate.key(), - input_objects, - epoch_store.epoch(), - ) - .await + self.input_loader.read_objects_for_execution( + epoch_store.as_ref(), + &certificate.key(), + input_objects, + epoch_store.epoch(), + ) } /// Test only wrapper for `try_execute_immediately()` above, useful for checking errors if the @@ -1736,16 +1729,13 @@ impl AuthorityState { 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(), - ) - .await?; + 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(); @@ -1955,16 +1945,13 @@ impl AuthorityState { self.get_backing_package_store().as_ref(), )?; - let (mut input_objects, receiving_objects) = self - .input_loader - .read_objects_for_signing( - // We don't want to cache this transaction since it's a dev inspect. - None, - &input_object_kinds, - &receiving_object_refs, - epoch_store.epoch(), - ) - .await?; + let (mut input_objects, receiving_objects) = self.input_loader.read_objects_for_signing( + // We don't want to cache this transaction since it's a dev inspect. + None, + &input_object_kinds, + &receiving_object_refs, + epoch_store.epoch(), + )?; // Create and use a dummy gas object if there is no gas object provided. let dummy_gas_object = Object::new_gas_with_balance_and_owner_for_testing( @@ -4853,9 +4840,7 @@ impl AuthorityState { ) .await?; - let input_objects = self - .read_objects_for_execution(&executable_tx, epoch_store) - .await?; + let input_objects = self.read_objects_for_execution(&executable_tx, epoch_store)?; let (temporary_store, effects, _execution_error_opt) = self.prepare_certificate(&execution_guard, &executable_tx, input_objects, epoch_store)?; diff --git a/crates/sui-core/src/transaction_input_loader.rs b/crates/sui-core/src/transaction_input_loader.rs index a33fbd1e45f89..b9f1028598c88 100644 --- a/crates/sui-core/src/transaction_input_loader.rs +++ b/crates/sui-core/src/transaction_input_loader.rs @@ -34,7 +34,7 @@ impl TransactionInputLoader { /// a single hash map lookup when notify_read_objects_for_execution is called later. /// TODO: implement this caching #[instrument(level = "trace", skip_all)] - pub async fn read_objects_for_signing( + pub fn read_objects_for_signing( &self, _tx_digest_for_caching: Option<&TransactionDigest>, input_object_kinds: &[InputObjectKind], @@ -122,7 +122,7 @@ impl TransactionInputLoader { /// cached, but only with appropriate invalidation logic for when an object is received by a /// different tx first. #[instrument(level = "trace", skip_all)] - pub async fn read_objects_for_execution( + pub fn read_objects_for_execution( &self, shared_lock_store: &impl GetSharedLocks, tx_key: &TransactionKey, diff --git a/crates/sui-core/src/unit_tests/authority_tests.rs b/crates/sui-core/src/unit_tests/authority_tests.rs index b0b48a4dfedb6..75e72190ec5f9 100644 --- a/crates/sui-core/src/unit_tests/authority_tests.rs +++ b/crates/sui-core/src/unit_tests/authority_tests.rs @@ -6156,7 +6156,6 @@ async fn test_consensus_handler_congestion_control_transaction_cancellation() { .unwrap(), authority.epoch_store_for_testing().epoch(), ) - .await .unwrap(); // The lamport version should be the lamport version of the owned objects. diff --git a/crates/sui-transactional-test-runner/src/lib.rs b/crates/sui-transactional-test-runner/src/lib.rs index fc4b5d7b22bfb..854ab9d096d81 100644 --- a/crates/sui-transactional-test-runner/src/lib.rs +++ b/crates/sui-transactional-test-runner/src/lib.rs @@ -142,9 +142,7 @@ impl TransactionalAdapter for ValidatorWithFullnode { ); let epoch_store = self.validator.load_epoch_store_one_call_per_task().clone(); - self.validator - .read_objects_for_execution(&tx, &epoch_store) - .await + self.validator.read_objects_for_execution(&tx, &epoch_store) } fn prepare_txn( From 15a406c583e8ef5eb16c9e5d207664cfc79eb8b6 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 19 Aug 2024 09:05:14 -0500 Subject: [PATCH 197/232] protocol-config: correct doc comment for max_tx_gas parameter --- crates/sui-protocol-config/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sui-protocol-config/src/lib.rs b/crates/sui-protocol-config/src/lib.rs index 58b9bed05183a..e62326fa160d5 100644 --- a/crates/sui-protocol-config/src/lib.rs +++ b/crates/sui-protocol-config/src/lib.rs @@ -695,7 +695,7 @@ pub struct ProtocolConfig { /// Max number of publish or upgrade commands allowed in a programmable transaction block. max_publish_or_upgrade_per_ptb: Option, - /// Maximum number of gas units that a single MoveCall transaction can use. Enforced by the Sui adapter. + /// Maximum gas budget in MIST that a transaction can use. max_tx_gas: Option, /// Maximum amount of the proposed gas price in MIST (defined in the transaction). From 2d7a7aae0cc0c37eef631d4dae2f206c08783641 Mon Sep 17 00:00:00 2001 From: plam-ml <127577476+plam-ml@users.noreply.github.com> Date: Tue, 20 Aug 2024 14:35:47 -0700 Subject: [PATCH 198/232] remove hover label (#19053) ## Description https://linear.app/mysten-labs/issue/APPS-246/remove-nft-name-andor-object-id-from-thumbnail-image ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- apps/wallet/src/ui/app/pages/home/nfts/VisualAssets.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wallet/src/ui/app/pages/home/nfts/VisualAssets.tsx b/apps/wallet/src/ui/app/pages/home/nfts/VisualAssets.tsx index d1327c728ac06..ccc3cba329863 100644 --- a/apps/wallet/src/ui/app/pages/home/nfts/VisualAssets.tsx +++ b/apps/wallet/src/ui/app/pages/home/nfts/VisualAssets.tsx @@ -65,7 +65,7 @@ export default function VisualAssets({ items }: { items: SuiObjectData[] }) { item?.objectType === object.type)} + hideLabel objectId={object.objectId} size="lg" animateHover From c6eb1e73a0f2486ad7a7c884aca8138842822d97 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 20 Aug 2024 17:55:16 -0500 Subject: [PATCH 199/232] rest: cleanup --- crates/sui-rest-api/src/checkpoints.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/sui-rest-api/src/checkpoints.rs b/crates/sui-rest-api/src/checkpoints.rs index f6939b76c8b5f..334e1b4cebbe0 100644 --- a/crates/sui-rest-api/src/checkpoints.rs +++ b/crates/sui-rest-api/src/checkpoints.rs @@ -59,7 +59,7 @@ async fn get_checkpoint_full( Path(checkpoint_id): Path, accept: AcceptFormat, State(state): State, -) -> Result> { +) -> Result> { let verified_summary = match checkpoint_id { CheckpointId::SequenceNumber(s) => { // Since we need object contents we need to check for the lowest available checkpoint @@ -85,8 +85,7 @@ async fn get_checkpoint_full( let checkpoint_data = state .inner() - .get_checkpoint_data(verified_summary, checkpoint_contents)? - .into(); + .get_checkpoint_data(verified_summary, checkpoint_contents)?; match accept { AcceptFormat::Json => ResponseContent::Json(checkpoint_data), From 61dc132b7d627304c1cc65dcb4046f3df7c16154 Mon Sep 17 00:00:00 2001 From: Ge Gao <106119108+gegaowp@users.noreply.github.com> Date: Tue, 20 Aug 2024 22:08:06 -0400 Subject: [PATCH 200/232] indexer:add experimental warning (#18890) --- crates/sui-indexer/README.md | 2 ++ crates/sui-indexer/src/main.rs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/sui-indexer/README.md b/crates/sui-indexer/README.md index cce84ca39feba..d81a7f2e89910 100644 --- a/crates/sui-indexer/README.md +++ b/crates/sui-indexer/README.md @@ -1,5 +1,7 @@ Sui indexer is an off-fullnode service to serve data from Sui protocol, including both data directly generated from chain and derivative data. +⚠ **Warning:** Sui indexer is still experimental and we expect occasional breaking changes that require backfills. + ## Architecture ![enhanced_FN](https://user-images.githubusercontent.com/106119108/221022505-a1d873c6-60e2-45f1-b2aa-e50192c4dfbb.png) diff --git a/crates/sui-indexer/src/main.rs b/crates/sui-indexer/src/main.rs index 02a92f7a507b2..dbb95bbc991a6 100644 --- a/crates/sui-indexer/src/main.rs +++ b/crates/sui-indexer/src/main.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use clap::Parser; -use tracing::info; +use tracing::{info, warn}; use sui_indexer::errors::IndexerError; use sui_indexer::metrics::start_prometheus_server; @@ -14,6 +14,7 @@ async fn main() -> Result<(), IndexerError> { let _guard = telemetry_subscribers::TelemetryConfig::new() .with_env() .init(); + warn!("WARNING: Sui indexer is still experimental and we expect occasional breaking changes that require backfills."); let mut indexer_config = IndexerConfig::parse(); // TODO: remove. Temporary safeguard to migrate to `rpc_client_url` usage From 1dd888add82f71810f1ef4992092169f19260c09 Mon Sep 17 00:00:00 2001 From: Christina <156356273+cratiu222@users.noreply.github.com> Date: Wed, 21 Aug 2024 17:30:40 +0300 Subject: [PATCH 201/232] Docs fix typo (#19059) Hello I found and fixed several minor typos. Hope it helps. --- docs/content/concepts/tokenomics.mdx | 4 ++-- docs/content/concepts/tokenomics/gas-in-sui.mdx | 2 +- docs/content/concepts/tokenomics/staking-unstaking.mdx | 2 +- docs/content/concepts/tokenomics/storage-fund.mdx | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/content/concepts/tokenomics.mdx b/docs/content/concepts/tokenomics.mdx index e005f0605d55f..08c545def53ef 100644 --- a/docs/content/concepts/tokenomics.mdx +++ b/docs/content/concepts/tokenomics.mdx @@ -4,7 +4,7 @@ title: Sui Tokenomics The collective ideation that the term tokenomics encompasses includes a wide range of concepts that define the science and behavior of blockchain economies. In basic terms, tokenomics are the financial foundation of blockchains. Much the same way a building with a poor foundation is doomed to fail, a blockchain without a well-researched, extensively planned, and painstakingly implemented token economy eventually crumbles. -Sui tokenomics are based on sound financial concepts informed by extensive blockchain research. Designed for scale, the Sui tokenomic structure is designed to support the financial needs of web3 now and into the future. +Sui tokenomics are based on sound financial concepts informed by extensive blockchain research. Designed for scale, the Sui tokenomics structure is designed to support the financial needs of web3 now and into the future. ## The Sui economy @@ -34,4 +34,4 @@ The following flowchart presents the tokenomic flow of Sui at a high level. Refe ## Tokenomics whitepaper -Beyond the topics in this section of the documentation, you can read [The Sui Smart Contracts Platform: Economics and Incentives](/paper/tokenomics.pdf) whitepaper to learn more about tokenomic design on Sui. +Beyond the topics in this section of the documentation, you can read [The Sui Smart Contracts Platform: Economics and Incentives](/paper/tokenomics.pdf) whitepaper to learn more about tokenomics design on Sui. diff --git a/docs/content/concepts/tokenomics/gas-in-sui.mdx b/docs/content/concepts/tokenomics/gas-in-sui.mdx index 4a33dbb6d547b..9e7505248eca6 100644 --- a/docs/content/concepts/tokenomics/gas-in-sui.mdx +++ b/docs/content/concepts/tokenomics/gas-in-sui.mdx @@ -13,7 +13,7 @@ Finally, Sui [Storage mechanics](storage-fund.mdx#storage-fund-rewards) provide `net_gas_fees = computation_gas_fee + storage_gas_fee - storage_rebate` -The information on net gas fees displays in a Sui network explorer for each transaction block: +The information on net gas fees is displayed in a Sui network explorer for each transaction block: ![Gas Fees displayed on a Sui network explorer](images/gas-fees-explorer.png "The Gas Fees section displayed on a Sui network explorer") _The Gas Fees section for a transaction block displayed on a Sui network explorer_ diff --git a/docs/content/concepts/tokenomics/staking-unstaking.mdx b/docs/content/concepts/tokenomics/staking-unstaking.mdx index 4fbacb7789b47..593f91aa59376 100644 --- a/docs/content/concepts/tokenomics/staking-unstaking.mdx +++ b/docs/content/concepts/tokenomics/staking-unstaking.mdx @@ -19,7 +19,7 @@ Similar to staking, a user withdraws stake from a validator by sending a transac When you stake on Sui, you have to choose a specific validator you would like to stake with. The choice of validator can potentially impact the amount of staking rewards you receive. The factors determining this amount include, but are not limited to: -- Validator commission rate: a validator can choose to set a non-zero commission rate specifying the percentage of staking rewards they are taking from the stakers. For example, if a validator has the commission rate of 10%, then 10% of every staker's staking rewards is given to the validator. Understand that a validator can choose its commission at a future moment in time without prior notice. +- Validator commission rate: a validator can choose to set a non-zero commission rate specifying the percentage of staking rewards they are taking from the stakers. For example, if a validator has a commission rate of 10%, then 10% of every staker's staking rewards is given to the validator. Understand that a validator can choose its commission at a future moment in time without prior notice. - Validator performance: a validator with bad performance might be punished according to the [tallying rule](./gas-pricing.mdx#tallying-rule). Punished validators do not receive any staking rewards for the epoch during which they are punished, and you also do not receive that epoch's rewards when you withdraw your stake from that validator. Sui-compatible crypto wallets and explorers typically provide validator information such as commission and APY. See the respective documentation for these tools for information on how to retrieve this data. diff --git a/docs/content/concepts/tokenomics/storage-fund.mdx b/docs/content/concepts/tokenomics/storage-fund.mdx index 23cc36707bfc3..69500cbec1836 100644 --- a/docs/content/concepts/tokenomics/storage-fund.mdx +++ b/docs/content/concepts/tokenomics/storage-fund.mdx @@ -40,5 +40,5 @@ The key property of the rebate function is that it limits storage fund outflows The storage fund introduces various desirable incentives into the Sui economy: - Its mechanics incentivize users to delete data and obtain a rebate on their storage fees when the cost of storing such data exceeds the value obtained from maintaining that data on-chain. This introduces a useful market-based mechanism where users free storage when it becomes uneconomical for them to keep it. -- It creates deflationary pressure over the SUI token in that increased activity leads to larger storage requirements and to more SUI removed from circulation. +- It creates deflationary pressure over the SUI token in that increased activity leads to larger storage requirements and to more SUI being removed from circulation. - It is capital efficient in that it is economically equivalent to a rent model where users pay for storage through a pay-per-period model. From ed221a6ba9144fa5739f964674701fd773dddbab Mon Sep 17 00:00:00 2001 From: Tony Lee Date: Wed, 21 Aug 2024 11:12:35 -0400 Subject: [PATCH 202/232] SDK constant update (#19060) ## Description Deepbook SDK constant update ## Test plan How did you test the new or updated feature? Testnet ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .changeset/itchy-papayas-peel.md | 5 +++++ sdk/deepbook-v3/src/utils/constants.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/itchy-papayas-peel.md diff --git a/.changeset/itchy-papayas-peel.md b/.changeset/itchy-papayas-peel.md new file mode 100644 index 0000000000000..e9aef57c4e05a --- /dev/null +++ b/.changeset/itchy-papayas-peel.md @@ -0,0 +1,5 @@ +--- +'@mysten/deepbook-v3': patch +--- + +Update package address diff --git a/sdk/deepbook-v3/src/utils/constants.ts b/sdk/deepbook-v3/src/utils/constants.ts index e906071c1a314..97119bab2f4b7 100644 --- a/sdk/deepbook-v3/src/utils/constants.ts +++ b/sdk/deepbook-v3/src/utils/constants.ts @@ -12,7 +12,7 @@ export interface DeepbookPackageIds { } export const testnetPackageIds = { - DEEPBOOK_PACKAGE_ID: '0x3228ce0225baacb211e230f74891ceaabe3edeae230090ea3a5a176e535af7e9', + DEEPBOOK_PACKAGE_ID: '0xc671049379e6d512e3ecd0e79da50cb28840f09764d80a342b904863d87e5389', REGISTRY_ID: '0x9162317a81a9eb66ecd42705529b2a39c7805f98f42312275c2e7a599d518437', DEEP_TREASURY_ID: '0x69fffdae0075f8f71f4fa793549c11079266910e8905169845af1f5d00e09dcb', } satisfies DeepbookPackageIds; From 3db05b0e144fb242bb696ab943dc688a5f1c7966 Mon Sep 17 00:00:00 2001 From: Mark Logan <103447440+mystenmark@users.noreply.github.com> Date: Wed, 21 Aug 2024 16:53:58 +0100 Subject: [PATCH 203/232] Do not truncate logs when container restarts (#18955) --- docker/sui-network/docker-compose-antithesis.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/sui-network/docker-compose-antithesis.yaml b/docker/sui-network/docker-compose-antithesis.yaml index eada610c7c315..1e67f2884507c 100644 --- a/docker/sui-network/docker-compose-antithesis.yaml +++ b/docker/sui-network/docker-compose-antithesis.yaml @@ -22,7 +22,7 @@ services: - ./dbs/validator1:/opt/sui/db:rw - ./logs/validator1:/opt/sui/logs command: - "bash -c '/usr/local/bin/sui-node-inst --config-path /opt/sui/config/validator.yaml > /opt/sui/logs/full_logs.log 2>&1'" + "bash -c '/usr/local/bin/sui-node-inst --config-path /opt/sui/config/validator.yaml >> /opt/sui/logs/full_logs.log 2>&1'" restart: on-failure logging: driver: "json-file" @@ -50,7 +50,7 @@ services: - ./dbs/validator2:/opt/sui/db:rw - ./logs/validator2:/opt/sui/logs command: - "bash -c '/usr/local/bin/sui-node-inst --config-path /opt/sui/config/validator.yaml > /opt/sui/logs/full_logs.log 2>&1'" + "bash -c '/usr/local/bin/sui-node-inst --config-path /opt/sui/config/validator.yaml >> /opt/sui/logs/full_logs.log 2>&1'" restart: on-failure logging: driver: "json-file" @@ -77,7 +77,7 @@ services: - ./dbs/validator3:/opt/sui/db:rw - ./logs/validator3:/opt/sui/logs command: - "bash -c '/usr/local/bin/sui-node-inst --config-path /opt/sui/config/validator.yaml > /opt/sui/logs/full_logs.log 2>&1'" + "bash -c '/usr/local/bin/sui-node-inst --config-path /opt/sui/config/validator.yaml >> /opt/sui/logs/full_logs.log 2>&1'" restart: on-failure logging: driver: "json-file" @@ -104,7 +104,7 @@ services: - ./dbs/validator4:/opt/sui/db:rw - ./logs/validator4:/opt/sui/logs command: - "bash -c '/usr/local/bin/sui-node-inst --config-path /opt/sui/config/validator.yaml > /opt/sui/logs/full_logs.log 2>&1'" + "bash -c '/usr/local/bin/sui-node-inst --config-path /opt/sui/config/validator.yaml >> /opt/sui/logs/full_logs.log 2>&1'" restart: on-failure logging: driver: "json-file" From b9d696e1f3986197ea4cc9cefc1e22a0eb5053f1 Mon Sep 17 00:00:00 2001 From: Patrick Kuo Date: Wed, 21 Aug 2024 19:25:59 +0100 Subject: [PATCH 204/232] Indexer builder bug fix (#19058) ## Description this PR fixes a bug in the indexer task creation, where it is trying to create the same task upon restart resulting in duplicate key violation in the database. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .../src/sui_bridge_indexer.rs | 4 +-- .../src/indexer_builder.rs | 29 +++++++++------ .../tests/indexer_test_utils.rs | 18 +++++----- .../tests/indexer_tests.rs | 35 +++++++++++++++++++ 4 files changed, 64 insertions(+), 22 deletions(-) diff --git a/crates/sui-bridge-indexer/src/sui_bridge_indexer.rs b/crates/sui-bridge-indexer/src/sui_bridge_indexer.rs index 453be19c7854a..d0e5c46f8375c 100644 --- a/crates/sui-bridge-indexer/src/sui_bridge_indexer.rs +++ b/crates/sui-bridge-indexer/src/sui_bridge_indexer.rs @@ -133,14 +133,14 @@ impl IndexerProgressStore for PgBridgePersistent { &mut self, task_name: String, checkpoint: u64, - target_checkpoint: i64, + target_checkpoint: u64, ) -> Result<(), anyhow::Error> { let mut conn = self.pool.get()?; diesel::insert_into(schema::progress_store::table) .values(models::ProgressStore { task_name, checkpoint: checkpoint as i64, - target_checkpoint, + target_checkpoint: target_checkpoint as i64, // Timestamp is defaulted to current time in DB if None timestamp: None, }) diff --git a/crates/sui-indexer-builder/src/indexer_builder.rs b/crates/sui-indexer-builder/src/indexer_builder.rs index 43006754da381..dd7b317c76141 100644 --- a/crates/sui-indexer-builder/src/indexer_builder.rs +++ b/crates/sui-indexer-builder/src/indexer_builder.rs @@ -157,7 +157,11 @@ impl Indexer { match tasks.live_task() { None => { self.storage - .register_task(format!("{} - Live", self.name), from_checkpoint, i64::MAX) + .register_task( + format!("{} - Live", self.name), + from_checkpoint, + i64::MAX as u64, + ) .await?; } Some(mut live_task) => { @@ -174,15 +178,18 @@ impl Indexer { None => { // No task in database, create backfill tasks from genesis to `start_from_checkpoint` if self.start_from_checkpoint != self.genesis_checkpoint { - self.create_backfill_tasks(self.genesis_checkpoint, self.start_from_checkpoint) - .await? + self.create_backfill_tasks( + self.genesis_checkpoint, + self.start_from_checkpoint - 1, + ) + .await? } } Some(latest_task) => { - if latest_task.target_checkpoint < self.start_from_checkpoint { + if latest_task.target_checkpoint + 1 < self.start_from_checkpoint { self.create_backfill_tasks( latest_task.target_checkpoint + 1, - self.start_from_checkpoint, + self.start_from_checkpoint - 1, ) .await?; } @@ -200,20 +207,20 @@ impl Indexer { BackfillStrategy::Simple => { self.storage .register_task( - format!("{} - backfill - {}", self.name, to_cp), + format!("{} - backfill - {from_cp}:{to_cp}", self.name), from_cp, - self.start_from_checkpoint as i64 - 1, + to_cp, ) .await } BackfillStrategy::Partitioned { task_size } => { while from_cp < self.start_from_checkpoint { - let target_cp = min(from_cp + task_size, to_cp) - 1; + let target_cp = min(from_cp + task_size - 1, to_cp); self.storage .register_task( - format!("{} - backfill - {target_cp}", self.name), + format!("{} - backfill - {from_cp}:{target_cp}", self.name), from_cp, - target_cp as i64, + target_cp, ) .await?; from_cp = target_cp + 1; @@ -245,7 +252,7 @@ pub trait IndexerProgressStore: Send { &mut self, task_name: String, checkpoint: u64, - target_checkpoint: i64, + target_checkpoint: u64, ) -> Result<(), anyhow::Error>; async fn update_task(&mut self, task: Task) -> Result<(), Error>; diff --git a/crates/sui-indexer-builder/tests/indexer_test_utils.rs b/crates/sui-indexer-builder/tests/indexer_test_utils.rs index 4b9fed4724407..17e8cb1ad35dc 100644 --- a/crates/sui-indexer-builder/tests/indexer_test_utils.rs +++ b/crates/sui-indexer-builder/tests/indexer_test_utils.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; -use anyhow::Error; +use anyhow::{anyhow, Error}; use async_trait::async_trait; use tokio::sync::Mutex; use tokio::task::JoinHandle; @@ -105,20 +105,20 @@ impl IndexerProgressStore for InMemoryPersistent { &mut self, task_name: String, checkpoint: u64, - target_checkpoint: i64, + target_checkpoint: u64, ) -> Result<(), Error> { - self.progress_store.lock().await.insert( + let existing = self.progress_store.lock().await.insert( task_name.clone(), Task { - task_name, + task_name: task_name.clone(), checkpoint, - target_checkpoint: target_checkpoint as u64, - timestamp: SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_millis() as u64, + target_checkpoint, + timestamp: SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() as u64, }, ); + if existing.is_some() { + return Err(anyhow!("Task {task_name} already exists")); + } Ok(()) } diff --git a/crates/sui-indexer-builder/tests/indexer_tests.rs b/crates/sui-indexer-builder/tests/indexer_tests.rs index 03db730de3392..c4febf9069a76 100644 --- a/crates/sui-indexer-builder/tests/indexer_tests.rs +++ b/crates/sui-indexer-builder/tests/indexer_tests.rs @@ -154,3 +154,38 @@ async fn indexer_partitioned_task_with_data_already_in_db_test2() { recorded_data.sort(); assert_eq!(data, recorded_data); } + +#[tokio::test] +async fn resume_test() { + telemetry_subscribers::init_for_testing(); + let registry = Registry::new(); + mysten_metrics::init_metrics(®istry); + + let data = (0..=50u64).collect::>(); + let datasource = TestDatasource { data: data.clone() }; + let persistent = InMemoryPersistent::new(); + persistent.progress_store.lock().await.insert( + "test_indexer - backfill - 30".to_string(), + Task { + task_name: "test_indexer - backfill - 30".to_string(), + checkpoint: 10, + target_checkpoint: 30, + timestamp: 0, + }, + ); + let indexer = IndexerBuilder::new("test_indexer", datasource, NoopDataMapper) + .with_backfill_strategy(BackfillStrategy::Simple) + .build(30, 0, persistent.clone()); + indexer.start().await.unwrap(); + + // it should have 2 task created for the indexer, one existing task and one live task + let tasks = persistent.tasks("test_indexer").await.unwrap(); + assert_eq!(2, tasks.len()); + // the first one will be the live task + assert_eq!(50, tasks.first().unwrap().checkpoint); + assert_eq!(i64::MAX as u64, tasks.first().unwrap().target_checkpoint); + // the data recorded in storage should be the same as the datasource + let mut recorded_data = persistent.data.lock().await.clone(); + recorded_data.sort(); + assert_eq!((10..=50u64).collect::>(), recorded_data); +} From b1976e689cb335c2014693c1aa0b19180dd8da0d Mon Sep 17 00:00:00 2001 From: Emma Zhong Date: Wed, 21 Aug 2024 13:34:49 -0700 Subject: [PATCH 205/232] [indexer] index protocol configs and feature flags (#18450) ## Description This PR adds two tables `protocol_configs` and `feature_flags` that get populated at indexer startup time and every epoch change time if protocol version has changed. ## Test plan Tested locally against devnet. --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [x] Indexer: adds two new indexer tables that stores protocol configs and features flags of different versions. - [ ] JSON-RPC: - [x] GraphQL: uses the stored data to query for protocol configs instead of native configs stored in the binary. - [ ] CLI: - [ ] Rust SDK: --- crates/sui-benchmark/tests/simtest.rs | 2 +- .../tests/epoch/protocol_configs.exp | 40 ++++++ .../tests/epoch/protocol_configs.move | 32 +++++ .../src/types/protocol_config.rs | 114 ++++++++++------- .../pg/2023-08-19-044052_epochs/up.sql | 19 +++ crates/sui-indexer/src/handlers/committer.rs | 13 ++ crates/sui-indexer/src/indexer.rs | 8 ++ crates/sui-indexer/src/models/epoch.rs | 18 ++- crates/sui-indexer/src/schema/mod.rs | 6 + crates/sui-indexer/src/schema/mysql.rs | 16 +++ crates/sui-indexer/src/schema/pg.rs | 37 ++++-- crates/sui-indexer/src/store/indexer_store.rs | 7 ++ .../sui-indexer/src/store/pg_indexer_store.rs | 116 +++++++++++++++++- 13 files changed, 366 insertions(+), 62 deletions(-) create mode 100644 crates/sui-graphql-e2e-tests/tests/epoch/protocol_configs.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/epoch/protocol_configs.move diff --git a/crates/sui-benchmark/tests/simtest.rs b/crates/sui-benchmark/tests/simtest.rs index f3a29e01ba079..b7d2e189da3a2 100644 --- a/crates/sui-benchmark/tests/simtest.rs +++ b/crates/sui-benchmark/tests/simtest.rs @@ -550,7 +550,7 @@ mod test { .build() .await, ); - test_simulated_load(test_cluster, 10).await; + test_simulated_load(test_cluster, 30).await; let checkpoint_files = std::fs::read_dir(path) .map(|entries| { diff --git a/crates/sui-graphql-e2e-tests/tests/epoch/protocol_configs.exp b/crates/sui-graphql-e2e-tests/tests/epoch/protocol_configs.exp new file mode 100644 index 0000000000000..e237e6000cd9a --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/epoch/protocol_configs.exp @@ -0,0 +1,40 @@ +processed 4 tasks + +init: +C: object(0,0) + +task 1, line 6: +//# create-checkpoint +Checkpoint created: 1 + +task 2, lines 8-19: +//# run-graphql +Response: { + "data": { + "protocolConfig": { + "protocolVersion": 51, + "config": { + "value": "128" + }, + "featureFlag": { + "value": true + } + } + } +} + +task 3, lines 21-32: +//# run-graphql +Response: { + "data": { + "protocolConfig": { + "protocolVersion": 8, + "config": { + "value": null + }, + "featureFlag": { + "value": false + } + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/epoch/protocol_configs.move b/crates/sui-graphql-e2e-tests/tests/epoch/protocol_configs.move new file mode 100644 index 0000000000000..901326e4969a3 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/epoch/protocol_configs.move @@ -0,0 +1,32 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//# init --protocol-version 51 --simulator --accounts C + +//# create-checkpoint + +//# run-graphql +{ + protocolConfig { + protocolVersion + config(key: "max_move_identifier_len") { + value + } + featureFlag(key: "enable_coin_deny_list") { + value + } + } +} + +//# run-graphql +{ + protocolConfig(protocolVersion: 8) { + protocolVersion + config(key: "max_move_identifier_len") { + value + } + featureFlag(key: "enable_coin_deny_list") { + value + } + } +} diff --git a/crates/sui-graphql-rpc/src/types/protocol_config.rs b/crates/sui-graphql-rpc/src/types/protocol_config.rs index ce60c22892d39..f4f8fd9caf771 100644 --- a/crates/sui-graphql-rpc/src/types/protocol_config.rs +++ b/crates/sui-graphql-rpc/src/types/protocol_config.rs @@ -1,15 +1,15 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use std::collections::BTreeMap; + use async_graphql::*; use diesel::{ExpressionMethods, QueryDsl}; -use sui_indexer::schema::{chain_identifier, epochs}; -use sui_protocol_config::{ProtocolConfig as NativeProtocolConfig, ProtocolVersion}; +use sui_indexer::schema::{epochs, feature_flags, protocol_configs}; use crate::{ data::{Db, DbConnection, QueryExecutor}, error::Error, - types::chain_identifier::ChainIdentifier, }; use super::uint53::UInt53; @@ -30,7 +30,9 @@ pub(crate) struct ProtocolConfigFeatureFlag { #[derive(Clone, Debug)] pub(crate) struct ProtocolConfigs { - native: NativeProtocolConfig, + version: u64, + configs: BTreeMap>, + feature_flags: BTreeMap, } /// Constants that control how the chain operates. @@ -41,15 +43,15 @@ impl ProtocolConfigs { /// The protocol is not required to change on every epoch boundary, so the protocol version /// tracks which change to the protocol these configs are from. async fn protocol_version(&self) -> UInt53 { - self.native.version.as_u64().into() + self.version.into() } /// List all available feature flags and their values. Feature flags are a form of boolean /// configuration that are usually used to gate features while they are in development. Once a /// flag has been enabled, it is rare for it to be disabled. async fn feature_flags(&self) -> Vec { - self.native - .feature_map() + self.feature_flags + .clone() .into_iter() .map(|(key, value)| ProtocolConfigFeatureFlag { key, value }) .collect() @@ -58,31 +60,24 @@ impl ProtocolConfigs { /// List all available configurations and their values. These configurations can take any value /// (but they will all be represented in string form), and do not include feature flags. async fn configs(&self) -> Vec { - self.native - .attr_map() + self.configs + .clone() .into_iter() - .map(|(key, value)| ProtocolConfigAttr { - key, - value: value.map(|v| v.to_string()), - }) + .map(|(key, value)| ProtocolConfigAttr { key, value }) .collect() } /// Query for the value of the configuration with name `key`. async fn config(&self, key: String) -> Option { - self.native - .attr_map() - .get(&key) - .map(|value| ProtocolConfigAttr { - key, - value: value.as_ref().map(|v| v.to_string()), - }) + self.configs.get(&key).map(|value| ProtocolConfigAttr { + key, + value: value.as_ref().map(|v| v.to_string()), + }) } /// Query for the state of the feature flag with name `key`. async fn feature_flag(&self, key: String) -> Option { - self.native - .feature_map() + self.feature_flags .get(&key) .map(|value| ProtocolConfigFeatureFlag { key, value: *value }) } @@ -90,36 +85,61 @@ impl ProtocolConfigs { impl ProtocolConfigs { pub(crate) async fn query(db: &Db, protocol_version: Option) -> Result { - use chain_identifier::dsl as c; use epochs::dsl as e; + use feature_flags::dsl as f; + use protocol_configs::dsl as p; + + let version = if let Some(version) = protocol_version { + version + } else { + let latest_version: i64 = db + .execute(move |conn| { + conn.first(move || { + e::epochs + .select(e::protocol_version) + .order_by(e::epoch.desc()) + }) + }) + .await + .map_err(|e| { + Error::Internal(format!( + "Failed to fetch latest protocol version in db: {e}" + )) + })?; + latest_version as u64 + }; + + // TODO: This could be optimized by fetching all configs and flags in a single query. + let configs: BTreeMap> = db + .execute(move |conn| { + conn.results(move || { + p::protocol_configs + .select((p::config_name, p::config_value)) + .filter(p::protocol_version.eq(version as i64)) + }) + }) + .await + .map_err(|e| Error::Internal(format!("Failed to fetch protocol configs in db: {e}")))? + .into_iter() + .collect(); - let (latest_version, digest_bytes): (i64, Option>) = db + let feature_flags: BTreeMap = db .execute(move |conn| { - conn.first(move || { - e::epochs - .select(( - e::protocol_version, - c::chain_identifier - .select(c::checkpoint_digest) - .single_value(), - )) - .order_by(e::epoch.desc()) + conn.results(move || { + f::feature_flags + .select((f::flag_name, f::flag_value)) + .filter(f::protocol_version.eq(version as i64)) }) }) .await - .map_err(|e| Error::Internal(format!("Failed to fetch system details: {e}")))?; - - let native = NativeProtocolConfig::get_for_version_if_supported( - protocol_version.unwrap_or(latest_version as u64).into(), - ChainIdentifier::from_bytes(digest_bytes.unwrap_or_default())?.chain(), - ) - .ok_or_else(|| { - Error::ProtocolVersionUnsupported( - ProtocolVersion::MIN.as_u64(), - ProtocolVersion::MAX.as_u64(), - ) - })?; - - Ok(ProtocolConfigs { native }) + .map_err(|e| Error::Internal(format!("Failed to fetch feature flags in db: {e}")))? + .into_iter() + .collect(); + + Ok(ProtocolConfigs { + version, + configs, + feature_flags, + }) } } diff --git a/crates/sui-indexer/migrations/pg/2023-08-19-044052_epochs/up.sql b/crates/sui-indexer/migrations/pg/2023-08-19-044052_epochs/up.sql index 4a0a17289ccec..5b540121cb849 100644 --- a/crates/sui-indexer/migrations/pg/2023-08-19-044052_epochs/up.sql +++ b/crates/sui-indexer/migrations/pg/2023-08-19-044052_epochs/up.sql @@ -26,3 +26,22 @@ CREATE TABLE epochs -- of the epoch epoch_commitments bytea ); + +-- Table storing the protocol configs for each protocol version. +-- Examples include gas schedule, transaction limits, etc. +CREATE TABLE protocol_configs +( + protocol_version BIGINT NOT NULL, + config_name TEXT NOT NULL, + config_value TEXT, + PRIMARY KEY(protocol_version, config_name) +); + +-- Table storing the feature flags for each protocol version. +CREATE TABLE feature_flags +( + protocol_version BIGINT NOT NULL, + flag_name TEXT NOT NULL, + flag_value BOOLEAN NOT NULL, + PRIMARY KEY(protocol_version, flag_name) +); diff --git a/crates/sui-indexer/src/handlers/committer.rs b/crates/sui-indexer/src/handlers/committer.rs index 58d6fe94e1f7c..f4e5504893f5c 100644 --- a/crates/sui-indexer/src/handlers/committer.rs +++ b/crates/sui-indexer/src/handlers/committer.rs @@ -162,6 +162,8 @@ async fn commit_checkpoints( .expect("Persisting data into DB should not fail."); } + let is_epoch_end = epoch.is_some(); + // handle partitioning on epoch boundary if let Some(epoch_data) = epoch { state @@ -184,6 +186,17 @@ async fn commit_checkpoints( ); }) .expect("Persisting data into 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 + .get_chain_identifier() + .await + .expect("Failed to get chain identifier") + .expect("Chain identifier should have been indexed at this point"); + let _ = state.persist_protocol_configs_and_feature_flags(chain_id); + } + let elapsed = guard.stop_and_record(); commit_notifier diff --git a/crates/sui-indexer/src/indexer.rs b/crates/sui-indexer/src/indexer.rs index 3b71cf795d37f..f38a7a1de140d 100644 --- a/crates/sui-indexer/src/indexer.rs +++ b/crates/sui-indexer/src/indexer.rs @@ -122,6 +122,14 @@ impl Indexer { spawn_monitored_task!(pruner.start(CancellationToken::new())); } + // If we already have chain identifier indexed (i.e. the first checkpoint has been indexed), + // then we persist protocol configs for protocol versions not yet in the db. + // Otherwise, we would do the persisting in `commit_checkpoint` while the first cp is + // being indexed. + if let Some(chain_id) = store.get_chain_identifier().await? { + store.persist_protocol_configs_and_feature_flags(chain_id)?; + } + let cancel_clone = cancel.clone(); let (exit_sender, exit_receiver) = oneshot::channel(); // Spawn a task that links the cancellation token to the exit sender diff --git a/crates/sui-indexer/src/models/epoch.rs b/crates/sui-indexer/src/models/epoch.rs index a392fafbbd4d5..0991203d5cd02 100644 --- a/crates/sui-indexer/src/models/epoch.rs +++ b/crates/sui-indexer/src/models/epoch.rs @@ -3,9 +3,9 @@ use diesel::{Insertable, Queryable, Selectable}; -use crate::errors::IndexerError; use crate::schema::epochs; use crate::types::IndexedEpochInfo; +use crate::{errors::IndexerError, schema::feature_flags, schema::protocol_configs}; use sui_json_rpc_types::{EndOfEpochInfo, EpochInfo}; use sui_types::sui_system_state::sui_system_state_summary::SuiSystemStateSummary; @@ -33,6 +33,22 @@ pub struct StoredEpochInfo { pub epoch_commitments: Option>, } +#[derive(Queryable, Insertable, Debug, Clone, Default)] +#[diesel(table_name = protocol_configs)] +pub struct StoredProtocolConfig { + pub protocol_version: i64, + pub config_name: String, + pub config_value: Option, +} + +#[derive(Queryable, Insertable, Debug, Clone, Default)] +#[diesel(table_name = feature_flags)] +pub struct StoredFeatureFlag { + pub protocol_version: i64, + pub flag_name: String, + pub flag_value: bool, +} + #[derive(Queryable, Selectable, Clone)] #[diesel(table_name = epochs)] pub struct QueryableEpochInfo { diff --git a/crates/sui-indexer/src/schema/mod.rs b/crates/sui-indexer/src/schema/mod.rs index 8f98297ac4d80..dfba1ad7b7b43 100644 --- a/crates/sui-indexer/src/schema/mod.rs +++ b/crates/sui-indexer/src/schema/mod.rs @@ -24,11 +24,13 @@ mod inner { pub use crate::schema::pg::event_struct_name; pub use crate::schema::pg::event_struct_package; pub use crate::schema::pg::events; + pub use crate::schema::pg::feature_flags; pub use crate::schema::pg::objects; pub use crate::schema::pg::objects_history; pub use crate::schema::pg::objects_snapshot; pub use crate::schema::pg::objects_version; pub use crate::schema::pg::packages; + pub use crate::schema::pg::protocol_configs; pub use crate::schema::pg::pruner_cp_watermark; pub use crate::schema::pg::transactions; pub use crate::schema::pg::tx_calls_fun; @@ -57,11 +59,13 @@ mod inner { pub use crate::schema::mysql::event_struct_name; pub use crate::schema::mysql::event_struct_package; pub use crate::schema::mysql::events; + pub use crate::schema::mysql::feature_flags; pub use crate::schema::mysql::objects; pub use crate::schema::mysql::objects_history; pub use crate::schema::mysql::objects_snapshot; pub use crate::schema::mysql::objects_version; pub use crate::schema::mysql::packages; + pub use crate::schema::mysql::protocol_configs; pub use crate::schema::mysql::pruner_cp_watermark; pub use crate::schema::mysql::transactions; pub use crate::schema::mysql::tx_calls_fun; @@ -87,11 +91,13 @@ pub use inner::event_struct_module; pub use inner::event_struct_name; pub use inner::event_struct_package; pub use inner::events; +pub use inner::feature_flags; pub use inner::objects; pub use inner::objects_history; pub use inner::objects_snapshot; pub use inner::objects_version; pub use inner::packages; +pub use inner::protocol_configs; pub use inner::pruner_cp_watermark; pub use inner::transactions; pub use inner::tx_calls_fun; diff --git a/crates/sui-indexer/src/schema/mysql.rs b/crates/sui-indexer/src/schema/mysql.rs index e7805ae562be0..4b4799566a0d4 100644 --- a/crates/sui-indexer/src/schema/mysql.rs +++ b/crates/sui-indexer/src/schema/mysql.rs @@ -150,6 +150,14 @@ diesel::table! { } } +diesel::table! { + feature_flags (protocol_version, flag_name) { + protocol_version -> Bigint, + flag_name -> Text, + flag_value -> Bool, + } +} + diesel::table! { objects (object_id) { object_id -> Blob, @@ -236,6 +244,14 @@ diesel::table! { } } +diesel::table! { + protocol_configs (protocol_version, config_name) { + protocol_version -> Bigint, + config_name -> Text, + config_value -> Nullable, + } +} + diesel::table! { pruner_cp_watermark (checkpoint_sequence_number) { checkpoint_sequence_number -> Bigint, diff --git a/crates/sui-indexer/src/schema/pg.rs b/crates/sui-indexer/src/schema/pg.rs index 1ef8e0aed8e81..2515c98d34c06 100644 --- a/crates/sui-indexer/src/schema/pg.rs +++ b/crates/sui-indexer/src/schema/pg.rs @@ -31,14 +31,6 @@ diesel::table! { } } -diesel::table! { - pruner_cp_watermark (checkpoint_sequence_number) { - checkpoint_sequence_number -> Int8, - min_tx_sequence_number -> Int8, - max_tx_sequence_number -> Int8, - } -} - diesel::table! { display (object_type) { object_type -> Text, @@ -176,6 +168,14 @@ diesel::table! { } } +diesel::table! { + feature_flags (protocol_version, flag_name) { + protocol_version -> Int8, + flag_name -> Text, + flag_value -> Bool, + } +} + diesel::table! { objects (object_id) { object_id -> Bytea, @@ -275,6 +275,14 @@ diesel::table! { } } +diesel::table! { + protocol_configs (protocol_version, config_name) { + protocol_version -> Int8, + config_name -> Text, + config_value -> Nullable, + } +} + diesel::table! { packages (package_id, original_id, package_version) { package_id -> Bytea, @@ -285,6 +293,14 @@ diesel::table! { } } +diesel::table! { + pruner_cp_watermark (checkpoint_sequence_number) { + checkpoint_sequence_number -> Int8, + min_tx_sequence_number -> Int8, + max_tx_sequence_number -> Int8, + } +} + diesel::table! { transactions (tx_sequence_number) { tx_sequence_number -> Int8, @@ -395,7 +411,6 @@ macro_rules! for_all_tables { $action!( chain_identifier, checkpoints, - pruner_cp_watermark, display, epochs, event_emit_module, @@ -406,11 +421,13 @@ macro_rules! for_all_tables { event_struct_name, event_struct_package, events, - objects, + feature_flags, objects_history, objects_snapshot, objects_version, packages, + protocol_configs, + pruner_cp_watermark, transactions, tx_calls_fun, tx_calls_mod, diff --git a/crates/sui-indexer/src/store/indexer_store.rs b/crates/sui-indexer/src/store/indexer_store.rs index 97516929ffe24..2fd2c1531b76a 100644 --- a/crates/sui-indexer/src/store/indexer_store.rs +++ b/crates/sui-indexer/src/store/indexer_store.rs @@ -32,6 +32,13 @@ pub trait IndexerStore: Any + Clone + Sync + Send + 'static { &self, ) -> Result, IndexerError>; + async fn get_chain_identifier(&self) -> Result>, IndexerError>; + + fn persist_protocol_configs_and_feature_flags( + &self, + chain_id: Vec, + ) -> Result<(), IndexerError>; + async fn persist_objects( &self, object_changes: Vec, diff --git a/crates/sui-indexer/src/store/pg_indexer_store.rs b/crates/sui-indexer/src/store/pg_indexer_store.rs index 59e6ba3dc07ff..7a2ac3cdb8854 100644 --- a/crates/sui-indexer/src/store/pg_indexer_store.rs +++ b/crates/sui-indexer/src/store/pg_indexer_store.rs @@ -20,6 +20,7 @@ use itertools::Itertools; use tap::TapFallible; use tracing::info; +use sui_protocol_config::ProtocolConfig; use sui_types::base_types::ObjectID; use crate::db::ConnectionPool; @@ -32,6 +33,7 @@ use crate::models::checkpoints::StoredCheckpoint; use crate::models::checkpoints::StoredCpTx; use crate::models::display::StoredDisplay; use crate::models::epoch::StoredEpochInfo; +use crate::models::epoch::{StoredFeatureFlag, StoredProtocolConfig}; use crate::models::events::StoredEvent; use crate::models::obj_indices::StoredObjectVersion; use crate::models::objects::{ @@ -43,9 +45,10 @@ use crate::models::transactions::StoredTransaction; use crate::schema::{ chain_identifier, checkpoints, display, epochs, event_emit_module, event_emit_package, event_senders, event_struct_instantiation, event_struct_module, event_struct_name, - event_struct_package, events, objects, objects_history, objects_snapshot, objects_version, - packages, pruner_cp_watermark, transactions, tx_calls_fun, tx_calls_mod, tx_calls_pkg, - tx_changed_objects, tx_digests, tx_input_objects, tx_kinds, tx_recipients, tx_senders, + event_struct_package, events, feature_flags, objects, objects_history, objects_snapshot, + objects_version, packages, protocol_configs, pruner_cp_watermark, transactions, tx_calls_fun, + tx_calls_mod, tx_calls_pkg, tx_changed_objects, tx_digests, tx_input_objects, tx_kinds, + tx_recipients, tx_senders, }; use crate::types::EventIndex; use crate::types::{IndexedCheckpoint, IndexedEvent, IndexedPackage, IndexedTransaction, TxIndex}; @@ -60,6 +63,7 @@ use super::ObjectChangeToCommit; #[cfg(feature = "postgres-feature")] use diesel::upsert::excluded; +use sui_types::digests::{ChainIdentifier, CheckpointDigest}; #[macro_export] macro_rules! chunk { @@ -195,6 +199,38 @@ impl PgIndexerStore { .context("Failed reading latest epoch id from PostgresDB") } + /// Get the range of the protocol versions that need to be indexed. + pub fn get_protocol_version_index_range(&self) -> Result<(i64, i64), IndexerError> { + // We start indexing from the next protocol version after the latest one stored in the db. + let start = read_only_blocking!(&self.blocking_cp, |conn| { + protocol_configs::dsl::protocol_configs + .select(max(protocol_configs::protocol_version)) + .first::>(conn) + }) + .context("Failed reading latest protocol version from PostgresDB")? + .map_or(1, |v| v + 1); + + // We end indexing at the protocol version of the latest epoch stored in the db. + let end = read_only_blocking!(&self.blocking_cp, |conn| { + epochs::dsl::epochs + .select(max(epochs::protocol_version)) + .first::>(conn) + }) + .context("Failed reading latest epoch protocol version from PostgresDB")? + .unwrap_or(1); + Ok((start, end)) + } + + pub fn get_chain_identifier(&self) -> Result>, IndexerError> { + read_only_blocking!(&self.blocking_cp, |conn| { + chain_identifier::dsl::chain_identifier + .select(chain_identifier::checkpoint_digest) + .first::>(conn) + .optional() + }) + .context("Failed reading chain id from PostgresDB") + } + fn get_latest_checkpoint_sequence_number(&self) -> Result, IndexerError> { read_only_blocking!(&self.blocking_cp, |conn| { checkpoints::dsl::checkpoints @@ -627,6 +663,8 @@ impl PgIndexerStore { // If the first checkpoint has sequence number 0, we need to persist the digest as // chain identifier. if first_checkpoint.sequence_number == 0 { + let checkpoint_digest = first_checkpoint.checkpoint_digest.into_inner().to_vec(); + self.persist_protocol_configs_and_feature_flags(checkpoint_digest.clone())?; transactional_blocking_with_retry!( &self.blocking_cp, |conn| { @@ -1629,6 +1667,11 @@ impl IndexerStore for PgIndexerStore { .await } + async fn get_chain_identifier(&self) -> Result>, IndexerError> { + self.execute_in_blocking_worker(|this| this.get_chain_identifier()) + .await + } + async fn get_latest_object_snapshot_checkpoint_sequence_number( &self, ) -> Result, IndexerError> { @@ -2143,6 +2186,73 @@ impl IndexerStore for PgIndexerStore { fn as_any(&self) -> &dyn StdAny { self } + + /// Persist protocol configs and feature flags until the protocol version for the latest epoch + /// we have stored in the db, inclusive. + fn persist_protocol_configs_and_feature_flags( + &self, + chain_id: Vec, + ) -> Result<(), IndexerError> { + let chain_id = ChainIdentifier::from( + CheckpointDigest::try_from(chain_id).expect("Unable to convert chain id"), + ); + + let mut all_configs = vec![]; + let mut all_flags = vec![]; + + let (start_version, end_version) = self.get_protocol_version_index_range()?; + info!( + "Persisting protocol configs with start_version: {}, end_version: {}", + start_version, end_version + ); + + // Gather all protocol configs and feature flags for all versions between start and end. + for version in start_version..=end_version { + let protocol_configs = ProtocolConfig::get_for_version_if_supported( + (version as u64).into(), + chain_id.chain(), + ) + .ok_or(IndexerError::GenericError(format!( + "Unable to fetch protocol version {} and chain {:?}", + version, + chain_id.chain() + )))?; + let configs_vec = protocol_configs + .attr_map() + .into_iter() + .map(|(k, v)| StoredProtocolConfig { + protocol_version: version, + config_name: k, + config_value: v.map(|v| v.to_string()), + }) + .collect::>(); + all_configs.extend(configs_vec); + + let feature_flags = protocol_configs + .feature_map() + .into_iter() + .map(|(k, v)| StoredFeatureFlag { + protocol_version: version, + flag_name: k, + flag_value: v, + }) + .collect::>(); + all_flags.extend(feature_flags); + } + + // Now insert all of them into the db. + // TODO: right now the size of these updates is manageable but later we may consider batching. + transactional_blocking_with_retry!( + &self.blocking_cp, + |conn| { + insert_or_ignore_into!(protocol_configs::table, all_configs.clone(), conn); + insert_or_ignore_into!(feature_flags::table, all_flags.clone(), conn); + Ok::<(), IndexerError>(()) + }, + PG_DB_COMMIT_SLEEP_DURATION + )?; + Ok(()) + } } /// Construct deleted objects and mutated objects to commit. From b2a362143c5fd5e470a3dc4a3719a31d46ab6e14 Mon Sep 17 00:00:00 2001 From: "sui-merge-bot[bot]" <114704316+sui-merge-bot[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 17:02:13 -0400 Subject: [PATCH 206/232] Version Packages (#19061) This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @mysten/deepbook-v3@0.3.3 ### Patch Changes - ed221a6: Update package address Co-authored-by: github-actions[bot] --- .changeset/itchy-papayas-peel.md | 5 ----- sdk/deepbook-v3/CHANGELOG.md | 6 ++++++ sdk/deepbook-v3/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/itchy-papayas-peel.md diff --git a/.changeset/itchy-papayas-peel.md b/.changeset/itchy-papayas-peel.md deleted file mode 100644 index e9aef57c4e05a..0000000000000 --- a/.changeset/itchy-papayas-peel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@mysten/deepbook-v3': patch ---- - -Update package address diff --git a/sdk/deepbook-v3/CHANGELOG.md b/sdk/deepbook-v3/CHANGELOG.md index 7430929925995..2501d9bb5de9e 100644 --- a/sdk/deepbook-v3/CHANGELOG.md +++ b/sdk/deepbook-v3/CHANGELOG.md @@ -1,5 +1,11 @@ # @mysten/deepbook-v3 +## 0.3.3 + +### Patch Changes + +- ed221a6: Update package address + ## 0.3.2 ### Patch Changes diff --git a/sdk/deepbook-v3/package.json b/sdk/deepbook-v3/package.json index fb17602959b0a..418706faa1890 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.3.2", + "version": "0.3.3", "license": "Apache-2.0", "type": "commonjs", "main": "./dist/cjs/index.js", From 9a24f66b339196a59e2816889796bf550c848caf Mon Sep 17 00:00:00 2001 From: Todd Nowacki Date: Wed, 21 Aug 2024 15:17:00 -0700 Subject: [PATCH 207/232] [move] Small lint cleanups (#18878) ## Description - Small changes/rewording to lints - Improved capability freezing lint - Fixed VISIT_TYPES ## Test plan - Updated tests --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .../sui_mode/linters/freezing_capability.rs | 60 +++++++++---------- .../src/sui_mode/linters/missing_key.rs | 13 ++-- .../move-compiler/src/typing/visitor.rs | 12 ++-- .../linter/edge_case_lint_missing_key.move | 9 ++- .../false_negative_lint_missing_key.exp | 2 +- .../false_negative_lint_missing_key.move | 2 +- .../freezing_capability_false_negatives.move | 10 +++- .../freezing_capability_false_positives.exp | 14 +++-- .../freezing_capability_false_positives.move | 10 ++-- .../freezing_capability_suppression.move | 2 +- .../freezing_capability_true_negatives.move | 34 ++++++++++- .../freezing_capability_true_positives.exp | 15 +++-- .../freezing_capability_true_positives.move | 2 +- .../linter/no_trigger_lint_missing_key.exp | 60 ------------------- .../linter/no_trigger_lint_missing_key.move | 31 +--------- .../linter/suppress_lint_missing_key.move | 2 +- .../linter/trigger_lint_missing_key.exp | 12 +--- .../linter/trigger_lint_missing_key.move | 23 +------ 18 files changed, 120 insertions(+), 193 deletions(-) delete mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/linter/no_trigger_lint_missing_key.exp diff --git a/external-crates/move/crates/move-compiler/src/sui_mode/linters/freezing_capability.rs b/external-crates/move/crates/move-compiler/src/sui_mode/linters/freezing_capability.rs index aa371ee00e3e6..d5f657d6cb2ee 100644 --- a/external-crates/move/crates/move-compiler/src/sui_mode/linters/freezing_capability.rs +++ b/external-crates/move/crates/move-compiler/src/sui_mode/linters/freezing_capability.rs @@ -15,11 +15,12 @@ use crate::{ shared::{CompilationEnv, Identifier}, sui_mode::linters::{FREEZE_FUN, PUBLIC_FREEZE_FUN, SUI_PKG_NAME, TRANSFER_MOD_NAME}, typing::{ - ast as T, + ast as T, core, visitor::{TypingVisitorConstructor, TypingVisitorContext}, }, }; use move_ir_types::location::*; +use once_cell::sync::Lazy; use regex::Regex; const FREEZE_CAPABILITY_DIAG: DiagnosticInfo = custom( @@ -27,7 +28,7 @@ const FREEZE_CAPABILITY_DIAG: DiagnosticInfo = custom( Severity::Warning, LinterDiagnosticCategory::Sui as u8, LinterDiagnosticCode::FreezingCapability as u8, - "Freezing a capability-like type can lead to design issues.", + "freezing potential capability", ); const FREEZE_FUNCTIONS: &[(&str, &str, &str)] = &[ @@ -39,16 +40,14 @@ pub struct WarnFreezeCapability; pub struct Context<'a> { env: &'a mut CompilationEnv, - capability_regex: Regex, } +static REGEX: Lazy = Lazy::new(|| Regex::new(r".*Cap(?:[A-Z0-9_]+|ability|$).*").unwrap()); + impl TypingVisitorConstructor for WarnFreezeCapability { type Context<'a> = Context<'a>; fn context<'a>(env: &'a mut CompilationEnv, _program: &T::Program) -> Self::Context<'a> { - Context { - env, - capability_regex: Regex::new(r"Cap(ability)?(\w*v?\d*)?$").unwrap(), - } + Context { env } } } @@ -74,8 +73,8 @@ impl<'a> TypingVisitorContext for Context<'a> { fn visit_exp_custom(&mut self, exp: &mut T::Exp) -> bool { if let T::UnannotatedExp_::ModuleCall(fun) = &exp.exp.value { - if self.is_freeze_function(fun) { - self.check_type_arguments(fun, exp.exp.loc); + if is_freeze_function(fun) { + check_type_arguments(self, fun, exp.exp.loc); } } false @@ -90,27 +89,28 @@ impl<'a> TypingVisitorContext for Context<'a> { } } -impl<'a> Context<'a> { - fn is_freeze_function(&self, fun: &T::ModuleCall) -> bool { - FREEZE_FUNCTIONS.iter().any(|(addr, module, fname)| { - fun.module.value.is(*addr, *module) && &fun.name.value().as_str() == fname - }) - } - - fn check_type_arguments(&mut self, fun: &T::ModuleCall, loc: Loc) { - for sp!(_, type_arg) in &fun.type_arguments { - if let Some(sp!(_, TypeName_::ModuleType(_, struct_name))) = type_arg.type_name() { - if self.capability_regex.is_match(struct_name.value().as_str()) { - self.report_freeze_capability(loc); - break; - } - } - } - } +fn is_freeze_function(fun: &T::ModuleCall) -> bool { + FREEZE_FUNCTIONS.iter().any(|(addr, module, fname)| { + fun.module.value.is(*addr, *module) && &fun.name.value().as_str() == fname + }) +} - fn report_freeze_capability(&mut self, loc: Loc) { - let msg = "Freezing a capability-like type can lead to design issues."; - let diag = diag!(FREEZE_CAPABILITY_DIAG, (loc, msg)); - self.env.add_diag(diag); +fn check_type_arguments(context: &mut Context, fun: &T::ModuleCall, loc: Loc) { + for sp!(_, type_arg) in &fun.type_arguments { + let Some(sp!(_, TypeName_::ModuleType(_, struct_name))) = type_arg.type_name() else { + continue; + }; + if REGEX.is_match(struct_name.value().as_str()) { + let msg = format!( + "The type {} is potentially a capability based on its name", + core::error_format_(type_arg, &core::Subst::empty()), + ); + let mut diag = diag!(FREEZE_CAPABILITY_DIAG, (loc, msg)); + diag.add_note( + "Freezing a capability might lock out critical operations \ + or otherwise open access to operations that otherwise should be restricted", + ); + context.env.add_diag(diag); + }; } } diff --git a/external-crates/move/crates/move-compiler/src/sui_mode/linters/missing_key.rs b/external-crates/move/crates/move-compiler/src/sui_mode/linters/missing_key.rs index cd20476c33a53..d058f11d255d8 100644 --- a/external-crates/move/crates/move-compiler/src/sui_mode/linters/missing_key.rs +++ b/external-crates/move/crates/move-compiler/src/sui_mode/linters/missing_key.rs @@ -26,7 +26,7 @@ const MISSING_KEY_ABILITY_DIAG: DiagnosticInfo = custom( Severity::Warning, LinterDiagnosticCategory::Sui as u8, LinterDiagnosticCode::MissingKey as u8, - "The struct's first field is 'id' of type 'sui::object::UID' but is missing the 'key' ability.", + "struct with id but missing key ability", ); pub struct MissingKeyVisitor; @@ -51,8 +51,6 @@ impl TypingVisitorContext for Context<'_> { self.env.pop_warning_filter_scope() } - const VISIT_TYPES: bool = true; - fn visit_struct_custom( &mut self, _module: ModuleIdent, @@ -70,9 +68,12 @@ impl TypingVisitorContext for Context<'_> { } fn first_field_has_id_field_of_type_uid(sdef: &StructDefinition) -> bool { - matches!(&sdef.fields, StructFields::Defined(_, fields) if fields.iter().any(|(_, symbol, ftype)| { - ftype.0 == 0 && symbol == &symbol!("id") && ftype.1.value.is("sui", "object", "UID") - })) + match &sdef.fields { + StructFields::Defined(_, fields) => fields.iter().any(|(_, symbol, (idx, ty))| { + *idx == 0 && symbol == &symbol!("id") && ty.value.is("sui", "object", "UID") + }), + StructFields::Native(_) => false, + } } fn lacks_key_ability(sdef: &StructDefinition) -> bool { diff --git a/external-crates/move/crates/move-compiler/src/typing/visitor.rs b/external-crates/move/crates/move-compiler/src/typing/visitor.rs index 1e9a51a0f5e02..8af7b81038e59 100644 --- a/external-crates/move/crates/move-compiler/src/typing/visitor.rs +++ b/external-crates/move/crates/move-compiler/src/typing/visitor.rs @@ -84,13 +84,11 @@ pub trait TypingVisitorContext { self.pop_warning_filter_scope(); return; } - if Self::VISIT_TYPES { - for (struct_name, sdef) in mdef.structs.key_cloned_iter_mut() { - self.visit_struct(ident, struct_name, sdef) - } - for (enum_name, edef) in mdef.enums.key_cloned_iter_mut() { - self.visit_enum(ident, enum_name, edef) - } + for (struct_name, sdef) in mdef.structs.key_cloned_iter_mut() { + self.visit_struct(ident, struct_name, sdef) + } + for (enum_name, edef) in mdef.enums.key_cloned_iter_mut() { + self.visit_enum(ident, enum_name, edef) } for (constant_name, cdef) in mdef.constants.key_cloned_iter_mut() { self.visit_constant(ident, constant_name, cdef) diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/edge_case_lint_missing_key.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/edge_case_lint_missing_key.move index 644fddfdcb112..270976888885f 100644 --- a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/edge_case_lint_missing_key.move +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/edge_case_lint_missing_key.move @@ -1,9 +1,12 @@ module a::edge_cases { - use sui::another::UID as AnotherUID; - + struct UID {} // Test case with a different UID type struct DifferentUID { - id: AnotherUID, + id: sui::another::UID, + } + + struct NotAnObject { + id: UID, } } diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/false_negative_lint_missing_key.exp b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/false_negative_lint_missing_key.exp index 59e10198d3f98..6162ba8ff27d4 100644 --- a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/false_negative_lint_missing_key.exp +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/false_negative_lint_missing_key.exp @@ -1,4 +1,4 @@ -warning[Lint W99007]: The struct's first field is 'id' of type 'sui::object::UID' but is missing the 'key' ability. +warning[Lint W99007]: struct with id but missing key ability ┌─ tests/sui_mode/linter/false_negative_lint_missing_key.move:22:5 │ 22 │ ╭ struct Wrapper { diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/false_negative_lint_missing_key.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/false_negative_lint_missing_key.move index c1db6e30d4591..667c9f88872ca 100644 --- a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/false_negative_lint_missing_key.move +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/false_negative_lint_missing_key.move @@ -28,4 +28,4 @@ module sui::object { struct UID has store { id: address, } -} \ No newline at end of file +} diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_negatives.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_negatives.move index d0371f2f4695a..2f7f69ec9fe5c 100644 --- a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_negatives.move +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_negatives.move @@ -17,6 +17,10 @@ module a::test_false_negatives { id: UID } + struct Capv0 has key { + id: UID + } + public fun freeze_admin_rights(w: AdminRights) { transfer::public_freeze_object(w); } @@ -28,6 +32,10 @@ module a::test_false_negatives { public fun freeze_access_control(w: AccessControl) { transfer::public_freeze_object(w); } + + public fun freeze_cap_v(w: Capv0) { + transfer::public_freeze_object(w); + } } module sui::object { @@ -40,4 +48,4 @@ module sui::transfer { public fun public_freeze_object(_: T) { abort 0 } -} \ No newline at end of file +} diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_positives.exp b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_positives.exp index 920ef1eaee67c..fad2409846d1c 100644 --- a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_positives.exp +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_positives.exp @@ -1,16 +1,18 @@ -warning[Lint W99008]: Freezing a capability-like type can lead to design issues. +warning[Lint W99008]: freezing potential capability ┌─ tests/sui_mode/linter/freezing_capability_false_positives.move:25:9 │ 25 │ transfer::public_freeze_object(w); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Freezing a capability-like type can lead to design issues. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The type 'a::test_false_positives::NoCap' is potentially a capability based on its name │ + = Freezing a capability might lock out critical operations or otherwise open access to operations that otherwise should be restricted = This warning can be suppressed with '#[allow(lint(freezing_capability))]' applied to the 'module' or module member ('const', 'fun', or 'struct') -warning[Lint W99008]: Freezing a capability-like type can lead to design issues. - ┌─ tests/sui_mode/linter/freezing_capability_false_positives.move:37:9 +warning[Lint W99008]: freezing potential capability + ┌─ tests/sui_mode/linter/freezing_capability_false_positives.move:29:9 │ -37 │ transfer::public_freeze_object(w); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Freezing a capability-like type can lead to design issues. +29 │ transfer::public_freeze_object(w); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The type 'a::test_false_positives::CapAndHat' is potentially a capability based on its name │ + = Freezing a capability might lock out critical operations or otherwise open access to operations that otherwise should be restricted = This warning can be suppressed with '#[allow(lint(freezing_capability))]' applied to the 'module' or module member ('const', 'fun', or 'struct') diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_positives.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_positives.move index 452dd131a86f0..c96c031482168 100644 --- a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_positives.move +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_false_positives.move @@ -5,11 +5,11 @@ module a::test_false_positives { use sui::object::UID; use sui::transfer; - struct Capture has key { + struct NoCap has key { id: UID } - struct Handicap has key { + struct CapAndHat has key { id: UID } @@ -21,11 +21,11 @@ module a::test_false_positives { id: UID } - public fun freeze_capture(w: Capture) { + public fun freeze_capture(w: NoCap) { transfer::public_freeze_object(w); } - public fun freeze_handicap(w: Handicap) { + public fun freeze_handicap(w: CapAndHat) { transfer::public_freeze_object(w); } @@ -48,4 +48,4 @@ module sui::transfer { public fun public_freeze_object(_: T) { abort 0 } -} \ No newline at end of file +} diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_suppression.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_suppression.move index 5a48458cd77e4..f0e4f249c0e38 100644 --- a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_suppression.move +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_suppression.move @@ -43,4 +43,4 @@ module sui::transfer { public fun public_freeze_object(_: T) { abort 0 } -} \ No newline at end of file +} diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_negatives.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_negatives.move index e63b71a98e588..e1fa1deb126f8 100644 --- a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_negatives.move +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_negatives.move @@ -17,6 +17,22 @@ module a::test_true_negatives { id: UID } + struct Capture has key { + id: UID + } + + struct Handicap has key { + id: UID + } + + struct Recap has key { + id: UID + } + + struct MyCapybara has key { + id: UID + } + public fun freeze_normal(w: NormalStruct) { transfer::public_freeze_object(w); } @@ -28,6 +44,22 @@ module a::test_true_negatives { public fun freeze_token(w: Token) { transfer::public_freeze_object(w); } + + public fun freeze_capture(w: Capture) { + transfer::public_freeze_object(w); + } + + public fun freeze_handicap(w: Handicap) { + transfer::public_freeze_object(w); + } + + public fun freeze_recap(w: Recap) { + transfer::public_freeze_object(w); + } + + public fun freeze_capybara(w: MyCapybara) { + transfer::public_freeze_object(w); + } } module sui::object { @@ -40,4 +72,4 @@ module sui::transfer { public fun public_freeze_object(_: T) { abort 0 } -} \ No newline at end of file +} diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_positives.exp b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_positives.exp index 935933ef6a714..78c5b8538a69c 100644 --- a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_positives.exp +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_positives.exp @@ -1,24 +1,27 @@ -warning[Lint W99008]: Freezing a capability-like type can lead to design issues. +warning[Lint W99008]: freezing potential capability ┌─ tests/sui_mode/linter/freezing_capability_true_positives.move:21:9 │ 21 │ transfer::public_freeze_object(w); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Freezing a capability-like type can lead to design issues. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The type 'a::test_true_positives::AdminCap' is potentially a capability based on its name │ + = Freezing a capability might lock out critical operations or otherwise open access to operations that otherwise should be restricted = This warning can be suppressed with '#[allow(lint(freezing_capability))]' applied to the 'module' or module member ('const', 'fun', or 'struct') -warning[Lint W99008]: Freezing a capability-like type can lead to design issues. +warning[Lint W99008]: freezing potential capability ┌─ tests/sui_mode/linter/freezing_capability_true_positives.move:25:9 │ 25 │ transfer::public_freeze_object(w); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Freezing a capability-like type can lead to design issues. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The type 'a::test_true_positives::UserCapability' is potentially a capability based on its name │ + = Freezing a capability might lock out critical operations or otherwise open access to operations that otherwise should be restricted = This warning can be suppressed with '#[allow(lint(freezing_capability))]' applied to the 'module' or module member ('const', 'fun', or 'struct') -warning[Lint W99008]: Freezing a capability-like type can lead to design issues. +warning[Lint W99008]: freezing potential capability ┌─ tests/sui_mode/linter/freezing_capability_true_positives.move:29:9 │ 29 │ transfer::public_freeze_object(w); - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Freezing a capability-like type can lead to design issues. + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The type 'a::test_true_positives::OwnerCapV2' is potentially a capability based on its name │ + = Freezing a capability might lock out critical operations or otherwise open access to operations that otherwise should be restricted = This warning can be suppressed with '#[allow(lint(freezing_capability))]' applied to the 'module' or module member ('const', 'fun', or 'struct') diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_positives.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_positives.move index 093051c49855c..03651926773a9 100644 --- a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_positives.move +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/freezing_capability_true_positives.move @@ -40,4 +40,4 @@ module sui::transfer { public fun public_freeze_object(_: T) { abort 0 } -} \ No newline at end of file +} diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/no_trigger_lint_missing_key.exp b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/no_trigger_lint_missing_key.exp deleted file mode 100644 index 0bb5344b4bbc7..0000000000000 --- a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/no_trigger_lint_missing_key.exp +++ /dev/null @@ -1,60 +0,0 @@ -error[Sui E02007]: invalid object declaration - ┌─ tests/sui_mode/linter/no_trigger_lint_missing_key.move:13:9 - │ -12 │ struct FP1_HasKeyButDifferentFieldName has key { - │ --- The 'key' ability is used to declare objects in Sui -13 │ uid: UID, - │ ^^^ Invalid object 'FP1_HasKeyButDifferentFieldName'. Structs with the 'key' ability must have 'id: sui::object::UID' as their first field - -error[Sui E02007]: invalid object declaration - ┌─ tests/sui_mode/linter/no_trigger_lint_missing_key.move:18:9 - │ -17 │ struct FP2_HasKeyUIDNotFirst has key { - │ --- The 'key' ability is used to declare objects in Sui -18 │ point: u64, - │ ^^^^^ Invalid object 'FP2_HasKeyUIDNotFirst'. Structs with the 'key' ability must have 'id: sui::object::UID' as their first field - -error[Sui E02007]: invalid object declaration - ┌─ tests/sui_mode/linter/no_trigger_lint_missing_key.move:24:9 - │ -23 │ struct FP3_HasKeyButIDNotUID has key { - │ --- The 'key' ability is used to declare objects in Sui -24 │ id: address, - │ ^^ ------- But found type: 'address' - │ │ - │ Invalid object 'FP3_HasKeyButIDNotUID'. Structs with the 'key' ability must have 'id: sui::object::UID' as their first field - -warning[Lint W99007]: The struct's first field is 'id' of type 'sui::object::UID' but is missing the 'key' ability. - ┌─ tests/sui_mode/linter/no_trigger_lint_missing_key.move:34:5 - │ -34 │ ╭ struct FP5_HasAbilityButNotKey has store, copy, drop { -35 │ │ id: UID, -36 │ │ } - │ ╰─────^ Struct's first field has an 'id' field of type 'sui::object::UID' but is missing the 'key' ability. - │ - = This warning can be suppressed with '#[allow(lint(missing_key))]' applied to the 'module' or module member ('const', 'fun', or 'struct') - -error[E05001]: ability constraint not satisfied - ┌─ tests/sui_mode/linter/no_trigger_lint_missing_key.move:35:13 - │ -35 │ id: UID, - │ ^^^ - │ │ - │ Invalid field type. The struct was declared with the ability 'copy' so all fields require the ability 'copy' - │ The type 'sui::object::UID' does not have the ability 'copy' - · -40 │ struct UID has store { - │ --- To satisfy the constraint, the 'copy' ability would need to be added here - -error[E05001]: ability constraint not satisfied - ┌─ tests/sui_mode/linter/no_trigger_lint_missing_key.move:35:13 - │ -35 │ id: UID, - │ ^^^ - │ │ - │ Invalid field type. The struct was declared with the ability 'drop' so all fields require the ability 'drop' - │ The type 'sui::object::UID' does not have the ability 'drop' - · -40 │ struct UID has store { - │ --- To satisfy the constraint, the 'drop' ability would need to be added here - diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/no_trigger_lint_missing_key.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/no_trigger_lint_missing_key.move index 4d5b6a0009d80..6918849f48883 100644 --- a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/no_trigger_lint_missing_key.move +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/no_trigger_lint_missing_key.move @@ -5,39 +5,10 @@ module a::no_trigger_lint_cases { struct HasKeyAbility has key { id: UID, } - - // False positive cases (should not trigger warning but might): - - // 1. Has key but different field name - struct FP1_HasKeyButDifferentFieldName has key { - uid: UID, - } - - // 2. Has key but UID field not first - struct FP2_HasKeyUIDNotFirst has key { - point: u64, - id: UID, - } - - // 3. Has key with ID field of different type - struct FP3_HasKeyButIDNotUID has key { - id: address, - } - - // 4. Suppress warning - #[allow(lint(missing_key))] - struct SuppressWarning { - id: UID, - } - - // 5. Has ability but not key - struct FP5_HasAbilityButNotKey has store, copy, drop { - id: UID, - } } module sui::object { struct UID has store { id: address, } -} \ No newline at end of file +} diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/suppress_lint_missing_key.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/suppress_lint_missing_key.move index 83d07d6655c94..3e86667a95af7 100644 --- a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/suppress_lint_missing_key.move +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/suppress_lint_missing_key.move @@ -12,4 +12,4 @@ module sui::object { struct UID has store { id: address, } -} \ No newline at end of file +} diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/trigger_lint_missing_key.exp b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/trigger_lint_missing_key.exp index 597643f3a342d..243ce11581d70 100644 --- a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/trigger_lint_missing_key.exp +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/trigger_lint_missing_key.exp @@ -1,4 +1,4 @@ -warning[Lint W99007]: The struct's first field is 'id' of type 'sui::object::UID' but is missing the 'key' ability. +warning[Lint W99007]: struct with id but missing key ability ┌─ tests/sui_mode/linter/trigger_lint_missing_key.move:5:5 │ 5 │ ╭ struct MissingKeyAbility { @@ -8,13 +8,3 @@ warning[Lint W99007]: The struct's first field is 'id' of type 'sui::object::UID │ = This warning can be suppressed with '#[allow(lint(missing_key))]' applied to the 'module' or module member ('const', 'fun', or 'struct') -warning[Lint W99007]: The struct's first field is 'id' of type 'sui::object::UID' but is missing the 'key' ability. - ┌─ tests/sui_mode/linter/trigger_lint_missing_key.move:27:5 - │ -27 │ ╭ struct Wrapper { -28 │ │ id: UID, -29 │ │ } - │ ╰─────^ Struct's first field has an 'id' field of type 'sui::object::UID' but is missing the 'key' ability. - │ - = This warning can be suppressed with '#[allow(lint(missing_key))]' applied to the 'module' or module member ('const', 'fun', or 'struct') - diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/trigger_lint_missing_key.move b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/trigger_lint_missing_key.move index 9d9c2952df71a..5b6bb908d4ff3 100644 --- a/external-crates/move/crates/move-compiler/tests/sui_mode/linter/trigger_lint_missing_key.move +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/linter/trigger_lint_missing_key.move @@ -6,31 +6,10 @@ module a::trigger_lint_cases { id: UID, } - // False negative cases (should trigger warning but might not): - - // 1. Different field name - struct FN1_MissingKeyWithDifferentFieldName { - uid: UID, - } - - // 2. UID field not first - struct FN2_MissingKeyUIDNotFirst { - point: u64, - id: UID, - } - - // 3. Nested UID - struct FN3_MissingKeyNestedUID { - wrapper: Wrapper, - } - - struct Wrapper { - id: UID, - } } module sui::object { struct UID has store { id: address, } -} \ No newline at end of file +} From 3182fe63c5ddbc569d574f7543a4051ec744a7d5 Mon Sep 17 00:00:00 2001 From: Tim Zakian <2895723+tzakian@users.noreply.github.com> Date: Wed, 21 Aug 2024 15:21:31 -0700 Subject: [PATCH 208/232] [move] Update error behavior on serialization boundaries (#19064) ## Description Update the semantics around serialization errors when computing type layouts to follow the underlying layout errors more closely. ## Test plan Manually tested. --- crates/sui-open-rpc/spec/openrpc.json | 1 + crates/sui-protocol-config/src/lib.rs | 11 +++++++++++ ...ocol_config__test__Mainnet_version_55.snap | 2 ++ ...ocol_config__test__Testnet_version_55.snap | 2 ++ ...sui_protocol_config__test__version_55.snap | 2 ++ .../move/crates/move-vm-config/src/runtime.rs | 4 ++++ .../crates/move-vm-runtime/src/runtime.rs | 19 ++++++++++++++----- .../latest/sui-adapter/src/adapter.rs | 2 ++ sui-execution/v0/sui-adapter/src/adapter.rs | 2 ++ sui-execution/v1/sui-adapter/src/adapter.rs | 2 ++ sui-execution/v2/sui-adapter/src/adapter.rs | 2 ++ 11 files changed, 44 insertions(+), 5 deletions(-) diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index 8abc1b3548a64..f01df691e4369 100644 --- a/crates/sui-open-rpc/spec/openrpc.json +++ b/crates/sui-open-rpc/spec/openrpc.json @@ -1341,6 +1341,7 @@ "reject_mutable_random_on_entry_functions": false, "reshare_at_same_initial_version": false, "resolve_abort_locations_to_package_id": false, + "rethrow_serialization_type_layout_errors": false, "scoring_decision_with_validity_cutoff": true, "shared_object_deletion": false, "simple_conservation_checks": false, diff --git a/crates/sui-protocol-config/src/lib.rs b/crates/sui-protocol-config/src/lib.rs index e62326fa160d5..3d679dafe056a 100644 --- a/crates/sui-protocol-config/src/lib.rs +++ b/crates/sui-protocol-config/src/lib.rs @@ -168,6 +168,7 @@ const MAX_PROTOCOL_VERSION: u64 = 55; // Version 54: Enable random beacon on mainnet. // Enable soft bundle on mainnet. // Version 55: Enable enums on mainnet. +// Rethrow serialization type layout errors instead of converting them. #[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct ProtocolVersion(u64); @@ -506,6 +507,10 @@ struct FeatureFlags { // Use AuthorityCapabilitiesV2 #[serde(skip_serializing_if = "is_false")] authority_capabilities_v2: bool, + + // Rethrow type layout errors during serialization instead of trying to convert them. + #[serde(skip_serializing_if = "is_false")] + rethrow_serialization_type_layout_errors: bool, } fn is_false(b: &bool) -> bool { @@ -1536,6 +1541,10 @@ impl ProtocolConfig { // 500 is the value used before this field is introduced. self.consensus_max_num_transactions_in_block.unwrap_or(500) } + + pub fn rethrow_serialization_type_layout_errors(&self) -> bool { + self.feature_flags.rethrow_serialization_type_layout_errors + } } #[cfg(not(msim))] @@ -2671,6 +2680,8 @@ impl ProtocolConfig { // Assume 20_000 TPS * 5% max stake per validator / (minimum) 4 blocks per round = 250 transactions per block maximum // Using a higher limit that is 512, to account for bursty traffic and system transactions. cfg.consensus_max_num_transactions_in_block = Some(512); + + cfg.feature_flags.rethrow_serialization_type_layout_errors = true; } // Use this template when making changes: // diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_55.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_55.snap index 3a9ecd48e722f..6d8a2e27dc2b0 100644 --- a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_55.snap +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_55.snap @@ -58,6 +58,7 @@ feature_flags: mysticeti_num_leaders_per_round: 1 soft_bundle: true enable_coin_deny_list_v2: true + rethrow_serialization_type_layout_errors: true max_tx_size_bytes: 131072 max_input_objects: 2048 max_size_written_objects: 5000000 @@ -319,3 +320,4 @@ checkpoint_summary_version_specific_data: 1 max_soft_bundle_size: 5 bridge_should_try_to_finalize_committee: false max_accumulated_txn_cost_per_object_in_mysticeti_commit: 10 + diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_55.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_55.snap index 13f98870266dd..223b9b68af4c3 100644 --- a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_55.snap +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_55.snap @@ -59,6 +59,7 @@ feature_flags: mysticeti_num_leaders_per_round: 1 soft_bundle: true enable_coin_deny_list_v2: true + rethrow_serialization_type_layout_errors: true max_tx_size_bytes: 131072 max_input_objects: 2048 max_size_written_objects: 5000000 @@ -320,3 +321,4 @@ 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: 10 + diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_55.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_55.snap index 13317be055abe..69b7747d1bb49 100644 --- a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_55.snap +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_55.snap @@ -64,6 +64,7 @@ feature_flags: enable_coin_deny_list_v2: true passkey_auth: true authority_capabilities_v2: true + rethrow_serialization_type_layout_errors: true max_tx_size_bytes: 131072 max_input_objects: 2048 max_size_written_objects: 5000000 @@ -329,3 +330,4 @@ 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: 10 + diff --git a/external-crates/move/crates/move-vm-config/src/runtime.rs b/external-crates/move/crates/move-vm-config/src/runtime.rs index 515efeec3f6ef..d51578a3d208d 100644 --- a/external-crates/move/crates/move-vm-config/src/runtime.rs +++ b/external-crates/move/crates/move-vm-config/src/runtime.rs @@ -33,6 +33,9 @@ pub struct VMConfig { pub error_execution_state: bool, // configuration for binary deserialization (modules) pub binary_config: BinaryConfig, + // Whether value serialization errors when generating type layouts should be rethrown or + // converted to a different error. + pub rethrow_serialization_type_layout_errors: bool, } impl Default for VMConfig { @@ -46,6 +49,7 @@ impl Default for VMConfig { profiler_config: None, error_execution_state: true, binary_config: BinaryConfig::with_extraneous_bytes_check(false), + rethrow_serialization_type_layout_errors: false, } } } diff --git a/external-crates/move/crates/move-vm-runtime/src/runtime.rs b/external-crates/move/crates/move-vm-runtime/src/runtime.rs index 924a8f9df983d..8a1a344f55b9f 100644 --- a/external-crates/move/crates/move-vm-runtime/src/runtime.rs +++ b/external-crates/move/crates/move-vm-runtime/src/runtime.rs @@ -265,11 +265,20 @@ impl VMRuntime { _ => (ty, value), }; - let layout = self.loader.type_to_type_layout(ty).map_err(|_err| { - PartialVMError::new(StatusCode::VERIFICATION_ERROR).with_message( - "entry point functions cannot have non-serializable return types".to_string(), - ) - })?; + let layout = if self + .loader() + .vm_config() + .rethrow_serialization_type_layout_errors + { + self.loader.type_to_type_layout(ty)? + } else { + self.loader.type_to_type_layout(ty).map_err(|_err| { + PartialVMError::new(StatusCode::VERIFICATION_ERROR).with_message( + "entry point functions cannot have non-serializable return types".to_string(), + ) + })? + }; + let bytes = value.simple_serialize(&layout).ok_or_else(|| { PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) .with_message("failed to serialize return values".to_string()) diff --git a/sui-execution/latest/sui-adapter/src/adapter.rs b/sui-execution/latest/sui-adapter/src/adapter.rs index 25da7df5a7617..149fa4b1a83b7 100644 --- a/sui-execution/latest/sui-adapter/src/adapter.rs +++ b/sui-execution/latest/sui-adapter/src/adapter.rs @@ -70,6 +70,8 @@ mod checked { // Don't augment errors with execution state on-chain error_execution_state: false, binary_config: to_binary_config(protocol_config), + rethrow_serialization_type_layout_errors: protocol_config + .rethrow_serialization_type_layout_errors(), }, ) .map_err(|_| SuiError::ExecutionInvariantViolation) diff --git a/sui-execution/v0/sui-adapter/src/adapter.rs b/sui-execution/v0/sui-adapter/src/adapter.rs index 7570eac77a7d1..73fccf3124b9a 100644 --- a/sui-execution/v0/sui-adapter/src/adapter.rs +++ b/sui-execution/v0/sui-adapter/src/adapter.rs @@ -72,6 +72,8 @@ mod checked { profiler_config: vm_profiler_config, binary_config: to_binary_config(protocol_config), + rethrow_serialization_type_layout_errors: protocol_config + .rethrow_serialization_type_layout_errors(), }, ) .map_err(|_| SuiError::ExecutionInvariantViolation) diff --git a/sui-execution/v1/sui-adapter/src/adapter.rs b/sui-execution/v1/sui-adapter/src/adapter.rs index bd9f4eeccb2d0..633abec22e6b2 100644 --- a/sui-execution/v1/sui-adapter/src/adapter.rs +++ b/sui-execution/v1/sui-adapter/src/adapter.rs @@ -72,6 +72,8 @@ mod checked { // Don't augment errors with execution state on-chain error_execution_state: false, binary_config: to_binary_config(protocol_config), + rethrow_serialization_type_layout_errors: protocol_config + .rethrow_serialization_type_layout_errors(), }, ) .map_err(|_| SuiError::ExecutionInvariantViolation) diff --git a/sui-execution/v2/sui-adapter/src/adapter.rs b/sui-execution/v2/sui-adapter/src/adapter.rs index d8d0d1bfbc7d7..b7f6ccfb3e04c 100644 --- a/sui-execution/v2/sui-adapter/src/adapter.rs +++ b/sui-execution/v2/sui-adapter/src/adapter.rs @@ -70,6 +70,8 @@ mod checked { // Don't augment errors with execution state on-chain error_execution_state: false, binary_config: to_binary_config(protocol_config), + rethrow_serialization_type_layout_errors: protocol_config + .rethrow_serialization_type_layout_errors(), }, ) .map_err(|_| SuiError::ExecutionInvariantViolation) From f746620d04b7d49be5e2162396fcd3088d029c6a Mon Sep 17 00:00:00 2001 From: wlmyng <127570466+wlmyng@users.noreply.github.com> Date: Thu, 22 Aug 2024 05:28:51 -0700 Subject: [PATCH 209/232] [GraphQL/TransactionBlock] Scan Limits (#18413) ## Description Implement learnings from GraphQL performance benchmarks: - Implement transaction block pagination as a two step process: First fetch the relevant transaction sequence numbers, then fetch their contents. - Every "atomic" filter on transaction blocks is served by a single `tx_` table, with two indices on it, both of which are prepped to perform index-only scans. - The primary index is used to apply the filter directly. - The secondary index applies the filter after limiting results to one sender. - Compound filters are served by joining multiple atomic filters together. - The "scan limit" concept is introduced to limit the amount of work done when dealing with compound filters (see below). ### Scan Limits - If a filter is compound, a scan limit must be provided, and controls how many transactions are considered as candidates when building a page of results. - There is an upperbound on the scan limit, currently 100M, which is enough for over a week of transactions at 100TPS. - When scan limits are enabled, pagination behaviour changes: Pages can be returned with fewer results than the page size (including no results), but still have a previous or next page, because there were no valid candidates in the area scanned but there is more room to scan on either side. - The start and end cursor for the page may no longer point to an element in the results, because they point to the first and last candidate transaction. ## Test plan ``` sui$ cargo build -p sui-indexer sui$ cargo nextest run -p sui-graphql-rpc sui$ cargo nextest run -p sui-graphql-e2e-tests --features pg_integration ``` --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [x] GraphQL: Introduce `scanLimit` for paginating `TransactionBlocks`. Queries that include multiple complex filters (filters on the function called, affected objects, recipient), need to include a scan limit which controls the number of transactions that are looked at as candidates. - [ ] CLI: - [ ] Rust SDK: --------- Co-authored-by: Ashok Menon --- .../tests/consistency/balances.exp | 24 +- .../tests/consistency/balances.move | 24 +- .../checkpoints/transaction_blocks.exp | 18 +- .../consistency/epochs/transaction_blocks.exp | 122 ++-- .../epochs/transaction_blocks.move | 8 +- .../tests/transactions/at_checkpoint.exp | 210 ++++++ .../tests/transactions/at_checkpoint.move | 63 ++ .../tests/transactions/filters/kind.exp | 246 +++++++ .../tests/transactions/filters/kind.move | 173 +++++ .../transactions/filters/transaction_ids.exp | 95 +++ .../transactions/filters/transaction_ids.move | 105 +++ .../tests/transactions/programmable.exp | 34 +- .../transactions/scan_limit/alternating.exp | 300 +++++++++ .../transactions/scan_limit/alternating.move | 235 +++++++ .../transactions/scan_limit/both_cursors.exp | 177 +++++ .../transactions/scan_limit/both_cursors.move | 133 ++++ .../transactions/scan_limit/equal/first.exp | 358 +++++++++++ .../transactions/scan_limit/equal/first.move | 281 ++++++++ .../transactions/scan_limit/equal/last.exp | 309 +++++++++ .../transactions/scan_limit/equal/last.move | 235 +++++++ .../transactions/scan_limit/ge_page/first.exp | 378 +++++++++++ .../scan_limit/ge_page/first.move | 258 ++++++++ .../transactions/scan_limit/ge_page/last.exp | 355 ++++++++++ .../transactions/scan_limit/ge_page/last.move | 251 ++++++++ .../scan_limit/invalid_limits.exp | 112 ++++ .../scan_limit/invalid_limits.move | 135 ++++ .../transactions/scan_limit/le_page/first.exp | 364 +++++++++++ .../scan_limit/le_page/first.move | 232 +++++++ .../transactions/scan_limit/le_page/last.exp | 365 +++++++++++ .../transactions/scan_limit/le_page/last.move | 227 +++++++ .../tests/transactions/scan_limit/require.exp | 606 ++++++++++++++++++ .../transactions/scan_limit/require.move | 269 ++++++++ ...h_tx_block_connection_latest_epoch.graphql | 2 +- crates/sui-graphql-rpc/schema.graphql | 237 ++++++- crates/sui-graphql-rpc/src/config.rs | 26 + crates/sui-graphql-rpc/src/connection.rs | 125 ++++ crates/sui-graphql-rpc/src/consistency.rs | 6 +- crates/sui-graphql-rpc/src/data/pg.rs | 16 +- crates/sui-graphql-rpc/src/lib.rs | 1 + crates/sui-graphql-rpc/src/raw_query.rs | 24 +- crates/sui-graphql-rpc/src/types/address.rs | 37 +- crates/sui-graphql-rpc/src/types/balance.rs | 4 +- .../sui-graphql-rpc/src/types/checkpoint.rs | 39 +- crates/sui-graphql-rpc/src/types/coin.rs | 25 +- .../src/types/coin_metadata.rs | 25 +- crates/sui-graphql-rpc/src/types/cursor.rs | 60 +- crates/sui-graphql-rpc/src/types/epoch.rs | 35 +- crates/sui-graphql-rpc/src/types/event.rs | 4 +- .../sui-graphql-rpc/src/types/move_object.rs | 25 +- .../sui-graphql-rpc/src/types/move_package.rs | 29 +- crates/sui-graphql-rpc/src/types/object.rs | 50 +- crates/sui-graphql-rpc/src/types/query.rs | 27 +- crates/sui-graphql-rpc/src/types/stake.rs | 25 +- .../src/types/suins_registration.rs | 25 +- .../src/types/transaction_block/cursor.rs | 173 +++++ .../src/types/transaction_block/filter.rs | 130 ++++ .../mod.rs} | 440 ++++++------- .../src/types/transaction_block/tx_lookups.rs | 433 +++++++++++++ .../sui-graphql-rpc/src/types/type_filter.rs | 26 - .../snapshot_tests__schema_sdl_export.snap | 237 ++++++- 60 files changed, 8523 insertions(+), 465 deletions(-) create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/at_checkpoint.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/at_checkpoint.move create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/filters/kind.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/filters/kind.move create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/filters/transaction_ids.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/filters/transaction_ids.move create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/alternating.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/alternating.move create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/both_cursors.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/both_cursors.move create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/equal/first.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/equal/first.move create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/equal/last.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/equal/last.move create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/ge_page/first.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/ge_page/first.move create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/ge_page/last.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/ge_page/last.move create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/invalid_limits.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/invalid_limits.move create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/le_page/first.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/le_page/first.move create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/le_page/last.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/le_page/last.move create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/require.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/require.move create mode 100644 crates/sui-graphql-rpc/src/connection.rs create mode 100644 crates/sui-graphql-rpc/src/types/transaction_block/cursor.rs create mode 100644 crates/sui-graphql-rpc/src/types/transaction_block/filter.rs rename crates/sui-graphql-rpc/src/types/{transaction_block.rs => transaction_block/mod.rs} (63%) create mode 100644 crates/sui-graphql-rpc/src/types/transaction_block/tx_lookups.rs diff --git a/crates/sui-graphql-e2e-tests/tests/consistency/balances.exp b/crates/sui-graphql-e2e-tests/tests/consistency/balances.exp index 0420427fbe84c..e4529876bfe9b 100644 --- a/crates/sui-graphql-e2e-tests/tests/consistency/balances.exp +++ b/crates/sui-graphql-e2e-tests/tests/consistency/balances.exp @@ -59,7 +59,7 @@ task 15, line 76: Checkpoint created: 7 task 16, lines 78-99: -//# run-graphql --cursors {"c":2,"t":1,"tc":1} +//# run-graphql --cursors {"c":2,"t":1,"i":false} Response: { "data": { "transactionBlocks": { @@ -95,7 +95,7 @@ Response: { } task 17, lines 101-122: -//# run-graphql --cursors {"c":3,"t":1,"tc":1} +//# run-graphql --cursors {"c":3,"t":1,"i":false} Response: { "data": { "transactionBlocks": { @@ -131,7 +131,7 @@ Response: { } task 18, lines 124-145: -//# run-graphql --cursors {"c":4,"t":1,"tc":1} +//# run-graphql --cursors {"c":4,"t":1,"i":false} Response: { "data": { "transactionBlocks": { @@ -175,7 +175,7 @@ task 20, line 149: Checkpoint created: 8 task 21, lines 151-172: -//# run-graphql --cursors {"c":2,"t":1,"tc":1} +//# run-graphql --cursors {"c":2,"t":1,"i":false} Response: { "data": { "transactionBlocks": { @@ -211,7 +211,7 @@ Response: { } task 22, lines 174-195: -//# run-graphql --cursors {"c":3,"t":1,"tc":1} +//# run-graphql --cursors {"c":3,"t":1,"i":false} Response: { "data": { "transactionBlocks": { @@ -247,7 +247,7 @@ Response: { } task 23, lines 197-218: -//# run-graphql --cursors {"c":4,"t":1,"tc":1} +//# run-graphql --cursors {"c":4,"t":1,"i":false} Response: { "data": { "transactionBlocks": { @@ -291,7 +291,7 @@ task 25, line 222: Checkpoint created: 9 task 26, lines 224-245: -//# run-graphql --cursors {"c":2,"t":1,"tc":1} +//# run-graphql --cursors {"c":2,"t":1,"i":false} Response: { "data": { "transactionBlocks": { @@ -326,7 +326,7 @@ Response: { } task 27, lines 247-268: -//# run-graphql --cursors {"c":3,"t":1,"tc":1} +//# run-graphql --cursors {"c":3,"t":1,"i":false} Response: { "data": { "transactionBlocks": { @@ -362,7 +362,7 @@ Response: { } task 28, lines 270-291: -//# run-graphql --cursors {"c":4,"t":1,"tc":1} +//# run-graphql --cursors {"c":4,"t":1,"i":false} Response: { "data": { "transactionBlocks": { @@ -406,7 +406,7 @@ task 30, line 296: Checkpoint created: 10 task 31, lines 298-319: -//# run-graphql --cursors {"c":2,"t":1,"tc":1} +//# run-graphql --cursors {"c":2,"t":1,"i":false} Response: { "data": { "transactionBlocks": { @@ -441,7 +441,7 @@ Response: { } task 32, lines 321-342: -//# run-graphql --cursors {"c":3,"t":1,"tc":1} +//# run-graphql --cursors {"c":3,"t":1,"i":false} Response: { "data": { "transactionBlocks": { @@ -476,7 +476,7 @@ Response: { } task 33, lines 344-365: -//# run-graphql --cursors {"c":4,"t":1,"tc":1} +//# run-graphql --cursors {"c":4,"t":1,"i":false} Response: { "data": { "transactionBlocks": { diff --git a/crates/sui-graphql-e2e-tests/tests/consistency/balances.move b/crates/sui-graphql-e2e-tests/tests/consistency/balances.move index 70059773d3946..48bb049a865ab 100644 --- a/crates/sui-graphql-e2e-tests/tests/consistency/balances.move +++ b/crates/sui-graphql-e2e-tests/tests/consistency/balances.move @@ -75,7 +75,7 @@ module P0::fake { //# create-checkpoint -//# run-graphql --cursors {"c":2,"t":1,"tc":1} +//# 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}"}) { @@ -98,7 +98,7 @@ module P0::fake { } } -//# run-graphql --cursors {"c":3,"t":1,"tc":1} +//# 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}"}) { @@ -121,7 +121,7 @@ module P0::fake { } } -//# run-graphql --cursors {"c":4,"t":1,"tc":1} +//# 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}"}) { @@ -148,7 +148,7 @@ module P0::fake { //# create-checkpoint -//# run-graphql --cursors {"c":2,"t":1,"tc":1} +//# 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}"}) { @@ -171,7 +171,7 @@ module P0::fake { } } -//# run-graphql --cursors {"c":3,"t":1,"tc":1} +//# 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}"}) { @@ -194,7 +194,7 @@ module P0::fake { } } -//# run-graphql --cursors {"c":4,"t":1,"tc":1} +//# 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}"}) { @@ -221,7 +221,7 @@ module P0::fake { //# create-checkpoint -//# run-graphql --cursors {"c":2,"t":1,"tc":1} +//# run-graphql --cursors {"c":2,"t":1,"i":false} # Outside available range { transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { @@ -244,7 +244,7 @@ module P0::fake { } } -//# run-graphql --cursors {"c":3,"t":1,"tc":1} +//# 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}"}) { @@ -267,7 +267,7 @@ module P0::fake { } } -//# run-graphql --cursors {"c":4,"t":1,"tc":1} +//# 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}"}) { @@ -295,7 +295,7 @@ module P0::fake { //# create-checkpoint -//# run-graphql --cursors {"c":2,"t":1,"tc":1} +//# run-graphql --cursors {"c":2,"t":1,"i":false} # Outside available range { transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { @@ -318,7 +318,7 @@ module P0::fake { } } -//# run-graphql --cursors {"c":3,"t":1,"tc":1} +//# run-graphql --cursors {"c":3,"t":1,"i":false} # Outside available range { transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { @@ -341,7 +341,7 @@ module P0::fake { } } -//# run-graphql --cursors {"c":4,"t":1,"tc":1} +//# run-graphql --cursors {"c":4,"t":1,"i":false} # Outside available range { transactionBlocks(first: 1, after: "@{cursor_0}", filter: {signAddress: "@{A}"}) { diff --git a/crates/sui-graphql-e2e-tests/tests/consistency/checkpoints/transaction_blocks.exp b/crates/sui-graphql-e2e-tests/tests/consistency/checkpoints/transaction_blocks.exp index a02e97976ee4a..add91acf3898d 100644 --- a/crates/sui-graphql-e2e-tests/tests/consistency/checkpoints/transaction_blocks.exp +++ b/crates/sui-graphql-e2e-tests/tests/consistency/checkpoints/transaction_blocks.exp @@ -92,7 +92,7 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjozLCJ0IjoyLCJ0YyI6MX0", + "cursor": "eyJjIjozLCJ0IjoyLCJpIjpmYWxzZX0", "node": { "digest": "Cwqr9jTgQjajoYaqcjzAaQGcQEyCg8XxoN7smGCLiBrs", "sender": { @@ -107,7 +107,7 @@ Response: { } }, { - "cursor": "eyJjIjozLCJ0IjozLCJ0YyI6MX0", + "cursor": "eyJjIjozLCJ0IjozLCJpIjpmYWxzZX0", "node": { "digest": "H1WU8uXMGaENQs54EpoHGpV1iMYdH8P5scd1d16s9ECB", "sender": { @@ -122,7 +122,7 @@ Response: { } }, { - "cursor": "eyJjIjozLCJ0Ijo0LCJ0YyI6MX0", + "cursor": "eyJjIjozLCJ0Ijo0LCJpIjpmYWxzZX0", "node": { "digest": "4vJbSYKwEJb5sYU2jiayqsZNRnBywD8y6sd3RQoMppF9", "sender": { @@ -137,7 +137,7 @@ Response: { } }, { - "cursor": "eyJjIjozLCJ0Ijo1LCJ0YyI6MX0", + "cursor": "eyJjIjozLCJ0Ijo1LCJpIjpmYWxzZX0", "node": { "digest": "4W23PZz7dHVxoZ2VMCWU9j38Jxy7tLkqcFBcJUB3aCSB", "sender": { @@ -159,7 +159,7 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjozLCJ0Ijo2LCJ0YyI6Mn0", + "cursor": "eyJjIjozLCJ0Ijo2LCJpIjpmYWxzZX0", "node": { "digest": "JLAF7P6DumC8rgzT1Ygp2QgTwpHE2FUqQbVXL6cGEEQ", "sender": { @@ -174,7 +174,7 @@ Response: { } }, { - "cursor": "eyJjIjozLCJ0Ijo3LCJ0YyI6Mn0", + "cursor": "eyJjIjozLCJ0Ijo3LCJpIjpmYWxzZX0", "node": { "digest": "BVMVdn7DDpTbCjtYwWFekcFA9sNeMgDh1wTNWRrngZxh", "sender": { @@ -189,7 +189,7 @@ Response: { } }, { - "cursor": "eyJjIjozLCJ0Ijo4LCJ0YyI6Mn0", + "cursor": "eyJjIjozLCJ0Ijo4LCJpIjpmYWxzZX0", "node": { "digest": "4J5tno4AoU4NPS2NgEseAZK7cpLDh6KJduVtbtwzmHk5", "sender": { @@ -211,7 +211,7 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjozLCJ0Ijo5LCJ0YyI6M30", + "cursor": "eyJjIjozLCJ0Ijo5LCJpIjpmYWxzZX0", "node": { "digest": "5BCS9sencxEJRJHBBPeGhx3rWutYoGSuLFCmnMAaYcDm", "sender": { @@ -226,7 +226,7 @@ Response: { } }, { - "cursor": "eyJjIjozLCJ0IjoxMCwidGMiOjN9", + "cursor": "eyJjIjozLCJ0IjoxMCwiaSI6ZmFsc2V9", "node": { "digest": "HQYJnLLcGf4DwgTkpqF4zHbQsLHwc1s4WbQ3Xr5BBaxh", "sender": { diff --git a/crates/sui-graphql-e2e-tests/tests/consistency/epochs/transaction_blocks.exp b/crates/sui-graphql-e2e-tests/tests/consistency/epochs/transaction_blocks.exp index 721c1e0dafaf0..7b25ef908d6bf 100644 --- a/crates/sui-graphql-e2e-tests/tests/consistency/epochs/transaction_blocks.exp +++ b/crates/sui-graphql-e2e-tests/tests/consistency/epochs/transaction_blocks.exp @@ -39,25 +39,25 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjozLCJ0IjowLCJ0YyI6MH0", + "cursor": "eyJjIjozLCJ0IjowLCJpIjpmYWxzZX0", "node": { "digest": "J7mHXcoa7LXwyjzZUWsk8zvYZjek359TM4d2hQK4LGHo" } }, { - "cursor": "eyJjIjozLCJ0IjoxLCJ0YyI6MX0", + "cursor": "eyJjIjozLCJ0IjoxLCJpIjpmYWxzZX0", "node": { "digest": "J1pYPDrTgsKgzB8XWtW8jLJ8RPsbJcC1SQ4Mv2T1hAWt" } }, { - "cursor": "eyJjIjozLCJ0IjoyLCJ0YyI6Mn0", + "cursor": "eyJjIjozLCJ0IjoyLCJpIjpmYWxzZX0", "node": { "digest": "Cwqr9jTgQjajoYaqcjzAaQGcQEyCg8XxoN7smGCLiBrs" } }, { - "cursor": "eyJjIjozLCJ0IjozLCJ0YyI6M30", + "cursor": "eyJjIjozLCJ0IjozLCJpIjpmYWxzZX0", "node": { "digest": "Bym7b7ELP77KxVHtgj6F4FB7H6n5LYQuBQYmdvvFxEmM" } @@ -141,7 +141,7 @@ task 21, line 91: Epoch advanced: 3 task 22, lines 93-157: -//# run-graphql --cursors {"t":3,"tc":3,"c":4} {"t":7,"tc":7,"c":8} {"t":11,"tc":11,"c":12} +//# run-graphql --cursors {"t":3,"i":false,"c":4} {"t":7,"i":false,"c":8} {"t":11,"i":false,"c":12} Response: { "data": { "checkpoint": { @@ -152,25 +152,25 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjoxMiwidCI6MCwidGMiOjB9", + "cursor": "eyJjIjoxMiwidCI6MCwiaSI6ZmFsc2V9", "node": { "digest": "J7mHXcoa7LXwyjzZUWsk8zvYZjek359TM4d2hQK4LGHo" } }, { - "cursor": "eyJjIjoxMiwidCI6MSwidGMiOjF9", + "cursor": "eyJjIjoxMiwidCI6MSwiaSI6ZmFsc2V9", "node": { "digest": "J1pYPDrTgsKgzB8XWtW8jLJ8RPsbJcC1SQ4Mv2T1hAWt" } }, { - "cursor": "eyJjIjoxMiwidCI6MiwidGMiOjJ9", + "cursor": "eyJjIjoxMiwidCI6MiwiaSI6ZmFsc2V9", "node": { "digest": "Cwqr9jTgQjajoYaqcjzAaQGcQEyCg8XxoN7smGCLiBrs" } }, { - "cursor": "eyJjIjoxMiwidCI6MywidGMiOjN9", + "cursor": "eyJjIjoxMiwidCI6MywiaSI6ZmFsc2V9", "node": { "digest": "Bym7b7ELP77KxVHtgj6F4FB7H6n5LYQuBQYmdvvFxEmM" } @@ -181,19 +181,19 @@ Response: { "txs_epoch_0": { "edges": [ { - "cursor": "eyJjIjo0LCJ0IjowLCJ0YyI6MH0", + "cursor": "eyJjIjo0LCJ0IjowLCJpIjpmYWxzZX0", "node": { "digest": "J7mHXcoa7LXwyjzZUWsk8zvYZjek359TM4d2hQK4LGHo" } }, { - "cursor": "eyJjIjo0LCJ0IjoxLCJ0YyI6MX0", + "cursor": "eyJjIjo0LCJ0IjoxLCJpIjpmYWxzZX0", "node": { "digest": "J1pYPDrTgsKgzB8XWtW8jLJ8RPsbJcC1SQ4Mv2T1hAWt" } }, { - "cursor": "eyJjIjo0LCJ0IjoyLCJ0YyI6Mn0", + "cursor": "eyJjIjo0LCJ0IjoyLCJpIjpmYWxzZX0", "node": { "digest": "Cwqr9jTgQjajoYaqcjzAaQGcQEyCg8XxoN7smGCLiBrs" } @@ -205,25 +205,25 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjoxMiwidCI6NCwidGMiOjR9", + "cursor": "eyJjIjoxMiwidCI6NCwiaSI6ZmFsc2V9", "node": { "digest": "H1WU8uXMGaENQs54EpoHGpV1iMYdH8P5scd1d16s9ECB" } }, { - "cursor": "eyJjIjoxMiwidCI6NSwidGMiOjV9", + "cursor": "eyJjIjoxMiwidCI6NSwiaSI6ZmFsc2V9", "node": { "digest": "4vJbSYKwEJb5sYU2jiayqsZNRnBywD8y6sd3RQoMppF9" } }, { - "cursor": "eyJjIjoxMiwidCI6NiwidGMiOjZ9", + "cursor": "eyJjIjoxMiwidCI6NiwiaSI6ZmFsc2V9", "node": { "digest": "4W23PZz7dHVxoZ2VMCWU9j38Jxy7tLkqcFBcJUB3aCSB" } }, { - "cursor": "eyJjIjoxMiwidCI6NywidGMiOjd9", + "cursor": "eyJjIjoxMiwidCI6NywiaSI6ZmFsc2V9", "node": { "digest": "D251V1BnvyRKNFZmiFxaf7gSZLGdLo8fYbbVDb5vJWfd" } @@ -234,43 +234,43 @@ Response: { "txs_epoch_1": { "edges": [ { - "cursor": "eyJjIjo4LCJ0IjowLCJ0YyI6MH0", + "cursor": "eyJjIjo4LCJ0IjowLCJpIjpmYWxzZX0", "node": { "digest": "J7mHXcoa7LXwyjzZUWsk8zvYZjek359TM4d2hQK4LGHo" } }, { - "cursor": "eyJjIjo4LCJ0IjoxLCJ0YyI6MX0", + "cursor": "eyJjIjo4LCJ0IjoxLCJpIjpmYWxzZX0", "node": { "digest": "J1pYPDrTgsKgzB8XWtW8jLJ8RPsbJcC1SQ4Mv2T1hAWt" } }, { - "cursor": "eyJjIjo4LCJ0IjoyLCJ0YyI6Mn0", + "cursor": "eyJjIjo4LCJ0IjoyLCJpIjpmYWxzZX0", "node": { "digest": "Cwqr9jTgQjajoYaqcjzAaQGcQEyCg8XxoN7smGCLiBrs" } }, { - "cursor": "eyJjIjo4LCJ0IjozLCJ0YyI6M30", + "cursor": "eyJjIjo4LCJ0IjozLCJpIjpmYWxzZX0", "node": { "digest": "Bym7b7ELP77KxVHtgj6F4FB7H6n5LYQuBQYmdvvFxEmM" } }, { - "cursor": "eyJjIjo4LCJ0Ijo0LCJ0YyI6NH0", + "cursor": "eyJjIjo4LCJ0Ijo0LCJpIjpmYWxzZX0", "node": { "digest": "H1WU8uXMGaENQs54EpoHGpV1iMYdH8P5scd1d16s9ECB" } }, { - "cursor": "eyJjIjo4LCJ0Ijo1LCJ0YyI6NX0", + "cursor": "eyJjIjo4LCJ0Ijo1LCJpIjpmYWxzZX0", "node": { "digest": "4vJbSYKwEJb5sYU2jiayqsZNRnBywD8y6sd3RQoMppF9" } }, { - "cursor": "eyJjIjo4LCJ0Ijo2LCJ0YyI6Nn0", + "cursor": "eyJjIjo4LCJ0Ijo2LCJpIjpmYWxzZX0", "node": { "digest": "4W23PZz7dHVxoZ2VMCWU9j38Jxy7tLkqcFBcJUB3aCSB" } @@ -282,25 +282,25 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjoxMiwidCI6OCwidGMiOjh9", + "cursor": "eyJjIjoxMiwidCI6OCwiaSI6ZmFsc2V9", "node": { "digest": "JLAF7P6DumC8rgzT1Ygp2QgTwpHE2FUqQbVXL6cGEEQ" } }, { - "cursor": "eyJjIjoxMiwidCI6OSwidGMiOjl9", + "cursor": "eyJjIjoxMiwidCI6OSwiaSI6ZmFsc2V9", "node": { "digest": "BVMVdn7DDpTbCjtYwWFekcFA9sNeMgDh1wTNWRrngZxh" } }, { - "cursor": "eyJjIjoxMiwidCI6MTAsInRjIjoxMH0", + "cursor": "eyJjIjoxMiwidCI6MTAsImkiOmZhbHNlfQ", "node": { "digest": "4J5tno4AoU4NPS2NgEseAZK7cpLDh6KJduVtbtwzmHk5" } }, { - "cursor": "eyJjIjoxMiwidCI6MTEsInRjIjoxMX0", + "cursor": "eyJjIjoxMiwidCI6MTEsImkiOmZhbHNlfQ", "node": { "digest": "GngPX2ztACkKE96VUfoujZ3vA11MMDhPSwwgKhK7hVa" } @@ -311,67 +311,67 @@ Response: { "txs_epoch_2": { "edges": [ { - "cursor": "eyJjIjoxMiwidCI6MCwidGMiOjB9", + "cursor": "eyJjIjoxMiwidCI6MCwiaSI6ZmFsc2V9", "node": { "digest": "J7mHXcoa7LXwyjzZUWsk8zvYZjek359TM4d2hQK4LGHo" } }, { - "cursor": "eyJjIjoxMiwidCI6MSwidGMiOjF9", + "cursor": "eyJjIjoxMiwidCI6MSwiaSI6ZmFsc2V9", "node": { "digest": "J1pYPDrTgsKgzB8XWtW8jLJ8RPsbJcC1SQ4Mv2T1hAWt" } }, { - "cursor": "eyJjIjoxMiwidCI6MiwidGMiOjJ9", + "cursor": "eyJjIjoxMiwidCI6MiwiaSI6ZmFsc2V9", "node": { "digest": "Cwqr9jTgQjajoYaqcjzAaQGcQEyCg8XxoN7smGCLiBrs" } }, { - "cursor": "eyJjIjoxMiwidCI6MywidGMiOjN9", + "cursor": "eyJjIjoxMiwidCI6MywiaSI6ZmFsc2V9", "node": { "digest": "Bym7b7ELP77KxVHtgj6F4FB7H6n5LYQuBQYmdvvFxEmM" } }, { - "cursor": "eyJjIjoxMiwidCI6NCwidGMiOjR9", + "cursor": "eyJjIjoxMiwidCI6NCwiaSI6ZmFsc2V9", "node": { "digest": "H1WU8uXMGaENQs54EpoHGpV1iMYdH8P5scd1d16s9ECB" } }, { - "cursor": "eyJjIjoxMiwidCI6NSwidGMiOjV9", + "cursor": "eyJjIjoxMiwidCI6NSwiaSI6ZmFsc2V9", "node": { "digest": "4vJbSYKwEJb5sYU2jiayqsZNRnBywD8y6sd3RQoMppF9" } }, { - "cursor": "eyJjIjoxMiwidCI6NiwidGMiOjZ9", + "cursor": "eyJjIjoxMiwidCI6NiwiaSI6ZmFsc2V9", "node": { "digest": "4W23PZz7dHVxoZ2VMCWU9j38Jxy7tLkqcFBcJUB3aCSB" } }, { - "cursor": "eyJjIjoxMiwidCI6NywidGMiOjd9", + "cursor": "eyJjIjoxMiwidCI6NywiaSI6ZmFsc2V9", "node": { "digest": "D251V1BnvyRKNFZmiFxaf7gSZLGdLo8fYbbVDb5vJWfd" } }, { - "cursor": "eyJjIjoxMiwidCI6OCwidGMiOjh9", + "cursor": "eyJjIjoxMiwidCI6OCwiaSI6ZmFsc2V9", "node": { "digest": "JLAF7P6DumC8rgzT1Ygp2QgTwpHE2FUqQbVXL6cGEEQ" } }, { - "cursor": "eyJjIjoxMiwidCI6OSwidGMiOjl9", + "cursor": "eyJjIjoxMiwidCI6OSwiaSI6ZmFsc2V9", "node": { "digest": "BVMVdn7DDpTbCjtYwWFekcFA9sNeMgDh1wTNWRrngZxh" } }, { - "cursor": "eyJjIjoxMiwidCI6MTAsInRjIjoxMH0", + "cursor": "eyJjIjoxMiwidCI6MTAsImkiOmZhbHNlfQ", "node": { "digest": "4J5tno4AoU4NPS2NgEseAZK7cpLDh6KJduVtbtwzmHk5" } @@ -382,7 +382,7 @@ Response: { } task 23, lines 159-199: -//# run-graphql --cursors {"t":0,"tc":0,"c":7} {"t":4,"tc":4,"c":11} {"t":8,"tc":8,"c":12} +//# run-graphql --cursors {"t":0,"i":false,"c":7} {"t":4,"i":false,"c":11} {"t":8,"i":false,"c":12} Response: { "data": { "checkpoint": { @@ -393,19 +393,19 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjo3LCJ0IjoxLCJ0YyI6MX0", + "cursor": "eyJjIjo3LCJ0IjoxLCJpIjpmYWxzZX0", "node": { "digest": "J1pYPDrTgsKgzB8XWtW8jLJ8RPsbJcC1SQ4Mv2T1hAWt" } }, { - "cursor": "eyJjIjo3LCJ0IjoyLCJ0YyI6Mn0", + "cursor": "eyJjIjo3LCJ0IjoyLCJpIjpmYWxzZX0", "node": { "digest": "Cwqr9jTgQjajoYaqcjzAaQGcQEyCg8XxoN7smGCLiBrs" } }, { - "cursor": "eyJjIjo3LCJ0IjozLCJ0YyI6M30", + "cursor": "eyJjIjo3LCJ0IjozLCJpIjpmYWxzZX0", "node": { "digest": "Bym7b7ELP77KxVHtgj6F4FB7H6n5LYQuBQYmdvvFxEmM" } @@ -418,19 +418,19 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjoxMSwidCI6NSwidGMiOjV9", + "cursor": "eyJjIjoxMSwidCI6NSwiaSI6ZmFsc2V9", "node": { "digest": "4vJbSYKwEJb5sYU2jiayqsZNRnBywD8y6sd3RQoMppF9" } }, { - "cursor": "eyJjIjoxMSwidCI6NiwidGMiOjZ9", + "cursor": "eyJjIjoxMSwidCI6NiwiaSI6ZmFsc2V9", "node": { "digest": "4W23PZz7dHVxoZ2VMCWU9j38Jxy7tLkqcFBcJUB3aCSB" } }, { - "cursor": "eyJjIjoxMSwidCI6NywidGMiOjd9", + "cursor": "eyJjIjoxMSwidCI6NywiaSI6ZmFsc2V9", "node": { "digest": "D251V1BnvyRKNFZmiFxaf7gSZLGdLo8fYbbVDb5vJWfd" } @@ -443,19 +443,19 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjoxMiwidCI6OSwidGMiOjl9", + "cursor": "eyJjIjoxMiwidCI6OSwiaSI6ZmFsc2V9", "node": { "digest": "BVMVdn7DDpTbCjtYwWFekcFA9sNeMgDh1wTNWRrngZxh" } }, { - "cursor": "eyJjIjoxMiwidCI6MTAsInRjIjoxMH0", + "cursor": "eyJjIjoxMiwidCI6MTAsImkiOmZhbHNlfQ", "node": { "digest": "4J5tno4AoU4NPS2NgEseAZK7cpLDh6KJduVtbtwzmHk5" } }, { - "cursor": "eyJjIjoxMiwidCI6MTEsInRjIjoxMX0", + "cursor": "eyJjIjoxMiwidCI6MTEsImkiOmZhbHNlfQ", "node": { "digest": "GngPX2ztACkKE96VUfoujZ3vA11MMDhPSwwgKhK7hVa" } @@ -467,7 +467,7 @@ Response: { } task 24, lines 201-241: -//# run-graphql --cursors {"t":1,"tc":1,"c":2} {"t":5,"tc":5,"c":6} {"t":9,"tc":9,"c":10} +//# run-graphql --cursors {"t":1,"i":false,"c":2} {"t":5,"i":false,"c":6} {"t":9,"i":false,"c":10} Response: { "data": { "checkpoint": { @@ -478,7 +478,7 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjoyLCJ0IjoyLCJ0YyI6Mn0", + "cursor": "eyJjIjoyLCJ0IjoyLCJpIjpmYWxzZX0", "node": { "digest": "Cwqr9jTgQjajoYaqcjzAaQGcQEyCg8XxoN7smGCLiBrs" } @@ -491,7 +491,7 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjo2LCJ0Ijo2LCJ0YyI6Nn0", + "cursor": "eyJjIjo2LCJ0Ijo2LCJpIjpmYWxzZX0", "node": { "digest": "4W23PZz7dHVxoZ2VMCWU9j38Jxy7tLkqcFBcJUB3aCSB" } @@ -504,7 +504,7 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjoxMCwidCI6MTAsInRjIjoxMH0", + "cursor": "eyJjIjoxMCwidCI6MTAsImkiOmZhbHNlfQ", "node": { "digest": "4J5tno4AoU4NPS2NgEseAZK7cpLDh6KJduVtbtwzmHk5" } @@ -516,7 +516,7 @@ Response: { } task 25, lines 243-282: -//# run-graphql --cursors {"t":5,"tc":5,"c":6} +//# run-graphql --cursors {"t":5,"i":false,"c":6} Response: { "data": { "checkpoint": { @@ -525,7 +525,7 @@ Response: { "with_cursor": { "edges": [ { - "cursor": "eyJjIjo2LCJ0Ijo2LCJ0YyI6Nn0", + "cursor": "eyJjIjo2LCJ0Ijo2LCJpIjpmYWxzZX0", "node": { "digest": "4W23PZz7dHVxoZ2VMCWU9j38Jxy7tLkqcFBcJUB3aCSB", "sender": { @@ -556,7 +556,7 @@ Response: { "without_cursor": { "edges": [ { - "cursor": "eyJjIjoxMiwidCI6MiwidGMiOjJ9", + "cursor": "eyJjIjoxMiwidCI6MiwiaSI6ZmFsc2V9", "node": { "digest": "Cwqr9jTgQjajoYaqcjzAaQGcQEyCg8XxoN7smGCLiBrs", "sender": { @@ -592,7 +592,7 @@ Response: { } }, { - "cursor": "eyJjIjoxMiwidCI6NCwidGMiOjR9", + "cursor": "eyJjIjoxMiwidCI6NCwiaSI6ZmFsc2V9", "node": { "digest": "H1WU8uXMGaENQs54EpoHGpV1iMYdH8P5scd1d16s9ECB", "sender": { @@ -628,7 +628,7 @@ Response: { } }, { - "cursor": "eyJjIjoxMiwidCI6NSwidGMiOjV9", + "cursor": "eyJjIjoxMiwidCI6NSwiaSI6ZmFsc2V9", "node": { "digest": "4vJbSYKwEJb5sYU2jiayqsZNRnBywD8y6sd3RQoMppF9", "sender": { @@ -664,7 +664,7 @@ Response: { } }, { - "cursor": "eyJjIjoxMiwidCI6NiwidGMiOjZ9", + "cursor": "eyJjIjoxMiwidCI6NiwiaSI6ZmFsc2V9", "node": { "digest": "4W23PZz7dHVxoZ2VMCWU9j38Jxy7tLkqcFBcJUB3aCSB", "sender": { @@ -700,7 +700,7 @@ Response: { } }, { - "cursor": "eyJjIjoxMiwidCI6OCwidGMiOjh9", + "cursor": "eyJjIjoxMiwidCI6OCwiaSI6ZmFsc2V9", "node": { "digest": "JLAF7P6DumC8rgzT1Ygp2QgTwpHE2FUqQbVXL6cGEEQ", "sender": { @@ -736,7 +736,7 @@ Response: { } }, { - "cursor": "eyJjIjoxMiwidCI6OSwidGMiOjl9", + "cursor": "eyJjIjoxMiwidCI6OSwiaSI6ZmFsc2V9", "node": { "digest": "BVMVdn7DDpTbCjtYwWFekcFA9sNeMgDh1wTNWRrngZxh", "sender": { @@ -772,7 +772,7 @@ Response: { } }, { - "cursor": "eyJjIjoxMiwidCI6MTAsInRjIjoxMH0", + "cursor": "eyJjIjoxMiwidCI6MTAsImkiOmZhbHNlfQ", "node": { "digest": "4J5tno4AoU4NPS2NgEseAZK7cpLDh6KJduVtbtwzmHk5", "sender": { diff --git a/crates/sui-graphql-e2e-tests/tests/consistency/epochs/transaction_blocks.move b/crates/sui-graphql-e2e-tests/tests/consistency/epochs/transaction_blocks.move index 39c8368818df3..425849aef9e16 100644 --- a/crates/sui-graphql-e2e-tests/tests/consistency/epochs/transaction_blocks.move +++ b/crates/sui-graphql-e2e-tests/tests/consistency/epochs/transaction_blocks.move @@ -90,7 +90,7 @@ module Test::M1 { //# advance-epoch -//# run-graphql --cursors {"t":3,"tc":3,"c":4} {"t":7,"tc":7,"c":8} {"t":11,"tc":11,"c":12} +//# run-graphql --cursors {"t":3,"i":false,"c":4} {"t":7,"i":false,"c":8} {"t":11,"i":false,"c":12} # View transactions before the last transaction in each epoch, from the perspective of the first # checkpoint in the next epoch. { @@ -156,7 +156,7 @@ module Test::M1 { } } -//# run-graphql --cursors {"t":0,"tc":0,"c":7} {"t":4,"tc":4,"c":11} {"t":8,"tc":8,"c":12} +//# run-graphql --cursors {"t":0,"i":false,"c":7} {"t":4,"i":false,"c":11} {"t":8,"i":false,"c":12} # View transactions after the first transaction in each epoch, from the perspective of the last # checkpoint in the next epoch. { @@ -198,7 +198,7 @@ module Test::M1 { } } -//# run-graphql --cursors {"t":1,"tc":1,"c":2} {"t":5,"tc":5,"c":6} {"t":9,"tc":9,"c":10} +//# run-graphql --cursors {"t":1,"i":false,"c":2} {"t":5,"i":false,"c":6} {"t":9,"i":false,"c":10} # View transactions after the second transaction in each epoch, from the perspective of a checkpoint # around the middle of each epoch. { @@ -240,7 +240,7 @@ module Test::M1 { } } -//# run-graphql --cursors {"t":5,"tc":5,"c":6} +//# run-graphql --cursors {"t":5,"i":false,"c":6} # Verify that with a cursor, we are locked into a view as if we were at the checkpoint stored in # the cursor. Compare against `without_cursor`, which should show the latest state at the actual # latest checkpoint. There should only be 1 transaction block in the `with_cursor` query, but diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/at_checkpoint.exp b/crates/sui-graphql-e2e-tests/tests/transactions/at_checkpoint.exp new file mode 100644 index 0000000000000..48209abf430e8 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/at_checkpoint.exp @@ -0,0 +1,210 @@ +processed 11 tasks + +init: +A: object(0,0) + +task 2, lines 10-12: +//# programmable --sender A --inputs 1 @A +//> 0: SplitCoins(Gas, [Input(0)]); +//> TransferObjects([Result(0)], Input(1)) +created: object(2,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 1976000, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 3, line 14: +//# create-checkpoint +Checkpoint created: 1 + +task 5, line 18: +//# create-checkpoint +Checkpoint created: 2 + +task 7, line 22: +//# create-checkpoint +Checkpoint created: 3 + +task 8, lines 24-36: +//# run-graphql +Response: { + "data": { + "c0": { + "nodes": [ + { + "digest": "FPhSSzT7tHmrPhs3H9GT1n4Dqj3eyCgaFLkQSc9FEDVV", + "kind": { + "__typename": "GenesisTransaction" + } + } + ] + }, + "c1": { + "nodes": [ + { + "digest": "43wY12GuxKzFAJAAW7oCcYfRGb3BSKXxgrVTtXwuELfn", + "kind": { + "__typename": "ConsensusCommitPrologueTransaction" + } + }, + { + "digest": "Cn6D9eKgVx5EeZddUSpQeTFcVyHKjqmt6yeiroKgr9h6", + "kind": { + "__typename": "ProgrammableTransactionBlock" + } + } + ] + }, + "c2": { + "nodes": [ + { + "digest": "9eMYXfB8mzZhdQgJ6HJTTdcwyXZ3EHVXDpcvERnnBWvR", + "kind": { + "__typename": "ConsensusCommitPrologueTransaction" + } + } + ] + }, + "c3": { + "nodes": [ + { + "digest": "E1TmDoToDfVSW7kMEFiYsNFL2UeCaL1wNbWLdFjxe5mx", + "kind": { + "__typename": "ConsensusCommitPrologueTransaction" + } + } + ] + }, + "c4": { + "nodes": [] + } + } +} + +task 9, lines 38-50: +//# run-graphql +Response: { + "data": { + "c0": { + "transactionBlocks": { + "nodes": [ + { + "digest": "FPhSSzT7tHmrPhs3H9GT1n4Dqj3eyCgaFLkQSc9FEDVV", + "kind": { + "__typename": "GenesisTransaction" + } + } + ] + } + }, + "c1": { + "transactionBlocks": { + "nodes": [ + { + "digest": "43wY12GuxKzFAJAAW7oCcYfRGb3BSKXxgrVTtXwuELfn", + "kind": { + "__typename": "ConsensusCommitPrologueTransaction" + } + }, + { + "digest": "Cn6D9eKgVx5EeZddUSpQeTFcVyHKjqmt6yeiroKgr9h6", + "kind": { + "__typename": "ProgrammableTransactionBlock" + } + } + ] + } + }, + "c2": { + "transactionBlocks": { + "nodes": [ + { + "digest": "9eMYXfB8mzZhdQgJ6HJTTdcwyXZ3EHVXDpcvERnnBWvR", + "kind": { + "__typename": "ConsensusCommitPrologueTransaction" + } + } + ] + } + }, + "c3": { + "transactionBlocks": { + "nodes": [ + { + "digest": "E1TmDoToDfVSW7kMEFiYsNFL2UeCaL1wNbWLdFjxe5mx", + "kind": { + "__typename": "ConsensusCommitPrologueTransaction" + } + } + ] + } + }, + "c4": null + } +} + +task 10, lines 52-63: +//# run-graphql +Response: { + "data": { + "checkpoints": { + "pageInfo": { + "hasNextPage": false + }, + "nodes": [ + { + "transactionBlocks": { + "nodes": [ + { + "digest": "FPhSSzT7tHmrPhs3H9GT1n4Dqj3eyCgaFLkQSc9FEDVV", + "kind": { + "__typename": "GenesisTransaction" + } + } + ] + } + }, + { + "transactionBlocks": { + "nodes": [ + { + "digest": "43wY12GuxKzFAJAAW7oCcYfRGb3BSKXxgrVTtXwuELfn", + "kind": { + "__typename": "ConsensusCommitPrologueTransaction" + } + }, + { + "digest": "Cn6D9eKgVx5EeZddUSpQeTFcVyHKjqmt6yeiroKgr9h6", + "kind": { + "__typename": "ProgrammableTransactionBlock" + } + } + ] + } + }, + { + "transactionBlocks": { + "nodes": [ + { + "digest": "9eMYXfB8mzZhdQgJ6HJTTdcwyXZ3EHVXDpcvERnnBWvR", + "kind": { + "__typename": "ConsensusCommitPrologueTransaction" + } + } + ] + } + }, + { + "transactionBlocks": { + "nodes": [ + { + "digest": "E1TmDoToDfVSW7kMEFiYsNFL2UeCaL1wNbWLdFjxe5mx", + "kind": { + "__typename": "ConsensusCommitPrologueTransaction" + } + } + ] + } + } + ] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/at_checkpoint.move b/crates/sui-graphql-e2e-tests/tests/transactions/at_checkpoint.move new file mode 100644 index 0000000000000..5d5e27dbed2e8 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/at_checkpoint.move @@ -0,0 +1,63 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//# init --protocol-version 51 --accounts A --simulator + +// Limiting transactions by the checkpoint they are in + +//# advance-clock --duration-ns 1 + +//# programmable --sender A --inputs 1 @A +//> 0: SplitCoins(Gas, [Input(0)]); +//> TransferObjects([Result(0)], Input(1)) + +//# create-checkpoint + +//# advance-clock --duration-ns 1 + +//# create-checkpoint + +//# advance-clock --duration-ns 1 + +//# create-checkpoint + +//# run-graphql +{ # Top-level query, with a filter + c0: transactionBlocks(filter: { atCheckpoint: 0 }) { nodes { ...Tx } } + c1: transactionBlocks(filter: { atCheckpoint: 1 }) { nodes { ...Tx } } + c2: transactionBlocks(filter: { atCheckpoint: 2 }) { nodes { ...Tx } } + c3: transactionBlocks(filter: { atCheckpoint: 3 }) { nodes { ...Tx } } + c4: transactionBlocks(filter: { atCheckpoint: 4 }) { nodes { ...Tx } } +} + +fragment Tx on TransactionBlock { + digest + kind { __typename } +} + +//# run-graphql +{ # Via a checkpoint query + c0: checkpoint(id: { sequenceNumber: 0 }) { transactionBlocks { nodes { ...Tx } } } + c1: checkpoint(id: { sequenceNumber: 1 }) { transactionBlocks { nodes { ...Tx } } } + c2: checkpoint(id: { sequenceNumber: 2 }) { transactionBlocks { nodes { ...Tx } } } + c3: checkpoint(id: { sequenceNumber: 3 }) { transactionBlocks { nodes { ...Tx } } } + c4: checkpoint(id: { sequenceNumber: 4 }) { transactionBlocks { nodes { ...Tx } } } +} + +fragment Tx on TransactionBlock { + digest + kind { __typename } +} + +//# run-graphql +{ # Via paginating checkpoints + checkpoints(first: 5) { + pageInfo { hasNextPage } + nodes { transactionBlocks { nodes { ...Tx } } } + } +} + +fragment Tx on TransactionBlock { + digest + kind { __typename } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/filters/kind.exp b/crates/sui-graphql-e2e-tests/tests/transactions/filters/kind.exp new file mode 100644 index 0000000000000..5f9783d3a76fe --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/filters/kind.exp @@ -0,0 +1,246 @@ +processed 16 tasks + +init: +A: object(0,0), B: object(0,1), C: object(0,2), D: object(0,3), E: object(0,4) + +task 1, lines 6-19: +//# publish +created: object(1,0) +mutated: object(0,5) +gas summary: computation_cost: 1000000, storage_cost: 5175600, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 21: +//# create-checkpoint +Checkpoint created: 1 + +task 3, line 23: +//# run Test::M1::create --args 0 @A --sender A +created: object(3,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 4, line 25: +//# run Test::M1::create --args 1 @A --sender B +created: object(4,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 5, line 27: +//# run Test::M1::create --args 2 @A --sender C +created: object(5,0) +mutated: object(0,2) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 6, line 29: +//# run Test::M1::create --args 3 @A --sender D +created: object(6,0) +mutated: object(0,3) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 7, line 31: +//# run Test::M1::create --args 4 @A --sender E +created: object(7,0) +mutated: object(0,4) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 8, line 33: +//# create-checkpoint +Checkpoint created: 2 + +task 9, lines 35-53: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "endCursor": "eyJjIjoyLCJ0Ijo2LCJpIjpmYWxzZX0", + "startCursor": "eyJjIjoyLCJ0IjoyLCJpIjpmYWxzZX0" + }, + "nodes": [ + { + "digest": "78YAzuJPHbHXsqqj2GjiBibpAiWim7sha6MseSCN2Y6g", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "9zVSnZuHjSQZKcbrwmpkUfsRTA4J9VKqSiqzNCmycPex", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "BmipCooPMB1CHeRuFHS15q4VQ14pSLoYvuuNEfNsvZBc", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "W3YEPxp4z4LzuwoGq4kmCBiy12xv4cNuEvwusrsxDem", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "HCNwnSLqsQYEju3KoQbxpa3SD5mVG6FLcggkd2ZYxHvB", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + ] + } + } +} + +task 10, lines 55-73: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "endCursor": "eyJjIjoyLCJ0IjoyLCJpIjpmYWxzZX0", + "startCursor": "eyJjIjoyLCJ0IjoyLCJpIjpmYWxzZX0" + }, + "nodes": [ + { + "digest": "78YAzuJPHbHXsqqj2GjiBibpAiWim7sha6MseSCN2Y6g", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + ] + } + } +} + +task 11, lines 75-93: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "endCursor": "eyJjIjoyLCJ0IjozLCJpIjpmYWxzZX0", + "startCursor": "eyJjIjoyLCJ0IjozLCJpIjpmYWxzZX0" + }, + "nodes": [ + { + "digest": "9zVSnZuHjSQZKcbrwmpkUfsRTA4J9VKqSiqzNCmycPex", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + ] + } + } +} + +task 12, lines 95-113: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "endCursor": "eyJjIjoyLCJ0Ijo0LCJpIjpmYWxzZX0", + "startCursor": "eyJjIjoyLCJ0Ijo0LCJpIjpmYWxzZX0" + }, + "nodes": [ + { + "digest": "BmipCooPMB1CHeRuFHS15q4VQ14pSLoYvuuNEfNsvZBc", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + ] + } + } +} + +task 13, lines 115-133: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "endCursor": "eyJjIjoyLCJ0Ijo1LCJpIjpmYWxzZX0", + "startCursor": "eyJjIjoyLCJ0Ijo1LCJpIjpmYWxzZX0" + }, + "nodes": [ + { + "digest": "W3YEPxp4z4LzuwoGq4kmCBiy12xv4cNuEvwusrsxDem", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + ] + } + } +} + +task 14, lines 135-153: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "endCursor": "eyJjIjoyLCJ0Ijo2LCJpIjpmYWxzZX0", + "startCursor": "eyJjIjoyLCJ0Ijo2LCJpIjpmYWxzZX0" + }, + "nodes": [ + { + "digest": "HCNwnSLqsQYEju3KoQbxpa3SD5mVG6FLcggkd2ZYxHvB", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + ] + } + } +} + +task 15, lines 155-173: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "endCursor": null, + "startCursor": null + }, + "nodes": [] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/filters/kind.move b/crates/sui-graphql-e2e-tests/tests/transactions/filters/kind.move new file mode 100644 index 0000000000000..485cb837f2023 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/filters/kind.move @@ -0,0 +1,173 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//# init --protocol-version 48 --addresses Test=0x0 --accounts A B C D E --simulator + +//# publish +module Test::M1 { + public struct Object has key, store { + id: UID, + value: u64, + } + + public entry fun create(value: u64, recipient: address, ctx: &mut TxContext) { + transfer::public_transfer( + Object { id: object::new(ctx), value }, + recipient + ) + } +} + +//# create-checkpoint + +//# run Test::M1::create --args 0 @A --sender A + +//# run Test::M1::create --args 1 @A --sender B + +//# run Test::M1::create --args 2 @A --sender C + +//# run Test::M1::create --args 3 @A --sender D + +//# run Test::M1::create --args 4 @A --sender E + +//# create-checkpoint + +//# run-graphql +{ + transactionBlocks(first: 50 filter: {kind: PROGRAMMABLE_TX atCheckpoint: 2}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +{ + transactionBlocks(first: 50 filter: {kind: PROGRAMMABLE_TX atCheckpoint: 2 signAddress: "@{A}"}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +{ + transactionBlocks(first: 50 filter: {kind: PROGRAMMABLE_TX atCheckpoint: 2 signAddress: "@{B}"}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +{ + transactionBlocks(first: 50 filter: {kind: PROGRAMMABLE_TX atCheckpoint: 2 signAddress: "@{C}"}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +{ + transactionBlocks(first: 50 filter: {kind: PROGRAMMABLE_TX atCheckpoint: 2 signAddress: "@{D}"}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +{ + transactionBlocks(first: 50 filter: {kind: PROGRAMMABLE_TX atCheckpoint: 2 signAddress: "@{E}"}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +{ + transactionBlocks(first: 50 filter: {kind: SYSTEM_TX atCheckpoint: 2}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/filters/transaction_ids.exp b/crates/sui-graphql-e2e-tests/tests/transactions/filters/transaction_ids.exp new file mode 100644 index 0000000000000..68ccdaff1ab7f --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/filters/transaction_ids.exp @@ -0,0 +1,95 @@ +processed 9 tasks + +init: +A: object(0,0) + +task 1, lines 6-19: +//# publish +created: object(1,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 5175600, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 21: +//# create-checkpoint +Checkpoint created: 1 + +task 3, line 23: +//# run Test::M1::create --args 0 @A --sender A +created: object(3,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 4, line 25: +//# create-checkpoint +Checkpoint created: 2 + +task 5, lines 27-45: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "endCursor": null, + "startCursor": null + }, + "nodes": [] + } + } +} + +task 6, lines 47-65: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "endCursor": null, + "startCursor": null + }, + "nodes": [] + } + } +} + +task 7, lines 67-85: +//# run-graphql +Response: { + "data": null, + "errors": [ + { + "message": "A scan limit must be specified for the given filter combination", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "transactionBlocks" + ], + "extensions": { + "code": "BAD_USER_INPUT" + } + } + ] +} + +task 8, lines 87-105: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "endCursor": "eyJjIjoyLCJ0IjoyLCJpIjp0cnVlfQ", + "startCursor": "eyJjIjoyLCJ0IjowLCJpIjp0cnVlfQ" + }, + "nodes": [] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/filters/transaction_ids.move b/crates/sui-graphql-e2e-tests/tests/transactions/filters/transaction_ids.move new file mode 100644 index 0000000000000..ba15bcdc5ce9b --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/filters/transaction_ids.move @@ -0,0 +1,105 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//# init --protocol-version 48 --addresses Test=0x0 --accounts A --simulator + +//# publish +module Test::M1 { + public struct Object has key, store { + id: UID, + value: u64, + } + + public entry fun create(value: u64, recipient: address, ctx: &mut TxContext) { + transfer::public_transfer( + Object { id: object::new(ctx), value }, + recipient + ) + } +} + +//# create-checkpoint + +//# run Test::M1::create --args 0 @A --sender A + +//# create-checkpoint + +//# run-graphql +{ + transactionBlocks(filter: {transactionIds: []}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +{ + transactionBlocks(filter: {signAddress: "@{A}" transactionIds: []}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +{ + transactionBlocks(filter: {recvAddress: "@{A}" transactionIds: []}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +{ + transactionBlocks(scanLimit: 10 filter: {recvAddress: "@{A}" transactionIds: []}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/programmable.exp b/crates/sui-graphql-e2e-tests/tests/transactions/programmable.exp index d1a45fd72cb9b..96578345e23ca 100644 --- a/crates/sui-graphql-e2e-tests/tests/transactions/programmable.exp +++ b/crates/sui-graphql-e2e-tests/tests/transactions/programmable.exp @@ -1008,7 +1008,7 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjo0LCJ0IjowLCJ0YyI6MH0", + "cursor": "eyJjIjo0LCJ0IjowLCJpIjpmYWxzZX0", "node": { "kind": { "__typename": "GenesisTransaction" @@ -1016,7 +1016,7 @@ Response: { } }, { - "cursor": "eyJjIjo0LCJ0IjoxLCJ0YyI6MX0", + "cursor": "eyJjIjo0LCJ0IjoxLCJpIjpmYWxzZX0", "node": { "kind": { "__typename": "ProgrammableTransactionBlock" @@ -1024,7 +1024,7 @@ Response: { } }, { - "cursor": "eyJjIjo0LCJ0IjoyLCJ0YyI6Mn0", + "cursor": "eyJjIjo0LCJ0IjoyLCJpIjpmYWxzZX0", "node": { "kind": { "__typename": "ProgrammableTransactionBlock" @@ -1032,7 +1032,7 @@ Response: { } }, { - "cursor": "eyJjIjo0LCJ0IjozLCJ0YyI6M30", + "cursor": "eyJjIjo0LCJ0IjozLCJpIjpmYWxzZX0", "node": { "kind": { "__typename": "ProgrammableTransactionBlock" @@ -1040,7 +1040,7 @@ Response: { } }, { - "cursor": "eyJjIjo0LCJ0Ijo0LCJ0YyI6M30", + "cursor": "eyJjIjo0LCJ0Ijo0LCJpIjpmYWxzZX0", "node": { "kind": { "__typename": "ProgrammableTransactionBlock" @@ -1048,7 +1048,7 @@ Response: { } }, { - "cursor": "eyJjIjo0LCJ0Ijo1LCJ0YyI6NH0", + "cursor": "eyJjIjo0LCJ0Ijo1LCJpIjpmYWxzZX0", "node": { "kind": { "__typename": "ProgrammableTransactionBlock" @@ -1067,7 +1067,7 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjo0LCJ0IjowLCJ0YyI6MH0", + "cursor": "eyJjIjo0LCJ0IjowLCJpIjpmYWxzZX0", "node": { "kind": { "__typename": "GenesisTransaction" @@ -1086,7 +1086,7 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjo0LCJ0IjoxLCJ0YyI6MX0", + "cursor": "eyJjIjo0LCJ0IjoxLCJpIjpmYWxzZX0", "node": { "kind": { "__typename": "ProgrammableTransactionBlock" @@ -1094,7 +1094,7 @@ Response: { } }, { - "cursor": "eyJjIjo0LCJ0IjoyLCJ0YyI6Mn0", + "cursor": "eyJjIjo0LCJ0IjoyLCJpIjpmYWxzZX0", "node": { "kind": { "__typename": "ProgrammableTransactionBlock" @@ -1102,7 +1102,7 @@ Response: { } }, { - "cursor": "eyJjIjo0LCJ0IjozLCJ0YyI6M30", + "cursor": "eyJjIjo0LCJ0IjozLCJpIjpmYWxzZX0", "node": { "kind": { "__typename": "ProgrammableTransactionBlock" @@ -1110,7 +1110,7 @@ Response: { } }, { - "cursor": "eyJjIjo0LCJ0Ijo0LCJ0YyI6M30", + "cursor": "eyJjIjo0LCJ0Ijo0LCJpIjpmYWxzZX0", "node": { "kind": { "__typename": "ProgrammableTransactionBlock" @@ -1118,7 +1118,7 @@ Response: { } }, { - "cursor": "eyJjIjo0LCJ0Ijo1LCJ0YyI6NH0", + "cursor": "eyJjIjo0LCJ0Ijo1LCJpIjpmYWxzZX0", "node": { "kind": { "__typename": "ProgrammableTransactionBlock" @@ -1149,10 +1149,10 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjo0LCJ0IjoyLCJ0YyI6Mn0" + "cursor": "eyJjIjo0LCJ0IjoyLCJpIjpmYWxzZX0" }, { - "cursor": "eyJjIjo0LCJ0IjozLCJ0YyI6M30" + "cursor": "eyJjIjo0LCJ0IjozLCJpIjpmYWxzZX0" } ] } @@ -1166,10 +1166,10 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjo0LCJ0IjozLCJ0YyI6M30" + "cursor": "eyJjIjo0LCJ0IjozLCJpIjpmYWxzZX0" }, { - "cursor": "eyJjIjo0LCJ0Ijo0LCJ0YyI6M30" + "cursor": "eyJjIjo0LCJ0Ijo0LCJpIjpmYWxzZX0" } ] } @@ -1183,7 +1183,7 @@ Response: { "transactionBlocks": { "edges": [ { - "cursor": "eyJjIjo0LCJ0IjozLCJ0YyI6M30" + "cursor": "eyJjIjo0LCJ0IjozLCJpIjpmYWxzZX0" } ] } diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/alternating.exp b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/alternating.exp new file mode 100644 index 0000000000000..3ae2d4b5218c5 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/alternating.exp @@ -0,0 +1,300 @@ +processed 22 tasks + +init: +A: object(0,0), B: object(0,1) + +task 1, lines 8-29: +//# publish +created: object(1,0) +mutated: object(0,2) +gas summary: computation_cost: 1000000, storage_cost: 5798800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 31: +//# create-checkpoint +Checkpoint created: 1 + +task 3, line 33: +//# run Test::M1::create --args 0 @A --sender A +created: object(3,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 4, line 35: +//# run Test::M1::create --args 1 @B --sender B +created: object(4,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 5, line 37: +//# run Test::M1::create --args 2 @A --sender A +created: object(5,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 6, line 39: +//# run Test::M1::create --args 3 @B --sender B +created: object(6,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 7, line 41: +//# run Test::M1::create --args 4 @A --sender A +created: object(7,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 8, line 43: +//# create-checkpoint +Checkpoint created: 2 + +task 9, line 45: +//# run Test::M1::create --args 100 @B --sender B +created: object(9,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 10, line 47: +//# run Test::M1::create --args 101 @A --sender A +created: object(10,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 11, line 49: +//# run Test::M1::create --args 102 @B --sender B +created: object(11,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 12, line 51: +//# run Test::M1::create --args 103 @A --sender A +created: object(12,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 13, line 53: +//# run Test::M1::create --args 104 @B --sender B +created: object(13,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 14, line 55: +//# create-checkpoint +Checkpoint created: 3 + +task 15, lines 57-78: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": true, + "startCursor": "eyJjIjozLCJ0IjoyLCJpIjp0cnVlfQ", + "endCursor": "eyJjIjozLCJ0IjozLCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjozLCJ0IjoyLCJpIjpmYWxzZX0", + "node": { + "digest": "CReUjLynvpq4dD4w6zekGxvSyBBQF2e3KG3K2Rs7oD8L", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 16, lines 80-104: +//# run-graphql --cursors {"c":3,"t":3,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": true, + "startCursor": "eyJjIjozLCJ0Ijo0LCJpIjpmYWxzZX0", + "endCursor": "eyJjIjozLCJ0Ijo2LCJpIjpmYWxzZX0" + }, + "edges": [ + { + "cursor": "eyJjIjozLCJ0Ijo0LCJpIjpmYWxzZX0", + "node": { + "digest": "Hgu3LePqrpyR8Vq3Ve4L2KmvErcdcz92u7YiiotkKJ1N", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo2LCJpIjpmYWxzZX0", + "node": { + "digest": "2EwyAHiMofhbM5z5ty7XT1QXs4sNZfHmZLX513Ag8sD3", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 17, lines 106-129: +//# run-graphql --cursors {"c":3,"t":3,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjozLCJ0Ijo0LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjozLCJ0Ijo1LCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjozLCJ0Ijo0LCJpIjpmYWxzZX0", + "node": { + "digest": "Hgu3LePqrpyR8Vq3Ve4L2KmvErcdcz92u7YiiotkKJ1N", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 18, lines 131-155: +//# run-graphql --cursors {"c":3,"t":6,"i":false} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjozLCJ0Ijo3LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjozLCJ0Ijo4LCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjozLCJ0Ijo4LCJpIjpmYWxzZX0", + "node": { + "digest": "4LUhoFJMmZfG71RHiRkwa9KHovrDv3S3mqUM1vu9JWKJ", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} + +task 19, lines 157-184: +//# run-graphql --cursors {"c":3,"t":5,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjozLCJ0Ijo2LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjozLCJ0Ijo4LCJpIjpmYWxzZX0" + }, + "edges": [ + { + "cursor": "eyJjIjozLCJ0Ijo2LCJpIjpmYWxzZX0", + "node": { + "digest": "2EwyAHiMofhbM5z5ty7XT1QXs4sNZfHmZLX513Ag8sD3", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo4LCJpIjpmYWxzZX0", + "node": { + "digest": "4LUhoFJMmZfG71RHiRkwa9KHovrDv3S3mqUM1vu9JWKJ", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} + +task 20, lines 186-209: +//# run-graphql --cursors {"c":3,"t":8,"i":false} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": false, + "startCursor": "eyJjIjozLCJ0IjoxMCwiaSI6ZmFsc2V9", + "endCursor": "eyJjIjozLCJ0IjoxMCwiaSI6ZmFsc2V9" + }, + "edges": [ + { + "cursor": "eyJjIjozLCJ0IjoxMCwiaSI6ZmFsc2V9", + "node": { + "digest": "AnqDERsdbEiE26CACJa6KtJTLsggisgu7yxhMJ6mU1JZ", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} + +task 21, lines 211-235: +//# run-graphql --cursors {"c":3,"t":8,"i":false} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": false, + "startCursor": "eyJjIjozLCJ0Ijo5LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjozLCJ0IjoxMSwiaSI6dHJ1ZX0" + }, + "edges": [ + { + "cursor": "eyJjIjozLCJ0IjoxMCwiaSI6ZmFsc2V9", + "node": { + "digest": "AnqDERsdbEiE26CACJa6KtJTLsggisgu7yxhMJ6mU1JZ", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/alternating.move b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/alternating.move new file mode 100644 index 0000000000000..a5f339d31d94e --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/alternating.move @@ -0,0 +1,235 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Testing behavior of alternating between a scan-limited and normal query + +//# init --protocol-version 48 --addresses Test=0x0 --accounts A B --simulator + +//# publish +module Test::M1 { + public struct Object has key, store { + id: UID, + value: u64, + } + + public entry fun create(value: u64, recipient: address, ctx: &mut TxContext) { + transfer::public_transfer( + Object { id: object::new(ctx), value }, + recipient + ) + } + + public fun swap_value_and_send(mut lhs: Object, mut rhs: Object, recipient: address) { + let tmp = lhs.value; + lhs.value = rhs.value; + rhs.value = tmp; + transfer::public_transfer(lhs, recipient); + transfer::public_transfer(rhs, recipient); + } +} + +//# create-checkpoint + +//# run Test::M1::create --args 0 @A --sender A + +//# run Test::M1::create --args 1 @B --sender B + +//# run Test::M1::create --args 2 @A --sender A + +//# run Test::M1::create --args 3 @B --sender B + +//# run Test::M1::create --args 4 @A --sender A + +//# create-checkpoint + +//# run Test::M1::create --args 100 @B --sender B + +//# run Test::M1::create --args 101 @A --sender A + +//# run Test::M1::create --args 102 @B --sender B + +//# run Test::M1::create --args 103 @A --sender A + +//# run Test::M1::create --args 104 @B --sender B + +//# create-checkpoint + +//# run-graphql +{ + transactionBlocks(first: 2 scanLimit: 2 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":3,"t":3,"i":true} +# This should return the next two matching transactions after 3, +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":3,"t":3,"i":true} +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":3,"t":6,"i":false} +# From a previous query that was not scan limited, paginate with scan limit +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":3,"t":5,"i":true} +# from tx 5, select the next two transactions that match +# setting the scanLimit to impose all of the remaining txs +# even though we've finished scanning +# we should indicate there is a next page so we don't skip any txs +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":3,"t":8,"i":false} +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":3,"t":8,"i":false} +# fetch the last tx with scan limit +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/both_cursors.exp b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/both_cursors.exp new file mode 100644 index 0000000000000..2fee54d94c6f6 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/both_cursors.exp @@ -0,0 +1,177 @@ +processed 18 tasks + +init: +A: object(0,0), B: object(0,1) + +task 1, lines 9-30: +//# publish +created: object(1,0) +mutated: object(0,2) +gas summary: computation_cost: 1000000, storage_cost: 5798800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 32: +//# create-checkpoint +Checkpoint created: 1 + +task 3, line 34: +//# run Test::M1::create --args 0 @B --sender A +created: object(3,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 4, line 36: +//# run Test::M1::create --args 1 @A --sender A +created: object(4,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 5, line 38: +//# run Test::M1::create --args 2 @B --sender A +created: object(5,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 6, line 40: +//# run Test::M1::create --args 3 @A --sender A +created: object(6,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 7, line 42: +//# run Test::M1::create --args 4 @B --sender A +created: object(7,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 8, line 44: +//# create-checkpoint +Checkpoint created: 2 + +task 9, line 46: +//# run Test::M1::create --args 100 @A --sender A +created: object(9,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 10, line 48: +//# run Test::M1::create --args 101 @A --sender A +created: object(10,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 11, line 50: +//# run Test::M1::create --args 102 @A --sender A +created: object(11,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 12, line 52: +//# run Test::M1::create --args 103 @B --sender A +created: object(12,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 13, line 54: +//# run Test::M1::create --args 104 @B --sender A +created: object(13,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 14, line 56: +//# create-checkpoint +Checkpoint created: 3 + +task 15, lines 58-81: +//# run-graphql --cursors {"c":4,"t":2,"i":true} {"c":4,"t":7,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0IjozLCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0Ijo0LCJpIjpmYWxzZX0" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0Ijo0LCJpIjpmYWxzZX0", + "node": { + "digest": "6RKZYt946ztfY8ZVspCv8faXBzKxDcTUEHnrCyBSZ4Li", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 16, lines 83-108: +//# run-graphql --cursors {"c":4,"t":2,"i":true} {"c":4,"t":7,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0IjozLCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0Ijo2LCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0Ijo0LCJpIjpmYWxzZX0", + "node": { + "digest": "6RKZYt946ztfY8ZVspCv8faXBzKxDcTUEHnrCyBSZ4Li", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjo0LCJ0Ijo2LCJpIjpmYWxzZX0", + "node": { + "digest": "83AZLnLVtQeUdrXGg3igLkeo94j3wTLuwY4izobLLVBT", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 17, lines 110-133: +//# run-graphql --cursors {"c":4,"t":4,"i":true} {"c":4,"t":8,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0Ijo1LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0Ijo1LCJpIjpmYWxzZX0" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0Ijo1LCJpIjpmYWxzZX0", + "node": { + "digest": "74pdWZw8nEhvtan9aoYHAeZxGouHsj9cwBr5GcgncNAz", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/both_cursors.move b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/both_cursors.move new file mode 100644 index 0000000000000..ba913319b65ad --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/both_cursors.move @@ -0,0 +1,133 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Tests paginating forwards where first and scanLimit are equal. The 1st, 3rd, 5th, and 7th through +// 10th transactions will match the filtering criteria. + +//# init --protocol-version 48 --addresses Test=0x0 --accounts A B --simulator + +//# publish +module Test::M1 { + public struct Object has key, store { + id: UID, + value: u64, + } + + public entry fun create(value: u64, recipient: address, ctx: &mut TxContext) { + transfer::public_transfer( + Object { id: object::new(ctx), value }, + recipient + ) + } + + public fun swap_value_and_send(mut lhs: Object, mut rhs: Object, recipient: address) { + let tmp = lhs.value; + lhs.value = rhs.value; + rhs.value = tmp; + transfer::public_transfer(lhs, recipient); + transfer::public_transfer(rhs, recipient); + } +} + +//# create-checkpoint + +//# run Test::M1::create --args 0 @B --sender A + +//# run Test::M1::create --args 1 @A --sender A + +//# run Test::M1::create --args 2 @B --sender A + +//# run Test::M1::create --args 3 @A --sender A + +//# run Test::M1::create --args 4 @B --sender A + +//# create-checkpoint + +//# run Test::M1::create --args 100 @A --sender A + +//# run Test::M1::create --args 101 @A --sender A + +//# run Test::M1::create --args 102 @A --sender A + +//# run Test::M1::create --args 103 @B --sender A + +//# run Test::M1::create --args 104 @B --sender A + +//# create-checkpoint + +//# run-graphql --cursors {"c":4,"t":2,"i":true} {"c":4,"t":7,"i":true} +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":4,"t":2,"i":true} {"c":4,"t":7,"i":true} +# startCursor is at 3 + scanLimited, endCursor at 6 + scanLimited +# we return txs 4 and 6, paginate_results thinks we do not have a next page, +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":4,"t":4,"i":true} {"c":4,"t":8,"i":true} +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/equal/first.exp b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/equal/first.exp new file mode 100644 index 0000000000000..b02eb2652a412 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/equal/first.exp @@ -0,0 +1,358 @@ +processed 25 tasks + +init: +A: object(0,0), B: object(0,1) + +task 1, lines 9-30: +//# publish +created: object(1,0) +mutated: object(0,2) +gas summary: computation_cost: 1000000, storage_cost: 5798800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 32: +//# create-checkpoint +Checkpoint created: 1 + +task 3, line 34: +//# run Test::M1::create --args 0 @B --sender A +created: object(3,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 4, line 36: +//# run Test::M1::create --args 1 @A --sender A +created: object(4,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 5, line 38: +//# run Test::M1::create --args 2 @B --sender A +created: object(5,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 6, line 40: +//# run Test::M1::create --args 3 @A --sender A +created: object(6,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 7, line 42: +//# run Test::M1::create --args 4 @B --sender A +created: object(7,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 8, line 44: +//# create-checkpoint +Checkpoint created: 2 + +task 9, line 46: +//# run Test::M1::create --args 100 @A --sender A +created: object(9,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 10, line 48: +//# run Test::M1::create --args 101 @A --sender A +created: object(10,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 11, line 50: +//# run Test::M1::create --args 102 @A --sender A +created: object(11,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 12, line 52: +//# run Test::M1::create --args 103 @B --sender A +created: object(12,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 13, line 54: +//# run Test::M1::create --args 104 @B --sender A +created: object(13,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 14, line 56: +//# create-checkpoint +Checkpoint created: 3 + +task 15, lines 58-82: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false, + "startCursor": "eyJjIjozLCJ0IjoyLCJpIjpmYWxzZX0", + "endCursor": "eyJjIjozLCJ0IjoxMSwiaSI6ZmFsc2V9" + }, + "edges": [ + { + "cursor": "eyJjIjozLCJ0IjoyLCJpIjpmYWxzZX0", + "node": { + "digest": "HzyC8gcn4m1ymKxYSpWMaNnmbrqm4hX7UBteJ4me3LFd", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo0LCJpIjpmYWxzZX0", + "node": { + "digest": "6RKZYt946ztfY8ZVspCv8faXBzKxDcTUEHnrCyBSZ4Li", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo2LCJpIjpmYWxzZX0", + "node": { + "digest": "83AZLnLVtQeUdrXGg3igLkeo94j3wTLuwY4izobLLVBT", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0IjoxMCwiaSI6ZmFsc2V9", + "node": { + "digest": "AWWgnnumcijVEY2YUzs4MtqzFPeLKFAbHnnjXwUyn6Gj", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0IjoxMSwiaSI6ZmFsc2V9", + "node": { + "digest": "DVVVd1cLYDpV3KHhXpirV5NFpk3DKaCuKvXXFeG2owA7", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} + +task 16, lines 85-111: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": true, + "startCursor": "eyJjIjozLCJ0IjoyLCJpIjp0cnVlfQ", + "endCursor": "eyJjIjozLCJ0IjozLCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjozLCJ0IjoyLCJpIjpmYWxzZX0", + "node": { + "digest": "HzyC8gcn4m1ymKxYSpWMaNnmbrqm4hX7UBteJ4me3LFd", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 17, lines 113-137: +//# run-graphql --cursors {"c":4,"t":3,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0Ijo0LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0Ijo0LCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0Ijo0LCJpIjpmYWxzZX0", + "node": { + "digest": "6RKZYt946ztfY8ZVspCv8faXBzKxDcTUEHnrCyBSZ4Li", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 18, lines 139-165: +//# run-graphql --cursors {"c":4,"t":4,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0Ijo1LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0Ijo3LCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0Ijo2LCJpIjpmYWxzZX0", + "node": { + "digest": "83AZLnLVtQeUdrXGg3igLkeo94j3wTLuwY4izobLLVBT", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 19, lines 167-193: +//# run-graphql --cursors {"c":4,"t":7,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0Ijo4LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0Ijo5LCJpIjp0cnVlfQ" + }, + "edges": [] + } + } +} + +task 20, lines 195-220: +//# run-graphql --cursors {"c":4,"t":9,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": false, + "startCursor": "eyJjIjo0LCJ0IjoxMCwiaSI6dHJ1ZX0", + "endCursor": "eyJjIjo0LCJ0IjoxMSwiaSI6dHJ1ZX0" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0IjoxMCwiaSI6ZmFsc2V9", + "node": { + "digest": "AWWgnnumcijVEY2YUzs4MtqzFPeLKFAbHnnjXwUyn6Gj", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + }, + { + "cursor": "eyJjIjo0LCJ0IjoxMSwiaSI6ZmFsc2V9", + "node": { + "digest": "DVVVd1cLYDpV3KHhXpirV5NFpk3DKaCuKvXXFeG2owA7", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} + +task 21, line 222: +//# run Test::M1::create --args 105 @A --sender A +created: object(21,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 22, line 224: +//# create-checkpoint +Checkpoint created: 4 + +task 23, lines 226-252: +//# run-graphql --cursors {"c":4,"t":11,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": false, + "startCursor": "eyJjIjo0LCJ0IjoxMiwiaSI6dHJ1ZX0", + "endCursor": "eyJjIjo0LCJ0IjoxMiwiaSI6dHJ1ZX0" + }, + "edges": [] + } + } +} + +task 24, lines 254-281: +//# run-graphql --cursors {"c":4,"t":12,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0IjoxMCwiaSI6dHJ1ZX0", + "endCursor": "eyJjIjo0LCJ0IjoxMSwiaSI6dHJ1ZX0" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0IjoxMCwiaSI6ZmFsc2V9", + "node": { + "digest": "AWWgnnumcijVEY2YUzs4MtqzFPeLKFAbHnnjXwUyn6Gj", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + }, + { + "cursor": "eyJjIjo0LCJ0IjoxMSwiaSI6ZmFsc2V9", + "node": { + "digest": "DVVVd1cLYDpV3KHhXpirV5NFpk3DKaCuKvXXFeG2owA7", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/equal/first.move b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/equal/first.move new file mode 100644 index 0000000000000..a3a3705413cba --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/equal/first.move @@ -0,0 +1,281 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Tests paginating forwards where first and scanLimit are equal. The 1st, 3rd, 5th, and 7th through +// 10th transactions will match the filtering criteria. + +//# init --protocol-version 48 --addresses Test=0x0 --accounts A B --simulator + +//# publish +module Test::M1 { + public struct Object has key, store { + id: UID, + value: u64, + } + + public entry fun create(value: u64, recipient: address, ctx: &mut TxContext) { + transfer::public_transfer( + Object { id: object::new(ctx), value }, + recipient + ) + } + + public fun swap_value_and_send(mut lhs: Object, mut rhs: Object, recipient: address) { + let tmp = lhs.value; + lhs.value = rhs.value; + rhs.value = tmp; + transfer::public_transfer(lhs, recipient); + transfer::public_transfer(rhs, recipient); + } +} + +//# create-checkpoint + +//# run Test::M1::create --args 0 @B --sender A + +//# run Test::M1::create --args 1 @A --sender A + +//# run Test::M1::create --args 2 @B --sender A + +//# run Test::M1::create --args 3 @A --sender A + +//# run Test::M1::create --args 4 @B --sender A + +//# create-checkpoint + +//# run Test::M1::create --args 100 @A --sender A + +//# run Test::M1::create --args 101 @A --sender A + +//# run Test::M1::create --args 102 @A --sender A + +//# run Test::M1::create --args 103 @B --sender A + +//# run Test::M1::create --args 104 @B --sender A + +//# create-checkpoint + +//# run-graphql +# Expect 7 results +# [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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + + +//# run-graphql +# scans [B, A] -> [2, 3] +# Because `scanLimit` is specified, both the start and end cursors should have `is_scan_limited` flag to true +# startCursor is at 2, endCursor is at 3 +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":4,"t":3,"i":true} +# scans [B] -> [4] +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":4,"t":4,"i":true} +# scans [A, B, A] -> [5, 6, 7] +# both the start and end cursors should have `is_scan_limited` flag to true +# startCursor at 5, the sole element has cursor at 6, endCursor at 7 +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":4,"t":7,"i":true} +# scans [A, A] -> [8, 9] +# both the start and end cursors should have `is_scan_limited` flag to true +# startCursor at 5, the sole element has cursor at 8, endCursor at 9 +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":4,"t":9,"i":true} +# scans [B, B] -> [10, 11] +# both the start and end cursors should have `is_scan_limited` flag to true +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run Test::M1::create --args 105 @A --sender A + +//# create-checkpoint + +//# run-graphql --cursors {"c":4,"t":11,"i":true} +# we've introduced a new final transaction that doesn't match the filter +# both the start and end cursors should have `is_scan_limited` flag to true +# startCursor = endCursor = 12, because there is only 1 more from the given cursor, +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":4,"t":12,"i":true} +# try paginating backwards on the last `endCursor` +# should yield startCursor at 10, endCursor at 11 +# and the result set consists of txs 10 and 11 +# the scanLimit is exclusive of the cursor, hence we reach tx 10 inclusively +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/equal/last.exp b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/equal/last.exp new file mode 100644 index 0000000000000..e2b301c1dc7e3 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/equal/last.exp @@ -0,0 +1,309 @@ +processed 22 tasks + +init: +A: object(0,0), B: object(0,1) + +task 1, lines 8-29: +//# publish +created: object(1,0) +mutated: object(0,2) +gas summary: computation_cost: 1000000, storage_cost: 5798800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 31: +//# create-checkpoint +Checkpoint created: 1 + +task 3, line 33: +//# run Test::M1::create --args 0 @B --sender A +created: object(3,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 4, line 35: +//# run Test::M1::create --args 1 @B --sender A +created: object(4,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 5, line 37: +//# run Test::M1::create --args 2 @A --sender A +created: object(5,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 6, line 39: +//# run Test::M1::create --args 3 @A --sender A +created: object(6,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 7, line 41: +//# run Test::M1::create --args 4 @A --sender A +created: object(7,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 8, line 43: +//# create-checkpoint +Checkpoint created: 2 + +task 9, line 45: +//# run Test::M1::create --args 100 @B --sender A +created: object(9,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 10, line 47: +//# run Test::M1::create --args 101 @A --sender A +created: object(10,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 11, line 49: +//# run Test::M1::create --args 102 @B --sender A +created: object(11,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 12, line 51: +//# run Test::M1::create --args 103 @A --sender A +created: object(12,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 13, line 53: +//# run Test::M1::create --args 104 @B --sender A +created: object(13,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 14, line 55: +//# create-checkpoint +Checkpoint created: 3 + +task 15, lines 57-79: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false, + "startCursor": "eyJjIjozLCJ0IjoyLCJpIjpmYWxzZX0", + "endCursor": "eyJjIjozLCJ0IjoxMSwiaSI6ZmFsc2V9" + }, + "edges": [ + { + "cursor": "eyJjIjozLCJ0IjoyLCJpIjpmYWxzZX0", + "node": { + "digest": "HzyC8gcn4m1ymKxYSpWMaNnmbrqm4hX7UBteJ4me3LFd", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0IjozLCJpIjpmYWxzZX0", + "node": { + "digest": "DiywoRFzC33smQhVf5K7AcM853XFgfgFxBGErLTEvVWi", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo3LCJpIjpmYWxzZX0", + "node": { + "digest": "F32vrNL7p5sa1iFeykvFQ17UYLeM1urXnSNbbGyoqDRx", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo5LCJpIjpmYWxzZX0", + "node": { + "digest": "3eHY9XENiqep4VvNBr3ws79TEMGhp5egGvFEymk679dc", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0IjoxMSwiaSI6ZmFsc2V9", + "node": { + "digest": "4p5avK1cStj1xtBnvCnHgEUVVr5qnfXQ76ujUZx4ZvP8", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} + +task 16, lines 82-106: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": false, + "startCursor": "eyJjIjozLCJ0IjoxMCwiaSI6dHJ1ZX0", + "endCursor": "eyJjIjozLCJ0IjoxMSwiaSI6dHJ1ZX0" + }, + "edges": [ + { + "cursor": "eyJjIjozLCJ0IjoxMSwiaSI6ZmFsc2V9", + "node": { + "digest": "4p5avK1cStj1xtBnvCnHgEUVVr5qnfXQ76ujUZx4ZvP8", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} + +task 17, lines 108-132: +//# run-graphql --cursors {"c":4,"t":10,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0Ijo5LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0Ijo5LCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0Ijo5LCJpIjpmYWxzZX0", + "node": { + "digest": "3eHY9XENiqep4VvNBr3ws79TEMGhp5egGvFEymk679dc", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} + +task 18, lines 134-158: +//# run-graphql --cursors {"c":4,"t":9,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0Ijo2LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0Ijo4LCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0Ijo3LCJpIjpmYWxzZX0", + "node": { + "digest": "F32vrNL7p5sa1iFeykvFQ17UYLeM1urXnSNbbGyoqDRx", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} + +task 19, lines 160-184: +//# run-graphql --cursors {"c":4,"t":6,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0Ijo0LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0Ijo1LCJpIjp0cnVlfQ" + }, + "edges": [] + } + } +} + +task 20, lines 186-209: +//# run-graphql --cursors {"c":4,"t":4,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0IjoyLCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0IjozLCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0IjoyLCJpIjpmYWxzZX0", + "node": { + "digest": "HzyC8gcn4m1ymKxYSpWMaNnmbrqm4hX7UBteJ4me3LFd", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjo0LCJ0IjozLCJpIjpmYWxzZX0", + "node": { + "digest": "DiywoRFzC33smQhVf5K7AcM853XFgfgFxBGErLTEvVWi", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 21, lines 212-235: +//# run-graphql --cursors {"c":4,"t":2,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false, + "startCursor": null, + "endCursor": null + }, + "edges": [] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/equal/last.move b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/equal/last.move new file mode 100644 index 0000000000000..6a84f9eabe90b --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/equal/last.move @@ -0,0 +1,235 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Mirrors scan_limit/equal/first.move, paginating backwards where first and scanLimit are equal. + +//# init --protocol-version 48 --addresses Test=0x0 --accounts A B --simulator + +//# publish +module Test::M1 { + public struct Object has key, store { + id: UID, + value: u64, + } + + public entry fun create(value: u64, recipient: address, ctx: &mut TxContext) { + transfer::public_transfer( + Object { id: object::new(ctx), value }, + recipient + ) + } + + public fun swap_value_and_send(mut lhs: Object, mut rhs: Object, recipient: address) { + let tmp = lhs.value; + lhs.value = rhs.value; + rhs.value = tmp; + transfer::public_transfer(lhs, recipient); + transfer::public_transfer(rhs, recipient); + } +} + +//# create-checkpoint + +//# run Test::M1::create --args 0 @B --sender A + +//# run Test::M1::create --args 1 @B --sender A + +//# run Test::M1::create --args 2 @A --sender A + +//# run Test::M1::create --args 3 @A --sender A + +//# run Test::M1::create --args 4 @A --sender A + +//# create-checkpoint + +//# run Test::M1::create --args 100 @B --sender A + +//# run Test::M1::create --args 101 @A --sender A + +//# run Test::M1::create --args 102 @B --sender A + +//# run Test::M1::create --args 103 @A --sender A + +//# run Test::M1::create --args 104 @B --sender A + +//# create-checkpoint + +//# run-graphql +# Expect ten results +{ + transactionBlocks(last: 50 filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + + +//# run-graphql +# boundary cursors are scan limited +# startCursor: 10, endCursor: 11 +# result is single element with cursor: 11 +{ + transactionBlocks(last: 2 scanLimit: 2 filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":4,"t":10,"i":true} +# boundary cursors are scan limited +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":4,"t":9,"i":true} +# boundary cursors are scan limited +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":4,"t":6,"i":true} +# boundary cursors are scan limited +# startCursor: 4, endCursor: 5 +# expect empty set +{ + transactionBlocks(last: 2 scanLimit: 2 before: "@{cursor_0}" filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":4,"t":4,"i":true} +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + + +//# run-graphql --cursors {"c":4,"t":2,"i":true} +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/ge_page/first.exp b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/ge_page/first.exp new file mode 100644 index 0000000000000..9f785684321d9 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/ge_page/first.exp @@ -0,0 +1,378 @@ +processed 34 tasks + +init: +A: object(0,0), B: object(0,1) + +task 1, lines 6-27: +//# publish +created: object(1,0) +mutated: object(0,2) +gas summary: computation_cost: 1000000, storage_cost: 5798800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 29: +//# create-checkpoint +Checkpoint created: 1 + +task 3, line 31: +//# run Test::M1::create --args 0 @A --sender A +created: object(3,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 4, line 33: +//# run Test::M1::create --args 1 @A --sender A +created: object(4,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 5, line 35: +//# run Test::M1::create --args 2 @B --sender B +created: object(5,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 6, line 37: +//# run Test::M1::create --args 3 @B --sender B +created: object(6,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 7, line 39: +//# run Test::M1::create --args 4 @B --sender B +created: object(7,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 8, line 41: +//# create-checkpoint +Checkpoint created: 2 + +task 9, line 43: +//# run Test::M1::create --args 100 @B --sender B +created: object(9,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 10, line 45: +//# run Test::M1::create --args 101 @B --sender B +created: object(10,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 11, line 47: +//# run Test::M1::create --args 102 @B --sender B +created: object(11,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 12, line 49: +//# run Test::M1::create --args 103 @B --sender B +created: object(12,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 13, line 51: +//# run Test::M1::create --args 104 @B --sender B +created: object(13,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 14, line 53: +//# create-checkpoint +Checkpoint created: 3 + +task 15, line 55: +//# run Test::M1::create --args 100 @B --sender B +created: object(15,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 16, line 57: +//# run Test::M1::create --args 101 @B --sender B +created: object(16,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 17, line 59: +//# run Test::M1::create --args 102 @B --sender B +created: object(17,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 18, line 61: +//# run Test::M1::create --args 103 @B --sender B +created: object(18,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 19, line 63: +//# run Test::M1::create --args 104 @B --sender B +created: object(19,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 20, line 65: +//# create-checkpoint +Checkpoint created: 4 + +task 21, line 67: +//# run Test::M1::create --args 200 @A --sender A +created: object(21,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 22, line 69: +//# run Test::M1::create --args 201 @B --sender A +created: object(22,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 23, line 71: +//# run Test::M1::create --args 202 @B --sender B +created: object(23,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 24, line 73: +//# run Test::M1::create --args 203 @B --sender B +created: object(24,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 25, line 75: +//# run Test::M1::create --args 204 @A --sender A +created: object(25,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 26, line 77: +//# create-checkpoint +Checkpoint created: 5 + +task 27, lines 79-100: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false, + "startCursor": "eyJjIjo1LCJ0IjoyLCJpIjpmYWxzZX0", + "endCursor": "eyJjIjo1LCJ0IjoyMSwiaSI6ZmFsc2V9" + }, + "edges": [ + { + "cursor": "eyJjIjo1LCJ0IjoyLCJpIjpmYWxzZX0", + "node": { + "digest": "CReUjLynvpq4dD4w6zekGxvSyBBQF2e3KG3K2Rs7oD8L", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjo1LCJ0IjozLCJpIjpmYWxzZX0", + "node": { + "digest": "BB6bMUxrBJX9wANzzptKnJM3bMVFKnD4xtY8DGWthaH9", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjo1LCJ0IjoxNywiaSI6ZmFsc2V9", + "node": { + "digest": "BhW3cLzaCgdRYc6jcmXB6DrQ6KaTfmYmb6vBNiu5xPhg", + "effects": { + "checkpoint": { + "sequenceNumber": 5 + } + } + } + }, + { + "cursor": "eyJjIjo1LCJ0IjoxOCwiaSI6ZmFsc2V9", + "node": { + "digest": "FjpAF5bT173BfV6HLuBdBd2bYevsUVDeJNmYnmJYsTLb", + "effects": { + "checkpoint": { + "sequenceNumber": 5 + } + } + } + }, + { + "cursor": "eyJjIjo1LCJ0IjoyMSwiaSI6ZmFsc2V9", + "node": { + "digest": "Eq611DTcfsBjH1P9Smrv7xFYQZXNtUMgbQCUr5jo3ycJ", + "effects": { + "checkpoint": { + "sequenceNumber": 5 + } + } + } + } + ] + } + } +} + +task 28, lines 103-129: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": true, + "startCursor": "eyJjIjo1LCJ0IjoyLCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo1LCJ0IjoyLCJpIjpmYWxzZX0" + }, + "edges": [ + { + "cursor": "eyJjIjo1LCJ0IjoyLCJpIjpmYWxzZX0", + "node": { + "digest": "CReUjLynvpq4dD4w6zekGxvSyBBQF2e3KG3K2Rs7oD8L", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 29, lines 131-155: +//# run-graphql --cursors {"c":7,"t":2,"i":false} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo3LCJ0IjozLCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo3LCJ0Ijo3LCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjo3LCJ0IjozLCJpIjpmYWxzZX0", + "node": { + "digest": "BB6bMUxrBJX9wANzzptKnJM3bMVFKnD4xtY8DGWthaH9", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 30, lines 157-180: +//# run-graphql --cursors {"c":7,"t":7,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo3LCJ0Ijo4LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo3LCJ0IjoxMiwiaSI6dHJ1ZX0" + }, + "edges": [] + } + } +} + +task 31, lines 182-205: +//# run-graphql --cursors {"c":7,"t":12,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo3LCJ0IjoxMywiaSI6dHJ1ZX0", + "endCursor": "eyJjIjo3LCJ0IjoxNywiaSI6dHJ1ZX0" + }, + "edges": [ + { + "cursor": "eyJjIjo3LCJ0IjoxNywiaSI6ZmFsc2V9", + "node": { + "digest": "BhW3cLzaCgdRYc6jcmXB6DrQ6KaTfmYmb6vBNiu5xPhg", + "effects": { + "checkpoint": { + "sequenceNumber": 5 + } + } + } + } + ] + } + } +} + +task 32, lines 207-232: +//# run-graphql --cursors {"c":7,"t":17,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo3LCJ0IjoxOCwiaSI6dHJ1ZX0", + "endCursor": "eyJjIjo3LCJ0IjoxOCwiaSI6ZmFsc2V9" + }, + "edges": [ + { + "cursor": "eyJjIjo3LCJ0IjoxOCwiaSI6ZmFsc2V9", + "node": { + "digest": "FjpAF5bT173BfV6HLuBdBd2bYevsUVDeJNmYnmJYsTLb", + "effects": { + "checkpoint": { + "sequenceNumber": 5 + } + } + } + } + ] + } + } +} + +task 33, lines 234-258: +//# run-graphql --cursors {"c":7,"t":18,"i":false} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": false, + "startCursor": "eyJjIjo3LCJ0IjoxOSwiaSI6dHJ1ZX0", + "endCursor": "eyJjIjo3LCJ0IjoyMSwiaSI6dHJ1ZX0" + }, + "edges": [ + { + "cursor": "eyJjIjo3LCJ0IjoyMSwiaSI6ZmFsc2V9", + "node": { + "digest": "Eq611DTcfsBjH1P9Smrv7xFYQZXNtUMgbQCUr5jo3ycJ", + "effects": { + "checkpoint": { + "sequenceNumber": 5 + } + } + } + } + ] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/ge_page/first.move b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/ge_page/first.move new file mode 100644 index 0000000000000..b9aea53452ff2 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/ge_page/first.move @@ -0,0 +1,258 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//# init --protocol-version 48 --addresses Test=0x0 --accounts A B --simulator + +//# publish +module Test::M1 { + public struct Object has key, store { + id: UID, + value: u64, + } + + public entry fun create(value: u64, recipient: address, ctx: &mut TxContext) { + transfer::public_transfer( + Object { id: object::new(ctx), value }, + recipient + ) + } + + public fun swap_value_and_send(mut lhs: Object, mut rhs: Object, recipient: address) { + let tmp = lhs.value; + lhs.value = rhs.value; + rhs.value = tmp; + transfer::public_transfer(lhs, recipient); + transfer::public_transfer(rhs, recipient); + } +} + +//# create-checkpoint + +//# run Test::M1::create --args 0 @A --sender A + +//# run Test::M1::create --args 1 @A --sender A + +//# run Test::M1::create --args 2 @B --sender B + +//# run Test::M1::create --args 3 @B --sender B + +//# run Test::M1::create --args 4 @B --sender B + +//# create-checkpoint + +//# run Test::M1::create --args 100 @B --sender B + +//# run Test::M1::create --args 101 @B --sender B + +//# run Test::M1::create --args 102 @B --sender B + +//# run Test::M1::create --args 103 @B --sender B + +//# run Test::M1::create --args 104 @B --sender B + +//# create-checkpoint + +//# run Test::M1::create --args 100 @B --sender B + +//# run Test::M1::create --args 101 @B --sender B + +//# run Test::M1::create --args 102 @B --sender B + +//# run Test::M1::create --args 103 @B --sender B + +//# run Test::M1::create --args 104 @B --sender B + +//# create-checkpoint + +//# run Test::M1::create --args 200 @A --sender A + +//# run Test::M1::create --args 201 @B --sender A + +//# run Test::M1::create --args 202 @B --sender B + +//# run Test::M1::create --args 203 @B --sender B + +//# run Test::M1::create --args 204 @A --sender A + +//# create-checkpoint + +//# run-graphql +{ + transactionBlocks(filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + + +//# run-graphql +# startCursor is 2 and scanLimited, endCursor is 2 and not scanLimited +# instead of setting the endCursor to the last transaction scanned, +# we set it to the last transaction in the set +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":7,"t":2,"i":false} +# startCursor: 3, endCursor: 7, both are scan-limited +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":7,"t":7,"i":true} +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":7,"t":12,"i":true} +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":7,"t":17,"i":true} +# startCursor: 18 scanLimited, endCursor: 18 not scanLimited +# this is because we have multiple matches within the scanning range +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":7,"t":18,"i":false} +# startCursor: 19, endCursor: 21, both are scan-limited +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/ge_page/last.exp b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/ge_page/last.exp new file mode 100644 index 0000000000000..6f4367472900e --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/ge_page/last.exp @@ -0,0 +1,355 @@ +processed 34 tasks + +init: +A: object(0,0), B: object(0,1) + +task 1, lines 6-27: +//# publish +created: object(1,0) +mutated: object(0,2) +gas summary: computation_cost: 1000000, storage_cost: 5798800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 29: +//# create-checkpoint +Checkpoint created: 1 + +task 3, line 31: +//# run Test::M1::create --args 0 @A --sender A +created: object(3,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 4, line 33: +//# run Test::M1::create --args 1 @A --sender A +created: object(4,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 5, line 35: +//# run Test::M1::create --args 2 @B --sender B +created: object(5,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 6, line 37: +//# run Test::M1::create --args 3 @B --sender B +created: object(6,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 7, line 39: +//# run Test::M1::create --args 4 @B --sender B +created: object(7,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 8, line 41: +//# create-checkpoint +Checkpoint created: 2 + +task 9, line 43: +//# run Test::M1::create --args 100 @B --sender B +created: object(9,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 10, line 45: +//# run Test::M1::create --args 101 @B --sender B +created: object(10,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 11, line 47: +//# run Test::M1::create --args 102 @B --sender B +created: object(11,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 12, line 49: +//# run Test::M1::create --args 103 @B --sender B +created: object(12,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 13, line 51: +//# run Test::M1::create --args 104 @B --sender B +created: object(13,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 14, line 53: +//# create-checkpoint +Checkpoint created: 3 + +task 15, line 55: +//# run Test::M1::create --args 100 @B --sender B +created: object(15,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 16, line 57: +//# run Test::M1::create --args 101 @B --sender B +created: object(16,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 17, line 59: +//# run Test::M1::create --args 102 @B --sender B +created: object(17,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 18, line 61: +//# run Test::M1::create --args 103 @B --sender B +created: object(18,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 19, line 63: +//# run Test::M1::create --args 104 @B --sender B +created: object(19,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 20, line 65: +//# create-checkpoint +Checkpoint created: 4 + +task 21, line 67: +//# run Test::M1::create --args 200 @A --sender A +created: object(21,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 22, line 69: +//# run Test::M1::create --args 201 @B --sender B +created: object(22,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 23, line 71: +//# run Test::M1::create --args 202 @B --sender B +created: object(23,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 24, line 73: +//# run Test::M1::create --args 203 @B --sender B +created: object(24,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 25, line 75: +//# run Test::M1::create --args 204 @A --sender A +created: object(25,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 26, line 77: +//# create-checkpoint +Checkpoint created: 5 + +task 27, lines 79-100: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false, + "startCursor": "eyJjIjo1LCJ0IjoyLCJpIjpmYWxzZX0", + "endCursor": "eyJjIjo1LCJ0IjoyMSwiaSI6ZmFsc2V9" + }, + "edges": [ + { + "cursor": "eyJjIjo1LCJ0IjoyLCJpIjpmYWxzZX0", + "node": { + "digest": "CReUjLynvpq4dD4w6zekGxvSyBBQF2e3KG3K2Rs7oD8L", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjo1LCJ0IjozLCJpIjpmYWxzZX0", + "node": { + "digest": "BB6bMUxrBJX9wANzzptKnJM3bMVFKnD4xtY8DGWthaH9", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjo1LCJ0IjoxNywiaSI6ZmFsc2V9", + "node": { + "digest": "BhW3cLzaCgdRYc6jcmXB6DrQ6KaTfmYmb6vBNiu5xPhg", + "effects": { + "checkpoint": { + "sequenceNumber": 5 + } + } + } + }, + { + "cursor": "eyJjIjo1LCJ0IjoyMSwiaSI6ZmFsc2V9", + "node": { + "digest": "G7DhbdbJeze8e5kmVLNY6aVKckM7yfrzadaABv71ssjx", + "effects": { + "checkpoint": { + "sequenceNumber": 5 + } + } + } + } + ] + } + } +} + +task 28, lines 103-125: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": false, + "startCursor": "eyJjIjo1LCJ0IjoyMSwiaSI6ZmFsc2V9", + "endCursor": "eyJjIjo1LCJ0IjoyMSwiaSI6dHJ1ZX0" + }, + "edges": [ + { + "cursor": "eyJjIjo1LCJ0IjoyMSwiaSI6ZmFsc2V9", + "node": { + "digest": "G7DhbdbJeze8e5kmVLNY6aVKckM7yfrzadaABv71ssjx", + "effects": { + "checkpoint": { + "sequenceNumber": 5 + } + } + } + } + ] + } + } +} + +task 29, lines 127-152: +//# run-graphql --cursors {"c":7,"t":21,"i":false} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo3LCJ0IjoxNiwiaSI6dHJ1ZX0", + "endCursor": "eyJjIjo3LCJ0IjoyMCwiaSI6dHJ1ZX0" + }, + "edges": [ + { + "cursor": "eyJjIjo3LCJ0IjoxNywiaSI6ZmFsc2V9", + "node": { + "digest": "BhW3cLzaCgdRYc6jcmXB6DrQ6KaTfmYmb6vBNiu5xPhg", + "effects": { + "checkpoint": { + "sequenceNumber": 5 + } + } + } + } + ] + } + } +} + +task 30, lines 154-177: +//# run-graphql --cursors {"c":7,"t":16,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo3LCJ0IjoxMSwiaSI6dHJ1ZX0", + "endCursor": "eyJjIjo3LCJ0IjoxNSwiaSI6dHJ1ZX0" + }, + "edges": [] + } + } +} + +task 31, lines 179-201: +//# run-graphql --cursors {"c":7,"t":11,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo3LCJ0Ijo2LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo3LCJ0IjoxMCwiaSI6dHJ1ZX0" + }, + "edges": [] + } + } +} + +task 32, lines 203-227: +//# run-graphql --cursors {"c":7,"t":6,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo3LCJ0IjozLCJpIjpmYWxzZX0", + "endCursor": "eyJjIjo3LCJ0Ijo1LCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjo3LCJ0IjozLCJpIjpmYWxzZX0", + "node": { + "digest": "BB6bMUxrBJX9wANzzptKnJM3bMVFKnD4xtY8DGWthaH9", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 33, lines 229-251: +//# run-graphql --cursors {"c":7,"t":3,"i":false} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": true, + "startCursor": "eyJjIjo3LCJ0IjoyLCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo3LCJ0IjoyLCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjo3LCJ0IjoyLCJpIjpmYWxzZX0", + "node": { + "digest": "CReUjLynvpq4dD4w6zekGxvSyBBQF2e3KG3K2Rs7oD8L", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/ge_page/last.move b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/ge_page/last.move new file mode 100644 index 0000000000000..1b8103b356dd2 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/ge_page/last.move @@ -0,0 +1,251 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//# init --protocol-version 48 --addresses Test=0x0 --accounts A B --simulator + +//# publish +module Test::M1 { + public struct Object has key, store { + id: UID, + value: u64, + } + + public entry fun create(value: u64, recipient: address, ctx: &mut TxContext) { + transfer::public_transfer( + Object { id: object::new(ctx), value }, + recipient + ) + } + + public fun swap_value_and_send(mut lhs: Object, mut rhs: Object, recipient: address) { + let tmp = lhs.value; + lhs.value = rhs.value; + rhs.value = tmp; + transfer::public_transfer(lhs, recipient); + transfer::public_transfer(rhs, recipient); + } +} + +//# create-checkpoint + +//# run Test::M1::create --args 0 @A --sender A + +//# run Test::M1::create --args 1 @A --sender A + +//# run Test::M1::create --args 2 @B --sender B + +//# run Test::M1::create --args 3 @B --sender B + +//# run Test::M1::create --args 4 @B --sender B + +//# create-checkpoint + +//# run Test::M1::create --args 100 @B --sender B + +//# run Test::M1::create --args 101 @B --sender B + +//# run Test::M1::create --args 102 @B --sender B + +//# run Test::M1::create --args 103 @B --sender B + +//# run Test::M1::create --args 104 @B --sender B + +//# create-checkpoint + +//# run Test::M1::create --args 100 @B --sender B + +//# run Test::M1::create --args 101 @B --sender B + +//# run Test::M1::create --args 102 @B --sender B + +//# run Test::M1::create --args 103 @B --sender B + +//# run Test::M1::create --args 104 @B --sender B + +//# create-checkpoint + +//# run Test::M1::create --args 200 @A --sender A + +//# run Test::M1::create --args 201 @B --sender B + +//# run Test::M1::create --args 202 @B --sender B + +//# run Test::M1::create --args 203 @B --sender B + +//# run Test::M1::create --args 204 @A --sender A + +//# create-checkpoint + +//# run-graphql +{ + transactionBlocks(filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + + +//# run-graphql +# startCursor 21 not scan limited, endCursor 21 is scan limited +{ + transactionBlocks(last: 1 scanLimit: 5 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 6}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":7,"t":21,"i":false} +# startCursor 16, endCursor 20, both isScanLimited +# This might be a bit confusing, but the `startCursor` is 16 and not 17 because +# `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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":7,"t":16,"i":true} +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":7,"t":6,"i":true} +# startCursor is 3, not scanLimited, endCursor is 5, scanLimited +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/invalid_limits.exp b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/invalid_limits.exp new file mode 100644 index 0000000000000..2dc10793ef702 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/invalid_limits.exp @@ -0,0 +1,112 @@ +processed 13 tasks + +init: +A: object(0,0), B: object(0,1) + +task 1, lines 8-29: +//# publish +created: object(1,0) +mutated: object(0,2) +gas summary: computation_cost: 1000000, storage_cost: 5798800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 31: +//# create-checkpoint +Checkpoint created: 1 + +task 3, line 33: +//# run Test::M1::create --args 0 @B --sender A +created: object(3,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 4, line 35: +//# run Test::M1::create --args 1 @A --sender A +created: object(4,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 5, line 37: +//# run Test::M1::create --args 2 @B --sender A +created: object(5,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 6, line 39: +//# run Test::M1::create --args 3 @A --sender A +created: object(6,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 7, line 41: +//# run Test::M1::create --args 4 @B --sender A +created: object(7,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 8, line 43: +//# create-checkpoint +Checkpoint created: 2 + +task 9, lines 45-66: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false, + "startCursor": null, + "endCursor": null + }, + "edges": [] + } + } +} + +task 10, lines 68-89: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false, + "startCursor": null, + "endCursor": null + }, + "edges": [] + } + } +} + +task 11, lines 91-112: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false, + "startCursor": null, + "endCursor": null + }, + "edges": [] + } + } +} + +task 12, lines 114-135: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false, + "startCursor": null, + "endCursor": null + }, + "edges": [] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/invalid_limits.move b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/invalid_limits.move new file mode 100644 index 0000000000000..18c025402d2f3 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/invalid_limits.move @@ -0,0 +1,135 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// For any instance where limit is 0 or scan limit is 0, we should return an empty result + +//# init --protocol-version 48 --addresses Test=0x0 --accounts A B --simulator + +//# publish +module Test::M1 { + public struct Object has key, store { + id: UID, + value: u64, + } + + public entry fun create(value: u64, recipient: address, ctx: &mut TxContext) { + transfer::public_transfer( + Object { id: object::new(ctx), value }, + recipient + ) + } + + public fun swap_value_and_send(mut lhs: Object, mut rhs: Object, recipient: address) { + let tmp = lhs.value; + lhs.value = rhs.value; + rhs.value = tmp; + transfer::public_transfer(lhs, recipient); + transfer::public_transfer(rhs, recipient); + } +} + +//# create-checkpoint + +//# run Test::M1::create --args 0 @B --sender A + +//# run Test::M1::create --args 1 @A --sender A + +//# run Test::M1::create --args 2 @B --sender A + +//# run Test::M1::create --args 3 @A --sender A + +//# run Test::M1::create --args 4 @B --sender A + +//# create-checkpoint + +//# run-graphql +{ + transactionBlocks(first: 0 scanLimit: 2 filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql +{ + transactionBlocks(first: 2 scanLimit: 0 filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql +{ + transactionBlocks(first: 0 scanLimit: 0 filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql +{ + transactionBlocks(first: 0 filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/le_page/first.exp b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/le_page/first.exp new file mode 100644 index 0000000000000..4cd2dabaa365b --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/le_page/first.exp @@ -0,0 +1,364 @@ +processed 22 tasks + +init: +A: object(0,0), B: object(0,1) + +task 1, lines 10-31: +//# publish +created: object(1,0) +mutated: object(0,2) +gas summary: computation_cost: 1000000, storage_cost: 5798800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 33: +//# create-checkpoint +Checkpoint created: 1 + +task 3, line 35: +//# run Test::M1::create --args 0 @A --sender A +created: object(3,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 4, line 37: +//# run Test::M1::create --args 1 @B --sender B +created: object(4,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 5, line 39: +//# run Test::M1::create --args 2 @A --sender A +created: object(5,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 6, line 41: +//# run Test::M1::create --args 3 @B --sender B +created: object(6,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 7, line 43: +//# run Test::M1::create --args 4 @A --sender A +created: object(7,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 8, line 45: +//# create-checkpoint +Checkpoint created: 2 + +task 9, line 47: +//# run Test::M1::create --args 100 @B --sender B +created: object(9,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 10, line 49: +//# run Test::M1::create --args 101 @A --sender A +created: object(10,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 11, line 51: +//# run Test::M1::create --args 102 @B --sender B +created: object(11,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 12, line 53: +//# run Test::M1::create --args 103 @A --sender A +created: object(12,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 13, line 55: +//# run Test::M1::create --args 104 @B --sender B +created: object(13,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 14, line 57: +//# create-checkpoint +Checkpoint created: 3 + +task 15, lines 59-81: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false, + "startCursor": "eyJjIjozLCJ0IjoyLCJpIjpmYWxzZX0", + "endCursor": "eyJjIjozLCJ0IjoxMSwiaSI6ZmFsc2V9" + }, + "edges": [ + { + "cursor": "eyJjIjozLCJ0IjoyLCJpIjpmYWxzZX0", + "node": { + "digest": "CReUjLynvpq4dD4w6zekGxvSyBBQF2e3KG3K2Rs7oD8L", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0IjozLCJpIjpmYWxzZX0", + "node": { + "digest": "FveHd4cC5ykjtknvewFjmf6V1gHYhdkuEoUEBAV3y52h", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo0LCJpIjpmYWxzZX0", + "node": { + "digest": "Hgu3LePqrpyR8Vq3Ve4L2KmvErcdcz92u7YiiotkKJ1N", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo1LCJpIjpmYWxzZX0", + "node": { + "digest": "5pVYLMHazkfshyLvmQyow1UY4vn9V481KWDxUYpX65BZ", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo2LCJpIjpmYWxzZX0", + "node": { + "digest": "2EwyAHiMofhbM5z5ty7XT1QXs4sNZfHmZLX513Ag8sD3", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo3LCJpIjpmYWxzZX0", + "node": { + "digest": "FtVAPJKNoPumPcQuyzznmZB99yhYNqdsmC7wKPj17xR3", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo4LCJpIjpmYWxzZX0", + "node": { + "digest": "4LUhoFJMmZfG71RHiRkwa9KHovrDv3S3mqUM1vu9JWKJ", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo5LCJpIjpmYWxzZX0", + "node": { + "digest": "FasAf1kHei9QkZuPLBSkCdXXMtv13RbCgiywktFzx58m", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0IjoxMCwiaSI6ZmFsc2V9", + "node": { + "digest": "AnqDERsdbEiE26CACJa6KtJTLsggisgu7yxhMJ6mU1JZ", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0IjoxMSwiaSI6ZmFsc2V9", + "node": { + "digest": "GKKT2n7jc6YyJDcckP3kXhVN5FEZJEk1fN3aFzcgRyRr", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} + +task 16, lines 84-106: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": true, + "startCursor": "eyJjIjozLCJ0IjoyLCJpIjp0cnVlfQ", + "endCursor": "eyJjIjozLCJ0IjozLCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjozLCJ0IjoyLCJpIjpmYWxzZX0", + "node": { + "digest": "CReUjLynvpq4dD4w6zekGxvSyBBQF2e3KG3K2Rs7oD8L", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 17, lines 108-130: +//# run-graphql --cursors {"c":4,"t":3,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0Ijo0LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0Ijo1LCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0Ijo0LCJpIjpmYWxzZX0", + "node": { + "digest": "Hgu3LePqrpyR8Vq3Ve4L2KmvErcdcz92u7YiiotkKJ1N", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 18, lines 132-158: +//# run-graphql --cursors {"c":4,"t":5,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0Ijo2LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0Ijo4LCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0Ijo2LCJpIjpmYWxzZX0", + "node": { + "digest": "2EwyAHiMofhbM5z5ty7XT1QXs4sNZfHmZLX513Ag8sD3", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjo0LCJ0Ijo4LCJpIjpmYWxzZX0", + "node": { + "digest": "4LUhoFJMmZfG71RHiRkwa9KHovrDv3S3mqUM1vu9JWKJ", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} + +task 19, lines 160-182: +//# run-graphql --cursors {"c":4,"t":8,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": false, + "startCursor": "eyJjIjo0LCJ0Ijo5LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0IjoxMSwiaSI6dHJ1ZX0" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0IjoxMCwiaSI6ZmFsc2V9", + "node": { + "digest": "AnqDERsdbEiE26CACJa6KtJTLsggisgu7yxhMJ6mU1JZ", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} + +task 20, lines 184-207: +//# run-graphql --cursors {"c":4,"t":10,"i":false} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": false, + "startCursor": "eyJjIjo0LCJ0IjoxMSwiaSI6dHJ1ZX0", + "endCursor": "eyJjIjo0LCJ0IjoxMSwiaSI6dHJ1ZX0" + }, + "edges": [] + } + } +} + +task 21, lines 209-232: +//# run-graphql --cursors {"c":4,"t":11,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false, + "startCursor": null, + "endCursor": null + }, + "edges": [] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/le_page/first.move b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/le_page/first.move new file mode 100644 index 0000000000000..bd9b3d2fe2ace --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/le_page/first.move @@ -0,0 +1,232 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Without a scan limit, we would expect each query to yield a response containing two results. +// However, because we have a scanLimit of 2, we'll be limited to filtering only two candidate +// transactions per page, of which one will match the filtering criteria. + +//# init --protocol-version 48 --addresses Test=0x0 --accounts A B --simulator + +//# publish +module Test::M1 { + public struct Object has key, store { + id: UID, + value: u64, + } + + public entry fun create(value: u64, recipient: address, ctx: &mut TxContext) { + transfer::public_transfer( + Object { id: object::new(ctx), value }, + recipient + ) + } + + public fun swap_value_and_send(mut lhs: Object, mut rhs: Object, recipient: address) { + let tmp = lhs.value; + lhs.value = rhs.value; + rhs.value = tmp; + transfer::public_transfer(lhs, recipient); + transfer::public_transfer(rhs, recipient); + } +} + +//# create-checkpoint + +//# run Test::M1::create --args 0 @A --sender A + +//# run Test::M1::create --args 1 @B --sender B + +//# run Test::M1::create --args 2 @A --sender A + +//# run Test::M1::create --args 3 @B --sender B + +//# run Test::M1::create --args 4 @A --sender A + +//# create-checkpoint + +//# run Test::M1::create --args 100 @B --sender B + +//# run Test::M1::create --args 101 @A --sender A + +//# run Test::M1::create --args 102 @B --sender B + +//# run Test::M1::create --args 103 @A --sender A + +//# run Test::M1::create --args 104 @B --sender B + +//# create-checkpoint + +//# run-graphql +# ten transactions total +{ + transactionBlocks(first: 50 filter: {afterCheckpoint: 1 beforeCheckpoint: 4}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + + +//# run-graphql +# startCursor 2, endCursor 3, both scan limited +{ + transactionBlocks(first: 3 scanLimit: 2 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":4,"t":5,"i":true} +# note the changes: first 3 -> 4, scanLimit 2 -> 3 +# startCursor: 6, endCursor: 8 both scanLimited +# because we've exhausted all matches in the scanned window, +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":4,"t":10,"i":false} +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":4,"t":11,"i":true} +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/le_page/last.exp b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/le_page/last.exp new file mode 100644 index 0000000000000..714548cfc5c09 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/le_page/last.exp @@ -0,0 +1,365 @@ +processed 22 tasks + +init: +A: object(0,0), B: object(0,1) + +task 1, lines 10-31: +//# publish +created: object(1,0) +mutated: object(0,2) +gas summary: computation_cost: 1000000, storage_cost: 5798800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 33: +//# create-checkpoint +Checkpoint created: 1 + +task 3, line 35: +//# run Test::M1::create --args 0 @A --sender A +created: object(3,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 4, line 37: +//# run Test::M1::create --args 1 @B --sender B +created: object(4,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 5, line 39: +//# run Test::M1::create --args 2 @A --sender A +created: object(5,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 6, line 41: +//# run Test::M1::create --args 3 @B --sender B +created: object(6,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 7, line 43: +//# run Test::M1::create --args 4 @A --sender A +created: object(7,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 8, line 45: +//# create-checkpoint +Checkpoint created: 2 + +task 9, line 47: +//# run Test::M1::create --args 100 @B --sender B +created: object(9,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 10, line 49: +//# run Test::M1::create --args 101 @A --sender A +created: object(10,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 11, line 51: +//# run Test::M1::create --args 102 @B --sender B +created: object(11,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 12, line 53: +//# run Test::M1::create --args 103 @A --sender A +created: object(12,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 13, line 55: +//# run Test::M1::create --args 104 @B --sender B +created: object(13,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 14, line 57: +//# create-checkpoint +Checkpoint created: 3 + +task 15, lines 59-81: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false, + "startCursor": "eyJjIjozLCJ0IjoyLCJpIjpmYWxzZX0", + "endCursor": "eyJjIjozLCJ0IjoxMSwiaSI6ZmFsc2V9" + }, + "edges": [ + { + "cursor": "eyJjIjozLCJ0IjoyLCJpIjpmYWxzZX0", + "node": { + "digest": "CReUjLynvpq4dD4w6zekGxvSyBBQF2e3KG3K2Rs7oD8L", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0IjozLCJpIjpmYWxzZX0", + "node": { + "digest": "FveHd4cC5ykjtknvewFjmf6V1gHYhdkuEoUEBAV3y52h", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo0LCJpIjpmYWxzZX0", + "node": { + "digest": "Hgu3LePqrpyR8Vq3Ve4L2KmvErcdcz92u7YiiotkKJ1N", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo1LCJpIjpmYWxzZX0", + "node": { + "digest": "5pVYLMHazkfshyLvmQyow1UY4vn9V481KWDxUYpX65BZ", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo2LCJpIjpmYWxzZX0", + "node": { + "digest": "2EwyAHiMofhbM5z5ty7XT1QXs4sNZfHmZLX513Ag8sD3", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo3LCJpIjpmYWxzZX0", + "node": { + "digest": "FtVAPJKNoPumPcQuyzznmZB99yhYNqdsmC7wKPj17xR3", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo4LCJpIjpmYWxzZX0", + "node": { + "digest": "4LUhoFJMmZfG71RHiRkwa9KHovrDv3S3mqUM1vu9JWKJ", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0Ijo5LCJpIjpmYWxzZX0", + "node": { + "digest": "FasAf1kHei9QkZuPLBSkCdXXMtv13RbCgiywktFzx58m", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0IjoxMCwiaSI6ZmFsc2V9", + "node": { + "digest": "AnqDERsdbEiE26CACJa6KtJTLsggisgu7yxhMJ6mU1JZ", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + }, + { + "cursor": "eyJjIjozLCJ0IjoxMSwiaSI6ZmFsc2V9", + "node": { + "digest": "GKKT2n7jc6YyJDcckP3kXhVN5FEZJEk1fN3aFzcgRyRr", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} + +task 16, lines 83-105: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": false, + "startCursor": "eyJjIjozLCJ0IjoxMCwiaSI6dHJ1ZX0", + "endCursor": "eyJjIjozLCJ0IjoxMSwiaSI6dHJ1ZX0" + }, + "edges": [ + { + "cursor": "eyJjIjozLCJ0IjoxMCwiaSI6ZmFsc2V9", + "node": { + "digest": "AnqDERsdbEiE26CACJa6KtJTLsggisgu7yxhMJ6mU1JZ", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} + +task 17, lines 107-129: +//# run-graphql --cursors {"c":4,"t":10,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0Ijo4LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0Ijo5LCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0Ijo4LCJpIjpmYWxzZX0", + "node": { + "digest": "4LUhoFJMmZfG71RHiRkwa9KHovrDv3S3mqUM1vu9JWKJ", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + } + ] + } + } +} + +task 18, lines 131-154: +//# run-graphql --cursors {"c":4,"t":8,"i":false} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0Ijo2LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0Ijo3LCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0Ijo2LCJpIjpmYWxzZX0", + "node": { + "digest": "2EwyAHiMofhbM5z5ty7XT1QXs4sNZfHmZLX513Ag8sD3", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 19, lines 156-178: +//# run-graphql --cursors {"c":4,"t":6,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0Ijo0LCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0Ijo1LCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0Ijo0LCJpIjpmYWxzZX0", + "node": { + "digest": "Hgu3LePqrpyR8Vq3Ve4L2KmvErcdcz92u7YiiotkKJ1N", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 20, lines 180-202: +//# run-graphql --cursors {"c":4,"t":4,"i":false} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": true, + "startCursor": "eyJjIjo0LCJ0IjoyLCJpIjp0cnVlfQ", + "endCursor": "eyJjIjo0LCJ0IjozLCJpIjp0cnVlfQ" + }, + "edges": [ + { + "cursor": "eyJjIjo0LCJ0IjoyLCJpIjpmYWxzZX0", + "node": { + "digest": "CReUjLynvpq4dD4w6zekGxvSyBBQF2e3KG3K2Rs7oD8L", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} + +task 21, lines 205-227: +//# run-graphql --cursors {"c":4,"t":2,"i":true} +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false, + "startCursor": null, + "endCursor": null + }, + "edges": [] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/le_page/last.move b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/le_page/last.move new file mode 100644 index 0000000000000..9a1280bed8b3d --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/le_page/last.move @@ -0,0 +1,227 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Without a scan limit, we would expect each query to yield a response containing two results. +// However, because we have a scanLimit of 2, we'll be limited to filtering only two candidate +// transactions per page, of which one will match the filtering criteria. + +//# init --protocol-version 48 --addresses Test=0x0 --accounts A B --simulator + +//# publish +module Test::M1 { + public struct Object has key, store { + id: UID, + value: u64, + } + + public entry fun create(value: u64, recipient: address, ctx: &mut TxContext) { + transfer::public_transfer( + Object { id: object::new(ctx), value }, + recipient + ) + } + + public fun swap_value_and_send(mut lhs: Object, mut rhs: Object, recipient: address) { + let tmp = lhs.value; + lhs.value = rhs.value; + rhs.value = tmp; + transfer::public_transfer(lhs, recipient); + transfer::public_transfer(rhs, recipient); + } +} + +//# create-checkpoint + +//# run Test::M1::create --args 0 @A --sender A + +//# run Test::M1::create --args 1 @B --sender B + +//# run Test::M1::create --args 2 @A --sender A + +//# run Test::M1::create --args 3 @B --sender B + +//# run Test::M1::create --args 4 @A --sender A + +//# create-checkpoint + +//# run Test::M1::create --args 100 @B --sender B + +//# run Test::M1::create --args 101 @A --sender A + +//# run Test::M1::create --args 102 @B --sender B + +//# run Test::M1::create --args 103 @A --sender A + +//# run Test::M1::create --args 104 @B --sender B + +//# create-checkpoint + +//# run-graphql +# ten transactions total +{ + transactionBlocks(last: 50 filter: {afterCheckpoint: 1 beforeCheckpoint: 4}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql +# startCursor: 10, endCursor: 11, both scan limited +{ + transactionBlocks(last: 3 scanLimit: 2 filter: {recvAddress: "@{A}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# run-graphql --cursors {"c":4,"t":8,"i":false} +# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + +//# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} + + +//# 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}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/require.exp b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/require.exp new file mode 100644 index 0000000000000..1e5207ad10ed9 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/require.exp @@ -0,0 +1,606 @@ +processed 25 tasks + +init: +A: object(0,0), B: object(0,1) + +task 1, lines 6-27: +//# publish +created: object(1,0) +mutated: object(0,2) +gas summary: computation_cost: 1000000, storage_cost: 5798800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 29: +//# create-checkpoint +Checkpoint created: 1 + +task 3, line 31: +//# run Test::M1::create --args 0 @B --sender A +created: object(3,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 4, line 33: +//# run Test::M1::create --args 1 @B --sender A +created: object(4,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 5, line 35: +//# run Test::M1::create --args 2 @B --sender A +created: object(5,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 6, line 37: +//# run Test::M1::create --args 3 @B --sender A +created: object(6,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 7, line 39: +//# run Test::M1::create --args 4 @B --sender A +created: object(7,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 8, line 41: +//# create-checkpoint +Checkpoint created: 2 + +task 9, line 43: +//# run Test::M1::create --args 100 @B --sender A +created: object(9,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 10, line 45: +//# run Test::M1::create --args 101 @B --sender A +created: object(10,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 11, line 47: +//# run Test::M1::create --args 102 @B --sender A +created: object(11,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 12, line 49: +//# run Test::M1::create --args 103 @B --sender A +created: object(12,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 13, line 51: +//# run Test::M1::create --args 104 @B --sender A +created: object(13,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 14, line 53: +//# create-checkpoint +Checkpoint created: 3 + +task 15, lines 55-74: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "endCursor": "eyJjIjozLCJ0IjoxMSwiaSI6ZmFsc2V9", + "startCursor": "eyJjIjozLCJ0IjoyLCJpIjpmYWxzZX0" + }, + "nodes": [ + { + "digest": "HzyC8gcn4m1ymKxYSpWMaNnmbrqm4hX7UBteJ4me3LFd", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "DiywoRFzC33smQhVf5K7AcM853XFgfgFxBGErLTEvVWi", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "7MgEfj6QXfsjDFtvJSAE9FNL3RYt8Kdw21NnfuuVXkbt", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "B8mg7JvC64cnh656yuwHyXyFPULutxBECMFkCPQNStmZ", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "7i7Evom2DUeS1PYxKwDnLfoZpv2r6kxdGoEWxXdFA9xV", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "5HoMDKMTYX3gibs8VroZUeSC3134MroLJN7hfAVZxdPM", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + }, + { + "digest": "BU8q4bm7XjaZiV8cPmchVp6SsmU8cmBWNUVmFTUNAHfb", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + }, + { + "digest": "HckXhLnDYV8hfMh1p8M8r7dpEpvzp5GpG9oHzv9dmv4R", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + }, + { + "digest": "Hvh3oJuoTeRYRU2wkTriwDsozpaay5c7dhRS7Ru3S2S5", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + }, + { + "digest": "ADhRGJc24AQCvUnQNgkfjnna3hsTFK51YTgq5J5VAKQr", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + ] + } + } +} + +task 16, lines 76-95: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "endCursor": "eyJjIjozLCJ0IjoxMSwiaSI6ZmFsc2V9", + "startCursor": "eyJjIjozLCJ0IjoyLCJpIjpmYWxzZX0" + }, + "nodes": [ + { + "digest": "HzyC8gcn4m1ymKxYSpWMaNnmbrqm4hX7UBteJ4me3LFd", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "DiywoRFzC33smQhVf5K7AcM853XFgfgFxBGErLTEvVWi", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "7MgEfj6QXfsjDFtvJSAE9FNL3RYt8Kdw21NnfuuVXkbt", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "B8mg7JvC64cnh656yuwHyXyFPULutxBECMFkCPQNStmZ", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "7i7Evom2DUeS1PYxKwDnLfoZpv2r6kxdGoEWxXdFA9xV", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "5HoMDKMTYX3gibs8VroZUeSC3134MroLJN7hfAVZxdPM", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + }, + { + "digest": "BU8q4bm7XjaZiV8cPmchVp6SsmU8cmBWNUVmFTUNAHfb", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + }, + { + "digest": "HckXhLnDYV8hfMh1p8M8r7dpEpvzp5GpG9oHzv9dmv4R", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + }, + { + "digest": "Hvh3oJuoTeRYRU2wkTriwDsozpaay5c7dhRS7Ru3S2S5", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + }, + { + "digest": "ADhRGJc24AQCvUnQNgkfjnna3hsTFK51YTgq5J5VAKQr", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + ] + } + } +} + +task 17, lines 97-116: +//# run-graphql +Response: { + "data": null, + "errors": [ + { + "message": "A scan limit must be specified for the given filter combination", + "locations": [ + { + "line": 3, + "column": 3 + } + ], + "path": [ + "transactionBlocks" + ], + "extensions": { + "code": "BAD_USER_INPUT" + } + } + ] +} + +task 18, lines 118-137: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "endCursor": "eyJjIjozLCJ0IjoxMSwiaSI6dHJ1ZX0", + "startCursor": "eyJjIjozLCJ0IjoyLCJpIjp0cnVlfQ" + }, + "nodes": [ + { + "digest": "HzyC8gcn4m1ymKxYSpWMaNnmbrqm4hX7UBteJ4me3LFd", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "DiywoRFzC33smQhVf5K7AcM853XFgfgFxBGErLTEvVWi", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "7MgEfj6QXfsjDFtvJSAE9FNL3RYt8Kdw21NnfuuVXkbt", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "B8mg7JvC64cnh656yuwHyXyFPULutxBECMFkCPQNStmZ", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "7i7Evom2DUeS1PYxKwDnLfoZpv2r6kxdGoEWxXdFA9xV", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "5HoMDKMTYX3gibs8VroZUeSC3134MroLJN7hfAVZxdPM", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + }, + { + "digest": "BU8q4bm7XjaZiV8cPmchVp6SsmU8cmBWNUVmFTUNAHfb", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + }, + { + "digest": "HckXhLnDYV8hfMh1p8M8r7dpEpvzp5GpG9oHzv9dmv4R", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + }, + { + "digest": "Hvh3oJuoTeRYRU2wkTriwDsozpaay5c7dhRS7Ru3S2S5", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + }, + { + "digest": "ADhRGJc24AQCvUnQNgkfjnna3hsTFK51YTgq5J5VAKQr", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + ] + } + } +} + +task 19, lines 139-158: +//# run-graphql +Response: { + "data": null, + "errors": [ + { + "message": "A scan limit must be specified for the given filter combination", + "locations": [ + { + "line": 3, + "column": 3 + } + ], + "path": [ + "transactionBlocks" + ], + "extensions": { + "code": "BAD_USER_INPUT" + } + } + ] +} + +task 20, lines 160-179: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "endCursor": "eyJjIjozLCJ0IjoxMSwiaSI6dHJ1ZX0", + "startCursor": "eyJjIjozLCJ0IjoyLCJpIjp0cnVlfQ" + }, + "nodes": [ + { + "digest": "HzyC8gcn4m1ymKxYSpWMaNnmbrqm4hX7UBteJ4me3LFd", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "DiywoRFzC33smQhVf5K7AcM853XFgfgFxBGErLTEvVWi", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "7MgEfj6QXfsjDFtvJSAE9FNL3RYt8Kdw21NnfuuVXkbt", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "B8mg7JvC64cnh656yuwHyXyFPULutxBECMFkCPQNStmZ", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "7i7Evom2DUeS1PYxKwDnLfoZpv2r6kxdGoEWxXdFA9xV", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + }, + { + "digest": "5HoMDKMTYX3gibs8VroZUeSC3134MroLJN7hfAVZxdPM", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + }, + { + "digest": "BU8q4bm7XjaZiV8cPmchVp6SsmU8cmBWNUVmFTUNAHfb", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + }, + { + "digest": "HckXhLnDYV8hfMh1p8M8r7dpEpvzp5GpG9oHzv9dmv4R", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + }, + { + "digest": "Hvh3oJuoTeRYRU2wkTriwDsozpaay5c7dhRS7Ru3S2S5", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + }, + { + "digest": "ADhRGJc24AQCvUnQNgkfjnna3hsTFK51YTgq5J5VAKQr", + "effects": { + "checkpoint": { + "sequenceNumber": 3 + } + } + } + ] + } + } +} + +task 21, lines 181-200: +//# run-graphql +Response: { + "data": null, + "errors": [ + { + "message": "A scan limit must be specified for the given filter combination", + "locations": [ + { + "line": 3, + "column": 3 + } + ], + "path": [ + "transactionBlocks" + ], + "extensions": { + "code": "BAD_USER_INPUT" + } + } + ] +} + +task 22, lines 202-221: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "endCursor": "eyJjIjozLCJ0IjoxMSwiaSI6dHJ1ZX0", + "startCursor": "eyJjIjozLCJ0IjoyLCJpIjp0cnVlfQ" + }, + "nodes": [] + } + } +} + +task 23, lines 224-243: +//# run-graphql +Response: { + "data": null, + "errors": [ + { + "message": "A scan limit must be specified for the given filter combination", + "locations": [ + { + "line": 3, + "column": 3 + } + ], + "path": [ + "transactionBlocks" + ], + "extensions": { + "code": "BAD_USER_INPUT" + } + } + ] +} + +task 24, lines 245-269: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": false, + "startCursor": "eyJjIjozLCJ0IjoyLCJpIjp0cnVlfQ", + "endCursor": "eyJjIjozLCJ0IjoxMSwiaSI6dHJ1ZX0" + }, + "edges": [ + { + "cursor": "eyJjIjozLCJ0IjoyLCJpIjpmYWxzZX0", + "node": { + "digest": "HzyC8gcn4m1ymKxYSpWMaNnmbrqm4hX7UBteJ4me3LFd", + "effects": { + "checkpoint": { + "sequenceNumber": 2 + } + } + } + } + ] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/require.move b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/require.move new file mode 100644 index 0000000000000..90b5570b7ded3 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/transactions/scan_limit/require.move @@ -0,0 +1,269 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//# init --protocol-version 48 --addresses Test=0x0 --accounts A B --simulator + +//# publish +module Test::M1 { + public struct Object has key, store { + id: UID, + value: u64, + } + + public entry fun create(value: u64, recipient: address, ctx: &mut TxContext) { + transfer::public_transfer( + Object { id: object::new(ctx), value }, + recipient + ) + } + + public fun swap_value_and_send(mut lhs: Object, mut rhs: Object, recipient: address) { + let tmp = lhs.value; + lhs.value = rhs.value; + rhs.value = tmp; + transfer::public_transfer(lhs, recipient); + transfer::public_transfer(rhs, recipient); + } +} + +//# create-checkpoint + +//# run Test::M1::create --args 0 @B --sender A + +//# run Test::M1::create --args 1 @B --sender A + +//# run Test::M1::create --args 2 @B --sender A + +//# run Test::M1::create --args 3 @B --sender A + +//# run Test::M1::create --args 4 @B --sender A + +//# create-checkpoint + +//# run Test::M1::create --args 100 @B --sender A + +//# run Test::M1::create --args 101 @B --sender A + +//# run Test::M1::create --args 102 @B --sender A + +//# run Test::M1::create --args 103 @B --sender A + +//# run Test::M1::create --args 104 @B --sender A + +//# create-checkpoint + +//# run-graphql +# Expect ten results +{ + transactionBlocks(filter: {recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +# Don't need scanLimit with sender +{ + transactionBlocks(filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +# scanLimit required +{ + transactionBlocks(filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 function: "@{Test}::M1::create"}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +# valid +{ + transactionBlocks(scanLimit: 50 filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 function: "@{Test}::M1::create"}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +# scanLimit required +{ + transactionBlocks(filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 kind: PROGRAMMABLE_TX}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +# valid +{ + transactionBlocks(scanLimit: 50 filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 kind: PROGRAMMABLE_TX}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +# scanLimit required +{ + transactionBlocks(filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 inputObject: "@{obj_3_0}"}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +# valid +{ + transactionBlocks(scanLimit: 50 filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 inputObject: "@{obj_3_0}"}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + + +//# run-graphql +# scanLimit required +{ + transactionBlocks(filter: {signAddress: "@{A}" recvAddress: "@{B}" afterCheckpoint: 1 beforeCheckpoint: 4 changedObject: "@{obj_3_0}"}) { + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + nodes { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } +} + +//# run-graphql +# Only one of the transactions will match this filter +# 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}"}) { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + edges { + cursor + node { + digest + effects { + checkpoint { + sequenceNumber + } + } + } + } + } +} diff --git a/crates/sui-graphql-rpc/examples/epoch/with_tx_block_connection_latest_epoch.graphql b/crates/sui-graphql-rpc/examples/epoch/with_tx_block_connection_latest_epoch.graphql index 9fdc8ea5a7fa3..62bc23f9f5855 100644 --- a/crates/sui-graphql-rpc/examples/epoch/with_tx_block_connection_latest_epoch.graphql +++ b/crates/sui-graphql-rpc/examples/epoch/with_tx_block_connection_latest_epoch.graphql @@ -1,6 +1,6 @@ { epoch { - transactionBlocks(first: 20, after: "eyJjIjoyNjkzMzMyNCwidCI6MTEwMTYxMDQ4MywidGMiOjI2ODUxMjQ4fQ") { + transactionBlocks(first: 20, after: "eyJjIjoyNjkzMzMyNCwidCI6MTEwMTYxMDQ4MywiaSI6ZmFsc2V9") { pageInfo { hasNextPage endCursor diff --git a/crates/sui-graphql-rpc/schema.graphql b/crates/sui-graphql-rpc/schema.graphql index db2f967fa4085..defc55cc329d2 100644 --- a/crates/sui-graphql-rpc/schema.graphql +++ b/crates/sui-graphql-rpc/schema.graphql @@ -98,8 +98,27 @@ type Address implements IOwner { """ Similar behavior to the `transactionBlocks` in Query but supporting the additional `AddressTransactionBlockRelationship` filter, which defaults to `SIGN`. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - transactionBlocks(first: Int, after: String, last: Int, before: String, relation: AddressTransactionBlockRelationship, filter: TransactionBlockFilter): TransactionBlockConnection! + transactionBlocks(first: Int, after: String, last: Int, before: String, relation: AddressTransactionBlockRelationship, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! } type AddressConnection { @@ -409,8 +428,25 @@ type Checkpoint { epoch: Epoch """ Transactions in this checkpoint. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range consists of all transactions in this checkpoint. """ - transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! } type CheckpointConnection { @@ -517,8 +553,27 @@ type Coin implements IMoveObject & IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -676,8 +731,27 @@ type CoinMetadata implements IMoveObject & IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -1093,8 +1167,25 @@ type Epoch { checkpoints(first: Int, after: String, last: Int, before: String): CheckpointConnection! """ The epoch's corresponding transaction blocks. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range consists of all transactions in this epoch. """ - transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! } type Event { @@ -1429,7 +1520,7 @@ interface IObject { """ The transaction blocks that sent objects to this object. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -1972,8 +2063,27 @@ type MoveObject implements IMoveObject & IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -2161,8 +2271,27 @@ type MovePackage implements IObject & IOwner { The transaction blocks that sent objects to this package. Note that objects that have been sent to a package become inaccessible. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the package's content. """ @@ -2540,8 +2669,27 @@ type Object implements IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -3142,8 +3290,27 @@ type Query { checkpoints(first: Int, after: String, last: Int, before: String): CheckpointConnection! """ The transaction blocks that exist in the network. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The events that exist in the network. """ @@ -3361,6 +3528,14 @@ type ServiceConfig { Maximum nesting allowed in struct fields when calculating the layout of a single Move Type. """ maxMoveValueDepth: Int! + """ + Maximum number of transaction ids that can be passed to a `TransactionBlockFilter`. + """ + maxTransactionIds: Int! + """ + Maximum number of candidates to scan when gathering a page of results. + """ + maxScanLimit: Int! } """ @@ -3578,8 +3753,27 @@ type StakedSui implements IMoveObject & IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -3780,8 +3974,27 @@ type SuinsRegistration implements IMoveObject & IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ diff --git a/crates/sui-graphql-rpc/src/config.rs b/crates/sui-graphql-rpc/src/config.rs index 71590683d308f..42f6feab0b084 100644 --- a/crates/sui-graphql-rpc/src/config.rs +++ b/crates/sui-graphql-rpc/src/config.rs @@ -92,6 +92,10 @@ pub struct Limits { pub max_type_nodes: u32, /// Maximum deph of a move value. pub max_move_value_depth: u32, + /// Maximum number of transaction ids that can be passed to a `TransactionBlockFilter`. + pub max_transaction_ids: u32, + /// Maximum number of candidates to scan when gathering a page of results. + pub max_scan_limit: u32, } #[GraphQLConfig] @@ -282,6 +286,16 @@ impl ServiceConfig { async fn max_move_value_depth(&self) -> u32 { self.limits.max_move_value_depth } + + /// Maximum number of transaction ids that can be passed to a `TransactionBlockFilter`. + async fn max_transaction_ids(&self) -> u32 { + self.limits.max_transaction_ids + } + + /// Maximum number of candidates to scan when gathering a page of results. + async fn max_scan_limit(&self) -> u32 { + self.limits.max_scan_limit + } } impl TxExecFullNodeConfig { @@ -452,6 +466,10 @@ impl Default for Limits { max_type_nodes: 256, // max_move_value_depth: 128, + // Filter-specific limits, such as the number of transaction ids that can be specified + // for the `TransactionBlockFilter`. + max_transaction_ids: 1000, + max_scan_limit: 100_000_000, } } } @@ -514,6 +532,8 @@ mod tests { max-type-argument-width = 64 max-type-nodes = 128 max-move-value-depth = 256 + max-transaction-ids = 11 + max-scan-limit = 50 "#, ) .unwrap(); @@ -533,6 +553,8 @@ mod tests { max_type_argument_width: 64, max_type_nodes: 128, max_move_value_depth: 256, + max_transaction_ids: 11, + max_scan_limit: 50, }, ..Default::default() }; @@ -596,6 +618,8 @@ mod tests { max-type-argument-width = 64 max-type-nodes = 128 max-move-value-depth = 256 + max-transaction-ids = 42 + max-scan-limit = 420 [experiments] test-flag = true @@ -618,6 +642,8 @@ mod tests { max_type_argument_width: 64, max_type_nodes: 128, max_move_value_depth: 256, + max_transaction_ids: 42, + max_scan_limit: 420, }, disabled_features: BTreeSet::from([FunctionalGroup::Analytics]), experiments: Experiments { test_flag: true }, diff --git a/crates/sui-graphql-rpc/src/connection.rs b/crates/sui-graphql-rpc/src/connection.rs new file mode 100644 index 0000000000000..4c48fc1727d0e --- /dev/null +++ b/crates/sui-graphql-rpc/src/connection.rs @@ -0,0 +1,125 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::borrow::Cow; +use std::marker::PhantomData; + +use async_graphql::connection::{ + ConnectionNameType, CursorType, DefaultConnectionName, DefaultEdgeName, Edge, EdgeNameType, + EmptyFields, EnableNodesField, NodesFieldSwitcherSealed, PageInfo, +}; +use async_graphql::{Object, ObjectType, OutputType, TypeName}; + +/// Mirrors the `Connection` type from async-graphql, with the exception that if `start_cursor` and/ +/// or `end_cursor` is set on the struct, then when `page_info` is called, it will use those values +/// before deferring to `edges`. The default implementation derives these cursors from the first and +/// last element of `edges`, so if `edges` is empty, both are set to null. This is undesirable for +/// queries that make use of `scan_limit`; when the scan limit is reached, a caller can continue to +/// paginate forwards or backwards until all candidates in the scanning range have been visited, +/// even if the current page yields no results. +pub(crate) struct ScanConnection< + Cursor, + Node, + EdgeFields = EmptyFields, + Name = DefaultConnectionName, + EdgeName = DefaultEdgeName, + NodesField = EnableNodesField, +> where + Cursor: CursorType + Send + Sync, + Node: OutputType, + EdgeFields: ObjectType, + Name: ConnectionNameType, + EdgeName: EdgeNameType, + NodesField: NodesFieldSwitcherSealed, +{ + _mark1: PhantomData, + _mark2: PhantomData, + _mark3: PhantomData, + pub edges: Vec>, + pub has_previous_page: bool, + pub has_next_page: bool, + pub start_cursor: Option, + pub end_cursor: Option, +} + +#[Object(name_type)] +impl + ScanConnection +where + Cursor: CursorType + Send + Sync, + Node: OutputType, + EdgeFields: ObjectType, + Name: ConnectionNameType, + EdgeName: EdgeNameType, +{ + /// Information to aid in pagination. + async fn page_info(&self) -> PageInfo { + // Unlike the default implementation, this Connection will use `start_cursor` and + // `end_cursor` if they are `Some`. + PageInfo { + has_previous_page: self.has_previous_page, + has_next_page: self.has_next_page, + start_cursor: self + .start_cursor + .clone() + .or_else(|| self.edges.first().map(|edge| edge.cursor.encode_cursor())), + end_cursor: self + .end_cursor + .clone() + .or_else(|| self.edges.last().map(|edge| edge.cursor.encode_cursor())), + } + } + + /// A list of edges. + #[inline] + async fn edges(&self) -> &[Edge] { + &self.edges + } + + /// A list of nodes. + async fn nodes(&self) -> Vec<&Node> { + self.edges.iter().map(|e| &e.node).collect() + } +} + +impl + ScanConnection +where + Cursor: CursorType + Send + Sync, + Node: OutputType, + EdgeFields: ObjectType, + Name: ConnectionNameType, + EdgeName: EdgeNameType, + NodesField: NodesFieldSwitcherSealed, +{ + /// Create a new connection. + #[inline] + pub fn new(has_previous_page: bool, has_next_page: bool) -> Self { + ScanConnection { + _mark1: PhantomData, + _mark2: PhantomData, + _mark3: PhantomData, + edges: Vec::new(), + has_previous_page, + has_next_page, + start_cursor: None, + end_cursor: None, + } + } +} + +impl TypeName + for ScanConnection +where + Cursor: CursorType + Send + Sync, + Node: OutputType, + EdgeFields: ObjectType, + Name: ConnectionNameType, + EdgeName: EdgeNameType, + NodesField: NodesFieldSwitcherSealed, +{ + #[inline] + fn type_name() -> Cow<'static, str> { + Name::type_name::().into() + } +} diff --git a/crates/sui-graphql-rpc/src/consistency.rs b/crates/sui-graphql-rpc/src/consistency.rs index a4719e5855cdd..285e6ce8f36ba 100644 --- a/crates/sui-graphql-rpc/src/consistency.rs +++ b/crates/sui-graphql-rpc/src/consistency.rs @@ -7,7 +7,7 @@ use sui_indexer::models::objects::StoredHistoryObject; use crate::raw_query::RawQuery; use crate::types::available_range::AvailableRange; -use crate::types::cursor::{JsonCursor, Page}; +use crate::types::cursor::{JsonCursor, Page, ScanLimited}; use crate::types::object::Cursor; use crate::{filter, query}; @@ -59,6 +59,10 @@ impl Checkpointed for JsonCursor { } } +impl ScanLimited for JsonCursor {} + +impl ScanLimited for JsonCursor {} + /// Constructs a `RawQuery` against the `objects_snapshot` and `objects_history` table to fetch /// objects that satisfy some filtering criteria `filter_fn` within the provided checkpoint `range`. /// The `objects_snapshot` table contains the latest versions of objects up to a checkpoint sequence diff --git a/crates/sui-graphql-rpc/src/data/pg.rs b/crates/sui-graphql-rpc/src/data/pg.rs index 71b45248a2e2f..cdf57f7d542b9 100644 --- a/crates/sui-graphql-rpc/src/data/pg.rs +++ b/crates/sui-graphql-rpc/src/data/pg.rs @@ -1,8 +1,6 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::time::Instant; - use super::QueryExecutor; use crate::{config::Limits, error::Error, metrics::Metrics}; use async_trait::async_trait; @@ -12,6 +10,8 @@ use diesel::{ query_dsl::LoadQuery, QueryResult, RunQueryDsl, }; +use std::fmt; +use std::time::Instant; use sui_indexer::indexer_reader::IndexerReader; use sui_indexer::{run_query_async, run_query_repeatable_async, spawn_read_only_blocking}; @@ -29,6 +29,8 @@ pub(crate) struct PgConnection<'c> { conn: &'c mut diesel::PgConnection, } +pub(crate) struct ByteaLiteral<'a>(pub &'a [u8]); + impl PgExecutor { pub(crate) fn new( inner: IndexerReader, @@ -118,6 +120,16 @@ impl<'c> super::DbConnection for PgConnection<'c> { } } +impl fmt::Display for ByteaLiteral<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "'\\x{}'::bytea", hex::encode(self.0)) + } +} + +pub(crate) fn bytea_literal(slice: &[u8]) -> ByteaLiteral<'_> { + ByteaLiteral(slice) +} + /// Support for calculating estimated query cost using EXPLAIN and then logging it. mod query_cost { use super::*; diff --git a/crates/sui-graphql-rpc/src/lib.rs b/crates/sui-graphql-rpc/src/lib.rs index c2f7cd3f8687b..e299aa241eec9 100644 --- a/crates/sui-graphql-rpc/src/lib.rs +++ b/crates/sui-graphql-rpc/src/lib.rs @@ -4,6 +4,7 @@ pub use sui_graphql_rpc_client as client; pub mod commands; pub mod config; +pub(crate) mod connection; pub(crate) mod consistency; pub mod context_data; pub(crate) mod data; diff --git a/crates/sui-graphql-rpc/src/raw_query.rs b/crates/sui-graphql-rpc/src/raw_query.rs index e372d478fb22f..55fed2d96c2f3 100644 --- a/crates/sui-graphql-rpc/src/raw_query.rs +++ b/crates/sui-graphql-rpc/src/raw_query.rs @@ -179,6 +179,27 @@ macro_rules! or_filter { }}; } +/// Accepts two `RawQuery` instances and a third expression consisting of which columns to join on. +#[macro_export] +macro_rules! inner_join { + ($lhs:expr, $alias:expr => $rhs_query:expr, using: [$using:expr $(, $more_using:expr)*]) => {{ + use $crate::raw_query::RawQuery; + + let (lhs_sql, mut binds) = $lhs.finish(); + let (rhs_sql, rhs_binds) = $rhs_query.finish(); + + binds.extend(rhs_binds); + + let sql = format!( + "{lhs_sql} INNER JOIN ({rhs_sql}) AS {} USING ({})", + $alias, + stringify!($using $(, $more_using)*), + ); + + RawQuery::new(sql, binds) + }}; +} + /// Accepts a `SELECT FROM` format string and optional subqueries. If subqueries are provided, there /// should be curly braces `{}` in the format string to interpolate each subquery's sql string into. /// Concatenates subqueries to the `SELECT FROM` clause, and creates a new `RawQuery` from the @@ -193,7 +214,8 @@ macro_rules! query { }; // Expects a select clause and one or more subqueries. The select clause should contain curly - // braces for subqueries to be interpolated into. + // braces for subqueries to be interpolated into. Use when the subqueries can be aliased + // directly in the select statement. ($select:expr $(,$subquery:expr)+) => {{ use $crate::raw_query::RawQuery; let mut binds = vec![]; diff --git a/crates/sui-graphql-rpc/src/types/address.rs b/crates/sui-graphql-rpc/src/types/address.rs index 6ba4dd390c79d..abd07bc9701e9 100644 --- a/crates/sui-graphql-rpc/src/types/address.rs +++ b/crates/sui-graphql-rpc/src/types/address.rs @@ -1,6 +1,8 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use crate::connection::ScanConnection; + use super::{ balance::{self, Balance}, coin::Coin, @@ -135,6 +137,25 @@ impl Address { /// Similar behavior to the `transactionBlocks` in Query but supporting the additional /// `AddressTransactionBlockRelationship` filter, which defaults to `SIGN`. + /// + /// `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + /// results. It is required for queries that apply more than two complex filters (on function, + /// kind, sender, recipient, input object, changed object, or ids), and can be at most + /// `serviceConfig.maxScanLimit`. + /// + /// When the scan limit is reached the page will be returned even if it has fewer than `first` + /// results when paginating forward (`last` when paginating backwards). If there are more + /// transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + /// `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + /// transaction that was scanned as opposed to the last (or first) transaction in the page. + /// + /// Requesting the next (or previous) page after this cursor will resume the search, scanning + /// the next `scanLimit` many transactions in the direction of pagination, and so on until all + /// transactions in the scanning range have been visited. + /// + /// By default, the scanning range includes all transactions known to GraphQL, but it can be + /// restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + /// `afterCheckpoint` and `atCheckpoint` filters. async fn transaction_blocks( &self, ctx: &Context<'_>, @@ -144,7 +165,8 @@ impl Address { before: Option, relation: Option, filter: Option, - ) -> Result> { + scan_limit: Option, + ) -> Result> { use AddressTransactionBlockRelationship as R; let page = Page::from_params(ctx.data_unchecked(), first, after, last, before)?; @@ -160,17 +182,12 @@ impl Address { ..Default::default() }, }) else { - return Ok(Connection::new(false, false)); + return Ok(ScanConnection::new(false, false)); }; - TransactionBlock::paginate( - ctx.data_unchecked(), - page, - filter, - self.checkpoint_viewed_at, - ) - .await - .extend() + TransactionBlock::paginate(ctx, page, filter, self.checkpoint_viewed_at, scan_limit) + .await + .extend() } } diff --git a/crates/sui-graphql-rpc/src/types/balance.rs b/crates/sui-graphql-rpc/src/types/balance.rs index 8e4d199df0be0..57eb83935d407 100644 --- a/crates/sui-graphql-rpc/src/types/balance.rs +++ b/crates/sui-graphql-rpc/src/types/balance.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use super::available_range::AvailableRange; -use super::cursor::{self, Page, RawPaginated, Target}; +use super::cursor::{self, Page, RawPaginated, ScanLimited, Target}; use super::uint53::UInt53; use super::{big_int::BigInt, move_type::MoveType, sui_address::SuiAddress}; use crate::consistency::Checkpointed; @@ -161,6 +161,8 @@ impl Checkpointed for Cursor { } } +impl ScanLimited for Cursor {} + impl TryFrom for Balance { type Error = Error; diff --git a/crates/sui-graphql-rpc/src/types/checkpoint.rs b/crates/sui-graphql-rpc/src/types/checkpoint.rs index f59ad8c46ae17..852c492967f21 100644 --- a/crates/sui-graphql-rpc/src/types/checkpoint.rs +++ b/crates/sui-graphql-rpc/src/types/checkpoint.rs @@ -5,7 +5,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap}; use super::{ base64::Base64, - cursor::{self, Page, Paginated, Target}, + cursor::{self, Page, Paginated, ScanLimited, Target}, date_time::DateTime, digest::Digest, epoch::Epoch, @@ -13,7 +13,7 @@ use super::{ transaction_block::{self, TransactionBlock, TransactionBlockFilter}, uint53::UInt53, }; -use crate::consistency::Checkpointed; +use crate::{connection::ScanConnection, consistency::Checkpointed}; use crate::{ data::{self, Conn, DataLoader, Db, DbConnection, QueryExecutor}, error::Error, @@ -144,6 +144,23 @@ impl Checkpoint { } /// Transactions in this checkpoint. + /// + /// `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + /// results. It is required for queries that apply more than two complex filters (on function, + /// kind, sender, recipient, input object, changed object, or ids), and can be at most + /// `serviceConfig.maxScanLimit`. + /// + /// When the scan limit is reached the page will be returned even if it has fewer than `first` + /// results when paginating forward (`last` when paginating backwards). If there are more + /// transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + /// `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + /// transaction that was scanned as opposed to the last (or first) transaction in the page. + /// + /// Requesting the next (or previous) page after this cursor will resume the search, scanning + /// the next `scanLimit` many transactions in the direction of pagination, and so on until all + /// transactions in the scanning range have been visited. + /// + /// By default, the scanning range consists of all transactions in this checkpoint. async fn transaction_blocks( &self, ctx: &Context<'_>, @@ -152,7 +169,8 @@ impl Checkpoint { last: Option, before: Option, filter: Option, - ) -> Result> { + scan_limit: Option, + ) -> Result> { let page = Page::from_params(ctx.data_unchecked(), first, after, last, before)?; let Some(filter) = filter @@ -162,17 +180,12 @@ impl Checkpoint { ..Default::default() }) else { - return Ok(Connection::new(false, false)); + return Ok(ScanConnection::new(false, false)); }; - TransactionBlock::paginate( - ctx.data_unchecked(), - page, - filter, - self.checkpoint_viewed_at, - ) - .await - .extend() + TransactionBlock::paginate(ctx, page, filter, self.checkpoint_viewed_at, scan_limit) + .await + .extend() } } @@ -373,6 +386,8 @@ impl Checkpointed for Cursor { } } +impl ScanLimited for Cursor {} + #[async_trait::async_trait] impl Loader for Db { type Value = Checkpoint; diff --git a/crates/sui-graphql-rpc/src/types/coin.rs b/crates/sui-graphql-rpc/src/types/coin.rs index 537c7ca2c44b0..d9654bbe87f10 100644 --- a/crates/sui-graphql-rpc/src/types/coin.rs +++ b/crates/sui-graphql-rpc/src/types/coin.rs @@ -1,6 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use crate::connection::ScanConnection; use crate::consistency::{build_objects_query, View}; use crate::data::{Db, QueryExecutor}; use crate::error::Error; @@ -193,6 +194,25 @@ impl Coin { } /// The transaction blocks that sent objects to this object. + /// + /// `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + /// results. It is required for queries that apply more than two complex filters (on function, + /// kind, sender, recipient, input object, changed object, or ids), and can be at most + /// `serviceConfig.maxScanLimit`. + /// + /// When the scan limit is reached the page will be returned even if it has fewer than `first` + /// results when paginating forward (`last` when paginating backwards). If there are more + /// transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + /// `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + /// transaction that was scanned as opposed to the last (or first) transaction in the page. + /// + /// Requesting the next (or previous) page after this cursor will resume the search, scanning + /// the next `scanLimit` many transactions in the direction of pagination, and so on until all + /// transactions in the scanning range have been visited. + /// + /// By default, the scanning range includes all transactions known to GraphQL, but it can be + /// restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + /// `afterCheckpoint` and `atCheckpoint` filters. pub(crate) async fn received_transaction_blocks( &self, ctx: &Context<'_>, @@ -201,9 +221,10 @@ impl Coin { last: Option, before: Option, filter: Option, - ) -> Result> { + scan_limit: Option, + ) -> Result> { ObjectImpl(&self.super_.super_) - .received_transaction_blocks(ctx, first, after, last, before, filter) + .received_transaction_blocks(ctx, first, after, last, before, filter, scan_limit) .await } diff --git a/crates/sui-graphql-rpc/src/types/coin_metadata.rs b/crates/sui-graphql-rpc/src/types/coin_metadata.rs index bad0545636b84..2052c8b42b03d 100644 --- a/crates/sui-graphql-rpc/src/types/coin_metadata.rs +++ b/crates/sui-graphql-rpc/src/types/coin_metadata.rs @@ -17,6 +17,7 @@ use super::suins_registration::{DomainFormat, SuinsRegistration}; use super::transaction_block::{self, TransactionBlock, TransactionBlockFilter}; use super::type_filter::ExactTypeFilter; use super::uint53::UInt53; +use crate::connection::ScanConnection; use crate::data::Db; use crate::error::Error; use async_graphql::connection::Connection; @@ -182,6 +183,25 @@ impl CoinMetadata { } /// The transaction blocks that sent objects to this object. + /// + /// `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + /// results. It is required for queries that apply more than two complex filters (on function, + /// kind, sender, recipient, input object, changed object, or ids), and can be at most + /// `serviceConfig.maxScanLimit`. + /// + /// When the scan limit is reached the page will be returned even if it has fewer than `first` + /// results when paginating forward (`last` when paginating backwards). If there are more + /// transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + /// `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + /// transaction that was scanned as opposed to the last (or first) transaction in the page. + /// + /// Requesting the next (or previous) page after this cursor will resume the search, scanning + /// the next `scanLimit` many transactions in the direction of pagination, and so on until all + /// transactions in the scanning range have been visited. + /// + /// By default, the scanning range includes all transactions known to GraphQL, but it can be + /// restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + /// `afterCheckpoint` and `atCheckpoint` filters. pub(crate) async fn received_transaction_blocks( &self, ctx: &Context<'_>, @@ -190,9 +210,10 @@ impl CoinMetadata { last: Option, before: Option, filter: Option, - ) -> Result> { + scan_limit: Option, + ) -> Result> { ObjectImpl(&self.super_.super_) - .received_transaction_blocks(ctx, first, after, last, before, filter) + .received_transaction_blocks(ctx, first, after, last, before, filter, scan_limit) .await } diff --git a/crates/sui-graphql-rpc/src/types/cursor.rs b/crates/sui-graphql-rpc/src/types/cursor.rs index 65f7e21673bbb..868bfed21388b 100644 --- a/crates/sui-graphql-rpc/src/types/cursor.rs +++ b/crates/sui-graphql-rpc/src/types/cursor.rs @@ -50,7 +50,7 @@ pub(crate) struct Page { /// Whether the page is extracted from the beginning or the end of the range bounded by the cursors. #[derive(PartialEq, Eq, Debug, Clone, Copy)] -enum End { +pub(crate) enum End { Front, Back, } @@ -101,6 +101,21 @@ pub(crate) trait Target { fn cursor(&self, checkpoint_viewed_at: u64) -> C; } +/// Interface for dealing with cursors that may come from a scan-limit-ed query. +pub(crate) trait ScanLimited: Clone + PartialEq { + /// Whether the cursor was derived from a scan limit. Only applicable to the `startCursor` and + /// `endCursor` returned from a Connection's `PageInfo`, and indicates that the cursor may not + /// have a corresponding node in the result set. + fn is_scan_limited(&self) -> bool { + false + } + + /// Returns a version of the cursor that is not scan limited. + fn unlimited(&self) -> Self { + self.clone() + } +} + impl JsonCursor { pub(crate) fn new(cursor: C) -> Self { JsonCursor(OpaqueCursor(cursor)) @@ -184,6 +199,10 @@ impl Page { pub(crate) fn is_from_front(&self) -> bool { matches!(self.end, End::Front) } + + pub(crate) fn end(&self) -> End { + self.end + } } impl Page @@ -261,7 +280,7 @@ impl Page> { } } -impl Page { +impl Page { /// Treat the cursors of this page as upper- and lowerbound filters for a database `query`. /// Returns two booleans indicating whether there is a previous or next page in the range, /// followed by an iterator of values in the page, fetched from the database. @@ -361,7 +380,9 @@ impl Page { } /// Given the results of a database query, determine whether the result set has a previous and - /// next page and is consistent with the provided cursors. + /// next page and is consistent with the provided cursors. Slightly different logic applies + /// depending on whether the provided cursors stem from either tip of the response, or if they + /// were derived from a scan limit. /// /// Returns two booleans indicating whether there is a previous or next page in the range, /// followed by an iterator of values in the page, fetched from the database. The values @@ -373,7 +394,7 @@ impl Page { results: Vec, ) -> (bool, bool, impl Iterator) where - T: Send + 'static, + T: Target + Send + 'static, { // Detect whether the results imply the existence of a previous or next page. let (prev, next, prefix, suffix) = @@ -386,27 +407,32 @@ impl Page { } // Page drawn from the front, and the cursor for the first element does not match - // `after`. This implies the bound was invalid, so we return an empty result. - (Some(a), Some(f), _, _, End::Front) if f != *a => { + // `after`. If that cursor is not from a scan limit, then it must have appeared in + // the previous page, and should also be at the tip of the current page. This + // absence implies the bound was invalid, so we return an empty result. + (Some(a), Some(f), _, _, End::Front) if f != *a && !a.is_scan_limited() => { return (false, false, vec![].into_iter()); } // Similar to above case, but for back of results. - (_, _, Some(l), Some(b), End::Back) if l != *b => { + (_, _, Some(l), Some(b), End::Back) if l != *b && !b.is_scan_limited() => { return (false, false, vec![].into_iter()); } - // From here onwards, we know that the results are non-empty and if a cursor was - // supplied on the end the page is being drawn from, it was found in the results - // (implying a page follows in that direction). - (after, _, Some(l), before, End::Front) => { - let has_previous_page = after.is_some(); + // From here onwards, we know that the results are non-empty. In the forward + // pagination scenario, the presence of a previous page is determined by whether a + // cursor supplied on the end the page is being drawn from is found in the first + // position. The presence of a next page is determined by whether we have more + // results than the provided limit, and/ or if the end cursor element appears in the + // result set. + (after, Some(f), Some(l), before, End::Front) => { + let has_previous_page = after.is_some_and(|a| a.unlimited() == f); let prefix = has_previous_page as usize; // If results end with the before cursor, we will at least need to trim one element // from the suffix and we trim more off the end if there is more after applying the // limit. - let mut suffix = before.is_some_and(|b| *b == l) as usize; + let mut suffix = before.is_some_and(|b| b.unlimited() == l) as usize; suffix += results.len().saturating_sub(self.limit() + prefix + suffix); let has_next_page = suffix > 0; @@ -414,11 +440,13 @@ impl Page { } // Symmetric to the previous case, but drawing from the back. - (after, Some(f), _, before, End::Back) => { - let has_next_page = before.is_some(); + (after, Some(f), Some(l), before, End::Back) => { + // There is a next page if the last element of the results matches the `before`. + // This last element will get pruned from the result set. + let has_next_page = before.is_some_and(|b| b.unlimited() == l); let suffix = has_next_page as usize; - let mut prefix = after.is_some_and(|a| *a == f) as usize; + let mut prefix = after.is_some_and(|a| a.unlimited() == f) as usize; prefix += results.len().saturating_sub(self.limit() + prefix + suffix); let has_previous_page = prefix > 0; diff --git a/crates/sui-graphql-rpc/src/types/epoch.rs b/crates/sui-graphql-rpc/src/types/epoch.rs index 7ecfa9c7d773e..915493217d1f7 100644 --- a/crates/sui-graphql-rpc/src/types/epoch.rs +++ b/crates/sui-graphql-rpc/src/types/epoch.rs @@ -3,6 +3,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap}; +use crate::connection::ScanConnection; use crate::context_data::db_data_provider::{convert_to_validators, PgManager}; use crate::data::{DataLoader, Db, DbConnection, QueryExecutor}; use crate::error::Error; @@ -229,6 +230,23 @@ impl Epoch { } /// The epoch's corresponding transaction blocks. + /// + /// `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + /// results. It is required for queries that apply more than two complex filters (on function, + /// kind, sender, recipient, input object, changed object, or ids), and can be at most + /// `serviceConfig.maxScanLimit`. + /// + /// When the scan limit is reached the page will be returned even if it has fewer than `first` + /// results when paginating forward (`last` when paginating backwards). If there are more + /// transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + /// `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + /// transaction that was scanned as opposed to the last (or first) transaction in the page. + /// + /// Requesting the next (or previous) page after this cursor will resume the search, scanning + /// the next `scanLimit` many transactions in the direction of pagination, and so on until all + /// transactions in the scanning range have been visited. + /// + /// By default, the scanning range consists of all transactions in this epoch. async fn transaction_blocks( &self, ctx: &Context<'_>, @@ -237,13 +255,15 @@ impl Epoch { last: Option, before: Option, filter: Option, - ) -> Result> { + scan_limit: Option, + ) -> Result> { let page = Page::from_params(ctx.data_unchecked(), first, after, last, before)?; #[allow(clippy::unnecessary_lazy_evaluations)] // rust-lang/rust-clippy#9422 let Some(filter) = filter .unwrap_or_default() .intersect(TransactionBlockFilter { + // If `first_checkpoint_id` is 0, we include the 0th checkpoint by leaving it None after_checkpoint: (self.stored.first_checkpoint_id > 0) .then(|| UInt53::from(self.stored.first_checkpoint_id as u64 - 1)), before_checkpoint: self @@ -253,17 +273,12 @@ impl Epoch { ..Default::default() }) else { - return Ok(Connection::new(false, false)); + return Ok(ScanConnection::new(false, false)); }; - TransactionBlock::paginate( - ctx.data_unchecked(), - page, - filter, - self.checkpoint_viewed_at, - ) - .await - .extend() + TransactionBlock::paginate(ctx, page, filter, self.checkpoint_viewed_at, scan_limit) + .await + .extend() } } diff --git a/crates/sui-graphql-rpc/src/types/event.rs b/crates/sui-graphql-rpc/src/types/event.rs index e195b10844c26..e2555bb0214eb 100644 --- a/crates/sui-graphql-rpc/src/types/event.rs +++ b/crates/sui-graphql-rpc/src/types/event.rs @@ -3,7 +3,7 @@ use std::str::FromStr; -use super::cursor::{self, Page, Paginated, Target}; +use super::cursor::{self, Page, Paginated, ScanLimited, Target}; use super::digest::Digest; use super::type_filter::{ModuleFilter, TypeFilter}; use super::{ @@ -375,3 +375,5 @@ impl Checkpointed for Cursor { self.checkpoint_viewed_at } } + +impl ScanLimited for Cursor {} diff --git a/crates/sui-graphql-rpc/src/types/move_object.rs b/crates/sui-graphql-rpc/src/types/move_object.rs index d41b8dd639420..c5cf724604089 100644 --- a/crates/sui-graphql-rpc/src/types/move_object.rs +++ b/crates/sui-graphql-rpc/src/types/move_object.rs @@ -20,6 +20,7 @@ use super::transaction_block::{self, TransactionBlock, TransactionBlockFilter}; use super::type_filter::ExactTypeFilter; use super::uint53::UInt53; use super::{coin::Coin, object::Object}; +use crate::connection::ScanConnection; use crate::data::Db; use crate::error::Error; use crate::types::stake::StakedSui; @@ -261,6 +262,25 @@ impl MoveObject { } /// The transaction blocks that sent objects to this object. + /// + /// `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + /// results. It is required for queries that apply more than two complex filters (on function, + /// kind, sender, recipient, input object, changed object, or ids), and can be at most + /// `serviceConfig.maxScanLimit`. + /// + /// When the scan limit is reached the page will be returned even if it has fewer than `first` + /// results when paginating forward (`last` when paginating backwards). If there are more + /// transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + /// `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + /// transaction that was scanned as opposed to the last (or first) transaction in the page. + /// + /// Requesting the next (or previous) page after this cursor will resume the search, scanning + /// the next `scanLimit` many transactions in the direction of pagination, and so on until all + /// transactions in the scanning range have been visited. + /// + /// By default, the scanning range includes all transactions known to GraphQL, but it can be + /// restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + /// `afterCheckpoint` and `atCheckpoint` filters. pub(crate) async fn received_transaction_blocks( &self, ctx: &Context<'_>, @@ -269,9 +289,10 @@ impl MoveObject { last: Option, before: Option, filter: Option, - ) -> Result> { + scan_limit: Option, + ) -> Result> { ObjectImpl(&self.super_) - .received_transaction_blocks(ctx, first, after, last, before, filter) + .received_transaction_blocks(ctx, first, after, last, before, filter, scan_limit) .await } diff --git a/crates/sui-graphql-rpc/src/types/move_package.rs b/crates/sui-graphql-rpc/src/types/move_package.rs index bb92ba1e599a3..a85bc75d8a661 100644 --- a/crates/sui-graphql-rpc/src/types/move_package.rs +++ b/crates/sui-graphql-rpc/src/types/move_package.rs @@ -7,7 +7,7 @@ use super::balance::{self, Balance}; use super::base64::Base64; use super::big_int::BigInt; use super::coin::Coin; -use super::cursor::{BcsCursor, JsonCursor, Page, RawPaginated, Target}; +use super::cursor::{BcsCursor, JsonCursor, Page, RawPaginated, ScanLimited, Target}; use super::move_module::MoveModule; use super::move_object::MoveObject; use super::object::{self, Object, ObjectFilter, ObjectImpl, ObjectOwner, ObjectStatus}; @@ -18,6 +18,7 @@ use super::suins_registration::{DomainFormat, SuinsRegistration}; use super::transaction_block::{self, TransactionBlock, TransactionBlockFilter}; use super::type_filter::ExactTypeFilter; use super::uint53::UInt53; +use crate::connection::ScanConnection; use crate::consistency::{Checkpointed, ConsistentNamedCursor}; use crate::data::{DataLoader, Db, DbConnection, QueryExecutor}; use crate::error::Error; @@ -332,6 +333,25 @@ impl MovePackage { /// The transaction blocks that sent objects to this package. /// /// Note that objects that have been sent to a package become inaccessible. + /// + /// `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + /// results. It is required for queries that apply more than two complex filters (on function, + /// kind, sender, recipient, input object, changed object, or ids), and can be at most + /// `serviceConfig.maxScanLimit`. + /// + /// When the scan limit is reached the page will be returned even if it has fewer than `first` + /// results when paginating forward (`last` when paginating backwards). If there are more + /// transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + /// `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + /// transaction that was scanned as opposed to the last (or first) transaction in the page. + /// + /// Requesting the next (or previous) page after this cursor will resume the search, scanning + /// the next `scanLimit` many transactions in the direction of pagination, and so on until all + /// transactions in the scanning range have been visited. + /// + /// By default, the scanning range includes all transactions known to GraphQL, but it can be + /// restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + /// `afterCheckpoint` and `atCheckpoint` filters. pub(crate) async fn received_transaction_blocks( &self, ctx: &Context<'_>, @@ -340,9 +360,10 @@ impl MovePackage { last: Option, before: Option, filter: Option, - ) -> Result> { + scan_limit: Option, + ) -> Result> { ObjectImpl(&self.super_) - .received_transaction_blocks(ctx, first, after, last, before, filter) + .received_transaction_blocks(ctx, first, after, last, before, filter, scan_limit) .await } @@ -859,6 +880,8 @@ impl Target for StoredHistoryPackage { } } +impl ScanLimited for BcsCursor {} + #[async_trait::async_trait] impl Loader for Db { type Value = SuiAddress; diff --git a/crates/sui-graphql-rpc/src/types/object.rs b/crates/sui-graphql-rpc/src/types/object.rs index 66887aa7be54c..5cb49d63ff03c 100644 --- a/crates/sui-graphql-rpc/src/types/object.rs +++ b/crates/sui-graphql-rpc/src/types/object.rs @@ -9,7 +9,7 @@ use super::balance::{self, Balance}; use super::big_int::BigInt; use super::coin::Coin; use super::coin_metadata::CoinMetadata; -use super::cursor::{self, Page, RawPaginated, Target}; +use super::cursor::{self, Page, RawPaginated, ScanLimited, Target}; use super::digest::Digest; use super::display::{Display, DisplayEntry}; use super::dynamic_field::{DynamicField, DynamicFieldName}; @@ -24,6 +24,7 @@ use super::transaction_block::TransactionBlockFilter; use super::type_filter::{ExactTypeFilter, TypeFilter}; use super::uint53::UInt53; use super::{owner::Owner, sui_address::SuiAddress, transaction_block::TransactionBlock}; +use crate::connection::ScanConnection; use crate::consistency::{build_objects_query, Checkpointed, View}; use crate::data::package_resolver::PackageResolver; use crate::data::{DataLoader, Db, DbConnection, QueryExecutor}; @@ -261,7 +262,8 @@ pub(crate) struct HistoricalObjectCursor { arg(name = "last", ty = "Option"), arg(name = "before", ty = "Option"), arg(name = "filter", ty = "Option"), - ty = "Connection", + arg(name = "scan_limit", ty = "Option"), + ty = "ScanConnection", desc = "The transaction blocks that sent objects to this object." ), field( @@ -452,6 +454,25 @@ impl Object { } /// The transaction blocks that sent objects to this object. + /// + /// `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + /// results. It is required for queries that apply more than two complex filters (on function, + /// kind, sender, recipient, input object, changed object, or ids), and can be at most + /// `serviceConfig.maxScanLimit`. + /// + /// When the scan limit is reached the page will be returned even if it has fewer than `first` + /// results when paginating forward (`last` when paginating backwards). If there are more + /// transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + /// `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + /// transaction that was scanned as opposed to the last (or first) transaction in the page. + /// + /// Requesting the next (or previous) page after this cursor will resume the search, scanning + /// the next `scanLimit` many transactions in the direction of pagination, and so on until all + /// transactions in the scanning range have been visited. + /// + /// By default, the scanning range includes all transactions known to GraphQL, but it can be + /// restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + /// `afterCheckpoint` and `atCheckpoint` filters. pub(crate) async fn received_transaction_blocks( &self, ctx: &Context<'_>, @@ -460,9 +481,10 @@ impl Object { last: Option, before: Option, filter: Option, - ) -> Result> { + scan_limit: Option, + ) -> Result> { ObjectImpl(self) - .received_transaction_blocks(ctx, first, after, last, before, filter) + .received_transaction_blocks(ctx, first, after, last, before, filter, scan_limit) .await } @@ -621,7 +643,8 @@ impl ObjectImpl<'_> { last: Option, before: Option, filter: Option, - ) -> Result> { + scan_limit: Option, + ) -> Result> { let page = Page::from_params(ctx.data_unchecked(), first, after, last, before)?; let Some(filter) = filter @@ -631,17 +654,12 @@ impl ObjectImpl<'_> { ..Default::default() }) else { - return Ok(Connection::new(false, false)); + return Ok(ScanConnection::new(false, false)); }; - TransactionBlock::paginate( - ctx.data_unchecked(), - page, - filter, - self.0.checkpoint_viewed_at, - ) - .await - .extend() + TransactionBlock::paginate(ctx, page, filter, self.0.checkpoint_viewed_at, scan_limit) + .await + .extend() } pub(crate) async fn bcs(&self) -> Result> { @@ -1084,7 +1102,7 @@ impl ObjectFilter { hex::encode(id.into_vec()) ) .unwrap(); - prefix = ","; + prefix = ", "; } inner.push(')'); query = or_filter!(query, inner); @@ -1158,6 +1176,8 @@ impl Checkpointed for Cursor { } } +impl ScanLimited for Cursor {} + impl RawPaginated for StoredHistoryObject { fn filter_ge(cursor: &Cursor, query: RawQuery) -> RawQuery { filter!( diff --git a/crates/sui-graphql-rpc/src/types/query.rs b/crates/sui-graphql-rpc/src/types/query.rs index 07b51ef649d88..f403fbf8657b5 100644 --- a/crates/sui-graphql-rpc/src/types/query.rs +++ b/crates/sui-graphql-rpc/src/types/query.rs @@ -39,6 +39,7 @@ use super::{ transaction_metadata::TransactionMetadata, type_filter::ExactTypeFilter, }; +use crate::connection::ScanConnection; use crate::server::watermark_task::Watermark; use crate::types::base64::Base64 as GraphQLBase64; use crate::types::zklogin_verify_signature::verify_zklogin_signature; @@ -369,6 +370,25 @@ impl Query { } /// The transaction blocks that exist in the network. + /// + /// `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + /// results. It is required for queries that apply more than two complex filters (on function, + /// kind, sender, recipient, input object, changed object, or ids), and can be at most + /// `serviceConfig.maxScanLimit`. + /// + /// When the scan limit is reached the page will be returned even if it has fewer than `first` + /// results when paginating forward (`last` when paginating backwards). If there are more + /// transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + /// `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + /// transaction that was scanned as opposed to the last (or first) transaction in the page. + /// + /// Requesting the next (or previous) page after this cursor will resume the search, scanning + /// the next `scanLimit` many transactions in the direction of pagination, and so on until all + /// transactions in the scanning range have been visited. + /// + /// By default, the scanning range includes all transactions known to GraphQL, but it can be + /// restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + /// `afterCheckpoint` and `atCheckpoint` filters. async fn transaction_blocks( &self, ctx: &Context<'_>, @@ -377,15 +397,18 @@ impl Query { last: Option, before: Option, filter: Option, - ) -> Result> { + scan_limit: Option, + ) -> Result> { let Watermark { checkpoint, .. } = *ctx.data()?; let page = Page::from_params(ctx.data_unchecked(), first, after, last, before)?; + TransactionBlock::paginate( - ctx.data_unchecked(), + ctx, page, filter.unwrap_or_default(), checkpoint, + scan_limit, ) .await .extend() diff --git a/crates/sui-graphql-rpc/src/types/stake.rs b/crates/sui-graphql-rpc/src/types/stake.rs index 763741c67b747..75c0a07dabf5a 100644 --- a/crates/sui-graphql-rpc/src/types/stake.rs +++ b/crates/sui-graphql-rpc/src/types/stake.rs @@ -1,6 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use crate::connection::ScanConnection; use crate::error::Error; use crate::{context_data::db_data_provider::PgManager, data::Db}; @@ -201,6 +202,25 @@ impl StakedSui { } /// The transaction blocks that sent objects to this object. + /// + /// `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + /// results. It is required for queries that apply more than two complex filters (on function, + /// kind, sender, recipient, input object, changed object, or ids), and can be at most + /// `serviceConfig.maxScanLimit`. + /// + /// When the scan limit is reached the page will be returned even if it has fewer than `first` + /// results when paginating forward (`last` when paginating backwards). If there are more + /// transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + /// `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + /// transaction that was scanned as opposed to the last (or first) transaction in the page. + /// + /// Requesting the next (or previous) page after this cursor will resume the search, scanning + /// the next `scanLimit` many transactions in the direction of pagination, and so on until all + /// transactions in the scanning range have been visited. + /// + /// By default, the scanning range includes all transactions known to GraphQL, but it can be + /// restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + /// `afterCheckpoint` and `atCheckpoint` filters. pub(crate) async fn received_transaction_blocks( &self, ctx: &Context<'_>, @@ -209,9 +229,10 @@ impl StakedSui { last: Option, before: Option, filter: Option, - ) -> Result> { + scan_limit: Option, + ) -> Result> { ObjectImpl(&self.super_.super_) - .received_transaction_blocks(ctx, first, after, last, before, filter) + .received_transaction_blocks(ctx, first, after, last, before, filter, scan_limit) .await } diff --git a/crates/sui-graphql-rpc/src/types/suins_registration.rs b/crates/sui-graphql-rpc/src/types/suins_registration.rs index ed5337cb5d84f..e7391a258346e 100644 --- a/crates/sui-graphql-rpc/src/types/suins_registration.rs +++ b/crates/sui-graphql-rpc/src/types/suins_registration.rs @@ -25,6 +25,7 @@ use super::{ uint53::UInt53, }; use crate::{ + connection::ScanConnection, consistency::{build_objects_query, View}, data::{Db, DbConnection, QueryExecutor}, error::Error, @@ -238,6 +239,25 @@ impl SuinsRegistration { } /// The transaction blocks that sent objects to this object. + /// + /// `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + /// results. It is required for queries that apply more than two complex filters (on function, + /// kind, sender, recipient, input object, changed object, or ids), and can be at most + /// `serviceConfig.maxScanLimit`. + /// + /// When the scan limit is reached the page will be returned even if it has fewer than `first` + /// results when paginating forward (`last` when paginating backwards). If there are more + /// transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + /// `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + /// transaction that was scanned as opposed to the last (or first) transaction in the page. + /// + /// Requesting the next (or previous) page after this cursor will resume the search, scanning + /// the next `scanLimit` many transactions in the direction of pagination, and so on until all + /// transactions in the scanning range have been visited. + /// + /// By default, the scanning range includes all transactions known to GraphQL, but it can be + /// restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + /// `afterCheckpoint` and `atCheckpoint` filters. pub(crate) async fn received_transaction_blocks( &self, ctx: &Context<'_>, @@ -246,9 +266,10 @@ impl SuinsRegistration { last: Option, before: Option, filter: Option, - ) -> Result> { + scan_limit: Option, + ) -> Result> { ObjectImpl(&self.super_.super_) - .received_transaction_blocks(ctx, first, after, last, before, filter) + .received_transaction_blocks(ctx, first, after, last, before, filter, scan_limit) .await } diff --git a/crates/sui-graphql-rpc/src/types/transaction_block/cursor.rs b/crates/sui-graphql-rpc/src/types/transaction_block/cursor.rs new file mode 100644 index 0000000000000..56a82609bda1e --- /dev/null +++ b/crates/sui-graphql-rpc/src/types/transaction_block/cursor.rs @@ -0,0 +1,173 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + consistency::Checkpointed, + filter, + raw_query::RawQuery, + types::cursor::{self, Paginated, RawPaginated, ScanLimited, Target}, +}; +use diesel::{ + backend::Backend, + deserialize::{self, FromSql, QueryableByName}, + row::NamedRow, + ExpressionMethods, QueryDsl, +}; +use serde::{Deserialize, Serialize}; +use sui_indexer::{models::transactions::StoredTransaction, schema::transactions}; + +use super::Query; + +pub(crate) type Cursor = cursor::JsonCursor; + +/// The cursor returned for each `TransactionBlock` 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 TransactionBlockCursor { + /// The checkpoint sequence number this was viewed at. + #[serde(rename = "c")] + pub checkpoint_viewed_at: u64, + #[serde(rename = "t")] + pub tx_sequence_number: u64, + /// Whether the cursor was derived from a `scan_limit`. Only applicable to the `startCursor` and + /// `endCursor` returned from a Connection's `PageInfo`, and indicates that the cursor may not + /// have a corresponding node in the result set. + #[serde(rename = "i")] + pub is_scan_limited: bool, +} + +/// Results from raw queries in Diesel can only be deserialized into structs that implements +/// `QueryableByName`. This struct is used to represent a row of `tx_sequence_number` returned from +/// subqueries against tx lookup tables. +#[derive(Clone, Debug)] +pub struct TxLookup { + pub tx_sequence_number: i64, +} + +impl Checkpointed for Cursor { + fn checkpoint_viewed_at(&self) -> u64 { + self.checkpoint_viewed_at + } +} + +impl ScanLimited for Cursor { + fn is_scan_limited(&self) -> bool { + self.is_scan_limited + } + + fn unlimited(&self) -> Self { + Cursor::new(TransactionBlockCursor { + is_scan_limited: false, + tx_sequence_number: self.tx_sequence_number, + checkpoint_viewed_at: self.checkpoint_viewed_at, + }) + } +} + +impl Paginated for StoredTransaction { + type Source = transactions::table; + + fn filter_ge(cursor: &Cursor, query: Query) -> Query { + query.filter(transactions::dsl::tx_sequence_number.ge(cursor.tx_sequence_number as i64)) + } + + fn filter_le(cursor: &Cursor, query: Query) -> Query { + query.filter(transactions::dsl::tx_sequence_number.le(cursor.tx_sequence_number as i64)) + } + + fn order(asc: bool, query: Query) -> Query { + use transactions::dsl; + if asc { + query.order_by(dsl::tx_sequence_number.asc()) + } else { + query.order_by(dsl::tx_sequence_number.desc()) + } + } +} + +impl Target for StoredTransaction { + fn cursor(&self, checkpoint_viewed_at: u64) -> Cursor { + Cursor::new(TransactionBlockCursor { + tx_sequence_number: self.tx_sequence_number as u64, + checkpoint_viewed_at, + is_scan_limited: false, + }) + } +} + +impl RawPaginated for StoredTransaction { + fn filter_ge(cursor: &Cursor, query: RawQuery) -> RawQuery { + filter!( + query, + format!("tx_sequence_number >= {}", cursor.tx_sequence_number) + ) + } + + fn filter_le(cursor: &Cursor, query: RawQuery) -> RawQuery { + filter!( + query, + format!("tx_sequence_number <= {}", cursor.tx_sequence_number) + ) + } + + fn order(asc: bool, query: RawQuery) -> RawQuery { + if asc { + query.order_by("tx_sequence_number ASC") + } else { + query.order_by("tx_sequence_number DESC") + } + } +} + +impl Target for TxLookup { + fn cursor(&self, checkpoint_viewed_at: u64) -> Cursor { + Cursor::new(TransactionBlockCursor { + tx_sequence_number: self.tx_sequence_number as u64, + checkpoint_viewed_at, + is_scan_limited: false, + }) + } +} + +impl RawPaginated for TxLookup { + fn filter_ge(cursor: &Cursor, query: RawQuery) -> RawQuery { + filter!( + query, + format!("tx_sequence_number >= {}", cursor.tx_sequence_number) + ) + } + + fn filter_le(cursor: &Cursor, query: RawQuery) -> RawQuery { + filter!( + query, + format!("tx_sequence_number <= {}", cursor.tx_sequence_number) + ) + } + + fn order(asc: bool, query: RawQuery) -> RawQuery { + if asc { + query.order_by("tx_sequence_number ASC") + } else { + query.order_by("tx_sequence_number DESC") + } + } +} + +/// `sql_query` raw queries require `QueryableByName`. The default implementation looks for a table +/// based on the struct name, and it also expects the struct's fields to reflect the table's +/// columns. We can override this behavior by implementing `QueryableByName` for our struct. For +/// `TxBounds`, its fields are derived from `checkpoints`, so we can't leverage the default +/// implementation directly. +impl QueryableByName for TxLookup +where + DB: Backend, + i64: FromSql, +{ + fn build<'a>(row: &impl NamedRow<'a, DB>) -> deserialize::Result { + let tx_sequence_number = + NamedRow::get::(row, "tx_sequence_number")?; + + Ok(Self { tx_sequence_number }) + } +} diff --git a/crates/sui-graphql-rpc/src/types/transaction_block/filter.rs b/crates/sui-graphql-rpc/src/types/transaction_block/filter.rs new file mode 100644 index 0000000000000..29c104ac9484c --- /dev/null +++ b/crates/sui-graphql-rpc/src/types/transaction_block/filter.rs @@ -0,0 +1,130 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use super::TransactionBlockKindInput; +use crate::types::{digest::Digest, sui_address::SuiAddress, type_filter::FqNameFilter}; +use crate::types::{intersect, uint53::UInt53}; +use async_graphql::InputObject; +use std::collections::BTreeSet; +use sui_types::base_types::SuiAddress as NativeSuiAddress; + +#[derive(InputObject, Debug, Default, Clone)] +pub(crate) struct TransactionBlockFilter { + pub function: Option, + + /// An input filter selecting for either system or programmable transactions. + pub kind: Option, + pub after_checkpoint: Option, + pub at_checkpoint: Option, + pub before_checkpoint: Option, + + pub sign_address: Option, + pub recv_address: Option, + + pub input_object: Option, + pub changed_object: Option, + + pub transaction_ids: Option>, +} + +impl TransactionBlockFilter { + /// Try to create a filter whose results are the intersection of transaction blocks in `self`'s + /// results and transaction blocks in `other`'s results. This may not be possible if the + /// resulting filter is inconsistent in some way (e.g. a filter that requires one field to be + /// two different values simultaneously). + pub(crate) fn intersect(self, other: Self) -> Option { + macro_rules! intersect { + ($field:ident, $body:expr) => { + intersect::field(self.$field, other.$field, $body) + }; + } + + Some(Self { + function: intersect!(function, FqNameFilter::intersect)?, + kind: intersect!(kind, intersect::by_eq)?, + + after_checkpoint: intersect!(after_checkpoint, intersect::by_max)?, + at_checkpoint: intersect!(at_checkpoint, intersect::by_eq)?, + before_checkpoint: intersect!(before_checkpoint, intersect::by_min)?, + + sign_address: intersect!(sign_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)?, + + transaction_ids: intersect!(transaction_ids, |a, b| { + let a = BTreeSet::from_iter(a.into_iter()); + let b = BTreeSet::from_iter(b.into_iter()); + Some(a.intersection(&b).cloned().collect()) + })?, + }) + } + + /// Most filter conditions require a scan limit if used in tandem with other filters. The + /// exception to this is sender and checkpoint, since sender is denormalized on all tables, and + /// the corresponding tx range can be determined for a checkpoint. + pub(crate) fn requires_scan_limit(&self) -> bool { + [ + self.function.is_some(), + self.kind.is_some(), + self.recv_address.is_some(), + self.input_object.is_some(), + self.changed_object.is_some(), + self.transaction_ids.is_some(), + ] + .into_iter() + .filter(|is_set| *is_set) + .count() + > 1 + } + + /// If we don't query a lookup table that has a denormalized sender column, we need to + /// explicitly sp + pub(crate) fn explicit_sender(&self) -> Option { + if self.function.is_none() + && self.kind.is_none() + && self.recv_address.is_none() + && self.input_object.is_none() + && self.changed_object.is_none() + { + self.sign_address + } else { + None + } + } + + /// A TransactionBlockFilter is considered not to have any filters if no filters are specified, + /// or if the only filters are on `checkpoint`. + pub(crate) fn has_filters(&self) -> bool { + self.function.is_some() + || self.kind.is_some() + || self.sign_address.is_some() + || self.recv_address.is_some() + || self.input_object.is_some() + || self.changed_object.is_some() + || self.transaction_ids.is_some() + } + + pub(crate) fn is_empty(&self) -> bool { + self.before_checkpoint == Some(UInt53::from(0)) + || matches!( + (self.after_checkpoint, self.before_checkpoint), + (Some(after), Some(before)) if after >= before + ) + || matches!( + (self.after_checkpoint, self.at_checkpoint), + (Some(after), Some(at)) if after >= at + ) + || matches!( + (self.at_checkpoint, self.before_checkpoint), + (Some(at), Some(before)) if at >= before + ) + // If SystemTx, sender if specified must be 0x0. Conversely, if sender is 0x0, kind must be SystemTx. + || matches!( + (self.kind, self.sign_address), + (Some(kind), Some(signer)) + if (kind == TransactionBlockKindInput::SystemTx) + != (signer == SuiAddress::from(NativeSuiAddress::ZERO)) + ) + } +} diff --git a/crates/sui-graphql-rpc/src/types/transaction_block.rs b/crates/sui-graphql-rpc/src/types/transaction_block/mod.rs similarity index 63% rename from crates/sui-graphql-rpc/src/types/transaction_block.rs rename to crates/sui-graphql-rpc/src/types/transaction_block/mod.rs index c33da8133001a..1573fe97cfeab 100644 --- a/crates/sui-graphql-rpc/src/types/transaction_block.rs +++ b/crates/sui-graphql-rpc/src/types/transaction_block/mod.rs @@ -1,22 +1,34 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::collections::{BTreeMap, BTreeSet, HashMap}; - -use async_graphql::{ - connection::{Connection, CursorType, Edge}, - dataloader::Loader, - *, +use super::{ + address::Address, + base64::Base64, + cursor::{Page, Target}, + digest::Digest, + epoch::Epoch, + gas::GasInput, + sui_address::SuiAddress, + transaction_block_effects::{TransactionBlockEffects, TransactionBlockEffectsKind}, + transaction_block_kind::TransactionBlockKind, }; +use crate::{ + config::ServiceConfig, + connection::ScanConnection, + data::{self, DataLoader, Db, DbConnection, QueryExecutor}, + error::Error, + server::watermark_task::Watermark, +}; +use async_graphql::{connection::CursorType, dataloader::Loader, *}; +use connection::Edge; +use cursor::TxLookup; use diesel::{ExpressionMethods, JoinOnDsl, QueryDsl, SelectableHelper}; use fastcrypto::encoding::{Base58, Encoding}; use serde::{Deserialize, Serialize}; +use std::collections::{BTreeMap, HashMap}; use sui_indexer::{ models::transactions::StoredTransaction, - schema::{ - transactions, tx_calls_fun, tx_changed_objects, tx_digests, tx_input_objects, - tx_recipients, tx_senders, - }, + schema::{transactions, tx_digests}, }; use sui_types::{ base_types::SuiAddress as NativeSuiAddress, @@ -29,27 +41,13 @@ use sui_types::{ }, }; -use crate::{ - consistency::Checkpointed, - data::{self, DataLoader, Db, DbConnection, QueryExecutor}, - error::Error, - server::watermark_task::Watermark, - types::intersect, -}; +mod cursor; +mod filter; +mod tx_lookups; -use super::{ - address::Address, - base64::Base64, - cursor::{self, Page, Paginated, Target}, - digest::Digest, - epoch::Epoch, - gas::GasInput, - sui_address::SuiAddress, - transaction_block_effects::{TransactionBlockEffects, TransactionBlockEffectsKind}, - transaction_block_kind::TransactionBlockKind, - type_filter::FqNameFilter, - uint53::UInt53, -}; +pub(crate) use cursor::Cursor; +pub(crate) use filter::TransactionBlockFilter; +pub(crate) use tx_lookups::{subqueries, TxBounds}; /// Wraps the actual transaction block data with the checkpoint sequence number at which the data /// was viewed, for consistent results on paginating through and resolving nested types. @@ -94,26 +92,6 @@ pub(crate) enum TransactionBlockKindInput { ProgrammableTx = 1, } -#[derive(InputObject, Debug, Default, Clone)] -pub(crate) struct TransactionBlockFilter { - pub function: Option, - - /// An input filter selecting for either system or programmable transactions. - pub kind: Option, - pub after_checkpoint: Option, - pub at_checkpoint: Option, - pub before_checkpoint: Option, - - pub sign_address: Option, - pub recv_address: Option, - - pub input_object: Option, - pub changed_object: Option, - - pub transaction_ids: Option>, -} - -pub(crate) type Cursor = cursor::JsonCursor; type Query = data::Query; /// The cursor returned for each `TransactionBlock` in a connection's page of results. The @@ -298,104 +276,132 @@ 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 + /// limiting how many transactions to scan through before applying filters, and also affects + /// pagination behavior. pub(crate) async fn paginate( - db: &Db, + ctx: &Context<'_>, page: Page, filter: TransactionBlockFilter, checkpoint_viewed_at: u64, - ) -> Result, Error> { - use transactions as tx; + scan_limit: Option, + ) -> Result, Error> { + let limits = &ctx.data_unchecked::().limits; + + // If the caller has provided some arbitrary combination of `function`, `kind`, + // `recvAddress`, `inputObject`, or `changedObject`, we require setting a `scanLimit`. + if let Some(scan_limit) = scan_limit { + if scan_limit > limits.max_scan_limit as u64 { + return Err(Error::Client(format!( + "Scan limit exceeds max limit of '{}'", + limits.max_scan_limit + ))); + } + } else if filter.requires_scan_limit() { + return Err(Error::Client( + "A scan limit must be specified for the given filter combination".to_string(), + )); + } + + if let Some(tx_ids) = &filter.transaction_ids { + if tx_ids.len() > limits.max_transaction_ids as usize { + return Err(Error::Client(format!( + "Transaction IDs exceed max limit of '{}'", + limits.max_transaction_ids + ))); + } + } + + // If page size or scan limit is 0, we want to standardize behavior by returning an empty + // connection + if filter.is_empty() || page.limit() == 0 || scan_limit.is_some_and(|v| v == 0) { + return Ok(ScanConnection::new(false, false)); + } let cursor_viewed_at = page.validate_cursor_consistency()?; let checkpoint_viewed_at = cursor_viewed_at.unwrap_or(checkpoint_viewed_at); + let db: &Db = ctx.data_unchecked(); + let is_from_front = page.is_from_front(); - let (prev, next, results) = db - .execute(move |conn| { - page.paginate_query::( + use transactions::dsl as tx; + let (prev, next, transactions, tx_bounds): ( + bool, + bool, + Vec, + Option, + ) = db + .execute_repeatable(move |conn| { + let Some(tx_bounds) = TxBounds::query( conn, + filter.after_checkpoint.map(u64::from), + filter.at_checkpoint.map(u64::from), + filter.before_checkpoint.map(u64::from), checkpoint_viewed_at, - move || { - let mut query = tx::dsl::transactions.into_boxed(); - - if let Some(f) = &filter.function { - let sub_query = tx_calls_fun::dsl::tx_calls_fun - .select(tx_calls_fun::dsl::tx_sequence_number) - .into_boxed(); - - query = query.filter(tx::dsl::tx_sequence_number.eq_any(f.apply( - sub_query, - tx_calls_fun::dsl::package, - tx_calls_fun::dsl::module, - tx_calls_fun::dsl::func, - ))); - } - - if let Some(k) = &filter.kind { - query = query.filter(tx::dsl::transaction_kind.eq(*k as i16)) - } - - if let Some(c) = &filter.after_checkpoint { - query = - query.filter(tx::dsl::checkpoint_sequence_number.gt(i64::from(*c))); - } - - if let Some(c) = &filter.at_checkpoint { - query = - query.filter(tx::dsl::checkpoint_sequence_number.eq(i64::from(*c))); - } - - let before_checkpoint = filter - .before_checkpoint - .map_or(checkpoint_viewed_at + 1, |c| { - u64::from(c).min(checkpoint_viewed_at + 1) - }); - query = query.filter( - tx::dsl::checkpoint_sequence_number.lt(before_checkpoint as i64), - ); - - if let Some(a) = &filter.sign_address { - let sub_query = tx_senders::dsl::tx_senders - .select(tx_senders::dsl::tx_sequence_number) - .filter(tx_senders::dsl::sender.eq(a.into_vec())); - query = query.filter(tx::dsl::tx_sequence_number.eq_any(sub_query)); - } - - if let Some(a) = &filter.recv_address { - let sub_query = tx_recipients::dsl::tx_recipients - .select(tx_recipients::dsl::tx_sequence_number) - .filter(tx_recipients::dsl::recipient.eq(a.into_vec())); - query = query.filter(tx::dsl::tx_sequence_number.eq_any(sub_query)); - } - - if let Some(o) = &filter.input_object { - let sub_query = tx_input_objects::dsl::tx_input_objects - .select(tx_input_objects::dsl::tx_sequence_number) - .filter(tx_input_objects::dsl::object_id.eq(o.into_vec())); - query = query.filter(tx::dsl::tx_sequence_number.eq_any(sub_query)); - } - - if let Some(o) = &filter.changed_object { - let sub_query = tx_changed_objects::dsl::tx_changed_objects - .select(tx_changed_objects::dsl::tx_sequence_number) - .filter(tx_changed_objects::dsl::object_id.eq(o.into_vec())); - query = query.filter(tx::dsl::tx_sequence_number.eq_any(sub_query)); - } - - if let Some(txs) = &filter.transaction_ids { - let digests: Vec<_> = txs.iter().map(|d| d.to_vec()).collect(); - query = query.filter(tx::dsl::transaction_digest.eq_any(digests)); - } - - query - }, - ) + scan_limit, + &page, + )? + else { + return Ok::<_, diesel::result::Error>((false, false, Vec::new(), None)); + }; + + // If no filters are selected, or if the filter is composed of only checkpoint + // filters, we can directly query the main `transactions` table. Otherwise, we first + // fetch the set of `tx_sequence_number` from a join over relevant lookup tables, + // and then issue a query against the `transactions` table to fetch the remaining + // contents. + let (prev, next, transactions) = if !filter.has_filters() { + let (prev, next, iter) = page.paginate_query::( + conn, + checkpoint_viewed_at, + move || { + tx::transactions + .filter(tx::tx_sequence_number.ge(tx_bounds.scan_lo() as i64)) + .filter(tx::tx_sequence_number.lt(tx_bounds.scan_hi() as i64)) + .into_boxed() + }, + )?; + + (prev, next, iter.collect()) + } else { + let subquery = subqueries(&filter, tx_bounds).unwrap(); + let (prev, next, results) = + page.paginate_raw_query::(conn, checkpoint_viewed_at, subquery)?; + + let tx_sequence_numbers = results + .into_iter() + .map(|x| x.tx_sequence_number) + .collect::>(); + + let transactions = conn.results(move || { + tx::transactions + .filter(tx::tx_sequence_number.eq_any(tx_sequence_numbers.clone())) + })?; + + (prev, next, transactions) + }; + + Ok::<_, diesel::result::Error>((prev, next, transactions, Some(tx_bounds))) }) .await?; - let mut conn = Connection::new(prev, next); + let mut conn = ScanConnection::new(prev, next); + + let Some(tx_bounds) = tx_bounds else { + return Ok(conn); + }; - // The "checkpoint viewed at" sets a consistent upper bound for the nested queries. - for stored in results { + if scan_limit.is_some() { + apply_scan_limited_pagination( + &mut conn, + tx_bounds, + checkpoint_viewed_at, + is_from_front, + ); + } + + for stored in transactions { let cursor = stored.cursor(checkpoint_viewed_at).encode_cursor(); let inner = TransactionBlockInner::try_from(stored)?; let transaction = TransactionBlock { @@ -409,87 +415,6 @@ impl TransactionBlock { } } -impl TransactionBlockFilter { - /// Try to create a filter whose results are the intersection of transaction blocks in `self`'s - /// results and transaction blocks in `other`'s results. This may not be possible if the - /// resulting filter is inconsistent in some way (e.g. a filter that requires one field to be - /// two different values simultaneously). - pub(crate) fn intersect(self, other: Self) -> Option { - macro_rules! intersect { - ($field:ident, $body:expr) => { - intersect::field(self.$field, other.$field, $body) - }; - } - - Some(Self { - function: intersect!(function, FqNameFilter::intersect)?, - kind: intersect!(kind, intersect::by_eq)?, - - after_checkpoint: intersect!(after_checkpoint, intersect::by_max)?, - at_checkpoint: intersect!(at_checkpoint, intersect::by_eq)?, - before_checkpoint: intersect!(before_checkpoint, intersect::by_min)?, - - sign_address: intersect!(sign_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)?, - - transaction_ids: intersect!(transaction_ids, |a, b| { - let a = BTreeSet::from_iter(a.into_iter()); - let b = BTreeSet::from_iter(b.into_iter()); - Some(a.intersection(&b).cloned().collect()) - })?, - }) - } -} - -impl Paginated for StoredTransaction { - type Source = transactions::table; - - fn filter_ge(cursor: &Cursor, query: Query) -> Query { - query - .filter(transactions::dsl::tx_sequence_number.ge(cursor.tx_sequence_number as i64)) - .filter( - transactions::dsl::checkpoint_sequence_number - .ge(cursor.tx_checkpoint_number as i64), - ) - } - - fn filter_le(cursor: &Cursor, query: Query) -> Query { - query - .filter(transactions::dsl::tx_sequence_number.le(cursor.tx_sequence_number as i64)) - .filter( - transactions::dsl::checkpoint_sequence_number - .le(cursor.tx_checkpoint_number as i64), - ) - } - - fn order(asc: bool, query: Query) -> Query { - use transactions::dsl; - if asc { - query.order_by(dsl::tx_sequence_number.asc()) - } else { - query.order_by(dsl::tx_sequence_number.desc()) - } - } -} - -impl Target for StoredTransaction { - fn cursor(&self, checkpoint_viewed_at: u64) -> Cursor { - Cursor::new(TransactionBlockCursor { - tx_sequence_number: self.tx_sequence_number as u64, - tx_checkpoint_number: self.checkpoint_sequence_number as u64, - checkpoint_viewed_at, - }) - } -} - -impl Checkpointed for Cursor { - fn checkpoint_viewed_at(&self) -> u64 { - self.checkpoint_viewed_at - } -} - #[async_trait::async_trait] impl Loader for Db { type Value = TransactionBlock; @@ -599,3 +524,88 @@ impl TryFrom for TransactionBlock { }) } } + +fn apply_scan_limited_pagination( + conn: &mut ScanConnection, + tx_bounds: TxBounds, + checkpoint_viewed_at: u64, + is_from_front: bool, +) { + if is_from_front { + apply_forward_scan_limited_pagination(conn, tx_bounds, checkpoint_viewed_at); + } else { + apply_backward_scan_limited_pagination(conn, tx_bounds, checkpoint_viewed_at); + } +} + +/// When paginating forwards on a scan-limited query, the starting cursor and previous page flag +/// will be the first tx scanned in the current window, and whether this window is within the +/// scanning range. The ending cursor and next page flag wraps the last element of the result set if +/// there are more matches in the scanned window that are truncated - if the page size is smaller +/// than the scan limit - but otherwise is expanded out to the last tx scanned. +fn apply_forward_scan_limited_pagination( + conn: &mut ScanConnection, + tx_bounds: TxBounds, + checkpoint_viewed_at: u64, +) { + conn.has_previous_page = tx_bounds.scan_has_prev_page(); + conn.start_cursor = Some( + Cursor::new(cursor::TransactionBlockCursor { + checkpoint_viewed_at, + tx_sequence_number: tx_bounds.scan_start_cursor(), + is_scan_limited: true, + }) + .encode_cursor(), + ); + + // There may be more results within the scanned range that got truncated, which occurs when page + // size is less than `scan_limit`, so only overwrite the end when the base pagination reports no + // next page. + if !conn.has_next_page { + conn.has_next_page = tx_bounds.scan_has_next_page(); + conn.end_cursor = Some( + Cursor::new(cursor::TransactionBlockCursor { + checkpoint_viewed_at, + tx_sequence_number: tx_bounds.scan_end_cursor(), + is_scan_limited: true, + }) + .encode_cursor(), + ); + } +} + +/// When paginating backwards on a scan-limited query, the ending cursor and next page flag will be +/// the last tx scanned in the current window, and whether this window is within the scanning range. +/// The starting cursor and previous page flag wraps the first element of the result set if there +/// are more matches in the scanned window that are truncated - if the page size is smaller than the +/// scan limit - but otherwise is expanded out to the first tx scanned. +fn apply_backward_scan_limited_pagination( + conn: &mut ScanConnection, + tx_bounds: TxBounds, + checkpoint_viewed_at: u64, +) { + conn.has_next_page = tx_bounds.scan_has_next_page(); + conn.end_cursor = Some( + Cursor::new(cursor::TransactionBlockCursor { + checkpoint_viewed_at, + tx_sequence_number: tx_bounds.scan_end_cursor(), + is_scan_limited: true, + }) + .encode_cursor(), + ); + + // There may be more results within the scanned range that are truncated, especially if page + // size is less than `scan_limit`, so only overwrite the end when the base pagination reports no + // next page. + if !conn.has_previous_page { + conn.has_previous_page = tx_bounds.scan_has_prev_page(); + conn.start_cursor = Some( + Cursor::new(cursor::TransactionBlockCursor { + checkpoint_viewed_at, + tx_sequence_number: tx_bounds.scan_start_cursor(), + is_scan_limited: true, + }) + .encode_cursor(), + ); + } +} 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 new file mode 100644 index 0000000000000..e3d551976d5d4 --- /dev/null +++ b/crates/sui-graphql-rpc/src/types/transaction_block/tx_lookups.rs @@ -0,0 +1,433 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use super::{Cursor, TransactionBlockFilter}; +use crate::{ + data::{pg::bytea_literal, Conn, DbConnection}, + filter, inner_join, query, + raw_query::RawQuery, + types::{ + cursor::{End, Page}, + digest::Digest, + sui_address::SuiAddress, + transaction_block::TransactionBlockKindInput, + type_filter::{FqNameFilter, ModuleFilter}, + }, +}; +use diesel::{ExpressionMethods, OptionalExtension, QueryDsl}; +use std::fmt::Write; +use sui_indexer::schema::checkpoints; + +/// Bounds on transaction sequence number, imposed by filters, cursors, and the scan limit. The +/// outermost bounds are determined by the checkpoint filters. These get translated into bounds in +/// terms of transaction sequence numbers: +/// +/// ```ignore +/// tx_lo tx_hi +/// [-----------------------------------------------------------------) +/// ``` +/// +/// If cursors are provided, they further restrict the range of transactions to scan. Cursors are +/// exclusive, but when issuing database queries, we treat them inclusively so that we can detect +/// previous and next pages based on the existence of cursors in the results: +/// +/// ```ignore +/// cursor_lo cursor_hi_inclusive +/// [------------------------------------------] +/// ``` +/// +/// Finally, the scan limit restricts the number of transactions to scan. The scan limit can be +/// applied to either the front (forward pagination) or the back (backward pagination): +/// +/// ```ignore +/// [-----scan-limit-----)---------------------| end = Front +/// |---------------------[-----scan-limit------) end = Back +/// ``` +/// +/// This data structure can be used to compute the interval of transactions to look in for +/// candidates to include in a page of results. It can also determine whether the scanning has been +/// cut short on either side, implying that there is a previous or next page of values to scan. +/// +/// NOTE: for consistency, assume that lowerbounds are inclusive and upperbounds are exclusive. +/// Bounds that do not follow this convention will be annotated explicitly (e.g. `lo_exclusive` or +/// `hi_inclusive`). +#[derive(Clone, Debug, Copy)] +pub(crate) struct TxBounds { + /// The inclusive lower bound tx_sequence_number derived from checkpoint bounds. If checkpoint + /// bounds are not provided, this will default to `0`. + tx_lo: u64, + + /// The exclusive upper bound tx_sequence_number derived from checkpoint bounds. If checkpoint + /// bounds are not provided, this will default to the total transaction count at the checkpoint + /// viewed. + tx_hi: u64, + + /// The starting cursor (aka `after`). + cursor_lo_exclusive: Option, + + // The ending cursor (aka `before`). + cursor_hi: Option, + + /// The number of transactions to treat as candidates, defaults to all the transactions in the + /// range defined by the bounds above. + scan_limit: Option, + + /// Which end of the range candidates will be scanned from. + end: End, +} + +impl TxBounds { + /// Determines the `tx_sequence_number` range from the checkpoint bounds for a transaction block + /// query. If no checkpoint range is specified, the default is between 0 and the + /// `checkpoint_viewed_at`. The corresponding `tx_sequence_number` range is fetched from db, and + /// further adjusted by cursors and scan limit. If there are any inconsistencies or invalid + /// combinations, i.e. `after` cursor is greater than the upper bound, return None. + pub(crate) fn query( + conn: &mut Conn, + cp_after: Option, + cp_at: Option, + cp_before: Option, + checkpoint_viewed_at: u64, + scan_limit: Option, + page: &Page, + ) -> Result, diesel::result::Error> { + // Lowerbound in terms of checkpoint sequence number. We want to get the total transaction + // count of the checkpoint before this one, or 0 if there is no previous checkpoint. + let cp_lo = max_option([cp_after.map(|x| x.saturating_add(1)), cp_at]).unwrap_or(0); + + let cp_before_inclusive = match cp_before { + // There are no results strictly before checkpoint 0. + Some(0) => return Ok(None), + Some(x) => Some(x - 1), + None => None, + }; + + // Upperbound in terms of checkpoint sequence number. We want to get the total transaction + // count at the end of this checkpoint. If no upperbound is given, use + // `checkpoint_viewed_at`. + // + // SAFETY: we can unwrap because of the `Some(checkpoint_viewed_at) + let cp_hi = min_option([cp_before_inclusive, cp_at, Some(checkpoint_viewed_at)]).unwrap(); + + use checkpoints::dsl; + let (tx_lo, tx_hi) = if let Some(cp_prev) = cp_lo.checked_sub(1) { + let res: Vec = conn.results(move || { + dsl::checkpoints + .select(dsl::network_total_transactions) + .filter(dsl::sequence_number.eq_any([cp_prev as i64, cp_hi as i64])) + .order_by(dsl::network_total_transactions.asc()) + })?; + + // If there are not two distinct results, it means that the transaction bounds are + // empty (lo and hi are the same), or it means that the one or other of the checkpoints + // doesn't exist, so we can return early. + let &[lo, hi] = res.as_slice() else { + return Ok(None); + }; + + (lo as u64, hi as u64) + } else { + let res: Option = conn + .first(move || { + dsl::checkpoints + .select(dsl::network_total_transactions) + .filter(dsl::sequence_number.eq(cp_hi as i64)) + }) + .optional()?; + + // If there is no result, it means that the checkpoint doesn't exist, so we can return + // early. + let Some(hi) = res else { + return Ok(None); + }; + + (0, hi as u64) + }; + + // If the cursors point outside checkpoint bounds, we can return early. + if matches!(page.after(), Some(a) if tx_hi <= a.tx_sequence_number.saturating_add(1)) { + return Ok(None); + } + + if matches!(page.before(), Some(b) if b.tx_sequence_number <= tx_lo) { + return Ok(None); + } + + Ok(Some(Self { + tx_lo, + tx_hi, + cursor_lo_exclusive: page.after().map(|a| a.tx_sequence_number), + cursor_hi: page.before().map(|b| b.tx_sequence_number), + scan_limit, + end: page.end(), + })) + } + + /// Inclusive lowerbound for range of transactions to scan, accounting for the bounds from + /// filters and the cursor, but not scan limits. For the purposes of scanning records in the + /// DB, cursors are treated inclusively, even though they are exclusive bounds. + fn db_lo(&self) -> u64 { + max_option([self.cursor_lo_exclusive, Some(self.tx_lo)]).unwrap() + } + + /// Exclusive upperbound for range of transactions to scan, accounting for the bounds from + /// filters and the cursor, but not scan limits. For the purposes of scanning records in the + /// DB, cursors are treated inclusively, even though they are exclusive bounds. + fn db_hi(&self) -> u64 { + min_option([ + self.cursor_hi.map(|h| h.saturating_add(1)), + Some(self.tx_hi), + ]) + .unwrap() + } + + /// Whether the cursor lowerbound restricts the transaction range. + fn has_cursor_prev_page(&self) -> bool { + self.cursor_lo_exclusive.is_some_and(|lo| self.tx_lo <= lo) + } + + /// Whether the cursor upperbound restricts the transaction range. + fn has_cursor_next_page(&self) -> bool { + self.cursor_hi.is_some_and(|hi| hi < self.tx_hi) + } + + /// Inclusive lowerbound of range of transactions to scan. + pub(crate) fn scan_lo(&self) -> u64 { + match (self.end, self.scan_limit) { + (End::Front, _) | (_, None) => self.db_lo(), + (End::Back, Some(scan_limit)) => self + .db_hi() + // If there is a next page, additionally scan the cursor upperbound. + .saturating_sub(self.has_cursor_next_page() as u64) + .saturating_sub(scan_limit) + .max(self.db_lo()), + } + } + + /// Exclusive upperbound of range of transactions to scan. + pub(crate) fn scan_hi(&self) -> u64 { + match (self.end, self.scan_limit) { + (End::Back, _) | (_, None) => self.db_hi(), + (End::Front, Some(scan_limit)) => self + .db_lo() + // If there is a previous page, additionally scan the cursor lowerbound. + .saturating_add(self.has_cursor_prev_page() as u64) + .saturating_add(scan_limit) + .min(self.db_hi()), + } + } + + /// The first transaction scanned, ignoring transactions pointed at by cursors. + pub(crate) fn scan_start_cursor(&self) -> u64 { + let skip_cursor_lo = self.end == End::Front && self.has_cursor_prev_page(); + self.scan_lo().saturating_add(skip_cursor_lo as u64) + } + + /// The last transaction scanned, ignoring transactions pointed at by cursors. + pub(crate) fn scan_end_cursor(&self) -> u64 { + let skip_cursor_hi = self.end == End::Back && self.has_cursor_next_page(); + self.scan_hi().saturating_sub(skip_cursor_hi as u64 + 1) + } + + /// Whether there are more transactions to scan before this page. + pub(crate) fn scan_has_prev_page(&self) -> bool { + self.tx_lo < self.scan_start_cursor() + } + + /// Whether there are more transactions to scan after this page. + pub(crate) fn scan_has_next_page(&self) -> bool { + self.scan_end_cursor() + 1 < self.tx_hi + } +} + +/// Determines the maximum value in an arbitrary number of Option. +fn max_option(xs: impl IntoIterator>) -> Option { + xs.into_iter().flatten().max() +} + +/// Determines the minimum value in an arbitrary number of Option. +fn min_option(xs: impl IntoIterator>) -> Option { + xs.into_iter().flatten().min() +} + +/// 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.sign_address; + + let mut subqueries = vec![]; + + if let Some(f) = &filter.function { + subqueries.push(match f { + FqNameFilter::ByModule(filter) => match filter { + ModuleFilter::ByPackage(p) => ("tx_calls_pkg", select_pkg(p, sender, tx_bounds)), + ModuleFilter::ByModule(p, m) => { + ("tx_calls_mod", select_mod(p, m.clone(), sender, tx_bounds)) + } + }, + FqNameFilter::ByFqName(p, m, n) => ( + "tx_calls_fun", + select_fun(p, m.clone(), n.clone(), sender, tx_bounds), + ), + }); + } + if let Some(kind) = &filter.kind { + subqueries.push(("tx_kinds", select_kind(*kind, sender, tx_bounds))); + } + 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))); + } + if let Some(changed) = &filter.changed_object { + subqueries.push(( + "tx_changed_objects", + select_changed(changed, sender, tx_bounds), + )); + } + if let Some(sender) = &filter.explicit_sender() { + subqueries.push(("tx_senders", select_sender(sender, tx_bounds))); + } + if let Some(txs) = &filter.transaction_ids { + subqueries.push(("tx_digests", select_ids(txs, tx_bounds))); + } + + let Some((_, mut subquery)) = subqueries.pop() else { + return None; + }; + + if !subqueries.is_empty() { + subquery = query!("SELECT tx_sequence_number FROM ({}) AS initial", subquery); + while let Some((alias, subselect)) = subqueries.pop() { + subquery = inner_join!(subquery, alias => subselect, using: ["tx_sequence_number"]); + } + } + + Some(subquery) +} + +fn select_tx(sender: Option, bound: TxBounds, from: &str) -> RawQuery { + let mut query = filter!( + query!(format!("SELECT tx_sequence_number FROM {from}")), + format!( + "{} <= tx_sequence_number AND tx_sequence_number < {}", + bound.scan_lo(), + bound.scan_hi() + ) + ); + + if let Some(sender) = sender { + query = filter!( + query, + format!("sender = {}", bytea_literal(sender.as_slice())) + ); + } + + query +} + +fn select_pkg(pkg: &SuiAddress, sender: Option, bound: TxBounds) -> RawQuery { + filter!( + select_tx(sender, bound, "tx_calls_pkg"), + format!("package = {}", bytea_literal(pkg.as_slice())) + ) +} + +fn select_mod( + pkg: &SuiAddress, + mod_: String, + sender: Option, + bound: TxBounds, +) -> RawQuery { + filter!( + select_tx(sender, bound, "tx_calls_mod"), + format!( + "package = {} and module = {{}}", + bytea_literal(pkg.as_slice()) + ), + mod_ + ) +} + +fn select_fun( + pkg: &SuiAddress, + mod_: String, + fun: String, + sender: Option, + bound: TxBounds, +) -> RawQuery { + filter!( + select_tx(sender, bound, "tx_calls_fun"), + format!( + "package = {} AND module = {{}} AND func = {{}}", + bytea_literal(pkg.as_slice()), + ), + mod_, + 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. +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), + // Otherwise, we can ignore the sender always, and just query the `tx_kinds` table. + _ => filter!( + select_tx(None, bound, "tx_kinds"), + format!("tx_kind = {}", kind as i16) + ), + } +} + +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"), + format!("object_id = {}", bytea_literal(input.as_slice())) + ) +} + +fn select_changed(changed: &SuiAddress, sender: Option, bound: TxBounds) -> RawQuery { + filter!( + select_tx(sender, bound, "tx_changed_objects"), + format!("object_id = {}", bytea_literal(changed.as_slice())) + ) +} + +fn select_ids(ids: &Vec, bound: TxBounds) -> RawQuery { + let query = select_tx(None, bound, "tx_digests"); + if ids.is_empty() { + filter!(query, "1=0") + } else { + let mut inner = String::new(); + let mut prefix = "tx_digest IN ("; + for id in ids { + write!(&mut inner, "{prefix}{}", bytea_literal(id.as_slice())).unwrap(); + prefix = ", "; + } + inner.push(')'); + filter!(query, inner) + } +} diff --git a/crates/sui-graphql-rpc/src/types/type_filter.rs b/crates/sui-graphql-rpc/src/types/type_filter.rs index 31fa106d43577..f2028483989ff 100644 --- a/crates/sui-graphql-rpc/src/types/type_filter.rs +++ b/crates/sui-graphql-rpc/src/types/type_filter.rs @@ -268,32 +268,6 @@ impl TypeFilter { } impl FqNameFilter { - /// Modify `query` to apply this filter, treating `package` as the column containing the package - /// address, `module` as the module containing the module name, and `name` as the column - /// containing the module member name. - pub(crate) fn apply( - &self, - query: Query, - package: P, - module: M, - name: N, - ) -> Query - where - Query: QueryDsl, - P: Field, - M: Field, - N: Field, - QS: QuerySource, - { - match self { - FqNameFilter::ByModule(filter) => filter.apply(query, package, module), - FqNameFilter::ByFqName(p, m, n) => query - .filter(package.eq(p.into_vec())) - .filter(module.eq(m.clone())) - .filter(name.eq(n.clone())), - } - } - /// Try to create a filter whose results are the intersection of the results of the input /// filters (`self` and `other`). This may not be possible if the resulting filter is /// inconsistent (e.g. a filter that requires the module member's package to be at two different diff --git a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap index 93eadb6691d92..fd04f186f34b6 100644 --- a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap +++ b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap @@ -102,8 +102,27 @@ type Address implements IOwner { """ Similar behavior to the `transactionBlocks` in Query but supporting the additional `AddressTransactionBlockRelationship` filter, which defaults to `SIGN`. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - transactionBlocks(first: Int, after: String, last: Int, before: String, relation: AddressTransactionBlockRelationship, filter: TransactionBlockFilter): TransactionBlockConnection! + transactionBlocks(first: Int, after: String, last: Int, before: String, relation: AddressTransactionBlockRelationship, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! } type AddressConnection { @@ -413,8 +432,25 @@ type Checkpoint { epoch: Epoch """ Transactions in this checkpoint. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range consists of all transactions in this checkpoint. """ - transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! } type CheckpointConnection { @@ -521,8 +557,27 @@ type Coin implements IMoveObject & IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -680,8 +735,27 @@ type CoinMetadata implements IMoveObject & IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -1097,8 +1171,25 @@ type Epoch { checkpoints(first: Int, after: String, last: Int, before: String): CheckpointConnection! """ The epoch's corresponding transaction blocks. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range consists of all transactions in this epoch. """ - transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! } type Event { @@ -1433,7 +1524,7 @@ interface IObject { """ The transaction blocks that sent objects to this object. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -1976,8 +2067,27 @@ type MoveObject implements IMoveObject & IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -2165,8 +2275,27 @@ type MovePackage implements IObject & IOwner { The transaction blocks that sent objects to this package. Note that objects that have been sent to a package become inaccessible. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the package's content. """ @@ -2544,8 +2673,27 @@ type Object implements IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -3146,8 +3294,27 @@ type Query { checkpoints(first: Int, after: String, last: Int, before: String): CheckpointConnection! """ The transaction blocks that exist in the network. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The events that exist in the network. """ @@ -3365,6 +3532,14 @@ type ServiceConfig { Maximum nesting allowed in struct fields when calculating the layout of a single Move Type. """ maxMoveValueDepth: Int! + """ + Maximum number of transaction ids that can be passed to a `TransactionBlockFilter`. + """ + maxTransactionIds: Int! + """ + Maximum number of candidates to scan when gathering a page of results. + """ + maxScanLimit: Int! } """ @@ -3582,8 +3757,27 @@ type StakedSui implements IMoveObject & IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -3784,8 +3978,27 @@ type SuinsRegistration implements IMoveObject & IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ From 9141931f11ac715a6ddb60833c573eb0be33b545 Mon Sep 17 00:00:00 2001 From: Emma Zhong Date: Thu, 22 Aug 2024 08:46:49 -0700 Subject: [PATCH 210/232] return 0 for lowest available cp on unpruned fn (#19066) ## Description If the fullnodes is unpruned, then we should return 0 directly for lowest available cp. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-core/src/storage.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/crates/sui-core/src/storage.rs b/crates/sui-core/src/storage.rs index 80e06efb7029a..fe274bd1f0123 100644 --- a/crates/sui-core/src/storage.rs +++ b/crates/sui-core/src/storage.rs @@ -119,10 +119,16 @@ impl ReadStore for RocksDbStore { } fn get_lowest_available_checkpoint(&self) -> Result { - self.checkpoint_store + let highest_pruned_cp = self + .checkpoint_store .get_highest_pruned_checkpoint_seq_number() - .map(|seq| seq + 1) - .map_err(Into::into) + .map_err(Into::::into)?; + + if highest_pruned_cp == 0 { + Ok(0) + } else { + Ok(highest_pruned_cp + 1) + } } fn get_full_checkpoint_contents_by_sequence_number( @@ -508,11 +514,17 @@ impl RestStateReader for RestReadStore { fn get_lowest_available_checkpoint_objects( &self, ) -> sui_types::storage::error::Result { - self.state + let highest_pruned_cp = self + .state .get_object_cache_reader() .get_highest_pruned_checkpoint() - .map(|seq| seq + 1) - .map_err(StorageError::custom) + .map_err(StorageError::custom)?; + + if highest_pruned_cp == 0 { + Ok(0) + } else { + Ok(highest_pruned_cp + 1) + } } fn get_chain_identifier( From ba7e08577ae6d5e51e3c418594a60ed5f09df380 Mon Sep 17 00:00:00 2001 From: Anastasios Kichidis Date: Thu, 22 Aug 2024 19:14:47 +0300 Subject: [PATCH 211/232] [Consensus] enable amnesia recovery & refactor retry approach (#18771) ## Description This PR is enabling amnesia recovery automatically when: * node has not accepted any block yet (nothing in DagState) * consensus authority boot counter is == 0 , which means that consensus has started for the first time while the binary runs. This avoids from running the amnesia recovery during normal epoch change The timeout parameter `sync_last_known_own_block_timeout` is still being used but now it is timing out a whole "iteration" attempt to fetch the last own block. Also, if the `sync_last_known_own_block_timeout` is set to `zero` it is disabling the amnesia recovery mechanism overall which might be desired under some conditions. Also, the synchronizer has been refactored in order to not panic anymore when node doesn't manage to hear back from other peers, but keep trying until it finally does hear back from `f+1` nodes. Liveness is not affected as we should always have `f+1` nodes available before we are able to make any meaningful round advancement. **Note:** The node crash recovery will be successful when both the authority db & consensus db have been wiped out. If only the consensus db is wiped out, although the sync of the last own block will be successful, it's possible to reach the checks in the commit observer here https://github.com/MystenLabs/sui/blob/25d2f3087a3797184d141929a4cf3dfcb244604f/consensus/core/src/commit_observer.rs#L111 as the authority node might have already consumed from previous run committed sub dags. We can relax the rule there but not touching it for now. ## Test plan * **CI** * **private-testnet**: delete the authority & consensus databases from a node , after it has made some progress for a few minutes, and restart the node. Node gets into recovery mode and asks the peers for last proposed block. Node manages to recover successfully and participate to network --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- consensus/config/src/parameters.rs | 22 +- .../parameters_test__parameters.snap | 4 +- consensus/core/src/authority_node.rs | 238 +++++++++--------- consensus/core/src/authority_service.rs | 2 + consensus/core/src/core.rs | 25 +- consensus/core/src/core_thread.rs | 1 + consensus/core/src/metrics.rs | 6 + consensus/core/src/synchronizer.rs | 194 +++++++++----- .../consensus_manager/mysticeti_manager.rs | 18 +- .../src/unit_tests/mysticeti_manager_tests.rs | 50 ++-- 10 files changed, 330 insertions(+), 230 deletions(-) diff --git a/consensus/config/src/parameters.rs b/consensus/config/src/parameters.rs index 8c6fa6bf58d26..6df7da3cb21e0 100644 --- a/consensus/config/src/parameters.rs +++ b/consensus/config/src/parameters.rs @@ -73,8 +73,8 @@ pub struct Parameters { /// Time to wait during node start up until the node has synced the last proposed block via the /// network peers. When set to `0` the sync mechanism is disabled. This property is meant to be /// used for amnesia recovery. - #[serde(default = "Parameters::default_sync_last_proposed_block_timeout")] - pub sync_last_proposed_block_timeout: Duration, + #[serde(default = "Parameters::default_sync_last_known_own_block_timeout")] + pub sync_last_known_own_block_timeout: Duration, } impl Parameters { @@ -134,12 +134,14 @@ impl Parameters { 80 } - pub(crate) fn default_sync_last_proposed_block_timeout() -> Duration { - Duration::ZERO - } - - pub fn is_sync_last_proposed_block_enabled(&self) -> bool { - !self.sync_last_proposed_block_timeout.is_zero() + pub(crate) fn default_sync_last_known_own_block_timeout() -> Duration { + if cfg!(msim) { + Duration::from_millis(500) + } else { + // Here we prioritise liveness over the complete de-risking of block equivocation. 5 seconds + // in the majority of cases should be good enough for this given a healthy network. + Duration::from_secs(5) + } } } @@ -152,8 +154,8 @@ impl Default for Parameters { max_forward_time_drift: Parameters::default_max_forward_time_drift(), dag_state_cached_rounds: Parameters::default_dag_state_cached_rounds(), max_blocks_per_fetch: Parameters::default_max_blocks_per_fetch(), - sync_last_proposed_block_timeout: Parameters::default_sync_last_proposed_block_timeout( - ), + sync_last_known_own_block_timeout: + Parameters::default_sync_last_known_own_block_timeout(), commit_sync_parallel_fetches: Parameters::default_commit_sync_parallel_fetches(), commit_sync_batch_size: Parameters::default_commit_sync_batch_size(), commit_sync_batches_ahead: Parameters::default_commit_sync_batches_ahead(), diff --git a/consensus/config/tests/snapshots/parameters_test__parameters.snap b/consensus/config/tests/snapshots/parameters_test__parameters.snap index 533ab5ff84c8f..60ed151ba6303 100644 --- a/consensus/config/tests/snapshots/parameters_test__parameters.snap +++ b/consensus/config/tests/snapshots/parameters_test__parameters.snap @@ -25,6 +25,6 @@ tonic: connection_buffer_size: 33554432 excessive_message_size: 16777216 message_size_limit: 67108864 -sync_last_proposed_block_timeout: - secs: 0 +sync_last_known_own_block_timeout: + secs: 5 nanos: 0 diff --git a/consensus/core/src/authority_node.rs b/consensus/core/src/authority_node.rs index f684d05bcfdcd..f896dc1a0bd89 100644 --- a/consensus/core/src/authority_node.rs +++ b/consensus/core/src/authority_node.rs @@ -55,6 +55,11 @@ impl ConsensusAuthority { transaction_verifier: Arc, commit_consumer: CommitConsumer, registry: Registry, + // A counter that keeps track of how many times the authority node has been booted while the binary + // or the component that is calling the `ConsensusAuthority` has been running. It's mostly useful to + // make decisions on whether amnesia recovery should run or not. When `boot_counter` is 0, then `ConsensusAuthority` + // will initiate the process of amnesia recovery if that's enabled in the parameters. + boot_counter: u64, ) -> Self { match network_type { ConsensusNetwork::Anemo => { @@ -68,6 +73,7 @@ impl ConsensusAuthority { transaction_verifier, commit_consumer, registry, + boot_counter, ) .await; Self::WithAnemo(authority) @@ -83,6 +89,7 @@ impl ConsensusAuthority { transaction_verifier, commit_consumer, registry, + boot_counter, ) .await; Self::WithTonic(authority) @@ -111,6 +118,14 @@ impl ConsensusAuthority { Self::WithTonic(authority) => &authority.context, } } + + #[allow(unused)] + fn sync_last_known_own_block_enabled(&self) -> bool { + match self { + Self::WithAnemo(authority) => authority.sync_last_known_own_block, + Self::WithTonic(authority) => authority.sync_last_known_own_block, + } + } } pub(crate) struct AuthorityNode @@ -129,6 +144,7 @@ where broadcaster: Option, subscriber: Option>>, network_manager: N, + sync_last_known_own_block: bool, } impl AuthorityNode @@ -147,10 +163,11 @@ where transaction_verifier: Arc, commit_consumer: CommitConsumer, registry: Registry, + boot_counter: u64, ) -> Self { info!( - "Starting consensus authority {}\n{:#?}\n{:#?}\n{:?}", - own_index, committee, parameters, protocol_config.version + "Starting consensus authority {}\n{:#?}\n{:#?}\n{:?}\nBoot counter: {}", + own_index, committee, parameters, protocol_config.version, boot_counter ); assert!(committee.is_valid_index(own_index)); let context = Arc::new(Context::new( @@ -186,6 +203,13 @@ where let store_path = context.parameters.db_path.as_path().to_str().unwrap(); let store = Arc::new(RocksDBStore::new(store_path)); let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store.clone()))); + let sync_last_known_own_block = boot_counter == 0 + && dag_state.read().highest_accepted_round() == 0 + && !context + .parameters + .sync_last_known_own_block_timeout + .is_zero(); + info!("Sync last known own block: {sync_last_known_own_block}"); let block_verifier = Arc::new(SignedBlockVerifier::new( context.clone(), @@ -231,6 +255,7 @@ where core_signals, protocol_keypair, dag_state.clone(), + sync_last_known_own_block, ); let (core_dispatcher, core_thread_handle) = @@ -248,6 +273,7 @@ where commit_vote_monitor.clone(), block_verifier.clone(), dag_state.clone(), + sync_last_known_own_block, ); let commit_syncer_handle = CommitSyncer::new( @@ -307,6 +333,7 @@ where broadcaster, subscriber, network_manager, + sync_last_known_own_block, } } @@ -356,7 +383,7 @@ where mod tests { #![allow(non_snake_case)] - use std::sync::Mutex; + use std::collections::BTreeMap; use std::{collections::BTreeSet, sync::Arc, time::Duration}; use consensus_config::{local_committee_and_keys, Parameters}; @@ -365,10 +392,11 @@ mod tests { use rstest::rstest; use sui_protocol_config::ProtocolConfig; use tempfile::TempDir; - use tokio::time::sleep; + use tokio::time::{sleep, timeout}; use typed_store::DBMetrics; use super::*; + use crate::block::GENESIS_ROUND; use crate::{block::BlockAPI as _, transaction::NoopTransactionVerifier, CommittedSubDag}; #[rstest] @@ -404,6 +432,7 @@ mod tests { Arc::new(txn_verifier), commit_consumer, registry, + 0, ) .await; @@ -423,11 +452,15 @@ mod tests { let db_registry = Registry::new(); DBMetrics::init(&db_registry); - let (committee, keypairs) = local_committee_and_keys(0, vec![1, 1, 1, 1]); - let temp_dirs = (0..4).map(|_| TempDir::new().unwrap()).collect::>(); + const NUM_OF_AUTHORITIES: usize = 4; + let (committee, keypairs) = local_committee_and_keys(0, [1; NUM_OF_AUTHORITIES].to_vec()); + let temp_dirs = (0..NUM_OF_AUTHORITIES) + .map(|_| TempDir::new().unwrap()) + .collect::>(); let mut output_receivers = Vec::with_capacity(committee.size()); let mut authorities = Vec::with_capacity(committee.size()); + let mut boot_counters = [0; NUM_OF_AUTHORITIES]; for (index, _authority_info) in committee.authorities() { let (authority, receiver) = make_authority( @@ -436,8 +469,10 @@ mod tests { committee.clone(), keypairs.clone(), network_type, + boot_counters[index], ) .await; + boot_counters[index] += 1; output_receivers.push(receiver); authorities.push(authority); } @@ -490,8 +525,10 @@ mod tests { committee.clone(), keypairs.clone(), network_type, + boot_counters[index], ) .await; + boot_counters[index] += 1; output_receivers[index] = receiver; authorities.insert(index.value(), authority); sleep(Duration::from_secs(10)).await; @@ -504,168 +541,122 @@ mod tests { #[rstest] #[tokio::test(flavor = "current_thread")] - async fn test_amnesia_success( + async fn test_amnesia_recovery_success( #[values(ConsensusNetwork::Anemo, ConsensusNetwork::Tonic)] network_type: ConsensusNetwork, ) { telemetry_subscribers::init_for_testing(); let db_registry = Registry::new(); DBMetrics::init(&db_registry); - let (committee, keypairs) = local_committee_and_keys(0, vec![1, 1, 1, 1]); + const NUM_OF_AUTHORITIES: usize = 4; + let (committee, keypairs) = local_committee_and_keys(0, [1; NUM_OF_AUTHORITIES].to_vec()); let mut output_receivers = vec![]; - let mut authorities = vec![]; + let mut authorities = BTreeMap::new(); + let mut temp_dirs = BTreeMap::new(); + let mut boot_counters = [0; NUM_OF_AUTHORITIES]; for (index, _authority_info) in committee.authorities() { + let dir = TempDir::new().unwrap(); let (authority, receiver) = make_authority( index, - &TempDir::new().unwrap(), + &dir, committee.clone(), keypairs.clone(), network_type, + boot_counters[index], ) .await; + assert!(authority.sync_last_known_own_block_enabled(), "Expected syncing of last known own block to be enabled as all authorities are of empty db and boot for first time."); + boot_counters[index] += 1; output_receivers.push(receiver); - authorities.push(authority); + authorities.insert(index, authority); + temp_dirs.insert(index, dir); } - const NUM_TRANSACTIONS: u8 = 15; - let mut submitted_transactions = BTreeSet::>::new(); - for i in 0..NUM_TRANSACTIONS { - let txn = vec![i; 16]; - submitted_transactions.insert(txn.clone()); - authorities[i as usize % authorities.len()] - .transaction_client() - .submit(vec![txn]) + // Now we take the receiver of authority 1 and we wait until we see at least one block committed from this authority + // We wait until we see at least one committed block authored from this authority. That way we'll be 100% sure that + // at least one block has been proposed and successfully received by a quorum of nodes. + let index_1 = committee.to_authority_index(1).unwrap(); + 'outer: while let Some(result) = + timeout(Duration::from_secs(10), output_receivers[index_1].recv()) .await - .unwrap(); - } - - for receiver in &mut output_receivers { - let mut expected_transactions = submitted_transactions.clone(); - loop { - let committed_subdag = - tokio::time::timeout(Duration::from_secs(1), receiver.recv()) - .await - .unwrap() - .unwrap(); - for b in committed_subdag.blocks { - for txn in b.transactions().iter().map(|t| t.data().to_vec()) { - assert!( - expected_transactions.remove(&txn), - "Transaction not submitted or already seen: {:?}", - txn - ); - } - } - assert_eq!(committed_subdag.reputation_scores_desc, vec![]); - if expected_transactions.is_empty() { - break; + .expect("Timed out while waiting for at least one committed block from authority 1") + { + for block in result.blocks { + if block.round() > GENESIS_ROUND && block.author() == index_1 { + break 'outer; } } } - // Stop authority 1. - let index = committee.to_authority_index(1).unwrap(); - authorities.remove(index.value()).stop().await; + // Stop authority 1 & 2. + // * Authority 1 will be used to wipe out their DB and practically "force" the amnesia recovery. + // * Authority 2 is stopped in order to simulate less than f+1 availability which will + // make authority 1 retry during amnesia recovery until it has finally managed to successfully get back f+1 responses. + // once authority 2 is up and running again. + authorities.remove(&index_1).unwrap().stop().await; + let index_2 = committee.to_authority_index(2).unwrap(); + authorities.remove(&index_2).unwrap().stop().await; sleep(Duration::from_secs(5)).await; - // now create a new directory to simulate amnesia. The node will start having participated previously - // to consensus but now will attempt to synchronize the last own block and recover from there. + // Authority 1: create a new directory to simulate amnesia. The node will start having participated previously + // to consensus but now will attempt to synchronize the last own block and recover from there. It won't be able + // to do that successfully as authority 2 is still down. + let dir = TempDir::new().unwrap(); + // We do reset the boot counter for this one to simulate a "binary" restart + boot_counters[index_1] = 0; let (authority, mut receiver) = make_authority( - index, - &TempDir::new().unwrap(), + index_1, + &dir, + committee.clone(), + keypairs.clone(), + network_type, + boot_counters[index_1], + ) + .await; + assert!( + authority.sync_last_known_own_block_enabled(), + "Authority should have the sync of last own block enabled" + ); + boot_counters[index_1] += 1; + authorities.insert(index_1, authority); + temp_dirs.insert(index_1, dir); + sleep(Duration::from_secs(5)).await; + + // Now spin up authority 2 using its earlier directly - so no amnesia recovery should be forced here. + // Authority 1 should be able to recover from amnesia successfully. + let (authority, _receiver) = make_authority( + index_2, + &temp_dirs[&index_2], committee.clone(), keypairs, network_type, + boot_counters[index_2], ) .await; - authorities.insert(index.value(), authority); + assert!( + !authority.sync_last_known_own_block_enabled(), + "Authority should not have attempted to sync the last own block" + ); + boot_counters[index_2] += 1; + authorities.insert(index_2, authority); sleep(Duration::from_secs(5)).await; // We wait until we see at least one committed block authored from this authority 'outer: while let Some(result) = receiver.recv().await { for block in result.blocks { - if block.author() == index { + if block.round() > GENESIS_ROUND && block.author() == index_1 { break 'outer; } } } // Stop all authorities and exit. - for authority in authorities { + for (_, authority) in authorities { authority.stop().await; } } - #[rstest] - #[tokio::test] - async fn test_amnesia_failure( - #[values(ConsensusNetwork::Anemo, ConsensusNetwork::Tonic)] network_type: ConsensusNetwork, - ) { - telemetry_subscribers::init_for_testing(); - - let occurred_panic = Arc::new(Mutex::new(None)); - let occurred_panic_cloned = occurred_panic.clone(); - - let default_panic_handler = std::panic::take_hook(); - std::panic::set_hook(Box::new(move |panic| { - let mut l = occurred_panic_cloned.lock().unwrap(); - *l = Some(panic.to_string()); - default_panic_handler(panic); - })); - - let db_registry = Registry::new(); - DBMetrics::init(&db_registry); - - let (committee, keypairs) = local_committee_and_keys(0, vec![1, 1, 1, 1]); - let mut output_receivers = vec![]; - let mut authorities = vec![]; - - for (index, _authority_info) in committee.authorities() { - let (authority, receiver) = make_authority( - index, - &TempDir::new().unwrap(), - committee.clone(), - keypairs.clone(), - network_type, - ) - .await; - output_receivers.push(receiver); - authorities.push(authority); - } - - // Let the network run for a few seconds - sleep(Duration::from_secs(5)).await; - - // Stop all authorities - while let Some(authority) = authorities.pop() { - authority.stop().await; - } - - sleep(Duration::from_secs(2)).await; - - let index = AuthorityIndex::new_for_test(0); - let (_authority, _receiver) = make_authority( - index, - &TempDir::new().unwrap(), - committee, - keypairs, - network_type, - ) - .await; - sleep(Duration::from_secs(5)).await; - - // Now reset the panic hook - let _default_panic_handler = std::panic::take_hook(); - - // We expect this test to panic as all the other peers are down and the node that tries to - // recover its last produced block fails. - let panic_info = occurred_panic.lock().unwrap().take().unwrap(); - assert!(panic_info.contains( - "No peer has returned any acceptable result, can not safely update min round" - )); - } - // TODO: create a fixture async fn make_authority( index: AuthorityIndex, @@ -673,6 +664,7 @@ mod tests { committee: Committee, keypairs: Vec<(NetworkKeyPair, ProtocolKeyPair)>, network_type: ConsensusNetwork, + boot_counter: u64, ) -> (ConsensusAuthority, UnboundedReceiver) { let registry = Registry::new(); @@ -682,7 +674,7 @@ mod tests { dag_state_cached_rounds: 5, commit_sync_parallel_fetches: 3, commit_sync_batch_size: 3, - sync_last_proposed_block_timeout: Duration::from_millis(2_000), + sync_last_known_own_block_timeout: Duration::from_millis(2_000), ..Default::default() }; let txn_verifier = NoopTransactionVerifier {}; @@ -704,8 +696,10 @@ mod tests { Arc::new(txn_verifier), commit_consumer, registry, + boot_counter, ) .await; + (authority, receiver) } } diff --git a/consensus/core/src/authority_service.rs b/consensus/core/src/authority_service.rs index 83fd3e173e286..c1c38f9dfbfb6 100644 --- a/consensus/core/src/authority_service.rs +++ b/consensus/core/src/authority_service.rs @@ -702,6 +702,7 @@ mod tests { commit_vote_monitor.clone(), block_verifier.clone(), dag_state.clone(), + false, ); let authority_service = Arc::new(AuthorityService::new( context.clone(), @@ -760,6 +761,7 @@ mod tests { commit_vote_monitor.clone(), block_verifier.clone(), dag_state.clone(), + true, ); let authority_service = Arc::new(AuthorityService::new( context.clone(), diff --git a/consensus/core/src/core.rs b/consensus/core/src/core.rs index 3c1636bd4e3ff..f9d5f2041b0aa 100644 --- a/consensus/core/src/core.rs +++ b/consensus/core/src/core.rs @@ -100,6 +100,7 @@ impl Core { signals: CoreSignals, block_signer: ProtocolKeyPair, dag_state: Arc>, + sync_last_known_own_block: bool, ) -> Self { let last_decided_leader = dag_state.read().last_commit_leader(); let number_of_leaders = context @@ -133,7 +134,7 @@ impl Core { last_included_ancestors[ancestor.author] = Some(*ancestor); } - let min_propose_round = if context.parameters.is_sync_last_proposed_block_enabled() { + let min_propose_round = if sync_last_known_own_block { None } else { // if the sync is disabled then we practically don't want to impose any restriction. @@ -305,16 +306,9 @@ impl Core { /// `> last_known_proposed_round`. At the moment is allowed to call the method only once leading to a panic /// if attempt to do multiple times. pub(crate) fn set_last_known_proposed_round(&mut self, round: Round) { - assert!( - self.context - .parameters - .is_sync_last_proposed_block_enabled(), - "Should not attempt to set the last known proposed round if that has been already set" - ); - assert!( - self.last_known_proposed_round.is_none(), - "Attempted to set the last known proposed round more than once" - ); + if self.last_known_proposed_round.is_some() { + panic!("Should not attempt to set the last known proposed round if that has been already set"); + } self.last_known_proposed_round = Some(round); info!("Set last known proposed round to {round}"); } @@ -894,6 +888,7 @@ impl CoreTextFixture { signals, block_signer, dag_state, + false, ); Self { @@ -999,6 +994,7 @@ mod test { signals, key_pairs.remove(context.own_index.value()).1, dag_state.clone(), + false, ); // New round should be 5 @@ -1116,6 +1112,7 @@ mod test { signals, key_pairs.remove(context.own_index.value()).1, dag_state.clone(), + false, ); // New round should be 4 @@ -1203,6 +1200,7 @@ mod test { signals, key_pairs.remove(context.own_index.value()).1, dag_state.clone(), + false, ); // Send some transactions @@ -1312,6 +1310,7 @@ mod test { signals, key_pairs.remove(context.own_index.value()).1, dag_state.clone(), + false, ); let mut expected_ancestors = BTreeSet::new(); @@ -1358,7 +1357,7 @@ mod test { telemetry_subscribers::init_for_testing(); let (context, mut key_pairs) = Context::new_for_test(4); let context = Arc::new(context.with_parameters(Parameters { - sync_last_proposed_block_timeout: Duration::from_millis(2_000), + sync_last_known_own_block_timeout: Duration::from_millis(2_000), ..Default::default() })); @@ -1400,6 +1399,7 @@ mod test { signals, key_pairs.remove(context.own_index.value()).1, dag_state.clone(), + true, ); // No new block should have been produced @@ -1587,6 +1587,7 @@ mod test { signals, key_pairs.remove(context.own_index.value()).1, dag_state.clone(), + false, ); // No proposal during recovery. diff --git a/consensus/core/src/core_thread.rs b/consensus/core/src/core_thread.rs index eeeeb06d26959..8c39d6059746f 100644 --- a/consensus/core/src/core_thread.rs +++ b/consensus/core/src/core_thread.rs @@ -354,6 +354,7 @@ mod test { signals, key_pairs.remove(context.own_index.value()).1, dag_state, + false, ); let (core_dispatcher, handle) = ChannelCoreThreadDispatcher::start(core, context); diff --git a/consensus/core/src/metrics.rs b/consensus/core/src/metrics.rs index 897607c00452b..8d9f60b179b6b 100644 --- a/consensus/core/src/metrics.rs +++ b/consensus/core/src/metrics.rs @@ -134,6 +134,7 @@ pub(crate) struct NodeMetrics { pub(crate) last_committed_leader_round: IntGauge, pub(crate) last_commit_index: IntGauge, pub(crate) last_known_own_block_round: IntGauge, + pub(crate) sync_last_known_own_block_retries: IntCounter, pub(crate) commit_round_advancement_interval: Histogram, pub(crate) last_decided_leader_round: IntGauge, pub(crate) leader_timeout_total: IntCounterVec, @@ -334,6 +335,11 @@ impl NodeMetrics { "The highest round of our own block as this has been synced from peers during an amnesia recovery", registry, ).unwrap(), + sync_last_known_own_block_retries: register_int_counter_with_registry!( + "sync_last_known_own_block_retries", + "Number of times this node tried to fetch the last own block from peers", + registry, + ).unwrap(), // TODO: add a short status label. invalid_blocks: register_int_counter_vec_with_registry!( "invalid_blocks", diff --git a/consensus/core/src/synchronizer.rs b/consensus/core/src/synchronizer.rs index 599cbd166bdd2..e3bc24c2e2b07 100644 --- a/consensus/core/src/synchronizer.rs +++ b/consensus/core/src/synchronizer.rs @@ -244,6 +244,7 @@ impl Synchronizer, block_verifier: Arc, dag_state: Arc>, + sync_last_known_own_block: bool, ) -> Arc { let (commands_sender, commands_receiver) = channel("consensus_synchronizer_commands", 1_000); @@ -273,14 +274,14 @@ impl Synchronizer Synchronizer Synchronizer Synchronizer, authority_index: AuthorityIndex| -> ConsensusResult> { let mut result = Vec::new(); for serialized_block in blocks { @@ -751,50 +739,80 @@ impl Synchronizer { - let Some((result, authority_index)) = result else { - break; - }; - match result { - Ok(result) => { - match process_blocks(result, authority_index) { - Ok(blocks) => { - let max_round = blocks.into_iter().map(|b|b.round()).max().unwrap_or(0); - highest_round = highest_round.max(max_round); - - total_stake += context.committee.stake(authority_index); - }, - Err(err) => { - warn!("Invalid result returned from {authority_index} while fetching last own block: {err}"); + // Get the highest of all the results. Retry until at least `f+1` results have been gathered. + let mut total_stake; + let mut highest_round; + let mut retries = 0; + let mut retry_delay_step = Duration::from_millis(500); + 'main:loop { + total_stake = 0; + highest_round = 0; + + // Ask all the other peers about our last block + let mut results = FuturesUnordered::new(); + + for (authority_index, _authority) in context.committee.authorities() { + if authority_index != context.own_index { + results.push(fetch_own_block(authority_index, Duration::from_millis(0))); + } + } + + // Gather the results but wait to timeout as well + let timer = sleep_until(Instant::now() + context.parameters.sync_last_known_own_block_timeout); + tokio::pin!(timer); + + 'inner: loop { + tokio::select! { + result = results.next() => { + let Some((result, authority_index)) = result else { + break 'inner; + }; + match result { + Ok(result) => { + match process_blocks(result, authority_index) { + Ok(blocks) => { + let max_round = blocks.into_iter().map(|b|b.round()).max().unwrap_or(0); + highest_round = highest_round.max(max_round); + + total_stake += context.committee.stake(authority_index); + }, + Err(err) => { + warn!("Invalid result returned from {authority_index} while fetching last own block: {err}"); + } } + }, + Err(err) => { + warn!("Error {err} while fetching our own block from peer {authority_index}. Will retry."); + results.push(fetch_own_block(authority_index, FETCH_OWN_BLOCK_RETRY_DELAY)); } - }, - Err(err) => { - warn!("Error {err} while fetching our own block from peer {authority_index}. Will retry."); - results.push(fetch_own_block(authority_index, FETCH_OWN_BLOCK_RETRY_DELAY)); } + }, + () = &mut timer => { + info!("Timeout while trying to sync our own last block from peers"); + break 'inner; } - }, - () = &mut timer => { - info!("Timeout while trying to sync our own last block from peers"); - break; } } - } - // Update the Core with the highest detected round - if total_stake == 0 { - panic!("No peer has returned any acceptable result, can not safely update min round"); + // Request at least f+1 stake to have replied back. + if context.committee.reached_validity(total_stake) { + info!("{} out of {} total stake returned acceptable results for our own last block with highest round {}, with {retries} retries.", total_stake, context.committee.total_stake(), highest_round); + break 'main; + } else { + retries += 1; + context.metrics.node_metrics.sync_last_known_own_block_retries.inc(); + warn!("Not enough stake: {} out of {} total stake returned acceptable results for our own last block with highest round {}. Will now retry {retries}.", total_stake, context.committee.total_stake(), highest_round); + + sleep(retry_delay_step).await; + + retry_delay_step = Duration::from_secs_f64(retry_delay_step.as_secs_f64() * 1.5); + retry_delay_step = retry_delay_step.min(MAX_RETRY_DELAY_STEP); + } } + // Update the Core with the highest detected round context.metrics.node_metrics.last_known_own_block_round.set(highest_round as i64); - info!("{} out of {} total stake returned acceptable results for our own last block with highest round {}", total_stake, context.committee.total_stake(), highest_round); if let Err(err) = core_dispatcher.set_last_known_proposed_round(highest_round) { warn!("Error received while calling dispatcher, probably dispatcher is shutting down, will now exit: {err:?}"); } @@ -1027,7 +1045,7 @@ mod tests { struct MockNetworkClient { fetch_blocks_requests: Mutex>, fetch_latest_blocks_requests: - Mutex>, + Mutex>>, } impl MockNetworkClient { @@ -1053,7 +1071,14 @@ mod tests { latency: Option, ) { let mut lock = self.fetch_latest_blocks_requests.lock().await; - lock.insert((peer, authorities), (blocks, latency)); + lock.entry((peer, authorities)) + .or_default() + .push((blocks, latency)); + } + + async fn fetch_latest_blocks_pending_calls(&self) -> usize { + let lock = self.fetch_latest_blocks_requests.lock().await; + lock.len() } } @@ -1122,22 +1147,27 @@ mod tests { _timeout: Duration, ) -> ConsensusResult> { let mut lock = self.fetch_latest_blocks_requests.lock().await; - let response = lock - .remove(&(peer, authorities)) + let mut responses = lock + .remove(&(peer, authorities.clone())) .expect("Unexpected fetch blocks request made"); + let response = responses.remove(0); let serialised = response .0 .into_iter() .map(|block| block.serialized().clone()) .collect::>(); - if let Some(latency) = response.1 { - sleep(latency).await; + if !responses.is_empty() { + lock.insert((peer, authorities), responses); } drop(lock); + if let Some(latency) = response.1 { + sleep(latency).await; + } + Ok(serialised) } } @@ -1229,6 +1259,7 @@ mod tests { commit_vote_monitor, block_verifier, dag_state, + false, ); // Create some test blocks @@ -1276,6 +1307,7 @@ mod tests { commit_vote_monitor, block_verifier, dag_state, + false, ); // Create some test blocks @@ -1367,6 +1399,7 @@ mod tests { commit_vote_monitor, block_verifier, dag_state, + false, ); sleep(2 * FETCH_REQUEST_TIMEOUT).await; @@ -1453,6 +1486,7 @@ mod tests { commit_vote_monitor.clone(), block_verifier, dag_state.clone(), + false, ); sleep(4 * FETCH_REQUEST_TIMEOUT).await; @@ -1491,7 +1525,7 @@ mod tests { // GIVEN let (context, _) = Context::new_for_test(4); let context = Arc::new(context.with_parameters(Parameters { - sync_last_proposed_block_timeout: Duration::from_millis(2_000), + sync_last_known_own_block_timeout: Duration::from_millis(2_000), ..Default::default() })); let block_verifier = Arc::new(NoopBlockVerifier {}); @@ -1509,9 +1543,18 @@ mod tests { // Now set different latest blocks for the peers // For peer 1 we give the block of round 10 (highest) + let block_1 = expected_blocks.pop().unwrap(); + network_client + .stub_fetch_latest_blocks( + vec![block_1.clone()], + AuthorityIndex::new_for_test(1), + vec![our_index], + None, + ) + .await; network_client .stub_fetch_latest_blocks( - vec![expected_blocks.pop().unwrap()], + vec![block_1], AuthorityIndex::new_for_test(1), vec![our_index], None, @@ -1519,9 +1562,18 @@ mod tests { .await; // For peer 2 we give the block of round 9 + let block_2 = expected_blocks.pop().unwrap(); + network_client + .stub_fetch_latest_blocks( + vec![block_2.clone()], + AuthorityIndex::new_for_test(2), + vec![our_index], + Some(Duration::from_secs(10)), + ) + .await; network_client .stub_fetch_latest_blocks( - vec![expected_blocks.pop().unwrap()], + vec![block_2], AuthorityIndex::new_for_test(2), vec![our_index], None, @@ -1529,6 +1581,14 @@ mod tests { .await; // For peer 3 we don't give any block - and it should return an empty vector + network_client + .stub_fetch_latest_blocks( + vec![], + AuthorityIndex::new_for_test(3), + vec![our_index], + Some(Duration::from_secs(10)), + ) + .await; network_client .stub_fetch_latest_blocks( vec![], @@ -1546,10 +1606,11 @@ mod tests { commit_vote_monitor, block_verifier, dag_state, + true, ); // Wait at least for the timeout time - sleep(context.parameters.sync_last_proposed_block_timeout * 2).await; + sleep(context.parameters.sync_last_known_own_block_timeout * 2).await; // Assert that core has been called to set the min propose round assert_eq!( @@ -1557,6 +1618,19 @@ mod tests { vec![10] ); + // Ensure that all the requests have been called + assert_eq!(network_client.fetch_latest_blocks_pending_calls().await, 0); + + // And we got one retry + assert_eq!( + context + .metrics + .node_metrics + .sync_last_known_own_block_retries + .get(), + 1 + ); + // Ensure that no panic occurred if let Err(err) = handle.stop().await { if err.is_panic() { diff --git a/crates/sui-core/src/consensus_manager/mysticeti_manager.rs b/crates/sui-core/src/consensus_manager/mysticeti_manager.rs index 8d9053f57ad06..6a0304f06b2d0 100644 --- a/crates/sui-core/src/consensus_manager/mysticeti_manager.rs +++ b/crates/sui-core/src/consensus_manager/mysticeti_manager.rs @@ -1,6 +1,6 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::{path::PathBuf, sync::Arc}; +use std::{path::PathBuf, sync::Arc, time::Duration}; use arc_swap::ArcSwapOption; use async_trait::async_trait; @@ -41,6 +41,7 @@ pub struct MysticetiManager { metrics: Arc, registry_service: RegistryService, authority: ArcSwapOption<(ConsensusAuthority, RegistryID)>, + boot_counter: Mutex, // Use a shared lazy mysticeti client so we can update the internal mysticeti // client that gets created for every new epoch. client: Arc, @@ -69,6 +70,7 @@ impl MysticetiManager { authority: ArcSwapOption::empty(), client, consensus_handler: Mutex::new(None), + boot_counter: Mutex::new(0), } } @@ -124,11 +126,17 @@ impl ConsensusManagerTrait for MysticetiManager { let consensus_config = config .consensus_config() .expect("consensus_config should exist"); - let parameters = Parameters { + + let mut parameters = Parameters { db_path: self.get_store_path(epoch), ..consensus_config.parameters.clone().unwrap_or_default() }; + // Disable the automated last known block sync for mainnet for now + if epoch_store.get_chain_identifier().chain() == sui_protocol_config::Chain::Mainnet { + parameters.sync_last_known_own_block_timeout = Duration::ZERO; + }; + let own_protocol_key = self.protocol_keypair.public(); let (own_index, _) = committee .authorities() @@ -149,6 +157,7 @@ impl ConsensusManagerTrait for MysticetiManager { // 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, own_index, @@ -160,10 +169,15 @@ impl ConsensusManagerTrait for MysticetiManager { Arc::new(tx_validator.clone()), consumer, registry.clone(), + boot_counter, ) .await; let client = authority.transaction_client(); + // Now increment the boot counter + let mut boot_counter = self.boot_counter.lock().await; + *boot_counter += 1; + let registry_id = self.registry_service.add(registry.clone()); self.authority diff --git a/crates/sui-core/src/unit_tests/mysticeti_manager_tests.rs b/crates/sui-core/src/unit_tests/mysticeti_manager_tests.rs index 394191e235bdd..f56e373ec8c54 100644 --- a/crates/sui-core/src/unit_tests/mysticeti_manager_tests.rs +++ b/crates/sui-core/src/unit_tests/mysticeti_manager_tests.rs @@ -29,32 +29,35 @@ async fn test_mysticeti_manager() { .committee_size(1.try_into().unwrap()) .build(); - for _i in 0..3 { - let config = &configs.validator_configs()[0]; + let config = &configs.validator_configs()[0]; - let consensus_config = config.consensus_config().unwrap(); - let registry_service = RegistryService::new(Registry::new()); - let secret = Arc::pin(config.protocol_key_pair().copy()); - let genesis = config.genesis().unwrap(); + let consensus_config = config.consensus_config().unwrap(); + let registry_service = RegistryService::new(Registry::new()); + let secret = Arc::pin(config.protocol_key_pair().copy()); + let genesis = config.genesis().unwrap(); - let state = TestAuthorityBuilder::new() - .with_genesis_and_keypair(genesis, &secret) - .build() - .await; + let state = TestAuthorityBuilder::new() + .with_genesis_and_keypair(genesis, &secret) + .build() + .await; - let metrics = Arc::new(ConsensusManagerMetrics::new(&Registry::new())); - let epoch_store = state.epoch_store_for_testing(); - let client = Arc::new(LazyMysticetiClient::default()); - - let manager = MysticetiManager::new( - config.worker_key_pair().copy(), - config.network_key_pair().copy(), - consensus_config.db_path().to_path_buf(), - registry_service, - metrics, - client, - ); + let metrics = Arc::new(ConsensusManagerMetrics::new(&Registry::new())); + let epoch_store = state.epoch_store_for_testing(); + let client = Arc::new(LazyMysticetiClient::default()); + + let manager = MysticetiManager::new( + config.worker_key_pair().copy(), + config.network_key_pair().copy(), + consensus_config.db_path().to_path_buf(), + registry_service, + metrics, + client, + ); + let boot_counter = *manager.boot_counter.lock().await; + assert_eq!(boot_counter, 0); + + for i in 1..=3 { let consensus_handler_initializer = ConsensusHandlerInitializer::new_for_testing( state.clone(), checkpoint_service_for_testing(state.clone()), @@ -86,5 +89,8 @@ async fn test_mysticeti_manager() { // THEN assert!(!manager.is_running().await); + + let boot_counter = *manager.boot_counter.lock().await; + assert_eq!(boot_counter, i); } } From 2969b591b42c46f5bc9a876e6c897b5fd6fa746e Mon Sep 17 00:00:00 2001 From: mwtian <81660174+mwtian@users.noreply.github.com> Date: Thu, 22 Aug 2024 09:54:45 -0700 Subject: [PATCH 212/232] [Consensus] panic in tests when transaction exceeds consensus size limit (#19065) ## Description This makes the submission failure more obvious when it is due to exceeding size limit. ## Test plan CI --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- consensus/core/src/lib.rs | 2 +- crates/sui-core/src/mysticeti_adapter.rs | 20 ++++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/consensus/core/src/lib.rs b/consensus/core/src/lib.rs index d9823f44bea2e..729fe63c18153 100644 --- a/consensus/core/src/lib.rs +++ b/consensus/core/src/lib.rs @@ -44,7 +44,7 @@ pub use authority_node::ConsensusAuthority; pub use block::{BlockAPI, Round}; pub use commit::{CommitDigest, CommitIndex, CommitRef, CommittedSubDag}; pub use commit_consumer::{CommitConsumer, CommitConsumerMonitor}; -pub use transaction::{TransactionClient, TransactionVerifier, ValidationError}; +pub use transaction::{ClientError, TransactionClient, TransactionVerifier, ValidationError}; #[cfg(test)] #[path = "tests/randomized_tests.rs"] diff --git a/crates/sui-core/src/mysticeti_adapter.rs b/crates/sui-core/src/mysticeti_adapter.rs index 7fe53780e0e29..48bbb39b77a71 100644 --- a/crates/sui-core/src/mysticeti_adapter.rs +++ b/crates/sui-core/src/mysticeti_adapter.rs @@ -4,14 +4,14 @@ use std::{sync::Arc, time::Duration}; use arc_swap::{ArcSwapOption, Guard}; -use consensus_core::TransactionClient; +use consensus_core::{ClientError, TransactionClient}; use sui_types::{ error::{SuiError, SuiResult}, messages_consensus::{ConsensusTransaction, ConsensusTransactionKind}, }; use tap::prelude::*; use tokio::time::{sleep, Instant}; -use tracing::warn; +use tracing::{error, info, warn}; use crate::{ authority::authority_per_epoch_store::AuthorityPerEpochStore, @@ -92,9 +92,21 @@ impl SubmitToConsensus for LazyMysticetiClient { .expect("Client should always be returned") .submit(transactions_bytes) .await - .tap_err(|r| { + .tap_err(|err| { // Will be logged by caller as well. - warn!("Submit transactions failed with: {:?}", r); + let msg = format!("Transaction submission failed with: {:?}", err); + match err { + ClientError::ConsensusShuttingDown(_) => { + info!("{}", msg); + } + ClientError::OversizedTransaction(_, _) => { + if cfg!(debug_assertions) { + panic!("{}", msg); + } else { + error!("{}", msg); + } + } + }; }) .map_err(|err| SuiError::FailedToSubmitToConsensus(err.to_string()))?; From 22aa24d27ddbeaea0a3fbcfd6a329c80471418ae Mon Sep 17 00:00:00 2001 From: Todd Nowacki Date: Thu, 22 Aug 2024 11:04:16 -0700 Subject: [PATCH 213/232] [move] Rewrite verifier metering (#19036) ## Description - Added ability cache for verifier ability queries - Redid metering for typing, local safety, and reference safety ## Test plan - calibrated constants --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .../src/metered_verifier.rs | 8 +- crates/sui-protocol-config/src/lib.rs | 15 +- crates/sui-transaction-checks/src/lib.rs | 2 +- crates/sui/src/client_commands.rs | 2 +- .../src/unit_tests/prop_tests.rs | 38 +- .../src/unit_tests/code_unit_tests.rs | 82 +++- .../src/unit_tests/mod.rs | 2 +- .../unit_tests/negative_stack_size_tests.rs | 43 +- .../src/unit_tests/signature_tests.rs | 6 +- .../move-abstract-interpreter/src/absint.rs | 10 +- .../move/crates/move-binary-format/src/lib.rs | 6 +- .../crates/move-borrow-graph/src/graph.rs | 5 +- .../move-bytecode-verifier-meter/src/lib.rs | 1 + .../src/ability_cache.rs | 100 ++++ .../src/ability_field_requirements.rs | 33 +- .../src/code_unit_verifier.rs | 197 ++++---- .../src/control_flow.rs | 12 +- .../crates/move-bytecode-verifier/src/lib.rs | 1 + .../src/locals_safety/abstract_state.rs | 28 +- .../src/locals_safety/mod.rs | 9 +- .../src/reference_safety/abstract_state.rs | 441 ++++++++++++------ .../src/reference_safety/mod.rs | 81 ++-- .../move-bytecode-verifier/src/signature.rs | 43 +- .../move-bytecode-verifier/src/type_safety.rs | 329 +++++++------ .../move-bytecode-verifier/src/verifier.rs | 24 +- .../move-compiler/src/cfgir/borrows/state.rs | 2 +- .../crates/move-vm-config/src/verifier.rs | 16 +- .../src/unit_tests/mod.rs | 2 +- .../src/reference_safety/abstract_state.rs | 1 + .../src/unit_tests/mod.rs | 2 +- .../src/reference_safety/abstract_state.rs | 1 + .../src/unit_tests/mod.rs | 2 +- .../src/reference_safety/abstract_state.rs | 1 + 33 files changed, 971 insertions(+), 574 deletions(-) create mode 100644 external-crates/move/crates/move-bytecode-verifier/src/ability_cache.rs diff --git a/crates/sui-framework-tests/src/metered_verifier.rs b/crates/sui-framework-tests/src/metered_verifier.rs index 0d30fe4febfbe..3c8ca440704f5 100644 --- a/crates/sui-framework-tests/src/metered_verifier.rs +++ b/crates/sui-framework-tests/src/metered_verifier.rs @@ -35,7 +35,7 @@ fn test_metered_move_bytecode_verifier() { let protocol_config = ProtocolConfig::get_for_max_version_UNSAFE(); let mut verifier_config = protocol_config.verifier_config(/* for_signing */ true); - let mut meter_config = protocol_config.meter_config(); + let mut meter_config = protocol_config.meter_config_for_signing(); let registry = &Registry::new(); let bytecode_verifier_metrics = Arc::new(BytecodeVerifierMetrics::new(registry)); let mut meter = SuiVerifierMeter::new(meter_config.clone()); @@ -202,7 +202,7 @@ fn test_metered_move_bytecode_verifier() { let protocol_config = ProtocolConfig::get_for_max_version_UNSAFE(); let verifier_config = protocol_config.verifier_config(/* for_signing */ true); - let meter_config = protocol_config.meter_config(); + let meter_config = protocol_config.meter_config_for_signing(); // Check if the same meter is indeed used multiple invocations of the verifier let mut meter = SuiVerifierMeter::new(meter_config); @@ -229,7 +229,7 @@ fn test_meter_system_packages() { let protocol_config = ProtocolConfig::get_for_max_version_UNSAFE(); let verifier_config = protocol_config.verifier_config(/* for_signing */ true); - let meter_config = protocol_config.meter_config(); + let meter_config = protocol_config.meter_config_for_signing(); let registry = &Registry::new(); let bytecode_verifier_metrics = Arc::new(BytecodeVerifierMetrics::new(registry)); let mut meter = SuiVerifierMeter::new(meter_config); @@ -283,7 +283,7 @@ fn test_build_and_verify_programmability_examples() { let protocol_config = ProtocolConfig::get_for_max_version_UNSAFE(); let verifier_config = protocol_config.verifier_config(/* for_signing */ true); - let meter_config = protocol_config.meter_config(); + let meter_config = protocol_config.meter_config_for_signing(); let registry = &Registry::new(); let bytecode_verifier_metrics = Arc::new(BytecodeVerifierMetrics::new(registry)); let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../examples"); diff --git a/crates/sui-protocol-config/src/lib.rs b/crates/sui-protocol-config/src/lib.rs index 3d679dafe056a..370bb61286a09 100644 --- a/crates/sui-protocol-config/src/lib.rs +++ b/crates/sui-protocol-config/src/lib.rs @@ -2736,16 +2736,13 @@ impl ProtocolConfig { } } - pub fn meter_config(&self) -> MeterConfig { + /// MeterConfig for metering packages during signing. It is NOT stable between binaries and + /// cannot used during execution. + pub fn meter_config_for_signing(&self) -> MeterConfig { MeterConfig { - max_per_fun_meter_units: Some(self.max_verifier_meter_ticks_per_function() as u128), - max_per_mod_meter_units: Some(self.max_meter_ticks_per_module() as u128), - max_per_pkg_meter_units: Some( - // Until the per-package limit was introduced, the per-module limit played double - // duty. - self.max_meter_ticks_per_package_as_option() - .unwrap_or_else(|| self.max_meter_ticks_per_module()) as u128, - ), + max_per_fun_meter_units: Some(2_200_000), + max_per_mod_meter_units: Some(2_200_000), + max_per_pkg_meter_units: Some(2_200_000), } } diff --git a/crates/sui-transaction-checks/src/lib.rs b/crates/sui-transaction-checks/src/lib.rs index 2e399e24b9820..1188850a135b2 100644 --- a/crates/sui-transaction-checks/src/lib.rs +++ b/crates/sui-transaction-checks/src/lib.rs @@ -562,7 +562,7 @@ mod checked { // Use the same verifier and meter for all packages, custom configured for signing. let for_signing = true; let mut verifier = sui_execution::verifier(protocol_config, for_signing, metrics); - let mut meter = verifier.meter(protocol_config.meter_config()); + let mut meter = verifier.meter(protocol_config.meter_config_for_signing()); // Measure time for verifying all packages in the PTB let shared_meter_verifier_timer = metrics diff --git a/crates/sui/src/client_commands.rs b/crates/sui/src/client_commands.rs index 018fcc200177a..58d6a75f60d96 100644 --- a/crates/sui/src/client_commands.rs +++ b/crates/sui/src/client_commands.rs @@ -1106,7 +1106,7 @@ impl SuiClientCommands { let mut used_ticks = meter.accumulator(Scope::Package).clone(); used_ticks.name = pkg_name; - let meter_config = protocol_config.meter_config(); + let meter_config = protocol_config.meter_config_for_signing(); let exceeded = matches!( meter_config.max_per_pkg_meter_units, diff --git a/external-crates/move/crates/bytecode-verifier-prop-tests/src/unit_tests/prop_tests.rs b/external-crates/move/crates/bytecode-verifier-prop-tests/src/unit_tests/prop_tests.rs index 2d799592a7107..518ead16b9a99 100644 --- a/external-crates/move/crates/bytecode-verifier-prop-tests/src/unit_tests/prop_tests.rs +++ b/external-crates/move/crates/bytecode-verifier-prop-tests/src/unit_tests/prop_tests.rs @@ -13,9 +13,11 @@ use move_binary_format::{ proptest_types::CompiledModuleStrategyGen, }; use move_bytecode_verifier::{ - ability_field_requirements, constants, instantiation_loops::InstantiationLoopChecker, - DuplicationChecker, InstructionConsistency, RecursiveDataDefChecker, SignatureChecker, + ability_cache::AbilityCache, ability_field_requirements, constants, + instantiation_loops::InstantiationLoopChecker, DuplicationChecker, InstructionConsistency, + RecursiveDataDefChecker, SignatureChecker, }; +use move_bytecode_verifier_meter::dummy::DummyMeter; use move_core_types::{ account_address::AccountAddress, identifier::Identifier, vm_status::StatusCode, }; @@ -28,7 +30,9 @@ proptest! { #[test] fn valid_ability_transitivity(module in CompiledModule::valid_strategy(20)) { - prop_assert!(ability_field_requirements::verify_module(&module).is_ok()); + let module = &module; + let ability_cache = &mut AbilityCache::new(module); + prop_assert!(ability_field_requirements::verify_module(module, ability_cache, &mut DummyMeter).is_ok()); } #[test] @@ -101,18 +105,22 @@ proptest! { #[test] fn check_verifier_passes(module in CompiledModule::valid_strategy(20)) { - DuplicationChecker::verify_module(&module).expect("DuplicationChecker failure"); - SignatureChecker::verify_module(&module).expect("SignatureChecker failure"); - InstructionConsistency::verify_module(&module).expect("InstructionConsistency failure"); - constants::verify_module(&module).expect("constants failure"); - ability_field_requirements::verify_module(&module).expect("ability_field_requirements failure"); - RecursiveDataDefChecker::verify_module(&module).expect("RecursiveDataDefChecker failure"); - InstantiationLoopChecker::verify_module(&module).expect("InstantiationLoopChecker failure"); + let module = &module; + let ability_cache = &mut AbilityCache::new(module); + DuplicationChecker::verify_module(module).expect("DuplicationChecker failure"); + SignatureChecker::verify_module(module, ability_cache, &mut DummyMeter).expect("SignatureChecker failure"); + InstructionConsistency::verify_module(module).expect("InstructionConsistency failure"); + constants::verify_module(module).expect("constants failure"); + ability_field_requirements::verify_module(module, ability_cache, &mut DummyMeter).expect("ability_field_requirements failure"); + RecursiveDataDefChecker::verify_module(module).expect("RecursiveDataDefChecker failure"); + InstantiationLoopChecker::verify_module(module).expect("InstantiationLoopChecker failure"); } #[test] fn valid_signatures(module in CompiledModule::valid_strategy(20)) { - prop_assert!(SignatureChecker::verify_module(&module).is_ok()) + let module = &module; + let ability_cache = &mut AbilityCache::new(module); + prop_assert!(SignatureChecker::verify_module(module, ability_cache, &mut DummyMeter).is_ok()) } #[test] @@ -123,7 +131,9 @@ proptest! { let context = SignatureRefMutation::new(&mut module, mutations); let expected_violations = context.apply(); - let result = SignatureChecker::verify_module(&module); + let module = &module; + let ability_cache = &mut AbilityCache::new(module); + let result = SignatureChecker::verify_module(module, ability_cache, &mut DummyMeter); prop_assert_eq!(expected_violations, result.is_err()); } @@ -136,7 +146,9 @@ proptest! { let context = FieldRefMutation::new(&mut module, mutations); let expected_violations = context.apply(); - let result = SignatureChecker::verify_module(&module); + let module = &module; + let ability_cache = &mut AbilityCache::new(module); + let result = SignatureChecker::verify_module(module, ability_cache, &mut DummyMeter); prop_assert_eq!(expected_violations, result.is_err()); } diff --git a/external-crates/move/crates/bytecode-verifier-tests/src/unit_tests/code_unit_tests.rs b/external-crates/move/crates/bytecode-verifier-tests/src/unit_tests/code_unit_tests.rs index befa382686d74..9993b6d0c4027 100644 --- a/external-crates/move/crates/bytecode-verifier-tests/src/unit_tests/code_unit_tests.rs +++ b/external-crates/move/crates/bytecode-verifier-tests/src/unit_tests/code_unit_tests.rs @@ -4,15 +4,22 @@ use crate::support::dummy_procedure_module; use move_binary_format::file_format::Bytecode; -use move_bytecode_verifier::CodeUnitVerifier; +use move_bytecode_verifier::ability_cache::AbilityCache; +use move_bytecode_verifier::code_unit_verifier; use move_bytecode_verifier_meter::dummy::DummyMeter; use move_core_types::vm_status::StatusCode; use move_vm_config::verifier::VerifierConfig; #[test] fn invalid_fallthrough_br_true() { - let module = dummy_procedure_module(vec![Bytecode::LdFalse, Bytecode::BrTrue(1)]); - let result = CodeUnitVerifier::verify_module(&Default::default(), &module, &mut DummyMeter); + let module = &dummy_procedure_module(vec![Bytecode::LdFalse, Bytecode::BrTrue(1)]); + let ability_cache = &mut AbilityCache::new(module); + let result = code_unit_verifier::verify_module( + &Default::default(), + module, + ability_cache, + &mut DummyMeter, + ); assert_eq!( result.unwrap_err().major_status(), StatusCode::INVALID_FALL_THROUGH @@ -21,8 +28,14 @@ fn invalid_fallthrough_br_true() { #[test] fn invalid_fallthrough_br_false() { - let module = dummy_procedure_module(vec![Bytecode::LdTrue, Bytecode::BrFalse(1)]); - let result = CodeUnitVerifier::verify_module(&Default::default(), &module, &mut DummyMeter); + let module = &dummy_procedure_module(vec![Bytecode::LdTrue, Bytecode::BrFalse(1)]); + let ability_cache = &mut AbilityCache::new(module); + let result = code_unit_verifier::verify_module( + &Default::default(), + module, + ability_cache, + &mut DummyMeter, + ); assert_eq!( result.unwrap_err().major_status(), StatusCode::INVALID_FALL_THROUGH @@ -32,8 +45,14 @@ fn invalid_fallthrough_br_false() { // all non-branch instructions should trigger invalid fallthrough; just check one of them #[test] fn invalid_fallthrough_non_branch() { - let module = dummy_procedure_module(vec![Bytecode::LdTrue, Bytecode::Pop]); - let result = CodeUnitVerifier::verify_module(&Default::default(), &module, &mut DummyMeter); + let module = &dummy_procedure_module(vec![Bytecode::LdTrue, Bytecode::Pop]); + let ability_cache = &mut AbilityCache::new(module); + let result = code_unit_verifier::verify_module( + &Default::default(), + module, + ability_cache, + &mut DummyMeter, + ); assert_eq!( result.unwrap_err().major_status(), StatusCode::INVALID_FALL_THROUGH @@ -42,22 +61,40 @@ fn invalid_fallthrough_non_branch() { #[test] fn valid_fallthrough_branch() { - let module = dummy_procedure_module(vec![Bytecode::Branch(0)]); - let result = CodeUnitVerifier::verify_module(&Default::default(), &module, &mut DummyMeter); + let module = &dummy_procedure_module(vec![Bytecode::Branch(0)]); + let ability_cache = &mut AbilityCache::new(module); + let result = code_unit_verifier::verify_module( + &Default::default(), + module, + ability_cache, + &mut DummyMeter, + ); assert!(result.is_ok()); } #[test] fn valid_fallthrough_ret() { - let module = dummy_procedure_module(vec![Bytecode::Ret]); - let result = CodeUnitVerifier::verify_module(&Default::default(), &module, &mut DummyMeter); + let module = &dummy_procedure_module(vec![Bytecode::Ret]); + let ability_cache = &mut AbilityCache::new(module); + let result = code_unit_verifier::verify_module( + &Default::default(), + module, + ability_cache, + &mut DummyMeter, + ); assert!(result.is_ok()); } #[test] fn valid_fallthrough_abort() { - let module = dummy_procedure_module(vec![Bytecode::LdU64(7), Bytecode::Abort]); - let result = CodeUnitVerifier::verify_module(&Default::default(), &module, &mut DummyMeter); + let module = &dummy_procedure_module(vec![Bytecode::LdU64(7), Bytecode::Abort]); + let ability_cache = &mut AbilityCache::new(module); + let result = code_unit_verifier::verify_module( + &Default::default(), + module, + ability_cache, + &mut DummyMeter, + ); assert!(result.is_ok()); } @@ -68,10 +105,15 @@ fn test_max_number_of_bytecode() { nops.push(Bytecode::Nop); } nops.push(Bytecode::Ret); - let module = dummy_procedure_module(nops); + let module = &dummy_procedure_module(nops); + let ability_cache = &mut AbilityCache::new(module); - let result = - CodeUnitVerifier::verify_module(&VerifierConfig::default(), &module, &mut DummyMeter); + let result = code_unit_verifier::verify_module( + &VerifierConfig::default(), + module, + ability_cache, + &mut DummyMeter, + ); assert!(result.is_ok()); } @@ -81,14 +123,16 @@ fn test_max_basic_blocks() { .map(|idx| Bytecode::Branch(idx + 1)) .collect::>(); code.push(Bytecode::Ret); - let module = dummy_procedure_module(code); + let module = &dummy_procedure_module(code); + let ability_cache = &mut AbilityCache::new(module); - let result = CodeUnitVerifier::verify_module( + let result = code_unit_verifier::verify_module( &VerifierConfig { max_basic_blocks: Some(16), ..Default::default() }, - &module, + module, + ability_cache, &mut DummyMeter, ); assert_eq!( diff --git a/external-crates/move/crates/bytecode-verifier-tests/src/unit_tests/mod.rs b/external-crates/move/crates/bytecode-verifier-tests/src/unit_tests/mod.rs index 17676ad1fe557..c3a6a22b7e08b 100644 --- a/external-crates/move/crates/bytecode-verifier-tests/src/unit_tests/mod.rs +++ b/external-crates/move/crates/bytecode-verifier-tests/src/unit_tests/mod.rs @@ -53,6 +53,6 @@ pub(crate) fn production_config() -> (VerifierConfig, MeterConfig) { bytecode_version: VERSION_MAX, max_variants_in_enum: Some(DEFAULT_MAX_VARIANTS), }, - MeterConfig::default(), + MeterConfig::old_default(), ) } diff --git a/external-crates/move/crates/bytecode-verifier-tests/src/unit_tests/negative_stack_size_tests.rs b/external-crates/move/crates/bytecode-verifier-tests/src/unit_tests/negative_stack_size_tests.rs index 9f9eb9327e916..1b160ce6be167 100644 --- a/external-crates/move/crates/bytecode-verifier-tests/src/unit_tests/negative_stack_size_tests.rs +++ b/external-crates/move/crates/bytecode-verifier-tests/src/unit_tests/negative_stack_size_tests.rs @@ -4,14 +4,21 @@ use crate::support::dummy_procedure_module; use move_binary_format::file_format::Bytecode; -use move_bytecode_verifier::CodeUnitVerifier; +use move_bytecode_verifier::ability_cache::AbilityCache; +use move_bytecode_verifier::code_unit_verifier; use move_bytecode_verifier_meter::dummy::DummyMeter; use move_core_types::vm_status::StatusCode; #[test] fn one_pop_no_push() { - let module = dummy_procedure_module(vec![Bytecode::Pop, Bytecode::Ret]); - let result = CodeUnitVerifier::verify_module(&Default::default(), &module, &mut DummyMeter); + let module = &dummy_procedure_module(vec![Bytecode::Pop, Bytecode::Ret]); + let ability_cache = &mut AbilityCache::new(module); + let result = code_unit_verifier::verify_module( + &Default::default(), + module, + ability_cache, + &mut DummyMeter, + ); assert_eq!( result.unwrap_err().major_status(), StatusCode::NEGATIVE_STACK_SIZE_WITHIN_BLOCK @@ -21,8 +28,14 @@ fn one_pop_no_push() { #[test] fn one_pop_one_push() { // Height: 0 + (-1 + 1) = 0 would have passed original usage verifier - let module = dummy_procedure_module(vec![Bytecode::ReadRef, Bytecode::Ret]); - let result = CodeUnitVerifier::verify_module(&Default::default(), &module, &mut DummyMeter); + let module = &dummy_procedure_module(vec![Bytecode::ReadRef, Bytecode::Ret]); + let ability_cache = &mut AbilityCache::new(module); + let result = code_unit_verifier::verify_module( + &Default::default(), + module, + ability_cache, + &mut DummyMeter, + ); assert_eq!( result.unwrap_err().major_status(), StatusCode::NEGATIVE_STACK_SIZE_WITHIN_BLOCK @@ -32,8 +45,14 @@ fn one_pop_one_push() { #[test] fn two_pop_one_push() { // Height: 0 + 1 + (-2 + 1) = 0 would have passed original usage verifier - let module = dummy_procedure_module(vec![Bytecode::LdU64(0), Bytecode::Add, Bytecode::Ret]); - let result = CodeUnitVerifier::verify_module(&Default::default(), &module, &mut DummyMeter); + let module = &dummy_procedure_module(vec![Bytecode::LdU64(0), Bytecode::Add, Bytecode::Ret]); + let ability_cache = &mut AbilityCache::new(module); + let result = code_unit_verifier::verify_module( + &Default::default(), + module, + ability_cache, + &mut DummyMeter, + ); assert_eq!( result.unwrap_err().major_status(), StatusCode::NEGATIVE_STACK_SIZE_WITHIN_BLOCK @@ -42,8 +61,14 @@ fn two_pop_one_push() { #[test] fn two_pop_no_push() { - let module = dummy_procedure_module(vec![Bytecode::WriteRef, Bytecode::Ret]); - let result = CodeUnitVerifier::verify_module(&Default::default(), &module, &mut DummyMeter); + let module = &dummy_procedure_module(vec![Bytecode::WriteRef, Bytecode::Ret]); + let ability_cache = &mut AbilityCache::new(module); + let result = code_unit_verifier::verify_module( + &Default::default(), + module, + ability_cache, + &mut DummyMeter, + ); assert_eq!( result.unwrap_err().major_status(), StatusCode::NEGATIVE_STACK_SIZE_WITHIN_BLOCK diff --git a/external-crates/move/crates/bytecode-verifier-tests/src/unit_tests/signature_tests.rs b/external-crates/move/crates/bytecode-verifier-tests/src/unit_tests/signature_tests.rs index ad7b496b0a23f..b59ef9aa05152 100644 --- a/external-crates/move/crates/bytecode-verifier-tests/src/unit_tests/signature_tests.rs +++ b/external-crates/move/crates/bytecode-verifier-tests/src/unit_tests/signature_tests.rs @@ -7,7 +7,8 @@ use move_binary_format::file_format::{ Bytecode::*, CompiledModule, SignatureToken::*, Visibility::Public, *, }; use move_bytecode_verifier::{ - verify_module_unmetered, verify_module_with_config_for_test, SignatureChecker, + ability_cache::AbilityCache, verify_module_unmetered, verify_module_with_config_for_test, + SignatureChecker, }; use move_bytecode_verifier_meter::dummy::DummyMeter; use move_core_types::{ @@ -20,7 +21,8 @@ fn test_reference_of_reference() { m.signatures[0] = Signature(vec![Reference(Box::new(Reference(Box::new( SignatureToken::Bool, ))))]); - let errors = SignatureChecker::verify_module(&m); + let ability_cache = &mut AbilityCache::new(&m); + let errors = SignatureChecker::verify_module(&m, ability_cache, &mut DummyMeter); assert!(errors.is_err()); } diff --git a/external-crates/move/crates/move-abstract-interpreter/src/absint.rs b/external-crates/move/crates/move-abstract-interpreter/src/absint.rs index 5742c01504cf0..d8dc34a0a5fd2 100644 --- a/external-crates/move/crates/move-abstract-interpreter/src/absint.rs +++ b/external-crates/move/crates/move-abstract-interpreter/src/absint.rs @@ -202,23 +202,23 @@ impl<'a> FunctionContext<'a> { self.index } - pub fn code(&self) -> &CodeUnit { + pub fn code(&self) -> &'a CodeUnit { self.code } - pub fn parameters(&self) -> &Signature { + pub fn parameters(&self) -> &'a Signature { self.parameters } - pub fn return_(&self) -> &Signature { + pub fn return_(&self) -> &'a Signature { self.return_ } - pub fn locals(&self) -> &Signature { + pub fn locals(&self) -> &'a Signature { self.locals } - pub fn type_parameters(&self) -> &[AbilitySet] { + pub fn type_parameters(&self) -> &'a [AbilitySet] { self.type_parameters } diff --git a/external-crates/move/crates/move-binary-format/src/lib.rs b/external-crates/move/crates/move-binary-format/src/lib.rs index 2490f2d247205..78e3607f7dfc8 100644 --- a/external-crates/move/crates/move-binary-format/src/lib.rs +++ b/external-crates/move/crates/move-binary-format/src/lib.rs @@ -161,8 +161,10 @@ macro_rules! safe_unwrap { match $e { Some(x) => x, None => { - let err = PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) - .with_message(format!("{}:{} (none)", file!(), line!())); + let err = move_binary_format::errors::PartialVMError::new( + move_core_types::vm_status::StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR, + ) + .with_message(format!("{}:{} (none)", file!(), line!())); if cfg!(debug_assertions) { panic!("{:?}", err); } else { diff --git a/external-crates/move/crates/move-borrow-graph/src/graph.rs b/external-crates/move/crates/move-borrow-graph/src/graph.rs index bb3dfe4bf0b29..0f51ed4e5bc05 100644 --- a/external-crates/move/crates/move-borrow-graph/src/graph.rs +++ b/external-crates/move/crates/move-borrow-graph/src/graph.rs @@ -232,19 +232,21 @@ impl BorrowGraph { /// Remove reference `id` from the graph /// Fixes any transitive borrows, so if `parent` borrowed by `id` borrowed by `child` /// After the release, `parent` borrowed by `child` - pub fn release(&mut self, id: RefID) { + pub fn release(&mut self, id: RefID) -> usize { debug_assert!(self.check_invariant()); let Ref { borrowed_by, borrows_from, .. } = self.0.remove(&id).unwrap(); + let mut released_edges = 0; for parent_ref_id in borrows_from.into_iter() { let parent = self.0.get_mut(&parent_ref_id).unwrap(); let parent_edges = parent.borrowed_by.0.remove(&id).unwrap(); for parent_edge in parent_edges { for (child_ref_id, child_edges) in &borrowed_by.0 { for child_edge in child_edges { + released_edges += 1; self.splice_out_intermediate( parent_ref_id, &parent_edge, @@ -260,6 +262,7 @@ impl BorrowGraph { child.borrows_from.remove(&id); } debug_assert!(self.check_invariant()); + released_edges } fn splice_out_intermediate( diff --git a/external-crates/move/crates/move-bytecode-verifier-meter/src/lib.rs b/external-crates/move/crates/move-bytecode-verifier-meter/src/lib.rs index 64bc6bd6e123f..ce90b1e1db8f8 100644 --- a/external-crates/move/crates/move-bytecode-verifier-meter/src/lib.rs +++ b/external-crates/move/crates/move-bytecode-verifier-meter/src/lib.rs @@ -46,6 +46,7 @@ pub trait Meter { } /// Adds the number of items with growth factor + #[deprecated(note = "this function is extremely slow and should be avoided")] fn add_items_with_growth( &mut self, scope: Scope, diff --git a/external-crates/move/crates/move-bytecode-verifier/src/ability_cache.rs b/external-crates/move/crates/move-bytecode-verifier/src/ability_cache.rs new file mode 100644 index 0000000000000..39ba92b79c49c --- /dev/null +++ b/external-crates/move/crates/move-bytecode-verifier/src/ability_cache.rs @@ -0,0 +1,100 @@ +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +use move_binary_format::{ + errors::PartialVMResult, + file_format::{AbilitySet, DatatypeHandleIndex, SignatureToken}, + safe_unwrap, CompiledModule, +}; +use move_bytecode_verifier_meter::{Meter, Scope}; +use std::{ + cmp::max, + collections::{btree_map::Entry, BTreeMap}, +}; + +const TYPE_ARG_COST: u128 = 1; + +pub struct AbilityCache<'a> { + module: &'a CompiledModule, + vector_results: BTreeMap, + datatype_results: BTreeMap, AbilitySet>>, +} + +impl<'a> AbilityCache<'a> { + pub fn new(module: &'a CompiledModule) -> Self { + Self { + module, + vector_results: BTreeMap::new(), + datatype_results: BTreeMap::new(), + } + } + + pub fn abilities( + &mut self, + scope: Scope, + meter: &mut (impl Meter + ?Sized), + type_parameter_abilities: &[AbilitySet], + ty: &SignatureToken, + ) -> PartialVMResult { + use SignatureToken as S; + + Ok(match ty { + S::Bool | S::U8 | S::U16 | S::U32 | S::U64 | S::U128 | S::U256 | S::Address => { + AbilitySet::PRIMITIVES + } + + S::Reference(_) | S::MutableReference(_) => AbilitySet::REFERENCES, + S::Signer => AbilitySet::SIGNER, + S::TypeParameter(idx) => *safe_unwrap!(type_parameter_abilities.get(*idx as usize)), + S::Datatype(idx) => { + let sh = self.module.datatype_handle_at(*idx); + sh.abilities + } + S::Vector(inner) => { + let inner_abilities = + self.abilities(scope, meter, type_parameter_abilities, inner)?; + let entry = self.vector_results.entry(inner_abilities); + match entry { + Entry::Occupied(entry) => *entry.get(), + Entry::Vacant(entry) => { + meter.add(scope, TYPE_ARG_COST)?; + let abilities = AbilitySet::polymorphic_abilities( + AbilitySet::VECTOR, + vec![false], + vec![inner_abilities], + )?; + entry.insert(abilities); + abilities + } + } + } + S::DatatypeInstantiation(inst) => { + let (idx, type_args) = &**inst; + let type_arg_abilities = type_args + .iter() + .map(|arg| self.abilities(scope, meter, type_parameter_abilities, arg)) + .collect::>>()?; + let entry = self + .datatype_results + .entry(*idx) + .or_default() + .entry(type_arg_abilities.clone()); + match entry { + Entry::Occupied(entry) => *entry.get(), + Entry::Vacant(entry) => { + meter.add_items(scope, TYPE_ARG_COST, max(type_args.len(), 1))?; + let sh = self.module.datatype_handle_at(*idx); + let declared_abilities = sh.abilities; + let abilities = AbilitySet::polymorphic_abilities( + declared_abilities, + sh.type_parameters.iter().map(|param| param.is_phantom), + type_arg_abilities, + )?; + entry.insert(abilities); + abilities + } + } + } + }) + } +} diff --git a/external-crates/move/crates/move-bytecode-verifier/src/ability_field_requirements.rs b/external-crates/move/crates/move-bytecode-verifier/src/ability_field_requirements.rs index 1c868ad3856ad..3d462dcd04197 100644 --- a/external-crates/move/crates/move-bytecode-verifier/src/ability_field_requirements.rs +++ b/external-crates/move/crates/move-bytecode-verifier/src/ability_field_requirements.rs @@ -4,18 +4,29 @@ //! This module implements a checker for verifying that all of the struct's fields satisfy the //! abilities required by the struct's abilities +use crate::ability_cache::AbilityCache; use move_binary_format::{ errors::{verification_error, Location, PartialVMResult, VMResult}, file_format::{AbilitySet, CompiledModule, StructFieldInformation, TableIndex}, IndexKind, }; +use move_bytecode_verifier_meter::{Meter, Scope}; use move_core_types::vm_status::StatusCode; -pub fn verify_module(module: &CompiledModule) -> VMResult<()> { - verify_module_impl(module).map_err(|e| e.finish(Location::Module(module.self_id()))) +pub fn verify_module<'env>( + module: &'env CompiledModule, + ability_cache: &mut AbilityCache<'env>, + meter: &mut (impl Meter + ?Sized), +) -> VMResult<()> { + verify_module_impl(module, ability_cache, meter) + .map_err(|e| e.finish(Location::Module(module.self_id()))) } -fn verify_module_impl(module: &CompiledModule) -> PartialVMResult<()> { +fn verify_module_impl<'env>( + module: &'env CompiledModule, + ability_cache: &mut AbilityCache<'env>, + meter: &mut (impl Meter + ?Sized), +) -> PartialVMResult<()> { for (idx, struct_def) in module.struct_defs().iter().enumerate() { let sh = module.datatype_handle_at(struct_def.struct_handle); let fields = match &struct_def.field_information { @@ -35,8 +46,12 @@ fn verify_module_impl(module: &CompiledModule) -> PartialVMResult<()> { .map(|_| AbilitySet::ALL) .collect::>(); for field in fields { - let field_abilities = - module.abilities(&field.signature.0, &type_parameter_abilities)?; + let field_abilities = ability_cache.abilities( + Scope::Module, + meter, + &type_parameter_abilities, + &field.signature.0, + )?; if !required_abilities.is_subset(field_abilities) { return Err(verification_error( StatusCode::FIELD_MISSING_TYPE_ABILITY, @@ -63,8 +78,12 @@ fn verify_module_impl(module: &CompiledModule) -> PartialVMResult<()> { .collect::>(); for (i, variant) in enum_def.variants.iter().enumerate() { for (fi, field) in variant.fields.iter().enumerate() { - let field_abilities = - module.abilities(&field.signature.0, &type_parameter_abilities)?; + let field_abilities = ability_cache.abilities( + Scope::Module, + meter, + &type_parameter_abilities, + &field.signature.0, + )?; if !required_abilities.is_subset(field_abilities) { return Err(verification_error( StatusCode::FIELD_MISSING_TYPE_ABILITY, diff --git a/external-crates/move/crates/move-bytecode-verifier/src/code_unit_verifier.rs b/external-crates/move/crates/move-bytecode-verifier/src/code_unit_verifier.rs index a3b9a0ab07fa9..f8971d78924e4 100644 --- a/external-crates/move/crates/move-bytecode-verifier/src/code_unit_verifier.rs +++ b/external-crates/move/crates/move-bytecode-verifier/src/code_unit_verifier.rs @@ -6,8 +6,8 @@ //! The overall verification is split between stack_usage_verifier.rs and //! abstract_interpreter.rs. CodeUnitVerifier simply orchestrates calls into these two files. use crate::{ - acquires_list_verifier::AcquiresVerifier, control_flow, locals_safety, reference_safety, - stack_usage_verifier::StackUsageVerifier, type_safety, + ability_cache::AbilityCache, acquires_list_verifier::AcquiresVerifier, control_flow, + locals_safety, reference_safety, stack_usage_verifier::StackUsageVerifier, type_safety, }; use move_abstract_interpreter::{absint::FunctionContext, control_flow_graph::ControlFlowGraph}; use move_binary_format::{ @@ -22,123 +22,128 @@ use move_core_types::vm_status::StatusCode; use move_vm_config::verifier::VerifierConfig; use std::collections::HashMap; -pub struct CodeUnitVerifier<'a> { - module: &'a CompiledModule, - function_context: FunctionContext<'a>, +pub struct CodeUnitVerifier<'env, 'a> { + module: &'env CompiledModule, + function_context: FunctionContext<'env>, name_def_map: &'a HashMap, } -impl<'a> CodeUnitVerifier<'a> { - pub fn verify_module( - verifier_config: &VerifierConfig, - module: &'a CompiledModule, - meter: &mut (impl Meter + ?Sized), - ) -> VMResult<()> { - Self::verify_module_impl(verifier_config, module, meter) - .map_err(|e| e.finish(Location::Module(module.self_id()))) - } +pub fn verify_module<'env>( + verifier_config: &VerifierConfig, + module: &'env CompiledModule, + ability_cache: &mut AbilityCache<'env>, + meter: &mut (impl Meter + ?Sized), +) -> VMResult<()> { + verify_module_impl(verifier_config, module, ability_cache, meter) + .map_err(|e| e.finish(Location::Module(module.self_id()))) +} - fn verify_module_impl( - verifier_config: &VerifierConfig, - module: &CompiledModule, - meter: &mut (impl Meter + ?Sized), - ) -> PartialVMResult<()> { - let mut name_def_map = HashMap::new(); - for (idx, func_def) in module.function_defs().iter().enumerate() { - let fh = module.function_handle_at(func_def.function); - name_def_map.insert(fh.name, FunctionDefinitionIndex(idx as u16)); - } - let mut total_back_edges = 0; - for (idx, function_definition) in module.function_defs().iter().enumerate() { - let index = FunctionDefinitionIndex(idx as TableIndex); - let num_back_edges = Self::verify_function( - verifier_config, - index, - function_definition, - module, - &name_def_map, - meter, - ) - .map_err(|err| err.at_index(IndexKind::FunctionDefinition, index.0))?; - total_back_edges += num_back_edges; - } - if let Some(limit) = verifier_config.max_back_edges_per_module { - if total_back_edges > limit { - return Err(PartialVMError::new(StatusCode::TOO_MANY_BACK_EDGES)); - } - } - Ok(()) +fn verify_module_impl<'env>( + verifier_config: &VerifierConfig, + module: &'env CompiledModule, + ability_cache: &mut AbilityCache<'env>, + meter: &mut (impl Meter + ?Sized), +) -> PartialVMResult<()> { + let mut name_def_map = HashMap::new(); + for (idx, func_def) in module.function_defs().iter().enumerate() { + let fh = module.function_handle_at(func_def.function); + name_def_map.insert(fh.name, FunctionDefinitionIndex(idx as u16)); } - - fn verify_function( - verifier_config: &VerifierConfig, - index: FunctionDefinitionIndex, - function_definition: &FunctionDefinition, - module: &CompiledModule, - name_def_map: &HashMap, - meter: &mut (impl Meter + ?Sized), - ) -> PartialVMResult { - meter.enter_scope( - module - .identifier_at(module.function_handle_at(function_definition.function).name) - .as_str(), - Scope::Function, - ); - // nothing to verify for native function - let code = match &function_definition.code { - Some(code) => code, - None => return Ok(0), - }; - - // create `FunctionContext` and `BinaryIndexedView` - let function_context = control_flow::verify_function( + let mut total_back_edges = 0; + for (idx, function_definition) in module.function_defs().iter().enumerate() { + let index = FunctionDefinitionIndex(idx as TableIndex); + let num_back_edges = verify_function( verifier_config, - module, index, function_definition, - code, + module, + ability_cache, + &name_def_map, meter, - )?; + ) + .map_err(|err| err.at_index(IndexKind::FunctionDefinition, index.0))?; + total_back_edges += num_back_edges; + } + if let Some(limit) = verifier_config.max_back_edges_per_module { + if total_back_edges > limit { + return Err(PartialVMError::new(StatusCode::TOO_MANY_BACK_EDGES)); + } + } + Ok(()) +} - if let Some(limit) = verifier_config.max_basic_blocks { - if function_context.cfg().blocks().len() > limit { - return Err( - PartialVMError::new(StatusCode::TOO_MANY_BASIC_BLOCKS).at_code_offset(index, 0) - ); - } +fn verify_function<'env>( + verifier_config: &VerifierConfig, + index: FunctionDefinitionIndex, + function_definition: &'env FunctionDefinition, + module: &'env CompiledModule, + ability_cache: &mut AbilityCache<'env>, + name_def_map: &HashMap, + meter: &mut (impl Meter + ?Sized), +) -> PartialVMResult { + meter.enter_scope( + module + .identifier_at(module.function_handle_at(function_definition.function).name) + .as_str(), + Scope::Function, + ); + // nothing to verify for native function + let code = match &function_definition.code { + Some(code) => code, + None => return Ok(0), + }; + + // create `FunctionContext` and `BinaryIndexedView` + let function_context = control_flow::verify_function( + verifier_config, + module, + index, + function_definition, + code, + meter, + )?; + + if let Some(limit) = verifier_config.max_basic_blocks { + if function_context.cfg().blocks().len() > limit { + return Err( + PartialVMError::new(StatusCode::TOO_MANY_BASIC_BLOCKS).at_code_offset(index, 0) + ); } + } - let num_back_edges = function_context.cfg().num_back_edges(); - if let Some(limit) = verifier_config.max_back_edges_per_function { - if num_back_edges > limit { - return Err( - PartialVMError::new(StatusCode::TOO_MANY_BACK_EDGES).at_code_offset(index, 0) - ); - } + let num_back_edges = function_context.cfg().num_back_edges(); + if let Some(limit) = verifier_config.max_back_edges_per_function { + if num_back_edges > limit { + return Err( + PartialVMError::new(StatusCode::TOO_MANY_BACK_EDGES).at_code_offset(index, 0) + ); } + } - // verify - let code_unit_verifier = CodeUnitVerifier { - module, - function_context, - name_def_map, - }; - code_unit_verifier.verify_common(verifier_config, meter)?; - AcquiresVerifier::verify(module, index, function_definition, meter)?; + // verify + let code_unit_verifier = CodeUnitVerifier { + module, + function_context, + name_def_map, + }; + code_unit_verifier.verify_common(verifier_config, ability_cache, meter)?; + AcquiresVerifier::verify(module, index, function_definition, meter)?; - meter.transfer(Scope::Function, Scope::Module, 1.0)?; + meter.transfer(Scope::Function, Scope::Module, 1.0)?; - Ok(num_back_edges) - } + Ok(num_back_edges) +} +impl<'env, 'a> CodeUnitVerifier<'env, 'a> { fn verify_common( &self, verifier_config: &VerifierConfig, + ability_cache: &mut AbilityCache<'env>, meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult<()> { StackUsageVerifier::verify(verifier_config, self.module, &self.function_context, meter)?; - type_safety::verify(self.module, &self.function_context, meter)?; - locals_safety::verify(self.module, &self.function_context, meter)?; + type_safety::verify(self.module, &self.function_context, ability_cache, meter)?; + locals_safety::verify(self.module, &self.function_context, ability_cache, meter)?; reference_safety::verify( self.module, &self.function_context, diff --git a/external-crates/move/crates/move-bytecode-verifier/src/control_flow.rs b/external-crates/move/crates/move-bytecode-verifier/src/control_flow.rs index e32afcee37cff..07eb8b12991a0 100644 --- a/external-crates/move/crates/move-bytecode-verifier/src/control_flow.rs +++ b/external-crates/move/crates/move-bytecode-verifier/src/control_flow.rs @@ -29,14 +29,14 @@ use std::collections::BTreeSet; /// Perform control flow verification on the compiled function, returning its `FunctionContext` if /// verification was successful. -pub fn verify_function<'a>( - verifier_config: &'a VerifierConfig, - module: &'a CompiledModule, +pub fn verify_function<'env>( + verifier_config: &VerifierConfig, + module: &'env CompiledModule, index: FunctionDefinitionIndex, - function_definition: &'a FunctionDefinition, - code: &'a CodeUnit, + function_definition: &'env FunctionDefinition, + code: &'env CodeUnit, _meter: &mut (impl Meter + ?Sized), // TODO: metering -) -> PartialVMResult> { +) -> PartialVMResult> { let function_handle = module.function_handle_at(function_definition.function); if module.version() <= 5 { diff --git a/external-crates/move/crates/move-bytecode-verifier/src/lib.rs b/external-crates/move/crates/move-bytecode-verifier/src/lib.rs index 64a88f59842dd..660da728a05d5 100644 --- a/external-crates/move/crates/move-bytecode-verifier/src/lib.rs +++ b/external-crates/move/crates/move-bytecode-verifier/src/lib.rs @@ -7,6 +7,7 @@ //! Verifies bytecode sanity. // Bounds checks are implemented in the `vm` crate. +pub mod ability_cache; pub mod ability_field_requirements; pub mod check_duplication; pub mod code_unit_verifier; diff --git a/external-crates/move/crates/move-bytecode-verifier/src/locals_safety/abstract_state.rs b/external-crates/move/crates/move-bytecode-verifier/src/locals_safety/abstract_state.rs index c42e538bf5b87..26eae31969670 100644 --- a/external-crates/move/crates/move-bytecode-verifier/src/locals_safety/abstract_state.rs +++ b/external-crates/move/crates/move-bytecode-verifier/src/locals_safety/abstract_state.rs @@ -4,6 +4,7 @@ //! This module defines the abstract state for the local safety analysis. +use crate::ability_cache::AbilityCache; use move_abstract_interpreter::absint::{AbstractDomain, FunctionContext, JoinResult}; use move_binary_format::{ errors::{PartialVMError, PartialVMResult}, @@ -26,10 +27,9 @@ pub(crate) enum LocalState { } use LocalState::*; -pub(crate) const STEP_BASE_COST: u128 = 15; -pub(crate) const RET_PER_LOCAL_COST: u128 = 30; -pub(crate) const JOIN_BASE_COST: u128 = 10; -pub(crate) const JOIN_PER_LOCAL_COST: u128 = 5; +pub(crate) const STEP_BASE_COST: u128 = 1; +pub(crate) const RET_COST: u128 = 10; +pub(crate) const JOIN_COST: u128 = 10; #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct AbstractState { @@ -41,8 +41,10 @@ pub(crate) struct AbstractState { impl AbstractState { /// create a new abstract state pub fn new( - module: &CompiledModule, + _module: &CompiledModule, function_context: &FunctionContext, + ability_cache: &mut AbilityCache, + meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult { let num_args = function_context.parameters().len(); let num_locals = num_args + function_context.locals().len(); @@ -55,7 +57,14 @@ impl AbstractState { .0 .iter() .chain(function_context.locals().0.iter()) - .map(|st| module.abilities(st, function_context.type_parameters())) + .map(|st| { + ability_cache.abilities( + Scope::Function, + meter, + function_context.type_parameters(), + st, + ) + }) .collect::>>()?; Ok(Self { @@ -141,12 +150,7 @@ impl AbstractDomain for AbstractState { state: &AbstractState, meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult { - meter.add(Scope::Function, JOIN_BASE_COST)?; - meter.add_items( - Scope::Function, - JOIN_PER_LOCAL_COST, - state.local_states.len(), - )?; + meter.add(Scope::Function, JOIN_COST)?; let joined = Self::join_(self, state); assert!(self.local_states.len() == joined.local_states.len()); let locals_unchanged = self diff --git a/external-crates/move/crates/move-bytecode-verifier/src/locals_safety/mod.rs b/external-crates/move/crates/move-bytecode-verifier/src/locals_safety/mod.rs index f5eab08181ab5..1c9b0487b28d0 100644 --- a/external-crates/move/crates/move-bytecode-verifier/src/locals_safety/mod.rs +++ b/external-crates/move/crates/move-bytecode-verifier/src/locals_safety/mod.rs @@ -8,8 +8,8 @@ mod abstract_state; -use crate::locals_safety::abstract_state::{RET_PER_LOCAL_COST, STEP_BASE_COST}; -use abstract_state::{AbstractState, LocalState}; +use crate::ability_cache::AbilityCache; +use abstract_state::{AbstractState, LocalState, RET_COST, STEP_BASE_COST}; use move_abstract_interpreter::absint::{AbstractInterpreter, FunctionContext, TransferFunctions}; use move_binary_format::{ errors::{PartialVMError, PartialVMResult}, @@ -22,9 +22,10 @@ use move_core_types::vm_status::StatusCode; pub(crate) fn verify<'a>( module: &CompiledModule, function_context: &'a FunctionContext<'a>, + ability_cache: &mut AbilityCache, meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult<()> { - let initial_state = AbstractState::new(module, function_context)?; + let initial_state = AbstractState::new(module, function_context, ability_cache, meter)?; LocalsSafetyAnalysis().analyze_function(initial_state, function_context, meter) } @@ -70,7 +71,7 @@ fn execute_inner( Bytecode::Ret => { let local_states = state.local_states(); - meter.add_items(Scope::Function, RET_PER_LOCAL_COST, local_states.len())?; + meter.add_items(Scope::Function, RET_COST, local_states.len())?; let all_local_abilities = state.all_local_abilities(); assert!(local_states.len() == all_local_abilities.len()); for (local_state, local_abilities) in local_states.iter().zip(all_local_abilities) { diff --git a/external-crates/move/crates/move-bytecode-verifier/src/reference_safety/abstract_state.rs b/external-crates/move/crates/move-bytecode-verifier/src/reference_safety/abstract_state.rs index 6aaa140cdebec..2557a0f75ee88 100644 --- a/external-crates/move/crates/move-bytecode-verifier/src/reference_safety/abstract_state.rs +++ b/external-crates/move/crates/move-bytecode-verifier/src/reference_safety/abstract_state.rs @@ -16,7 +16,10 @@ use move_binary_format::{ use move_borrow_graph::references::RefID; use move_bytecode_verifier_meter::{Meter, Scope}; use move_core_types::vm_status::StatusCode; -use std::collections::{BTreeMap, BTreeSet}; +use std::{ + cmp::max, + collections::{BTreeMap, BTreeSet}, +}; type BorrowGraph = move_borrow_graph::graph::BorrowGraph<(), Label>; @@ -74,19 +77,18 @@ impl std::fmt::Display for Label { } } -pub(crate) const STEP_BASE_COST: u128 = 10; -pub(crate) const STEP_PER_LOCAL_COST: u128 = 20; -pub(crate) const STEP_PER_GRAPH_ITEM_COST: u128 = 50; -pub(crate) const JOIN_BASE_COST: u128 = 100; -pub(crate) const JOIN_PER_LOCAL_COST: u128 = 10; -pub(crate) const JOIN_PER_GRAPH_ITEM_COST: u128 = 50; +pub(crate) const STEP_BASE_COST: u128 = 1; +pub(crate) const JOIN_BASE_COST: u128 = 10; + +pub(crate) const PER_GRAPH_ITEM_COST: u128 = 4; -// The cost for an edge from an input reference parameter to output reference. -pub(crate) const REF_PARAM_EDGE_COST: u128 = 100; -pub(crate) const REF_PARAM_EDGE_COST_GROWTH: f32 = 1.5; +pub(crate) const RELEASE_ITEM_COST: u128 = 3; +pub(crate) const RELEASE_ITEM_QUADRATIC_THRESHOLD: usize = 5; -// The cost of an acquires in a call. -pub(crate) const CALL_PER_ACQUIRES_COST: u128 = 100; +pub(crate) const JOIN_ITEM_COST: u128 = 4; +pub(crate) const JOIN_ITEM_QUADRATIC_THRESHOLD: usize = 10; + +pub(crate) const ADD_BORROW_COST: u128 = 3; /// AbstractState is the analysis state over which abstract interpretation is performed. #[derive(Clone, Debug, PartialEq, Eq)] @@ -126,10 +128,6 @@ impl AbstractState { state } - pub(crate) fn local_count(&self) -> usize { - self.locals.len() - } - pub(crate) fn graph_size(&self) -> usize { self.borrow_graph.graph_size() } @@ -166,27 +164,63 @@ impl AbstractState { id } - fn add_copy(&mut self, parent: RefID, child: RefID) { - self.borrow_graph.add_strong_borrow((), parent, child) + fn add_copy( + &mut self, + parent: RefID, + child: RefID, + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult<()> { + meter.add(Scope::Function, ADD_BORROW_COST)?; + self.borrow_graph.add_strong_borrow((), parent, child); + Ok(()) } - fn add_borrow(&mut self, parent: RefID, child: RefID) { - self.borrow_graph.add_weak_borrow((), parent, child) + fn add_borrow( + &mut self, + parent: RefID, + child: RefID, + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult<()> { + meter.add(Scope::Function, ADD_BORROW_COST)?; + self.borrow_graph.add_weak_borrow((), parent, child); + Ok(()) } - fn add_field_borrow(&mut self, parent: RefID, field: FieldHandleIndex, child: RefID) { + fn add_field_borrow( + &mut self, + parent: RefID, + field: FieldHandleIndex, + child: RefID, + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult<()> { + meter.add(Scope::Function, ADD_BORROW_COST)?; self.borrow_graph - .add_strong_field_borrow((), parent, Label::StructField(field), child) + .add_strong_field_borrow((), parent, Label::StructField(field), child); + Ok(()) } - fn add_local_borrow(&mut self, local: LocalIndex, id: RefID) { + fn add_local_borrow( + &mut self, + local: LocalIndex, + id: RefID, + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult<()> { + meter.add(Scope::Function, ADD_BORROW_COST)?; self.borrow_graph - .add_strong_field_borrow((), self.frame_root(), Label::Local(local), id) + .add_strong_field_borrow((), self.frame_root(), Label::Local(local), id); + Ok(()) } - fn add_resource_borrow(&mut self, resource: StructDefinitionIndex, id: RefID) { + fn add_resource_borrow( + &mut self, + resource: StructDefinitionIndex, + id: RefID, + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult<()> { + meter.add(Scope::Function, ADD_BORROW_COST)?; self.borrow_graph - .add_weak_field_borrow((), self.frame_root(), Label::Global(resource), id) + .add_weak_field_borrow((), self.frame_root(), Label::Global(resource), id); + Ok(()) } fn add_variant_field_borrow( @@ -196,18 +230,22 @@ impl AbstractState { variant_tag: VariantTag, field_index: MemberCount, child_id: RefID, - ) { + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult<()> { + meter.add(Scope::Function, ADD_BORROW_COST)?; self.borrow_graph.add_strong_field_borrow( (), parent, Label::VariantField(enum_def_idx, variant_tag, field_index), child_id, - ) + ); + Ok(()) } /// removes `id` from borrow graph - fn release(&mut self, id: RefID) { - self.borrow_graph.release(id); + fn release(&mut self, id: RefID, meter: &mut (impl Meter + ?Sized)) -> PartialVMResult<()> { + let released_edges = self.borrow_graph.release(id); + charge_release(released_edges, meter) } //********************************************************************************************** @@ -258,52 +296,85 @@ impl AbstractState { /// checks if `id` is writable /// - Mutable references are writable if there are no consistent borrows /// - Immutable references are not writable by the typing rules - fn is_writable(&self, id: RefID) -> bool { + fn is_writable(&self, id: RefID, meter: &mut (impl Meter + ?Sized)) -> PartialVMResult { assert!(self.borrow_graph.is_mutable(id)); - !self.has_consistent_borrows(id, None) + charge_graph_size(self.graph_size(), meter)?; + Ok(!self.has_consistent_borrows(id, None)) } /// checks if `id` is freezable /// - Mutable references are freezable if there are no consistent mutable borrows /// - Immutable references are not freezable by the typing rules - fn is_freezable(&self, id: RefID, at_field_opt: Option) -> bool { + fn is_freezable( + &self, + id: RefID, + at_field_opt: Option, + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult { assert!(self.borrow_graph.is_mutable(id)); - !self.has_consistent_mutable_borrows(id, at_field_opt.map(Label::StructField)) + charge_graph_size(self.graph_size(), meter)?; + Ok(!self.has_consistent_mutable_borrows(id, at_field_opt.map(Label::StructField))) } /// checks if `id` is readable /// - Mutable references are readable if they are freezable /// - Immutable references are always readable - fn is_readable(&self, id: RefID, at_field_opt: Option) -> bool { + fn is_readable( + &self, + id: RefID, + at_field_opt: Option, + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult { let is_mutable = self.borrow_graph.is_mutable(id); - !is_mutable || self.is_freezable(id, at_field_opt) + Ok(!is_mutable || self.is_freezable(id, at_field_opt, meter)?) } /// checks if local@idx is borrowed - fn is_local_borrowed(&self, idx: LocalIndex) -> bool { - self.has_consistent_borrows(self.frame_root(), Some(Label::Local(idx))) + fn is_local_borrowed( + &self, + idx: LocalIndex, + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult { + charge_graph_size(self.graph_size(), meter)?; + Ok(self.has_consistent_borrows(self.frame_root(), Some(Label::Local(idx)))) } /// checks if local@idx is mutably borrowed - fn is_local_mutably_borrowed(&self, idx: LocalIndex) -> bool { - self.has_consistent_mutable_borrows(self.frame_root(), Some(Label::Local(idx))) + fn is_local_mutably_borrowed( + &self, + idx: LocalIndex, + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult { + charge_graph_size(self.graph_size(), meter)?; + Ok(self.has_consistent_mutable_borrows(self.frame_root(), Some(Label::Local(idx)))) } /// checks if global@idx is borrowed - fn is_global_borrowed(&self, resource: StructDefinitionIndex) -> bool { - self.has_consistent_borrows(self.frame_root(), Some(Label::Global(resource))) + fn is_global_borrowed( + &self, + resource: StructDefinitionIndex, + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult { + charge_graph_size(self.graph_size(), meter)?; + Ok(self.has_consistent_borrows(self.frame_root(), Some(Label::Global(resource)))) } /// checks if global@idx is mutably borrowed - fn is_global_mutably_borrowed(&self, resource: StructDefinitionIndex) -> bool { - self.has_consistent_mutable_borrows(self.frame_root(), Some(Label::Global(resource))) + fn is_global_mutably_borrowed( + &self, + resource: StructDefinitionIndex, + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult { + charge_graph_size(self.graph_size(), meter)?; + Ok(self.has_consistent_mutable_borrows(self.frame_root(), Some(Label::Global(resource)))) } /// checks if the stack frame of the function being analyzed can be safely destroyed. /// safe destruction requires that all references in locals have already been destroyed /// and all values in locals are copyable and unborrowed. - fn is_frame_safe_to_destroy(&self) -> bool { - !self.has_consistent_borrows(self.frame_root(), None) + fn is_frame_safe_to_destroy(&self, meter: &mut (impl Meter + ?Sized)) -> PartialVMResult { + charge_graph_size(self.graph_size(), meter)?; + Ok(!self.has_consistent_borrows(self.frame_root(), None)) } //********************************************************************************************** @@ -311,10 +382,14 @@ impl AbstractState { //********************************************************************************************** /// destroys local@idx - pub fn release_value(&mut self, value: AbstractValue) { + pub fn release_value( + &mut self, + value: AbstractValue, + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult<()> { match value { - AbstractValue::Reference(id) => self.release(id), - AbstractValue::NonReference => (), + AbstractValue::Reference(id) => self.release(id, meter), + AbstractValue::NonReference => Ok(()), } } @@ -322,15 +397,16 @@ impl AbstractState { &mut self, offset: CodeOffset, local: LocalIndex, + meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult { match safe_unwrap!(self.locals.get(local as usize)) { AbstractValue::Reference(id) => { let id = *id; let new_id = self.new_ref(self.borrow_graph.is_mutable(id)); - self.add_copy(id, new_id); + self.add_copy(id, new_id, meter)?; Ok(AbstractValue::Reference(new_id)) } - AbstractValue::NonReference if self.is_local_mutably_borrowed(local) => { + AbstractValue::NonReference if self.is_local_mutably_borrowed(local, meter)? => { Err(self.error(StatusCode::COPYLOC_EXISTS_BORROW_ERROR, offset)) } AbstractValue::NonReference => Ok(AbstractValue::NonReference), @@ -341,6 +417,7 @@ impl AbstractState { &mut self, offset: CodeOffset, local: LocalIndex, + meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult { let old_value = std::mem::replace( safe_unwrap!(self.locals.get_mut(local as usize)), @@ -348,7 +425,7 @@ impl AbstractState { ); match old_value { AbstractValue::Reference(id) => Ok(AbstractValue::Reference(id)), - AbstractValue::NonReference if self.is_local_borrowed(local) => { + AbstractValue::NonReference if self.is_local_borrowed(local, meter)? => { Err(self.error(StatusCode::MOVELOC_EXISTS_BORROW_ERROR, offset)) } AbstractValue::NonReference => Ok(AbstractValue::NonReference), @@ -360,29 +437,32 @@ impl AbstractState { offset: CodeOffset, local: LocalIndex, new_value: AbstractValue, + meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult<()> { let old_value = std::mem::replace(safe_unwrap!(self.locals.get_mut(local as usize)), new_value); match old_value { - AbstractValue::Reference(id) => { - self.release(id); - Ok(()) - } - AbstractValue::NonReference if self.is_local_borrowed(local) => { + AbstractValue::Reference(id) => self.release(id, meter), + AbstractValue::NonReference if self.is_local_borrowed(local, meter)? => { Err(self.error(StatusCode::STLOC_UNSAFE_TO_DESTROY_ERROR, offset)) } AbstractValue::NonReference => Ok(()), } } - pub fn freeze_ref(&mut self, offset: CodeOffset, id: RefID) -> PartialVMResult { - if !self.is_freezable(id, None) { + pub fn freeze_ref( + &mut self, + offset: CodeOffset, + id: RefID, + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult { + if !self.is_freezable(id, None, meter)? { return Err(self.error(StatusCode::FREEZEREF_EXISTS_MUTABLE_BORROW_ERROR, offset)); } let frozen_id = self.new_ref(false); - self.add_copy(id, frozen_id); - self.release(id); + self.add_copy(id, frozen_id, meter)?; + self.release(id, meter)?; Ok(AbstractValue::Reference(frozen_id)) } @@ -391,17 +471,19 @@ impl AbstractState { offset: CodeOffset, v1: AbstractValue, v2: AbstractValue, + meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult { match (v1, v2) { (AbstractValue::Reference(id1), AbstractValue::Reference(id2)) - if !self.is_readable(id1, None) || !self.is_readable(id2, None) => + if !self.is_readable(id1, None, meter)? + || !self.is_readable(id2, None, meter)? => { // TODO better error code return Err(self.error(StatusCode::READREF_EXISTS_MUTABLE_BORROW_ERROR, offset)); } (AbstractValue::Reference(id1), AbstractValue::Reference(id2)) => { - self.release(id1); - self.release(id2) + self.release(id1, meter)?; + self.release(id2, meter)?; } (v1, v2) => { assert!(v1.is_value()); @@ -411,21 +493,31 @@ impl AbstractState { Ok(AbstractValue::NonReference) } - pub fn read_ref(&mut self, offset: CodeOffset, id: RefID) -> PartialVMResult { - if !self.is_readable(id, None) { + pub fn read_ref( + &mut self, + offset: CodeOffset, + id: RefID, + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult { + if !self.is_readable(id, None, meter)? { return Err(self.error(StatusCode::READREF_EXISTS_MUTABLE_BORROW_ERROR, offset)); } - self.release(id); + self.release(id, meter)?; Ok(AbstractValue::NonReference) } - pub fn write_ref(&mut self, offset: CodeOffset, id: RefID) -> PartialVMResult<()> { - if !self.is_writable(id) { + pub fn write_ref( + &mut self, + offset: CodeOffset, + id: RefID, + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult<()> { + if !self.is_writable(id, meter)? { return Err(self.error(StatusCode::WRITEREF_EXISTS_BORROW_ERROR, offset)); } - self.release(id); + self.release(id, meter)?; Ok(()) } @@ -434,15 +526,16 @@ impl AbstractState { offset: CodeOffset, mut_: bool, local: LocalIndex, + meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult { // nothing to check in case borrow is mutable since the frame cannot have an full borrow/ // epsilon outgoing edge - if !mut_ && self.is_local_mutably_borrowed(local) { + if !mut_ && self.is_local_mutably_borrowed(local, meter)? { return Err(self.error(StatusCode::BORROWLOC_EXISTS_BORROW_ERROR, offset)); } let new_id = self.new_ref(mut_); - self.add_local_borrow(local, new_id); + self.add_local_borrow(local, new_id, meter)?; Ok(AbstractValue::Reference(new_id)) } @@ -452,21 +545,29 @@ impl AbstractState { mut_: bool, id: RefID, field: FieldHandleIndex, + meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult { // Any field borrows will be factored out, so don't check in the mutable case - let is_mut_borrow_with_full_borrows = || mut_ && self.has_full_borrows(id); + macro_rules! is_mut_borrow_with_full_borrows { + () => { + mut_ && self.has_full_borrows(id) + }; + } // For new immutable borrow, the reference must be readable at that field // This means that there could exist a mutable borrow on some other field - let is_imm_borrow_with_mut_borrows = || !mut_ && !self.is_readable(id, Some(field)); - - if is_mut_borrow_with_full_borrows() || is_imm_borrow_with_mut_borrows() { + macro_rules! is_imm_borrow_with_mut_borrows { + () => { + !mut_ && !self.is_readable(id, Some(field), meter)? + }; + } + if is_mut_borrow_with_full_borrows!() || is_imm_borrow_with_mut_borrows!() { // TODO improve error for mutable case return Err(self.error(StatusCode::FIELD_EXISTS_MUTABLE_BORROW_ERROR, offset)); } let field_borrow_id = self.new_ref(mut_); - self.add_field_borrow(id, field, field_borrow_id); - self.release(id); + self.add_field_borrow(id, field, field_borrow_id, meter)?; + self.release(id, meter)?; Ok(AbstractValue::Reference(field_borrow_id)) } @@ -478,14 +579,22 @@ impl AbstractState { variant_def: &VariantDefinition, mut_: bool, id: RefID, + meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult> { // Any field borrows will be factored out, so don't check in the mutable case - let is_mut_borrow_with_full_borrows = || mut_ && self.has_full_borrows(id); + macro_rules! is_mut_borrow_with_full_borrows { + () => { + mut_ && self.has_full_borrows(id) + }; + } // For new immutable borrow, the reference to the variant must be readable. // This means that there _does not_ exist a mutable borrow on some other field - let is_imm_borrow_with_mut_borrows = || !mut_ && !self.is_readable(id, None); - - if is_mut_borrow_with_full_borrows() || is_imm_borrow_with_mut_borrows() { + macro_rules! is_imm_borrow_with_mut_borrows { + () => { + !mut_ && !self.is_readable(id, None, meter)? + }; + } + if is_mut_borrow_with_full_borrows!() || is_imm_borrow_with_mut_borrows!() { return Err(self.error(StatusCode::FIELD_EXISTS_MUTABLE_BORROW_ERROR, offset)); } @@ -501,12 +610,13 @@ impl AbstractState { variant_tag, i as MemberCount, field_borrow_id, - ); - AbstractValue::Reference(field_borrow_id) + meter, + )?; + Ok(AbstractValue::Reference(field_borrow_id)) }) - .collect(); + .collect::>()?; - self.release(id); + self.release(id, meter)?; Ok(field_borrows) } @@ -515,14 +625,16 @@ impl AbstractState { offset: CodeOffset, mut_: bool, resource: StructDefinitionIndex, + meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult { - if (mut_ && self.is_global_borrowed(resource)) || self.is_global_mutably_borrowed(resource) + if (mut_ && self.is_global_borrowed(resource, meter)?) + || self.is_global_mutably_borrowed(resource, meter)? { return Err(self.error(StatusCode::GLOBAL_REFERENCE_ERROR, offset)); } let new_id = self.new_ref(mut_); - self.add_resource_borrow(resource, new_id); + self.add_resource_borrow(resource, new_id, meter)?; Ok(AbstractValue::Reference(new_id)) } @@ -530,8 +642,9 @@ impl AbstractState { &mut self, offset: CodeOffset, resource: StructDefinitionIndex, + meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult { - if self.is_global_borrowed(resource) { + if self.is_global_borrowed(resource, meter)? { Err(self.error(StatusCode::GLOBAL_REFERENCE_ERROR, offset)) } else { Ok(AbstractValue::NonReference) @@ -543,12 +656,13 @@ impl AbstractState { offset: CodeOffset, vector: AbstractValue, mut_: bool, + meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult<()> { let id = safe_unwrap!(vector.ref_id()); - if mut_ && !self.is_writable(id) { + if mut_ && !self.is_writable(id, meter)? { return Err(self.error(StatusCode::VEC_UPDATE_EXISTS_MUTABLE_BORROW_ERROR, offset)); } - self.release(id); + self.release(id, meter)?; Ok(()) } @@ -557,9 +671,10 @@ impl AbstractState { offset: CodeOffset, vector: AbstractValue, mut_: bool, + meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult { let vec_id = safe_unwrap!(vector.ref_id()); - if mut_ && !self.is_writable(vec_id) { + if mut_ && !self.is_writable(vec_id, meter)? { return Err(self.error( StatusCode::VEC_BORROW_ELEMENT_EXISTS_MUTABLE_BORROW_ERROR, offset, @@ -567,9 +682,9 @@ impl AbstractState { } let elem_id = self.new_ref(mut_); - self.add_borrow(vec_id, elem_id); + self.add_borrow(vec_id, elem_id, meter)?; - self.release(vec_id); + self.release(vec_id, meter)?; Ok(AbstractValue::Reference(elem_id)) } @@ -581,14 +696,9 @@ impl AbstractState { return_: &Signature, meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult> { - meter.add_items( - Scope::Function, - CALL_PER_ACQUIRES_COST, - acquired_resources.len(), - )?; // Check acquires for acquired_resource in acquired_resources { - if self.is_global_borrowed(*acquired_resource) { + if self.is_global_borrowed(*acquired_resource, meter)? { return Err(self.error(StatusCode::GLOBAL_REFERENCE_ERROR, offset)); } } @@ -598,7 +708,7 @@ impl AbstractState { let mut mutable_references_to_borrow_from = BTreeSet::new(); for id in arguments.iter().filter_map(|v| v.ref_id()) { if self.borrow_graph.is_mutable(id) { - if !self.is_writable(id) { + if !self.is_writable(id, meter)? { return Err( self.error(StatusCode::CALL_BORROWED_MUTABLE_REFERENCE_ERROR, offset) ); @@ -613,45 +723,42 @@ impl AbstractState { let return_values = return_ .0 .iter() - .map(|return_type| match return_type { - SignatureToken::MutableReference(_) => { - let id = self.new_ref(true); - for parent in &mutable_references_to_borrow_from { - self.add_borrow(*parent, id); + .map(|return_type| { + Ok(match return_type { + SignatureToken::MutableReference(_) => { + let id = self.new_ref(true); + for parent in &mutable_references_to_borrow_from { + self.add_borrow(*parent, id, meter)?; + } + returned_refs += 1; + AbstractValue::Reference(id) } - returned_refs += 1; - AbstractValue::Reference(id) - } - SignatureToken::Reference(_) => { - let id = self.new_ref(false); - for parent in &all_references_to_borrow_from { - self.add_borrow(*parent, id); + SignatureToken::Reference(_) => { + let id = self.new_ref(false); + for parent in &all_references_to_borrow_from { + self.add_borrow(*parent, id, meter)?; + } + returned_refs += 1; + AbstractValue::Reference(id) } - returned_refs += 1; - AbstractValue::Reference(id) - } - _ => AbstractValue::NonReference, + _ => AbstractValue::NonReference, + }) }) - .collect(); - - // Meter usage of reference edges - meter.add_items_with_growth( - Scope::Function, - REF_PARAM_EDGE_COST, - all_references_to_borrow_from - .len() - .saturating_mul(returned_refs), - REF_PARAM_EDGE_COST_GROWTH, - )?; + .collect::>()?; // Release input references for id in all_references_to_borrow_from { - self.release(id) + self.release(id, meter)? } Ok(return_values) } - pub fn ret(&mut self, offset: CodeOffset, values: Vec) -> PartialVMResult<()> { + pub fn ret( + &mut self, + offset: CodeOffset, + values: Vec, + meter: &mut (impl Meter + ?Sized), + ) -> PartialVMResult<()> { // release all local variables let mut released = BTreeSet::new(); for stored_value in self.locals.iter() { @@ -659,10 +766,12 @@ impl AbstractState { released.insert(*id); } } - released.into_iter().for_each(|id| self.release(id)); + for id in released { + self.release(id, meter)? + } // Check that no local or global is borrowed - if !self.is_frame_safe_to_destroy() { + if !self.is_frame_safe_to_destroy(meter)? { return Err(self.error( StatusCode::UNSAFE_RET_LOCAL_OR_RESOURCE_STILL_BORROWED, offset, @@ -671,7 +780,7 @@ impl AbstractState { // Check mutable references can be transferred for id in values.into_iter().filter_map(|v| v.ref_id()) { - if self.borrow_graph.is_mutable(id) && !self.is_writable(id) { + if self.borrow_graph.is_mutable(id) && !self.is_writable(id, meter)? { return Err(self.error(StatusCode::RET_BORROWED_MUTABLE_REFERENCE_ERROR, offset)); } } @@ -726,13 +835,14 @@ impl AbstractState { }) } - pub fn join_(&self, other: &Self) -> Self { + pub fn join_(&self, other: &Self) -> (Self, usize) { assert!(self.current_function == other.current_function); assert!(self.is_canonical() && other.is_canonical()); assert!(self.next_id == other.next_id); assert!(self.locals.len() == other.locals.len()); let mut self_graph = self.borrow_graph.clone(); let mut other_graph = other.borrow_graph.clone(); + let mut released = 0; let locals = self .locals .iter() @@ -740,11 +850,11 @@ impl AbstractState { .map(|(self_value, other_value)| { match (self_value, other_value) { (AbstractValue::Reference(id), AbstractValue::NonReference) => { - self_graph.release(*id); + released += self_graph.release(*id); AbstractValue::NonReference } (AbstractValue::NonReference, AbstractValue::Reference(id)) => { - other_graph.release(*id); + released += other_graph.release(*id); AbstractValue::NonReference } // The local has a value on each side, add it to the state @@ -759,13 +869,13 @@ impl AbstractState { let borrow_graph = self_graph.join(&other_graph); let current_function = self.current_function; let next_id = self.next_id; - - Self { + let joined = Self { current_function, locals, borrow_graph, next_id, - } + }; + (joined, released) } } @@ -776,16 +886,16 @@ impl AbstractDomain for AbstractState { state: &AbstractState, meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult { - let joined = Self::join_(self, state); + meter.add(Scope::Function, JOIN_BASE_COST)?; + let self_size = self.graph_size(); + let state_size = state.graph_size(); + let (joined, released) = Self::join_(self, state); assert!(joined.is_canonical()); assert!(self.locals.len() == joined.locals.len()); - meter.add(Scope::Function, JOIN_BASE_COST)?; - meter.add_items(Scope::Function, JOIN_PER_LOCAL_COST, self.locals.len())?; - meter.add_items( - Scope::Function, - JOIN_PER_GRAPH_ITEM_COST, - self.borrow_graph.graph_size(), - )?; + let max_size = max(max(self_size, state_size), joined.graph_size()); + charge_join(self_size, state_size, meter)?; + charge_graph_size(max_size, meter)?; + charge_release(released, meter)?; let locals_unchanged = self .locals .iter() @@ -801,3 +911,40 @@ impl AbstractDomain for AbstractState { } } } + +fn charge_graph_size(size: usize, meter: &mut (impl Meter + ?Sized)) -> PartialVMResult<()> { + let size = max(size, 1); + meter.add_items(Scope::Function, PER_GRAPH_ITEM_COST, size) +} + +fn charge_release(released: usize, meter: &mut (impl Meter + ?Sized)) -> PartialVMResult<()> { + let size = max(released, 1); + meter.add_items( + Scope::Function, + RELEASE_ITEM_COST, + // max(x, x^2/5) + max( + size, + size.saturating_mul(size) / RELEASE_ITEM_QUADRATIC_THRESHOLD, + ), + ) +} + +fn charge_join( + size1: usize, + size2: usize, + meter: &mut (impl Meter + ?Sized), +) -> PartialVMResult<()> { + let size1 = max(size1, 1); + let size2 = max(size2, 1); + let size = size1.saturating_add(size2); + meter.add_items( + Scope::Function, + JOIN_ITEM_COST, + // max(x, x^2/10) + max( + size, + size.saturating_mul(size) / JOIN_ITEM_QUADRATIC_THRESHOLD, + ), + ) +} diff --git a/external-crates/move/crates/move-bytecode-verifier/src/reference_safety/mod.rs b/external-crates/move/crates/move-bytecode-verifier/src/reference_safety/mod.rs index f3bfaf804cb16..9ac61018e27f3 100644 --- a/external-crates/move/crates/move-bytecode-verifier/src/reference_safety/mod.rs +++ b/external-crates/move/crates/move-bytecode-verifier/src/reference_safety/mod.rs @@ -10,9 +10,7 @@ mod abstract_state; -use crate::reference_safety::abstract_state::{ - STEP_BASE_COST, STEP_PER_GRAPH_ITEM_COST, STEP_PER_LOCAL_COST, -}; +use crate::reference_safety::abstract_state::STEP_BASE_COST; use abstract_state::{AbstractState, AbstractValue}; use move_abstract_interpreter::absint::{AbstractInterpreter, FunctionContext, TransferFunctions}; use move_abstract_stack::AbstractStack; @@ -182,113 +180,110 @@ fn execute_inner( meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult<()> { meter.add(Scope::Function, STEP_BASE_COST)?; - meter.add_items(Scope::Function, STEP_PER_LOCAL_COST, state.local_count())?; - meter.add_items( - Scope::Function, - STEP_PER_GRAPH_ITEM_COST, - state.graph_size(), - )?; match bytecode { - Bytecode::Pop => state.release_value(safe_unwrap_err!(verifier.stack.pop())), + Bytecode::Pop => state.release_value(safe_unwrap_err!(verifier.stack.pop()), meter)?, Bytecode::CopyLoc(local) => { - let value = state.copy_loc(offset, *local)?; + let value = state.copy_loc(offset, *local, meter)?; verifier.push(value)? } Bytecode::MoveLoc(local) => { - let value = state.move_loc(offset, *local)?; + let value = state.move_loc(offset, *local, meter)?; verifier.push(value)? } - Bytecode::StLoc(local) => { - state.st_loc(offset, *local, safe_unwrap_err!(verifier.stack.pop()))? - } + Bytecode::StLoc(local) => state.st_loc( + offset, + *local, + safe_unwrap_err!(verifier.stack.pop()), + meter, + )?, Bytecode::FreezeRef => { let id = safe_unwrap!(safe_unwrap_err!(verifier.stack.pop()).ref_id()); - let frozen = state.freeze_ref(offset, id)?; + let frozen = state.freeze_ref(offset, id, meter)?; verifier.push(frozen)? } Bytecode::Eq | Bytecode::Neq => { let v1 = safe_unwrap_err!(verifier.stack.pop()); let v2 = safe_unwrap_err!(verifier.stack.pop()); - let value = state.comparison(offset, v1, v2)?; + let value = state.comparison(offset, v1, v2, meter)?; verifier.push(value)? } Bytecode::ReadRef => { let id = safe_unwrap!(safe_unwrap_err!(verifier.stack.pop()).ref_id()); - let value = state.read_ref(offset, id)?; + let value = state.read_ref(offset, id, meter)?; verifier.push(value)? } Bytecode::WriteRef => { let id = safe_unwrap!(safe_unwrap_err!(verifier.stack.pop()).ref_id()); let val_operand = safe_unwrap_err!(verifier.stack.pop()); safe_assert!(val_operand.is_value()); - state.write_ref(offset, id)? + state.write_ref(offset, id, meter)? } Bytecode::MutBorrowLoc(local) => { - let value = state.borrow_loc(offset, true, *local)?; + let value = state.borrow_loc(offset, true, *local, meter)?; verifier.push(value)? } Bytecode::ImmBorrowLoc(local) => { - let value = state.borrow_loc(offset, false, *local)?; + let value = state.borrow_loc(offset, false, *local, meter)?; verifier.push(value)? } Bytecode::MutBorrowField(field_handle_index) => { let id = safe_unwrap!(safe_unwrap_err!(verifier.stack.pop()).ref_id()); - let value = state.borrow_field(offset, true, id, *field_handle_index)?; + let value = state.borrow_field(offset, true, id, *field_handle_index, meter)?; verifier.push(value)? } Bytecode::MutBorrowFieldGeneric(field_inst_index) => { let field_inst = verifier.module.field_instantiation_at(*field_inst_index); let id = safe_unwrap!(safe_unwrap_err!(verifier.stack.pop()).ref_id()); - let value = state.borrow_field(offset, true, id, field_inst.handle)?; + let value = state.borrow_field(offset, true, id, field_inst.handle, meter)?; verifier.push(value)? } Bytecode::ImmBorrowField(field_handle_index) => { let id = safe_unwrap!(safe_unwrap_err!(verifier.stack.pop()).ref_id()); - let value = state.borrow_field(offset, false, id, *field_handle_index)?; + let value = state.borrow_field(offset, false, id, *field_handle_index, meter)?; verifier.push(value)? } Bytecode::ImmBorrowFieldGeneric(field_inst_index) => { let field_inst = verifier.module.field_instantiation_at(*field_inst_index); let id = safe_unwrap!(safe_unwrap_err!(verifier.stack.pop()).ref_id()); - let value = state.borrow_field(offset, false, id, field_inst.handle)?; + let value = state.borrow_field(offset, false, id, field_inst.handle, meter)?; verifier.push(value)? } Bytecode::MutBorrowGlobalDeprecated(idx) => { safe_assert!(safe_unwrap_err!(verifier.stack.pop()).is_value()); - let value = state.borrow_global(offset, true, *idx)?; + let value = state.borrow_global(offset, true, *idx, meter)?; verifier.push(value)? } Bytecode::MutBorrowGlobalGenericDeprecated(idx) => { safe_assert!(safe_unwrap_err!(verifier.stack.pop()).is_value()); let struct_inst = verifier.module.struct_instantiation_at(*idx); - let value = state.borrow_global(offset, true, struct_inst.def)?; + let value = state.borrow_global(offset, true, struct_inst.def, meter)?; verifier.push(value)? } Bytecode::ImmBorrowGlobalDeprecated(idx) => { safe_assert!(safe_unwrap_err!(verifier.stack.pop()).is_value()); - let value = state.borrow_global(offset, false, *idx)?; + let value = state.borrow_global(offset, false, *idx, meter)?; verifier.push(value)? } Bytecode::ImmBorrowGlobalGenericDeprecated(idx) => { safe_assert!(safe_unwrap_err!(verifier.stack.pop()).is_value()); let struct_inst = verifier.module.struct_instantiation_at(*idx); - let value = state.borrow_global(offset, false, struct_inst.def)?; + let value = state.borrow_global(offset, false, struct_inst.def, meter)?; verifier.push(value)? } Bytecode::MoveFromDeprecated(idx) => { safe_assert!(safe_unwrap_err!(verifier.stack.pop()).is_value()); - let value = state.move_from(offset, *idx)?; + let value = state.move_from(offset, *idx, meter)?; verifier.push(value)? } Bytecode::MoveFromGenericDeprecated(idx) => { safe_assert!(safe_unwrap_err!(verifier.stack.pop()).is_value()); let struct_inst = verifier.module.struct_instantiation_at(*idx); - let value = state.move_from(offset, struct_inst.def)?; + let value = state.move_from(offset, struct_inst.def, meter)?; verifier.push(value)? } @@ -309,7 +304,7 @@ fn execute_inner( } return_values.reverse(); - state.ret(offset, return_values)? + state.ret(offset, return_values, meter)? } Bytecode::Branch(_) @@ -331,7 +326,7 @@ fn execute_inner( // resource value safe_assert!(safe_unwrap_err!(verifier.stack.pop()).is_value()); // signer reference - state.release_value(safe_unwrap_err!(verifier.stack.pop())); + state.release_value(safe_unwrap_err!(verifier.stack.pop()), meter)?; } Bytecode::LdTrue | Bytecode::LdFalse => { @@ -402,32 +397,32 @@ fn execute_inner( Bytecode::VecLen(_) => { let vec_ref = safe_unwrap_err!(verifier.stack.pop()); - state.vector_op(offset, vec_ref, false)?; + state.vector_op(offset, vec_ref, false, meter)?; verifier.push(state.value_for(&SignatureToken::U64))? } Bytecode::VecImmBorrow(_) => { safe_assert!(safe_unwrap_err!(verifier.stack.pop()).is_value()); let vec_ref = safe_unwrap_err!(verifier.stack.pop()); - let elem_ref = state.vector_element_borrow(offset, vec_ref, false)?; + let elem_ref = state.vector_element_borrow(offset, vec_ref, false, meter)?; verifier.push(elem_ref)? } Bytecode::VecMutBorrow(_) => { safe_assert!(safe_unwrap_err!(verifier.stack.pop()).is_value()); let vec_ref = safe_unwrap_err!(verifier.stack.pop()); - let elem_ref = state.vector_element_borrow(offset, vec_ref, true)?; + let elem_ref = state.vector_element_borrow(offset, vec_ref, true, meter)?; verifier.push(elem_ref)? } Bytecode::VecPushBack(_) => { safe_assert!(safe_unwrap_err!(verifier.stack.pop()).is_value()); let vec_ref = safe_unwrap_err!(verifier.stack.pop()); - state.vector_op(offset, vec_ref, true)?; + state.vector_op(offset, vec_ref, true, meter)?; } Bytecode::VecPopBack(idx) => { let vec_ref = safe_unwrap_err!(verifier.stack.pop()); - state.vector_op(offset, vec_ref, true)?; + state.vector_op(offset, vec_ref, true, meter)?; let element_type = vec_element_type(verifier, *idx)?; verifier.push(state.value_for(&element_type))? @@ -444,7 +439,7 @@ fn execute_inner( safe_assert!(safe_unwrap_err!(verifier.stack.pop()).is_value()); safe_assert!(safe_unwrap_err!(verifier.stack.pop()).is_value()); let vec_ref = safe_unwrap_err!(verifier.stack.pop()); - state.vector_op(offset, vec_ref, true)?; + state.vector_op(offset, vec_ref, true, meter)?; } Bytecode::PackVariant(vidx) => { let handle = verifier.module.variant_handle_at(*vidx); @@ -486,6 +481,7 @@ fn execute_inner( variant_def, false, id, + meter, )? .into_iter() { @@ -506,6 +502,7 @@ fn execute_inner( variant_def, true, id, + meter, )? .into_iter() { @@ -525,6 +522,7 @@ fn execute_inner( variant_def, false, id, + meter, )? .into_iter() { @@ -544,13 +542,16 @@ fn execute_inner( variant_def, true, id, + meter, )? .into_iter() { verifier.push(val)? } } - Bytecode::VariantSwitch(_) => state.release_value(safe_unwrap_err!(verifier.stack.pop())), + Bytecode::VariantSwitch(_) => { + state.release_value(safe_unwrap_err!(verifier.stack.pop()), meter)? + } }; Ok(()) } diff --git a/external-crates/move/crates/move-bytecode-verifier/src/signature.rs b/external-crates/move/crates/move-bytecode-verifier/src/signature.rs index 29b96a2a2af52..c1052c190ba6a 100644 --- a/external-crates/move/crates/move-bytecode-verifier/src/signature.rs +++ b/external-crates/move/crates/move-bytecode-verifier/src/signature.rs @@ -15,22 +15,38 @@ use move_binary_format::{ file_format_common::VERSION_6, IndexKind, }; +use move_bytecode_verifier_meter::{Meter, Scope}; use move_core_types::vm_status::StatusCode; use std::collections::{HashMap, HashSet}; -pub struct SignatureChecker<'a> { - module: &'a CompiledModule, +use crate::ability_cache::AbilityCache; + +pub struct SignatureChecker<'env, 'a, 'b, M: Meter + ?Sized> { + module: &'env CompiledModule, + module_ability_cache: &'a mut AbilityCache<'env>, + meter: &'b mut M, abilities_cache: HashMap>>, } -impl<'a> SignatureChecker<'a> { - pub fn verify_module(module: &'a CompiledModule) -> VMResult<()> { - Self::verify_module_impl(module).map_err(|e| e.finish(Location::Module(module.self_id()))) +impl<'env, 'a, 'b, M: Meter + ?Sized> SignatureChecker<'env, 'a, 'b, M> { + pub fn verify_module( + module: &'env CompiledModule, + module_ability_cache: &'a mut AbilityCache<'env>, + meter: &'b mut M, + ) -> VMResult<()> { + Self::verify_module_impl(module, module_ability_cache, meter) + .map_err(|e| e.finish(Location::Module(module.self_id()))) } - fn verify_module_impl(module: &'a CompiledModule) -> PartialVMResult<()> { + fn verify_module_impl( + module: &'env CompiledModule, + module_ability_cache: &'a mut AbilityCache<'env>, + meter: &'b mut M, + ) -> PartialVMResult<()> { let mut sig_check = Self { module, + module_ability_cache, + meter, abilities_cache: HashMap::new(), }; sig_check.verify_signature_pool(module.signatures())?; @@ -66,7 +82,7 @@ impl<'a> SignatureChecker<'a> { Ok(()) } - fn verify_struct_fields(&self, struct_defs: &[StructDefinition]) -> PartialVMResult<()> { + fn verify_struct_fields(&mut self, struct_defs: &[StructDefinition]) -> PartialVMResult<()> { for (struct_def_idx, struct_def) in struct_defs.iter().enumerate() { let fields = match &struct_def.field_information { StructFieldInformation::Native => continue, @@ -95,7 +111,7 @@ impl<'a> SignatureChecker<'a> { Ok(()) } - fn verify_enum_fields(&self, enum_defs: &[EnumDefinition]) -> PartialVMResult<()> { + fn verify_enum_fields(&mut self, enum_defs: &[EnumDefinition]) -> PartialVMResult<()> { for (enum_def_idx, enum_def) in enum_defs.iter().enumerate() { let enum_handle = self.module.datatype_handle_at(enum_def.enum_handle); let type_param_constraints: Vec<_> = enum_handle.type_param_constraints().collect(); @@ -419,7 +435,7 @@ impl<'a> SignatureChecker<'a> { } fn check_type_instantiation( - &self, + &mut self, s: &SignatureToken, type_parameters: &[AbilitySet], ) -> PartialVMResult<()> { @@ -435,7 +451,7 @@ impl<'a> SignatureChecker<'a> { } fn check_type_instantiation_( - &self, + &mut self, s: &SignatureToken, type_parameters: &[AbilitySet], ) -> PartialVMResult<()> { @@ -472,7 +488,7 @@ impl<'a> SignatureChecker<'a> { // Checks if the given types are well defined and satisfy the constraints in the given context. fn check_generic_instance( - &self, + &mut self, type_arguments: &[SignatureToken], constraints: impl ExactSizeIterator, global_abilities: &[AbilitySet], @@ -489,8 +505,11 @@ impl<'a> SignatureChecker<'a> { ); } + let meter: &mut M = self.meter; + let module_ability_cache: &mut AbilityCache = self.module_ability_cache; for (constraint, ty) in constraints.into_iter().zip(type_arguments) { - let given = self.module.abilities(ty, global_abilities)?; + let given = + module_ability_cache.abilities(Scope::Module, meter, global_abilities, ty)?; if !constraint.is_subset(given) { return Err(PartialVMError::new(StatusCode::CONSTRAINT_NOT_SATISFIED) .with_message(format!( diff --git a/external-crates/move/crates/move-bytecode-verifier/src/type_safety.rs b/external-crates/move/crates/move-bytecode-verifier/src/type_safety.rs index 6d2b33ee02425..ddace3cf61530 100644 --- a/external-crates/move/crates/move-bytecode-verifier/src/type_safety.rs +++ b/external-crates/move/crates/move-bytecode-verifier/src/type_safety.rs @@ -5,7 +5,7 @@ //! This module defines the transfer functions for verifying type safety of a procedure body. //! It does not utilize control flow, but does check each block independently -use std::num::NonZeroU64; +use std::{cmp::max, num::NonZeroU64}; use move_abstract_interpreter::{absint::FunctionContext, control_flow_graph::ControlFlowGraph}; use move_abstract_stack::AbstractStack; @@ -22,13 +22,17 @@ use move_binary_format::{ use move_bytecode_verifier_meter::{Meter, Scope}; use move_core_types::vm_status::StatusCode; +use crate::ability_cache::AbilityCache; + struct Locals<'a> { param_count: usize, parameters: &'a Signature, locals: &'a Signature, } -const TYPE_NODE_COST: u128 = 30; +const TYPE_NODE_COST: u128 = 6; +const TYPE_NODE_QUADRATIC_THRESHOLD: usize = 10; +const TYPE_PUSH_COST: u128 = 3; impl<'a> Locals<'a> { fn new(parameters: &'a Signature, locals: &'a Signature) -> Self { @@ -49,19 +53,25 @@ impl<'a> Locals<'a> { } } -struct TypeSafetyChecker<'a> { - module: &'a CompiledModule, - function_context: &'a FunctionContext<'a>, - locals: Locals<'a>, +struct TypeSafetyChecker<'env, 'a> { + module: &'env CompiledModule, + function_context: &'a FunctionContext<'env>, + ability_cache: &'a mut AbilityCache<'env>, + locals: Locals<'env>, stack: AbstractStack, } -impl<'a> TypeSafetyChecker<'a> { - fn new(module: &'a CompiledModule, function_context: &'a FunctionContext<'a>) -> Self { +impl<'env, 'a> TypeSafetyChecker<'env, 'a> { + fn new( + module: &'env CompiledModule, + function_context: &'a FunctionContext<'env>, + ability_cache: &'a mut AbilityCache<'env>, + ) -> Self { let locals = Locals::new(function_context.parameters(), function_context.locals()); Self { module, function_context, + ability_cache, locals, stack: AbstractStack::new(), } @@ -71,9 +81,17 @@ impl<'a> TypeSafetyChecker<'a> { self.locals.local_at(i) } - fn abilities(&self, t: &SignatureToken) -> PartialVMResult { - self.module - .abilities(t, self.function_context.type_parameters()) + fn abilities( + &mut self, + meter: &mut (impl Meter + ?Sized), + t: &SignatureToken, + ) -> PartialVMResult { + self.ability_cache.abilities( + Scope::Function, + meter, + self.function_context.type_parameters(), + t, + ) } fn error(&self, status: StatusCode, offset: CodeOffset) -> PartialVMError { @@ -90,7 +108,7 @@ impl<'a> TypeSafetyChecker<'a> { meter: &mut (impl Meter + ?Sized), ty: SignatureToken, ) -> PartialVMResult<()> { - self.charge_ty(meter, &ty)?; + meter.add(Scope::Function, TYPE_PUSH_COST)?; safe_unwrap_err!(self.stack.push(ty)); Ok(()) } @@ -101,50 +119,41 @@ impl<'a> TypeSafetyChecker<'a> { ty: SignatureToken, n: u64, ) -> PartialVMResult<()> { - self.charge_ty(meter, &ty)?; + meter.add_items(Scope::Function, TYPE_PUSH_COST, n as usize)?; safe_unwrap_err!(self.stack.push_n(ty, n)); Ok(()) } +} - fn charge_ty( - &mut self, - meter: &mut (impl Meter + ?Sized), - ty: &SignatureToken, - ) -> PartialVMResult<()> { - self.charge_ty_(meter, ty, 1) - } - - fn charge_ty_( - &mut self, - meter: &mut (impl Meter + ?Sized), - ty: &SignatureToken, - n: u64, - ) -> PartialVMResult<()> { - meter.add_items( - Scope::Function, - TYPE_NODE_COST, - ty.preorder_traversal().count() * (n as usize), - ) - } +macro_rules! charge_clone { + ($meter:ident, $ty:expr) => {{ + let ty: &SignatureToken = $ty; + charge_ty(&mut *$meter, ty)?; + ty.clone() + }}; +} - fn charge_tys( - &mut self, - meter: &mut (impl Meter + ?Sized), - tys: &[SignatureToken], - ) -> PartialVMResult<()> { - for ty in tys { - self.charge_ty(meter, ty)? - } - Ok(()) - } +fn charge_ty(meter: &mut (impl Meter + ?Sized), ty: &SignatureToken) -> PartialVMResult<()> { + let size = ty.preorder_traversal().count(); + meter.add_items( + Scope::Function, + TYPE_NODE_COST, + // max(x, x^2/10) + max( + size, + size.saturating_mul(size) / TYPE_NODE_QUADRATIC_THRESHOLD, + ), + ) } -pub(crate) fn verify<'a>( - module: &'a CompiledModule, - function_context: &'a FunctionContext<'a>, +pub(crate) fn verify<'env>( + module: &'env CompiledModule, + function_context: &FunctionContext<'env>, + ability_cache: &mut AbilityCache<'env>, meter: &mut (impl Meter + ?Sized), ) -> PartialVMResult<()> { - let verifier = &mut TypeSafetyChecker::new(module, function_context); + let mut checker = TypeSafetyChecker::new(module, function_context, ability_cache); + let verifier = &mut checker; for block_id in function_context.cfg().blocks() { for offset in function_context.cfg().instr_indexes(block_id) { @@ -220,7 +229,7 @@ fn borrow_field( // For generic fields access, this step materializes that type let field_handle = verifier.module.field_handle_at(field_handle_index); let struct_def = verifier.module.struct_def_at(field_handle.owner); - let expected_type = materialize_type(struct_def.struct_handle, type_args); + let expected_type = materialize_type(meter, struct_def.struct_handle, type_args)?; match operand { ST::Reference(inner) | ST::MutableReference(inner) if expected_type == *inner => (), _ => return Err(verifier.error(StatusCode::BORROWFIELD_TYPE_MISMATCH_ERROR, offset)), @@ -237,7 +246,7 @@ fn borrow_field( &fields[field_handle.field as usize] } }; - let field_type = Box::new(instantiate(&field_def.signature.0, type_args)); + let field_type = Box::new(instantiate(meter, &field_def.signature.0, type_args)?); verifier.push( meter, if mut_ { @@ -257,7 +266,7 @@ fn borrow_loc( mut_: bool, idx: LocalIndex, ) -> PartialVMResult<()> { - let loc_signature = verifier.local_at(idx).clone(); + let loc_signature = charge_clone!(meter, verifier.local_at(idx)); if loc_signature.is_reference() { return Err(verifier.error(StatusCode::BORROWLOC_REFERENCE_ERROR, offset)); @@ -289,12 +298,11 @@ fn borrow_global( } let struct_def = verifier.module.struct_def_at(idx); - let struct_type = materialize_type(struct_def.struct_handle, type_args); - if !verifier.abilities(&struct_type)?.has_key() { + let struct_type = materialize_type(meter, struct_def.struct_handle, type_args)?; + if !verifier.abilities(meter, &struct_type)?.has_key() { return Err(verifier.error(StatusCode::BORROWGLOBAL_WITHOUT_KEY_ABILITY, offset)); } - let struct_type = materialize_type(struct_def.struct_handle, type_args); verifier.push( meter, if mut_ { @@ -317,20 +325,21 @@ fn call( for parameter in parameters.0.iter().rev() { let arg = safe_unwrap_err!(verifier.stack.pop()); if (type_actuals.is_empty() && &arg != parameter) - || (!type_actuals.is_empty() && arg != instantiate(parameter, type_actuals)) + || (!type_actuals.is_empty() && arg != instantiate(meter, parameter, type_actuals)?) { return Err(verifier.error(StatusCode::CALL_TYPE_MISMATCH_ERROR, offset)); } } for return_type in &verifier.module.signature_at(function_handle.return_).0 { - verifier.push(meter, instantiate(return_type, type_actuals))? + let sig = instantiate(meter, return_type, type_actuals)?; + verifier.push(meter, sig)? } Ok(()) } fn type_fields_signature( verifier: &mut TypeSafetyChecker, - _meter: &mut (impl Meter + ?Sized), // TODO: metering + meter: &mut (impl Meter + ?Sized), offset: CodeOffset, struct_def: &StructDefinition, type_args: &Signature, @@ -341,10 +350,10 @@ fn type_fields_signature( Err(verifier.error(StatusCode::PACK_TYPE_MISMATCH_ERROR, offset)) } StructFieldInformation::Declared(fields) => { - let mut field_sig = vec![]; - for field_def in fields.iter() { - field_sig.push(instantiate(&field_def.signature.0, type_args)); - } + let field_sig = fields + .iter() + .map(|field_def| instantiate(meter, &field_def.signature.0, type_args)) + .collect::>()?; Ok(Signature(field_sig)) } } @@ -357,7 +366,6 @@ fn pack_struct( struct_def: &StructDefinition, type_args: &Signature, ) -> PartialVMResult<()> { - let struct_type = materialize_type(struct_def.struct_handle, type_args); let field_sig = type_fields_signature(verifier, meter, offset, struct_def, type_args)?; for sig in field_sig.0.iter().rev() { let arg = safe_unwrap_err!(verifier.stack.pop()); @@ -365,7 +373,7 @@ fn pack_struct( return Err(verifier.error(StatusCode::PACK_TYPE_MISMATCH_ERROR, offset)); } } - + let struct_type = materialize_type(meter, struct_def.struct_handle, type_args)?; verifier.push(meter, struct_type)?; Ok(()) } @@ -377,7 +385,7 @@ fn unpack_struct( struct_def: &StructDefinition, type_args: &Signature, ) -> PartialVMResult<()> { - let struct_type = materialize_type(struct_def.struct_handle, type_args); + let struct_type = materialize_type(meter, struct_def.struct_handle, type_args)?; // Pop an abstract value from the stack and check if its type is equal to the one // declared. @@ -401,18 +409,15 @@ fn pack_enum_variant( variant_def: &VariantDefinition, type_args: &Signature, ) -> PartialVMResult<()> { - let enum_type = materialize_type(enum_def.enum_handle, type_args); - let field_sig = variant_def - .fields - .iter() - .map(|field_def| instantiate(&field_def.signature.0, type_args)); - for sig in field_sig.rev() { + for field_def in variant_def.fields.iter().rev() { + let sig = instantiate(meter, &field_def.signature.0, type_args)?; let arg = safe_unwrap_err!(verifier.stack.pop()); if arg != sig { return Err(verifier.error(StatusCode::PACK_TYPE_MISMATCH_ERROR, offset)); } } + let enum_type = materialize_type(meter, enum_def.enum_handle, type_args)?; verifier.push(meter, enum_type)?; Ok(()) } @@ -425,7 +430,7 @@ fn unpack_enum_variant_by_value( variant_def: &VariantDefinition, type_args: &Signature, ) -> PartialVMResult<()> { - let enum_type = materialize_type(enum_def.enum_handle, type_args); + let enum_type = materialize_type(meter, enum_def.enum_handle, type_args)?; // Pop an abstract value from the stack and check if its type is equal to the one // declared. @@ -434,11 +439,8 @@ fn unpack_enum_variant_by_value( return Err(verifier.error(StatusCode::UNPACK_TYPE_MISMATCH_ERROR, offset)); } - let field_sig = variant_def - .fields - .iter() - .map(|field_def| instantiate(&field_def.signature.0, type_args)); - for sig in field_sig { + for field_def in &variant_def.fields { + let sig = instantiate(meter, &field_def.signature.0, type_args)?; verifier.push(meter, sig)? } Ok(()) @@ -453,8 +455,6 @@ fn unpack_enum_variant_by_ref( variant_def: &VariantDefinition, type_args: &Signature, ) -> PartialVMResult<()> { - let enum_type = materialize_type(enum_def.enum_handle, type_args); - // Pop an abstract value from the stack and check if its type is equal to the one // declared. let arg = safe_unwrap_err!(verifier.stack.pop()); @@ -467,20 +467,18 @@ fn unpack_enum_variant_by_ref( _ => return Err(verifier.error(StatusCode::UNPACK_TYPE_MISMATCH_ERROR, offset)), }; + let enum_type = materialize_type(meter, enum_def.enum_handle, type_args)?; if *inner != enum_type { return Err(verifier.error(StatusCode::UNPACK_TYPE_MISMATCH_ERROR, offset)); } - let field_sig = variant_def - .fields - .iter() - .map(|field_def| instantiate(&field_def.signature.0, type_args)); - for sig in field_sig { - let mk_sig = if mut_ { - ST::MutableReference - } else { - ST::Reference - }; + let mk_sig = if mut_ { + ST::MutableReference + } else { + ST::Reference + }; + for field_def in &variant_def.fields { + let sig = instantiate(meter, &field_def.signature.0, type_args)?; verifier.push(meter, mk_sig(Box::new(sig)))? } Ok(()) @@ -493,8 +491,8 @@ fn exists( struct_def: &StructDefinition, type_args: &Signature, ) -> PartialVMResult<()> { - let struct_type = materialize_type(struct_def.struct_handle, type_args); - if !verifier.abilities(&struct_type)?.has_key() { + let struct_type = materialize_type(meter, struct_def.struct_handle, type_args)?; + if !verifier.abilities(meter, &struct_type)?.has_key() { return Err(verifier.error( StatusCode::EXISTS_WITHOUT_KEY_ABILITY_OR_BAD_ARGUMENT, offset, @@ -521,12 +519,11 @@ fn move_from( struct_def: &StructDefinition, type_args: &Signature, ) -> PartialVMResult<()> { - let struct_type = materialize_type(struct_def.struct_handle, type_args); - if !verifier.abilities(&struct_type)?.has_key() { + let struct_type = materialize_type(meter, struct_def.struct_handle, type_args)?; + if !verifier.abilities(meter, &struct_type)?.has_key() { return Err(verifier.error(StatusCode::MOVEFROM_WITHOUT_KEY_ABILITY, offset)); } - let struct_type = materialize_type(struct_def.struct_handle, type_args); let operand = safe_unwrap_err!(verifier.stack.pop()); if operand != ST::Address { return Err(verifier.error(StatusCode::MOVEFROM_TYPE_MISMATCH_ERROR, offset)); @@ -538,16 +535,16 @@ fn move_from( fn move_to( verifier: &mut TypeSafetyChecker, + meter: &mut (impl Meter + ?Sized), offset: CodeOffset, struct_def: &StructDefinition, type_args: &Signature, ) -> PartialVMResult<()> { - let struct_type = materialize_type(struct_def.struct_handle, type_args); - if !verifier.abilities(&struct_type)?.has_key() { + let struct_type = materialize_type(meter, struct_def.struct_handle, type_args)?; + if !verifier.abilities(meter, &struct_type)?.has_key() { return Err(verifier.error(StatusCode::MOVETO_WITHOUT_KEY_ABILITY, offset)); } - let struct_type = materialize_type(struct_def.struct_handle, type_args); let key_struct_operand = safe_unwrap_err!(verifier.stack.pop()); let signer_reference_operand = safe_unwrap_err!(verifier.stack.pop()); if key_struct_operand != struct_type { @@ -602,10 +599,8 @@ fn verify_instr( match bytecode { Bytecode::Pop => { let operand = safe_unwrap_err!(verifier.stack.pop()); - let abilities = verifier - .module - .abilities(&operand, verifier.function_context.type_parameters()); - if !abilities?.has_drop() { + let abilities = verifier.abilities(meter, &operand)?; + if !abilities.has_drop() { return Err(verifier.error(StatusCode::POP_WITHOUT_DROP_ABILITY, offset)); } } @@ -663,7 +658,6 @@ fn verify_instr( Bytecode::MutBorrowFieldGeneric(field_inst_index) => { let field_inst = verifier.module.field_instantiation_at(*field_inst_index); let type_inst = verifier.module.signature_at(field_inst.type_parameters); - verifier.charge_tys(meter, &type_inst.0)?; borrow_field(verifier, meter, offset, true, field_inst.handle, type_inst)? } @@ -679,7 +673,6 @@ fn verify_instr( Bytecode::ImmBorrowFieldGeneric(field_inst_index) => { let field_inst = verifier.module.field_instantiation_at(*field_inst_index); let type_inst = verifier.module.signature_at(field_inst.type_parameters); - verifier.charge_tys(meter, &type_inst.0)?; borrow_field(verifier, meter, offset, false, field_inst.handle, type_inst)? } @@ -708,7 +701,7 @@ fn verify_instr( } Bytecode::LdConst(idx) => { - let signature = verifier.module.constant_at(*idx).type_.clone(); + let signature = charge_clone!(meter, &verifier.module.constant_at(*idx).type_); verifier.push(meter, signature)?; } @@ -717,22 +710,15 @@ fn verify_instr( } Bytecode::CopyLoc(idx) => { - let local_signature = verifier.local_at(*idx).clone(); - if !verifier - .module - .abilities( - &local_signature, - verifier.function_context.type_parameters(), - )? - .has_copy() - { + let local_signature = charge_clone!(meter, verifier.local_at(*idx)); + if !verifier.abilities(meter, &local_signature)?.has_copy() { return Err(verifier.error(StatusCode::COPYLOC_WITHOUT_COPY_ABILITY, offset)); } verifier.push(meter, local_signature)? } Bytecode::MoveLoc(idx) => { - let local_signature = verifier.local_at(*idx).clone(); + let local_signature = charge_clone!(meter, verifier.local_at(*idx)); verifier.push(meter, local_signature)? } @@ -749,7 +735,6 @@ fn verify_instr( let func_inst = verifier.module.function_instantiation_at(*idx); let func_handle = verifier.module.function_handle_at(func_inst.handle); let type_args = &verifier.module.signature_at(func_inst.type_parameters); - verifier.charge_tys(meter, &type_args.0)?; call(verifier, meter, offset, func_handle, type_args)? } @@ -768,7 +753,6 @@ fn verify_instr( let struct_inst = verifier.module.struct_instantiation_at(*idx); let struct_def = verifier.module.struct_def_at(struct_inst.def); let type_args = verifier.module.signature_at(struct_inst.type_parameters); - verifier.charge_tys(meter, &type_args.0)?; pack_struct(verifier, meter, offset, struct_def, type_args)? } @@ -787,7 +771,6 @@ fn verify_instr( let struct_inst = verifier.module.struct_instantiation_at(*idx); let struct_def = verifier.module.struct_def_at(struct_inst.def); let type_args = verifier.module.signature_at(struct_inst.type_parameters); - verifier.charge_tys(meter, &type_args.0)?; unpack_struct(verifier, meter, offset, struct_def, type_args)? } @@ -795,7 +778,7 @@ fn verify_instr( let operand = safe_unwrap_err!(verifier.stack.pop()); match operand { ST::Reference(inner) | ST::MutableReference(inner) => { - if !verifier.abilities(&inner)?.has_copy() { + if !verifier.abilities(meter, &inner)?.has_copy() { return Err( verifier.error(StatusCode::READREF_WITHOUT_COPY_ABILITY, offset) ); @@ -817,7 +800,7 @@ fn verify_instr( ) } }; - if !verifier.abilities(&ref_inner_signature)?.has_drop() { + if !verifier.abilities(meter, &ref_inner_signature)?.has_drop() { return Err(verifier.error(StatusCode::WRITEREF_WITHOUT_DROP_ABILITY, offset)); } @@ -897,7 +880,7 @@ fn verify_instr( Bytecode::Eq | Bytecode::Neq => { let operand1 = safe_unwrap_err!(verifier.stack.pop()); let operand2 = safe_unwrap_err!(verifier.stack.pop()); - if verifier.abilities(&operand1)?.has_drop() && operand1 == operand2 { + if verifier.abilities(meter, &operand1)?.has_drop() && operand1 == operand2 { verifier.push(meter, ST::Bool)?; } else { return Err(verifier.error(StatusCode::EQUALITY_OP_TYPE_MISMATCH_ERROR, offset)); @@ -921,7 +904,6 @@ fn verify_instr( Bytecode::MutBorrowGlobalGenericDeprecated(idx) => { let struct_inst = verifier.module.struct_instantiation_at(*idx); let type_inst = verifier.module.signature_at(struct_inst.type_parameters); - verifier.charge_tys(meter, &type_inst.0)?; borrow_global(verifier, meter, offset, true, struct_inst.def, type_inst)? } @@ -932,7 +914,6 @@ fn verify_instr( Bytecode::ImmBorrowGlobalGenericDeprecated(idx) => { let struct_inst = verifier.module.struct_instantiation_at(*idx); let type_inst = verifier.module.signature_at(struct_inst.type_parameters); - verifier.charge_tys(meter, &type_inst.0)?; borrow_global(verifier, meter, offset, false, struct_inst.def, type_inst)? } @@ -945,7 +926,6 @@ fn verify_instr( let struct_inst = verifier.module.struct_instantiation_at(*idx); let struct_def = verifier.module.struct_def_at(struct_inst.def); let type_args = verifier.module.signature_at(struct_inst.type_parameters); - verifier.charge_tys(meter, &type_args.0)?; exists(verifier, meter, offset, struct_def, type_args)? } @@ -958,21 +938,19 @@ fn verify_instr( let struct_inst = verifier.module.struct_instantiation_at(*idx); let struct_def = verifier.module.struct_def_at(struct_inst.def); let type_args = verifier.module.signature_at(struct_inst.type_parameters); - verifier.charge_tys(meter, &type_args.0)?; move_from(verifier, meter, offset, struct_def, type_args)? } Bytecode::MoveToDeprecated(idx) => { let struct_def = verifier.module.struct_def_at(*idx); - move_to(verifier, offset, struct_def, &Signature(vec![]))? + move_to(verifier, meter, offset, struct_def, &Signature(vec![]))? } Bytecode::MoveToGenericDeprecated(idx) => { let struct_inst = verifier.module.struct_instantiation_at(*idx); let struct_def = verifier.module.struct_def_at(struct_inst.def); let type_args = verifier.module.signature_at(struct_inst.type_parameters); - verifier.charge_tys(meter, &type_args.0)?; - move_to(verifier, offset, struct_def, type_args)? + move_to(verifier, meter, offset, struct_def, type_args)? } Bytecode::VecPack(idx, num) => { @@ -987,7 +965,8 @@ fn verify_instr( return Err(verifier.error(StatusCode::TYPE_MISMATCH, offset)); } } - verifier.push(meter, ST::Vector(Box::new(element_type.clone())))?; + let element_type = charge_clone!(meter, element_type); + verifier.push(meter, ST::Vector(Box::new(element_type)))?; } Bytecode::VecLen(idx) => { @@ -1037,10 +1016,13 @@ fn verify_instr( Bytecode::VecUnpack(idx, num) => { let operand_vec = safe_unwrap_err!(verifier.stack.pop()); let declared_element_type = &verifier.module.signature_at(*idx).0[0]; - if operand_vec != ST::Vector(Box::new(declared_element_type.clone())) { + let correct_vec_ty = + matches!(operand_vec, ST::Vector(inner) if &*inner == declared_element_type); + if !correct_vec_ty { return Err(verifier.error(StatusCode::TYPE_MISMATCH, offset)); } - verifier.push_n(meter, declared_element_type.clone(), *num)?; + let declared_element_type = charge_clone!(meter, declared_element_type); + verifier.push_n(meter, declared_element_type, *num)?; } Bytecode::VecSwap(idx) => { @@ -1096,7 +1078,6 @@ fn verify_instr( let type_args = verifier.module.signature_at(enum_inst.type_parameters); let enum_def = verifier.module.enum_def_at(enum_inst.def); let variant_def = &enum_def.variants[handle.variant as usize]; - verifier.charge_tys(meter, &type_args.0)?; pack_enum_variant(verifier, meter, offset, enum_def, variant_def, type_args)? } Bytecode::UnpackVariant(vidx) => { @@ -1118,7 +1099,6 @@ fn verify_instr( let type_args = verifier.module.signature_at(enum_inst.type_parameters); let enum_def = verifier.module.enum_def_at(enum_inst.def); let variant_def = &enum_def.variants[handle.variant as usize]; - verifier.charge_tys(meter, &type_args.0)?; unpack_enum_variant_by_value(verifier, meter, offset, enum_def, variant_def, type_args)? } Bytecode::UnpackVariantImmRef(vidx) => { @@ -1155,7 +1135,6 @@ fn verify_instr( let type_args = verifier.module.signature_at(enum_inst.type_parameters); let enum_def = verifier.module.enum_def_at(enum_inst.def); let variant_def = &enum_def.variants[handle.variant as usize]; - verifier.charge_tys(meter, &type_args.0)?; unpack_enum_variant_by_ref( verifier, meter, @@ -1172,7 +1151,6 @@ fn verify_instr( let type_args = verifier.module.signature_at(enum_inst.type_parameters); let enum_def = verifier.module.enum_def_at(enum_inst.def); let variant_def = &enum_def.variants[handle.variant as usize]; - verifier.charge_tys(meter, &type_args.0)?; unpack_enum_variant_by_ref( verifier, meter, @@ -1195,49 +1173,64 @@ fn verify_instr( // Helpers functions for types // -fn materialize_type(struct_handle: DatatypeHandleIndex, type_args: &Signature) -> SignatureToken { - if type_args.is_empty() { +fn materialize_type( + meter: &mut (impl Meter + ?Sized), + struct_handle: DatatypeHandleIndex, + type_args: &Signature, +) -> PartialVMResult { + let ty = if type_args.is_empty() { ST::Datatype(struct_handle) } else { ST::DatatypeInstantiation(Box::new((struct_handle, type_args.0.clone()))) - } + }; + charge_ty(meter, &ty)?; + Ok(ty) } -fn instantiate(token: &SignatureToken, subst: &Signature) -> SignatureToken { - use SignatureToken::*; - - if subst.0.is_empty() { - return token.clone(); - } - - match token { - Bool => Bool, - U8 => U8, - U16 => U16, - U32 => U32, - U64 => U64, - U128 => U128, - U256 => U256, - Address => Address, - Signer => Signer, - Vector(ty) => Vector(Box::new(instantiate(ty, subst))), - Datatype(idx) => Datatype(*idx), - DatatypeInstantiation(inst) => { - let (idx, type_args) = &**inst; - DatatypeInstantiation(Box::new(( - *idx, - type_args.iter().map(|ty| instantiate(ty, subst)).collect(), - ))) - } - Reference(ty) => Reference(Box::new(instantiate(ty, subst))), - MutableReference(ty) => MutableReference(Box::new(instantiate(ty, subst))), - TypeParameter(idx) => { - // Assume that the caller has previously parsed and verified the structure of the - // file and that this guarantees that type parameter indices are always in bounds. - debug_assert!((*idx as usize) < subst.len()); - subst.0[*idx as usize].clone() +fn instantiate( + meter: &mut (impl Meter + ?Sized), + token: &SignatureToken, + subst: &Signature, +) -> PartialVMResult { + fn rec(token: &SignatureToken, subst: &Signature) -> SignatureToken { + use SignatureToken::*; + + if subst.0.is_empty() { + return token.clone(); + } + + match token { + Bool => Bool, + U8 => U8, + U16 => U16, + U32 => U32, + U64 => U64, + U128 => U128, + U256 => U256, + Address => Address, + Signer => Signer, + Vector(ty) => Vector(Box::new(rec(ty, subst))), + Datatype(idx) => Datatype(*idx), + DatatypeInstantiation(inst) => { + let (idx, type_args) = &**inst; + DatatypeInstantiation(Box::new(( + *idx, + type_args.iter().map(|ty| rec(ty, subst)).collect(), + ))) + } + Reference(ty) => Reference(Box::new(rec(ty, subst))), + MutableReference(ty) => MutableReference(Box::new(rec(ty, subst))), + TypeParameter(idx) => { + // Assume that the caller has previously parsed and verified the structure of the + // file and that this guarantees that type parameter indices are always in bounds. + debug_assert!((*idx as usize) < subst.len()); + subst.0[*idx as usize].clone() + } } } + let ty = rec(token, subst); + charge_ty(meter, &ty)?; + Ok(ty) } fn get_vector_element_type( diff --git a/external-crates/move/crates/move-bytecode-verifier/src/verifier.rs b/external-crates/move/crates/move-bytecode-verifier/src/verifier.rs index 16fc546c260c8..2c441ce0cd2bb 100644 --- a/external-crates/move/crates/move-bytecode-verifier/src/verifier.rs +++ b/external-crates/move/crates/move-bytecode-verifier/src/verifier.rs @@ -4,11 +4,18 @@ //! This module contains the public APIs supported by the bytecode verifier. use crate::{ - ability_field_requirements, check_duplication::DuplicationChecker, - code_unit_verifier::CodeUnitVerifier, constants, data_defs::RecursiveDataDefChecker, friends, - instantiation_loops::InstantiationLoopChecker, instruction_consistency::InstructionConsistency, - limits::LimitsVerifier, script_signature, - script_signature::no_additional_script_signature_checks, signature::SignatureChecker, + ability_cache::AbilityCache, + ability_field_requirements, + check_duplication::DuplicationChecker, + code_unit_verifier::{self}, + constants, + data_defs::RecursiveDataDefChecker, + friends, + instantiation_loops::InstantiationLoopChecker, + instruction_consistency::InstructionConsistency, + limits::LimitsVerifier, + script_signature::{self, no_additional_script_signature_checks}, + signature::SignatureChecker, }; use move_binary_format::{ check_bounds::BoundsChecker, @@ -76,6 +83,7 @@ pub fn verify_module_with_config_metered( module: &CompiledModule, meter: &mut (impl Meter + ?Sized), ) -> VMResult<()> { + let ability_cache = &mut AbilityCache::new(module); BoundsChecker::verify_module(module).map_err(|e| { // We can't point the error at the module, because if bounds-checking // failed, we cannot safely index into module's handle to itself. @@ -83,14 +91,14 @@ pub fn verify_module_with_config_metered( })?; LimitsVerifier::verify_module(config, module)?; DuplicationChecker::verify_module(module)?; - SignatureChecker::verify_module(module)?; + SignatureChecker::verify_module(module, ability_cache, meter)?; InstructionConsistency::verify_module(module)?; constants::verify_module(module)?; friends::verify_module(module)?; - ability_field_requirements::verify_module(module)?; + ability_field_requirements::verify_module(module, ability_cache, meter)?; RecursiveDataDefChecker::verify_module(module)?; InstantiationLoopChecker::verify_module(module)?; - CodeUnitVerifier::verify_module(config, module, meter)?; + code_unit_verifier::verify_module(config, module, ability_cache, meter)?; script_signature::verify_module(module, no_additional_script_signature_checks) } diff --git a/external-crates/move/crates/move-compiler/src/cfgir/borrows/state.rs b/external-crates/move/crates/move-compiler/src/cfgir/borrows/state.rs index 93d345fd3965b..a9a97be7e1efe 100644 --- a/external-crates/move/crates/move-compiler/src/cfgir/borrows/state.rs +++ b/external-crates/move/crates/move-compiler/src/cfgir/borrows/state.rs @@ -369,7 +369,7 @@ impl BorrowState { fn release(&mut self, ref_id: RefID) { self.id_to_exp.remove(&ref_id); - self.borrows.release(ref_id) + self.borrows.release(ref_id); } fn divergent_control_flow(&mut self) { diff --git a/external-crates/move/crates/move-vm-config/src/verifier.rs b/external-crates/move/crates/move-vm-config/src/verifier.rs index d33febd865d83..d029b308cf260 100644 --- a/external-crates/move/crates/move-vm-config/src/verifier.rs +++ b/external-crates/move/crates/move-vm-config/src/verifier.rs @@ -77,12 +77,22 @@ impl Default for VerifierConfig { } } +impl MeterConfig { + pub fn old_default() -> Self { + Self { + max_per_fun_meter_units: Some(8_000_000), + max_per_mod_meter_units: Some(8_000_000), + max_per_pkg_meter_units: Some(8_000_000), + } + } +} + impl Default for MeterConfig { fn default() -> Self { Self { - max_per_fun_meter_units: Some(1000 * 8000), - max_per_mod_meter_units: Some(1000 * 8000), - max_per_pkg_meter_units: Some(1000 * 8000), + max_per_fun_meter_units: Some(2_200_000), + max_per_mod_meter_units: Some(2_200_000), + max_per_pkg_meter_units: Some(2_200_000), } } } diff --git a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/mod.rs b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/mod.rs index b18560955e1e9..20ef87ddf6fa1 100644 --- a/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/mod.rs +++ b/external-crates/move/move-execution/v0/crates/bytecode-verifier-tests/src/unit_tests/mod.rs @@ -53,6 +53,6 @@ pub(crate) fn production_config() -> (VerifierConfig, MeterConfig) { bytecode_version: VERSION_6, max_variants_in_enum: Some(VARIANT_COUNT_MAX), }, - MeterConfig::default(), + MeterConfig::old_default(), ) } diff --git a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/reference_safety/abstract_state.rs b/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/reference_safety/abstract_state.rs index d8d7b9f00a9a9..673660db4466b 100644 --- a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/reference_safety/abstract_state.rs +++ b/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/reference_safety/abstract_state.rs @@ -512,6 +512,7 @@ impl AbstractState { Ok(AbstractValue::Reference(elem_id)) } + #[allow(deprecated)] pub fn call( &mut self, offset: CodeOffset, diff --git a/external-crates/move/move-execution/v1/crates/bytecode-verifier-tests/src/unit_tests/mod.rs b/external-crates/move/move-execution/v1/crates/bytecode-verifier-tests/src/unit_tests/mod.rs index b18560955e1e9..20ef87ddf6fa1 100644 --- a/external-crates/move/move-execution/v1/crates/bytecode-verifier-tests/src/unit_tests/mod.rs +++ b/external-crates/move/move-execution/v1/crates/bytecode-verifier-tests/src/unit_tests/mod.rs @@ -53,6 +53,6 @@ pub(crate) fn production_config() -> (VerifierConfig, MeterConfig) { bytecode_version: VERSION_6, max_variants_in_enum: Some(VARIANT_COUNT_MAX), }, - MeterConfig::default(), + MeterConfig::old_default(), ) } diff --git a/external-crates/move/move-execution/v1/crates/move-bytecode-verifier/src/reference_safety/abstract_state.rs b/external-crates/move/move-execution/v1/crates/move-bytecode-verifier/src/reference_safety/abstract_state.rs index d8d7b9f00a9a9..673660db4466b 100644 --- a/external-crates/move/move-execution/v1/crates/move-bytecode-verifier/src/reference_safety/abstract_state.rs +++ b/external-crates/move/move-execution/v1/crates/move-bytecode-verifier/src/reference_safety/abstract_state.rs @@ -512,6 +512,7 @@ impl AbstractState { Ok(AbstractValue::Reference(elem_id)) } + #[allow(deprecated)] pub fn call( &mut self, offset: CodeOffset, diff --git a/external-crates/move/move-execution/v2/crates/bytecode-verifier-tests/src/unit_tests/mod.rs b/external-crates/move/move-execution/v2/crates/bytecode-verifier-tests/src/unit_tests/mod.rs index b18560955e1e9..20ef87ddf6fa1 100644 --- a/external-crates/move/move-execution/v2/crates/bytecode-verifier-tests/src/unit_tests/mod.rs +++ b/external-crates/move/move-execution/v2/crates/bytecode-verifier-tests/src/unit_tests/mod.rs @@ -53,6 +53,6 @@ pub(crate) fn production_config() -> (VerifierConfig, MeterConfig) { bytecode_version: VERSION_6, max_variants_in_enum: Some(VARIANT_COUNT_MAX), }, - MeterConfig::default(), + MeterConfig::old_default(), ) } diff --git a/external-crates/move/move-execution/v2/crates/move-bytecode-verifier/src/reference_safety/abstract_state.rs b/external-crates/move/move-execution/v2/crates/move-bytecode-verifier/src/reference_safety/abstract_state.rs index d8d7b9f00a9a9..673660db4466b 100644 --- a/external-crates/move/move-execution/v2/crates/move-bytecode-verifier/src/reference_safety/abstract_state.rs +++ b/external-crates/move/move-execution/v2/crates/move-bytecode-verifier/src/reference_safety/abstract_state.rs @@ -512,6 +512,7 @@ impl AbstractState { Ok(AbstractValue::Reference(elem_id)) } + #[allow(deprecated)] pub fn call( &mut self, offset: CodeOffset, From bacc76c3fe2747d278f7eacb5f3564076f900d18 Mon Sep 17 00:00:00 2001 From: Andrey Chursin Date: Thu, 22 Aug 2024 18:05:46 +0000 Subject: [PATCH 214/232] Rename RocksDB metrics (#19067) Some rocksdb metric names have typos --- crates/typed-store/src/metrics.rs | 24 ++++++++++++------------ crates/typed-store/src/rocks/mod.rs | 8 ++++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/crates/typed-store/src/metrics.rs b/crates/typed-store/src/metrics.rs index e3da36b8443bb..904ad5a31cf67 100644 --- a/crates/typed-store/src/metrics.rs +++ b/crates/typed-store/src/metrics.rs @@ -85,13 +85,13 @@ pub struct ColumnFamilyMetrics { pub rocksdb_block_cache_capacity: IntGaugeVec, pub rocksdb_block_cache_usage: IntGaugeVec, pub rocksdb_block_cache_pinned_usage: IntGaugeVec, - pub rocskdb_estimate_table_readers_mem: IntGaugeVec, + pub rocksdb_estimate_table_readers_mem: IntGaugeVec, pub rocksdb_mem_table_flush_pending: IntGaugeVec, - pub rocskdb_compaction_pending: IntGaugeVec, - pub rocskdb_num_running_compactions: IntGaugeVec, + pub rocksdb_compaction_pending: IntGaugeVec, + pub rocksdb_num_running_compactions: IntGaugeVec, pub rocksdb_num_running_flushes: IntGaugeVec, pub rocksdb_estimate_oldest_key_time: IntGaugeVec, - pub rocskdb_background_errors: IntGaugeVec, + pub rocksdb_background_errors: IntGaugeVec, pub rocksdb_estimated_num_keys: IntGaugeVec, } @@ -168,8 +168,8 @@ impl ColumnFamilyMetrics { registry, ) .unwrap(), - rocskdb_estimate_table_readers_mem: register_int_gauge_vec_with_registry!( - "rocskdb_estimate_table_readers_mem", + rocksdb_estimate_table_readers_mem: register_int_gauge_vec_with_registry!( + "rocksdb_estimate_table_readers_mem", "The estimated memory size used for reading SST tables in this column family such as filters and index blocks. Note that this number does not include the memory used in block cache.", @@ -186,8 +186,8 @@ impl ColumnFamilyMetrics { registry, ) .unwrap(), - rocskdb_compaction_pending: register_int_gauge_vec_with_registry!( - "rocskdb_compaction_pending", + rocksdb_compaction_pending: register_int_gauge_vec_with_registry!( + "rocksdb_compaction_pending", "A 1 or 0 flag indicating whether a compaction job is pending. If this number is 1, it means some part of the column family requires compaction in order to maintain shape of LSM tree, but the compaction @@ -198,8 +198,8 @@ impl ColumnFamilyMetrics { registry, ) .unwrap(), - rocskdb_num_running_compactions: register_int_gauge_vec_with_registry!( - "rocskdb_num_running_compactions", + rocksdb_num_running_compactions: register_int_gauge_vec_with_registry!( + "rocksdb_num_running_compactions", "The number of compactions that are currently running for the column family.", &["cf_name"], registry, @@ -227,8 +227,8 @@ impl ColumnFamilyMetrics { registry, ) .unwrap(), - rocskdb_background_errors: register_int_gauge_vec_with_registry!( - "rocskdb_background_errors", + rocksdb_background_errors: register_int_gauge_vec_with_registry!( + "rocksdb_background_errors", "The accumulated number of RocksDB background errors.", &["cf_name"], registry, diff --git a/crates/typed-store/src/rocks/mod.rs b/crates/typed-store/src/rocks/mod.rs index de33dd7952ca3..752b42fba8ab3 100644 --- a/crates/typed-store/src/rocks/mod.rs +++ b/crates/typed-store/src/rocks/mod.rs @@ -1050,7 +1050,7 @@ impl DBMap { ); db_metrics .cf_metrics - .rocskdb_estimate_table_readers_mem + .rocksdb_estimate_table_readers_mem .with_label_values(&[cf_name]) .set( Self::get_int_property(rocksdb, &cf, properties::ESTIMATE_TABLE_READERS_MEM) @@ -1074,7 +1074,7 @@ impl DBMap { ); db_metrics .cf_metrics - .rocskdb_compaction_pending + .rocksdb_compaction_pending .with_label_values(&[cf_name]) .set( Self::get_int_property(rocksdb, &cf, properties::COMPACTION_PENDING) @@ -1082,7 +1082,7 @@ impl DBMap { ); db_metrics .cf_metrics - .rocskdb_num_running_compactions + .rocksdb_num_running_compactions .with_label_values(&[cf_name]) .set( Self::get_int_property(rocksdb, &cf, properties::NUM_RUNNING_COMPACTIONS) @@ -1106,7 +1106,7 @@ impl DBMap { ); db_metrics .cf_metrics - .rocskdb_background_errors + .rocksdb_background_errors .with_label_values(&[cf_name]) .set( Self::get_int_property(rocksdb, &cf, properties::BACKGROUND_ERRORS) From bd0dc2fb9b74ca3cfdc3922bc63db2fb8d6098dc Mon Sep 17 00:00:00 2001 From: Ge Gao <106119108+gegaowp@users.noreply.github.com> Date: Thu, 22 Aug 2024 16:42:51 -0400 Subject: [PATCH 215/232] indexer fix: multiple object mutations in one checkpoint (#18991) ## Description per Xun's report https://linear.app/mysten-labs/issue/DP-43/bug-epochendindexingobjectstore-might-contain-multiple-versions-of ## Test plan CI ideally in the long run we want to have an embedded DB and test it on CI with a test, but we lack that today. --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-core/src/rest_index.rs | 2 +- .../src/handlers/checkpoint_handler.rs | 225 ++++++------------ .../sui-indexer/src/handlers/tx_processor.rs | 4 +- .../sui-types/src/full_checkpoint_content.rs | 51 +++- 4 files changed, 115 insertions(+), 167 deletions(-) diff --git a/crates/sui-core/src/rest_index.rs b/crates/sui-core/src/rest_index.rs index beb762c6d6a7f..e999cfe2abc2e 100644 --- a/crates/sui-core/src/rest_index.rs +++ b/crates/sui-core/src/rest_index.rs @@ -406,7 +406,7 @@ impl IndexStoreTables { for tx in &checkpoint.transactions { // determine changes from removed objects - for removed_object in tx.removed_objects() { + for removed_object in tx.removed_objects_pre_version() { match removed_object.owner() { Owner::AddressOwner(address) => { let owner_key = OwnerIndexKey::new(*address, removed_object.id()); diff --git a/crates/sui-indexer/src/handlers/checkpoint_handler.rs b/crates/sui-indexer/src/handlers/checkpoint_handler.rs index 7a8f8efe67fa9..a5273f6302e80 100644 --- a/crates/sui-indexer/src/handlers/checkpoint_handler.rs +++ b/crates/sui-indexer/src/handlers/checkpoint_handler.rs @@ -1,51 +1,45 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::handlers::committer::start_tx_checkpoint_commit_task; -use crate::handlers::tx_processor::IndexingPackageBuffer; -use crate::models::display::StoredDisplay; +use std::collections::{BTreeMap, HashMap}; +use std::sync::{Arc, Mutex}; + use async_trait::async_trait; +use diesel::r2d2::R2D2Connection; use itertools::Itertools; +use tap::tap::TapFallible; +use tokio::sync::watch; +use tokio_util::sync::CancellationToken; +use tracing::{info, warn}; + use move_core_types::annotated_value::{MoveStructLayout, MoveTypeLayout}; use move_core_types::language_storage::{StructTag, TypeTag}; use mysten_metrics::{get_metrics, spawn_monitored_task}; -use std::collections::{BTreeMap, HashMap}; -use std::sync::{Arc, Mutex}; +use sui_data_ingestion_core::Worker; +use sui_json_rpc_types::SuiMoveValue; use sui_package_resolver::{PackageStore, PackageStoreWithLruCache, Resolver}; use sui_rest_api::{CheckpointData, CheckpointTransaction}; -use sui_types::base_types::ObjectRef; +use sui_types::base_types::ObjectID; use sui_types::dynamic_field::DynamicFieldInfo; use sui_types::dynamic_field::DynamicFieldName; use sui_types::dynamic_field::DynamicFieldType; +use sui_types::effects::TransactionEffectsAPI; +use sui_types::event::SystemEpochInfoEvent; use sui_types::messages_checkpoint::{ CertifiedCheckpointSummary, CheckpointContents, CheckpointSequenceNumber, }; use sui_types::object::Object; -use tokio_util::sync::CancellationToken; - -use tokio::sync::watch; - -use diesel::r2d2::R2D2Connection; -use std::collections::hash_map::Entry; -use std::collections::HashSet; -use sui_data_ingestion_core::Worker; -use sui_json_rpc_types::SuiMoveValue; -use sui_types::base_types::SequenceNumber; -use sui_types::effects::{TransactionEffects, TransactionEffectsAPI}; -use sui_types::event::SystemEpochInfoEvent; use sui_types::object::Owner; -use sui_types::transaction::TransactionDataAPI; -use tap::tap::TapFallible; -use tracing::{info, warn}; - -use sui_types::base_types::ObjectID; use sui_types::sui_system_state::sui_system_state_summary::SuiSystemStateSummary; use sui_types::sui_system_state::{get_sui_system_state, SuiSystemStateTrait}; +use sui_types::transaction::TransactionDataAPI; +use crate::db::ConnectionPool; use crate::errors::IndexerError; +use crate::handlers::committer::start_tx_checkpoint_commit_task; +use crate::handlers::tx_processor::IndexingPackageBuffer; use crate::metrics::IndexerMetrics; - -use crate::db::ConnectionPool; +use crate::models::display::StoredDisplay; use crate::store::package_resolver::{IndexerStorePackageResolver, InterimPackageResolver}; use crate::store::{IndexerStore, PgIndexerStore}; use crate::types::{ @@ -537,71 +531,41 @@ where ) -> Result { let _timer = metrics.indexing_objects_latency.start_timer(); let checkpoint_seq = data.checkpoint_summary.sequence_number; - let deleted_objects = data - .transactions - .iter() - .flat_map(|tx| get_deleted_objects(&tx.effects)) - .collect::>(); - let deleted_object_ids = deleted_objects - .iter() - .map(|o| (o.0, o.1)) - .collect::>(); - let indexed_deleted_objects = deleted_objects + + let eventually_removed_object_refs_post_version = + data.eventually_removed_object_refs_post_version(); + let indexed_eventually_removed_objects = eventually_removed_object_refs_post_version .into_iter() - .map(|o| IndexedDeletedObject { - object_id: o.0, - object_version: o.1.value(), + .map(|obj_ref| IndexedDeletedObject { + object_id: obj_ref.0, + object_version: obj_ref.1.into(), checkpoint_sequence_number: checkpoint_seq, }) .collect(); - let (latest_objects, intermediate_versions) = get_latest_objects(data.output_objects()); - - let live_objects: Vec = data - .transactions - .iter() - .flat_map(|tx| { - let CheckpointTransaction { - transaction: tx, - effects: fx, - .. - } = tx; - fx.all_changed_objects() - .into_iter() - .filter_map(|(oref, _owner, _kind)| { - // We don't care about objects that are deleted or updated more than once - if intermediate_versions.contains(&(oref.0, oref.1)) - || deleted_object_ids.contains(&(oref.0, oref.1)) - { - return None; - } - let object = latest_objects.get(&(oref.0)).unwrap_or_else(|| { - panic!( - "object {:?} not found in CheckpointData (tx_digest: {})", - oref.0, - tx.digest() - ) - }); - assert_eq!(oref.1, object.version()); - Some(object.clone()) - }) - .collect::>() - }) - .collect(); - + let latest_live_output_objects = data.latest_live_output_objects(); + let latest_live_output_object_map = latest_live_output_objects + .clone() + .into_iter() + .map(|o| (o.id(), o.clone())) + .collect::>(); let move_struct_layout_map = - get_move_struct_layout_map(&live_objects, package_resolver).await?; - let changed_objects = live_objects + get_move_struct_layout_map(latest_live_output_objects.clone(), package_resolver) + .await?; + let changed_objects = latest_live_output_objects .into_iter() .map(|o| { - let df_info = - try_create_dynamic_field_info(&o, &move_struct_layout_map, &latest_objects); - df_info.map(|info| IndexedObject::from_object(checkpoint_seq, o, info)) + let df_info = try_create_dynamic_field_info( + o, + &move_struct_layout_map, + &latest_live_output_object_map, + ); + df_info.map(|info| IndexedObject::from_object(checkpoint_seq, o.clone(), info)) }) .collect::, _>>()?; Ok(TransactionObjectChangesToCommit { changed_objects, - deleted_objects: indexed_deleted_objects, + deleted_objects: indexed_eventually_removed_objects, }) } @@ -614,59 +578,42 @@ where let deleted_objects = data .transactions .iter() - .flat_map(|tx| get_deleted_objects(&tx.effects)) + .flat_map(|tx| tx.removed_object_refs_post_version()) .collect::>(); let indexed_deleted_objects: Vec = deleted_objects .into_iter() - .map(|o| IndexedDeletedObject { - object_id: o.0, - object_version: o.1.value(), + .map(|obj_ref| IndexedDeletedObject { + object_id: obj_ref.0, + object_version: obj_ref.1.into(), checkpoint_sequence_number: checkpoint_seq, }) .collect(); - let (latest_objects, _) = get_latest_objects(data.output_objects()); - let history_object_map = data - .output_objects() + let latest_live_output_objects = data.latest_live_output_objects(); + let latest_live_output_object_map = latest_live_output_objects + .clone() .into_iter() - .map(|o| ((o.id(), o.version()), o.clone())) + .map(|o| (o.id(), o.clone())) .collect::>(); - let history_objects: Vec = data + let output_objects = data .transactions .iter() - .flat_map(|tx| { - let CheckpointTransaction { - transaction: tx, - effects: fx, - .. - } = tx; - fx.all_changed_objects() - .into_iter() - .map(|(oref, _owner, _kind)| { - let history_object = history_object_map.get(&(oref.0, oref.1)).unwrap_or_else(|| { - panic!( - "object {:?} version {:?} not found in CheckpointData (tx_digest: {})", - oref.0, - oref.1, - tx.digest() - ) - }); - assert_eq!(oref.2, history_object.digest()); - history_object.clone() - }) - .collect::>() - }) - .collect(); - + .flat_map(|tx| &tx.output_objects) + .collect::>(); + // TODO(gegaowp): the current df_info implementation is not correct, + // but we have decided remove all df_* except df_kind. let move_struct_layout_map = - get_move_struct_layout_map(&history_objects, package_resolver).await?; - let changed_objects = history_objects + get_move_struct_layout_map(output_objects.clone(), package_resolver).await?; + let changed_objects = output_objects .into_iter() .map(|o| { - let df_info = - try_create_dynamic_field_info(&o, &move_struct_layout_map, &latest_objects); - df_info.map(|info| IndexedObject::from_object(checkpoint_seq, o, info)) + let df_info = try_create_dynamic_field_info( + o, + &move_struct_layout_map, + &latest_live_output_object_map, + ); + df_info.map(|info| IndexedObject::from_object(checkpoint_seq, o.clone(), info)) }) .collect::, _>>()?; @@ -685,8 +632,9 @@ where .iter() .flat_map(|data| { let checkpoint_sequence_number = data.checkpoint_summary.sequence_number; - data.output_objects() + data.transactions .iter() + .flat_map(|tx| &tx.output_objects) .filter_map(|o| { if let sui_types::object::Data::Package(p) = &o.data { Some(IndexedPackage { @@ -710,8 +658,9 @@ where .iter() .flat_map(|data| { let checkpoint_sequence_number = data.checkpoint_summary.sequence_number; - data.output_objects() + data.transactions .iter() + .flat_map(|tx| &tx.output_objects) .filter_map(|o| { if let sui_types::object::Data::Package(p) = &o.data { let indexed_pkg = IndexedPackage { @@ -719,7 +668,7 @@ where move_package: p.clone(), checkpoint_sequence_number, }; - Some((indexed_pkg, (**o).clone())) + Some((indexed_pkg, o.clone())) } else { None } @@ -741,11 +690,11 @@ where } async fn get_move_struct_layout_map( - objects: &[Object], + objects: Vec<&Object>, package_resolver: Arc>, ) -> Result, IndexerError> { let struct_tags = objects - .iter() + .into_iter() .filter_map(|o| { let move_object = o.data.try_as_move().cloned(); move_object.map(|move_object| { @@ -796,40 +745,6 @@ async fn get_move_struct_layout_map( Ok(move_struct_layout_map) } -pub fn get_deleted_objects(effects: &TransactionEffects) -> Vec { - let deleted = effects.deleted().into_iter(); - let wrapped = effects.wrapped().into_iter(); - let unwrapped_then_deleted = effects.unwrapped_then_deleted().into_iter(); - deleted - .chain(wrapped) - .chain(unwrapped_then_deleted) - .collect::>() -} - -pub fn get_latest_objects( - objects: Vec<&Object>, -) -> ( - HashMap, - HashSet<(ObjectID, SequenceNumber)>, -) { - let mut latest_objects = HashMap::new(); - let mut discarded_versions = HashSet::new(); - for object in objects { - match latest_objects.entry(object.id()) { - Entry::Vacant(e) => { - e.insert(object.clone()); - } - Entry::Occupied(mut e) => { - if object.version() > e.get().version() { - discarded_versions.insert((e.get().id(), e.get().version())); - e.insert(object.clone()); - } - } - } - } - (latest_objects, discarded_versions) -} - fn try_create_dynamic_field_info( o: &Object, struct_tag_to_move_struct_layout: &HashMap, diff --git a/crates/sui-indexer/src/handlers/tx_processor.rs b/crates/sui-indexer/src/handlers/tx_processor.rs index 04d96d6eafd04..2ee43f5880ead 100644 --- a/crates/sui-indexer/src/handlers/tx_processor.rs +++ b/crates/sui-indexer/src/handlers/tx_processor.rs @@ -30,7 +30,6 @@ use sui_types::messages_checkpoint::CheckpointSequenceNumber; use crate::errors::IndexerError; use crate::metrics::IndexerMetrics; - use crate::types::IndexedPackage; use crate::types::{IndexedObjectChange, IndexerResult}; @@ -289,9 +288,8 @@ pub(crate) struct EpochEndIndexingObjectStore<'a> { impl<'a> EpochEndIndexingObjectStore<'a> { pub fn new(data: &'a CheckpointData) -> Self { - // We only care about output objects for end-of-epoch indexing Self { - objects: data.output_objects(), + objects: data.latest_live_output_objects(), } } } diff --git a/crates/sui-types/src/full_checkpoint_content.rs b/crates/sui-types/src/full_checkpoint_content.rs index 02b7a897c3bed..4d0cb95217bbc 100644 --- a/crates/sui-types/src/full_checkpoint_content.rs +++ b/crates/sui-types/src/full_checkpoint_content.rs @@ -1,7 +1,12 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::effects::{IDOperation, ObjectIn, ObjectOut, TransactionEffects, TransactionEvents}; +use std::collections::BTreeMap; + +use crate::base_types::ObjectRef; +use crate::effects::{ + IDOperation, ObjectIn, ObjectOut, TransactionEffects, TransactionEffectsAPI, TransactionEvents, +}; use crate::messages_checkpoint::{CertifiedCheckpointSummary, CheckpointContents}; use crate::object::Object; use crate::storage::BackingPackageStore; @@ -18,11 +23,32 @@ pub struct CheckpointData { } impl CheckpointData { - pub fn output_objects(&self) -> Vec<&Object> { - self.transactions - .iter() - .flat_map(|tx| &tx.output_objects) - .collect() + // returns the latest versions of the output objects that still exist at the end of the checkpoint + pub fn latest_live_output_objects(&self) -> Vec<&Object> { + let mut latest_live_objects = BTreeMap::new(); + for tx in self.transactions.iter() { + for obj in tx.output_objects.iter() { + latest_live_objects.insert(obj.id(), obj); + } + for obj_ref in tx.removed_object_refs_post_version() { + latest_live_objects.remove(&(obj_ref.0)); + } + } + latest_live_objects.into_values().collect() + } + + // returns the object refs that are eventually deleted or wrapped in the current checkpoint + pub fn eventually_removed_object_refs_post_version(&self) -> Vec { + let mut eventually_removed_object_refs = BTreeMap::new(); + for tx in self.transactions.iter() { + for obj_ref in tx.removed_object_refs_post_version() { + eventually_removed_object_refs.insert(obj_ref.0, obj_ref); + } + for obj in tx.output_objects.iter() { + eventually_removed_object_refs.remove(&(obj.id())); + } + } + eventually_removed_object_refs.into_values().collect() } pub fn input_objects(&self) -> Vec<&Object> { @@ -51,19 +77,21 @@ pub struct CheckpointTransaction { pub events: Option, /// The state of all inputs to this transaction as they were prior to execution. pub input_objects: Vec, - /// The state of all output objects created or mutated by this transaction. + /// The state of all output objects created or mutated or unwrapped by this transaction. pub output_objects: Vec, } impl CheckpointTransaction { // provide an iterator over all deleted or wrapped objects in this transaction - pub fn removed_objects(&self) -> impl Iterator { + pub fn removed_objects_pre_version(&self) -> impl Iterator { // Iterator over id and versions for all deleted or wrapped objects match &self.effects { TransactionEffects::V1(v1) => Either::Left( // Effects v1 has delted and wrapped objects versions as the "new" version, not the // old one that was actually removed. So we need to take these and then look them // up in the `modified_at_versions`. + // No need to chain unwrapped_then_deleted because these objects must have been wrapped + // before the transaction, hence they will not be in modified_at_versions / input_objects. v1.deleted().iter().chain(v1.wrapped()).map(|(id, _, _)| { // lookup the old version for mutated objects let (_, old_version) = v1 @@ -108,6 +136,13 @@ impl CheckpointTransaction { }) } + pub fn removed_object_refs_post_version(&self) -> impl Iterator { + let deleted = self.effects.deleted().into_iter(); + let wrapped = self.effects.wrapped().into_iter(); + let unwrapped_then_deleted = self.effects.unwrapped_then_deleted().into_iter(); + deleted.chain(wrapped).chain(unwrapped_then_deleted) + } + pub fn changed_objects(&self) -> impl Iterator)> { // Iterator over ((ObjectId, new version), Option) match &self.effects { From 3170d58bcd8eb9706cd02d7ebf1bf78b8c337dba Mon Sep 17 00:00:00 2001 From: Todd Nowacki Date: Thu, 22 Aug 2024 15:41:06 -0700 Subject: [PATCH 216/232] [move] Add large enum test (#19075) ## Description - Fix compiler's sui-mode entry rules for enums - Add large enum tests for entry functions ## Test plan - Added tests --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- .../tests/entry_points/large_enum.exp | 22 ++++++++++ .../tests/entry_points/large_enum.move | 41 ++++++++++++++++++ .../tests/functions/large_enum.exp | 15 +++++++ .../tests/functions/large_enum.move | 36 ++++++++++++++++ .../move-compiler/src/shared/program_info.rs | 15 +++++++ .../move-compiler/src/sui_mode/typing.rs | 6 +-- .../typing/enum_in_struct_position.exp | 39 +++++++++++++++++ .../typing/enum_in_struct_position.move | 42 +++++++++++++++++++ 8 files changed, 213 insertions(+), 3 deletions(-) create mode 100644 crates/sui-adapter-transactional-tests/tests/entry_points/large_enum.exp create mode 100644 crates/sui-adapter-transactional-tests/tests/entry_points/large_enum.move create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/functions/large_enum.exp create mode 100644 external-crates/move/crates/move-compiler-transactional-tests/tests/functions/large_enum.move create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/move_2024/typing/enum_in_struct_position.exp create mode 100644 external-crates/move/crates/move-compiler/tests/sui_mode/move_2024/typing/enum_in_struct_position.move diff --git a/crates/sui-adapter-transactional-tests/tests/entry_points/large_enum.exp b/crates/sui-adapter-transactional-tests/tests/entry_points/large_enum.exp new file mode 100644 index 0000000000000..4c3435ef46f66 --- /dev/null +++ b/crates/sui-adapter-transactional-tests/tests/entry_points/large_enum.exp @@ -0,0 +1,22 @@ +processed 4 tasks + +init: +A: object(0,0) + +task 1, lines 8-35: +//# publish +created: object(1,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 6452400, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, lines 37-38: +//# programmable --sender A +//> test::m::x1() +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 3, lines 40-41: +//# programmable --sender A +//> test::m::x3() +Error: Transaction Effects Status: Move Bytecode Verification Error. Please run the Bytecode Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: VMVerificationOrDeserializationError, source: Some(VMError { major_status: TOO_MANY_TYPE_NODES, sub_status: None, message: None, exec_state: None, location: Undefined, indices: [], offsets: [] }), command: Some(0) } } diff --git a/crates/sui-adapter-transactional-tests/tests/entry_points/large_enum.move b/crates/sui-adapter-transactional-tests/tests/entry_points/large_enum.move new file mode 100644 index 0000000000000..da3a8468d81b6 --- /dev/null +++ b/crates/sui-adapter-transactional-tests/tests/entry_points/large_enum.move @@ -0,0 +1,41 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// tests error after serializing a large enum return value + +//# init --addresses test=0x0 --accounts A + +//# publish + +module test::m { + +public enum X1 has drop { + Big1(u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8), +} + +public enum X2 has drop { + V1(X1, X1, X1), + V2(X1, X1, X1), + V3(X1, X1, X1), +} + +public enum X3 has drop { + X2(X2, X2, X2), + U64(u64), +} + +entry fun x1(): X1 { + X1::Big1(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) +} + +entry fun x3(): X3 { + X3::U64(0) +} + +} + +//# programmable --sender A +//> test::m::x1() + +//# programmable --sender A +//> test::m::x3() diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/functions/large_enum.exp b/external-crates/move/crates/move-compiler-transactional-tests/tests/functions/large_enum.exp new file mode 100644 index 0000000000000..f32b667741b9f --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/functions/large_enum.exp @@ -0,0 +1,15 @@ +processed 4 tasks + +task 2, line 34: +//# run 0x42::m::x1 +return values: |0|{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + +task 3, line 36: +//# run 0x42::m::x3 +Error: Function execution failed with VMError: { + major_status: VERIFICATION_ERROR, + sub_status: None, + location: undefined, + indices: [], + offsets: [], +} diff --git a/external-crates/move/crates/move-compiler-transactional-tests/tests/functions/large_enum.move b/external-crates/move/crates/move-compiler-transactional-tests/tests/functions/large_enum.move new file mode 100644 index 0000000000000..97813d424a567 --- /dev/null +++ b/external-crates/move/crates/move-compiler-transactional-tests/tests/functions/large_enum.move @@ -0,0 +1,36 @@ +// tests error after serializing a large enum return value + +//# init --edition 2024.alpha + +//# publish + +module 0x42::m { + +public enum X1 { + Big(u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8), +} + +public enum X2 { + V1(X1, X1, X1), + V2(X1, X1, X1), + V3(X1, X1, X1), +} + +public enum X3 { + X2(X2, X2, X2), + U64(u64), +} + +entry fun x1(): X1 { + X1::Big(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) +} + +entry fun x3(): X3 { + X3::U64(0) +} + +} + +//# run 0x42::m::x1 + +//# run 0x42::m::x3 diff --git a/external-crates/move/crates/move-compiler/src/shared/program_info.rs b/external-crates/move/crates/move-compiler/src/shared/program_info.rs index 250bdeeb01213..2ceadbb461cce 100644 --- a/external-crates/move/crates/move-compiler/src/shared/program_info.rs +++ b/external-crates/move/crates/move-compiler/src/shared/program_info.rs @@ -219,6 +219,21 @@ impl ProgramInfo { _ => panic!("ICE should have failed in naming"), } } + + pub fn datatype_declared_loc(&self, m: &ModuleIdent, n: &DatatypeName) -> Loc { + match self.datatype_kind(m, n) { + DatatypeKind::Struct => self.struct_declared_loc_(m, &n.0.value), + DatatypeKind::Enum => self.enum_declared_loc_(m, &n.0.value), + } + } + + pub fn datatype_declared_abilities(&self, m: &ModuleIdent, n: &DatatypeName) -> &AbilitySet { + match self.datatype_kind(m, n) { + DatatypeKind::Struct => self.struct_declared_abilities(m, n), + DatatypeKind::Enum => self.enum_declared_abilities(m, n), + } + } + pub fn struct_definition(&self, m: &ModuleIdent, n: &DatatypeName) -> &StructDefinition { self.struct_definition_opt(m, n) .expect("ICE should have failed in naming") diff --git a/external-crates/move/crates/move-compiler/src/sui_mode/typing.rs b/external-crates/move/crates/move-compiler/src/sui_mode/typing.rs index 31238adb5258d..aba3fc400fe56 100644 --- a/external-crates/move/crates/move-compiler/src/sui_mode/typing.rs +++ b/external-crates/move/crates/move-compiler/src/sui_mode/typing.rs @@ -868,8 +868,8 @@ fn entry_return( let (declared_loc_opt, declared_abilities) = match tn_ { TypeName_::Multiple(_) => (None, AbilitySet::collection(*tloc)), TypeName_::ModuleType(m, n) => ( - Some(context.info.struct_declared_loc(m, n)), - context.info.struct_declared_abilities(m, n).clone(), + Some(context.info.datatype_declared_loc(m, n)), + context.info.datatype_declared_abilities(m, n).clone(), ), TypeName_::Builtin(b) => (None, b.value.declared_abilities(b.loc)), }; @@ -1064,7 +1064,7 @@ fn check_private_transfer(context: &mut Context, loc: Loc, mcall: &ModuleCall) { let store_loc = if let Some((first_ty_module, first_ty_name)) = &first_ty_tn { let abilities = context .info - .struct_declared_abilities(first_ty_module, first_ty_name); + .datatype_declared_abilities(first_ty_module, first_ty_name); abilities.ability_loc_(Ability_::Store).unwrap() } else { first_ty diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/move_2024/typing/enum_in_struct_position.exp b/external-crates/move/crates/move-compiler/tests/sui_mode/move_2024/typing/enum_in_struct_position.exp new file mode 100644 index 0000000000000..5f388c9fe77c9 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/move_2024/typing/enum_in_struct_position.exp @@ -0,0 +1,39 @@ +error[Sui E02007]: invalid object declaration + ┌─ tests/sui_mode/move_2024/typing/enum_in_struct_position.move:11:13 + │ +11 │ public enum Obj has key, store { + │ ^^^ --- Enums cannot have the 'key' ability. + │ │ + │ Invalid object 'Obj' + +error[Sui E02002]: invalid 'entry' function signature + ┌─ tests/sui_mode/move_2024/typing/enum_in_struct_position.move:18:1 + │ + 3 │ public enum E { + │ - To satisfy the constraint, the 'drop' ability would need to be added here + · +18 │ entry fun ret(): E { + │ ^^^^^ - The type 'a::m::E' does not have the ability 'drop' + │ │ + │ Invalid return type for entry function 'ret' + +error[Sui E02002]: invalid 'entry' function signature + ┌─ tests/sui_mode/move_2024/typing/enum_in_struct_position.move:22:14 + │ +22 │ entry fun x3(_: E) { + │ ----- ^ - 'entry' parameters must be primitives (by-value), vectors of primitives, objects (by-reference or by-value), vectors of objects, or 'Receiving' arguments (by-reference or by-value) + │ │ │ + │ │ Invalid 'entry' parameter type for parameter '_' + │ 'x3' was declared 'entry' here + +error[Sui E02009]: invalid private transfer call + ┌─ tests/sui_mode/move_2024/typing/enum_in_struct_position.move:30:5 + │ +11 │ public enum Obj has key, store { + │ ----- The object has 'store' so 'sui::transfer::public_transfer' can be called instead + · +29 │ public fun transfer(o: a::m::Obj) { + │ --------- The type 'a::m::Obj' is not declared in the current module +30 │ transfer::transfer(o, @0) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid private transfer. The function 'sui::transfer::transfer' is restricted to being called in the object's module, 'a::m' + diff --git a/external-crates/move/crates/move-compiler/tests/sui_mode/move_2024/typing/enum_in_struct_position.move b/external-crates/move/crates/move-compiler/tests/sui_mode/move_2024/typing/enum_in_struct_position.move new file mode 100644 index 0000000000000..3a38fc40f633e --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/sui_mode/move_2024/typing/enum_in_struct_position.move @@ -0,0 +1,42 @@ +module a::m { + +public enum E { + V() +} + +public enum M has drop { + V() +} + +public enum Obj has key, store { + V() +} + +fun init(_: M, _: &mut TxContext) { +} + +entry fun ret(): E { + E::V() +} + +entry fun x3(_: E) { + abort 0 +} + +} + +module a::n { +public fun transfer(o: a::m::Obj) { + transfer::transfer(o, @0) +} +} + +module sui::transfer { +public fun transfer(_: T, _: address) { + abort 0 +} +} + +module sui::tx_context{ +public struct TxContext has drop {} +} From c4e4be46815cdef92eda2c138bead8a2d3580b49 Mon Sep 17 00:00:00 2001 From: Lu Zhang <8418040+longbowlu@users.noreply.github.com> Date: Thu, 22 Aug 2024 16:48:54 -0700 Subject: [PATCH 217/232] [bridge] extend add coin e2e test to cover evm side, and a bug fix (#18861) ## Description In this PR we extend e2e test `test_add_new_coins_on_sui` to `test_add_new_coins_on_sui_and_eth`, also testing 1. add new coin on Eth 2. bridge node capture the new token event without restarting Also fixed a bug where bridge node did not listen to move events from treasury.move and limiter.move (caught by the updated e2e tests, apparently) ## Test plan e2e tests --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- bridge/evm/script/deploy_bridge.s.sol | 2 + bridge/evm/test/mocks/MockTokens.sol | 18 + crates/sui-bridge/abi/erc20.json | 358 ++++++++++++++++++ crates/sui-bridge/src/abi.rs | 6 + crates/sui-bridge/src/e2e_tests/basic.rs | 206 ++++++++-- crates/sui-bridge/src/e2e_tests/complex.rs | 6 +- crates/sui-bridge/src/e2e_tests/test_utils.rs | 64 +++- crates/sui-bridge/src/node.rs | 25 +- crates/sui-types/src/bridge.rs | 2 + 9 files changed, 641 insertions(+), 46 deletions(-) create mode 100644 crates/sui-bridge/abi/erc20.json diff --git a/bridge/evm/script/deploy_bridge.s.sol b/bridge/evm/script/deploy_bridge.s.sol index 4df43f5384a52..dfb5c6609c15a 100644 --- a/bridge/evm/script/deploy_bridge.s.sol +++ b/bridge/evm/script/deploy_bridge.s.sol @@ -44,6 +44,8 @@ contract DeployBridge is Script { MockWBTC wBTC = new MockWBTC(); MockUSDC USDC = new MockUSDC(); MockUSDT USDT = new MockUSDT(); + MockKA KA = new MockKA(); + console.log("[Deployed] KA:", address(KA)); // update deployConfig with mock addresses deployConfig.supportedTokens = new address[](5); diff --git a/bridge/evm/test/mocks/MockTokens.sol b/bridge/evm/test/mocks/MockTokens.sol index 3a1bfbd059f39..05f463b36781e 100644 --- a/bridge/evm/test/mocks/MockTokens.sol +++ b/bridge/evm/test/mocks/MockTokens.sol @@ -57,6 +57,24 @@ contract MockUSDT is ERC20 { function testSkip() public {} } +contract MockKA is ERC20 { + constructor() ERC20("Ka Coin", "KA") {} + + function mint(address to, uint256 amount) public virtual { + _mint(to, amount); + } + + function burn(address form, uint256 amount) public virtual { + _burn(form, amount); + } + + function decimals() public view virtual override returns (uint8) { + return 9; + } + + function testSkip() public {} +} + contract WETH { string public name = "Wrapped Ether"; string public symbol = "WETH"; diff --git a/crates/sui-bridge/abi/erc20.json b/crates/sui-bridge/abi/erc20.json new file mode 100644 index 0000000000000..5829d4b2816e5 --- /dev/null +++ b/crates/sui-bridge/abi/erc20.json @@ -0,0 +1,358 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "allowance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC20InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC20InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC20InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "ERC20InvalidSpender", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "form", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "testSkip", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/crates/sui-bridge/src/abi.rs b/crates/sui-bridge/src/abi.rs index e60d119877936..d1b844402be70 100644 --- a/crates/sui-bridge/src/abi.rs +++ b/crates/sui-bridge/src/abi.rs @@ -88,6 +88,12 @@ gen_eth_events!( gen_eth_events!(EthBridgeVault, "abi/bridge_vault.json"); +abigen!( + EthERC20, + "abi/erc20.json", + event_derives(serde::Deserialize, serde::Serialize) +); + impl EthBridgeEvent { pub fn try_into_bridge_action( self, diff --git a/crates/sui-bridge/src/e2e_tests/basic.rs b/crates/sui-bridge/src/e2e_tests/basic.rs index e875ef9efd72d..fd730708ae559 100644 --- a/crates/sui-bridge/src/e2e_tests/basic.rs +++ b/crates/sui-bridge/src/e2e_tests/basic.rs @@ -1,16 +1,19 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::abi::{eth_sui_bridge, EthBridgeEvent, EthSuiBridge}; +use crate::abi::{eth_sui_bridge, EthBridgeEvent, EthERC20, EthSuiBridge}; use crate::client::bridge_authority_aggregator::BridgeAuthorityAggregator; use crate::e2e_tests::test_utils::BridgeTestCluster; -use crate::e2e_tests::test_utils::{get_signatures, BridgeTestClusterBuilder}; +use crate::e2e_tests::test_utils::{ + get_signatures, send_eth_tx_and_get_tx_receipt, BridgeTestClusterBuilder, +}; +use crate::eth_transaction_builder::build_eth_transaction; use crate::events::{ SuiBridgeEvent, SuiToEthTokenBridgeV1, TokenTransferApproved, TokenTransferClaimed, }; use crate::sui_client::SuiBridgeClient; use crate::sui_transaction_builder::build_add_tokens_on_sui_transaction; -use crate::types::{BridgeAction, BridgeActionStatus, SuiToEthBridgeAction}; +use crate::types::{AddTokensOnEvmAction, BridgeAction, BridgeActionStatus, SuiToEthBridgeAction}; use crate::utils::publish_and_register_coins_return_add_coins_on_sui_action; use crate::utils::EthSigner; use eth_sui_bridge::EthSuiBridgeEvents; @@ -58,7 +61,7 @@ async fn test_bridge_from_eth_to_sui_to_eth() { let amount = 42; let sui_amount = amount * 100_000_000; - initiate_bridge_eth_to_sui(&bridge_test_cluster, amount, sui_amount, TOKEN_ID_ETH, 0) + initiate_bridge_eth_to_sui(&bridge_test_cluster, amount, 0) .await .unwrap(); let events = bridge_test_cluster @@ -141,8 +144,9 @@ async fn test_bridge_from_eth_to_sui_to_eth() { bridge_test_cluster.contracts().sui_bridge, eth_signer.clone().into(), ); - let tx = eth_sui_bridge.transfer_bridged_tokens_with_signatures(signatures, message); - let _eth_claim_tx_receipt = tx.send().await.unwrap().await.unwrap().unwrap(); + let call = eth_sui_bridge.transfer_bridged_tokens_with_signatures(signatures, message); + let eth_claim_tx_receipt = send_eth_tx_and_get_tx_receipt(call).await; + assert_eq!(eth_claim_tx_receipt.status.unwrap().as_u64(), 1); info!("Sui to Eth bridge transfer claimed"); // Assert eth_address_1 has received ETH assert_eq!( @@ -151,8 +155,10 @@ async fn test_bridge_from_eth_to_sui_to_eth() { ); } +// Test add new coins on both Sui and Eth +// Also test bridge ndoe handling `NewTokenEvent`` #[tokio::test(flavor = "multi_thread", worker_threads = 8)] -async fn test_add_new_coins_on_sui() { +async fn test_add_new_coins_on_sui_and_eth() { telemetry_subscribers::init_for_testing(); let mut bridge_test_cluster = BridgeTestClusterBuilder::new() .with_eth_env(true) @@ -163,12 +169,13 @@ async fn test_add_new_coins_on_sui() { let bridge_arg = bridge_test_cluster.get_mut_bridge_arg().await.unwrap(); - // Register tokens - let token_id = 42; + // Register tokens on Sui + let token_id = 5; + let token_sui_decimal = 9; // this needs to match ka.move let token_price = 10000; let sender = bridge_test_cluster.sui_user_address(); info!("Published new token"); - let action = publish_and_register_coins_return_add_coins_on_sui_action( + let sui_action = publish_and_register_coins_return_add_coins_on_sui_action( bridge_test_cluster.wallet(), bridge_arg, vec![Path::new("../../bridge/move/tokens/mock/ka").into()], @@ -177,13 +184,23 @@ async fn test_add_new_coins_on_sui() { 1, // seq num ) .await; + let new_token_erc_address = bridge_test_cluster.contracts().ka; + let eth_action = BridgeAction::AddTokensOnEvmAction(AddTokensOnEvmAction { + nonce: 0, + chain_id: BridgeChainId::EthCustom, + native: true, + token_ids: vec![token_id], + token_addresses: vec![new_token_erc_address], + token_sui_decimals: vec![token_sui_decimal], + token_prices: vec![token_price], + }); info!("Starting bridge cluster"); bridge_test_cluster.set_approved_governance_actions_for_next_start(vec![ - vec![action.clone()], - vec![action.clone()], - vec![], + vec![sui_action.clone(), eth_action.clone()], + vec![sui_action.clone()], + vec![eth_action.clone()], ]); bridge_test_cluster.start_bridge_cluster().await; bridge_test_cluster @@ -199,10 +216,14 @@ async fn test_add_new_coins_on_sui() { .expect("Failed to get bridge committee"), ); let agg = BridgeAuthorityAggregator::new(bridge_committee); - let certified_action = agg - .request_committee_signatures(action) + let certified_sui_action = agg + .request_committee_signatures(sui_action) + .await + .expect("Failed to request committee signatures for AddTokensOnSuiAction"); + let certified_eth_action = agg + .request_committee_signatures(eth_action.clone()) .await - .expect("Failed to request committee signatures"); + .expect("Failed to request committee signatures for AddTokensOnEvmAction"); let tx = build_add_tokens_on_sui_transaction( sender, @@ -212,18 +233,26 @@ async fn test_add_new_coins_on_sui() { .await .unwrap() .unwrap(), - certified_action, + certified_sui_action, bridge_arg, 1000, ) .unwrap(); let response = bridge_test_cluster.sign_and_execute_transaction(&tx).await; - assert_eq!( - response.effects.unwrap().status(), - &SuiExecutionStatus::Success - ); - info!("Approved new token"); + let effects = response.effects.unwrap(); + assert_eq!(effects.status(), &SuiExecutionStatus::Success); + assert!(response.events.unwrap().data.iter().any(|e| { + let sui_bridge_event = SuiBridgeEvent::try_from_sui_event(e).unwrap().unwrap(); + match sui_bridge_event { + SuiBridgeEvent::NewTokenEvent(e) => { + assert_eq!(e.token_id, token_id); + true + } + _ => false, + } + })); + info!("Approved new token on Sui"); // Assert new token is correctly added let treasury_summary = bridge_test_cluster @@ -251,6 +280,34 @@ async fn test_add_new_coins_on_sui() { native_token: false, } ); + + // Add new token on EVM + let config_address = bridge_test_cluster.contracts().bridge_config; + let eth_signer = bridge_test_cluster.get_eth_signer().await; + let eth_call = build_eth_transaction(config_address, eth_signer, certified_eth_action) + .await + .unwrap(); + let eth_receipt = send_eth_tx_and_get_tx_receipt(eth_call).await; + assert_eq!(eth_receipt.status.unwrap().as_u64(), 1); + + // Verify new tokens are added on EVM + let (address, dp, price) = bridge_test_cluster + .eth_env() + .get_supported_token(token_id) + .await; + assert_eq!(address, new_token_erc_address); + assert_eq!(dp, 9); + assert_eq!(price, token_price); + + initiate_bridge_erc20_to_sui( + &bridge_test_cluster, + 100, + new_token_erc_address, + token_id, + 0, + ) + .await + .unwrap(); } pub(crate) async fn deposit_native_eth_to_sol_contract( @@ -313,14 +370,100 @@ async fn deposit_eth_to_sui_package( wallet_context.execute_transaction_may_fail(tx).await } +pub async fn initiate_bridge_erc20_to_sui( + bridge_test_cluster: &BridgeTestCluster, + amount_u64: u64, + token_address: EthAddress, + token_id: u8, + nonce: u64, +) -> Result<(), anyhow::Error> { + let (eth_signer, eth_address) = bridge_test_cluster + .get_eth_signer_and_address() + .await + .unwrap(); + + // First, mint ERC20 tokens to the signer + let contract = EthERC20::new(token_address, eth_signer.clone().into()); + let decimal = contract.decimals().await? as usize; + let amount = U256::from(amount_u64) * U256::exp10(decimal); + let sui_amount = amount.as_u64(); + let mint_call = contract.mint(eth_address, amount); + let mint_tx_receipt = send_eth_tx_and_get_tx_receipt(mint_call).await; + assert_eq!(mint_tx_receipt.status.unwrap().as_u64(), 1); + + // Second, set allowance + let allowance_call = contract.approve(bridge_test_cluster.contracts().sui_bridge, amount); + let allowance_tx_receipt = send_eth_tx_and_get_tx_receipt(allowance_call).await; + assert_eq!(allowance_tx_receipt.status.unwrap().as_u64(), 1); + + // Third, deposit to bridge + let sui_recipient_address = bridge_test_cluster.sui_user_address(); + let sui_chain_id = bridge_test_cluster.sui_chain_id(); + let eth_chain_id = bridge_test_cluster.eth_chain_id(); + + info!( + "Depositing ERC20 (token id:{}, token_address: {}) to Solidity contract", + token_id, token_address + ); + let contract = EthSuiBridge::new( + bridge_test_cluster.contracts().sui_bridge, + eth_signer.clone().into(), + ); + let deposit_call = contract.bridge_erc20( + token_id, + amount, + sui_recipient_address.to_vec().into(), + sui_chain_id as u8, + ); + let tx_receipt = send_eth_tx_and_get_tx_receipt(deposit_call).await; + let eth_bridge_event = tx_receipt + .logs + .iter() + .find_map(EthBridgeEvent::try_from_log) + .unwrap(); + let EthBridgeEvent::EthSuiBridgeEvents(EthSuiBridgeEvents::TokensDepositedFilter( + eth_bridge_event, + )) = eth_bridge_event + else { + unreachable!(); + }; + // assert eth log matches + assert_eq!(eth_bridge_event.source_chain_id, eth_chain_id as u8); + assert_eq!(eth_bridge_event.nonce, nonce); + assert_eq!(eth_bridge_event.destination_chain_id, sui_chain_id as u8); + assert_eq!(eth_bridge_event.token_id, token_id); + assert_eq!(eth_bridge_event.sui_adjusted_amount, sui_amount); + assert_eq!(eth_bridge_event.sender_address, eth_address); + assert_eq!( + eth_bridge_event.recipient_address, + sui_recipient_address.to_vec() + ); + info!( + "Deposited ERC20 (token id:{}, token_address: {}) to Solidity contract", + token_id, token_address + ); + + wait_for_transfer_action_status( + bridge_test_cluster.bridge_client(), + eth_chain_id, + nonce, + BridgeActionStatus::Claimed, + ) + .await + .tap_ok(|_| { + info!( + nonce, + token_id, amount_u64, "Eth to Sui bridge transfer claimed" + ); + }) +} + pub async fn initiate_bridge_eth_to_sui( bridge_test_cluster: &BridgeTestCluster, amount: u64, - sui_amount: u64, - token_id: u8, nonce: u64, ) -> Result<(), anyhow::Error> { - info!("Depositing Eth to Solidity contract"); + info!("Depositing native Ether to Solidity contract, nonce: {nonce}, amount: {amount}"); let (eth_signer, eth_address) = bridge_test_cluster .get_eth_signer_and_address() .await @@ -329,6 +472,9 @@ pub async fn initiate_bridge_eth_to_sui( let sui_address = bridge_test_cluster.sui_user_address(); let sui_chain_id = bridge_test_cluster.sui_chain_id(); let eth_chain_id = bridge_test_cluster.eth_chain_id(); + let token_id = TOKEN_ID_ETH; + + let sui_amount = (U256::from(amount) * U256::exp10(8)).as_u64(); // DP for Ether on Sui let eth_tx = deposit_native_eth_to_sol_contract( ð_signer, @@ -338,8 +484,7 @@ pub async fn initiate_bridge_eth_to_sui( amount, ) .await; - let pending_tx = eth_tx.send().await.unwrap(); - let tx_receipt = pending_tx.await.unwrap().unwrap(); + let tx_receipt = send_eth_tx_and_get_tx_receipt(eth_tx).await; let eth_bridge_event = tx_receipt .logs .iter() @@ -474,9 +619,16 @@ async fn wait_for_transfer_action_status( status, chain_id as u8 ); loop { + let timer = std::time::Instant::now(); let res = sui_bridge_client .get_token_transfer_action_onchain_status_until_success(chain_id as u8, nonce) .await; + info!( + "get_token_transfer_action_onchain_status_until_success took {:?}, status: {:?}", + timer.elapsed(), + res + ); + if res == status { info!( "detected on chain status {:?}. chain: {:?}, nonce: {nonce}", diff --git a/crates/sui-bridge/src/e2e_tests/complex.rs b/crates/sui-bridge/src/e2e_tests/complex.rs index bc20619d511af..d822074146ae6 100644 --- a/crates/sui-bridge/src/e2e_tests/complex.rs +++ b/crates/sui-bridge/src/e2e_tests/complex.rs @@ -54,7 +54,7 @@ async fn test_sui_bridge_paused() { assert!(!bridge_client.get_bridge_summary().await.unwrap().is_frozen); // try bridge from eth and verify it works on sui - initiate_bridge_eth_to_sui(&bridge_test_cluster, 10, 10 * 100_000_000, TOKEN_ID_ETH, 0) + initiate_bridge_eth_to_sui(&bridge_test_cluster, 10, 0) .await .unwrap(); // verify Eth was transferred to Sui address @@ -107,9 +107,7 @@ async fn test_sui_bridge_paused() { assert!(bridge_client.get_bridge_summary().await.unwrap().is_frozen); // Transfer from eth to sui should fail on Sui - let eth_to_sui_bridge_action = - initiate_bridge_eth_to_sui(&bridge_test_cluster, 10, 10 * 100_000_000, TOKEN_ID_ETH, 1) - .await; + let eth_to_sui_bridge_action = initiate_bridge_eth_to_sui(&bridge_test_cluster, 10, 1).await; assert!(eth_to_sui_bridge_action.is_err()); // message should not be recorded on Sui when the bridge is paused let res = bridge_test_cluster diff --git a/crates/sui-bridge/src/e2e_tests/test_utils.rs b/crates/sui-bridge/src/e2e_tests/test_utils.rs index f75969f6a0b34..67c3e61b3e295 100644 --- a/crates/sui-bridge/src/e2e_tests/test_utils.rs +++ b/crates/sui-bridge/src/e2e_tests/test_utils.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::abi::EthBridgeCommittee; +use crate::abi::EthBridgeConfig; use crate::crypto::BridgeAuthorityKeyPair; use crate::crypto::BridgeAuthorityPublicKeyBytes; use crate::events::*; @@ -24,6 +25,7 @@ use std::path::Path; use std::path::PathBuf; use std::process::Command; use std::str::FromStr; +use std::sync::Arc; use sui_json_rpc_types::SuiEvent; use sui_json_rpc_types::SuiTransactionBlockResponse; use sui_json_rpc_types::SuiTransactionBlockResponseOptions; @@ -67,6 +69,7 @@ const BTC_NAME: &str = "BTC"; const ETH_NAME: &str = "ETH"; const USDC_NAME: &str = "USDC"; const USDT_NAME: &str = "USDT"; +const KA_NAME: &str = "KA"; pub const TEST_PK: &str = "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356"; @@ -238,6 +241,11 @@ impl BridgeTestCluster { Ok((eth_signer, eth_address)) } + pub async fn get_eth_signer(&self) -> EthSigner { + let (eth_signer, _) = self.get_eth_signer_and_private_key().await.unwrap(); + eth_signer + } + pub fn bridge_client(&self) -> &SuiBridgeClient { &self.bridge_client } @@ -258,6 +266,10 @@ impl BridgeTestCluster { self.eth_chain_id } + pub(crate) fn eth_env(&self) -> &EthBridgeEnvironment { + &self.eth_environment + } + pub fn contracts(&self) -> &DeployedSolContracts { self.eth_environment.contracts() } @@ -437,6 +449,7 @@ pub struct DeployedSolContracts { pub eth: EthAddress, pub usdc: EthAddress, pub usdt: EthAddress, + pub ka: EthAddress, } impl DeployedSolContracts { @@ -590,15 +603,16 @@ pub(crate) async fn deploy_sol_contract( } let contracts = DeployedSolContracts { - sui_bridge: *deployed_contracts.get(SUI_BRIDGE_NAME).unwrap(), - bridge_committee: *deployed_contracts.get(BRIDGE_COMMITTEE_NAME).unwrap(), - bridge_config: *deployed_contracts.get(BRIDGE_CONFIG_NAME).unwrap(), - bridge_limiter: *deployed_contracts.get(BRIDGE_LIMITER_NAME).unwrap(), - bridge_vault: *deployed_contracts.get(BRIDGE_VAULT_NAME).unwrap(), - btc: *deployed_contracts.get(BTC_NAME).unwrap(), - eth: *deployed_contracts.get(ETH_NAME).unwrap(), - usdc: *deployed_contracts.get(USDC_NAME).unwrap(), - usdt: *deployed_contracts.get(USDT_NAME).unwrap(), + sui_bridge: deployed_contracts.remove(SUI_BRIDGE_NAME).unwrap(), + bridge_committee: deployed_contracts.remove(BRIDGE_COMMITTEE_NAME).unwrap(), + bridge_config: deployed_contracts.remove(BRIDGE_CONFIG_NAME).unwrap(), + bridge_limiter: deployed_contracts.remove(BRIDGE_LIMITER_NAME).unwrap(), + bridge_vault: deployed_contracts.remove(BRIDGE_VAULT_NAME).unwrap(), + btc: deployed_contracts.remove(BTC_NAME).unwrap(), + eth: deployed_contracts.remove(ETH_NAME).unwrap(), + usdc: deployed_contracts.remove(USDC_NAME).unwrap(), + usdt: deployed_contracts.remove(USDT_NAME).unwrap(), + ka: deployed_contracts.remove(KA_NAME).unwrap(), }; let eth_bridge_committee = EthBridgeCommittee::new(contracts.bridge_committee, eth_signer.clone().into()); @@ -628,7 +642,7 @@ pub(crate) async fn deploy_sol_contract( } #[derive(Debug)] -pub(crate) struct EthBridgeEnvironment { +pub struct EthBridgeEnvironment { pub rpc_url: String, process: Child, contracts: Option, @@ -665,6 +679,25 @@ impl EthBridgeEnvironment { pub(crate) fn contracts(&self) -> &DeployedSolContracts { self.contracts.as_ref().unwrap() } + + pub(crate) fn get_bridge_config( + &self, + ) -> EthBridgeConfig> { + let provider = Arc::new( + ethers::prelude::Provider::::try_from(&self.rpc_url) + .unwrap() + .interval(std::time::Duration::from_millis(2000)), + ); + EthBridgeConfig::new(self.contracts().bridge_config, provider.clone()) + } + + pub(crate) async fn get_supported_token(&self, token_id: u8) -> (EthAddress, u8, u64) { + let config = self.get_bridge_config(); + let token_address = config.token_address_of(token_id).call().await.unwrap(); + let token_sui_decimal = config.token_sui_decimal_of(token_id).call().await.unwrap(); + let token_price = config.token_price_of(token_id).call().await.unwrap(); + (token_address, token_sui_decimal, token_price) + } } impl Drop for EthBridgeEnvironment { @@ -766,6 +799,17 @@ pub(crate) async fn get_signatures( .collect() } +pub(crate) async fn send_eth_tx_and_get_tx_receipt( + call: FunctionCall, +) -> TransactionReceipt +where + M: Middleware, + B: std::borrow::Borrow, + D: ethers::abi::Detokenize, +{ + call.send().await.unwrap().await.unwrap().unwrap() +} + /// A simple struct to create a temporary directory that /// will be removed when it goes out of scope. struct TempDir { diff --git a/crates/sui-bridge/src/node.rs b/crates/sui-bridge/src/node.rs index 031152ce2a776..97a7596cbcb74 100644 --- a/crates/sui-bridge/src/node.rs +++ b/crates/sui-bridge/src/node.rs @@ -24,7 +24,10 @@ use std::{ time::Duration, }; use sui_types::{ - bridge::{BRIDGE_COMMITTEE_MODULE_NAME, BRIDGE_MODULE_NAME}, + bridge::{ + BRIDGE_COMMITTEE_MODULE_NAME, BRIDGE_LIMITER_MODULE_NAME, BRIDGE_MODULE_NAME, + BRIDGE_TREASURY_MODULE_NAME, + }, event::EventID, Identifier, }; @@ -166,6 +169,8 @@ fn get_sui_modules_to_watch( let sui_bridge_modules = vec![ BRIDGE_MODULE_NAME.to_owned(), BRIDGE_COMMITTEE_MODULE_NAME.to_owned(), + BRIDGE_TREASURY_MODULE_NAME.to_owned(), + BRIDGE_LIMITER_MODULE_NAME.to_owned(), ]; if let Some(cursor) = sui_bridge_module_last_processed_event_id_override { info!("Overriding cursor for sui bridge modules to {:?}", cursor); @@ -315,13 +320,17 @@ mod tests { let store = BridgeOrchestratorTables::new(temp_dir.path()); let bridge_module = BRIDGE_MODULE_NAME.to_owned(); let committee_module = BRIDGE_COMMITTEE_MODULE_NAME.to_owned(); + let treasury_module = BRIDGE_TREASURY_MODULE_NAME.to_owned(); + let limiter_module = BRIDGE_LIMITER_MODULE_NAME.to_owned(); // No override, no stored watermark, use None let sui_modules_to_watch = get_sui_modules_to_watch(&store, None); assert_eq!( sui_modules_to_watch, vec![ (bridge_module.clone(), None), - (committee_module.clone(), None) + (committee_module.clone(), None), + (treasury_module.clone(), None), + (limiter_module.clone(), None) ] .into_iter() .collect::>() @@ -337,7 +346,9 @@ mod tests { sui_modules_to_watch, vec![ (bridge_module.clone(), Some(override_cursor)), - (committee_module.clone(), Some(override_cursor)) + (committee_module.clone(), Some(override_cursor)), + (treasury_module.clone(), Some(override_cursor)), + (limiter_module.clone(), Some(override_cursor)) ] .into_iter() .collect::>() @@ -357,7 +368,9 @@ mod tests { sui_modules_to_watch, vec![ (bridge_module.clone(), Some(stored_cursor)), - (committee_module.clone(), None) + (committee_module.clone(), None), + (treasury_module.clone(), None), + (limiter_module.clone(), None) ] .into_iter() .collect::>() @@ -376,7 +389,9 @@ mod tests { sui_modules_to_watch, vec![ (bridge_module.clone(), Some(override_cursor)), - (committee_module.clone(), Some(override_cursor)) + (committee_module.clone(), Some(override_cursor)), + (treasury_module.clone(), Some(override_cursor)), + (limiter_module.clone(), Some(override_cursor)) ] .into_iter() .collect::>() diff --git a/crates/sui-types/src/bridge.rs b/crates/sui-types/src/bridge.rs index e00c9cc3bb3da..84e65ed53fdd5 100644 --- a/crates/sui-types/src/bridge.rs +++ b/crates/sui-types/src/bridge.rs @@ -34,6 +34,8 @@ pub type BridgeRecordDyanmicField = Field< >; pub const BRIDGE_MODULE_NAME: &IdentStr = ident_str!("bridge"); +pub const BRIDGE_TREASURY_MODULE_NAME: &IdentStr = ident_str!("treasury"); +pub const BRIDGE_LIMITER_MODULE_NAME: &IdentStr = ident_str!("limiter"); pub const BRIDGE_COMMITTEE_MODULE_NAME: &IdentStr = ident_str!("committee"); pub const BRIDGE_MESSAGE_MODULE_NAME: &IdentStr = ident_str!("message"); pub const BRIDGE_CREATE_FUNCTION_NAME: &IdentStr = ident_str!("create"); From 753e4638290e906ae5a37c214f2589e151e3009a Mon Sep 17 00:00:00 2001 From: Mark Logan <103447440+mystenmark@users.noreply.github.com> Date: Fri, 23 Aug 2024 01:21:05 +0100 Subject: [PATCH 218/232] Add ServerTiming headers to fullnode responses (#19063) - Add facility for adding ServerTiming measurements from arbitrary points in the code. - Instrument wait_for_finality and local execution --- Cargo.lock | 15 +++- Cargo.toml | 5 +- crates/mysten-metrics/Cargo.toml | 1 + crates/mysten-metrics/src/lib.rs | 81 ++++++++++++++++++- crates/mysten-service/Cargo.toml | 1 + crates/mysten-service/src/lib.rs | 1 + crates/mysten-service/src/server_timing.rs | 28 +++++++ .../sui-core/src/transaction_orchestrator.rs | 9 ++- crates/sui-json-rpc/Cargo.toml | 1 + crates/sui-json-rpc/src/read_api.rs | 7 +- crates/sui-node/Cargo.toml | 2 + crates/sui-node/src/lib.rs | 3 + scripts/simtest/cargo-simtest | 4 +- scripts/simtest/config-patch | 4 +- 14 files changed, 145 insertions(+), 17 deletions(-) create mode 100644 crates/mysten-service/src/server_timing.rs diff --git a/Cargo.lock b/Cargo.lock index 0a0a3e0de15c8..7d0bedcbdc523 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7801,7 +7801,7 @@ dependencies = [ [[package]] name = "msim" version = "0.1.0" -source = "git+https://github.com/MystenLabs/mysten-sim.git?rev=220f52a15804a768610ac0ae3b8da7de4a5c4d2b#220f52a15804a768610ac0ae3b8da7de4a5c4d2b" +source = "git+https://github.com/MystenLabs/mysten-sim.git?rev=b320996d8dfb99b273fe31c0222c659332283c99#b320996d8dfb99b273fe31c0222c659332283c99" dependencies = [ "ahash 0.7.8", "async-task", @@ -7830,7 +7830,7 @@ dependencies = [ [[package]] name = "msim-macros" version = "0.1.0" -source = "git+https://github.com/MystenLabs/mysten-sim.git?rev=220f52a15804a768610ac0ae3b8da7de4a5c4d2b#220f52a15804a768610ac0ae3b8da7de4a5c4d2b" +source = "git+https://github.com/MystenLabs/mysten-sim.git?rev=b320996d8dfb99b273fe31c0222c659332283c99#b320996d8dfb99b273fe31c0222c659332283c99" dependencies = [ "darling 0.14.2", "proc-macro2 1.0.78", @@ -7947,6 +7947,7 @@ dependencies = [ "prometheus", "prometheus-closure-metric", "scopeguard", + "simple-server-timing-header", "tap", "tokio", "tracing", @@ -7986,6 +7987,7 @@ dependencies = [ "prometheus", "serde", "serde_json", + "simple-server-timing-header", "telemetry-subscribers", "tokio", "tower", @@ -11991,6 +11993,12 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" +[[package]] +name = "simple-server-timing-header" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e78919e05c9b8e123d435a4ad104b488ad1585631830e413830985c214086e" + [[package]] name = "simple_asn1" version = "0.6.2" @@ -13670,6 +13678,7 @@ dependencies = [ "move-core-types", "move-package", "mysten-metrics", + "mysten-service", "once_cell", "prometheus", "serde", @@ -14075,8 +14084,10 @@ dependencies = [ "mysten-common", "mysten-metrics", "mysten-network", + "mysten-service", "narwhal-network", "narwhal-worker", + "parking_lot 0.12.1", "prometheus", "reqwest 0.12.5", "serde", diff --git a/Cargo.toml b/Cargo.toml index 7e815dd578d33..35176bf63b231 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -381,8 +381,8 @@ moka = { version = "0.12", default-features = false, features = [ "atomic64", ] } more-asserts = "0.3.1" -msim = { git = "https://github.com/MystenLabs/mysten-sim.git", rev = "220f52a15804a768610ac0ae3b8da7de4a5c4d2b", package = "msim" } -msim-macros = { git = "https://github.com/MystenLabs/mysten-sim.git", rev = "220f52a15804a768610ac0ae3b8da7de4a5c4d2b", package = "msim-macros" } +msim = { git = "https://github.com/MystenLabs/mysten-sim.git", rev = "b320996d8dfb99b273fe31c0222c659332283c99", package = "msim" } +msim-macros = { git = "https://github.com/MystenLabs/mysten-sim.git", rev = "b320996d8dfb99b273fe31c0222c659332283c99", package = "msim-macros" } multiaddr = "0.17.0" nexlint = { git = "https://github.com/nextest-rs/nexlint.git", rev = "94da5c787636dad779c340affa65219134d127f5" } nexlint-lints = { git = "https://github.com/nextest-rs/nexlint.git", rev = "94da5c787636dad779c340affa65219134d127f5" } @@ -456,6 +456,7 @@ shell-words = "1.1.0" shellexpand = "3.1.0" signature = "1.6.0" similar = "2.4.0" +simple-server-timing-header = "0.1.1" slip10_ed25519 = "0.1.3" smallvec = "1.10.0" snap = "1.1.0" diff --git a/crates/mysten-metrics/Cargo.toml b/crates/mysten-metrics/Cargo.toml index 2a26c989a9451..0686c96048bef 100644 --- a/crates/mysten-metrics/Cargo.toml +++ b/crates/mysten-metrics/Cargo.toml @@ -20,3 +20,4 @@ parking_lot.workspace = true futures.workspace = true async-trait.workspace = true prometheus-closure-metric.workspace = true +simple-server-timing-header.workspace = true diff --git a/crates/mysten-metrics/src/lib.rs b/crates/mysten-metrics/src/lib.rs index ae45b80f98e4f..a55e36632af7f 100644 --- a/crates/mysten-metrics/src/lib.rs +++ b/crates/mysten-metrics/src/lib.rs @@ -3,6 +3,8 @@ use axum::{extract::Extension, http::StatusCode, routing::get, Router}; use dashmap::DashMap; +use parking_lot::Mutex; +use simple_server_timing_header::Timer; use std::future::Future; use std::net::SocketAddr; use std::pin::Pin; @@ -132,6 +134,60 @@ pub fn get_metrics() -> Option<&'static Metrics> { METRICS.get() } +tokio::task_local! { + static SERVER_TIMING: Arc>; +} + +/// Create a new task-local ServerTiming context and run the provided future within it. +/// Should be used at the top-most level of a request handler. Can be added to an axum router +/// as a layer by using mysten_service::server_timing_middleware. +pub async fn with_new_server_timing(fut: impl Future + Send + 'static) -> T { + let timer = Arc::new(Mutex::new(Timer::new())); + + let mut ret = None; + SERVER_TIMING + .scope(timer, async { + ret = Some(fut.await); + }) + .await; + + ret.unwrap() +} + +/// Create a new task-local ServerTiming context and run the provided future within it. +/// Only intended for use by macros within this module. +pub async fn with_server_timing( + timer: Arc>, + fut: impl Future + Send + 'static, +) -> T { + let mut ret = None; + SERVER_TIMING + .scope(timer, async { + ret = Some(fut.await); + }) + .await; + + ret.unwrap() +} + +/// Get the currently active ServerTiming context. Only intended for use by macros within this module. +pub fn get_server_timing() -> Option>> { + SERVER_TIMING.try_with(|timer| timer.clone()).ok() +} + +/// Add a new entry to the ServerTiming header. +/// If the caller is not currently in a ServerTiming context (created with `with_new_server_timing`), +/// an error is logged. +pub fn add_server_timing(name: &str) { + let res = SERVER_TIMING.try_with(|timer| { + timer.lock().add(name); + }); + + if res.is_err() { + tracing::error!("Server timing context not found"); + } +} + #[macro_export] macro_rules! monitored_future { ($fut: expr) => {{ @@ -181,25 +237,42 @@ macro_rules! monitored_future { }}; } +#[macro_export] +macro_rules! forward_server_timing_and_spawn { + ($fut: expr) => { + if let Some(timing) = $crate::get_server_timing() { + tokio::task::spawn(async move { $crate::with_server_timing(timing, $fut).await }) + } else { + tokio::task::spawn($fut) + } + }; +} + #[macro_export] macro_rules! spawn_monitored_task { ($fut: expr) => { - tokio::task::spawn($crate::monitored_future!(tasks, $fut, "", INFO, false)) + $crate::forward_server_timing_and_spawn!($crate::monitored_future!( + tasks, $fut, "", INFO, false + )) }; } #[macro_export] macro_rules! spawn_logged_monitored_task { ($fut: expr) => { - tokio::task::spawn($crate::monitored_future!(tasks, $fut, "", INFO, true)) + $crate::forward_server_timing_and_spawn!($crate::monitored_future!( + tasks, $fut, "", INFO, true + )) }; ($fut: expr, $name: expr) => { - tokio::task::spawn($crate::monitored_future!(tasks, $fut, $name, INFO, true)) + $crate::forward_server_timing_and_spawn!($crate::monitored_future!( + tasks, $fut, $name, INFO, true + )) }; ($fut: expr, $name: expr, $logging_level: ident) => { - tokio::task::spawn($crate::monitored_future!( + $crate::forward_server_timing_and_spawn!($crate::monitored_future!( tasks, $fut, $name, diff --git a/crates/mysten-service/Cargo.toml b/crates/mysten-service/Cargo.toml index 0432f5f80d5b5..08e3a72a48395 100644 --- a/crates/mysten-service/Cargo.toml +++ b/crates/mysten-service/Cargo.toml @@ -12,6 +12,7 @@ axum.workspace = true tokio.workspace = true serde.workspace = true prometheus.workspace = true +simple-server-timing-header.workspace = true mysten-metrics.workspace = true telemetry-subscribers.workspace = true tracing.workspace = true diff --git a/crates/mysten-service/src/lib.rs b/crates/mysten-service/src/lib.rs index 5af492b3ac48a..fc06357e5eee0 100644 --- a/crates/mysten-service/src/lib.rs +++ b/crates/mysten-service/src/lib.rs @@ -4,6 +4,7 @@ mod health; pub mod logging; pub mod metrics; +pub mod server_timing; mod service; pub use service::get_mysten_service; diff --git a/crates/mysten-service/src/server_timing.rs b/crates/mysten-service/src/server_timing.rs new file mode 100644 index 0000000000000..9cdff1d13c571 --- /dev/null +++ b/crates/mysten-service/src/server_timing.rs @@ -0,0 +1,28 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use axum::extract::Request; +use axum::middleware::Next; +use axum::response::Response; +use mysten_metrics::{add_server_timing, get_server_timing, with_new_server_timing}; +use simple_server_timing_header::Timer; + +pub async fn server_timing_middleware(request: Request, next: Next) -> Response { + with_new_server_timing(async move { + let mut response = next.run(request).await; + add_server_timing("finish_request"); + + if let Ok(header_value) = get_server_timing() + .expect("server timing not set") + .lock() + .header_value() + .try_into() + { + response + .headers_mut() + .insert(Timer::header_key(), header_value); + } + response + }) + .await +} diff --git a/crates/sui-core/src/transaction_orchestrator.rs b/crates/sui-core/src/transaction_orchestrator.rs index 42bf5158cef13..77f2caa85500b 100644 --- a/crates/sui-core/src/transaction_orchestrator.rs +++ b/crates/sui-core/src/transaction_orchestrator.rs @@ -16,7 +16,7 @@ use crate::quorum_driver::{QuorumDriverHandler, QuorumDriverHandlerBuilder, Quor use futures::future::{select, Either, Future}; use futures::FutureExt; use mysten_common::sync::notify_read::NotifyRead; -use mysten_metrics::{spawn_logged_monitored_task, spawn_monitored_task}; +use mysten_metrics::{add_server_timing, spawn_logged_monitored_task, spawn_monitored_task}; use mysten_metrics::{TX_TYPE_SHARED_OBJ_TX, TX_TYPE_SINGLE_WRITER_TX}; use prometheus::core::{AtomicI64, AtomicU64, GenericCounter, GenericGauge}; use prometheus::{ @@ -165,7 +165,7 @@ where transaction, response.effects_cert.executed_epoch(), ); - Self::execute_finalized_tx_locally_with_timeout( + let executed_locally = Self::execute_finalized_tx_locally_with_timeout( &self.validator_state, &epoch_store, &executable_tx, @@ -173,7 +173,9 @@ where &self.metrics, ) .await - .is_ok() + .is_ok(); + add_server_timing("local_execution"); + executed_locally } else { false }; @@ -279,6 +281,7 @@ where self.metrics.wait_for_finality_timeout.inc(); return Err(QuorumDriverError::TimeoutBeforeFinality); }; + add_server_timing("wait_for_finality"); drop(_txn_finality_timer); drop(_wait_for_finality_gauge); diff --git a/crates/sui-json-rpc/Cargo.toml b/crates/sui-json-rpc/Cargo.toml index 25ddb1b48ea60..8afaf8f974122 100644 --- a/crates/sui-json-rpc/Cargo.toml +++ b/crates/sui-json-rpc/Cargo.toml @@ -52,6 +52,7 @@ sui-json-rpc-types.workspace = true sui-macros.workspace = true sui-transaction-builder.workspace = true mysten-metrics.workspace = true +mysten-service.workspace = true shared-crypto.workspace = true typed-store-error.workspace = true cached.workspace = true diff --git a/crates/sui-json-rpc/src/read_api.rs b/crates/sui-json-rpc/src/read_api.rs index aa9c96747c13e..809322187d490 100644 --- a/crates/sui-json-rpc/src/read_api.rs +++ b/crates/sui-json-rpc/src/read_api.rs @@ -17,6 +17,7 @@ use move_core_types::language_storage::StructTag; use tap::TapFallible; use tracing::{debug, error, info, instrument, trace, warn}; +use mysten_metrics::add_server_timing; use mysten_metrics::spawn_monitored_task; use sui_core::authority::AuthorityState; use sui_json_rpc_api::{ @@ -721,10 +722,12 @@ impl ReadApiServer for ReadApi { // Fetch transaction to determine existence let transaction_kv_store = self.transaction_kv_store.clone(); let transaction = spawn_monitored_task!(async move { - transaction_kv_store.get_tx(digest).await.map_err(|err| { + let ret = transaction_kv_store.get_tx(digest).await.map_err(|err| { debug!(tx_digest=?digest, "Failed to get transaction: {:?}", err); Error::from(err) - }) + }); + add_server_timing("tx_kv_lookup"); + ret }) .await .map_err(Error::from)??; diff --git a/crates/sui-node/Cargo.toml b/crates/sui-node/Cargo.toml index 2159ad62b713b..9411c6f9e3f9c 100644 --- a/crates/sui-node/Cargo.toml +++ b/crates/sui-node/Cargo.toml @@ -19,6 +19,7 @@ prometheus.workspace = true tokio = { workspace = true, features = ["full"] } tracing.workspace = true futures.workspace = true +parking_lot.workspace = true tower.workspace = true reqwest.workspace = true tap.workspace = true @@ -43,6 +44,7 @@ sui-snapshot.workspace = true sui-telemetry.workspace = true sui-types.workspace = true mysten-metrics.workspace = true +mysten-service.workspace = true mysten-common.workspace = true narwhal-network.workspace = true narwhal-worker.workspace = true diff --git a/crates/sui-node/src/lib.rs b/crates/sui-node/src/lib.rs index db0f848b33209..1f4f881d26f1f 100644 --- a/crates/sui-node/src/lib.rs +++ b/crates/sui-node/src/lib.rs @@ -55,6 +55,7 @@ use fastcrypto_zkp::bn254::zk_login::JWK; pub use handle::SuiNodeHandle; use mysten_metrics::{spawn_monitored_task, RegistryService}; use mysten_network::server::ServerBuilder; +use mysten_service::server_timing::server_timing_middleware; use narwhal_network::metrics::MetricsMakeCallbackHandler; use narwhal_network::metrics::{NetworkConnectionMetrics, NetworkMetrics}; use sui_archival::reader::ArchiveReaderBalancer; @@ -2049,6 +2050,8 @@ pub async fn build_http_server( .unwrap(); let addr = listener.local_addr().unwrap(); + router = router.layer(axum::middleware::from_fn(server_timing_middleware)); + let handle = tokio::spawn(async move { axum::serve( listener, diff --git a/scripts/simtest/cargo-simtest b/scripts/simtest/cargo-simtest index 2c591c251d653..bd07db8332388 100755 --- a/scripts/simtest/cargo-simtest +++ b/scripts/simtest/cargo-simtest @@ -54,9 +54,9 @@ if [ -n "$LOCAL_MSIM_PATH" ]; then else cargo_patch_args=( --config 'patch.crates-io.tokio.git = "https://github.com/MystenLabs/mysten-sim.git"' - --config 'patch.crates-io.tokio.rev = "220f52a15804a768610ac0ae3b8da7de4a5c4d2b"' + --config 'patch.crates-io.tokio.rev = "b320996d8dfb99b273fe31c0222c659332283c99"' --config 'patch.crates-io.futures-timer.git = "https://github.com/MystenLabs/mysten-sim.git"' - --config 'patch.crates-io.futures-timer.rev = "220f52a15804a768610ac0ae3b8da7de4a5c4d2b"' + --config 'patch.crates-io.futures-timer.rev = "b320996d8dfb99b273fe31c0222c659332283c99"' ) fi diff --git a/scripts/simtest/config-patch b/scripts/simtest/config-patch index fe106f225f90e..655ca6ce64adc 100644 --- a/scripts/simtest/config-patch +++ b/scripts/simtest/config-patch @@ -18,5 +18,5 @@ index c0829bc1b6..4007f97d66 100644 include_dir = "0.7.3" [patch.crates-io] -+tokio = { git = "https://github.com/MystenLabs/mysten-sim.git", rev = "220f52a15804a768610ac0ae3b8da7de4a5c4d2b" } -+futures-timer = { git = "https://github.com/MystenLabs/mysten-sim.git", rev = "220f52a15804a768610ac0ae3b8da7de4a5c4d2b" } ++tokio = { git = "https://github.com/MystenLabs/mysten-sim.git", rev = "b320996d8dfb99b273fe31c0222c659332283c99" } ++futures-timer = { git = "https://github.com/MystenLabs/mysten-sim.git", rev = "b320996d8dfb99b273fe31c0222c659332283c99" } From e19a8015fb457e5c7f6b5524bd01fd74a5c0f73a Mon Sep 17 00:00:00 2001 From: Mark Logan <103447440+mystenmark@users.noreply.github.com> Date: Fri, 23 Aug 2024 01:46:45 +0100 Subject: [PATCH 219/232] Revert "Migrate users of mysten_metrics Histogram to prometheus Histogram (#19043)" (#19079) This reverts commit ed69f7cce7ac200a849647c5d8efa7113969d44a. Need to revert this as it breaks health checks for services behind LB --- crates/sui-core/src/authority_aggregator.rs | 18 +-- crates/sui-core/src/authority_server.rs | 69 +++++------ .../checkpoint_executor/metrics.rs | 52 ++++---- .../checkpoints/checkpoint_executor/mod.rs | 16 ++- .../src/checkpoints/checkpoint_output.rs | 4 +- crates/sui-core/src/checkpoints/metrics.rs | 35 +++--- crates/sui-core/src/checkpoints/mod.rs | 4 +- crates/sui-core/src/consensus_adapter.rs | 9 +- crates/sui-core/src/quorum_driver/metrics.rs | 17 +-- crates/sui-core/src/quorum_driver/mod.rs | 4 +- crates/sui-core/src/safe_client.rs | 12 +- .../sui-core/src/transaction_orchestrator.rs | 24 ++-- crates/sui-json-rpc-api/src/lib.rs | 113 +++++++----------- crates/sui-json-rpc/src/coin_api.rs | 6 +- crates/sui-json-rpc/src/governance_api.rs | 2 +- crates/sui-json-rpc/src/indexer_api.rs | 16 +-- crates/sui-json-rpc/src/read_api.rs | 12 +- crates/sui-network/src/state_sync/metrics.rs | 12 +- crates/sui-network/src/state_sync/mod.rs | 2 +- crates/sui-oracle/src/lib.rs | 8 +- crates/sui-oracle/src/metrics.rs | 22 ++-- crates/sui-types/src/messages_checkpoint.rs | 7 +- narwhal/primary/src/consensus/bullshark.rs | 2 +- narwhal/primary/src/consensus/metrics.rs | 7 +- 24 files changed, 202 insertions(+), 271 deletions(-) diff --git a/crates/sui-core/src/authority_aggregator.rs b/crates/sui-core/src/authority_aggregator.rs index 80201ae3530e8..65955171bcbec 100644 --- a/crates/sui-core/src/authority_aggregator.rs +++ b/crates/sui-core/src/authority_aggregator.rs @@ -8,6 +8,7 @@ use crate::authority_client::{ }; use crate::safe_client::{SafeClient, SafeClientMetrics, SafeClientMetricsBase}; use futures::{future::BoxFuture, stream::FuturesUnordered, StreamExt}; +use mysten_metrics::histogram::Histogram; use mysten_metrics::{monitored_future, spawn_monitored_task, GaugeGuard, MonitorCancellation}; use mysten_network::config::Config; use std::convert::AsRef; @@ -39,9 +40,8 @@ use tracing::{debug, error, info, instrument, trace, trace_span, warn, Instrumen use crate::epoch::committee_store::CommitteeStore; use crate::stake_aggregator::{InsertResult, MultiStakeAggregator, StakeAggregator}; use prometheus::{ - register_histogram_with_registry, register_int_counter_vec_with_registry, - register_int_counter_with_registry, register_int_gauge_with_registry, Histogram, IntCounter, - IntCounterVec, IntGauge, Registry, + register_int_counter_vec_with_registry, register_int_counter_with_registry, + register_int_gauge_with_registry, IntCounter, IntCounterVec, IntGauge, Registry, }; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::string::ToString; @@ -179,16 +179,16 @@ impl AuthAggMetrics { registry, ) .unwrap(), - remaining_tasks_when_reaching_cert_quorum: register_histogram_with_registry!( + remaining_tasks_when_reaching_cert_quorum: mysten_metrics::histogram::Histogram::new_in_registry( "auth_agg_remaining_tasks_when_reaching_cert_quorum", "Number of remaining tasks when reaching certificate quorum", registry, - ).unwrap(), - remaining_tasks_when_cert_broadcasting_post_quorum_timeout: register_histogram_with_registry!( + ), + remaining_tasks_when_cert_broadcasting_post_quorum_timeout: mysten_metrics::histogram::Histogram::new_in_registry( "auth_agg_remaining_tasks_when_cert_broadcasting_post_quorum_timeout", "Number of remaining tasks when post quorum certificate broadcasting times out", registry, - ).unwrap() + ) } } @@ -1667,7 +1667,7 @@ where let metrics = self.metrics.clone(); metrics .remaining_tasks_when_reaching_cert_quorum - .observe(remaining_tasks.len() as f64); + .report(remaining_tasks.len() as u64); if !remaining_tasks.is_empty() { // Use best efforts to send the cert to remaining validators. spawn_monitored_task!(async move { @@ -1677,7 +1677,7 @@ where _ = &mut timeout => { debug!(?tx_digest, "Timed out in post quorum cert broadcasting: {:?}. Remaining tasks: {:?}", timeout_after_quorum, remaining_tasks.len()); metrics.cert_broadcasting_post_quorum_timeout.inc(); - metrics.remaining_tasks_when_cert_broadcasting_post_quorum_timeout.observe(remaining_tasks.len() as f64); + metrics.remaining_tasks_when_cert_broadcasting_post_quorum_timeout.report(remaining_tasks.len() as u64); break; } res = remaining_tasks.next() => { diff --git a/crates/sui-core/src/authority_server.rs b/crates/sui-core/src/authority_server.rs index 872659a522800..cb819f18a1a16 100644 --- a/crates/sui-core/src/authority_server.rs +++ b/crates/sui-core/src/authority_server.rs @@ -4,11 +4,12 @@ use anyhow::Result; use async_trait::async_trait; +use mysten_metrics::histogram::Histogram as MystenHistogram; use mysten_metrics::spawn_monitored_task; use narwhal_worker::LazyNarwhalClient; use prometheus::{ - register_histogram_with_registry, register_int_counter_vec_with_registry, - register_int_counter_with_registry, Histogram, IntCounter, IntCounterVec, Registry, + register_int_counter_vec_with_registry, register_int_counter_with_registry, IntCounter, + IntCounterVec, Registry, }; use std::{ io, @@ -165,14 +166,14 @@ impl AuthorityServer { pub struct ValidatorServiceMetrics { pub signature_errors: IntCounter, - pub tx_verification_latency: Histogram, - pub cert_verification_latency: Histogram, - pub consensus_latency: Histogram, - pub handle_transaction_latency: Histogram, - pub submit_certificate_consensus_latency: Histogram, - pub handle_certificate_consensus_latency: Histogram, - pub handle_certificate_non_consensus_latency: Histogram, - pub handle_soft_bundle_certificates_consensus_latency: Histogram, + pub tx_verification_latency: MystenHistogram, + pub cert_verification_latency: MystenHistogram, + pub consensus_latency: MystenHistogram, + pub handle_transaction_latency: MystenHistogram, + pub submit_certificate_consensus_latency: MystenHistogram, + pub handle_certificate_consensus_latency: MystenHistogram, + pub handle_certificate_non_consensus_latency: MystenHistogram, + pub handle_soft_bundle_certificates_consensus_latency: MystenHistogram, num_rejected_tx_in_epoch_boundary: IntCounter, num_rejected_cert_in_epoch_boundary: IntCounter, @@ -193,62 +194,46 @@ impl ValidatorServiceMetrics { registry, ) .unwrap(), - tx_verification_latency: register_histogram_with_registry!( + tx_verification_latency: MystenHistogram::new_in_registry( "validator_service_tx_verification_latency", "Latency of verifying a transaction", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(), - cert_verification_latency: register_histogram_with_registry!( + ), + cert_verification_latency: MystenHistogram::new_in_registry( "validator_service_cert_verification_latency", "Latency of verifying a certificate", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(), - consensus_latency: register_histogram_with_registry!( + ), + consensus_latency: MystenHistogram::new_in_registry( "validator_service_consensus_latency", "Time spent between submitting a shared obj txn to consensus and getting result", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(), - handle_transaction_latency: register_histogram_with_registry!( + ), + handle_transaction_latency: MystenHistogram::new_in_registry( "validator_service_handle_transaction_latency", "Latency of handling a transaction", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(), - handle_certificate_consensus_latency: register_histogram_with_registry!( + ), + handle_certificate_consensus_latency: MystenHistogram::new_in_registry( "validator_service_handle_certificate_consensus_latency", "Latency of handling a consensus transaction certificate", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(), - submit_certificate_consensus_latency: register_histogram_with_registry!( + ), + submit_certificate_consensus_latency: MystenHistogram::new_in_registry( "validator_service_submit_certificate_consensus_latency", "Latency of submit_certificate RPC handler", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(), - handle_certificate_non_consensus_latency: register_histogram_with_registry!( + ), + handle_certificate_non_consensus_latency: MystenHistogram::new_in_registry( "validator_service_handle_certificate_non_consensus_latency", "Latency of handling a non-consensus transaction certificate", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(), - handle_soft_bundle_certificates_consensus_latency: register_histogram_with_registry!( + ), + handle_soft_bundle_certificates_consensus_latency: MystenHistogram::new_in_registry( "validator_service_handle_soft_bundle_certificates_consensus_latency", "Latency of handling a consensus soft bundle", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(), + ), num_rejected_tx_in_epoch_boundary: register_int_counter_with_registry!( "validator_service_num_rejected_tx_in_epoch_boundary", "Number of rejected transaction during epoch transitioning", diff --git a/crates/sui-core/src/checkpoints/checkpoint_executor/metrics.rs b/crates/sui-core/src/checkpoints/checkpoint_executor/metrics.rs index f81e44e9887cc..c51c4c6e61a8f 100644 --- a/crates/sui-core/src/checkpoints/checkpoint_executor/metrics.rs +++ b/crates/sui-core/src/checkpoints/checkpoint_executor/metrics.rs @@ -1,9 +1,10 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use mysten_metrics::histogram::Histogram; use prometheus::{ - register_histogram_with_registry, register_int_counter_with_registry, - register_int_gauge_with_registry, Histogram, IntCounter, IntGauge, Registry, + register_int_counter_with_registry, register_int_gauge_with_registry, IntCounter, IntGauge, + Registry, }; use std::sync::Arc; @@ -14,11 +15,11 @@ pub struct CheckpointExecutorMetrics { pub checkpoint_exec_errors: IntCounter, pub checkpoint_exec_epoch: IntGauge, pub checkpoint_exec_inflight: IntGauge, - pub checkpoint_exec_latency: Histogram, - pub checkpoint_prepare_latency: Histogram, + pub checkpoint_exec_latency_us: Histogram, + pub checkpoint_prepare_latency_us: Histogram, pub checkpoint_transaction_count: Histogram, - pub checkpoint_contents_age: Histogram, - pub last_executed_checkpoint_age: Histogram, + pub checkpoint_contents_age_ms: Histogram, + pub last_executed_checkpoint_age_ms: Histogram, } impl CheckpointExecutorMetrics { @@ -60,38 +61,31 @@ impl CheckpointExecutorMetrics { registry ) .unwrap(), - checkpoint_exec_latency: register_histogram_with_registry!( - "checkpoint_exec_latency", - "Latency of executing a checkpoint from enqueue to all effects available", + checkpoint_exec_latency_us: Histogram::new_in_registry( + "checkpoint_exec_latency_us", + "Latency of executing a checkpoint from enqueue to all effects available, in microseconds", registry, - ) - .unwrap(), - checkpoint_prepare_latency: register_histogram_with_registry!( - "checkpoint_prepare_latency", - "Latency of preparing a checkpoint to enqueue for execution", + ), + checkpoint_prepare_latency_us: Histogram::new_in_registry( + "checkpoint_prepare_latency_us", + "Latency of preparing a checkpoint to enqueue for execution, in microseconds", registry, - ) - .unwrap(), - checkpoint_transaction_count: register_histogram_with_registry!( + ), + checkpoint_transaction_count: Histogram::new_in_registry( "checkpoint_transaction_count", "Number of transactions in the checkpoint", registry, - ) - .unwrap(), - checkpoint_contents_age: register_histogram_with_registry!( - "checkpoint_contents_age", + ), + checkpoint_contents_age_ms: Histogram::new_in_registry( + "checkpoint_contents_age_ms", "Age of checkpoints when they arrive for execution", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(), - last_executed_checkpoint_age: register_histogram_with_registry!( - "last_executed_checkpoint_age", + ), + last_executed_checkpoint_age_ms: Histogram::new_in_registry( + "last_executed_checkpoint_age_ms", "Age of the last executed checkpoint", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry - ) - .unwrap(), + ), }; Arc::new(this) } diff --git a/crates/sui-core/src/checkpoints/checkpoint_executor/mod.rs b/crates/sui-core/src/checkpoints/checkpoint_executor/mod.rs index 885904151d1ed..b1872a9f21723 100644 --- a/crates/sui-core/src/checkpoints/checkpoint_executor/mod.rs +++ b/crates/sui-core/src/checkpoints/checkpoint_executor/mod.rs @@ -327,7 +327,7 @@ impl CheckpointExecutor { sequence_number = ?checkpoint.sequence_number, "Received checkpoint summary from state sync" ); - checkpoint.report_checkpoint_age(&self.metrics.checkpoint_contents_age); + checkpoint.report_checkpoint_age_ms(&self.metrics.checkpoint_contents_age_ms); }, Err(RecvError::Lagged(num_skipped)) => { debug!( @@ -411,7 +411,7 @@ impl CheckpointExecutor { self.metrics .last_executed_checkpoint_timestamp_ms .set(checkpoint.timestamp_ms as i64); - checkpoint.report_checkpoint_age(&self.metrics.last_executed_checkpoint_age); + checkpoint.report_checkpoint_age_ms(&self.metrics.last_executed_checkpoint_age_ms); } /// Post processing and plumbing after we executed a checkpoint. This function is guaranteed @@ -741,9 +741,7 @@ async fn execute_checkpoint( let tx_count = execution_digests.len(); debug!("Number of transactions in the checkpoint: {:?}", tx_count); - metrics - .checkpoint_transaction_count - .observe(tx_count as f64); + metrics.checkpoint_transaction_count.report(tx_count as u64); let checkpoint_acc = execute_transactions( execution_digests, @@ -1246,8 +1244,8 @@ async fn execute_transactions( let prepare_elapsed = prepare_start.elapsed(); metrics - .checkpoint_prepare_latency - .observe(prepare_elapsed.as_secs_f64()); + .checkpoint_prepare_latency_us + .report(prepare_elapsed.as_micros() as u64); if checkpoint.sequence_number % CHECKPOINT_PROGRESS_LOG_COUNT_INTERVAL == 0 { info!( "Checkpoint preparation for execution took {:?}", @@ -1276,8 +1274,8 @@ async fn execute_transactions( let exec_elapsed = exec_start.elapsed(); metrics - .checkpoint_exec_latency - .observe(exec_elapsed.as_secs_f64()); + .checkpoint_exec_latency_us + .report(exec_elapsed.as_micros() as u64); if checkpoint.sequence_number % CHECKPOINT_PROGRESS_LOG_COUNT_INTERVAL == 0 { info!("Checkpoint execution took {:?}", exec_elapsed); } diff --git a/crates/sui-core/src/checkpoints/checkpoint_output.rs b/crates/sui-core/src/checkpoints/checkpoint_output.rs index 4db01df368895..dcf51b801ab4e 100644 --- a/crates/sui-core/src/checkpoints/checkpoint_output.rs +++ b/crates/sui-core/src/checkpoints/checkpoint_output.rs @@ -74,12 +74,12 @@ impl CheckpointOutput let checkpoint_timestamp = summary.timestamp_ms; let checkpoint_seq = summary.sequence_number; - self.metrics.checkpoint_creation_latency.observe( + self.metrics.checkpoint_creation_latency_ms.observe( summary .timestamp() .elapsed() .unwrap_or_default() - .as_secs_f64(), + .as_millis() as u64, ); let highest_verified_checkpoint = checkpoint_store diff --git a/crates/sui-core/src/checkpoints/metrics.rs b/crates/sui-core/src/checkpoints/metrics.rs index 36b639f9d2d6a..8c3ad94121f72 100644 --- a/crates/sui-core/src/checkpoints/metrics.rs +++ b/crates/sui-core/src/checkpoints/metrics.rs @@ -1,11 +1,11 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use mysten_metrics::histogram::Histogram; use prometheus::{ - register_histogram_with_registry, register_int_counter_vec_with_registry, - register_int_counter_with_registry, register_int_gauge_vec_with_registry, - register_int_gauge_with_registry, Histogram, IntCounter, IntCounterVec, IntGauge, IntGaugeVec, - Registry, + register_int_counter_vec_with_registry, register_int_counter_with_registry, + register_int_gauge_vec_with_registry, register_int_gauge_with_registry, IntCounter, + IntCounterVec, IntGauge, IntGaugeVec, Registry, }; use std::sync::Arc; @@ -21,11 +21,11 @@ pub struct CheckpointMetrics { pub last_skipped_checkpoint_signature_submission: IntGauge, pub last_ignored_checkpoint_signature_received: IntGauge, pub highest_accumulated_epoch: IntGauge, - pub checkpoint_creation_latency: Histogram, + pub checkpoint_creation_latency_ms: Histogram, pub remote_checkpoint_forks: IntCounter, pub split_brain_checkpoint_forks: IntCounter, - pub last_created_checkpoint_age: Histogram, - pub last_certified_checkpoint_age: Histogram, + pub last_created_checkpoint_age_ms: Histogram, + pub last_certified_checkpoint_age_ms: Histogram, } impl CheckpointMetrics { @@ -43,18 +43,16 @@ impl CheckpointMetrics { registry ) .unwrap(), - last_created_checkpoint_age: register_histogram_with_registry!( - "last_created_checkpoint_age", + last_created_checkpoint_age_ms: Histogram::new_in_registry( + "last_created_checkpoint_age_ms", "Age of the last created checkpoint", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry - ).unwrap(), - last_certified_checkpoint_age: register_histogram_with_registry!( - "last_certified_checkpoint_age", + ), + last_certified_checkpoint_age_ms: Histogram::new_in_registry( + "last_certified_checkpoint_age_ms", "Age of the last certified checkpoint", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry - ).unwrap(), + ), checkpoint_errors: register_int_counter_with_registry!( "checkpoint_errors", "Checkpoints errors count", @@ -111,12 +109,11 @@ impl CheckpointMetrics { registry ) .unwrap(), - checkpoint_creation_latency: register_histogram_with_registry!( - "checkpoint_creation_latency", + checkpoint_creation_latency_ms: Histogram::new_in_registry( + "checkpoint_creation_latency_ms", "Latency from consensus commit timstamp to local checkpoint creation in milliseconds", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ).unwrap(), + ), remote_checkpoint_forks: register_int_counter_with_registry!( "remote_checkpoint_forks", "Number of remote checkpoints that forked from local checkpoints", diff --git a/crates/sui-core/src/checkpoints/mod.rs b/crates/sui-core/src/checkpoints/mod.rs index bd3387b7fa299..6b64df4e7877c 100644 --- a/crates/sui-core/src/checkpoints/mod.rs +++ b/crates/sui-core/src/checkpoints/mod.rs @@ -1511,7 +1511,7 @@ impl CheckpointBuilder { timestamp_ms, matching_randomness_rounds, ); - summary.report_checkpoint_age(&self.metrics.last_created_checkpoint_age); + summary.report_checkpoint_age_ms(&self.metrics.last_created_checkpoint_age_ms); if last_checkpoint_of_epoch { info!( checkpoint_seq = sequence_number, @@ -1894,7 +1894,7 @@ impl CheckpointAggregator { .set(current.summary.sequence_number as i64); current .summary - .report_checkpoint_age(&self.metrics.last_certified_checkpoint_age); + .report_checkpoint_age_ms(&self.metrics.last_certified_checkpoint_age_ms); result.push(summary.into_inner()); self.current = None; continue 'outer; diff --git a/crates/sui-core/src/consensus_adapter.rs b/crates/sui-core/src/consensus_adapter.rs index 9ddb0a406a223..8b33d8031fac7 100644 --- a/crates/sui-core/src/consensus_adapter.rs +++ b/crates/sui-core/src/consensus_adapter.rs @@ -74,7 +74,7 @@ pub struct ConsensusAdapterMetrics { pub sequencing_certificate_success: IntCounterVec, pub sequencing_certificate_failures: IntCounterVec, pub sequencing_certificate_inflight: IntGaugeVec, - pub sequencing_acknowledge_latency: HistogramVec, + pub sequencing_acknowledge_latency: mysten_metrics::histogram::HistogramVec, pub sequencing_certificate_latency: HistogramVec, pub sequencing_certificate_authority_position: Histogram, pub sequencing_certificate_positions_moved: Histogram, @@ -116,13 +116,12 @@ impl ConsensusAdapterMetrics { registry, ) .unwrap(), - sequencing_acknowledge_latency: register_histogram_vec_with_registry!( + sequencing_acknowledge_latency: mysten_metrics::histogram::HistogramVec::new_in_registry( "sequencing_acknowledge_latency", "The latency for acknowledgement from sequencing engine. The overall sequencing latency is measured by the sequencing_certificate_latency metric", &["retry", "tx_type"], - SEQUENCING_CERTIFICATE_LATENCY_SEC_BUCKETS.to_vec(), registry, - ).unwrap(), + ), sequencing_certificate_latency: register_histogram_vec_with_registry!( "sequencing_certificate_latency", "The latency for sequencing a certificate.", @@ -836,7 +835,7 @@ impl ConsensusAdapter { self.metrics .sequencing_acknowledge_latency .with_label_values(&[&bucket, tx_type]) - .observe(ack_start.elapsed().as_secs_f64()); + .report(ack_start.elapsed().as_millis() as u64); }; match select(processed_waiter, submit_inner.boxed()).await { Either::Left((processed, _submit_inner)) => processed, diff --git a/crates/sui-core/src/quorum_driver/metrics.rs b/crates/sui-core/src/quorum_driver/metrics.rs index a556fac444ec9..0f08b7b110605 100644 --- a/crates/sui-core/src/quorum_driver/metrics.rs +++ b/crates/sui-core/src/quorum_driver/metrics.rs @@ -3,12 +3,13 @@ // SPDX-License-Identifier: Apache-2.0 use prometheus::{ - register_histogram_vec_with_registry, register_histogram_with_registry, - register_int_counter_vec_with_registry, register_int_counter_with_registry, - register_int_gauge_with_registry, Histogram, HistogramVec, IntCounter, IntCounterVec, IntGauge, - Registry, + register_histogram_vec_with_registry, register_int_counter_vec_with_registry, + register_int_counter_with_registry, register_int_gauge_with_registry, HistogramVec, IntCounter, + IntCounterVec, IntGauge, Registry, }; +use mysten_metrics::histogram::Histogram; + const FINALITY_LATENCY_SEC_BUCKETS: &[f64] = &[ 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, @@ -67,11 +68,11 @@ impl QuorumDriverMetrics { registry, ) .unwrap(), - attempt_times_ok_response: register_histogram_with_registry!( + attempt_times_ok_response: Histogram::new_in_registry( "quorum_driver_attempt_times_ok_response", "Total attempt times of ok response", registry, - ).unwrap(), + ), current_requests_in_flight: register_int_gauge_with_registry!( "current_requests_in_flight", "Current number of requests being processed in QuorumDriver", @@ -108,11 +109,11 @@ impl QuorumDriverMetrics { registry, ) .unwrap(), - transaction_retry_count: register_histogram_with_registry!( + transaction_retry_count: Histogram::new_in_registry( "quorum_driver_transaction_retry_count", "Histogram of transaction retry count", registry, - ).unwrap(), + ), current_transactions_in_retry: register_int_gauge_with_registry!( "current_transactions_in_retry", "Current number of transactions in retry loop in QuorumDriver", diff --git a/crates/sui-core/src/quorum_driver/mod.rs b/crates/sui-core/src/quorum_driver/mod.rs index 94ffdfdbf6771..a01afe41206b5 100644 --- a/crates/sui-core/src/quorum_driver/mod.rs +++ b/crates/sui-core/src/quorum_driver/mod.rs @@ -127,7 +127,7 @@ impl QuorumDriver { } self.metrics .transaction_retry_count - .observe(task.retry_times as f64); + .report(task.retry_times as u64); } }) .map_err(|e| SuiError::QuorumDriverCommunicationError { @@ -210,7 +210,7 @@ impl QuorumDriver { self.metrics.total_ok_responses.inc(); self.metrics .attempt_times_ok_response - .observe(total_attempts as f64); + .report(total_attempts as u64); Ok((transaction.clone(), resp.clone())) } Err(err) => { diff --git a/crates/sui-core/src/safe_client.rs b/crates/sui-core/src/safe_client.rs index 750ce1fabcc6f..74c80cd42afd4 100644 --- a/crates/sui-core/src/safe_client.rs +++ b/crates/sui-core/src/safe_client.rs @@ -4,11 +4,9 @@ use crate::authority_client::AuthorityAPI; use crate::epoch::committee_store::CommitteeStore; +use mysten_metrics::histogram::{Histogram, HistogramVec}; use prometheus::core::GenericCounter; -use prometheus::{ - register_histogram_vec_with_registry, register_int_counter_vec_with_registry, Histogram, - HistogramVec, IntCounterVec, Registry, -}; +use prometheus::{register_int_counter_vec_with_registry, IntCounterVec, Registry}; use std::collections::HashMap; use std::net::SocketAddr; use std::sync::Arc; @@ -68,14 +66,12 @@ impl SafeClientMetricsBase { registry, ) .unwrap(), - latency: register_histogram_vec_with_registry!( + latency: HistogramVec::new_in_registry( "safe_client_latency", "RPC latency observed by safe client aggregator, group by address and method", &["address", "method"], - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(), + ), } } } diff --git a/crates/sui-core/src/transaction_orchestrator.rs b/crates/sui-core/src/transaction_orchestrator.rs index 77f2caa85500b..ce69aeb474033 100644 --- a/crates/sui-core/src/transaction_orchestrator.rs +++ b/crates/sui-core/src/transaction_orchestrator.rs @@ -16,13 +16,13 @@ use crate::quorum_driver::{QuorumDriverHandler, QuorumDriverHandlerBuilder, Quor use futures::future::{select, Either, Future}; use futures::FutureExt; use mysten_common::sync::notify_read::NotifyRead; +use mysten_metrics::histogram::{Histogram, HistogramVec}; use mysten_metrics::{add_server_timing, spawn_logged_monitored_task, spawn_monitored_task}; use mysten_metrics::{TX_TYPE_SHARED_OBJ_TX, TX_TYPE_SINGLE_WRITER_TX}; use prometheus::core::{AtomicI64, AtomicU64, GenericCounter, GenericGauge}; use prometheus::{ - register_histogram_vec_with_registry, register_int_counter_vec_with_registry, - register_int_counter_with_registry, register_int_gauge_vec_with_registry, - register_int_gauge_with_registry, Histogram, Registry, + register_int_counter_vec_with_registry, register_int_counter_with_registry, + register_int_gauge_vec_with_registry, register_int_gauge_with_registry, Registry, }; use std::net::SocketAddr; use std::ops::Deref; @@ -625,30 +625,24 @@ impl TransactionOrchestratorMetrics { req_in_flight.with_label_values(&[TX_TYPE_SINGLE_WRITER_TX]); let req_in_flight_shared_object = req_in_flight.with_label_values(&[TX_TYPE_SHARED_OBJ_TX]); - let request_latency = register_histogram_vec_with_registry!( + let request_latency = HistogramVec::new_in_registry( "tx_orchestrator_request_latency", "Time spent in processing one Transaction Orchestrator request", &["tx_type"], - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(); - let wait_for_finality_latency = register_histogram_vec_with_registry!( + ); + let wait_for_finality_latency = HistogramVec::new_in_registry( "tx_orchestrator_wait_for_finality_latency", "Time spent in waiting for one Transaction Orchestrator request gets finalized", &["tx_type"], - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(); - let local_execution_latency = register_histogram_vec_with_registry!( + ); + let local_execution_latency = HistogramVec::new_in_registry( "tx_orchestrator_local_execution_latency", "Time spent in waiting for one Transaction Orchestrator gets locally executed", &["tx_type"], - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(); + ); Self { total_req_received_single_writer, diff --git a/crates/sui-json-rpc-api/src/lib.rs b/crates/sui-json-rpc-api/src/lib.rs index 1f98a9573fd20..104963812e31f 100644 --- a/crates/sui-json-rpc-api/src/lib.rs +++ b/crates/sui-json-rpc-api/src/lib.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use anyhow::anyhow; +use mysten_metrics::histogram::Histogram; + pub use bridge::BridgeReadApiClient; pub use bridge::BridgeReadApiOpenRpc; pub use bridge::BridgeReadApiServer; @@ -21,8 +23,6 @@ pub use move_utils::MoveUtilsClient; pub use move_utils::MoveUtilsOpenRpc; pub use move_utils::MoveUtilsServer; use once_cell::sync::Lazy; -use prometheus::register_histogram_with_registry; -use prometheus::Histogram; use prometheus::{register_int_counter_with_registry, IntCounter}; pub use read::ReadApiClient; pub use read::ReadApiOpenRpc; @@ -114,190 +114,165 @@ pub struct JsonRpcMetrics { impl JsonRpcMetrics { pub fn new(registry: &prometheus::Registry) -> Self { Self { - get_objects_limit: register_histogram_with_registry!( + get_objects_limit: Histogram::new_in_registry( "json_rpc_get_objects_limit", "The input limit for multi_get_objects, after applying the cap", registry, - ) - .unwrap(), - get_objects_result_size: register_histogram_with_registry!( + ), + get_objects_result_size: Histogram::new_in_registry( "json_rpc_get_objects_result_size", "The return size for multi_get_objects", registry, - ) - .unwrap(), + ), get_objects_result_size_total: register_int_counter_with_registry!( "json_rpc_get_objects_result_size_total", "The total return size for multi_get_objects", registry ) .unwrap(), - get_tx_blocks_limit: register_histogram_with_registry!( + get_tx_blocks_limit: Histogram::new_in_registry( "json_rpc_get_tx_blocks_limit", "The input limit for get_tx_blocks, after applying the cap", registry, - ) - .unwrap(), - get_tx_blocks_result_size: register_histogram_with_registry!( + ), + get_tx_blocks_result_size: Histogram::new_in_registry( "json_rpc_get_tx_blocks_result_size", "The return size for get_tx_blocks", registry, - ) - .unwrap(), + ), get_tx_blocks_result_size_total: register_int_counter_with_registry!( "json_rpc_get_tx_blocks_result_size_total", "The total return size for get_tx_blocks", registry ) .unwrap(), - get_checkpoints_limit: register_histogram_with_registry!( + get_checkpoints_limit: Histogram::new_in_registry( "json_rpc_get_checkpoints_limit", "The input limit for get_checkpoints, after applying the cap", registry, - ) - .unwrap(), - get_checkpoints_result_size: register_histogram_with_registry!( + ), + get_checkpoints_result_size: Histogram::new_in_registry( "json_rpc_get_checkpoints_result_size", "The return size for get_checkpoints", registry, - ) - .unwrap(), + ), get_checkpoints_result_size_total: register_int_counter_with_registry!( "json_rpc_get_checkpoints_result_size_total", "The total return size for get_checkpoints", registry ) .unwrap(), - get_owned_objects_limit: register_histogram_with_registry!( + get_owned_objects_limit: Histogram::new_in_registry( "json_rpc_get_owned_objects_limit", "The input limit for get_owned_objects, after applying the cap", registry, - ) - .unwrap(), - get_owned_objects_result_size: register_histogram_with_registry!( + ), + get_owned_objects_result_size: Histogram::new_in_registry( "json_rpc_get_owned_objects_result_size", "The return size for get_owned_objects", registry, - ) - .unwrap(), + ), get_owned_objects_result_size_total: register_int_counter_with_registry!( "json_rpc_get_owned_objects_result_size_total", "The total return size for get_owned_objects", registry ) .unwrap(), - get_coins_limit: register_histogram_with_registry!( + get_coins_limit: Histogram::new_in_registry( "json_rpc_get_coins_limit", "The input limit for get_coins, after applying the cap", registry, - ) - .unwrap(), - get_coins_result_size: register_histogram_with_registry!( + ), + get_coins_result_size: Histogram::new_in_registry( "json_rpc_get_coins_result_size", "The return size for get_coins", registry, - ) - .unwrap(), + ), get_coins_result_size_total: register_int_counter_with_registry!( "json_rpc_get_coins_result_size_total", "The total return size for get_coins", registry ) .unwrap(), - get_dynamic_fields_limit: register_histogram_with_registry!( + get_dynamic_fields_limit: Histogram::new_in_registry( "json_rpc_get_dynamic_fields_limit", "The input limit for get_dynamic_fields, after applying the cap", registry, - ) - .unwrap(), - get_dynamic_fields_result_size: register_histogram_with_registry!( + ), + get_dynamic_fields_result_size: Histogram::new_in_registry( "json_rpc_get_dynamic_fields_result_size", "The return size for get_dynamic_fields", registry, - ) - .unwrap(), + ), get_dynamic_fields_result_size_total: register_int_counter_with_registry!( "json_rpc_get_dynamic_fields_result_size_total", "The total return size for get_dynamic_fields", registry ) .unwrap(), - query_tx_blocks_limit: register_histogram_with_registry!( + query_tx_blocks_limit: Histogram::new_in_registry( "json_rpc_query_tx_blocks_limit", "The input limit for query_tx_blocks, after applying the cap", registry, - ) - .unwrap(), - query_tx_blocks_result_size: register_histogram_with_registry!( + ), + query_tx_blocks_result_size: Histogram::new_in_registry( "json_rpc_query_tx_blocks_result_size", "The return size for query_tx_blocks", registry, - ) - .unwrap(), + ), query_tx_blocks_result_size_total: register_int_counter_with_registry!( "json_rpc_query_tx_blocks_result_size_total", "The total return size for query_tx_blocks", registry ) .unwrap(), - query_events_limit: register_histogram_with_registry!( + query_events_limit: Histogram::new_in_registry( "json_rpc_query_events_limit", "The input limit for query_events, after applying the cap", registry, - ) - .unwrap(), - query_events_result_size: register_histogram_with_registry!( + ), + query_events_result_size: Histogram::new_in_registry( "json_rpc_query_events_result_size", "The return size for query_events", registry, - ) - .unwrap(), + ), query_events_result_size_total: register_int_counter_with_registry!( "json_rpc_query_events_result_size_total", "The total return size for query_events", registry ) .unwrap(), - get_stake_sui_result_size: register_histogram_with_registry!( + get_stake_sui_result_size: Histogram::new_in_registry( "json_rpc_get_stake_sui_result_size", "The return size for get_stake_sui", registry, - ) - .unwrap(), + ), get_stake_sui_result_size_total: register_int_counter_with_registry!( "json_rpc_get_stake_sui_result_size_total", "The total return size for get_stake_sui", registry ) .unwrap(), - get_stake_sui_latency: register_histogram_with_registry!( + get_stake_sui_latency: Histogram::new_in_registry( "get_stake_sui_latency", "The latency of get stake sui, in ms", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(), - get_delegated_sui_latency: register_histogram_with_registry!( + ), + get_delegated_sui_latency: Histogram::new_in_registry( "get_delegated_sui_latency", "The latency of get delegated sui, in ms", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(), - orchestrator_latency_ms: register_histogram_with_registry!( + ), + orchestrator_latency_ms: Histogram::new_in_registry( "json_rpc_orchestrator_latency", "The latency of submitting transaction via transaction orchestrator, in ms", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(), - post_orchestrator_latency_ms: register_histogram_with_registry!( + ), + post_orchestrator_latency_ms: Histogram::new_in_registry( "json_rpc_post_orchestrator_latency", "The latency of response processing after transaction orchestrator, in ms", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(), + ), } } diff --git a/crates/sui-json-rpc/src/coin_api.rs b/crates/sui-json-rpc/src/coin_api.rs index 99d7613d7ce61..fafb06d4bfd89 100644 --- a/crates/sui-json-rpc/src/coin_api.rs +++ b/crates/sui-json-rpc/src/coin_api.rs @@ -373,7 +373,7 @@ impl CoinReadInternal for CoinReadInternalImpl { one_coin_type_only: bool, ) -> RpcInterimResult { let limit = cap_page_limit(limit); - self.metrics.get_coins_limit.observe(limit as f64); + self.metrics.get_coins_limit.report(limit as u64); let state = self.get_state(); let mut data = spawn_monitored_task!(async move { state.get_owned_coins(owner, cursor, limit + 1, one_coin_type_only) @@ -383,9 +383,7 @@ impl CoinReadInternal for CoinReadInternalImpl { let has_next_page = data.len() > limit; data.truncate(limit); - self.metrics - .get_coins_result_size - .observe(data.len() as f64); + self.metrics.get_coins_result_size.report(data.len() as u64); self.metrics .get_coins_result_size_total .inc_by(data.len() as u64); diff --git a/crates/sui-json-rpc/src/governance_api.rs b/crates/sui-json-rpc/src/governance_api.rs index a8a23a41952ed..5f8e514d1d04a 100644 --- a/crates/sui-json-rpc/src/governance_api.rs +++ b/crates/sui-json-rpc/src/governance_api.rs @@ -54,7 +54,7 @@ impl GovernanceReadApi { self.metrics .get_stake_sui_result_size - .observe(result.len() as f64); + .report(result.len() as u64); self.metrics .get_stake_sui_result_size_total .inc_by(result.len() as u64); diff --git a/crates/sui-json-rpc/src/indexer_api.rs b/crates/sui-json-rpc/src/indexer_api.rs index 53709116966b4..e29e373dd2872 100644 --- a/crates/sui-json-rpc/src/indexer_api.rs +++ b/crates/sui-json-rpc/src/indexer_api.rs @@ -148,7 +148,7 @@ impl IndexerApiServer for IndexerApi { with_tracing!(async move { let limit = validate_limit(limit, *QUERY_MAX_RESULT_LIMIT).map_err(SuiRpcInputError::from)?; - self.metrics.get_owned_objects_limit.observe(limit as f64); + self.metrics.get_owned_objects_limit.report(limit as u64); let SuiObjectResponseQuery { filter, options } = query.unwrap_or_default(); let options = options.unwrap_or_default(); let mut objects = self @@ -179,7 +179,7 @@ impl IndexerApiServer for IndexerApi { self.metrics .get_owned_objects_result_size - .observe(data.len() as f64); + .report(data.len() as u64); self.metrics .get_owned_objects_result_size_total .inc_by(data.len() as u64); @@ -202,7 +202,7 @@ impl IndexerApiServer for IndexerApi { ) -> RpcResult { with_tracing!(async move { let limit = cap_page_limit(limit); - self.metrics.query_tx_blocks_limit.observe(limit as f64); + self.metrics.query_tx_blocks_limit.report(limit as u64); let descending = descending_order.unwrap_or_default(); let opts = query.options.unwrap_or_default(); @@ -241,7 +241,7 @@ impl IndexerApiServer for IndexerApi { self.metrics .query_tx_blocks_result_size - .observe(data.len() as f64); + .report(data.len() as u64); self.metrics .query_tx_blocks_result_size_total .inc_by(data.len() as u64); @@ -264,7 +264,7 @@ impl IndexerApiServer for IndexerApi { with_tracing!(async move { let descending = descending_order.unwrap_or_default(); let limit = cap_page_limit(limit); - self.metrics.query_events_limit.observe(limit as f64); + self.metrics.query_events_limit.report(limit as u64); // Retrieve 1 extra item for next cursor let mut data = self .state @@ -282,7 +282,7 @@ impl IndexerApiServer for IndexerApi { let next_cursor = data.last().map_or(cursor, |e| Some(e.id)); self.metrics .query_events_result_size - .observe(data.len() as f64); + .report(data.len() as u64); self.metrics .query_events_result_size_total .inc_by(data.len() as u64); @@ -333,7 +333,7 @@ impl IndexerApiServer for IndexerApi { ) -> RpcResult { with_tracing!(async move { let limit = cap_page_limit(limit); - self.metrics.get_dynamic_fields_limit.observe(limit as f64); + self.metrics.get_dynamic_fields_limit.report(limit as u64); let mut data = self .state .get_dynamic_fields(parent_object_id, cursor, limit + 1) @@ -343,7 +343,7 @@ impl IndexerApiServer for IndexerApi { let next_cursor = data.last().cloned().map_or(cursor, |c| Some(c.0)); self.metrics .get_dynamic_fields_result_size - .observe(data.len() as f64); + .report(data.len() as u64); self.metrics .get_dynamic_fields_result_size_total .inc_by(data.len() as u64); diff --git a/crates/sui-json-rpc/src/read_api.rs b/crates/sui-json-rpc/src/read_api.rs index 809322187d490..4dfdccc1086c6 100644 --- a/crates/sui-json-rpc/src/read_api.rs +++ b/crates/sui-json-rpc/src/read_api.rs @@ -210,7 +210,7 @@ impl ReadApi { } self.metrics .get_tx_blocks_limit - .observe(digests.len() as f64); + .report(digests.len() as u64); let opts = opts.unwrap_or_default(); @@ -466,7 +466,7 @@ impl ReadApi { self.metrics .get_tx_blocks_result_size - .observe(converted_tx_block_resps.len() as f64); + .report(converted_tx_block_resps.len() as u64); self.metrics .get_tx_blocks_result_size_total .inc_by(converted_tx_block_resps.len() as u64); @@ -543,7 +543,7 @@ impl ReadApiServer for ReadApi { if object_ids.len() <= *QUERY_MAX_RESULT_LIMIT { self.metrics .get_objects_limit - .observe(object_ids.len() as f64); + .report(object_ids.len() as u64); let mut futures = vec![]; for object_id in object_ids { futures.push(self.get_object(object_id, options.clone())); @@ -567,7 +567,7 @@ impl ReadApiServer for ReadApi { self.metrics .get_objects_result_size - .observe(objects.len() as f64); + .report(objects.len() as u64); self.metrics .get_objects_result_size_total .inc_by(objects.len() as u64); @@ -964,7 +964,7 @@ impl ReadApiServer for ReadApi { let state = self.state.clone(); let kv_store = self.transaction_kv_store.clone(); - self.metrics.get_checkpoints_limit.observe(limit as f64); + self.metrics.get_checkpoints_limit.report(limit as u64); let mut data = spawn_monitored_task!(Self::get_checkpoints_internal( state, @@ -988,7 +988,7 @@ impl ReadApiServer for ReadApi { self.metrics .get_checkpoints_result_size - .observe(data.len() as f64); + .report(data.len() as u64); self.metrics .get_checkpoints_result_size_total .inc_by(data.len() as u64); diff --git a/crates/sui-network/src/state_sync/metrics.rs b/crates/sui-network/src/state_sync/metrics.rs index f88c1592a66af..3d3e5b0ccd53c 100644 --- a/crates/sui-network/src/state_sync/metrics.rs +++ b/crates/sui-network/src/state_sync/metrics.rs @@ -1,10 +1,8 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use prometheus::{ - register_histogram_with_registry, register_int_gauge_with_registry, Histogram, IntGauge, - Registry, -}; +use mysten_metrics::histogram::Histogram; +use prometheus::{register_int_gauge_with_registry, IntGauge, Registry}; use std::sync::Arc; use sui_types::messages_checkpoint::CheckpointSequenceNumber; use tap::Pipe; @@ -86,13 +84,11 @@ impl Inner { ) .unwrap(), - checkpoint_summary_age_ms: register_histogram_with_registry!( + checkpoint_summary_age_ms: Histogram::new_in_registry( "checkpoint_summary_age_ms", "Age of checkpoints summaries when they arrive and are verified.", - mysten_metrics::LATENCY_SEC_BUCKETS.to_vec(), registry, - ) - .unwrap(), + ), } .pipe(Arc::new) } diff --git a/crates/sui-network/src/state_sync/mod.rs b/crates/sui-network/src/state_sync/mod.rs index a4d9bb87ec7c5..0a29e219ebf5e 100644 --- a/crates/sui-network/src/state_sync/mod.rs +++ b/crates/sui-network/src/state_sync/mod.rs @@ -1089,7 +1089,7 @@ where debug!(checkpoint_seq = ?checkpoint.sequence_number(), "verified checkpoint summary"); if let Some(checkpoint_summary_age_metric) = metrics.checkpoint_summary_age_metric() { - checkpoint.report_checkpoint_age(checkpoint_summary_age_metric); + checkpoint.report_checkpoint_age_ms(checkpoint_summary_age_metric); } current = checkpoint.clone(); diff --git a/crates/sui-oracle/src/lib.rs b/crates/sui-oracle/src/lib.rs index 8fbdb85e0a385..3f649466280d0 100644 --- a/crates/sui-oracle/src/lib.rs +++ b/crates/sui-oracle/src/lib.rs @@ -525,7 +525,7 @@ impl OnChainDataUploader { self.metrics .uploaded_values .with_label_values(&[&data_point.feed_name]) - .observe(data_point.value as f64); + .observe(data_point.value); } else { self.metrics .upload_data_errors @@ -538,9 +538,7 @@ impl OnChainDataUploader { let storage_rebate = effects.gas_cost_summary().storage_rebate; let computation_cost = effects.gas_cost_summary().computation_cost; let net_gas_usage = effects.gas_cost_summary().net_gas_usage(); - self.metrics - .computation_gas_used - .observe(computation_cost as f64); + self.metrics.computation_gas_used.observe(computation_cost); self.metrics.total_gas_cost.inc_by(gas_usage); self.metrics.total_gas_rebate.inc_by(storage_rebate); @@ -662,7 +660,7 @@ impl OnChainDataReader { self.metrics .downloaded_values .with_label_values(&[feed_name]) - .observe(value * METRICS_MULTIPLIER); + .observe((value * METRICS_MULTIPLIER) as u64); self.metrics .download_successes .with_label_values(&[feed_name, &object_id.to_string()]) diff --git a/crates/sui-oracle/src/metrics.rs b/crates/sui-oracle/src/metrics.rs index 365c677ce45fe..552e68ae6da0b 100644 --- a/crates/sui-oracle/src/metrics.rs +++ b/crates/sui-oracle/src/metrics.rs @@ -2,11 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 use prometheus::{ - register_histogram_vec_with_registry, register_histogram_with_registry, - register_int_counter_vec_with_registry, register_int_counter_with_registry, Histogram, - HistogramVec, IntCounter, IntCounterVec, Registry, + register_int_counter_vec_with_registry, register_int_counter_with_registry, IntCounter, + IntCounterVec, Registry, }; +use mysten_metrics::histogram::{Histogram, HistogramVec}; + #[derive(Clone)] pub struct OracleMetrics { pub(crate) data_source_successes: IntCounterVec, @@ -77,20 +78,18 @@ impl OracleMetrics { registry, ) .unwrap(), - uploaded_values: register_histogram_vec_with_registry!( + uploaded_values: HistogramVec::new_in_registry( "oracle_uploaded_values", "Values uploaded on chain", &["feed"], registry, - ) - .unwrap(), - downloaded_values: register_histogram_vec_with_registry!( + ), + downloaded_values: HistogramVec::new_in_registry( "oracle_downloaded_values", "Values downloaded on chain", &["feed"], registry, - ) - .unwrap(), + ), total_gas_cost: register_int_counter_with_registry!( "oracle_total_gas_cost", "Total number of gas used, before gas rebate", @@ -103,12 +102,11 @@ impl OracleMetrics { registry, ) .unwrap(), - computation_gas_used: register_histogram_with_registry!( + computation_gas_used: Histogram::new_in_registry( "oracle_computation_gas_used", "computation gas used", registry, - ) - .unwrap(), + ), total_data_points_uploaded: register_int_counter_with_registry!( "oracle_total_data_points_uploaded", "Total number of data points uploaded", diff --git a/crates/sui-types/src/messages_checkpoint.rs b/crates/sui-types/src/messages_checkpoint.rs index 163c68ef37630..ceea05079d484 100644 --- a/crates/sui-types/src/messages_checkpoint.rs +++ b/crates/sui-types/src/messages_checkpoint.rs @@ -25,7 +25,6 @@ use crate::{base_types::AuthorityName, committee::Committee, error::SuiError}; use anyhow::Result; use fastcrypto::hash::MultisetHash; use once_cell::sync::OnceCell; -use prometheus::Histogram; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_with::serde_as; @@ -43,6 +42,8 @@ pub use crate::digests::CheckpointDigest; pub type CheckpointSequenceNumber = u64; pub type CheckpointTimestamp = u64; +use mysten_metrics::histogram::Histogram; + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CheckpointRequest { /// if a sequence number is specified, return the checkpoint with that sequence number; @@ -264,10 +265,10 @@ impl CheckpointSummary { .map(|e| e.next_epoch_committee.as_slice()) } - pub fn report_checkpoint_age(&self, metrics: &Histogram) { + pub fn report_checkpoint_age_ms(&self, metrics: &Histogram) { SystemTime::now() .duration_since(self.timestamp()) - .map(|latency| metrics.observe(latency.as_secs_f64())) + .map(|latency| metrics.report(latency.as_millis() as u64)) .tap_err(|err| { warn!( checkpoint_seq = self.sequence_number, diff --git a/narwhal/primary/src/consensus/bullshark.rs b/narwhal/primary/src/consensus/bullshark.rs index 2d8f43cfd1f04..267b63012767e 100644 --- a/narwhal/primary/src/consensus/bullshark.rs +++ b/narwhal/primary/src/consensus/bullshark.rs @@ -204,7 +204,7 @@ impl Bullshark { self.metrics .committed_certificates - .observe(total_committed_certificates as f64); + .report(total_committed_certificates); Ok((Outcome::Commit, committed_sub_dags)) } diff --git a/narwhal/primary/src/consensus/metrics.rs b/narwhal/primary/src/consensus/metrics.rs index a880d92d50b94..d855a249327bf 100644 --- a/narwhal/primary/src/consensus/metrics.rs +++ b/narwhal/primary/src/consensus/metrics.rs @@ -1,6 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use mysten_metrics::histogram::Histogram as MystenHistogram; use prometheus::{ default_registry, register_histogram_with_registry, register_int_counter_vec_with_registry, register_int_counter_with_registry, register_int_gauge_vec_with_registry, @@ -27,7 +28,7 @@ pub struct ConsensusMetrics { /// The latency between two successful commit rounds pub commit_rounds_latency: Histogram, /// The number of certificates committed per commit round - pub committed_certificates: Histogram, + pub committed_certificates: MystenHistogram, /// The time it takes for a certificate from the moment it gets created /// up to the moment it gets committed. pub certificate_commit_latency: Histogram, @@ -85,11 +86,11 @@ impl ConsensusMetrics { LATENCY_SEC_BUCKETS.to_vec(), registry ).unwrap(), - committed_certificates: register_histogram_with_registry!( + committed_certificates: MystenHistogram::new_in_registry( "committed_certificates", "The number of certificates committed on a commit round", registry - ).unwrap(), + ), certificate_commit_latency: register_histogram_with_registry!( "certificate_commit_latency", "The time it takes for a certificate from the moment it gets created up to the moment it gets committed.", From 9726619730412beb6f12301eea58836bd4067b5b Mon Sep 17 00:00:00 2001 From: pei-mysten <147538877+pei-mysten@users.noreply.github.com> Date: Fri, 23 Aug 2024 05:19:17 +0100 Subject: [PATCH 220/232] [suiop] fix dup alias issue (#19073) ## Description as title ## Test plan tested locally --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/suiop-cli/src/cli/ci/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/suiop-cli/src/cli/ci/mod.rs b/crates/suiop-cli/src/cli/ci/mod.rs index 351d45809d8d0..ecbae85e75a19 100644 --- a/crates/suiop-cli/src/cli/ci/mod.rs +++ b/crates/suiop-cli/src/cli/ci/mod.rs @@ -20,7 +20,7 @@ pub struct CIArgs { pub(crate) enum CIAction { #[clap(aliases = ["k", "key"])] Keys(KeyArgs), - #[clap(aliases = ["i", "image"])] + #[clap(aliases = ["i"])] Image(ImageArgs), } From cc20b13c3207aac775c71609de8d1b16ccbb5833 Mon Sep 17 00:00:00 2001 From: pei-mysten <147538877+pei-mysten@users.noreply.github.com> Date: Fri, 23 Aug 2024 06:00:11 +0100 Subject: [PATCH 221/232] Sui v1.32 snapshot (#19081) ## Description Sui v1.32 snapshot --- ...00000000000000000000000000000000000000000001 | Bin 0 -> 11210 bytes ...00000000000000000000000000000000000000000002 | Bin 0 -> 66623 bytes ...00000000000000000000000000000000000000000003 | Bin 0 -> 41861 bytes ...0000000000000000000000000000000000000000000b | Bin 0 -> 19848 bytes ...0000000000000000000000000000000000000000dee9 | Bin 0 -> 33346 bytes crates/sui-framework-snapshot/manifest.json | 10 ++++++++++ 6 files changed, 10 insertions(+) create mode 100644 crates/sui-framework-snapshot/bytecode_snapshot/55/0x0000000000000000000000000000000000000000000000000000000000000001 create mode 100644 crates/sui-framework-snapshot/bytecode_snapshot/55/0x0000000000000000000000000000000000000000000000000000000000000002 create mode 100644 crates/sui-framework-snapshot/bytecode_snapshot/55/0x0000000000000000000000000000000000000000000000000000000000000003 create mode 100644 crates/sui-framework-snapshot/bytecode_snapshot/55/0x000000000000000000000000000000000000000000000000000000000000000b create mode 100644 crates/sui-framework-snapshot/bytecode_snapshot/55/0x000000000000000000000000000000000000000000000000000000000000dee9 diff --git a/crates/sui-framework-snapshot/bytecode_snapshot/55/0x0000000000000000000000000000000000000000000000000000000000000001 b/crates/sui-framework-snapshot/bytecode_snapshot/55/0x0000000000000000000000000000000000000000000000000000000000000001 new file mode 100644 index 0000000000000000000000000000000000000000..6aae601648984df646a52a28b178d6962f81f810 GIT binary patch literal 11210 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 i4=2YVB+y1J{Zsvf2h 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 literal 0 HcmV?d00001 diff --git a/crates/sui-framework-snapshot/bytecode_snapshot/55/0x0000000000000000000000000000000000000000000000000000000000000003 b/crates/sui-framework-snapshot/bytecode_snapshot/55/0x0000000000000000000000000000000000000000000000000000000000000003 new file mode 100644 index 0000000000000000000000000000000000000000..f5e57b11051a4720bd5f640d5007c4da8d763695 GIT binary patch literal 41861 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 ZfizyVoN2~tCSB5{(N|xkg7^EW{~yrTbHD%q literal 0 HcmV?d00001 diff --git a/crates/sui-framework-snapshot/bytecode_snapshot/55/0x000000000000000000000000000000000000000000000000000000000000000b b/crates/sui-framework-snapshot/bytecode_snapshot/55/0x000000000000000000000000000000000000000000000000000000000000000b new file mode 100644 index 0000000000000000000000000000000000000000..7a2443d5f54132e627ef68943f13b9a7ad506532 GIT binary patch literal 19848 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 J_lb)8{|#>VMj`+J literal 0 HcmV?d00001 diff --git a/crates/sui-framework-snapshot/bytecode_snapshot/55/0x000000000000000000000000000000000000000000000000000000000000dee9 b/crates/sui-framework-snapshot/bytecode_snapshot/55/0x000000000000000000000000000000000000000000000000000000000000dee9 new file mode 100644 index 0000000000000000000000000000000000000000..d6568ff2626faa32de6303366338f09330d01be9 GIT binary patch literal 33346 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ˈp`w;Dcw~u(su6h5*!rk&2tjk=>$W8Ef3H+7; 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 Date: Fri, 23 Aug 2024 14:31:26 -0500 Subject: [PATCH 222/232] chore: update rust to 1.80.1 (#19087) --- .cargo/{config => config.toml} | 5 + .github/workflows/external.yml | 3 +- .github/workflows/rust.yml | 3 +- Cargo.lock | 291 +++++++++--------- Cargo.toml | 18 +- consensus/config/Cargo.toml | 3 + consensus/core/Cargo.toml | 3 + consensus/core/src/authority_service.rs | 8 +- consensus/core/src/block_verifier.rs | 1 + consensus/core/src/commit_observer.rs | 8 +- consensus/core/src/commit_syncer.rs | 2 +- consensus/core/src/dag_state.rs | 4 +- consensus/core/src/leader_scoring.rs | 2 +- consensus/core/src/leader_scoring_strategy.rs | 1 + consensus/core/src/network/network_tests.rs | 12 +- consensus/core/src/stake_aggregator.rs | 1 + consensus/core/src/storage/mem_store.rs | 2 + consensus/core/src/storage/mod.rs | 1 + consensus/core/src/subscriber.rs | 6 +- consensus/core/src/synchronizer.rs | 4 +- consensus/core/src/test_dag_parser.rs | 2 +- consensus/core/src/transaction.rs | 1 + crates/mysten-metrics/Cargo.toml | 3 + crates/mysten-util-mem/src/malloc_size.rs | 65 ---- crates/mysten-util-mem/tests/derive.rs | 21 -- crates/sui-archival/src/lib.rs | 1 + crates/sui-aws-orchestrator/src/settings.rs | 1 - crates/sui-benchmark/Cargo.toml | 3 + .../src/embedded_reconfig_observer.rs | 1 + crates/sui-bridge-cli/src/lib.rs | 2 +- crates/sui-bridge/src/types.rs | 2 +- crates/sui-config/Cargo.toml | 3 + crates/sui-config/src/node.rs | 1 - crates/sui-config/src/p2p.rs | 1 + crates/sui-core/Cargo.toml | 3 + crates/sui-core/src/authority.rs | 5 +- .../authority/authority_per_epoch_store.rs | 18 +- .../src/authority/authority_store_pruner.rs | 4 + .../src/authority/authority_store_tables.rs | 5 +- .../shared_object_congestion_tracker.rs | 4 +- .../src/authority/test_authority_builder.rs | 5 +- crates/sui-core/src/authority_aggregator.rs | 1 + .../checkpoints/checkpoint_executor/mod.rs | 2 +- crates/sui-core/src/consensus_adapter.rs | 4 +- crates/sui-core/src/rest_index.rs | 4 +- crates/sui-core/src/transaction_manager.rs | 5 +- .../unit_tests/authority_aggregator_tests.rs | 2 +- .../src/unit_tests/execution_driver_tests.rs | 2 +- crates/sui-e2e-tests/Cargo.toml | 5 +- .../tests/dynamic_committee_tests.rs | 3 +- crates/sui-faucet/src/faucet/simple_faucet.rs | 5 +- crates/sui-framework-tests/Cargo.toml | 3 + crates/sui-framework/src/lib.rs | 4 +- crates/sui-genesis-builder/Cargo.toml | 3 + crates/sui-genesis-builder/src/lib.rs | 8 +- crates/sui-graphql-e2e-tests/Cargo.toml | 3 + crates/sui-graphql-rpc/src/server/version.rs | 1 + crates/sui-graphql-rpc/src/types/event.rs | 14 - .../sui-graphql-rpc/src/types/move_object.rs | 1 + crates/sui-graphql-rpc/src/types/object.rs | 5 +- crates/sui-graphql-rpc/src/types/owner.rs | 1 + .../src/types/transaction_block/tx_lookups.rs | 4 +- crates/sui-indexer/src/store/mod.rs | 7 +- .../sui-indexer/src/store/pg_indexer_store.rs | 1 + crates/sui-json-rpc-tests/Cargo.toml | 3 + .../sui-json-rpc-types/src/sui_transaction.rs | 2 +- crates/sui-json/src/lib.rs | 2 +- crates/sui-keys/src/keypair_file.rs | 1 + crates/sui-macros/Cargo.toml | 3 + crates/sui-macros/src/lib.rs | 4 +- crates/sui-move/src/unit_test.rs | 29 +- crates/sui-network/src/state_sync/server.rs | 2 +- crates/sui-node/Cargo.toml | 3 + crates/sui-node/src/admin.rs | 4 +- crates/sui-open-rpc-macros/src/lib.rs | 15 - crates/sui-open-rpc/Cargo.toml | 3 + crates/sui-open-rpc/src/examples.rs | 5 - crates/sui-package-resolver/src/lib.rs | 5 +- crates/sui-proc-macros/Cargo.toml | 3 + crates/sui-protocol-config/Cargo.toml | 3 + crates/sui-proxy/src/consumer.rs | 7 +- crates/sui-replay/src/lib.rs | 5 +- crates/sui-rest-api/src/reader.rs | 1 + crates/sui-rest-api/src/response.rs | 4 +- crates/sui-rosetta/src/construction.rs | 2 +- crates/sui-rosetta/tests/gas_budget_tests.rs | 1 + .../src/payload/query_transactions.rs | 2 +- .../sui-rpc-loadgen/src/payload/validation.rs | 2 +- crates/sui-sdk/src/lib.rs | 4 +- crates/sui-simulator/Cargo.toml | 3 + crates/sui-simulator/src/lib.rs | 2 +- crates/sui-snapshot/src/lib.rs | 1 + crates/sui-storage/Cargo.toml | 3 + .../sui-storage/src/http_key_value_store.rs | 9 +- crates/sui-swarm-config/Cargo.toml | 3 + crates/sui-swarm/Cargo.toml | 3 + crates/sui-swarm/src/memory/container.rs | 2 +- crates/sui-tool/src/commands.rs | 54 ---- crates/sui-tool/src/lib.rs | 37 --- .../sui-transactional-test-runner/Cargo.toml | 3 + .../src/simulator_persisted_store.rs | 16 +- crates/sui-types/Cargo.toml | 3 + crates/sui-types/src/crypto.rs | 4 +- crates/sui-types/src/execution.rs | 19 +- .../sui-types/src/full_checkpoint_content.rs | 4 +- crates/sui-types/src/messages_grpc.rs | 1 + crates/sui-types/tests/serde_tests.rs | 2 +- crates/sui/Cargo.toml | 3 + crates/sui/src/client_commands.rs | 1 + crates/sui/src/client_ptb/lexer.rs | 12 +- crates/sui/tests/cli_tests.rs | 6 +- crates/suiop-cli/src/command.rs | 6 +- crates/telemetry-subscribers/src/lib.rs | 2 +- .../src/span_latency_prom.rs | 1 + crates/test-cluster/Cargo.toml | 3 + crates/transaction-fuzzer/Cargo.toml | 3 + .../src/programmable_transaction_gen.rs | 2 +- crates/typed-store-derive/src/lib.rs | 2 +- crates/typed-store/src/lib.rs | 11 +- crates/typed-store/src/rocks/mod.rs | 8 +- crates/typed-store/tests/macro_tests.rs | 16 +- deny.toml | 56 +--- docker/deterministic-canary/Dockerfile | 2 +- docker/sui-bridge-indexer/Dockerfile | 2 +- docker/sui-graphql-rpc/Dockerfile | 2 +- docker/sui-indexer-tidb/Dockerfile | 2 +- docker/sui-indexer/Dockerfile | 2 +- docker/sui-node/Dockerfile | 2 +- docker/sui-services/Dockerfile | 2 +- docker/sui-source-service/Dockerfile | 2 +- docker/sui-tools/Dockerfile | 2 +- external-crates/move/Cargo.toml | 2 +- .../move-binary-format/src/file_format.rs | 14 +- .../src/file_format_common.rs | 2 +- .../src/proptest_types/functions.rs | 14 +- .../src/proptest_types/types.rs | 3 - .../move-binary-format/src/serializer.rs | 8 +- .../src/unit_tests/number_tests.rs | 4 +- .../src/control_flow_v5.rs | 2 +- .../src/script_signature.rs | 1 + .../move-command-line-common/src/files.rs | 4 +- .../crates/move-compiler/src/cfgir/cfg.rs | 1 + .../move-compiler/src/naming/translate.rs | 6 +- .../src/to_bytecode/canonicalize_handles.rs | 6 +- .../src/typing/deprecation_warnings.rs | 1 + .../crates/move-compiler/src/typing/expand.rs | 10 +- .../src/unit_test/plan_builder.rs | 2 +- .../move/crates/move-core-types/src/state.rs | 2 +- .../move/crates/move-core-types/src/u256.rs | 1 - .../crates/move-ir-to-bytecode/src/context.rs | 2 +- .../move/crates/move-model/src/ty.rs | 28 +- .../src/access_path.rs | 1 + .../src/inconsistency_check.rs | 1 + .../src/packed_types_analysis.rs | 1 + .../crates/move-stdlib-natives/src/debug.rs | 2 +- .../crates/move-vm-runtime/src/tracing.rs | 1 - .../move-vm-test-utils/src/gas_schedule.rs | 5 +- .../move-vm-types/src/values/values_impl.rs | 30 +- .../test-generation/src/abstract_state.rs | 4 +- .../test-generation/src/borrow_graph.rs | 2 +- .../test-generation/src/bytecode_generator.rs | 18 +- .../test-generation/src/control_flow_graph.rs | 12 +- .../move/crates/test-generation/src/lib.rs | 4 +- .../tests/generic_instructions.rs | 2 +- .../src/control_flow_v5.rs | 2 +- .../src/script_signature.rs | 1 + .../v0/crates/move-vm-runtime/src/tracing.rs | 1 - .../src/control_flow_v5.rs | 2 +- .../src/script_signature.rs | 1 + .../v1/crates/move-vm-runtime/src/tracing.rs | 1 - .../src/control_flow_v5.rs | 2 +- .../src/script_signature.rs | 1 + .../v2/crates/move-vm-runtime/src/tracing.rs | 1 - narwhal/config/src/lib.rs | 6 +- narwhal/node/src/benchmark_client.rs | 1 + narwhal/node/src/main.rs | 2 +- narwhal/primary/src/consensus/metrics.rs | 6 +- narwhal/primary/src/synchronizer.rs | 5 +- narwhal/primary/tests/randomized_tests.rs | 2 +- narwhal/storage/src/certificate_store.rs | 90 +++--- narwhal/test-utils/src/cluster.rs | 1 + narwhal/types/src/primary.rs | 1 + narwhal/worker/Cargo.toml | 3 + narwhal/worker/src/handlers.rs | 2 + rust-toolchain.toml | 2 +- sui-execution/latest/sui-adapter/Cargo.toml | 3 + .../sui-move-natives/src/crypto/group_ops.rs | 4 +- .../sui-verifier/src/entry_points_verifier.rs | 4 +- sui-execution/v0/sui-adapter/Cargo.toml | 3 + sui-execution/v0/sui-verifier/Cargo.toml | 3 + .../sui-verifier/src/entry_points_verifier.rs | 2 +- sui-execution/v1/sui-adapter/Cargo.toml | 3 + .../sui-verifier/src/entry_points_verifier.rs | 2 +- sui-execution/v2/sui-adapter/Cargo.toml | 3 + .../sui-move-natives/src/crypto/group_ops.rs | 4 +- .../sui-verifier/src/entry_points_verifier.rs | 2 +- 196 files changed, 620 insertions(+), 793 deletions(-) rename .cargo/{config => config.toml} (86%) diff --git a/.cargo/config b/.cargo/config.toml similarity index 86% rename from .cargo/config rename to .cargo/config.toml index b6879bf01e5f0..47c98de4efc2a 100644 --- a/.cargo/config +++ b/.cargo/config.toml @@ -7,6 +7,7 @@ xclippy = [ "clippy", "--all-targets", "--all-features", "--", "-Wclippy::all", "-Wclippy::disallowed_methods", + "-Aclippy::unnecessary_get_then_check", ] xlint = "run --package x --bin x -- lint" xtest = "run --package x --bin x -- external-crates-tests" @@ -22,6 +23,9 @@ move-clippy = [ "-Aclippy::upper_case_acronyms", "-Aclippy::type_complexity", "-Aclippy::new_without_default", + "-Aclippy::question_mark", + "-Aclippy::unnecessary_get_then_check", + "-Aclippy::needless_borrows_for_generic_args", ] mysql-clippy = [ @@ -38,6 +42,7 @@ mysql-clippy = [ "-Aclippy::upper_case_acronyms", "-Aclippy::type_complexity", "-Aclippy::new_without_default", + "-Aclippy::unnecessary_get_then_check", ] [build] diff --git a/.github/workflows/external.yml b/.github/workflows/external.yml index 2ded81bc787b3..40faac1020070 100644 --- a/.github/workflows/external.yml +++ b/.github/workflows/external.yml @@ -127,4 +127,5 @@ jobs: runs-on: [ubuntu-latest] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # Pin v4.1.1 - - uses: mystenlabs/cargo-deny-action@main + - uses: taiki-e/install-action@cargo-deny + - run: cargo deny check diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 68ff61c03e218..b549b5639e485 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -375,7 +375,8 @@ jobs: runs-on: [ ubuntu-latest ] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # Pin v4.1.1 - - uses: mystenlabs/cargo-deny-action@main + - uses: taiki-e/install-action@cargo-deny + - run: cargo deny check sui-excution-cut: name: cutting a new execution layer diff --git a/Cargo.lock b/Cargo.lock index 7d0bedcbdc523..9cec723dbb5e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,18 +100,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - -[[package]] -name = "aho-corasick" -version = "1.0.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -257,15 +248,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anstream" version = "0.6.13" @@ -316,9 +298,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" dependencies = [ "backtrace", ] @@ -1064,12 +1046,13 @@ checksum = "62af46d040ba9df09edc6528dae9d8e49f5f3e82f55b7d2ec31a733c38dbc49d" [[package]] name = "atomicwrites" -version = "0.3.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb8f2cd6962fa53c0e2a9d3f97eaa7dbd1e3cbbeeb4745403515b42ae07b3ff6" +checksum = "fc7b2dbe9169059af0f821e811180fddc971fc210c776c133c7819ccd6e478db" dependencies = [ + "rustix 0.38.28", "tempfile", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -1865,9 +1848,9 @@ dependencies = [ [[package]] name = "bimap" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0455254eb5c6964c4545d8bac815e1a1be4f3afe0ae695ea539c12d728d44b" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" [[package]] name = "bin-version" @@ -2172,13 +2155,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.1.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", - "once_cell", - "regex-automata 0.1.10", + "regex-automata 0.4.7", "serde", ] @@ -2301,9 +2283,9 @@ checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" [[package]] name = "camino" -version = "1.1.2" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77df041dc383319cc661b428b6961a005db4d6808d5e12536931b1ca9556055" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" dependencies = [ "serde", ] @@ -2325,7 +2307,7 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver 1.0.16", + "semver 1.0.23", "serde", "serde_json", ] @@ -2338,7 +2320,7 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver 1.0.16", + "semver 1.0.23", "serde", "serde_json", "thiserror", @@ -2352,7 +2334,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver 1.0.16", + "semver 1.0.23", "serde", "serde_json", "thiserror", @@ -2400,9 +2382,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.13.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a327683d7499ecc47369531a679fe38acdd300e09bf8c852d08b1e10558622bd" +checksum = "345c78335be0624ed29012dc10c49102196c6882c12dde65d9f35b02da2aada8" dependencies = [ "smallvec", "target-lexicon", @@ -2663,17 +2645,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "colored-diff" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410208eb08c3f3ad44b95b51c4fc0d5993cbcc9dd39cfadb4214b9115a97dcb5" -dependencies = [ - "ansi_term", - "dissimilar", - "itertools 0.10.5", -] - [[package]] name = "combine" version = "4.6.6" @@ -3647,26 +3618,26 @@ dependencies = [ [[package]] name = "determinator" -version = "0.10.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c644b91adb5bcc66d3533607b6d3ee5c1c2d858d2d95e41dd6aae673e29e0509" +checksum = "bf14b901cdfba3f731d01c4c184100e85f586a272d38874824175b845dbaeaf9" dependencies = [ "camino", "globset", "guppy", "guppy-workspace-hack", "once_cell", - "petgraph 0.6.2", + "petgraph 0.6.5", "rayon", "serde", - "toml 0.5.10", + "toml 0.5.11", ] [[package]] name = "diesel" -version = "2.1.4" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62c6fcf842f17f8c78ecf7c81d75c5ce84436b41ee07e03f490fbb5f5a8731d8" +checksum = "65e13bab2796f412722112327f3e575601a3e9cdcbe426f0d30dbf43f3f5dc71" dependencies = [ "bitflags 2.4.1", "byteorder", @@ -3695,11 +3666,12 @@ dependencies = [ [[package]] name = "diesel_derives" -version = "2.1.2" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8337737574f55a468005a83499da720f20c65586241ffea339db9ecdfd2b44" +checksum = "e7f2c3de51e2ba6bf2a648285696137aaf0f5f487bcbea93972fe8a364e131a4" dependencies = [ "diesel_table_macro_syntax", + "dsl_auto_type", "proc-macro2 1.0.78", "quote 1.0.35", "syn 2.0.48", @@ -3707,9 +3679,9 @@ dependencies = [ [[package]] name = "diesel_migrations" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" +checksum = "8a73ce704bad4231f001bff3314d91dce4aba0770cee8b233991859abc15c1f6" dependencies = [ "diesel", "migrations_internals", @@ -3718,9 +3690,9 @@ dependencies = [ [[package]] name = "diesel_table_macro_syntax" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" +checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" dependencies = [ "syn 2.0.48", ] @@ -3922,6 +3894,20 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "dsl_auto_type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607" +dependencies = [ + "darling 0.20.3", + "either", + "heck 0.5.0", + "proc-macro2 1.0.78", + "quote 1.0.35", + "syn 2.0.48", +] + [[package]] name = "dunce" version = "1.0.4" @@ -4441,7 +4427,7 @@ dependencies = [ "chrono", "ethers-core", "reqwest 0.11.20", - "semver 1.0.16", + "semver 1.0.23", "serde", "serde_json", "thiserror", @@ -4551,7 +4537,7 @@ dependencies = [ "path-slash", "rayon", "regex", - "semver 1.0.16", + "semver 1.0.23", "serde", "serde_json", "solang-parser", @@ -4734,7 +4720,7 @@ dependencies = [ "fastcrypto", "ff 0.13.0", "im", - "itertools 0.12.0", + "itertools 0.12.1", "lazy_static", "neptune", "num-bigint 0.4.4", @@ -5224,15 +5210,15 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.10" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" dependencies = [ - "aho-corasick 0.7.20", + "aho-corasick", "bstr", - "fnv", "log", - "regex", + "regex-automata 0.4.7", + "regex-syntax 0.8.2", ] [[package]] @@ -5291,31 +5277,32 @@ dependencies = [ [[package]] name = "guppy" -version = "0.15.2" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f822a2716041492e071691606474f5a7e4fa7c2acbfd7da7b29884fb448291c7" +checksum = "3bff2f6a9d515cf6453282af93363f93bdf570792a6f4f619756e46696d773fa" dependencies = [ + "ahash 0.8.11", "camino", - "cargo_metadata 0.15.4", + "cargo_metadata 0.18.1", "cfg-if", "debug-ignore", "fixedbitset 0.4.2", "guppy-summaries", "guppy-workspace-hack", - "indexmap 1.9.3", - "itertools 0.10.5", + "indexmap 2.2.6", + "itertools 0.13.0", "nested", "once_cell", "pathdiff", - "petgraph 0.6.2", + "petgraph 0.6.5", "rayon", - "semver 1.0.16", + "semver 1.0.23", "serde", "serde_json", "smallvec", "static_assertions", "target-spec", - "toml 0.5.10", + "toml 0.5.11", ] [[package]] @@ -5328,9 +5315,9 @@ dependencies = [ "cfg-if", "diffus", "guppy-workspace-hack", - "semver 1.0.16", + "semver 1.0.23", "serde", - "toml 0.5.10", + "toml 0.5.11", ] [[package]] @@ -5379,10 +5366,11 @@ dependencies = [ [[package]] name = "hakari" -version = "0.13.1" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2c76369039f6ac178748e96def487662aebbeb26d43070d01a0eadd53964994" +checksum = "12bd2b14c094d2793daf279eb7624f4525e26f555fbc1647613756cf83f44755" dependencies = [ + "ahash 0.8.11", "atomicwrites", "bimap", "camino", @@ -5393,15 +5381,15 @@ dependencies = [ "guppy-workspace-hack", "include_dir", "indenter", - "itertools 0.10.5", + "itertools 0.12.1", "owo-colors 3.5.0", "pathdiff", "rayon", "serde", "tabular", "target-spec", - "toml 0.5.10", - "toml_edit 0.15.0", + "toml 0.5.11", + "toml_edit 0.17.1", "twox-hash", ] @@ -6174,9 +6162,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] @@ -6479,7 +6467,7 @@ dependencies = [ "ena", "itertools 0.11.0", "lalrpop-util", - "petgraph 0.6.2", + "petgraph 0.6.5", "regex", "regex-syntax 0.8.2", "string_cache", @@ -6924,19 +6912,19 @@ dependencies = [ [[package]] name = "migrations_internals" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" +checksum = "fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff" dependencies = [ "serde", - "toml 0.7.4", + "toml 0.8.16", ] [[package]] name = "migrations_macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" +checksum = "ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd" dependencies = [ "migrations_internals", "proc-macro2 1.0.78", @@ -7481,7 +7469,7 @@ dependencies = [ "serde_yaml 0.8.26", "sha2 0.9.9", "tempfile", - "toml 0.5.10", + "toml 0.5.11", "toml_edit 0.14.4", "treeline", "vfs", @@ -7515,7 +7503,7 @@ dependencies = [ "once_cell", "serde", "simplelog", - "toml 0.5.10", + "toml 0.5.11", ] [[package]] @@ -7822,7 +7810,7 @@ dependencies = [ "socket2 0.4.9", "tap", "tokio-util 0.7.10 (git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e47aafebf98e9c1734a8848a1876d5946c44bdd1)", - "toml 0.5.10", + "toml 0.5.11", "tracing", "tracing-subscriber", ] @@ -8417,7 +8405,7 @@ dependencies = [ [[package]] name = "nexlint" version = "0.1.0" -source = "git+https://github.com/nextest-rs/nexlint.git?rev=94da5c787636dad779c340affa65219134d127f5#94da5c787636dad779c340affa65219134d127f5" +source = "git+https://github.com/nextest-rs/nexlint.git?rev=7ce56bd591242a57660ed05f14ca2483c37d895b#7ce56bd591242a57660ed05f14ca2483c37d895b" dependencies = [ "camino", "debug-ignore", @@ -8432,17 +8420,17 @@ dependencies = [ [[package]] name = "nexlint-lints" version = "0.1.0" -source = "git+https://github.com/nextest-rs/nexlint.git?rev=94da5c787636dad779c340affa65219134d127f5#94da5c787636dad779c340affa65219134d127f5" +source = "git+https://github.com/nextest-rs/nexlint.git?rev=7ce56bd591242a57660ed05f14ca2483c37d895b#7ce56bd591242a57660ed05f14ca2483c37d895b" dependencies = [ "anyhow", "camino", - "colored-diff", + "diffy", "globset", "guppy", "nexlint", "regex", "serde", - "toml 0.5.10", + "toml 0.5.11", ] [[package]] @@ -8469,9 +8457,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.24.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", "cfg-if", @@ -8494,6 +8482,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + [[package]] name = "nonempty" version = "0.9.0" @@ -9091,7 +9088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd" dependencies = [ "heck 0.4.1", - "itertools 0.12.0", + "itertools 0.12.1", "proc-macro2 1.0.78", "proc-macro2-diagnostics", "quote 1.0.35", @@ -9530,12 +9527,12 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.2" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset 0.4.2", - "indexmap 1.9.3", + "indexmap 2.2.6", ] [[package]] @@ -9745,16 +9742,16 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "pprof" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e20150f965e0e4c925982b9356da71c84bcd56cb66ef4e894825837cbcf6613e" +checksum = "ef5c97c51bd34c7e742402e216abdeb44d415fbe6ae41d56b114723e953711cb" dependencies = [ "backtrace", "cfg-if", "findshlibs", "libc", "log", - "nix 0.24.3", + "nix 0.26.4", "once_cell", "parking_lot 0.12.1", "smallvec", @@ -9892,7 +9889,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" dependencies = [ "thiserror", - "toml 0.5.10", + "toml 0.5.11", ] [[package]] @@ -10079,7 +10076,7 @@ dependencies = [ "log", "multimap", "once_cell", - "petgraph 0.6.2", + "petgraph 0.6.5", "prettyplease", "prost 0.13.1", "prost-types 0.13.1", @@ -10540,7 +10537,7 @@ version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ - "aho-corasick 1.0.2", + "aho-corasick", "memchr", "regex-automata 0.4.7", "regex-syntax 0.8.2", @@ -10561,7 +10558,7 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ - "aho-corasick 1.0.2", + "aho-corasick", "memchr", "regex-syntax 0.8.2", ] @@ -11103,7 +11100,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.16", + "semver 1.0.23", ] [[package]] @@ -11540,9 +11537,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.16" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] @@ -11576,9 +11573,9 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" dependencies = [ "serde_derive", ] @@ -11615,9 +11612,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2 1.0.78", "quote 1.0.35", @@ -15225,7 +15222,7 @@ dependencies = [ "rand 0.8.5", "regex", "reqwest 0.12.5", - "semver 1.0.16", + "semver 1.0.23", "serde", "serde_json", "serde_yaml 0.8.26", @@ -15272,7 +15269,7 @@ dependencies = [ "hex", "once_cell", "reqwest 0.11.20", - "semver 1.0.16", + "semver 1.0.23", "serde", "serde_json", "sha2 0.10.8", @@ -15283,9 +15280,9 @@ dependencies = [ [[package]] name = "symbolic-common" -version = "10.2.1" +version = "12.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b55cdc318ede251d0957f07afe5fed912119b8c1bc5a7804151826db999e737" +checksum = "26212dc7aeb75abb4cc84320a50dd482977089402f7b4043b454d6d79d8536e7" dependencies = [ "debugid", "memmap2", @@ -15295,9 +15292,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "10.2.1" +version = "12.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79be897be8a483a81fff6a3a4e195b4ac838ef73ca42d348b3f722da9902e489" +checksum = "76f1b0155f588568b2df0d693b30aeedb59360b647b85fc3c23942e81e8cc97a" dependencies = [ "cpp_demangle", "rustc-demangle", @@ -15435,20 +15432,21 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.5" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "target-spec" -version = "1.3.1" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb0303f2cecb4171c439135b28c33fe9da7b9fd7816fe674761834da70c9778" +checksum = "419ccf3482090c626619fa2574290aaa00b696f9ab73af08fbf48260565431bf" dependencies = [ "cfg-expr", "guppy-workspace-hack", "serde", "target-lexicon", + "unicode-ident", ] [[package]] @@ -15948,9 +15946,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "indexmap 1.9.3", "serde", @@ -16010,13 +16008,13 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.15.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1541ba70885967e662f69d31ab3aeca7b1aaecfcd58679590b893e9239c3646" +checksum = "a34cc558345efd7e88b9eda9626df2138b80bb46a7606f695e751c892bc7dac6" dependencies = [ - "combine", "indexmap 1.9.3", "itertools 0.10.5", + "nom8", "toml_datetime 0.5.1", ] @@ -16622,9 +16620,9 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-linebreak" @@ -16902,19 +16900,20 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", @@ -16939,9 +16938,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote 1.0.35", "wasm-bindgen-macro-support", @@ -16949,9 +16948,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2 1.0.78", "quote 1.0.35", @@ -16962,9 +16961,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-streams" diff --git a/Cargo.toml b/Cargo.toml index 35176bf63b231..2d175748d0d4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -237,6 +237,9 @@ overflow-checks = true # opt-level 1 gives >5x speedup for simulator tests without slowing down build times very much. opt-level = 1 +[workspace.lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(msim)', 'cfg(fail_points)'] } + # Dependencies that should be kept in sync through the whole workspace [workspace.dependencies] anyhow = "1.0.71" @@ -345,7 +348,6 @@ hashbrown = "0.12" hdrhistogram = "7.5.1" hex = "0.4.3" hex-literal = "0.3.4" -highlight = "all" http = "1" http-body = "1" humantime = "2.1.0" @@ -384,8 +386,8 @@ more-asserts = "0.3.1" msim = { git = "https://github.com/MystenLabs/mysten-sim.git", rev = "b320996d8dfb99b273fe31c0222c659332283c99", package = "msim" } msim-macros = { git = "https://github.com/MystenLabs/mysten-sim.git", rev = "b320996d8dfb99b273fe31c0222c659332283c99", package = "msim-macros" } multiaddr = "0.17.0" -nexlint = { git = "https://github.com/nextest-rs/nexlint.git", rev = "94da5c787636dad779c340affa65219134d127f5" } -nexlint-lints = { git = "https://github.com/nextest-rs/nexlint.git", rev = "94da5c787636dad779c340affa65219134d127f5" } +nexlint = { git = "https://github.com/nextest-rs/nexlint.git", rev = "7ce56bd591242a57660ed05f14ca2483c37d895b" } +nexlint-lints = { git = "https://github.com/nextest-rs/nexlint.git", rev = "7ce56bd591242a57660ed05f14ca2483c37d895b" } nonempty = "0.9.0" notify = "6.1.1" ntest = "0.9.0" @@ -398,12 +400,12 @@ ouroboros = "0.17" parking_lot = "0.12.1" parquet = "52" pkcs8 = { version = "0.9.0", features = ["std"] } -pprof = { version = "0.11.0", features = ["cpp", "frame-pointer"] } +pprof = { version = "0.13.0", features = ["cpp", "frame-pointer"] } pretty_assertions = "1.3.0" prettytable-rs = "0.10.0" proc-macro2 = "1.0.47" prometheus = "0.13.3" -prometheus-http-query = { version = "0.8", default_features = false, features = [ +prometheus-http-query = { version = "0.8", default-features = false, features = [ "rustls-tls", ] } prometheus-parse = { git = "https://github.com/asonnino/prometheus-parser.git", rev = "75334db" } @@ -418,7 +420,7 @@ rand = "0.8.5" rayon = "1.5.3" rcgen = "0.13" regex = "1.7.1" -reqwest = { version = "0.12", default_features = false, features = [ +reqwest = { version = "0.12", default-features = false, features = [ "http2", "json", "rustls-tls", @@ -426,10 +428,10 @@ reqwest = { version = "0.12", default_features = false, features = [ roaring = "0.10.6" ron = "0.8.0" rstest = "0.16.0" -rusoto_core = { version = "0.48.0", default_features = false, features = [ +rusoto_core = { version = "0.48.0", default-features = false, features = [ "rustls", ] } -rusoto_kms = { version = "0.48.0", default_features = false, features = [ +rusoto_kms = { version = "0.48.0", default-features = false, features = [ "rustls", ] } russh = "0.38.0" diff --git a/consensus/config/Cargo.toml b/consensus/config/Cargo.toml index aeb3978f12301..48697e2f7e91b 100644 --- a/consensus/config/Cargo.toml +++ b/consensus/config/Cargo.toml @@ -6,6 +6,9 @@ authors = ["Mysten Labs "] edition = "2021" publish = false +[lints] +workspace = true + [dependencies] fastcrypto.workspace = true mysten-network.workspace = true diff --git a/consensus/core/Cargo.toml b/consensus/core/Cargo.toml index 067901f002a16..2cb277834c9b2 100644 --- a/consensus/core/Cargo.toml +++ b/consensus/core/Cargo.toml @@ -6,6 +6,9 @@ authors = ["Mysten Labs "] edition = "2021" publish = false +[lints] +workspace = true + [dependencies] anemo.workspace = true anemo-tower.workspace = true diff --git a/consensus/core/src/authority_service.rs b/consensus/core/src/authority_service.rs index c1c38f9dfbfb6..94fa311e01a6b 100644 --- a/consensus/core/src/authority_service.rs +++ b/consensus/core/src/authority_service.rs @@ -133,7 +133,7 @@ impl NetworkService for AuthorityService { .metrics .node_metrics .rejected_future_blocks - .with_label_values(&[&peer_hostname]) + .with_label_values(&[peer_hostname]) .inc(); debug!( "Block {:?} timestamp ({} > {}) is too far in the future, rejected.", @@ -157,7 +157,7 @@ impl NetworkService for AuthorityService { .metrics .node_metrics .block_timestamp_drift_wait_ms - .with_label_values(&[peer_hostname, &"handle_send_block"]) + .with_label_values(&[peer_hostname, "handle_send_block"]) .inc_by(forward_time_drift.as_millis() as u64); debug!( "Block {:?} timestamp ({} > {}) is in the future, waiting for {}ms", @@ -192,7 +192,7 @@ impl NetworkService for AuthorityService { .metrics .node_metrics .rejected_blocks - .with_label_values(&[&"commit_lagging"]) + .with_label_values(&["commit_lagging"]) .inc(); debug!( "Block {:?} is rejected because last commit index is lagging quorum commit index too much ({} < {})", @@ -213,7 +213,7 @@ impl NetworkService for AuthorityService { .metrics .node_metrics .verified_blocks - .with_label_values(&[&peer_hostname]) + .with_label_values(&[peer_hostname]) .inc(); let missing_ancestors = self diff --git a/consensus/core/src/block_verifier.rs b/consensus/core/src/block_verifier.rs index 4d68f476455fe..2e876e9130f0b 100644 --- a/consensus/core/src/block_verifier.rs +++ b/consensus/core/src/block_verifier.rs @@ -206,6 +206,7 @@ impl BlockVerifier for SignedBlockVerifier { } } +#[allow(unused)] pub(crate) struct NoopBlockVerifier; impl BlockVerifier for NoopBlockVerifier { diff --git a/consensus/core/src/commit_observer.rs b/consensus/core/src/commit_observer.rs index f5c996687c029..a25113de387fb 100644 --- a/consensus/core/src/commit_observer.rs +++ b/consensus/core/src/commit_observer.rs @@ -23,14 +23,16 @@ use crate::{ /// Role of CommitObserver /// - Called by core when try_commit() returns newly committed leaders. /// - The newly committed leaders are sent to commit observer and then commit observer -/// gets subdags for each leader via the commit interpreter (linearizer) +/// gets subdags for each leader via the commit interpreter (linearizer) /// - The committed subdags are sent as consensus output via an unbounded tokio channel. +/// /// No back pressure mechanism is needed as backpressure is handled as input into /// consenus. +/// /// - Commit metadata including index is persisted in store, before the CommittedSubDag -/// is sent to the consumer. +/// is sent to the consumer. /// - When CommitObserver is initialized a last processed commit index can be used -/// to ensure any missing commits are re-sent. +/// to ensure any missing commits are re-sent. pub(crate) struct CommitObserver { context: Arc, /// Component to deterministically collect subdags for committed leaders. diff --git a/consensus/core/src/commit_syncer.rs b/consensus/core/src/commit_syncer.rs index 3594548eaa95e..f4ecf4fa2f951 100644 --- a/consensus/core/src/commit_syncer.rs +++ b/consensus/core/src/commit_syncer.rs @@ -567,7 +567,7 @@ impl CommitSyncer { .metrics .node_metrics .block_timestamp_drift_wait_ms - .with_label_values(&[peer_hostname, &"commit_syncer"]) + .with_label_values(&[peer_hostname, "commit_syncer"]) .inc_by(forward_drift); let forward_drift = Duration::from_millis(forward_drift); if forward_drift >= inner.context.parameters.max_forward_time_drift { diff --git a/consensus/core/src/dag_state.rs b/consensus/core/src/dag_state.rs index 5ebac04a585a1..aa7f3ca718f28 100644 --- a/consensus/core/src/dag_state.rs +++ b/consensus/core/src/dag_state.rs @@ -307,7 +307,7 @@ impl DagState { .metrics .node_metrics .dag_state_store_read_count - .with_label_values(&[&"get_blocks"]) + .with_label_values(&["get_blocks"]) .inc(); for ((index, _), result) in missing.into_iter().zip(store_results.into_iter()) { @@ -556,7 +556,7 @@ impl DagState { .metrics .node_metrics .dag_state_store_read_count - .with_label_values(&[&"contains_blocks"]) + .with_label_values(&["contains_blocks"]) .inc(); for ((index, _), result) in missing.into_iter().zip(store_results.into_iter()) { diff --git a/consensus/core/src/leader_scoring.rs b/consensus/core/src/leader_scoring.rs index eaafbcc439e00..380003debd37d 100644 --- a/consensus/core/src/leader_scoring.rs +++ b/consensus/core/src/leader_scoring.rs @@ -41,7 +41,7 @@ pub(crate) struct ReputationScoreCalculator<'a> { impl<'a> ReputationScoreCalculator<'a> { pub(crate) fn new( context: Arc, - unscored_subdags: &Vec, + unscored_subdags: &[CommittedSubDag], scoring_strategy: &'a dyn ScoringStrategy, ) -> Self { let num_authorities = context.committee.size(); diff --git a/consensus/core/src/leader_scoring_strategy.rs b/consensus/core/src/leader_scoring_strategy.rs index b635f5f474cd0..640e3667cd71c 100644 --- a/consensus/core/src/leader_scoring_strategy.rs +++ b/consensus/core/src/leader_scoring_strategy.rs @@ -10,6 +10,7 @@ use crate::{ stake_aggregator::{QuorumThreshold, StakeAggregator}, }; +#[allow(unused)] pub(crate) trait ScoringStrategy: Send + Sync { fn calculate_scores_for_leader(&self, subdag: &UnscoredSubdag, leader_slot: Slot) -> Vec; diff --git a/consensus/core/src/network/network_tests.rs b/consensus/core/src/network/network_tests.rs index d8113befb6b3a..cef1ff8530c54 100644 --- a/consensus/core/src/network/network_tests.rs +++ b/consensus/core/src/network/network_tests.rs @@ -31,7 +31,11 @@ trait ManagerBuilder { struct AnemoManagerBuilder {} impl ManagerBuilder for AnemoManagerBuilder { - fn build(&self, context: Arc, network_keypair: NetworkKeyPair) -> AnemoManager { + fn build( + &self, + context: Arc, + network_keypair: NetworkKeyPair, + ) -> impl NetworkManager> { AnemoManager::new(context, network_keypair) } } @@ -39,7 +43,11 @@ impl ManagerBuilder for AnemoManagerBuilder { struct TonicManagerBuilder {} impl ManagerBuilder for TonicManagerBuilder { - fn build(&self, context: Arc, network_keypair: NetworkKeyPair) -> TonicManager { + fn build( + &self, + context: Arc, + network_keypair: NetworkKeyPair, + ) -> impl NetworkManager> { TonicManager::new(context, network_keypair) } } diff --git a/consensus/core/src/stake_aggregator.rs b/consensus/core/src/stake_aggregator.rs index 7e907dee1fcb8..11501319e64e9 100644 --- a/consensus/core/src/stake_aggregator.rs +++ b/consensus/core/src/stake_aggregator.rs @@ -11,6 +11,7 @@ pub(crate) trait CommitteeThreshold { pub(crate) struct QuorumThreshold; +#[allow(unused)] pub(crate) struct ValidityThreshold; impl CommitteeThreshold for QuorumThreshold { diff --git a/consensus/core/src/storage/mem_store.rs b/consensus/core/src/storage/mem_store.rs index 2a88fc5bd71ad..ad23cd3652cee 100644 --- a/consensus/core/src/storage/mem_store.rs +++ b/consensus/core/src/storage/mem_store.rs @@ -20,10 +20,12 @@ use crate::{ }; /// In-memory storage for testing. +#[allow(unused)] pub(crate) struct MemStore { inner: RwLock, } +#[allow(unused)] struct Inner { blocks: BTreeMap<(Round, AuthorityIndex, BlockDigest), VerifiedBlock>, digests_by_authorities: BTreeSet<(AuthorityIndex, Round, BlockDigest)>, diff --git a/consensus/core/src/storage/mod.rs b/consensus/core/src/storage/mod.rs index 5c524be30318e..38258c87e8420 100644 --- a/consensus/core/src/storage/mod.rs +++ b/consensus/core/src/storage/mod.rs @@ -17,6 +17,7 @@ use crate::{ }; /// A common interface for consensus storage. +#[allow(unused)] pub(crate) trait Store: Send + Sync { /// Writes blocks, consensus commits and other data to store atomically. fn write(&self, write_batch: WriteBatch) -> ConsensusResult<()>; diff --git a/consensus/core/src/subscriber.rs b/consensus/core/src/subscriber.rs index bce04247c7a1d..7b350debf313c 100644 --- a/consensus/core/src/subscriber.rs +++ b/consensus/core/src/subscriber.rs @@ -152,7 +152,7 @@ impl Subscriber { .metrics .node_metrics .subscriber_connection_attempts - .with_label_values(&[&peer_hostname, "success"]) + .with_label_values(&[peer_hostname, "success"]) .inc(); blocks } @@ -162,7 +162,7 @@ impl Subscriber { .metrics .node_metrics .subscriber_connection_attempts - .with_label_values(&[&peer_hostname, "failure"]) + .with_label_values(&[peer_hostname, "failure"]) .inc(); continue 'subscription; } @@ -184,7 +184,7 @@ impl Subscriber { .metrics .node_metrics .subscribed_blocks - .with_label_values(&[&peer_hostname]) + .with_label_values(&[peer_hostname]) .inc(); let result = authority_service .handle_send_block(peer, block.clone()) diff --git a/consensus/core/src/synchronizer.rs b/consensus/core/src/synchronizer.rs index e3bc24c2e2b07..09193b7d06742 100644 --- a/consensus/core/src/synchronizer.rs +++ b/consensus/core/src/synchronizer.rs @@ -537,13 +537,13 @@ impl Synchronizer usize; } -/// Like `MallocSizeOf`, but only measures if the value hasn't already been -/// measured. For use with types like `Rc` and `Arc` when appropriate (e.g. -/// when there is no "primary" reference). -pub trait MallocConditionalSizeOf { - /// Measure the heap usage of all heap-allocated descendant structures, but - /// not the space taken up by the value itself, and only if that heap usage - /// hasn't already been measured. - fn conditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize; -} - -/// `MallocConditionalSizeOf` combined with `MallocShallowSizeOf`. -pub trait MallocConditionalShallowSizeOf { - /// `conditional_size_of` combined with `shallow_size_of`. - fn conditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize; -} - impl<'a, T: ?Sized> MallocSizeOf for &'a T { fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { // Zero makes sense for a non-owning reference. @@ -510,11 +492,6 @@ where // impl !MallocSizeOf for Arc { } // impl !MallocShallowSizeOf for Arc { } -#[cfg(feature = "std")] -fn arc_ptr(s: &Arc) -> *const T { - &(**s) as *const T -} - #[cfg(feature = "std")] impl MallocUnconditionalSizeOf for Arc { fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { @@ -522,28 +499,6 @@ impl MallocUnconditionalSizeOf for Arc { } } -#[cfg(feature = "std")] -impl MallocConditionalShallowSizeOf for Arc { - fn conditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - if ops.have_seen_ptr(arc_ptr(self)) { - 0 - } else { - self.unconditional_shallow_size_of(ops) - } - } -} - -#[cfg(feature = "std")] -impl MallocConditionalSizeOf for Arc { - fn conditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - if ops.have_seen_ptr(arc_ptr(self)) { - 0 - } else { - self.unconditional_size_of(ops) - } - } -} - /// If a mutex is stored directly as a member of a data type that is being measured, /// it is the unique owner of its contents and deserves to be measured. /// @@ -724,26 +679,6 @@ where } } -#[cfg(feature = "lru")] -impl MallocSizeOf for lru::LruCache -where - K: MallocSizeOf + rstd::cmp::Eq + rstd::hash::Hash, - V: MallocSizeOf, - S: rstd::hash::BuildHasher, -{ - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - let mut n = 0; - if let (Some(k), Some(v)) = (K::constant_size(), V::constant_size()) { - n += self.len() * (k + v) - } else { - n = self - .iter() - .fold(n, |acc, (k, v)| acc + k.size_of(ops) + v.size_of(ops)) - } - n - } -} - malloc_size_of_is_0!( [u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], [u8; 9], [u8; 10], [u8; 11], [u8; 12], [u8; 13], [u8; 14], [u8; 15], [u8; 16], [u8; 17], [u8; 18], [u8; 19], diff --git a/crates/mysten-util-mem/tests/derive.rs b/crates/mysten-util-mem/tests/derive.rs index 0beeb6fb11c47..ed4b0196b1978 100644 --- a/crates/mysten-util-mem/tests/derive.rs +++ b/crates/mysten-util-mem/tests/derive.rs @@ -55,27 +55,6 @@ fn derive_ignore() { assert!(t.malloc_size_of() < 3000); } -#[test] -#[cfg(all(feature = "lru", feature = "hashbrown"))] -fn derive_morecomplex() { - #[derive(MallocSizeOf)] - struct Trivia { - hm: hashbrown::HashMap>, - cache: lru::LruCache>, - } - - let mut t = Trivia { - hm: hashbrown::HashMap::new(), - cache: lru::LruCache::unbounded(), - }; - - t.hm.insert(1, vec![0u8; 2048]); - t.cache.put(1, vec![0u8; 2048]); - t.cache.put(2, vec![0u8; 4096]); - - assert!(t.malloc_size_of() > 8000); -} - #[test] fn derive_tuple() { #[derive(MallocSizeOf)] diff --git a/crates/sui-archival/src/lib.rs b/crates/sui-archival/src/lib.rs index 1dbcd632c254d..bdc5b6b3ab18a 100644 --- a/crates/sui-archival/src/lib.rs +++ b/crates/sui-archival/src/lib.rs @@ -61,6 +61,7 @@ use tracing::{error, info}; /// - epoch_1/ /// - 101000.chk /// - ... +/// /// Blob File Disk Format ///┌──────────────────────────────┐ ///│ magic <4 byte> │ diff --git a/crates/sui-aws-orchestrator/src/settings.rs b/crates/sui-aws-orchestrator/src/settings.rs index 8eaca609aa3a8..be4b17d3ca10e 100644 --- a/crates/sui-aws-orchestrator/src/settings.rs +++ b/crates/sui-aws-orchestrator/src/settings.rs @@ -121,7 +121,6 @@ impl Settings { .path_segments() .expect("Url should already be checked when loading settings") .collect::>()[1] - .to_string() .split('.') .next() .unwrap() diff --git a/crates/sui-benchmark/Cargo.toml b/crates/sui-benchmark/Cargo.toml index 54a9362e4675b..daad4d2158c51 100644 --- a/crates/sui-benchmark/Cargo.toml +++ b/crates/sui-benchmark/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] async-trait.workspace = true anyhow = { workspace = true, features = ["backtrace"] } diff --git a/crates/sui-benchmark/src/embedded_reconfig_observer.rs b/crates/sui-benchmark/src/embedded_reconfig_observer.rs index 9976491327bfd..42df82d81ce3b 100644 --- a/crates/sui-benchmark/src/embedded_reconfig_observer.rs +++ b/crates/sui-benchmark/src/embedded_reconfig_observer.rs @@ -23,6 +23,7 @@ use tracing::{error, info, trace}; /// and we happen to have a big committee rotation, it may /// fail to get quorum on the latest committee info from /// demissioned validators and then stop working. +/// /// Background: this is a temporary solution for stress before /// we see fullnode reconfiguration stabilizes. #[derive(Clone, Default)] diff --git a/crates/sui-bridge-cli/src/lib.rs b/crates/sui-bridge-cli/src/lib.rs index 3cafe745d4c9b..eacd3ca431f2c 100644 --- a/crates/sui-bridge-cli/src/lib.rs +++ b/crates/sui-bridge-cli/src/lib.rs @@ -313,7 +313,7 @@ pub fn make_action(chain_id: BridgeChainId, cmd: &GovernanceClientCommands) -> B } } -fn encode_call_data(function_selector: &str, params: &Vec) -> Vec { +fn encode_call_data(function_selector: &str, params: &[String]) -> Vec { let left = function_selector .find('(') .expect("Invalid function selector, no left parentheses"); diff --git a/crates/sui-bridge/src/types.rs b/crates/sui-bridge/src/types.rs index e722a79c65eea..4fb157254a5a4 100644 --- a/crates/sui-bridge/src/types.rs +++ b/crates/sui-bridge/src/types.rs @@ -354,7 +354,7 @@ impl BridgeAction { // Digest of BridgeAction (with Keccak256 hasher) pub fn digest(&self) -> BridgeActionDigest { let mut hasher = Keccak256::default(); - hasher.update(&self.to_bytes()); + hasher.update(self.to_bytes()); BridgeActionDigest::new(hasher.finalize().into()) } diff --git a/crates/sui-config/Cargo.toml b/crates/sui-config/Cargo.toml index 87f5ab16a33ad..9704c09230224 100644 --- a/crates/sui-config/Cargo.toml +++ b/crates/sui-config/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] anemo.workspace = true anyhow = { workspace = true, features = ["backtrace"] } diff --git a/crates/sui-config/src/node.rs b/crates/sui-config/src/node.rs index ff87d1c8cc506..9637ffe4e2be8 100644 --- a/crates/sui-config/src/node.rs +++ b/crates/sui-config/src/node.rs @@ -19,7 +19,6 @@ use std::num::NonZeroUsize; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::Duration; -use std::usize; use sui_keys::keypair_file::{read_authority_keypair_from_file, read_keypair_from_file}; use sui_types::base_types::{ObjectID, SuiAddress}; use sui_types::committee::EpochId; diff --git a/crates/sui-config/src/p2p.rs b/crates/sui-config/src/p2p.rs index 4be9e93ddde90..7b7b4ba59198d 100644 --- a/crates/sui-config/src/p2p.rs +++ b/crates/sui-config/src/p2p.rs @@ -267,6 +267,7 @@ impl StateSyncConfig { /// * If the node marks itself as Private, only nodes that have it in /// their `allowlisted_peers` or `seed_peers` will try to connect to it. /// * If not set, defaults to Public. +/// /// AccessType is useful when a network of nodes want to stay private. To achieve this, /// mark every node in this network as `Private` and allowlist/seed them to each other. #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] diff --git a/crates/sui-core/Cargo.toml b/crates/sui-core/Cargo.toml index a2103140851db..2c596b502a54b 100644 --- a/crates/sui-core/Cargo.toml +++ b/crates/sui-core/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] anyhow = { workspace = true, features = ["backtrace"] } arc-swap.workspace = true diff --git a/crates/sui-core/src/authority.rs b/crates/sui-core/src/authority.rs index 409a9f67f8de4..c94076206a994 100644 --- a/crates/sui-core/src/authority.rs +++ b/crates/sui-core/src/authority.rs @@ -3651,12 +3651,12 @@ impl AuthorityState { .rev() .skip_while(|d| cursor.is_some() && Some(*d) != cursor) .skip(usize::from(cursor.is_some())); - return Ok(iter.take(limit.unwrap_or(usize::max_value())).collect()); + return Ok(iter.take(limit.unwrap_or(usize::MAX)).collect()); } else { let iter = iter .skip_while(|d| cursor.is_some() && Some(*d) != cursor) .skip(usize::from(cursor.is_some())); - return Ok(iter.take(limit.unwrap_or(usize::max_value())).collect()); + return Ok(iter.take(limit.unwrap_or(usize::MAX)).collect()); } } self.get_indexes()? @@ -5469,7 +5469,6 @@ impl NodeStateDump { Ok(path) } - #[cfg(not(release))] pub fn read_from_file(path: &PathBuf) -> Result { let file = File::open(path)?; serde_json::from_reader(file).map_err(|e| anyhow::anyhow!(e)) 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 367e77965d1d7..9e922112eff57 100644 --- a/crates/sui-core/src/authority/authority_per_epoch_store.rs +++ b/crates/sui-core/src/authority/authority_per_epoch_store.rs @@ -125,8 +125,8 @@ pub(crate) type EncG = bls12381::G2Element; // CertLockGuard and CertTxGuard are functionally identical right now, but we retain a distinction // anyway. If we need to support distributed object storage, having this distinction will be // useful, as we will most likely have to re-implement a retry / write-ahead-log at that point. -pub struct CertLockGuard(MutexGuard); -pub struct CertTxGuard(CertLockGuard); +pub struct CertLockGuard(#[allow(unused)] MutexGuard); +pub struct CertTxGuard(#[allow(unused)] CertLockGuard); impl CertTxGuard { pub fn release(self) {} @@ -370,15 +370,15 @@ pub struct AuthorityEpochTables { /// The tables below manage shared object locks / versions. There are three ways they can be /// updated: /// 1. (validators only): Upon receiving a certified transaction from consensus, the authority - /// assigns the next version to each shared object of the transaction. The next versions of - /// the shared objects are updated as well. + /// assigns the next version to each shared object of the transaction. The next versions of + /// the shared objects are updated as well. /// 2. (validators only): Upon receiving a new consensus commit, the authority assigns the - /// next version of the randomness state object to an expected future transaction to be - /// generated after the next random value is available. The next version of the randomness - /// state object is updated as well. + /// next version of the randomness state object to an expected future transaction to be + /// generated after the next random value is available. The next version of the randomness + /// state object is updated as well. /// 3. (fullnodes + validators): Upon receiving a certified effect from state sync, or - /// transaction orchestrator fast execution path, the node assigns the shared object - /// versions from the transaction effect. Next object versions are not updated. + /// transaction orchestrator fast execution path, the node assigns the shared object + /// versions from the transaction effect. Next object versions are not updated. /// /// REQUIRED: all authorities must assign the same shared object versions for each transaction. assigned_shared_object_versions: DBMap>, diff --git a/crates/sui-core/src/authority/authority_store_pruner.rs b/crates/sui-core/src/authority/authority_store_pruner.rs index aca327f15f5a2..17f34f5755f89 100644 --- a/crates/sui-core/src/authority/authority_store_pruner.rs +++ b/crates/sui-core/src/authority/authority_store_pruner.rs @@ -1152,6 +1152,8 @@ mod pprof_tests { } #[tokio::test] + // un-ignore once https://github.com/tikv/pprof-rs/issues/250 is fixed + #[ignore] async fn ensure_no_tombstone_fragmentation_in_stack_frame_with_ignore_tombstones( ) -> Result<(), anyhow::Error> { // This test writes a bunch of objects to objects table, invokes pruning on it and @@ -1188,6 +1190,8 @@ mod pprof_tests { } #[tokio::test] + // un-ignore once https://github.com/tikv/pprof-rs/issues/250 is fixed + #[ignore] async fn ensure_no_tombstone_fragmentation_in_stack_frame_after_flush( ) -> Result<(), anyhow::Error> { // This test writes a bunch of objects to objects table, invokes pruning on it and diff --git a/crates/sui-core/src/authority/authority_store_tables.rs b/crates/sui-core/src/authority/authority_store_tables.rs index d61187a3050d3..db8394b2de2d5 100644 --- a/crates/sui-core/src/authority/authority_store_tables.rs +++ b/crates/sui-core/src/authority/authority_store_tables.rs @@ -71,9 +71,10 @@ pub struct AuthorityPerpetualTables { /// A map between the transaction digest of a certificate to the effects of its execution. /// We store effects into this table in two different cases: /// 1. When a transaction is synced through state_sync, we store the effects here. These effects - /// are known to be final in the network, but may not have been executed locally yet. + /// are known to be final in the network, but may not have been executed locally yet. /// 2. When the transaction is executed locally on this node, we store the effects here. This means that - /// it's possible to store the same effects twice (once for the synced transaction, and once for the executed). + /// it's possible to store the same effects twice (once for the synced transaction, and once for the executed). + /// /// It's also possible for the effects to be reverted if the transaction didn't make it into the epoch. #[default_options_override_fn = "effects_table_default_config"] pub(crate) effects: DBMap, 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 4732afe4b7399..041e523594074 100644 --- a/crates/sui-core/src/authority/shared_object_congestion_tracker.rs +++ b/crates/sui-core/src/authority/shared_object_congestion_tracker.rs @@ -78,9 +78,7 @@ impl SharedObjectCongestionTracker { previously_deferred_tx_digests: &HashMap, commit_round: Round, ) -> Option<(DeferralKey, Vec)> { - let Some(tx_cost) = self.get_tx_cost(cert) else { - return None; - }; + let tx_cost = self.get_tx_cost(cert)?; let shared_input_objects: Vec<_> = cert.shared_input_objects().collect(); if shared_input_objects.is_empty() { diff --git a/crates/sui-core/src/authority/test_authority_builder.rs b/crates/sui-core/src/authority/test_authority_builder.rs index a1fc9c40020c8..6ba9dd32d4801 100644 --- a/crates/sui-core/src/authority/test_authority_builder.rs +++ b/crates/sui-core/src/authority/test_authority_builder.rs @@ -227,10 +227,7 @@ impl<'a> TestAuthorityBuilder<'a> { epoch_flags, ) .unwrap(); - let expensive_safety_checks = match self.expensive_safety_checks { - None => ExpensiveSafetyCheckConfig::default(), - Some(config) => config, - }; + let expensive_safety_checks = self.expensive_safety_checks.unwrap_or_default(); let cache_traits = build_execution_cache(&epoch_start_configuration, ®istry, &authority_store); diff --git a/crates/sui-core/src/authority_aggregator.rs b/crates/sui-core/src/authority_aggregator.rs index 65955171bcbec..8365a2064fb3b 100644 --- a/crates/sui-core/src/authority_aggregator.rs +++ b/crates/sui-core/src/authority_aggregator.rs @@ -277,6 +277,7 @@ pub enum AggregatorProcessCertificateError { } pub fn group_errors(errors: Vec<(SuiError, Vec, StakeUnit)>) -> GroupedErrors { + #[allow(clippy::mutable_key_type)] let mut grouped_errors = HashMap::new(); for (error, names, stake) in errors { let entry = grouped_errors.entry(error).or_insert((0, vec![])); diff --git a/crates/sui-core/src/checkpoints/checkpoint_executor/mod.rs b/crates/sui-core/src/checkpoints/checkpoint_executor/mod.rs index b1872a9f21723..fd0a9c90a34d8 100644 --- a/crates/sui-core/src/checkpoints/checkpoint_executor/mod.rs +++ b/crates/sui-core/src/checkpoints/checkpoint_executor/mod.rs @@ -94,7 +94,7 @@ pub struct CheckpointTimeoutConfig { // the function is still very cheap to call so this is okay. thread_local! { static SCHEDULING_TIMEOUT: once_cell::sync::OnceCell = - once_cell::sync::OnceCell::new(); + const { once_cell::sync::OnceCell::new() }; } #[cfg(msim)] diff --git a/crates/sui-core/src/consensus_adapter.rs b/crates/sui-core/src/consensus_adapter.rs index 8b33d8031fac7..f67388061656c 100644 --- a/crates/sui-core/src/consensus_adapter.rs +++ b/crates/sui-core/src/consensus_adapter.rs @@ -1029,12 +1029,12 @@ impl<'a> InflightDropGuard<'a> { adapter .metrics .sequencing_certificate_inflight - .with_label_values(&[&tx_type]) + .with_label_values(&[tx_type]) .inc(); adapter .metrics .sequencing_certificate_attempt - .with_label_values(&[&tx_type]) + .with_label_values(&[tx_type]) .inc(); Self { adapter, diff --git a/crates/sui-core/src/rest_index.rs b/crates/sui-core/src/rest_index.rs index e999cfe2abc2e..ade98f8ada4dd 100644 --- a/crates/sui-core/src/rest_index.rs +++ b/crates/sui-core/src/rest_index.rs @@ -114,9 +114,9 @@ struct IndexStoreTables { /// /// A few uses for this singleton: /// - determining if the DB has been initialized (as some tables will still be empty post - /// initializatio) + /// initialization) /// - version of the DB. Everytime a new table or schema is changed the version number needs to - /// be incremented. + /// be incremented. meta: DBMap<(), MetadataInfo>, /// An index of extra metadata for Transactions. diff --git a/crates/sui-core/src/transaction_manager.rs b/crates/sui-core/src/transaction_manager.rs index 5accb919a8680..d7796b69edbc2 100644 --- a/crates/sui-core/src/transaction_manager.rs +++ b/crates/sui-core/src/transaction_manager.rs @@ -62,6 +62,7 @@ pub struct TransactionManager { #[derive(Clone, Debug)] pub struct PendingCertificateStats { // The time this certificate enters transaction manager. + #[allow(unused)] pub enqueue_time: Instant, // The time this certificate becomes ready for execution. pub ready_time: Option, @@ -966,9 +967,7 @@ impl TransactionQueue { /// After removing the digest, first() will return the new oldest entry /// in the queue (which may be unchanged). fn remove(&mut self, digest: &TransactionDigest) -> Option { - let Some(when) = self.digests.remove(digest) else { - return None; - }; + let when = self.digests.remove(digest)?; // This loop removes all previously inserted entries that no longer // correspond to live entries in self.digests. When the loop terminates, 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 cef3075a2e93f..cc3ffa82fb08e 100644 --- a/crates/sui-core/src/unit_tests/authority_aggregator_tests.rs +++ b/crates/sui-core/src/unit_tests/authority_aggregator_tests.rs @@ -693,7 +693,7 @@ fn get_genesis_agg( .build_custom_clients(clients) } -fn get_agg_at_epoch( +fn get_agg_at_epoch( authorities: BTreeMap, clients: BTreeMap, epoch: EpochId, diff --git a/crates/sui-core/src/unit_tests/execution_driver_tests.rs b/crates/sui-core/src/unit_tests/execution_driver_tests.rs index e9a5f381dceeb..a138886c4f6a7 100644 --- a/crates/sui-core/src/unit_tests/execution_driver_tests.rs +++ b/crates/sui-core/src/unit_tests/execution_driver_tests.rs @@ -46,7 +46,7 @@ use tokio::time::{sleep, timeout}; #[allow(dead_code)] async fn wait_for_certs( stream: &mut UnboundedReceiver, - certs: &Vec, + certs: &[VerifiedCertificate], ) { if certs.is_empty() { if timeout(Duration::from_secs(30), stream.recv()) diff --git a/crates/sui-e2e-tests/Cargo.toml b/crates/sui-e2e-tests/Cargo.toml index 4d7e294f06c75..970458f3a9e94 100644 --- a/crates/sui-e2e-tests/Cargo.toml +++ b/crates/sui-e2e-tests/Cargo.toml @@ -6,6 +6,9 @@ publish = false edition = "2021" version.workspace = true +[lints] +workspace = true + [dependencies] [dev-dependencies] @@ -67,4 +70,4 @@ passkey-client.workspace = true passkey-authenticator.workspace = true coset.workspace = true url.workspace = true -p256.workspace = true \ No newline at end of file +p256.workspace = true diff --git a/crates/sui-e2e-tests/tests/dynamic_committee_tests.rs b/crates/sui-e2e-tests/tests/dynamic_committee_tests.rs index 3d0b374dfefa8..454328bace837 100644 --- a/crates/sui-e2e-tests/tests/dynamic_committee_tests.rs +++ b/crates/sui-e2e-tests/tests/dynamic_committee_tests.rs @@ -57,6 +57,7 @@ trait StatePredicate { runner: &StressTestRunner, effects: &TransactionEffects, ); + #[allow(unused)] async fn post_epoch_post_condition( &mut self, runner: &StressTestRunner, @@ -351,7 +352,7 @@ async fn fuzz_dynamic_committee() { let num_operations = 10; // Add more actions here as we create them - let actions = vec![Box::new(add_stake::RequestAddStakeGen)]; + let actions = [Box::new(add_stake::RequestAddStakeGen)]; let mut runner = StressTestRunner::new().await; diff --git a/crates/sui-faucet/src/faucet/simple_faucet.rs b/crates/sui-faucet/src/faucet/simple_faucet.rs index 0bdbb8641d764..00133d92dcd03 100644 --- a/crates/sui-faucet/src/faucet/simple_faucet.rs +++ b/crates/sui-faucet/src/faucet/simple_faucet.rs @@ -328,6 +328,7 @@ impl SimpleFaucet { /// Check if the gas coin is still valid. A valid gas coin /// 1. Exists presently /// 2. is a gas coin + /// /// If the coin is valid, return Ok(Some(GasCoin)) /// If the coin invalid, return Ok(None) /// If the fullnode returns an unexpected error, returns Err(e) @@ -1303,7 +1304,7 @@ mod tests { .await .unwrap(); - let amounts = &vec![coin_amount]; + let amounts = &[coin_amount]; // Create a vector containing five randomly generated addresses let target_addresses: Vec = (0..5) @@ -1384,7 +1385,7 @@ mod tests { .await .unwrap(); - let amounts = &vec![1; 1]; + let amounts = &[1; 1]; // Create a vector containing five randomly generated addresses let target_addresses: Vec = (0..5) .map(|_| SuiAddress::random_for_testing_only()) diff --git a/crates/sui-framework-tests/Cargo.toml b/crates/sui-framework-tests/Cargo.toml index ad593b3634716..2093974fa826f 100644 --- a/crates/sui-framework-tests/Cargo.toml +++ b/crates/sui-framework-tests/Cargo.toml @@ -7,6 +7,9 @@ description = "Runs Move tests for sui-framework" license = "Apache-2.0" publish = false +[lints] +workspace = true + [[test]] name = "move_tests" harness = false diff --git a/crates/sui-framework/src/lib.rs b/crates/sui-framework/src/lib.rs index dfc8679cd2fc6..f51f746585acb 100644 --- a/crates/sui-framework/src/lib.rs +++ b/crates/sui-framework/src/lib.rs @@ -256,9 +256,7 @@ pub async fn compare_system_package( let mut new_normalized = new_pkg.normalize(binary_config).ok()?; for (name, cur_module) in cur_normalized { - let Some(new_module) = new_normalized.remove(&name) else { - return None; - }; + let new_module = new_normalized.remove(&name)?; if let Err(e) = compatibility.check(&cur_module, &new_module) { error!("Compatibility check failed, for new version of {id}::{name}: {e:?}"); diff --git a/crates/sui-genesis-builder/Cargo.toml b/crates/sui-genesis-builder/Cargo.toml index 32e62bf08712b..c439f28b4caf7 100644 --- a/crates/sui-genesis-builder/Cargo.toml +++ b/crates/sui-genesis-builder/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] anyhow.workspace = true bcs.workspace = true diff --git a/crates/sui-genesis-builder/src/lib.rs b/crates/sui-genesis-builder/src/lib.rs index fbab297181074..b0925b19951f3 100644 --- a/crates/sui-genesis-builder/src/lib.rs +++ b/crates/sui-genesis-builder/src/lib.rs @@ -693,11 +693,11 @@ fn create_genesis_context( ) -> TxContext { let mut hasher = DefaultHash::default(); hasher.update(b"sui-genesis"); - hasher.update(&bcs::to_bytes(genesis_chain_parameters).unwrap()); - hasher.update(&bcs::to_bytes(genesis_validators).unwrap()); - hasher.update(&bcs::to_bytes(token_distribution_schedule).unwrap()); + hasher.update(bcs::to_bytes(genesis_chain_parameters).unwrap()); + hasher.update(bcs::to_bytes(genesis_validators).unwrap()); + hasher.update(bcs::to_bytes(token_distribution_schedule).unwrap()); for system_package in system_packages { - hasher.update(&bcs::to_bytes(system_package.bytes()).unwrap()); + hasher.update(bcs::to_bytes(system_package.bytes()).unwrap()); } let hash = hasher.finalize(); diff --git a/crates/sui-graphql-e2e-tests/Cargo.toml b/crates/sui-graphql-e2e-tests/Cargo.toml index fb8642a2a404e..1bc925321f16e 100644 --- a/crates/sui-graphql-e2e-tests/Cargo.toml +++ b/crates/sui-graphql-e2e-tests/Cargo.toml @@ -7,6 +7,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dev-dependencies] datatest-stable.workspace = true sui-graphql-rpc.workspace = true diff --git a/crates/sui-graphql-rpc/src/server/version.rs b/crates/sui-graphql-rpc/src/server/version.rs index 0dcb46a5ba891..b73bc9b9d215d 100644 --- a/crates/sui-graphql-rpc/src/server/version.rs +++ b/crates/sui-graphql-rpc/src/server/version.rs @@ -17,6 +17,7 @@ use crate::{ pub(crate) static VERSION_HEADER: HeaderName = HeaderName::from_static("x-sui-rpc-version"); +#[allow(unused)] pub(crate) struct SuiRpcVersion(Vec, Vec>); const NAMED_VERSIONS: [&str; 3] = ["beta", "legacy", "stable"]; diff --git a/crates/sui-graphql-rpc/src/types/event.rs b/crates/sui-graphql-rpc/src/types/event.rs index e2555bb0214eb..cb558c2fba6c3 100644 --- a/crates/sui-graphql-rpc/src/types/event.rs +++ b/crates/sui-graphql-rpc/src/types/event.rs @@ -259,9 +259,6 @@ impl Event { checkpoint_sequence_number: stored_tx.checkpoint_sequence_number, #[cfg(feature = "postgres-feature")] senders: vec![Some(native_event.sender.to_vec())], - #[cfg(feature = "mysql-feature")] - #[cfg(not(feature = "postgres-feature"))] - senders: serde_json::to_value(vec![native_event.sender.to_vec()]).unwrap(), package: native_event.package_id.to_vec(), module: native_event.transaction_module.to_string(), event_type: native_event @@ -290,17 +287,6 @@ impl Event { { stored.senders.first() } - #[cfg(feature = "mysql-feature")] - #[cfg(not(feature = "postgres-feature"))] - { - stored - .senders - .as_array() - .ok_or_else(|| { - Error::Internal("Failed to parse event senders as array".to_string()) - })? - .first() - } }) else { return Err(Error::Internal("No senders found for event".to_string())); }; diff --git a/crates/sui-graphql-rpc/src/types/move_object.rs b/crates/sui-graphql-rpc/src/types/move_object.rs index c5cf724604089..cabe71c92bf60 100644 --- a/crates/sui-graphql-rpc/src/types/move_object.rs +++ b/crates/sui-graphql-rpc/src/types/move_object.rs @@ -50,6 +50,7 @@ pub(crate) enum MoveObjectDowncastError { /// This interface is implemented by types that represent a Move object on-chain (A Move value whose /// type has `key`). +#[allow(clippy::duplicated_attributes)] #[derive(Interface)] #[graphql( name = "IMoveObject", diff --git a/crates/sui-graphql-rpc/src/types/object.rs b/crates/sui-graphql-rpc/src/types/object.rs index 5cb49d63ff03c..eebe0b86447ed 100644 --- a/crates/sui-graphql-rpc/src/types/object.rs +++ b/crates/sui-graphql-rpc/src/types/object.rs @@ -221,6 +221,7 @@ pub(crate) struct HistoricalObjectCursor { /// Interface implemented by on-chain values that are addressable by an ID (also referred to as its /// address). This includes Move objects and packages. +#[allow(clippy::duplicated_attributes)] #[derive(Interface)] #[graphql( name = "IObject", @@ -579,9 +580,7 @@ impl ObjectImpl<'_> { pub(crate) async fn owner(&self, ctx: &Context<'_>) -> Option { use NativeOwner as O; - let Some(native) = self.0.native_impl() else { - return None; - }; + let native = self.0.native_impl()?; match native.owner { O::AddressOwner(address) => { diff --git a/crates/sui-graphql-rpc/src/types/owner.rs b/crates/sui-graphql-rpc/src/types/owner.rs index 3986a39f6ad23..8d790fdc2d699 100644 --- a/crates/sui-graphql-rpc/src/types/owner.rs +++ b/crates/sui-graphql-rpc/src/types/owner.rs @@ -56,6 +56,7 @@ pub(crate) struct OwnerImpl { /// are identified by an address which can represent either the public key of an account or another /// object. The same address can only refer to an account or an object, never both, but it is not /// possible to know which up-front. +#[allow(clippy::duplicated_attributes)] #[derive(Interface)] #[graphql( name = "IOwner", 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 e3d551976d5d4..8077f4b4d5da4 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 @@ -293,9 +293,7 @@ pub(crate) fn subqueries(filter: &TransactionBlockFilter, tx_bounds: TxBounds) - subqueries.push(("tx_digests", select_ids(txs, tx_bounds))); } - let Some((_, mut subquery)) = subqueries.pop() else { - return None; - }; + let (_, mut subquery) = subqueries.pop()?; if !subqueries.is_empty() { subquery = query!("SELECT tx_sequence_number FROM ({}) AS initial", subquery); diff --git a/crates/sui-indexer/src/store/mod.rs b/crates/sui-indexer/src/store/mod.rs index 920cc5817499c..f520f6afa1414 100644 --- a/crates/sui-indexer/src/store/mod.rs +++ b/crates/sui-indexer/src/store/mod.rs @@ -11,7 +11,7 @@ pub mod pg_partition_manager; pub mod diesel_macro { thread_local! { - pub static CALLED_FROM_BLOCKING_POOL: std::cell::RefCell = std::cell::RefCell::new(false); + pub static CALLED_FROM_BLOCKING_POOL: std::cell::RefCell = const { std::cell::RefCell::new(false) }; } #[macro_export] @@ -292,10 +292,11 @@ pub mod diesel_macro { /// Check that we are in a context conducive to making blocking calls. /// This is done by either: /// - Checking that we are not inside a tokio runtime context + /// /// Or: /// - If we are inside a tokio runtime context, ensure that the call went through - /// `IndexerReader::spawn_blocking` which properly moves the blocking call to a blocking thread - /// pool. + /// `IndexerReader::spawn_blocking` which properly moves the blocking call to a blocking thread + /// pool. #[macro_export] macro_rules! blocking_call_is_ok_or_panic { () => {{ diff --git a/crates/sui-indexer/src/store/pg_indexer_store.rs b/crates/sui-indexer/src/store/pg_indexer_store.rs index 7a2ac3cdb8854..23080f3f1fcaa 100644 --- a/crates/sui-indexer/src/store/pg_indexer_store.rs +++ b/crates/sui-indexer/src/store/pg_indexer_store.rs @@ -135,6 +135,7 @@ SET object_version = EXCLUDED.object_version, pub struct PgIndexerStoreConfig { pub parallel_chunk_size: usize, pub parallel_objects_chunk_size: usize, + #[allow(unused)] pub epochs_to_keep: Option, } diff --git a/crates/sui-json-rpc-tests/Cargo.toml b/crates/sui-json-rpc-tests/Cargo.toml index af78d3ccc56df..0f688d4b455e0 100644 --- a/crates/sui-json-rpc-tests/Cargo.toml +++ b/crates/sui-json-rpc-tests/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] [dev-dependencies] diff --git a/crates/sui-json-rpc-types/src/sui_transaction.rs b/crates/sui-json-rpc-types/src/sui_transaction.rs index dcd685349ec2a..8936d54831102 100644 --- a/crates/sui-json-rpc-types/src/sui_transaction.rs +++ b/crates/sui-json-rpc-types/src/sui_transaction.rs @@ -1803,7 +1803,7 @@ impl SuiProgrammableTransactionBlock { } fn resolve_input_type( - inputs: &Vec, + inputs: &[CallArg], commands: &[Command], module_cache: &impl GetModule, ) -> Vec> { diff --git a/crates/sui-json/src/lib.rs b/crates/sui-json/src/lib.rs index 6c367c0d6ba15..4782ed9e57116 100644 --- a/crates/sui-json/src/lib.rs +++ b/crates/sui-json/src/lib.rs @@ -703,7 +703,7 @@ fn resolve_object_vec_arg(idx: usize, arg: &SuiJsonValue) -> Result>( /// - Base64 encoded `privkey` for Raw key /// - Bech32 encoded private key prefixed with `suiprivkey` /// - Hex encoded `privkey` for Raw key +/// /// If `require_secp256k1` is true, it will return an error if the key is not Secp256k1. pub fn read_key(path: &PathBuf, require_secp256k1: bool) -> Result { if !path.exists() { diff --git a/crates/sui-macros/Cargo.toml b/crates/sui-macros/Cargo.toml index c7e99bd255f69..cb274a3af898c 100644 --- a/crates/sui-macros/Cargo.toml +++ b/crates/sui-macros/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] sui-proc-macros.workspace = true once_cell.workspace = true diff --git a/crates/sui-macros/src/lib.rs b/crates/sui-macros/src/lib.rs index 6e3cd85ca19be..a036a3dbfcfb0 100644 --- a/crates/sui-macros/src/lib.rs +++ b/crates/sui-macros/src/lib.rs @@ -528,7 +528,7 @@ mod test { assert_eq!(Foo::new(1, 2).b, 2); assert_eq!(new_foo(1).a, 1); - let v = vec![Foo::new(1, 2), Foo::new(3, 2)]; + let v = [Foo::new(1, 2), Foo::new(3, 2)]; assert_eq!(v[0].a, 1); assert_eq!(v[1].b, 2); @@ -720,7 +720,7 @@ mod test { assert_eq!(Foo::new(1, 2).b, 2); assert_eq!(new_foo(1).a, 1); - let v = vec![Foo::new(1, 2), Foo::new(3, 2)]; + let v = [Foo::new(1, 2), Foo::new(3, 2)]; assert_eq!(v[0].a, 1); assert_eq!(v[1].b, 2); diff --git a/crates/sui-move/src/unit_test.rs b/crates/sui-move/src/unit_test.rs index 3bb4da1f2da0f..78ae1e5260a52 100644 --- a/crates/sui-move/src/unit_test.rs +++ b/crates/sui-move/src/unit_test.rs @@ -20,13 +20,8 @@ use sui_move_natives::test_scenario::InMemoryTestStore; use sui_move_natives::{object_runtime::ObjectRuntime, NativesCostTable}; use sui_protocol_config::ProtocolConfig; use sui_types::{ - base_types::{ObjectID, SequenceNumber}, - error::SuiResult, - gas_model::tables::initial_cost_schedule_for_unit_tests, - in_memory_storage::InMemoryStorage, + gas_model::tables::initial_cost_schedule_for_unit_tests, in_memory_storage::InMemoryStorage, metrics::LimitsMetrics, - object::Object, - storage::ChildObjectResolver, }; // Move unit tests will halt after executing this many steps. This is a protection to avoid divergence @@ -63,28 +58,6 @@ impl Test { } } -struct DummyChildObjectStore {} - -impl ChildObjectResolver for DummyChildObjectStore { - fn read_child_object( - &self, - _parent: &ObjectID, - _child: &ObjectID, - _child_version_upper_bound: SequenceNumber, - ) -> SuiResult> { - Ok(None) - } - fn get_object_received_at_version( - &self, - _owner: &ObjectID, - _receiving_object_id: &ObjectID, - _receive_object_at_version: SequenceNumber, - _epoch_id: sui_types::committee::EpochId, - ) -> SuiResult> { - Ok(None) - } -} - static TEST_STORE_INNER: Lazy> = Lazy::new(|| RwLock::new(InMemoryStorage::default())); diff --git a/crates/sui-network/src/state_sync/server.rs b/crates/sui-network/src/state_sync/server.rs index f7e24b5a5202f..4e6b32cff059e 100644 --- a/crates/sui-network/src/state_sync/server.rs +++ b/crates/sui-network/src/state_sync/server.rs @@ -221,7 +221,7 @@ where } })?; - struct SemaphoreExtension(OwnedSemaphorePermit); + struct SemaphoreExtension(#[allow(unused)] OwnedSemaphorePermit); inner.call(req).await.map(move |mut response| { // Insert permit as extension so it's not dropped until the response is sent. response diff --git a/crates/sui-node/Cargo.toml b/crates/sui-node/Cargo.toml index 9411c6f9e3f9c..1e3449067aaf3 100644 --- a/crates/sui-node/Cargo.toml +++ b/crates/sui-node/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] anemo.workspace = true anemo-tower.workspace = true diff --git a/crates/sui-node/src/admin.rs b/crates/sui-node/src/admin.rs index 228707cebd2b4..3c867e2f994a0 100644 --- a/crates/sui-node/src/admin.rs +++ b/crates/sui-node/src/admin.rs @@ -241,12 +241,12 @@ async fn capabilities(State(state): State>) -> (StatusCode, String // Only one of v1 or v2 will be populated at a time let capabilities = epoch_store.get_capabilities_v1(); let mut output = String::new(); - for capability in &capabilities { + for capability in capabilities.unwrap_or_default() { output.push_str(&format!("{:?}\n", capability)); } let capabilities = epoch_store.get_capabilities_v2(); - for capability in &capabilities { + for capability in capabilities.unwrap_or_default() { output.push_str(&format!("{:?}\n", capability)); } diff --git a/crates/sui-open-rpc-macros/src/lib.rs b/crates/sui-open-rpc-macros/src/lib.rs index 6fa99cc211998..ccf58a392286a 100644 --- a/crates/sui-open-rpc-macros/src/lib.rs +++ b/crates/sui-open-rpc-macros/src/lib.rs @@ -121,10 +121,6 @@ pub fn open_rpc(attr: TokenStream, item: TokenStream) -> TokenStream { trait OptionalQuote { fn to_quote(&self) -> TokenStream2; - - fn unwrap_quote(&self, quote: F) -> TokenStream2 - where - F: FnOnce(LitStr) -> TokenStream2; } impl OptionalQuote for Option { @@ -135,17 +131,6 @@ impl OptionalQuote for Option { quote!(None) } } - - fn unwrap_quote(&self, quote: F) -> TokenStream2 - where - F: FnOnce(LitStr) -> TokenStream2, - { - if let Some(lit_str) = self { - quote(lit_str.clone()) - } else { - quote!() - } - } } struct RpcDefinition { diff --git a/crates/sui-open-rpc/Cargo.toml b/crates/sui-open-rpc/Cargo.toml index 3bbe0f53ab5d7..e6000fe18d0e2 100644 --- a/crates/sui-open-rpc/Cargo.toml +++ b/crates/sui-open-rpc/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] schemars.workspace = true serde.workspace = true diff --git a/crates/sui-open-rpc/src/examples.rs b/crates/sui-open-rpc/src/examples.rs index ed8c038571073..9c2f8d8ba6805 100644 --- a/crates/sui-open-rpc/src/examples.rs +++ b/crates/sui-open-rpc/src/examples.rs @@ -69,11 +69,6 @@ struct Examples { examples: Vec, } -#[derive(serde::Serialize)] -struct Value { - value: String, -} - impl Examples { fn new(name: &str, examples: Vec) -> Self { Self { diff --git a/crates/sui-package-resolver/src/lib.rs b/crates/sui-package-resolver/src/lib.rs index 17f1118e591d1..a6c1f3d3c0043 100644 --- a/crates/sui-package-resolver/src/lib.rs +++ b/crates/sui-package-resolver/src/lib.rs @@ -132,6 +132,7 @@ pub enum ErrorConstants { /// * A numeric value (u8, u16, u32, u64, u128, u256); or /// * A boolean value; or /// * An address value + /// /// Otherwise, the `Raw` bytes of the error constant are returned. Rendered { /// The name of the error constant. @@ -1547,7 +1548,7 @@ impl<'l> ResolutionContext<'l> { O::Datatype(key, params) => { // SAFETY: `add_signature` ensures `datatypes` has an element with this key. - let def = &self.datatypes[&key]; + let def = &self.datatypes[key]; let param_layouts = params .iter() @@ -1639,7 +1640,7 @@ impl<'l> ResolutionContext<'l> { O::Datatype(key, params) => { // SAFETY: `add_signature` ensures `datatypes` has an element with this key. - let defining_id = &self.datatypes[&key].defining_id; + let defining_id = &self.datatypes[key].defining_id; for param in params { self.relocate_signature(param)?; } diff --git a/crates/sui-proc-macros/Cargo.toml b/crates/sui-proc-macros/Cargo.toml index 0ccf1b0d77060..cb3bf09505604 100644 --- a/crates/sui-proc-macros/Cargo.toml +++ b/crates/sui-proc-macros/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [lib] proc-macro = true diff --git a/crates/sui-protocol-config/Cargo.toml b/crates/sui-protocol-config/Cargo.toml index edcea7bd5ba8b..f8a8dfec81551 100644 --- a/crates/sui-protocol-config/Cargo.toml +++ b/crates/sui-protocol-config/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] serde.workspace = true tracing.workspace = true diff --git a/crates/sui-proxy/src/consumer.rs b/crates/sui-proxy/src/consumer.rs index 7135c5eaa670b..c4b3af8ad9770 100644 --- a/crates/sui-proxy/src/consumer.rs +++ b/crates/sui-proxy/src/consumer.rs @@ -242,7 +242,7 @@ async fn check_response( async fn convert( mfs: Vec, ) -> Result, (StatusCode, &'static str)> { - let result = match tokio::task::spawn_blocking(|| { + let result = tokio::task::spawn_blocking(|| { let timer = CONSUMER_OPERATION_DURATION .with_label_values(&["convert_to_remote_write_task"]) .start_timer(); @@ -250,8 +250,9 @@ async fn convert( timer.observe_duration(); result.into_iter() }) - .await - { + .await; + + let result = match result { Ok(v) => v, Err(err) => { error!("unable to convert to remote_write; {err}"); diff --git a/crates/sui-replay/src/lib.rs b/crates/sui-replay/src/lib.rs index 4d139661dd794..26a27cf828767 100644 --- a/crates/sui-replay/src/lib.rs +++ b/crates/sui-replay/src/lib.rs @@ -586,9 +586,8 @@ pub(crate) fn chain_from_chain_id(chain: &str) -> Chain { fn parse_configs_versions( configs_and_versions: Option>, ) -> Option> { - let Some(configs_and_versions) = configs_and_versions else { - return None; - }; + let configs_and_versions = configs_and_versions?; + assert!(configs_and_versions.len() % 2 == 0, "Invalid number of arguments for configs and version -- you must supply a version for each config"); Some( configs_and_versions diff --git a/crates/sui-rest-api/src/reader.rs b/crates/sui-rest-api/src/reader.rs index dd07ba57ae88f..30b2f8c2271b7 100644 --- a/crates/sui-rest-api/src/reader.rs +++ b/crates/sui-rest-api/src/reader.rs @@ -260,6 +260,7 @@ impl Iterator for CheckpointTransactionsIter { pub struct CursorInfo { pub checkpoint: CheckpointSequenceNumber, pub timestamp_ms: u64, + #[allow(unused)] pub index: u64, // None if there are no more transactions in the store diff --git a/crates/sui-rest-api/src/response.rs b/crates/sui-rest-api/src/response.rs index 3d2bda9df3a5f..cc30c9935cd1c 100644 --- a/crates/sui-rest-api/src/response.rs +++ b/crates/sui-rest-api/src/response.rs @@ -102,9 +102,9 @@ impl axum::response::IntoResponse for BcsRejection { "Expected request with `Content-Type: application/bcs`", ) .into_response(), - BcsRejection::DeserializationError(_) => ( + BcsRejection::DeserializationError(e) => ( StatusCode::UNPROCESSABLE_ENTITY, - "Failed to deserialize the BCS body into the target type", + format!("Failed to deserialize the BCS body into the target type: {e}"), ) .into_response(), BcsRejection::BytesRejection(bytes_rejection) => bytes_rejection.into_response(), diff --git a/crates/sui-rosetta/src/construction.rs b/crates/sui-rosetta/src/construction.rs index 45eaaef6abe57..d67c37ce34a94 100644 --- a/crates/sui-rosetta/src/construction.rs +++ b/crates/sui-rosetta/src/construction.rs @@ -74,7 +74,7 @@ pub async fn payloads( let intent_msg_bytes = bcs::to_bytes(&intent_msg)?; let mut hasher = DefaultHash::default(); - hasher.update(&bcs::to_bytes(&intent_msg).expect("Message serialization should not fail")); + hasher.update(bcs::to_bytes(&intent_msg).expect("Message serialization should not fail")); let digest = hasher.finalize().digest; Ok(ConstructionPayloadsResponse { diff --git a/crates/sui-rosetta/tests/gas_budget_tests.rs b/crates/sui-rosetta/tests/gas_budget_tests.rs index ead522c111354..f5666298bdd58 100644 --- a/crates/sui-rosetta/tests/gas_budget_tests.rs +++ b/crates/sui-rosetta/tests/gas_budget_tests.rs @@ -28,6 +28,7 @@ mod rosetta_client; #[derive(Deserialize, Debug)] #[serde(untagged)] enum TransactionIdentifierResponseResult { + #[allow(unused)] Success(TransactionIdentifierResponse), Error(RosettaSubmitGasError), } diff --git a/crates/sui-rpc-loadgen/src/payload/query_transactions.rs b/crates/sui-rpc-loadgen/src/payload/query_transactions.rs index b4abb519fee19..5482372cdd812 100644 --- a/crates/sui-rpc-loadgen/src/payload/query_transactions.rs +++ b/crates/sui-rpc-loadgen/src/payload/query_transactions.rs @@ -82,7 +82,7 @@ impl<'a> ProcessPayload<'a, &'a QueryTransactionBlocks> for RpcCommandProcessor } }; - results = join_all(clients.iter().enumerate().map(|(_i, client)| { + results = join_all(clients.iter().map(|client| { let with_query = query.clone(); async move { query_transaction_blocks(client, with_query, cursor, None) diff --git a/crates/sui-rpc-loadgen/src/payload/validation.rs b/crates/sui-rpc-loadgen/src/payload/validation.rs index 00656da736ed7..8204692c73ab1 100644 --- a/crates/sui-rpc-loadgen/src/payload/validation.rs +++ b/crates/sui-rpc-loadgen/src/payload/validation.rs @@ -16,7 +16,7 @@ use tracing::log::warn; const LOADGEN_QUERY_MAX_RESULT_LIMIT: usize = 25; -pub(crate) fn cross_validate_entities(entities: &Vec>, entity_name: &str) +pub(crate) fn cross_validate_entities(entities: &[Vec], entity_name: &str) where U: PartialEq + Debug, { diff --git a/crates/sui-sdk/src/lib.rs b/crates/sui-sdk/src/lib.rs index 945a0e0ba5c86..9200d8451e5fc 100644 --- a/crates/sui-sdk/src/lib.rs +++ b/crates/sui-sdk/src/lib.rs @@ -14,9 +14,9 @@ //! * [EventApi] - provides event related functions functions to //! * [GovernanceApi] - provides functionality related to staking //! * [QuorumDriverApi] - provides functionality to execute a transaction -//! block and submit it to the fullnode(s) +//! block and submit it to the fullnode(s) //! * [ReadApi] - provides functions for retrieving data about different -//! objects and transactions +//! objects and transactions //! * TransactionBuilder - provides functions for building transactions //! //! # Usage diff --git a/crates/sui-simulator/Cargo.toml b/crates/sui-simulator/Cargo.toml index 5d2f864250cc3..4d12c46ef42a4 100644 --- a/crates/sui-simulator/Cargo.toml +++ b/crates/sui-simulator/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] move-package.workspace = true diff --git a/crates/sui-simulator/src/lib.rs b/crates/sui-simulator/src/lib.rs index 086d2f1282184..91fa6b5937d26 100644 --- a/crates/sui-simulator/src/lib.rs +++ b/crates/sui-simulator/src/lib.rs @@ -110,7 +110,7 @@ pub mod configs { } thread_local! { - static NODE_COUNT: AtomicUsize = AtomicUsize::new(0); + static NODE_COUNT: AtomicUsize = const { AtomicUsize::new(0) }; } pub struct NodeLeakDetector(()); diff --git a/crates/sui-snapshot/src/lib.rs b/crates/sui-snapshot/src/lib.rs index a19a090770d5c..ac083f021efe1 100644 --- a/crates/sui-snapshot/src/lib.rs +++ b/crates/sui-snapshot/src/lib.rs @@ -72,6 +72,7 @@ use tokio::time::Instant; /// - epoch_1/ /// - 1_1.obj /// - ... +/// /// Object File Disk Format ///┌──────────────────────────────┐ ///│ magic(0x00B7EC75) <4 byte> │ diff --git a/crates/sui-storage/Cargo.toml b/crates/sui-storage/Cargo.toml index e34973c342c0b..ff541a7b7e962 100644 --- a/crates/sui-storage/Cargo.toml +++ b/crates/sui-storage/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] integer-encoding.workspace = true async-trait.workspace = true diff --git a/crates/sui-storage/src/http_key_value_store.rs b/crates/sui-storage/src/http_key_value_store.rs index 9232bdfb1bc50..461a441cd2a02 100644 --- a/crates/sui-storage/src/http_key_value_store.rs +++ b/crates/sui-storage/src/http_key_value_store.rs @@ -148,12 +148,7 @@ impl HttpKVStore { async fn multi_fetch(&self, uris: Vec) -> Vec>> { let uris_vec = uris.to_vec(); - let fetches = stream::iter( - uris_vec - .into_iter() - .enumerate() - .map(|(_i, url)| self.fetch(url)), - ); + let fetches = stream::iter(uris_vec.into_iter().map(|url| self.fetch(url))); fetches.buffered(uris.len()).collect::>().await } @@ -221,7 +216,7 @@ fn multi_split_slice<'a, T>(slice: &'a [T], lengths: &'a [usize]) -> Vec<&'a [T] .collect() } -fn deser_check_digest( +fn deser_check_digest( digest: &D, bytes: &Bytes, get_expected_digest: impl FnOnce(&T) -> D, diff --git a/crates/sui-swarm-config/Cargo.toml b/crates/sui-swarm-config/Cargo.toml index 8c6561469549f..12a367ccbd6c4 100644 --- a/crates/sui-swarm-config/Cargo.toml +++ b/crates/sui-swarm-config/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] anemo.workspace = true anyhow.workspace = true diff --git a/crates/sui-swarm/Cargo.toml b/crates/sui-swarm/Cargo.toml index b67f22a309465..51caeadf3b797 100644 --- a/crates/sui-swarm/Cargo.toml +++ b/crates/sui-swarm/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] anyhow.workspace = true rand.workspace = true diff --git a/crates/sui-swarm/src/memory/container.rs b/crates/sui-swarm/src/memory/container.rs index edbb5e4b4f146..b763fc7893cbc 100644 --- a/crates/sui-swarm/src/memory/container.rs +++ b/crates/sui-swarm/src/memory/container.rs @@ -69,7 +69,7 @@ impl Container { RuntimeType::MultiThreaded => { thread_local! { static SPAN: std::cell::RefCell> = - std::cell::RefCell::new(None); + const { std::cell::RefCell::new(None) }; } let mut builder = tokio::runtime::Builder::new_multi_thread(); let span = span.clone(); diff --git a/crates/sui-tool/src/commands.rs b/crates/sui-tool/src/commands.rs index 5c352104c8aaa..d4a6eb83facfc 100644 --- a/crates/sui-tool/src/commands.rs +++ b/crates/sui-tool/src/commands.rs @@ -22,7 +22,6 @@ use telemetry_subscribers::TracingHandle; use sui_types::{ base_types::*, crypto::AuthorityPublicKeyBytes, messages_grpc::TransactionInfoRequest, - object::Owner, }; use clap::*; @@ -419,59 +418,6 @@ pub enum ToolCommand { }, } -trait OptionDebug { - fn opt_debug(&self, def_str: &str) -> String; -} -trait OptionDisplay { - fn opt_display(&self, def_str: &str) -> String; -} - -impl OptionDebug for Option -where - T: std::fmt::Debug, -{ - fn opt_debug(&self, def_str: &str) -> String { - match self { - None => def_str.to_string(), - Some(t) => format!("{:?}", t), - } - } -} - -impl OptionDisplay for Option -where - T: std::fmt::Display, -{ - fn opt_display(&self, def_str: &str) -> String { - match self { - None => def_str.to_string(), - Some(t) => format!("{}", t), - } - } -} - -struct OwnerOutput(Owner); - -// grep/awk-friendly output for Owner -impl std::fmt::Display for OwnerOutput { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match &self.0 { - Owner::AddressOwner(address) => { - write!(f, "address({})", address) - } - Owner::ObjectOwner(address) => { - write!(f, "object({})", address) - } - Owner::Immutable => { - write!(f, "immutable") - } - Owner::Shared { .. } => { - write!(f, "shared") - } - } - } -} - async fn check_locked_object( sui_client: &Arc, committee: Arc>, diff --git a/crates/sui-tool/src/lib.rs b/crates/sui-tool/src/lib.rs index b4290dc48b510..a49ac1b5e9498 100644 --- a/crates/sui-tool/src/lib.rs +++ b/crates/sui-tool/src/lib.rs @@ -127,9 +127,6 @@ pub struct ObjectData { trait OptionDebug { fn opt_debug(&self, def_str: &str) -> String; } -trait OptionDisplay { - fn opt_display(&self, def_str: &str) -> String; -} impl OptionDebug for Option where @@ -143,40 +140,6 @@ where } } -impl OptionDisplay for Option -where - T: std::fmt::Display, -{ - fn opt_display(&self, def_str: &str) -> String { - match self { - None => def_str.to_string(), - Some(t) => format!("{}", t), - } - } -} - -struct OwnerOutput(Owner); - -// grep/awk-friendly output for Owner -impl std::fmt::Display for OwnerOutput { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match &self.0 { - Owner::AddressOwner(address) => { - write!(f, "address({})", address) - } - Owner::ObjectOwner(address) => { - write!(f, "object({})", address) - } - Owner::Immutable => { - write!(f, "immutable") - } - Owner::Shared { .. } => { - write!(f, "shared") - } - } - } -} - #[allow(clippy::type_complexity)] pub struct GroupedObjectOutput { pub grouped_results: BTreeMap< diff --git a/crates/sui-transactional-test-runner/Cargo.toml b/crates/sui-transactional-test-runner/Cargo.toml index 46fd3ab10242f..d602aea50457a 100644 --- a/crates/sui-transactional-test-runner/Cargo.toml +++ b/crates/sui-transactional-test-runner/Cargo.toml @@ -7,6 +7,9 @@ description = "Move framework for Sui platform" license = "Apache-2.0" publish = false +[lints] +workspace = true + [dependencies] anyhow.workspace = true bcs.workspace = true diff --git a/crates/sui-transactional-test-runner/src/simulator_persisted_store.rs b/crates/sui-transactional-test-runner/src/simulator_persisted_store.rs index 2c2e9de3c7b90..64606fa58dc0d 100644 --- a/crates/sui-transactional-test-runner/src/simulator_persisted_store.rs +++ b/crates/sui-transactional-test-runner/src/simulator_persisted_store.rs @@ -313,16 +313,12 @@ impl SimulatorStore for PersistedStore { fn insert_committee(&mut self, committee: Committee) { let epoch = committee.epoch as usize; - let mut committees = if let Some(c) = self + let mut committees = self .read_write .epoch_to_committee .get(&()) .expect("Fatal: DB read failed") - { - c - } else { - vec![] - }; + .unwrap_or_default(); if committees.get(epoch).is_some() { return; @@ -397,16 +393,12 @@ impl SimulatorStore for PersistedStore { .live_objects .insert(&object_id, &version) .expect("Fatal: DB write failed"); - let mut q = if let Some(x) = self + let mut q = self .read_write .objects .get(&object_id) .expect("Fatal: DB read failed") - { - x - } else { - BTreeMap::new() - }; + .unwrap_or_default(); q.insert(version, object); self.read_write .objects diff --git a/crates/sui-types/Cargo.toml b/crates/sui-types/Cargo.toml index 90c35908f1b0a..9943fc5465d25 100644 --- a/crates/sui-types/Cargo.toml +++ b/crates/sui-types/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] async-trait.workspace = true anemo.workspace = true diff --git a/crates/sui-types/src/crypto.rs b/crates/sui-types/src/crypto.rs index 16c83f6fa2493..792906ce4f05e 100644 --- a/crates/sui-types/src/crypto.rs +++ b/crates/sui-types/src/crypto.rs @@ -730,7 +730,7 @@ impl Signature { // itself that computes the BCS hash of the Rust type prefix and `struct TransactionData`. // (See `fn digest` in `impl Message for SenderSignedData`). let mut hasher = DefaultHash::default(); - hasher.update(&bcs::to_bytes(&value).expect("Message serialization should not fail")); + hasher.update(bcs::to_bytes(&value).expect("Message serialization should not fail")); Signer::sign(secret, &hasher.finalize().digest) } @@ -999,7 +999,7 @@ impl SuiSignature for S { T: Serialize, { let mut hasher = DefaultHash::default(); - hasher.update(&bcs::to_bytes(&value).expect("Message serialization should not fail")); + hasher.update(bcs::to_bytes(&value).expect("Message serialization should not fail")); let digest = hasher.finalize().digest; let (sig, pk) = &self.get_verification_inputs()?; diff --git a/crates/sui-types/src/execution.rs b/crates/sui-types/src/execution.rs index c4bcf7ba293b8..53dbcfbd74797 100644 --- a/crates/sui-types/src/execution.rs +++ b/crates/sui-types/src/execution.rs @@ -175,18 +175,21 @@ impl ExecutionResultsV2 { /// gas smashing). Because this list is not gated by protocol version, there are a few important /// criteria for adding a digest to this list: /// 1. The certificate must be causing all validators to either panic or hang forever deterministically. -/// 2. If we ever ship a fix to make it no longer panic or hang when executing such transaction, -/// we must make sure the transaction is already in this list. Otherwise nodes running the newer version -/// without these transactions in the list will generate forked result. +/// 2. If we ever ship a fix to make it no longer panic or hang when executing such transaction, we +/// must make sure the transaction is already in this list. Otherwise nodes running the newer +/// version without these transactions in the list will generate forked result. +/// /// Below is a scenario of when we need to use this list: /// 1. We detect that a specific transaction is causing all validators to either panic or hang forever deterministically. /// 2. We push a CertificateDenyConfig to deny such transaction to all validators asap. -/// 3. To make sure that all fullnodes are able to sync to the latest version, we need to add the transaction digest -/// to this list as well asap, and ship this binary to all fullnodes, so that they can sync past this transaction. +/// 3. To make sure that all fullnodes are able to sync to the latest version, we need to add the +/// transaction digest to this list as well asap, and ship this binary to all fullnodes, so that +/// they can sync past this transaction. /// 4. We then can start fixing the issue, and ship the fix to all nodes. -/// 5. Unfortunately, we can't remove the transaction digest from this list, because if we do so, any future -/// node that sync from genesis will fork on this transaction. We may be able to remove it once -/// we have stable snapshots and the binary has a minimum supported protocol version past the epoch. +/// 5. Unfortunately, we can't remove the transaction digest from this list, because if we do so, +/// any future node that sync from genesis will fork on this transaction. We may be able to +/// remove it once we have stable snapshots and the binary has a minimum supported protocol +/// version past the epoch. pub fn get_denied_certificates() -> &'static HashSet { static DENIED_CERTIFICATES: Lazy> = Lazy::new(|| HashSet::from([])); Lazy::force(&DENIED_CERTIFICATES) diff --git a/crates/sui-types/src/full_checkpoint_content.rs b/crates/sui-types/src/full_checkpoint_content.rs index 4d0cb95217bbc..2c8eef47f237e 100644 --- a/crates/sui-types/src/full_checkpoint_content.rs +++ b/crates/sui-types/src/full_checkpoint_content.rs @@ -177,7 +177,7 @@ impl CheckpointTransaction { ObjectIn::NotExist, ObjectOut::PackageWrite((version, _)), IDOperation::Created, - ) => Some(((id, &version), None)), + ) => Some(((id, version), None)), // Unwrapped Objects (ObjectIn::NotExist, ObjectOut::ObjectWrite(_), IDOperation::None) => { @@ -241,7 +241,7 @@ impl CheckpointTransaction { ObjectIn::NotExist, ObjectOut::PackageWrite((version, _)), IDOperation::Created, - ) => Some((id, &version)), + ) => Some((id, version)), _ => None, } diff --git a/crates/sui-types/src/messages_grpc.rs b/crates/sui-types/src/messages_grpc.rs index f29707cca75b7..ffe8001980e68 100644 --- a/crates/sui-types/src/messages_grpc.rs +++ b/crates/sui-types/src/messages_grpc.rs @@ -233,6 +233,7 @@ impl From for HandleCertificateResponseV2 { /// If `wait_for_effects` is true, it is guaranteed that: /// - Number of responses will be equal to the number of input transactions. /// - The order of the responses matches the order of the input transactions. +/// /// Otherwise, `responses` will be empty. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct HandleSoftBundleCertificatesResponseV3 { diff --git a/crates/sui-types/tests/serde_tests.rs b/crates/sui-types/tests/serde_tests.rs index cd7056857b06b..2343c90b91329 100644 --- a/crates/sui-types/tests/serde_tests.rs +++ b/crates/sui-types/tests/serde_tests.rs @@ -18,7 +18,7 @@ fn test_struct_tag_serde() { struct TestStructTag(#[serde_as(as = "SuiStructTag")] StructTag); // serialize to json should not trim the leading 0 - let Value::String(json) = serde_json::to_value(&TestStructTag(tag.clone())).unwrap() else { + let Value::String(json) = serde_json::to_value(TestStructTag(tag.clone())).unwrap() else { panic!() }; assert_eq!(json, "0x07f89cdffd8968affa0b47bef91adc5314e19509080470c45bfd434cd83a766b::suifrens::SuiFren<0x07f89cdffd8968affa0b47bef91adc5314e19509080470c45bfd434cd83a766b::capy::Capy>"); diff --git a/crates/sui/Cargo.toml b/crates/sui/Cargo.toml index 9c06cb2893505..50afe8ab31472 100644 --- a/crates/sui/Cargo.toml +++ b/crates/sui/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] anemo.workspace = true anyhow.workspace = true diff --git a/crates/sui/src/client_commands.rs b/crates/sui/src/client_commands.rs index 58d6a75f60d96..aec1751251eb1 100644 --- a/crates/sui/src/client_commands.rs +++ b/crates/sui/src/client_commands.rs @@ -2715,6 +2715,7 @@ pub async fn execute_dry_run( /// Call a dry run with the transaction data to estimate the gas budget. /// 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 diff --git a/crates/sui/src/client_ptb/lexer.rs b/crates/sui/src/client_ptb/lexer.rs index 749e8f724ada6..2338683ffadad 100644 --- a/crates/sui/src/client_ptb/lexer.rs +++ b/crates/sui/src/client_ptb/lexer.rs @@ -17,9 +17,7 @@ pub struct Lexer<'l, I: Iterator> { impl<'l, I: Iterator> Lexer<'l, I> { pub fn new(mut tokens: I) -> Option { - let Some(buf) = tokens.next() else { - return None; - }; + let buf = tokens.next()?; Some(Self { buf, @@ -63,9 +61,7 @@ impl<'l, I: Iterator> Lexer<'l, I> { fn eat_prefix(&mut self, patt: &str) -> Option> { let start = self.offset; - let Some(rest) = self.buf.strip_prefix(patt) else { - return None; - }; + let rest = self.buf.strip_prefix(patt)?; let len = self.buf.len() - rest.len(); let value = &self.buf[..len]; @@ -120,9 +116,7 @@ impl<'l, I: Iterator> Lexer<'l, I> { /// Look at the next character in the current shell token without consuming it, if it exists. fn peek(&self) -> Option> { let start = self.offset; - let Some((ix, _)) = self.next_char_boundary() else { - return None; - }; + let (ix, _) = self.next_char_boundary()?; let value = &self.buf[..ix]; let span = Span { diff --git a/crates/sui/tests/cli_tests.rs b/crates/sui/tests/cli_tests.rs index 00c2f5034172e..7d0c374dce9df 100644 --- a/crates/sui/tests/cli_tests.rs +++ b/crates/sui/tests/cli_tests.rs @@ -691,7 +691,7 @@ async fn test_move_call_args_linter_command() -> Result<(), anyhow::Error> { // Try a transfer // This should fail due to mismatch of object being sent - let args = vec![ + let args = [ SuiJsonValue::new(json!(obj))?, SuiJsonValue::new(json!(address2))?, ]; @@ -712,7 +712,7 @@ async fn test_move_call_args_linter_command() -> Result<(), anyhow::Error> { // Try a transfer with explicitly set gas price. // It should fail due to that gas price is below RGP. - let args = vec![ + let args = [ SuiJsonValue::new(json!(created_obj))?, SuiJsonValue::new(json!(address2))?, ]; @@ -740,7 +740,7 @@ async fn test_move_call_args_linter_command() -> Result<(), anyhow::Error> { // assert!(err_string.contains(&format!("Expected argument of type {package_addr}::object_basics::Object, but found type {framework_addr}::coin::Coin<{framework_addr}::sui::SUI>"))); // Try a proper transfer - let args = vec![ + let args = [ SuiJsonValue::new(json!(created_obj))?, SuiJsonValue::new(json!(address2))?, ]; diff --git a/crates/suiop-cli/src/command.rs b/crates/suiop-cli/src/command.rs index c14c39cb3e4f2..8247dbb9938f1 100644 --- a/crates/suiop-cli/src/command.rs +++ b/crates/suiop-cli/src/command.rs @@ -38,11 +38,7 @@ impl Default for CommandOptions { pub fn run_cmd(cmd_in: Vec<&str>, options: Option) -> Result { debug!("attempting to run {}", cmd_in.join(" ")); - let opts = if let Some(opts) = options { - opts - } else { - CommandOptions::default() - }; + let opts = options.unwrap_or_default(); let mut cmd = Command::new(cmd_in[0]); // add extra args diff --git a/crates/telemetry-subscribers/src/lib.rs b/crates/telemetry-subscribers/src/lib.rs index ac032093bb193..7221034ad9273 100644 --- a/crates/telemetry-subscribers/src/lib.rs +++ b/crates/telemetry-subscribers/src/lib.rs @@ -500,7 +500,7 @@ impl SamplingFilter { fn clamp(sample_rate: f64) -> f64 { // clamp sample rate to between 0.0001 and 1.0 - sample_rate.max(0.0001).min(1.0) + sample_rate.clamp(0.0001, 1.0) } fn update_sampling_rate(&self, sample_rate: f64) { diff --git a/crates/telemetry-subscribers/src/span_latency_prom.rs b/crates/telemetry-subscribers/src/span_latency_prom.rs index a42783d84684b..476bf30151980 100644 --- a/crates/telemetry-subscribers/src/span_latency_prom.rs +++ b/crates/telemetry-subscribers/src/span_latency_prom.rs @@ -10,6 +10,7 @@ //! - tracing-timing does not output to Prometheus, and extracting data from its histograms takes extra CPU //! - tracing-timing records latencies using HDRHistogram, which is great, but uses extra memory when one //! is already using Prometheus +//! //! Thus this is a much smaller and more focused module. //! //! ## Making spans visible diff --git a/crates/test-cluster/Cargo.toml b/crates/test-cluster/Cargo.toml index 826433cb330f2..6f967e93389b8 100644 --- a/crates/test-cluster/Cargo.toml +++ b/crates/test-cluster/Cargo.toml @@ -6,6 +6,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] anyhow.workspace = true bcs.workspace = true diff --git a/crates/transaction-fuzzer/Cargo.toml b/crates/transaction-fuzzer/Cargo.toml index 15fe64e0735b4..106bebdf5a8d1 100644 --- a/crates/transaction-fuzzer/Cargo.toml +++ b/crates/transaction-fuzzer/Cargo.toml @@ -7,6 +7,9 @@ description = "Tool to fuzz the system with randomly generated transactions" license = "Apache-2.0" publish = false +[lints] +workspace = true + [dependencies] proptest.workspace = true proptest-derive.workspace = true diff --git a/crates/transaction-fuzzer/src/programmable_transaction_gen.rs b/crates/transaction-fuzzer/src/programmable_transaction_gen.rs index 85de6eabbe2a6..3526a02009d61 100644 --- a/crates/transaction-fuzzer/src/programmable_transaction_gen.rs +++ b/crates/transaction-fuzzer/src/programmable_transaction_gen.rs @@ -404,7 +404,7 @@ pub fn gen_move_vec_input( /// A helper function to generate enough input coins for a command (transfer, merge, or create vector) /// - either collect them all from previous command or generate additional ones if the previous -/// command does not deliver enough. +/// command does not deliver enough. fn gen_enough_arguments( builder: &mut ProgrammableTransactionBuilder, prev_cmd_num: i64, diff --git a/crates/typed-store-derive/src/lib.rs b/crates/typed-store-derive/src/lib.rs index 9b0c73b8acd12..be1d81862cb8d 100644 --- a/crates/typed-store-derive/src/lib.rs +++ b/crates/typed-store-derive/src/lib.rs @@ -93,7 +93,7 @@ fn extract_struct_info( } else { field_name.clone() }; - if attrs.get(DB_OPTIONS_DEPRECATE).is_some() { + if attrs.contains_key(DB_OPTIONS_DEPRECATE) { deprecated_cfs.push(field_name.clone()); } diff --git a/crates/typed-store/src/lib.rs b/crates/typed-store/src/lib.rs index 734d22dc9cfea..00743e16fd65c 100644 --- a/crates/typed-store/src/lib.rs +++ b/crates/typed-store/src/lib.rs @@ -33,7 +33,8 @@ pub type StoreError = typed_store_error::TypedStoreError; /// 5. Other convenience features /// /// 1. Flexible configuration: -/// a. Static options specified at struct definition +/// a. Static options specified at struct definition +/// /// The definer of the struct can specify the default options for each table using annotations /// We can also supply column family options on the default ones /// A user defined function of signature () -> Options can be provided for each table @@ -90,10 +91,10 @@ pub type StoreError = typed_store_error::TypedStoreError; ///``` /// /// 2. Auto-generated `open` routine -/// The function `open_tables_read_write` is generated which allows for specifying DB wide options and custom table configs as mentioned above +/// The function `open_tables_read_write` is generated which allows for specifying DB wide options and custom table configs as mentioned above /// /// 3. Auto-generated `read_only_mode` handle -/// This mode provides handle struct which opens the DB in read only mode and has certain features like dumping and counting the keys in the tables +/// This mode provides handle struct which opens the DB in read only mode and has certain features like dumping and counting the keys in the tables /// /// Use the function `Tables::get_read_only_handle` which returns a handle that only allows read only features ///``` @@ -138,10 +139,10 @@ pub type StoreError = typed_store_error::TypedStoreError; /// } /// ``` /// 4. Auto-generated memory stats method -/// `self.get_memory_usage` is derived to provide memory and cache usage +/// `self.get_memory_usage` is derived to provide memory and cache usage /// /// 5. Other convenience features -/// `Tables::describe_tables` is used to get a list of the table names and key-value types as string in a BTreeMap +/// `Tables::describe_tables` is used to get a list of the table names and key-value types as string in a BTreeMap /// /// // Bad usage example /// // Structs fields most only be of type Store or DMBap diff --git a/crates/typed-store/src/rocks/mod.rs b/crates/typed-store/src/rocks/mod.rs index 752b42fba8ab3..ca6505b9a448e 100644 --- a/crates/typed-store/src/rocks/mod.rs +++ b/crates/typed-store/src/rocks/mod.rs @@ -88,9 +88,8 @@ mod tests; /// # Arguments /// /// * `db` - a reference to a rocks DB object -/// * `cf;` - a comma separated list of column families to open. For each -/// column family a concatenation of column family name (cf) and Key-Value -/// should be provided. +/// * `cf;` - a comma separated list of column families to open. For each column family a +/// concatenation of column family name (cf) and Key-Value should be provided. /// /// # Examples /// @@ -2735,7 +2734,7 @@ fn populate_missing_cfs( /// Given a vec, find the value which is one more than the vector /// if the vector was a big endian number. /// If the vector is already minimum, don't change it. -fn big_endian_saturating_add_one(v: &mut Vec) { +fn big_endian_saturating_add_one(v: &mut [u8]) { if is_max(v) { return; } @@ -2769,7 +2768,6 @@ fn test_helpers() { uint::construct_uint! { // 32 byte number - #[cfg_attr(feature = "scale-info", derive(TypeInfo))] struct Num32(4); } diff --git a/crates/typed-store/tests/macro_tests.rs b/crates/typed-store/tests/macro_tests.rs index ccd040ebddef9..dc8252864c144 100644 --- a/crates/typed-store/tests/macro_tests.rs +++ b/crates/typed-store/tests/macro_tests.rs @@ -166,13 +166,13 @@ async fn macro_test() { // Test pagination let m = tbls_secondary.dump("table1", 2, 0).unwrap(); assert_eq!(2, m.len()); - assert_eq!(format!("\"1\""), *m.get(&"\"1\"".to_string()).unwrap()); - assert_eq!(format!("\"2\""), *m.get(&"\"2\"".to_string()).unwrap()); + assert_eq!(format!("\"1\""), *m.get("\"1\"").unwrap()); + assert_eq!(format!("\"2\""), *m.get("\"2\"").unwrap()); let m = tbls_secondary.dump("table1", 3, 2).unwrap(); assert_eq!(3, m.len()); - assert_eq!(format!("\"7\""), *m.get(&"\"7\"".to_string()).unwrap()); - assert_eq!(format!("\"8\""), *m.get(&"\"8\"".to_string()).unwrap()); + assert_eq!(format!("\"7\""), *m.get("\"7\"").unwrap()); + assert_eq!(format!("\"8\""), *m.get("\"8\"").unwrap()); } #[tokio::test] @@ -306,13 +306,13 @@ async fn test_sallydb() { // Test pagination let m = example_db_secondary.dump("col1", 2, 0).unwrap(); assert_eq!(2, m.len()); - assert_eq!(format!("\"1\""), *m.get(&"\"1\"".to_string()).unwrap()); - assert_eq!(format!("\"2\""), *m.get(&"\"2\"".to_string()).unwrap()); + assert_eq!(format!("\"1\""), *m.get("\"1\"").unwrap()); + assert_eq!(format!("\"2\""), *m.get("\"2\"").unwrap()); let m = example_db_secondary.dump("col1", 3, 2).unwrap(); assert_eq!(3, m.len()); - assert_eq!(format!("\"7\""), *m.get(&"\"7\"".to_string()).unwrap()); - assert_eq!(format!("\"8\""), *m.get(&"\"8\"".to_string()).unwrap()); + assert_eq!(format!("\"7\""), *m.get("\"7\"").unwrap()); + assert_eq!(format!("\"8\""), *m.get("\"8\"").unwrap()); } #[tokio::test] diff --git a/deny.toml b/deny.toml index 4d26ba89ccb0e..b1f837e63143e 100644 --- a/deny.toml +++ b/deny.toml @@ -9,42 +9,15 @@ # The values provided in this template are the default values that will be used # when any section or field is not specified in your own configuration -# If 1 or more target triples (and optionally, target_features) are specified, -# only the specified targets will be checked when running `cargo deny check`. -# This means, if a particular package is only ever used as a target specific -# dependency, such as, for example, the `nix` crate only being used via the -# `target_family = "unix"` configuration, that only having windows targets in -# this list would mean the nix crate, as well as any of its exclusive -# dependencies not shared by any other crates, would be ignored, as the target -# list here is effectively saying which targets you are building for. -targets = [ - # The triple can be any string, but only the target triples built in to - # rustc (as of 1.40) can be checked against actual config expressions - #{ triple = "x86_64-unknown-linux-musl" }, - # You can also specify which target_features you promise are enabled for a - # particular target. target_features are currently not validated against - # the actual valid features supported by the target architecture. - #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, -] - # This section is considered when running `cargo deny check advisories` # More documentation for the advisories section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html [advisories] +version = 2 # The path where the advisory database is cloned/fetched into db-path = "~/.cargo/advisory-db" # The url(s) of the advisory databases to use db-urls = ["https://github.com/rustsec/advisory-db"] -# The lint level for security vulnerabilities -vulnerability = "deny" -# The lint level for unmaintained crates -unmaintained = "warn" -# The lint level for crates that have been yanked from their source registry -yanked = "warn" -# The lint level for crates with security notices. Note that as of -# 2019-12-17 there are no security notice advisories in -# https://github.com/rustsec/advisory-db -notice = "warn" # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. ignore = [ @@ -55,8 +28,6 @@ ignore = [ "RUSTSEC-2022-0071", # `tui` is unmaintained; use `ratatui` instead "RUSTSEC-2023-0049", - # ansi_term is Unmaintained - "RUSTSEC-2021-0139", # we don't do RSA signing on Sui (only verifying for zklogin) "RUSTSEC-2023-0071", # A few dependencies use unpatched rustls. @@ -78,8 +49,7 @@ ignore = [ # More documentation for the licenses section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html [licenses] -# The lint level for crates which do not have a detectable license -unlicensed = "deny" +version = 2 # List of explicitly allowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.11 short identifier (+ optional exception)]. @@ -98,26 +68,6 @@ allow = [ "Unicode-DFS-2016", #"Apache-2.0 WITH LLVM-exception", ] -# List of explicitly disallowed licenses -# See https://spdx.org/licenses/ for list of possible licenses -# [possible values: any SPDX 3.11 short identifier (+ optional exception)]. -deny = [ - #"Nokia", -] -# Lint level for licenses considered copyleft -copyleft = "deny" -# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses -# * both - The license will be approved if it is both OSI-approved *AND* FSF -# * either - The license will be approved if it is either OSI-approved *OR* FSF -# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF -# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved -# * neither - This predicate is ignored and the default lint level is used -allow-osi-fsf-free = "neither" -# Lint level used when no other predicates are matched -# 1. License isn't in the allow or deny lists -# 2. License isn't copyleft -# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" -default = "deny" # The confidence threshold for detecting a license from license text. # The higher the value, the more closely the license text must be to the # canonical license text of a valid SPDX license file. @@ -251,8 +201,6 @@ github = [ "mystenmark", "bmwill", "mystenlabs", - "MystenLabs", "nextest-rs", "wlmyng", # jsonrpsee fork - "quinn-rs", ] diff --git a/docker/deterministic-canary/Dockerfile b/docker/deterministic-canary/Dockerfile index 67f253135d3b6..5ebdef630f182 100644 --- a/docker/deterministic-canary/Dockerfile +++ b/docker/deterministic-canary/Dockerfile @@ -1,7 +1,7 @@ ARG PROFILE=release # ARG BUILD_DATE # ARG GIT_REVISION -ARG RUST_VERSION=1.76.0 +ARG RUST_VERSION=1.80.1 FROM scratch AS base diff --git a/docker/sui-bridge-indexer/Dockerfile b/docker/sui-bridge-indexer/Dockerfile index 3be48a6e006cb..4f0fcde7b9c42 100644 --- a/docker/sui-bridge-indexer/Dockerfile +++ b/docker/sui-bridge-indexer/Dockerfile @@ -2,7 +2,7 @@ # # Copy in all crates, Cargo.toml and Cargo.lock unmodified, # and build the application. -FROM rust:1.75-bullseye AS builder +FROM rust:1.80.1-bullseye AS builder ARG PROFILE=release ARG GIT_REVISION ENV GIT_REVISION=$GIT_REVISION diff --git a/docker/sui-graphql-rpc/Dockerfile b/docker/sui-graphql-rpc/Dockerfile index 0035b8268bd81..049b8603e39c8 100644 --- a/docker/sui-graphql-rpc/Dockerfile +++ b/docker/sui-graphql-rpc/Dockerfile @@ -2,7 +2,7 @@ # # Copy in all crates, Cargo.toml and Cargo.lock unmodified, # and build the application. -FROM rust:1.75-bullseye AS builder +FROM rust:1.80.1-bullseye AS builder ARG PROFILE=release ENV PROFILE=$PROFILE ARG GIT_REVISION diff --git a/docker/sui-indexer-tidb/Dockerfile b/docker/sui-indexer-tidb/Dockerfile index d1d46bf81024c..6d11dc569bd60 100644 --- a/docker/sui-indexer-tidb/Dockerfile +++ b/docker/sui-indexer-tidb/Dockerfile @@ -2,7 +2,7 @@ # # Copy in all crates, Cargo.toml and Cargo.lock unmodified, # and build the application. -FROM rust:1.75-bullseye AS builder +FROM rust:1.80.1-bullseye AS builder ARG PROFILE=release ARG GIT_REVISION ENV GIT_REVISION=$GIT_REVISION diff --git a/docker/sui-indexer/Dockerfile b/docker/sui-indexer/Dockerfile index db27235841255..98635444f78c3 100644 --- a/docker/sui-indexer/Dockerfile +++ b/docker/sui-indexer/Dockerfile @@ -2,7 +2,7 @@ # # Copy in all crates, Cargo.toml and Cargo.lock unmodified, # and build the application. -FROM rust:1.75-bullseye AS builder +FROM rust:1.80.1-bullseye AS builder ARG PROFILE=release ARG GIT_REVISION ENV GIT_REVISION=$GIT_REVISION diff --git a/docker/sui-node/Dockerfile b/docker/sui-node/Dockerfile index 190e352cc6da6..e9557b7698adc 100644 --- a/docker/sui-node/Dockerfile +++ b/docker/sui-node/Dockerfile @@ -2,7 +2,7 @@ # # Copy in all crates, Cargo.toml and Cargo.lock unmodified, # and build the application. -FROM rust:1.75-bullseye AS builder +FROM rust:1.80.1-bullseye AS builder ARG PROFILE=release ARG GIT_REVISION ENV GIT_REVISION=$GIT_REVISION diff --git a/docker/sui-services/Dockerfile b/docker/sui-services/Dockerfile index dd9cc69f5edda..2002fe6b9140b 100644 --- a/docker/sui-services/Dockerfile +++ b/docker/sui-services/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.75-bullseye AS chef +FROM rust:1.80.1-bullseye AS chef WORKDIR sui ARG GIT_REVISION ENV GIT_REVISION=$GIT_REVISION diff --git a/docker/sui-source-service/Dockerfile b/docker/sui-source-service/Dockerfile index bb73ef2e3e0d5..0936bd0562256 100644 --- a/docker/sui-source-service/Dockerfile +++ b/docker/sui-source-service/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.75-bullseye AS chef +FROM rust:1.80.1-bullseye AS chef WORKDIR sui ARG GIT_REVISION ENV GIT_REVISION=$GIT_REVISION diff --git a/docker/sui-tools/Dockerfile b/docker/sui-tools/Dockerfile index e475fd2abf160..4f800e03b01e6 100644 --- a/docker/sui-tools/Dockerfile +++ b/docker/sui-tools/Dockerfile @@ -2,7 +2,7 @@ # # Copy in all crates, Cargo.toml and Cargo.lock unmodified, # and build the application. -FROM rust:1.75-bullseye AS builder +FROM rust:1.80.1-bullseye AS builder ARG PROFILE=release ARG GIT_REVISION ENV GIT_REVISION=$GIT_REVISION diff --git a/external-crates/move/Cargo.toml b/external-crates/move/Cargo.toml index 8bac472e296d6..4aeea3a73a592 100644 --- a/external-crates/move/Cargo.toml +++ b/external-crates/move/Cargo.toml @@ -83,7 +83,7 @@ parking_lot = "0.11.1" paste = "1.0.5" petgraph = "0.5.1" phf = { version = "0.11", features = ["macros"] } -plotters = { version = "0.3.0", default_features = false, features = ["evcxr", "line_series", "histogram"]} +plotters = { version = "0.3.0", default-features = false, features = ["evcxr", "line_series", "histogram"]} pretty = "0.10.0" prettydiff = "0.4.0" primitive-types = { version = "0.10.1", features = ["impl-serde"]} diff --git a/external-crates/move/crates/move-binary-format/src/file_format.rs b/external-crates/move/crates/move-binary-format/src/file_format.rs index ff60d27ee3849..c65207ff96e15 100644 --- a/external-crates/move/crates/move-binary-format/src/file_format.rs +++ b/external-crates/move/crates/move-binary-format/src/file_format.rs @@ -12,15 +12,15 @@ //! //! Overall the binary format is structured in a number of sections: //! - **Header**: this must start at offset 0 in the binary. It contains a blob that starts every -//! Diem binary, followed by the version of the VM used to compile the code, and last is the -//! number of tables present in this binary. +//! Diem binary, followed by the version of the VM used to compile the code, and last is the +//! number of tables present in this binary. //! - **Table Specification**: it's a number of tuple of the form -//! `(table type, starting_offset, byte_count)`. The number of entries is specified in the -//! header (last entry in header). There can only be a single entry per table type. The -//! `starting offset` is from the beginning of the binary. Tables must cover the entire size of -//! the binary blob and cannot overlap. +//! `(table type, starting_offset, byte_count)`. The number of entries is specified in the +//! header (last entry in header). There can only be a single entry per table type. The +//! `starting offset` is from the beginning of the binary. Tables must cover the entire size of +//! the binary blob and cannot overlap. //! - **Table Content**: the serialized form of the specific entries in the table. Those roughly -//! map to the structs defined in this module. Entries in each table must be unique. +//! map to the structs defined in this module. Entries in each table must be unique. //! //! We have two formats: one for modules here represented by `CompiledModule`, another //! for transaction scripts which is `CompiledScript`. Building those tables and passing them diff --git a/external-crates/move/crates/move-binary-format/src/file_format_common.rs b/external-crates/move/crates/move-binary-format/src/file_format_common.rs index 7c81c755a012a..7d006ef74f9f9 100644 --- a/external-crates/move/crates/move-binary-format/src/file_format_common.rs +++ b/external-crates/move/crates/move-binary-format/src/file_format_common.rs @@ -334,7 +334,7 @@ pub enum Opcodes { } /// Upper limit on the binary size -pub const BINARY_SIZE_LIMIT: usize = usize::max_value(); +pub const BINARY_SIZE_LIMIT: usize = usize::MAX; /// A wrapper for the binary vector #[derive(Default, Debug)] diff --git a/external-crates/move/crates/move-binary-format/src/proptest_types/functions.rs b/external-crates/move/crates/move-binary-format/src/proptest_types/functions.rs index a549e18fb8b94..42bbfacfc1b75 100644 --- a/external-crates/move/crates/move-binary-format/src/proptest_types/functions.rs +++ b/external-crates/move/crates/move-binary-format/src/proptest_types/functions.rs @@ -55,7 +55,7 @@ impl SignatureState { } fn add_signature(&mut self, sig: Signature) -> SignatureIndex { - debug_assert!(self.signatures.len() < TableSize::max_value() as usize); + debug_assert!(self.signatures.len() < TableSize::MAX as usize); if let Some(idx) = self.signature_map.get(&sig) { return *idx; } @@ -81,7 +81,7 @@ impl FieldHandleState { #[allow(unused)] fn add_field_handle(&mut self, fh: FieldHandle) -> FieldHandleIndex { - debug_assert!(self.field_handles.len() < TableSize::max_value() as usize); + debug_assert!(self.field_handles.len() < TableSize::MAX as usize); if let Some(idx) = self.field_map.get(&fh) { return *idx; } @@ -105,7 +105,7 @@ impl VariantHandleState { } fn add_variant_handle(&mut self, vh: VariantHandle) -> Option { - debug_assert!(self.variant_handles.len() < TableSize::max_value() as usize); + debug_assert!(self.variant_handles.len() < TableSize::MAX as usize); if let Some(idx) = self.variant_map.get(&vh) { return Some(*idx); } @@ -135,7 +135,7 @@ impl VariantInstantiationHandleState { &mut self, vh: VariantInstantiationHandle, ) -> Option { - debug_assert!(self.variant_instantiation_handles.len() < TableSize::max_value() as usize); + debug_assert!(self.variant_instantiation_handles.len() < TableSize::MAX as usize); if let Some(idx) = self.variant_map.get(&vh) { return Some(*idx); } @@ -179,7 +179,7 @@ where #[allow(unused)] fn add_instantiation(&mut self, inst: T) -> TableIndex { - debug_assert!(self.instantiations.len() < TableSize::max_value() as usize); + debug_assert!(self.instantiations.len() < TableSize::MAX as usize); if let Some(idx) = self.instantiation_map.get(&inst) { return *idx; } @@ -381,7 +381,7 @@ impl<'a> FnDefnMaterializeState<'a> { } fn add_function_handle(&mut self, handle: FunctionHandle) -> FunctionHandleIndex { - debug_assert!(self.function_handles.len() < TableSize::max_value() as usize); + debug_assert!(self.function_handles.len() < TableSize::MAX as usize); self.function_handles.push(handle); FunctionHandleIndex((self.function_handles.len() - 1) as TableIndex) } @@ -488,7 +488,7 @@ impl FunctionDefinitionGen { pub fn materialize(self, state: &mut FnDefnMaterializeState) -> Option { // This precondition should never fail because the table size cannot be greater - // than TableSize::max_value() + // than TableSize::MAX let iden_idx = IdentifierIndex(self.name.index(state.identifiers_len) as TableIndex); if state .def_function_handles diff --git a/external-crates/move/crates/move-binary-format/src/proptest_types/types.rs b/external-crates/move/crates/move-binary-format/src/proptest_types/types.rs index 6cd5e43e76afd..49071e100b080 100644 --- a/external-crates/move/crates/move-binary-format/src/proptest_types/types.rs +++ b/external-crates/move/crates/move-binary-format/src/proptest_types/types.rs @@ -23,9 +23,6 @@ use proptest::{ }; use std::collections::BTreeSet; -#[derive(Debug)] -struct TypeSignatureIndex(u16); - #[derive(Debug)] pub struct StDefnMaterializeState { pub self_module_handle_idx: ModuleHandleIndex, diff --git a/external-crates/move/crates/move-binary-format/src/serializer.rs b/external-crates/move/crates/move-binary-format/src/serializer.rs index 2d5a362e23914..0c8c6038df4d9 100644 --- a/external-crates/move/crates/move-binary-format/src/serializer.rs +++ b/external-crates/move/crates/move-binary-format/src/serializer.rs @@ -228,11 +228,11 @@ impl CompiledModule { let mut ser = ModuleSerializer::new(version); let mut temp = BinaryData::new(); ser.serialize_tables(&mut temp, self)?; - if temp.len() > u32::max_value() as usize { + if temp.len() > u32::MAX as usize { bail!( "table content size ({}) cannot exceed ({})", temp.len(), - u32::max_value() + u32::MAX ); } ser.common.serialize_header(&mut binary_data)?; @@ -296,11 +296,11 @@ struct ModuleSerializer { // Helpers // fn check_index_in_binary(index: usize) -> Result { - if index > u32::max_value() as usize { + if index > u32::MAX as usize { bail!( "Compilation unit too big ({}) cannot exceed {}", index, - u32::max_value() + u32::MAX ) } Ok(index as u32) diff --git a/external-crates/move/crates/move-binary-format/src/unit_tests/number_tests.rs b/external-crates/move/crates/move-binary-format/src/unit_tests/number_tests.rs index b99e94bb66e33..1e5f97f551374 100644 --- a/external-crates/move/crates/move-binary-format/src/unit_tests/number_tests.rs +++ b/external-crates/move/crates/move-binary-format/src/unit_tests/number_tests.rs @@ -42,8 +42,8 @@ fn uleb128_test() { uleb128_test_u64(2u64.pow(exp), n + 1); n += 1; } - uleb128_test_u64(u64::max_value() - 1, 10); - uleb128_test_u64(u64::max_value(), 10); + uleb128_test_u64(u64::MAX - 1, 10); + uleb128_test_u64(u64::MAX, 10); } #[test] diff --git a/external-crates/move/crates/move-bytecode-verifier/src/control_flow_v5.rs b/external-crates/move/crates/move-bytecode-verifier/src/control_flow_v5.rs index b24d7aeca62b9..eb705b9498b8b 100644 --- a/external-crates/move/crates/move-bytecode-verifier/src/control_flow_v5.rs +++ b/external-crates/move/crates/move-bytecode-verifier/src/control_flow_v5.rs @@ -37,7 +37,7 @@ pub fn verify( fn verify_fallthrough( current_function: FunctionDefinitionIndex, - code: &Vec, + code: &[Bytecode], ) -> PartialVMResult<()> { // Check to make sure that the bytecode vector ends with a branching instruction. match code.last() { diff --git a/external-crates/move/crates/move-bytecode-verifier/src/script_signature.rs b/external-crates/move/crates/move-bytecode-verifier/src/script_signature.rs index a26628c138ed0..8f11893a3f44a 100644 --- a/external-crates/move/crates/move-bytecode-verifier/src/script_signature.rs +++ b/external-crates/move/crates/move-bytecode-verifier/src/script_signature.rs @@ -9,6 +9,7 @@ //! - (DEPRECATED) Has an empty return type //! - All return types are not references //! - Satisfies the additional checks provided as an argument via `check_signature` +//! //! `check_signature` should be used by adapters to quickly and easily verify custom signature //! rules for entrypoints diff --git a/external-crates/move/crates/move-command-line-common/src/files.rs b/external-crates/move/crates/move-command-line-common/src/files.rs index 9300f49f895fc..b7b1039c7c12a 100644 --- a/external-crates/move/crates/move-command-line-common/src/files.rs +++ b/external-crates/move/crates/move-command-line-common/src/files.rs @@ -212,6 +212,7 @@ pub fn try_exists_vfs(vfs_path: &VfsPath) -> VfsResult { /// - For each directory in `paths`, it will return all files that satisfy the predicate /// - Any file explicitly passed in `paths`, it will include that file in the result, regardless /// of the file extension +/// /// It implements the same functionality as find_filenames above but for the virtual file system pub fn find_filenames_vfs bool>( paths: &[VfsPath], @@ -244,7 +245,8 @@ pub fn find_filenames_vfs bool>( /// - For each directory in `paths`, it will return all files with the `MOVE_EXTENSION` found /// recursively in that directory /// - If `keep_specified_files` any file explicitly passed in `paths`, will be added to the result -/// Otherwise, they will be discarded +/// +/// Otherwise, they will be discarded /// It implements the same functionality as find_move_filenames above but for the virtual file /// system pub fn find_move_filenames_vfs( diff --git a/external-crates/move/crates/move-compiler/src/cfgir/cfg.rs b/external-crates/move/crates/move-compiler/src/cfgir/cfg.rs index d640a3f20589a..8cad547a7ce11 100644 --- a/external-crates/move/crates/move-compiler/src/cfgir/cfg.rs +++ b/external-crates/move/crates/move-compiler/src/cfgir/cfg.rs @@ -205,6 +205,7 @@ impl<'a> ImmForwardCFG<'a> { /// Returns /// - A CFG /// - A set of infinite loop heads + /// /// This _must_ be called after `BlockMutCFG::new`, as the mutable version optimizes the code /// This will be done for external usage, /// since the Mut CFG is used during the building of the cfgir::ast::Program diff --git a/external-crates/move/crates/move-compiler/src/naming/translate.rs b/external-crates/move/crates/move-compiler/src/naming/translate.rs index ed6593ec344e9..f238eb60f6ff1 100644 --- a/external-crates/move/crates/move-compiler/src/naming/translate.rs +++ b/external-crates/move/crates/move-compiler/src/naming/translate.rs @@ -51,6 +51,7 @@ pub struct ResolvedModuleFunction { pub mident: ModuleIdent, pub name: FunctionName, pub tyarg_arity: usize, + #[allow(unused)] pub arity: usize, } @@ -93,6 +94,7 @@ pub enum FieldInfo { pub struct ResolvedConstant { pub mident: ModuleIdent, pub name: ConstantName, + #[allow(unused)] pub decl_loc: Loc, } @@ -138,6 +140,7 @@ pub(super) enum ResolvedConstructor { #[derive(Debug, Clone)] pub(super) enum ResolvedCallSubject { Builtin(Box), + #[allow(unused)] Constructor(Box), Function(Box), Var(Box), @@ -146,6 +149,7 @@ pub(super) enum ResolvedCallSubject { #[derive(Debug, Clone)] pub(super) enum ResolvedUseFunFunction { + #[allow(unused)] Builtin(Box), Module(Box), Unbound, @@ -3193,7 +3197,7 @@ fn unique_pattern_binders( ) -> Vec<(Mutability, P::Var)> { use E::MatchPattern_ as EP; - fn report_duplicate(context: &mut Context, var: P::Var, locs: &Vec<(Mutability, Loc)>) { + fn report_duplicate(context: &mut Context, var: P::Var, locs: &[(Mutability, Loc)]) { assert!(locs.len() > 1, "ICE pattern duplicate detection error"); let (_, first_loc) = locs.first().unwrap(); let mut diag = diag!( diff --git a/external-crates/move/crates/move-compiler/src/to_bytecode/canonicalize_handles.rs b/external-crates/move/crates/move-compiler/src/to_bytecode/canonicalize_handles.rs index 36d4a2544ef4b..5f816dd627ab4 100644 --- a/external-crates/move/crates/move-compiler/src/to_bytecode/canonicalize_handles.rs +++ b/external-crates/move/crates/move-compiler/src/to_bytecode/canonicalize_handles.rs @@ -31,7 +31,7 @@ use move_symbol_pool::Symbol; /// /// - Friend Declarations are sorted in lexical order (by address name and module name), followed by /// unnamed addresses in their original order. - +/// /// Key for ordering module handles, distinguishing the module's self handle, handles with names, /// and handles without names. #[derive(Eq, PartialEq, Ord, PartialOrd)] @@ -336,7 +336,7 @@ fn remap_code(code: &mut CodeUnit, functions: &[TableIndex]) { /// /// is sorted according to `key`. fn permutation<'p, T, K: Ord>( - pool: &'p Vec, + pool: &'p [T], key: impl Fn(TableIndex, &'p T) -> K + 'p, ) -> Vec { let mut inverse: Vec<_> = (0..pool.len() as TableIndex).collect(); @@ -352,7 +352,7 @@ fn permutation<'p, T, K: Ord>( /// Re-order `pool` according to the `permutation` array. `permutation[i]` is the new location of /// `pool[i]`. -fn apply_permutation(pool: &mut Vec, mut permutation: Vec) { +fn apply_permutation(pool: &mut [T], mut permutation: Vec) { assert_eq!(pool.len(), permutation.len()); // At every iteration we confirm that one more value is in its final position in the pool, diff --git a/external-crates/move/crates/move-compiler/src/typing/deprecation_warnings.rs b/external-crates/move/crates/move-compiler/src/typing/deprecation_warnings.rs index dc6c387a99cd9..9ba6765d66dc3 100644 --- a/external-crates/move/crates/move-compiler/src/typing/deprecation_warnings.rs +++ b/external-crates/move/crates/move-compiler/src/typing/deprecation_warnings.rs @@ -19,6 +19,7 @@ const NOTE_STR: &str = "note"; #[derive(Debug, Clone)] pub struct Deprecation { // The source location of the deprecation attribute + #[allow(unused)] pub source_location: Loc, // The type of the member that is deprecated (function, constant, etc.) pub location: AttributePosition, diff --git a/external-crates/move/crates/move-compiler/src/typing/expand.rs b/external-crates/move/crates/move-compiler/src/typing/expand.rs index 2fd127861672a..9f36764277e55 100644 --- a/external-crates/move/crates/move-compiler/src/typing/expand.rs +++ b/external-crates/move/crates/move-compiler/src/typing/expand.rs @@ -318,11 +318,11 @@ fn inferred_numerical_value( Some(sp!(_, bt)) if bt.is_numeric() => bt, _ => panic!("ICE inferred num failed {:?}", &ty.value), }; - let u8_max = U256::from(std::u8::MAX); - let u16_max = U256::from(std::u16::MAX); - let u32_max = U256::from(std::u32::MAX); - let u64_max = U256::from(std::u64::MAX); - let u128_max = U256::from(std::u128::MAX); + let u8_max = U256::from(u8::MAX); + let u16_max = U256::from(u16::MAX); + let u32_max = U256::from(u32::MAX); + let u64_max = U256::from(u64::MAX); + let u128_max = U256::from(u128::MAX); let u256_max = U256::max_value(); let max = match bt { BT::U8 => u8_max, diff --git a/external-crates/move/crates/move-compiler/src/unit_test/plan_builder.rs b/external-crates/move/crates/move-compiler/src/unit_test/plan_builder.rs index 066cd6f982799..7aab1a163d71d 100644 --- a/external-crates/move/crates/move-compiler/src/unit_test/plan_builder.rs +++ b/external-crates/move/crates/move-compiler/src/unit_test/plan_builder.rs @@ -713,7 +713,7 @@ fn convert_attribute_value_u64( ) -> Option<(Loc, u64)> { use E::{AttributeValue_ as EAV, Value_ as EV}; match value { - sp!(vloc, EAV::Value(sp!(_, EV::InferredNum(u)))) if *u <= U256::from(std::u64::MAX) => { + sp!(vloc, EAV::Value(sp!(_, EV::InferredNum(u)))) if *u <= U256::from(u64::MAX) => { Some((*vloc, u.down_cast_lossy())) } sp!(vloc, EAV::Value(sp!(_, EV::U64(u)))) => Some((*vloc, *u)), diff --git a/external-crates/move/crates/move-core-types/src/state.rs b/external-crates/move/crates/move-core-types/src/state.rs index 6d94c574a9dd6..f57e1abdcad79 100644 --- a/external-crates/move/crates/move-core-types/src/state.rs +++ b/external-crates/move/crates/move-core-types/src/state.rs @@ -13,7 +13,7 @@ pub enum VMState { } thread_local! { - static STATE: RefCell = RefCell::new(VMState::OTHER); + static STATE: RefCell = const { RefCell::new(VMState::OTHER) }; } pub fn set_state(state: VMState) -> VMState { diff --git a/external-crates/move/crates/move-core-types/src/u256.rs b/external-crates/move/crates/move-core-types/src/u256.rs index fb240326cebad..d47245857df5c 100644 --- a/external-crates/move/crates/move-core-types/src/u256.rs +++ b/external-crates/move/crates/move-core-types/src/u256.rs @@ -567,7 +567,6 @@ impl Distribution for Standard { // Rand impl below are inspired by u128 impl found in https://rust-random.github.io/rand/src/rand/distributions/uniform.rs.html #[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct UniformU256 { low: U256, range: U256, diff --git a/external-crates/move/crates/move-ir-to-bytecode/src/context.rs b/external-crates/move/crates/move-ir-to-bytecode/src/context.rs index b62cda8506c6d..1d320b4653cf9 100644 --- a/external-crates/move/crates/move-ir-to-bytecode/src/context.rs +++ b/external-crates/move/crates/move-ir-to-bytecode/src/context.rs @@ -48,7 +48,7 @@ macro_rules! get_or_add_item_macro { }}; } -pub const TABLE_MAX_SIZE: usize = u16::max_value() as usize; +pub const TABLE_MAX_SIZE: usize = u16::MAX as usize; fn get_or_add_item_ref( m: &mut HashMap, k: &K, diff --git a/external-crates/move/crates/move-model/src/ty.rs b/external-crates/move/crates/move-model/src/ty.rs index 8083e4eaa7119..31ef1d646d1b7 100644 --- a/external-crates/move/crates/move-model/src/ty.rs +++ b/external-crates/move/crates/move-model/src/ty.rs @@ -173,19 +173,18 @@ impl Type { /// Returns true if this is any number type. pub fn is_number(&self) -> bool { - if let Type::Primitive(p) = self { - if let PrimitiveType::U8 - | PrimitiveType::U16 - | PrimitiveType::U32 - | PrimitiveType::U64 - | PrimitiveType::U128 - | PrimitiveType::U256 - | PrimitiveType::Num = p - { - return true; - } - } - false + matches!( + self, + Type::Primitive( + PrimitiveType::U8 + | PrimitiveType::U16 + | PrimitiveType::U32 + | PrimitiveType::U64 + | PrimitiveType::U128 + | PrimitiveType::U256 + | PrimitiveType::Num, + ) + ) } /// Returns true if this is an address or signer type. pub fn is_signer_or_address(&self) -> bool { @@ -780,6 +779,7 @@ impl TypeUnificationAdapter { /// - any type parameter on the LHS with index < P will be treated as concrete types and /// - only type parameters on the LHS with index >= P are treated as variables and thus, /// participate in the type unification process. + /// /// The same rule applies to the RHS parameters via `treat_rhs_type_param_as_var_after_index`. fn new<'a, I>( lhs_types: I, @@ -982,6 +982,7 @@ impl TypeInstantiationDerivation { /// - be assigned with a concrete type already and hence, ceases to be a type parameter, or /// - does not have any matching instantiation and hence, either remains a type parameter or is /// represented as a type error. + /// /// But in anyway, these type parameters no longer participate in type unification anymore. /// /// If `target_lhs` is True, derive instantiations for the type parameter with @@ -1043,6 +1044,7 @@ impl TypeInstantiationDerivation { /// - finds all possible instantiations for parameter at index 2 (`inst_param_2`) /// - for each instantiation in `inst_param_2`, /// - ...... + /// /// The process continues until all type parameters are analyzed (i.e., reaching the type /// parameter at index `N`). /// diff --git a/external-crates/move/crates/move-stackless-bytecode/src/access_path.rs b/external-crates/move/crates/move-stackless-bytecode/src/access_path.rs index 52927f081a542..84d41e97e530d 100644 --- a/external-crates/move/crates/move-stackless-bytecode/src/access_path.rs +++ b/external-crates/move/crates/move-stackless-bytecode/src/access_path.rs @@ -6,6 +6,7 @@ //! memory. Some examples of concrete paths are: //! * `0x7/M::T/f` (i.e., the field `f` of the `M::T` resource stored at address `0x7` //! * `Formal(0)/[2]` (i.e., the value stored at index 2 of the array bound the 0th formal of the current procedure) +//! //! An abstract path is similar; it consists of the following components: //! * A *root*, which is either an abstract address or a local //! * Zero or more *offsets*, where an offset is a field, an unknown vector index, or an abstract struct type diff --git a/external-crates/move/crates/move-stackless-bytecode/src/inconsistency_check.rs b/external-crates/move/crates/move-stackless-bytecode/src/inconsistency_check.rs index 7ca6aad925ba2..8a5f2dec7e20a 100644 --- a/external-crates/move/crates/move-stackless-bytecode/src/inconsistency_check.rs +++ b/external-crates/move/crates/move-stackless-bytecode/src/inconsistency_check.rs @@ -11,6 +11,7 @@ //! an `assert false` before //! - every `return` and //! - every `abort` (if the `unconditional-abort-as-inconsistency` option is set). +//! //! In this way, if the instrumented `assert false` can be proved, it means we have an inconsistency //! in the specifications. //! diff --git a/external-crates/move/crates/move-stackless-bytecode/src/packed_types_analysis.rs b/external-crates/move/crates/move-stackless-bytecode/src/packed_types_analysis.rs index 1e0783cd8b308..10fc6fbe99891 100644 --- a/external-crates/move/crates/move-stackless-bytecode/src/packed_types_analysis.rs +++ b/external-crates/move/crates/move-stackless-bytecode/src/packed_types_analysis.rs @@ -26,6 +26,7 @@ use crate::{ /// - Transaction scripts have at most 1 type argument /// - The only values that can be bound to a transaction script type argument are XUS and /// XDX. Passing any other values will lead to an aborted transaction. +/// /// The first assumption is checked and will trigger an assert failure if violated. The second /// is unchecked, but would be a nice property for the prover. pub fn get_packed_types( diff --git a/external-crates/move/crates/move-stdlib-natives/src/debug.rs b/external-crates/move/crates/move-stdlib-natives/src/debug.rs index 1aee70ebed864..d4e55a46ea170 100644 --- a/external-crates/move/crates/move-stdlib-natives/src/debug.rs +++ b/external-crates/move/crates/move-stdlib-natives/src/debug.rs @@ -286,7 +286,7 @@ mod testing { Ok(()) } - fn is_non_empty_vector_u8(vec: &Vec) -> bool { + fn is_non_empty_vector_u8(vec: &[A::MoveValue]) -> bool { if vec.is_empty() { false } else { diff --git a/external-crates/move/crates/move-vm-runtime/src/tracing.rs b/external-crates/move/crates/move-vm-runtime/src/tracing.rs index b016879c9bb45..a4c984c62a537 100644 --- a/external-crates/move/crates/move-vm-runtime/src/tracing.rs +++ b/external-crates/move/crates/move-vm-runtime/src/tracing.rs @@ -48,7 +48,6 @@ static DEBUGGING_ENABLED: Lazy = static LOGGING_FILE: Lazy> = Lazy::new(|| { Mutex::new( OpenOptions::new() - .write(true) .create(true) .append(true) .open(&*FILE_PATH) diff --git a/external-crates/move/crates/move-vm-test-utils/src/gas_schedule.rs b/external-crates/move/crates/move-vm-test-utils/src/gas_schedule.rs index 2d4cfed4537ed..128d7e4447841 100644 --- a/external-crates/move/crates/move-vm-test-utils/src/gas_schedule.rs +++ b/external-crates/move/crates/move-vm-test-utils/src/gas_schedule.rs @@ -33,10 +33,7 @@ use move_vm_types::{ }; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; -use std::{ - ops::{Add, Mul}, - u64, -}; +use std::ops::{Add, Mul}; pub enum GasUnit {} pub type Gas = GasQuantity; diff --git a/external-crates/move/crates/move-vm-types/src/values/values_impl.rs b/external-crates/move/crates/move-vm-types/src/values/values_impl.rs index e09471bf6e027..aab6897fdc244 100644 --- a/external-crates/move/crates/move-vm-types/src/values/values_impl.rs +++ b/external-crates/move/crates/move-vm-types/src/values/values_impl.rs @@ -2018,7 +2018,7 @@ impl IntegerValue { match self { U8(x) => Ok(x), U16(x) => { - if x > (std::u8::MAX as u16) { + if x > (u8::MAX as u16) { Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR) .with_message(format!("Cannot cast u16({}) to u8", x))) } else { @@ -2026,7 +2026,7 @@ impl IntegerValue { } } U32(x) => { - if x > (std::u8::MAX as u32) { + if x > (u8::MAX as u32) { Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR) .with_message(format!("Cannot cast u32({}) to u8", x))) } else { @@ -2034,7 +2034,7 @@ impl IntegerValue { } } U64(x) => { - if x > (std::u8::MAX as u64) { + if x > (u8::MAX as u64) { Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR) .with_message(format!("Cannot cast u64({}) to u8", x))) } else { @@ -2042,7 +2042,7 @@ impl IntegerValue { } } U128(x) => { - if x > (std::u8::MAX as u128) { + if x > (u8::MAX as u128) { Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR) .with_message(format!("Cannot cast u128({}) to u8", x))) } else { @@ -2050,7 +2050,7 @@ impl IntegerValue { } } U256(x) => { - if x > (u256::U256::from(std::u8::MAX)) { + if x > (u256::U256::from(u8::MAX)) { Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR) .with_message(format!("Cannot cast u256({}) to u8", x))) } else { @@ -2067,7 +2067,7 @@ impl IntegerValue { U8(x) => Ok(x as u16), U16(x) => Ok(x), U32(x) => { - if x > (std::u16::MAX as u32) { + if x > (u16::MAX as u32) { Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR) .with_message(format!("Cannot cast u32({}) to u16", x))) } else { @@ -2075,7 +2075,7 @@ impl IntegerValue { } } U64(x) => { - if x > (std::u16::MAX as u64) { + if x > (u16::MAX as u64) { Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR) .with_message(format!("Cannot cast u64({}) to u16", x))) } else { @@ -2083,7 +2083,7 @@ impl IntegerValue { } } U128(x) => { - if x > (std::u16::MAX as u128) { + if x > (u16::MAX as u128) { Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR) .with_message(format!("Cannot cast u128({}) to u16", x))) } else { @@ -2091,7 +2091,7 @@ impl IntegerValue { } } U256(x) => { - if x > (u256::U256::from(std::u16::MAX)) { + if x > (u256::U256::from(u16::MAX)) { Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR) .with_message(format!("Cannot cast u256({}) to u16", x))) } else { @@ -2109,7 +2109,7 @@ impl IntegerValue { U16(x) => Ok(x as u32), U32(x) => Ok(x), U64(x) => { - if x > (std::u32::MAX as u64) { + if x > (u32::MAX as u64) { Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR) .with_message(format!("Cannot cast u64({}) to u32", x))) } else { @@ -2117,7 +2117,7 @@ impl IntegerValue { } } U128(x) => { - if x > (std::u32::MAX as u128) { + if x > (u32::MAX as u128) { Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR) .with_message(format!("Cannot cast u128({}) to u32", x))) } else { @@ -2125,7 +2125,7 @@ impl IntegerValue { } } U256(x) => { - if x > (u256::U256::from(std::u32::MAX)) { + if x > (u256::U256::from(u32::MAX)) { Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR) .with_message(format!("Cannot cast u128({}) to u32", x))) } else { @@ -2144,7 +2144,7 @@ impl IntegerValue { U32(x) => Ok(x as u64), U64(x) => Ok(x), U128(x) => { - if x > (std::u64::MAX as u128) { + if x > (u64::MAX as u128) { Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR) .with_message(format!("Cannot cast u128({}) to u64", x))) } else { @@ -2152,7 +2152,7 @@ impl IntegerValue { } } U256(x) => { - if x > (u256::U256::from(std::u64::MAX)) { + if x > (u256::U256::from(u64::MAX)) { Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR) .with_message(format!("Cannot cast u256({}) to u64", x))) } else { @@ -2172,7 +2172,7 @@ impl IntegerValue { U64(x) => Ok(x as u128), U128(x) => Ok(x), U256(x) => { - if x > (u256::U256::from(std::u128::MAX)) { + if x > (u256::U256::from(u128::MAX)) { Err(PartialVMError::new(StatusCode::ARITHMETIC_ERROR) .with_message(format!("Cannot cast u256({}) to u128", x))) } else { diff --git a/external-crates/move/crates/test-generation/src/abstract_state.rs b/external-crates/move/crates/test-generation/src/abstract_state.rs index 00325826f9402..b83e2759c98b9 100644 --- a/external-crates/move/crates/test-generation/src/abstract_state.rs +++ b/external-crates/move/crates/test-generation/src/abstract_state.rs @@ -511,7 +511,7 @@ impl AbstractState { pub fn stack_push(&mut self, item: AbstractValue) { // Programs that are large enough to exceed this bound // will not be generated - debug_assert!(self.stack.len() < usize::max_value()); + debug_assert!(self.stack.len() < usize::MAX); self.stack.push(item); } @@ -521,7 +521,7 @@ impl AbstractState { if let Some(abstract_value) = self.register_move() { // Programs that are large enough to exceed this bound // will not be generated - debug_assert!(self.stack.len() < usize::max_value()); + debug_assert!(self.stack.len() < usize::MAX); self.stack.push(abstract_value); Ok(()) } else { diff --git a/external-crates/move/crates/test-generation/src/borrow_graph.rs b/external-crates/move/crates/test-generation/src/borrow_graph.rs index df1ea3fde6c18..8d5a489a78136 100644 --- a/external-crates/move/crates/test-generation/src/borrow_graph.rs +++ b/external-crates/move/crates/test-generation/src/borrow_graph.rs @@ -67,7 +67,7 @@ impl BorrowGraph { } self.partition_map.insert(self.partition_counter, vec![n]); // Implication of `checked_add` - debug_assert!(self.partitions.len() < usize::max_value()); + debug_assert!(self.partitions.len() < usize::MAX); self.partitions.push(self.partition_counter); Ok(()) } else { diff --git a/external-crates/move/crates/test-generation/src/bytecode_generator.rs b/external-crates/move/crates/test-generation/src/bytecode_generator.rs index 3177706654b32..efbb3aae9141e 100644 --- a/external-crates/move/crates/test-generation/src/bytecode_generator.rs +++ b/external-crates/move/crates/test-generation/src/bytecode_generator.rs @@ -157,7 +157,7 @@ impl FunctionGenerationContext { pub fn incr_instruction_count(&mut self) -> Option<()> { self.bytecode_len += 1; - if self.bytecode_len >= (u16::max_value() - 1) as u64 { + if self.bytecode_len >= (u16::MAX - 1) as u64 { return None; } Some(()) @@ -357,25 +357,23 @@ impl<'a> BytecodeGenerator<'a> { } BytecodeType::U8(instruction) => { // Generate a random u8 constant to load - Some(instruction(self.rng.gen_range(0..u8::max_value()))) + Some(instruction(self.rng.gen_range(0..u8::MAX))) } BytecodeType::U16(instruction) => { // Generate a random u16 constant to load - Some(instruction(self.rng.gen_range(0..u16::max_value()))) + Some(instruction(self.rng.gen_range(0..u16::MAX))) } BytecodeType::U32(instruction) => { // Generate a random u32 constant to load - Some(instruction(self.rng.gen_range(0..u32::max_value()))) + Some(instruction(self.rng.gen_range(0..u32::MAX))) } BytecodeType::U64(instruction) => { // Generate a random u64 constant to load - Some(instruction(self.rng.gen_range(0..u64::max_value()))) + Some(instruction(self.rng.gen_range(0..u64::MAX))) } BytecodeType::U128(instruction) => { // Generate a random u128 constant to load - Some(instruction(Box::new( - self.rng.gen_range(0..u128::max_value()), - ))) + Some(instruction(Box::new(self.rng.gen_range(0..u128::MAX)))) } BytecodeType::U256(instruction) => { // Generate a random u256 constant to load @@ -442,7 +440,7 @@ impl<'a> BytecodeGenerator<'a> { || unsatisfied_preconditions == 0 { // The size of matches cannot be greater than the number of bytecode instructions - debug_assert!(matches.len() < usize::max_value()); + debug_assert!(matches.len() < usize::MAX); matches.push((*stack_effect, instruction)); } } @@ -572,7 +570,7 @@ impl<'a> BytecodeGenerator<'a> { exact: bool, ) -> Option { // Bytecode will never be generated this large - debug_assert!(bytecode.len() < usize::max_value()); + debug_assert!(bytecode.len() < usize::MAX); debug!("**********************"); debug!("State1: {}", state); debug!("Next instr: {:?}", instruction); diff --git a/external-crates/move/crates/test-generation/src/control_flow_graph.rs b/external-crates/move/crates/test-generation/src/control_flow_graph.rs index 78100aa710d89..341d68ed5f6d7 100644 --- a/external-crates/move/crates/test-generation/src/control_flow_graph.rs +++ b/external-crates/move/crates/test-generation/src/control_flow_graph.rs @@ -95,22 +95,22 @@ impl CFG { // The number of edges will be at most `2*target_blocks`` // Since target blocks is at most a `u16`, this will not overflow even if // `usize` is a `u32` - debug_assert!(edges.len() < usize::max_value()); + debug_assert!(edges.len() < usize::MAX); edges.push((parent_block_id, current_block_id)); block_queue.push_back(current_block_id); // `current_block_id` is bound by the max og `target_block_size` - debug_assert!(current_block_id < u16::max_value()); + debug_assert!(current_block_id < u16::MAX); current_block_id += 1; // Generate a second child edge with prob = 1/2 if rng.gen_bool(0.5) && current_block_id < target_blocks { // The number of edges will be at most `2*target_blocks`` // Since target blocks is at most a `u16`, this will not overflow even if // `usize` is a `u32` - debug_assert!(edges.len() < usize::max_value()); + debug_assert!(edges.len() < usize::MAX); edges.push((parent_block_id, current_block_id)); block_queue.push_back(current_block_id); // `current_block_id` is bound by the max og `target_block_size` - debug_assert!(current_block_id < u16::max_value()); + debug_assert!(current_block_id < u16::MAX); current_block_id += 1; } } @@ -156,7 +156,7 @@ impl CFG { for (parent, child) in self.edges.iter() { if *parent == block_id { // Length is bound by iteration on `self.edges` - debug_assert!(children_ids.len() < usize::max_value()); + debug_assert!(children_ids.len() < usize::MAX); children_ids.push(*child); } } @@ -175,7 +175,7 @@ impl CFG { for (parent, child) in self.edges.iter() { if *child == block_id { // Iteration is bound by the self.edges vector length - debug_assert!(parent_ids.len() < usize::max_value()); + debug_assert!(parent_ids.len() < usize::MAX); parent_ids.push(*parent); } } diff --git a/external-crates/move/crates/test-generation/src/lib.rs b/external-crates/move/crates/test-generation/src/lib.rs index 4a4ce8449659a..676044d88b632 100644 --- a/external-crates/move/crates/test-generation/src/lib.rs +++ b/external-crates/move/crates/test-generation/src/lib.rs @@ -236,9 +236,7 @@ pub fn module_frame_generation( let mut module = generate_module(&mut rng, generation_options.clone()); // Either get the number of iterations provided by the user, or iterate "infinitely"--up to // u128::MAX number of times. - let iters = num_iters - .map(|x| x as u128) - .unwrap_or_else(|| std::u128::MAX); + let iters = num_iters.map(|x| x as u128).unwrap_or_else(|| u128::MAX); while generated < iters && sender.send(module).is_ok() { module = generate_module(&mut rng, generation_options.clone()); diff --git a/external-crates/move/crates/test-generation/tests/generic_instructions.rs b/external-crates/move/crates/test-generation/tests/generic_instructions.rs index 66eb17078de50..0f6305f419638 100644 --- a/external-crates/move/crates/test-generation/tests/generic_instructions.rs +++ b/external-crates/move/crates/test-generation/tests/generic_instructions.rs @@ -13,7 +13,7 @@ use test_generation::transitions::Subst; #[test] fn unify_no_subst() { use SignatureToken::*; - let tys = vec![Bool, U64, Vector(Box::new(U8)), Address]; + let tys = [Bool, U64, Vector(Box::new(U8)), Address]; for tok1 in tys.iter() { for tok2 in tys.iter() { let should_unify = tok1.clone() == tok2.clone(); diff --git a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/control_flow_v5.rs b/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/control_flow_v5.rs index b24d7aeca62b9..eb705b9498b8b 100644 --- a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/control_flow_v5.rs +++ b/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/control_flow_v5.rs @@ -37,7 +37,7 @@ pub fn verify( fn verify_fallthrough( current_function: FunctionDefinitionIndex, - code: &Vec, + code: &[Bytecode], ) -> PartialVMResult<()> { // Check to make sure that the bytecode vector ends with a branching instruction. match code.last() { diff --git a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/script_signature.rs b/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/script_signature.rs index b55ea638e1e3d..35b065b204dbe 100644 --- a/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/script_signature.rs +++ b/external-crates/move/move-execution/v0/crates/move-bytecode-verifier/src/script_signature.rs @@ -9,6 +9,7 @@ //! - (DEPRECATED) Has an empty return type //! - All return types are not references //! - Satisfies the additional checks provided as an argument via `check_signature` +//! //! `check_signature` should be used by adapters to quickly and easily verify custom signature //! rules for entrypoints diff --git a/external-crates/move/move-execution/v0/crates/move-vm-runtime/src/tracing.rs b/external-crates/move/move-execution/v0/crates/move-vm-runtime/src/tracing.rs index b016879c9bb45..a4c984c62a537 100644 --- a/external-crates/move/move-execution/v0/crates/move-vm-runtime/src/tracing.rs +++ b/external-crates/move/move-execution/v0/crates/move-vm-runtime/src/tracing.rs @@ -48,7 +48,6 @@ static DEBUGGING_ENABLED: Lazy = static LOGGING_FILE: Lazy> = Lazy::new(|| { Mutex::new( OpenOptions::new() - .write(true) .create(true) .append(true) .open(&*FILE_PATH) diff --git a/external-crates/move/move-execution/v1/crates/move-bytecode-verifier/src/control_flow_v5.rs b/external-crates/move/move-execution/v1/crates/move-bytecode-verifier/src/control_flow_v5.rs index b24d7aeca62b9..eb705b9498b8b 100644 --- a/external-crates/move/move-execution/v1/crates/move-bytecode-verifier/src/control_flow_v5.rs +++ b/external-crates/move/move-execution/v1/crates/move-bytecode-verifier/src/control_flow_v5.rs @@ -37,7 +37,7 @@ pub fn verify( fn verify_fallthrough( current_function: FunctionDefinitionIndex, - code: &Vec, + code: &[Bytecode], ) -> PartialVMResult<()> { // Check to make sure that the bytecode vector ends with a branching instruction. match code.last() { diff --git a/external-crates/move/move-execution/v1/crates/move-bytecode-verifier/src/script_signature.rs b/external-crates/move/move-execution/v1/crates/move-bytecode-verifier/src/script_signature.rs index b55ea638e1e3d..35b065b204dbe 100644 --- a/external-crates/move/move-execution/v1/crates/move-bytecode-verifier/src/script_signature.rs +++ b/external-crates/move/move-execution/v1/crates/move-bytecode-verifier/src/script_signature.rs @@ -9,6 +9,7 @@ //! - (DEPRECATED) Has an empty return type //! - All return types are not references //! - Satisfies the additional checks provided as an argument via `check_signature` +//! //! `check_signature` should be used by adapters to quickly and easily verify custom signature //! rules for entrypoints diff --git a/external-crates/move/move-execution/v1/crates/move-vm-runtime/src/tracing.rs b/external-crates/move/move-execution/v1/crates/move-vm-runtime/src/tracing.rs index b016879c9bb45..a4c984c62a537 100644 --- a/external-crates/move/move-execution/v1/crates/move-vm-runtime/src/tracing.rs +++ b/external-crates/move/move-execution/v1/crates/move-vm-runtime/src/tracing.rs @@ -48,7 +48,6 @@ static DEBUGGING_ENABLED: Lazy = static LOGGING_FILE: Lazy> = Lazy::new(|| { Mutex::new( OpenOptions::new() - .write(true) .create(true) .append(true) .open(&*FILE_PATH) diff --git a/external-crates/move/move-execution/v2/crates/move-bytecode-verifier/src/control_flow_v5.rs b/external-crates/move/move-execution/v2/crates/move-bytecode-verifier/src/control_flow_v5.rs index b24d7aeca62b9..eb705b9498b8b 100644 --- a/external-crates/move/move-execution/v2/crates/move-bytecode-verifier/src/control_flow_v5.rs +++ b/external-crates/move/move-execution/v2/crates/move-bytecode-verifier/src/control_flow_v5.rs @@ -37,7 +37,7 @@ pub fn verify( fn verify_fallthrough( current_function: FunctionDefinitionIndex, - code: &Vec, + code: &[Bytecode], ) -> PartialVMResult<()> { // Check to make sure that the bytecode vector ends with a branching instruction. match code.last() { diff --git a/external-crates/move/move-execution/v2/crates/move-bytecode-verifier/src/script_signature.rs b/external-crates/move/move-execution/v2/crates/move-bytecode-verifier/src/script_signature.rs index b55ea638e1e3d..35b065b204dbe 100644 --- a/external-crates/move/move-execution/v2/crates/move-bytecode-verifier/src/script_signature.rs +++ b/external-crates/move/move-execution/v2/crates/move-bytecode-verifier/src/script_signature.rs @@ -9,6 +9,7 @@ //! - (DEPRECATED) Has an empty return type //! - All return types are not references //! - Satisfies the additional checks provided as an argument via `check_signature` +//! //! `check_signature` should be used by adapters to quickly and easily verify custom signature //! rules for entrypoints diff --git a/external-crates/move/move-execution/v2/crates/move-vm-runtime/src/tracing.rs b/external-crates/move/move-execution/v2/crates/move-vm-runtime/src/tracing.rs index b016879c9bb45..a4c984c62a537 100644 --- a/external-crates/move/move-execution/v2/crates/move-vm-runtime/src/tracing.rs +++ b/external-crates/move/move-execution/v2/crates/move-vm-runtime/src/tracing.rs @@ -48,7 +48,6 @@ static DEBUGGING_ENABLED: Lazy = static LOGGING_FILE: Lazy> = Lazy::new(|| { Mutex::new( OpenOptions::new() - .write(true) .create(true) .append(true) .open(&*FILE_PATH) diff --git a/narwhal/config/src/lib.rs b/narwhal/config/src/lib.rs index 37f92aec91fb9..6ee97a903091f 100644 --- a/narwhal/config/src/lib.rs +++ b/narwhal/config/src/lib.rs @@ -99,7 +99,11 @@ impl Import for D {} pub trait Export: Serialize { fn export(&self, path: &str) -> Result<(), ConfigError> { let writer = || -> Result<(), std::io::Error> { - let file = OpenOptions::new().create(true).write(true).open(path)?; + let file = OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open(path)?; let mut writer = BufWriter::new(file); let data = serde_json::to_string_pretty(self).unwrap(); writer.write_all(data.as_ref())?; diff --git a/narwhal/node/src/benchmark_client.rs b/narwhal/node/src/benchmark_client.rs index 1f6e10843dfec..a8d721f882d8c 100644 --- a/narwhal/node/src/benchmark_client.rs +++ b/narwhal/node/src/benchmark_client.rs @@ -31,6 +31,7 @@ use worker::LazyNarwhalClient; /// * the size of the transactions via the --size property /// * the worker address to send the transactions to. A url format is expected ex http://127.0.0.1:7000 /// * the rate of sending transactions via the --rate parameter +/// /// Optionally the --nodes parameter can be passed where a list of worker addresses /// should be passed. The benchmarking client will first try to connect to all of those nodes before start sending /// any transactions. That confirms the system is up and running and ready to start processing the transactions. diff --git a/narwhal/node/src/main.rs b/narwhal/node/src/main.rs index 8de09ecd12106..3aa919b9b5012 100644 --- a/narwhal/node/src/main.rs +++ b/narwhal/node/src/main.rs @@ -303,7 +303,7 @@ async fn main() -> Result<(), eyre::Report> { /// Generate all the genesis files required for benchmarks. fn benchmark_genesis( - ips: &Vec, + ips: &[String], working_directory: &PathBuf, num_workers: usize, base_port: usize, diff --git a/narwhal/primary/src/consensus/metrics.rs b/narwhal/primary/src/consensus/metrics.rs index d855a249327bf..11a16da29d8f9 100644 --- a/narwhal/primary/src/consensus/metrics.rs +++ b/narwhal/primary/src/consensus/metrics.rs @@ -37,7 +37,7 @@ pub struct ConsensusMetrics { /// all the nodes are expected to report the same results. For every leader of each round the /// output can be one of the following: /// * committed: the leader has been found and its subdag will get committed - no matter if the leader - /// is committed on its time or not (part of recursion) + /// is committed on its time or not (part of recursion) /// * not_found: the leader has not been found on the commit path and doesn't get committed /// * no_path: the leader exists but there is no path that leads to it pub leader_election: IntCounterVec, @@ -135,9 +135,9 @@ pub struct ChannelMetrics { /// occupancy of the channel from the `Consensus` to `SubscriberHandler`. /// See also: /// * tx_committed_certificates in primary, where the committed certificates - /// from `Consensus` are sent to `primary::StateHandler` + /// from `Consensus` are sent to `primary::StateHandler` /// * tx_new_certificates where the newly accepted certificates are sent - /// from `primary::Synchronizer` to `Consensus` + /// from `primary::Synchronizer` to `Consensus` pub tx_sequence: IntGauge, } diff --git a/narwhal/primary/src/synchronizer.rs b/narwhal/primary/src/synchronizer.rs index 6ce964d43501a..f7953c8bfab87 100644 --- a/narwhal/primary/src/synchronizer.rs +++ b/narwhal/primary/src/synchronizer.rs @@ -336,6 +336,7 @@ impl Inner { /// - Validating and accepting certificates received from peers. /// - Triggering fetching for certificates and batches. /// - Broadcasting created certificates. +/// /// `Synchronizer` contains most of the certificate processing logic in Narwhal. #[derive(Clone)] pub struct Synchronizer { @@ -1516,9 +1517,7 @@ impl State { ) -> Option<((u64, CertificateDigest), Option)> { // Accept suspended certificates at and below gc round because their parents will not // be accepted into the DAG store anymore, in sanitize_certificate(). - let Some(((round, digest), _children)) = self.missing.first_key_value() else { - return None; - }; + let ((round, digest), _children) = self.missing.first_key_value()?; // Note that gc_round is the highest round where certificates are gc'ed, and which will // never be in a consensus commit. It's safe to gc up to gc_round, so anything suspended on gc_round + 1 // can safely be accepted as their parents (of gc_round) have already been removed from the DAG. diff --git a/narwhal/primary/tests/randomized_tests.rs b/narwhal/primary/tests/randomized_tests.rs index 79274b9dbdd18..db357ab53ae5a 100644 --- a/narwhal/primary/tests/randomized_tests.rs +++ b/narwhal/primary/tests/randomized_tests.rs @@ -299,7 +299,7 @@ fn generate_randomised_dag( /// * nodes that don't create certificates at all for some rounds (failures) /// * leaders that don't get enough support (f+1) for their immediate round /// * slow nodes - nodes that create certificates but those might not referenced by nodes of -/// subsequent rounds. +/// subsequent rounds. pub fn make_certificates_with_parameters( seed: u64, committee: &Committee, diff --git a/narwhal/storage/src/certificate_store.rs b/narwhal/storage/src/certificate_store.rs index 17517c5edd686..8f1264f94745d 100644 --- a/narwhal/storage/src/certificate_store.rs +++ b/narwhal/storage/src/certificate_store.rs @@ -187,50 +187,6 @@ impl Cache for CertificateStoreCache { } } -/// An implementation that basically disables the caching functionality when used for CertificateStore. -#[derive(Clone)] -struct NoCache {} - -impl Cache for NoCache { - fn write(&self, _certificate: Certificate) { - // no-op - } - - fn write_all(&self, _certificate: Vec) { - // no-op - } - - fn read(&self, _digest: &CertificateDigest) -> Option { - None - } - - fn read_all( - &self, - digests: Vec, - ) -> Vec<(CertificateDigest, Option)> { - digests.into_iter().map(|digest| (digest, None)).collect() - } - - fn contains(&self, _digest: &CertificateDigest) -> bool { - false - } - - fn multi_contains<'a>( - &self, - digests: impl Iterator, - ) -> Vec { - digests.map(|_| false).collect() - } - - fn remove(&self, _digest: &CertificateDigest) { - // no-op - } - - fn remove_all(&self, _digests: Vec) { - // no-op - } -} - /// The main storage when we have to deal with certificates. It maintains /// two storages, one main which saves the certificates by their ids, and a /// secondary one which acts as an index to allow us fast retrieval based @@ -731,7 +687,7 @@ impl CertificateStore { #[cfg(test)] mod test { - use crate::certificate_store::{CertificateStore, NoCache}; + use crate::certificate_store::CertificateStore; use crate::{Cache, CertificateStoreCache}; use config::AuthorityIdentifier; use fastcrypto::hash::Hash; @@ -749,6 +705,50 @@ mod test { use test_utils::{latest_protocol_version, temp_dir, CommitteeFixture}; use types::{Certificate, CertificateAPI, CertificateDigest, HeaderAPI, Round}; + /// An implementation that basically disables the caching functionality when used for CertificateStore. + #[derive(Clone)] + struct NoCache {} + + impl Cache for NoCache { + fn write(&self, _certificate: Certificate) { + // no-op + } + + fn write_all(&self, _certificate: Vec) { + // no-op + } + + fn read(&self, _digest: &CertificateDigest) -> Option { + None + } + + fn read_all( + &self, + digests: Vec, + ) -> Vec<(CertificateDigest, Option)> { + digests.into_iter().map(|digest| (digest, None)).collect() + } + + fn contains(&self, _digest: &CertificateDigest) -> bool { + false + } + + fn multi_contains<'a>( + &self, + digests: impl Iterator, + ) -> Vec { + digests.map(|_| false).collect() + } + + fn remove(&self, _digest: &CertificateDigest) { + // no-op + } + + fn remove_all(&self, _digests: Vec) { + // no-op + } + } + fn new_store(path: std::path::PathBuf) -> CertificateStore { let (certificate_map, certificate_id_by_round_map, certificate_id_by_origin_map) = create_db_maps(path); diff --git a/narwhal/test-utils/src/cluster.rs b/narwhal/test-utils/src/cluster.rs index 84fbf658a12b2..cd0e426f2a90d 100644 --- a/narwhal/test-utils/src/cluster.rs +++ b/narwhal/test-utils/src/cluster.rs @@ -178,6 +178,7 @@ impl Cluster { /// Returns all the running authorities. Any authority that: /// * has been started ever /// * or has been stopped + /// /// will not be returned by this method. pub async fn authorities(&self) -> Vec { let mut result = Vec::new(); diff --git a/narwhal/types/src/primary.rs b/narwhal/types/src/primary.rs index a2ab6d0afcd68..a478479ab0e54 100644 --- a/narwhal/types/src/primary.rs +++ b/narwhal/types/src/primary.rs @@ -1729,6 +1729,7 @@ pub struct FetchCertificatesRequest { /// This contains per authority serialized RoaringBitmap for the round diffs between /// - rounds of certificates to be skipped from the response and /// - the GC round. + /// /// These rounds are skipped because the requestor already has them. pub skip_rounds: Vec<(AuthorityIdentifier, Vec)>, /// Maximum number of certificates that should be returned. diff --git a/narwhal/worker/Cargo.toml b/narwhal/worker/Cargo.toml index bfb56b3886dfe..6b70a2be94f81 100644 --- a/narwhal/worker/Cargo.toml +++ b/narwhal/worker/Cargo.toml @@ -6,6 +6,9 @@ authors = ["Mysten Labs "] edition = "2021" publish = false +[lints] +workspace = true + [dependencies] arc-swap.workspace = true async-trait.workspace = true diff --git a/narwhal/worker/src/handlers.rs b/narwhal/worker/src/handlers.rs index e908739dbd7ba..8aab5309d1820 100644 --- a/narwhal/worker/src/handlers.rs +++ b/narwhal/worker/src/handlers.rs @@ -114,6 +114,7 @@ impl WorkerToWorker for WorkerReceiverHandler { /// Defines how the network receiver handles incoming primary messages. pub struct PrimaryReceiverHandler { // The id of this authority. + #[allow(unused)] pub authority_id: AuthorityIdentifier, // The id of this worker. pub id: WorkerId, @@ -127,6 +128,7 @@ pub struct PrimaryReceiverHandler { // Timeout on RequestBatches RPC. pub request_batches_timeout: Duration, // Number of random nodes to query when retrying batch requests. + #[allow(unused)] pub request_batches_retry_nodes: usize, // Synchronize header payloads from other workers. pub network: Option, diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 6d833ff50699a..a56a283d2abc1 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.75" +channel = "1.80.1" diff --git a/sui-execution/latest/sui-adapter/Cargo.toml b/sui-execution/latest/sui-adapter/Cargo.toml index 04f1d9734cf8d..7a58fe23095ff 100644 --- a/sui-execution/latest/sui-adapter/Cargo.toml +++ b/sui-execution/latest/sui-adapter/Cargo.toml @@ -7,6 +7,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] anyhow = { workspace = true, features = ["backtrace"] } bcs.workspace = true diff --git a/sui-execution/latest/sui-move-natives/src/crypto/group_ops.rs b/sui-execution/latest/sui-move-natives/src/crypto/group_ops.rs index 7a740b6f2b191..93535741f7567 100644 --- a/sui-execution/latest/sui-move-natives/src/crypto/group_ops.rs +++ b/sui-execution/latest/sui-move-natives/src/crypto/group_ops.rs @@ -571,8 +571,8 @@ fn multi_scalar_mul( base_cost: Option, base_cost_per_addition: Option, max_len: u32, - scalars: &Vec, - points: &Vec, + scalars: &[u8], + points: &[u8], ) -> PartialVMResult where G: GroupElement diff --git a/sui-execution/latest/sui-verifier/src/entry_points_verifier.rs b/sui-execution/latest/sui-verifier/src/entry_points_verifier.rs index e3147b9d5a24d..9eadf862fcad0 100644 --- a/sui-execution/latest/sui-verifier/src/entry_points_verifier.rs +++ b/sui-execution/latest/sui-verifier/src/entry_points_verifier.rs @@ -28,8 +28,8 @@ use crate::{verification_failure, INIT_FN_NAME}; /// - The function must have `Visibility::Private` /// - The function can have at most two parameters: /// - mandatory &mut TxContext or &TxContext (see `is_tx_context`) in the last position -/// - optional one-time witness type (see one_time_witness verifier pass) passed by value in the first -/// position +/// - optional one-time witness type (see one_time_witness verifier pass) passed by value in the +/// first position /// /// For transaction entry points /// - The function must have `is_entry` true diff --git a/sui-execution/v0/sui-adapter/Cargo.toml b/sui-execution/v0/sui-adapter/Cargo.toml index 0eb11447ba112..745245766b109 100644 --- a/sui-execution/v0/sui-adapter/Cargo.toml +++ b/sui-execution/v0/sui-adapter/Cargo.toml @@ -7,6 +7,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] anyhow = { workspace = true, features = ["backtrace"] } bcs.workspace = true diff --git a/sui-execution/v0/sui-verifier/Cargo.toml b/sui-execution/v0/sui-verifier/Cargo.toml index bb3dde2501689..e5304b9386808 100644 --- a/sui-execution/v0/sui-verifier/Cargo.toml +++ b/sui-execution/v0/sui-verifier/Cargo.toml @@ -7,6 +7,9 @@ description = "Move framework for Sui platform" license = "Apache-2.0" publish = false +[lints] +workspace = true + [dependencies] move-binary-format.workspace = true move-bytecode-utils.workspace = true diff --git a/sui-execution/v0/sui-verifier/src/entry_points_verifier.rs b/sui-execution/v0/sui-verifier/src/entry_points_verifier.rs index b32ba8fcde498..002d0b3a795c3 100644 --- a/sui-execution/v0/sui-verifier/src/entry_points_verifier.rs +++ b/sui-execution/v0/sui-verifier/src/entry_points_verifier.rs @@ -27,7 +27,7 @@ use crate::{verification_failure, INIT_FN_NAME}; /// - The function can have at most two parameters: /// - mandatory &mut TxContext or &TxContext (see `is_tx_context`) in the last position /// - optional one-time witness type (see one_time_witness verifier pass) passed by value in the first -/// position +/// position /// /// For transaction entry points /// - The function must have `is_entry` true diff --git a/sui-execution/v1/sui-adapter/Cargo.toml b/sui-execution/v1/sui-adapter/Cargo.toml index 8dce979551406..d986af97a9856 100644 --- a/sui-execution/v1/sui-adapter/Cargo.toml +++ b/sui-execution/v1/sui-adapter/Cargo.toml @@ -7,6 +7,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] anyhow = { workspace = true, features = ["backtrace"] } bcs.workspace = true diff --git a/sui-execution/v1/sui-verifier/src/entry_points_verifier.rs b/sui-execution/v1/sui-verifier/src/entry_points_verifier.rs index d107c81b98f4b..2f6a706e0f24d 100644 --- a/sui-execution/v1/sui-verifier/src/entry_points_verifier.rs +++ b/sui-execution/v1/sui-verifier/src/entry_points_verifier.rs @@ -27,7 +27,7 @@ use crate::{verification_failure, INIT_FN_NAME}; /// - The function can have at most two parameters: /// - mandatory &mut TxContext or &TxContext (see `is_tx_context`) in the last position /// - optional one-time witness type (see one_time_witness verifier pass) passed by value in the first -/// position +/// position /// /// For transaction entry points /// - The function must have `is_entry` true diff --git a/sui-execution/v2/sui-adapter/Cargo.toml b/sui-execution/v2/sui-adapter/Cargo.toml index ae29ab4845fc0..cb8bf360a0b12 100644 --- a/sui-execution/v2/sui-adapter/Cargo.toml +++ b/sui-execution/v2/sui-adapter/Cargo.toml @@ -7,6 +7,9 @@ license = "Apache-2.0" publish = false edition = "2021" +[lints] +workspace = true + [dependencies] anyhow = { workspace = true, features = ["backtrace"] } bcs.workspace = true diff --git a/sui-execution/v2/sui-move-natives/src/crypto/group_ops.rs b/sui-execution/v2/sui-move-natives/src/crypto/group_ops.rs index 54cc84f55be48..8f1e459486371 100644 --- a/sui-execution/v2/sui-move-natives/src/crypto/group_ops.rs +++ b/sui-execution/v2/sui-move-natives/src/crypto/group_ops.rs @@ -563,8 +563,8 @@ fn multi_scalar_mul( base_cost: Option, base_cost_per_addition: Option, max_len: u32, - scalars: &Vec, - points: &Vec, + scalars: &[u8], + points: &[u8], ) -> PartialVMResult where G: GroupElement diff --git a/sui-execution/v2/sui-verifier/src/entry_points_verifier.rs b/sui-execution/v2/sui-verifier/src/entry_points_verifier.rs index 9785762cf5581..6e17dba9ffa3b 100644 --- a/sui-execution/v2/sui-verifier/src/entry_points_verifier.rs +++ b/sui-execution/v2/sui-verifier/src/entry_points_verifier.rs @@ -29,7 +29,7 @@ use crate::{verification_failure, INIT_FN_NAME}; /// - The function can have at most two parameters: /// - mandatory &mut TxContext or &TxContext (see `is_tx_context`) in the last position /// - optional one-time witness type (see one_time_witness verifier pass) passed by value in the first -/// position +/// position /// /// For transaction entry points /// - The function must have `is_entry` true From 5855c7fc4c9da7132fe3a22064092a2438734175 Mon Sep 17 00:00:00 2001 From: Damir Shamanaev Date: Mon, 26 Aug 2024 20:19:46 +0300 Subject: [PATCH 223/232] [framework] Adds Option macros to release (#19101) ## Description Adds recent Option changes to release branch. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- ...000000000000000000000000000000000000000002 | Bin 66623 -> 66660 bytes crates/sui-framework-snapshot/manifest.json | 2 +- .../packages/move-stdlib/sources/option.move | 43 ++++++++----- .../move-stdlib/tests/option_tests.move | 60 ++++++++++++++++++ .../packages_compiled/sui-framework | Bin 66558 -> 66595 bytes ..._populated_genesis_snapshot_matches-2.snap | 29 ++++----- 6 files changed, 103 insertions(+), 31 deletions(-) diff --git a/crates/sui-framework-snapshot/bytecode_snapshot/55/0x0000000000000000000000000000000000000000000000000000000000000002 b/crates/sui-framework-snapshot/bytecode_snapshot/55/0x0000000000000000000000000000000000000000000000000000000000000002 index 0c65581651840fbe5545ecd4f670d693e86bbe49..54d541c98c20262d6e1d64a7ab834589d98b3206 100644 GIT binary patch delta 1327 zcmZuwOK%%h6h8Mgb3J!5_Dsf(?L0h=$FZF_&Lno48#k{uG$CyhC=fPOED!>0h*Ua@^SbAp?|f(Oy??@gIpN>! z-P*rf|6=TSWdVRoph$~b?B$;*zsGKhPx(i}{wDLQlph8g)(^p7-1&ZF-Tk2Wz#V!R z%j~0=LWNu)FG2t%sKFF0!c%YswqQT~>elx0E)akhjM$?L;2DnMhCugOBc8L+ecndV zHLh>Oh|B;KYzJV;9BeC$?ci8&-B{O*^|a}7bqK|nhi>1?0*N9*fK;O@%A~>w(9z3` zWPr{s8fPPY^LF|5p0@)$HykpsZ64+e1KkYRK=<@A0lqRmLGf^P@+@gRKhUDF&WJ5$ zE)r@>(O%y$YIbb~l@(VBVRoxLN~GQMu7kaqKDZqn_q;u7bGg=;LzTIVNz%D=H32nx zg%PIeWFF=87S^iH`~u3gZM3U?#gF{DKU1oWjj#gCv)Hf3l{kv)ajg-@l#aU3=wL6 z{ZuWzdswS_Pb$y2Tybiyz*Gf6iq|p$*IAc)9Dz!TBM=kgsBs*o7?qgW{E|431&8RI zD;=354%L3dJ)5~ggpSObK%j@ymyY@*Og}zql6-pqXkQe(vK~oG@4kOr9L&O9OF@=7 zHWDc*TUmk7Wr}waRx?P31{5(dK`}i_)nnnD36D|tN#Sv7uI;7JriO{f^hCs^a1{`n zG_d0_C>&l<#e9!?*kQBh0P-M)heC43tmw#Cm|1n1!#W<-le~;Q6eC!P9BWuaQO6N) zVk(;EaSuLSM;NN84qfi>(832a_zH^WX*T`jj<@^ROy?uzXby8XXaA3`gnx!_XfPaT z>I_zg=^uBR$9W>!cusvoJ~$`O9gJphgE<4z81N2gfFPuCPT?%+AHNy?N2SUOgYxqY zMgO)l%W0Xt&|z%0zEgCd<1gC0%59-A$p-B2Pq<_VO$C0ar~O$)0?;!wH1HCS+YV?P NThE^U_}*(Le*=CBk;ni5 delta 1289 zcmZ`&J8v9S6h8MgbNAla*`0lPpWfHlyKC>R#~wQ#zwJ1&N=Jo+QihznPMRRLIFEb2`+eUT-Fbuxcv2ZC!!Z^P?;# zHyE+S)C!VHF};DLQuNo6R9fvOADmdc{68-MZ1 zGcpmM-d|0&6OPb=;&+5%Gp?N!<3H~wJ?RKnL>8}U5!ntG^@qprw-XJ%qAa2WQIaT2 z38kcxks{jCLMtsJ9iJcHO*n*_w=&s`A0M}C$=9h84%eKT_cB>S!Rof_aF?T4Nploq zqLOf!W>g}g`wdZ$0!5;fgIZ~#Xq0Yn*Jh3oIVDr(z>6~RXD7=f8~<|BCAs+N$&tvr z<){#^pME|!Hqz&gr6I+XjSCgEt*wlRN)#V1tR*zjG(%An0h&eS`0vxgd4)Q!4bx^= zrRFJb*BVzb<#<##6(SoBQ*7wc zsL9Jnp&7xOxKT^BAx*JpH+UDZSeifl=ok4YLp6=E@uN>$2mh;|rfI})5B@8qg?A-p zEHhM}xROA|;+gq?TW5uZ8dnHPPz`Cb)8uHU49y@X>EKQ;-CcoR_MP~_S?OKewDIby zS+Z;PqglMTS5z?c=55~Ow$O;N0ja#2Ly}l(@N!Yl>uZvMXo}{HlEm?}f@oSp)W3Xw H_Tj|~ip!4d diff --git a/crates/sui-framework-snapshot/manifest.json b/crates/sui-framework-snapshot/manifest.json index 251e84b33962f..52a6de4971010 100644 --- a/crates/sui-framework-snapshot/manifest.json +++ b/crates/sui-framework-snapshot/manifest.json @@ -438,7 +438,7 @@ ] }, "55": { - "git_revision": "e19a8015fb45", + "git_revision": "495a499c3ed2", "package_ids": [ "0x0000000000000000000000000000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000000000000000000000000000002", diff --git a/crates/sui-framework/packages/move-stdlib/sources/option.move b/crates/sui-framework/packages/move-stdlib/sources/option.move index b0f1862c91ba5..00d5b9a20686f 100644 --- a/crates/sui-framework/packages/move-stdlib/sources/option.move +++ b/crates/sui-framework/packages/move-stdlib/sources/option.move @@ -153,17 +153,14 @@ module std::option { /// Destroy `Option` and call the closure `f` on the value inside if it holds one. public macro fun do<$T>($o: Option<$T>, $f: |$T|) { let o = $o; - if (o.is_some()) { - $f(o.destroy_some()); - } + if (o.is_some()) $f(o.destroy_some()) + else o.destroy_none() } /// Execute a closure on the value inside `t` if it holds one. public macro fun do_ref<$T>($o: &Option<$T>, $f: |&$T|) { let o = $o; - if (o.is_some()) { - $f(o.borrow()); - } + if (o.is_some()) $f(o.borrow()); } /// Execute a closure on the mutable reference to the value inside `t` if it holds one. @@ -176,16 +173,24 @@ module std::option { /// Equivalent to Rust's `a.or(b)`. public macro fun or<$T>($o: Option<$T>, $default: Option<$T>): Option<$T> { let o = $o; - if (o.is_some()) o - else $default + if (o.is_some()) { + o + } else { + o.destroy_none(); + $default + } } /// If the value is `Some`, call the closure `f` on it. Otherwise, return `None`. /// Equivalent to Rust's `t.and_then(f)`. public macro fun and<$T, $U>($o: Option<$T>, $f: |$T| -> Option<$U>): Option<$U> { let o = $o; - if (o.is_some()) $f(o.extract()) - else none() + if (o.is_some()) { + $f(o.destroy_some()) + } else { + o.destroy_none(); + none() + } } /// If the value is `Some`, call the closure `f` on it. Otherwise, return `None`. @@ -199,9 +204,13 @@ module std::option { /// Map an `Option` to `Option` by applying a function to a contained value. /// Equivalent to Rust's `t.map(f)`. public macro fun map<$T, $U>($o: Option<$T>, $f: |$T| -> $U): Option<$U> { - let mut o = $o; - if (o.is_some()) some($f(o.extract())) - else none() + let o = $o; + if (o.is_some()) { + some($f(o.destroy_some())) + } else { + o.destroy_none(); + none() + } } /// Map an `Option` value to `Option` by applying a function to a contained value by reference. @@ -234,7 +243,11 @@ module std::option { /// deprecated in favor of this function. public macro fun destroy_or<$T>($o: Option<$T>, $default: $T): $T { let o = $o; - if (o.is_some()) o.destroy_some() - else $default + if (o.is_some()) { + o.destroy_some() + } else { + o.destroy_none(); + $default + } } } diff --git a/crates/sui-framework/packages/move-stdlib/tests/option_tests.move b/crates/sui-framework/packages/move-stdlib/tests/option_tests.move index a8cb8875d5495..18c02dcee2218 100644 --- a/crates/sui-framework/packages/move-stdlib/tests/option_tests.move +++ b/crates/sui-framework/packages/move-stdlib/tests/option_tests.move @@ -172,6 +172,8 @@ module std::option_tests { // === Macros === + public struct NoDrop {} + #[test] fun do_destroy() { let mut counter = 0; @@ -179,6 +181,12 @@ module std::option_tests { option::some(10).do!(|x| counter = counter + x); assert!(counter == 15); + + let some = option::some(NoDrop {}); + let none = option::none(); + + some.do!(|el| { let NoDrop {} = el; }); + none.do!(|el| { let NoDrop {} = el; }); } #[test] @@ -199,6 +207,49 @@ module std::option_tests { assert!(option::none().map_ref!(|x| vector[*x]) == option::none()); } + #[test] + fun map_no_drop() { + let none = option::none().map!(|el| { + let NoDrop {} = el; + 100u64 + }); + let some = option::some(NoDrop {}).map!(|el| { + let NoDrop {} = el; + 100u64 + }); + + assert!(none == option::none()); + assert!(some == option::some(100)); + } + + #[test] + fun or_no_drop() { + let none = option::none().or!(option::some(NoDrop {})); + let some = option::some(NoDrop {}).or!(option::some(NoDrop {})); + + assert!(none.is_some()); + assert!(some.is_some()); + + let NoDrop {} = none.destroy_some(); + let NoDrop {} = some.destroy_some(); + } + + #[test] + fun and_no_drop() { + let none = option::none().and!(|e| { + let NoDrop {} = e; + option::some(100) + }); + + let some = option::some(NoDrop {}).and!(|e| { + let NoDrop {} = e; + option::some(100) + }); + + assert!(some == option::some(100)); + assert!(none == option::none()); + } + #[test] fun filter() { assert!(option::some(5).filter!(|x| *x == 5) == option::some(5)); @@ -217,4 +268,13 @@ module std::option_tests { assert!(option::none().destroy_or!(10) == 10); assert!(option::some(5).destroy_or!(10) == 5); } + + #[test] + fun destroy_or_no_drop() { + let none = option::none().destroy_or!(NoDrop {}); + let some = option::some(NoDrop {}).destroy_or!(NoDrop {}); + + let NoDrop {} = some; + let NoDrop {} = none; + } } diff --git a/crates/sui-framework/packages_compiled/sui-framework b/crates/sui-framework/packages_compiled/sui-framework index 7e808acfe68d4facc2447aa27522b1728485b336..fa0f19b2b4ab59a685a19847154df0ccda882484 100644 GIT binary patch delta 1327 zcmZuw&2Jk;6o2!zJKou>y_>b;I3Hfe>)1{lXA?Wk#QAWVGzn>wLV<9qxT2N=Cz7Zj zBE*3r;Iub{gaE-ADJLYv1&ITQ6aN4gPN>IL;u3h{0Fg@f@V@4~_j|v$GZ*jkFCX%6 z+V_sWt9?HHhq3^`B~YZrUH0m4lz-3eick1Q!u~q*yObXWo7T_4U)=d=r0;%EeB=&2 ziDmX8rcfr=$SV-QC{$q@mf!~Lz%Cr6@7>!wI|KsovJtl_19+aJxFgVg!H5?vbYHSj zbd2j8F(NYn1=|5wHV1nOWBWK392o1Cv7R$sZVsUs_t5QmSs+nF2#`utL79{p0XnwD zNCxQql5sZEgZrh|y52tU+;GUiwt1K@40I!41Krgt1o+y-B*nw^smr8(exOCY&xkE% zR|&PHX!SRZn%kH~WyKXjnB6Rm5ove51F*N#U+za|UGFxvx!h>aqsnYyl60=$OhAq8 zFv3)gETEj(#ah)~SVXz8hjzs;`;lMsXGg2!BdoykEcPpLIga95T&>6PM026lX{{tl zr_(7Vqsi2Ay*IPg>@|AR>&0X_X}6NrwL~WiL8sm6CF9Nc&U|aE**#qBES|sp`UOjW zeC(gU^Tt%F;69 z3MD9R4T{zDEE*BNlGX3HBE^*T6q~I!|EG3kqq;}J-lRKFqLd~zA zuBM-!R4d-I$}=ukoSG{zT}F`NtxUjm*5w{Ypi#vUh=~c*I1W>cO3ZA0K^(|}Lv+rS zj?58hV;+=%`43eP%MNCXkOpj6ZR5)kCtSq3x!EGV1IwoB|~T`@IyW0&nXgso~5CImw4QAK;zhY L?)=-gb{_r>^hl56 delta 1290 zcmZ`&OK%%h6h8MgbA9i4?C~pp#;>*?f?nIo-L}_P&n5qn=d-A*vmgt{w;e^e8fKx_NR#-rF`UVTHj>;=IZN-4d;)EU!3Ga zU#5QYDb&d#IfM+9pamURfGxNN+wemC&fVR!eINkOn{YrGz%7p9S%K{{Cfv5Lea^-( zZ(7fU37G&W*b3m18SH30yNkVd0PfXvnz0b03g;%JeP zGoTv_gaAw3B-G|CdwtWS{Pc`Tp}Pk>oy{?@ZJx^)#<`ne;~YlI1o&!glHzc!bpg7Z zA7?SO!H6xUSCLeTnGGbBVsIHrrPXc{!t7qTLZls@x%*&WCHo*#*Ng#~9U22NKQabn zWynkl-CHKDk7h9?t{{O_n>PZ`m2F0t_Q@Q^{toi$;oLmN^DPVVZ*|*#wI2-T2a8cK8_tKNpcpilrdIlEy_N1tXMG~L6buKmgT-Je2*P2w z98~(Fa5O0Q!-KhS?)=@?AG7lh-~3Wc_oSYal2Fnll+q;@Dat5W5!{>zN&`vDQdyMs z;@94KL^|=EqqSr^;Rr1#en%)au{H& zT1j(MW1^C9m}XQWqW3vbkOD=bl!IDnqG*(EaMxyz5IH4N=D>?G@vA2*BpZKn(j&R} zhm%8*cgs;Bp1k+={KQzFJC=qNQ#Ni?)V8)VA}UdQxv-wlMAHmKO$2BbmE(u^3TGAS zJT*+4VU?P501w(38F!zj0c+ zgPS(~y=s>1nf+)MFYXl;jJyS#H@Ph|Vr)PvujY^>mKwZV)b|FOWFVTRIin Date: Thu, 29 Aug 2024 21:23:17 -0700 Subject: [PATCH 224/232] =?UTF-8?q?[pick][GraphQL/Events]=20Disable=20filt?= =?UTF-8?q?ering=20on=20both=20`event=5Ftype`=20and=20`emitting=E2=80=A6?= =?UTF-8?q?=20(#19135)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …_module` (#18740) ## Description Cherry pick event indexer + graphql changes. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --------- Co-authored-by: wlmyng <127570466+wlmyng@users.noreply.github.com> Co-authored-by: Lu Zhang <8418040+longbowlu@users.noreply.github.com> --- .github/workflows/bridge.yml | 4 +- bridge/evm/.gitignore | 3 +- bridge/evm/README.md | 13 +- bridge/evm/foundry.toml | 15 +- bridge/evm/remappings.txt | 13 +- bridge/evm/soldeer.lock | 24 -- .../event_connection/combo_filter_error.exp | 43 +++ .../event_connection/combo_filter_error.move | 51 +++ .../tests/event_connection/no_filter.exp | 254 ++++++++++++++ .../tests/event_connection/no_filter.move | 59 ++++ .../tests/event_connection/tx_digest.exp | 326 ++++++++++++++++++ .../tests/event_connection/tx_digest.move | 226 ++++++++++++ .../tests/event_connection/type_filter.exp | 211 ++++++++++++ .../tests/event_connection/type_filter.move | 140 ++++++++ .../event_connection/type_param_filter.exp | 182 ++++++++++ .../event_connection/type_param_filter.move | 92 +++++ crates/sui-graphql-rpc/schema.graphql | 6 +- .../sui-graphql-rpc/src/types/event/cursor.rs | 183 ++++++++++ .../sui-graphql-rpc/src/types/event/filter.rs | 44 +++ .../src/types/event/lookups.rs | 158 +++++++++ .../src/types/{event.rs => event/mod.rs} | 256 +++++--------- crates/sui-graphql-rpc/src/types/query.rs | 4 +- .../sui-graphql-rpc/src/types/type_filter.rs | 115 +----- .../snapshot_tests__schema_sdl_export.snap | 6 +- .../pg/2023-08-19-044020_events/up.sql | 8 - crates/sui-indexer/src/models/events.rs | 16 - crates/sui-indexer/src/schema/pg.rs | 8 - crates/sui-indexer/tests/ingestion_tests.rs | 69 +--- 28 files changed, 2093 insertions(+), 436 deletions(-) delete mode 100644 bridge/evm/soldeer.lock create mode 100644 crates/sui-graphql-e2e-tests/tests/event_connection/combo_filter_error.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/event_connection/combo_filter_error.move create mode 100644 crates/sui-graphql-e2e-tests/tests/event_connection/no_filter.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/event_connection/no_filter.move create mode 100644 crates/sui-graphql-e2e-tests/tests/event_connection/tx_digest.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/event_connection/tx_digest.move create mode 100644 crates/sui-graphql-e2e-tests/tests/event_connection/type_filter.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/event_connection/type_filter.move create mode 100644 crates/sui-graphql-e2e-tests/tests/event_connection/type_param_filter.exp create mode 100644 crates/sui-graphql-e2e-tests/tests/event_connection/type_param_filter.move create mode 100644 crates/sui-graphql-rpc/src/types/event/cursor.rs create mode 100644 crates/sui-graphql-rpc/src/types/event/filter.rs create mode 100644 crates/sui-graphql-rpc/src/types/event/lookups.rs rename crates/sui-graphql-rpc/src/types/{event.rs => event/mod.rs} (50%) diff --git a/.github/workflows/bridge.yml b/.github/workflows/bridge.yml index 3bd5fea50a24e..ff3a4f8288e2b 100644 --- a/.github/workflows/bridge.yml +++ b/.github/workflows/bridge.yml @@ -92,7 +92,7 @@ jobs: - name: Install Foundry Dependencies working-directory: bridge/evm run: | - forge soldeer update + forge install https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable@v5.0.1 https://github.com/foundry-rs/forge-std@v1.3.0 https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades --no-git --no-commit - name: cargo test run: | cargo nextest run --profile ci -E 'package(sui-bridge)' @@ -114,7 +114,7 @@ jobs: - name: Install Foundry Dependencies working-directory: bridge/evm run: | - forge soldeer update + forge install https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable@v5.0.1 https://github.com/foundry-rs/forge-std@v1.3.0 https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades --no-git --no-commit - name: Check Bridge EVM Unit Tests shell: bash working-directory: bridge/evm diff --git a/bridge/evm/.gitignore b/bridge/evm/.gitignore index eede9111c4f1a..b03db232ccd9e 100644 --- a/bridge/evm/.gitignore +++ b/bridge/evm/.gitignore @@ -10,4 +10,5 @@ out*/ lcov.info broadcast/**/31337 -dependencies +lib/* + diff --git a/bridge/evm/README.md b/bridge/evm/README.md index 94667326299ca..97cbdb270a45c 100644 --- a/bridge/evm/README.md +++ b/bridge/evm/README.md @@ -1,6 +1,6 @@ # 🏄‍♂️ Quick Start -This project leverages [Foundry](https://github.com/foundry-rs/foundry) to manage dependencies (via soldeer), contract compilation, testing, deployment, and on chain interactions via Solidity scripting. +This project leverages [Foundry](https://github.com/foundry-rs/foundry) to manage dependencies, contract compilation, testing, deployment, and on chain interactions via Solidity scripting. #### Environment configuration @@ -14,7 +14,7 @@ Duplicate rename the `.env.example` file to `.env`. You'll need accounts and api To install the project dependencies, run: ```bash -forge soldeer update +forge install https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable@v5.0.1 https://github.com/foundry-rs/forge-std@v1.3.0 https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades --no-git --no-commit ``` #### Compilation @@ -28,7 +28,8 @@ forge compile #### Testing ```bash -forge test +forge clean +forge test --ffi ``` #### Coverage @@ -44,13 +45,15 @@ forge coverage > The file should be named `.json` and should have the same fields and in the same order (alphabetical) as the `example.json`. ```bash -forge script script/deploy_bridge.s.sol --rpc-url <> --broadcast --verify +forge clean +forge script script/deploy_bridge.s.sol --rpc-url <> --broadcast --verify --ffi ``` **Local deployment** ```bash -forge script script/deploy_bridge.s.sol --fork-url anvil --broadcast +forge clean +forge script script/deploy_bridge.s.sol --fork-url anvil --broadcast --ffi ``` All deployments are saved in the `broadcast` directory. diff --git a/bridge/evm/foundry.toml b/bridge/evm/foundry.toml index ba31fcbba8e08..b2a3ebfec2a6d 100644 --- a/bridge/evm/foundry.toml +++ b/bridge/evm/foundry.toml @@ -3,31 +3,20 @@ src = 'contracts' test = 'test' no_match_test = "testSkip" out = 'out' -libs = ['dependencies'] +libs = ['lib'] solc = "0.8.20" build_info = true extra_output = ["storageLayout"] fs_permissions = [{ access = "read", path = "/"}] gas_reports = ["SuiBridge"] -ffi = true - [fmt] line_length = 100 - [fuzz] runs = 1000 - [rpc_endpoints] mainnet = "${MAINNET_RPC_URL}" sepolia = "${SEPOLIA_RPC_URL}" anvil = "http://localhost:8545" - [etherscan] sepolia = { key = "${ETHERSCAN_API_KEY}" } -mainnet = { key = "${ETHERSCAN_API_KEY}" } - -[dependencies] -forge-std = "1.9.2" -openzeppelin-foundry-upgrades = "0.3.1" -"@openzeppelin-contracts-upgradeable" = "5.0.1" -"@openzeppelin-contracts" = "5.0.1" \ No newline at end of file +mainnet = { key = "${ETHERSCAN_API_KEY}" } \ No newline at end of file diff --git a/bridge/evm/remappings.txt b/bridge/evm/remappings.txt index c680ee33d8dd9..5279b569511f7 100644 --- a/bridge/evm/remappings.txt +++ b/bridge/evm/remappings.txt @@ -1,8 +1,5 @@ -@forge-std=dependencies/forge-std-1.9.2/src -@openzeppelin/foundry-upgrades=dependencies/openzeppelin-foundry-upgrades-0.3.1/src -@openzeppelin/contracts=dependencies/@openzeppelin-contracts-5.0.1 -@openzeppelin/contracts-upgradeable=dependencies/@openzeppelin-contracts-upgradeable-5.0.1 -@forge-std-1.9.2=dependencies/forge-std-1.9.2 -@openzeppelin-foundry-upgrades-0.3.1=dependencies/openzeppelin-foundry-upgrades-0.3.1 -@openzeppelin-contracts-upgradeable-5.0.1=dependencies/@openzeppelin-contracts-upgradeable-5.0.1 -@openzeppelin-contracts-5.0.1=dependencies/@openzeppelin-contracts-5.0.1 \ No newline at end of file +@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/ +@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ +@openzeppelin/openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/ +ds-test/=lib/forge-std/lib/ds-test/src/ +forge-std/=lib/openzeppelin-foundry-upgrades/lib/forge-std/src/ \ No newline at end of file diff --git a/bridge/evm/soldeer.lock b/bridge/evm/soldeer.lock deleted file mode 100644 index 20bd2407a347b..0000000000000 --- a/bridge/evm/soldeer.lock +++ /dev/null @@ -1,24 +0,0 @@ - -[[dependencies]] -name = "forge-std" -version = "1.9.2" -source = "https://soldeer-revisions.s3.amazonaws.com/forge-std/1_9_2_06-08-2024_17:31:25_forge-std-1.9.2.zip" -checksum = "20fd008c7c69b6c737cc0284469d1c76497107bc3e004d8381f6d8781cb27980" - -[[dependencies]] -name = "openzeppelin-foundry-upgrades" -version = "0.3.1" -source = "https://soldeer-revisions.s3.amazonaws.com/openzeppelin-foundry-upgrades/0_3_1_25-06-2024_18:12:33_openzeppelin-foundry-upgrades.zip" -checksum = "16a43c67b7c62e4a638b669b35f7b19c98a37278811fe910750b62b6e6fdffa7" - -[[dependencies]] -name = "@openzeppelin-contracts-upgradeable" -version = "5.0.1" -source = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts-upgradeable/5_0_1_22-01-2024_13:15:10_contracts-upgradeable.zip" -checksum = "cca37ad1d376a5c3954d1c2a8d2675339f182eee535caa7ba7ebf8d589a2c19a" - -[[dependencies]] -name = "@openzeppelin-contracts" -version = "5.0.1" -source = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/5_0_1_22-01-2024_13:14:01_contracts.zip" -checksum = "c256cbf6f5f38d3b65c7528bbffb530d0bdb818a20c9d5b61235a829202d7df7" diff --git a/crates/sui-graphql-e2e-tests/tests/event_connection/combo_filter_error.exp b/crates/sui-graphql-e2e-tests/tests/event_connection/combo_filter_error.exp new file mode 100644 index 0000000000000..2e134867efbdc --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/event_connection/combo_filter_error.exp @@ -0,0 +1,43 @@ +processed 5 tasks + +init: +A: object(0,0), B: object(0,1) + +task 1, lines 9-28: +//# publish +created: object(1,0) +mutated: object(0,2) +gas summary: computation_cost: 1000000, storage_cost: 5380800, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 30: +//# run Test::M2::emit_emit_a --sender A --args 20 +events: Event { package_id: Test, transaction_module: Identifier("M2"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [20, 0, 0, 0, 0, 0, 0, 0] } +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 3, line 32: +//# create-checkpoint +Checkpoint created: 1 + +task 4, lines 34-51: +//# run-graphql +Response: { + "data": null, + "errors": [ + { + "message": "Filtering by both emitting module and event type is not supported", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "events" + ], + "extensions": { + "code": "BAD_USER_INPUT" + } + } + ] +} diff --git a/crates/sui-graphql-e2e-tests/tests/event_connection/combo_filter_error.move b/crates/sui-graphql-e2e-tests/tests/event_connection/combo_filter_error.move new file mode 100644 index 0000000000000..ad38316463e76 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/event_connection/combo_filter_error.move @@ -0,0 +1,51 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Tests that fetching events filtered on both emitting module and event would result +// in an error. + +//# init --protocol-version 51 --addresses Test=0x0 --accounts A B --simulator + +//# publish +module Test::M1 { + use sui::event; + + public struct EventA has copy, drop { + new_value: u64 + } + + public fun emit_a(value: u64) { + event::emit(EventA { new_value: value }) + } +} + +module Test::M2 { + use Test::M1; + + public fun emit_emit_a(value: u64) { + M1::emit_a(value); + } +} + +//# run Test::M2::emit_emit_a --sender A --args 20 + +//# create-checkpoint + +//# run-graphql +{ + events(filter: {sender: "@{A}", emittingModule: "@{Test}::M2", eventType: "@{Test}::M1::EventA"}) { + nodes { + sendingModule { + name + } + type { + repr + } + sender { + address + } + json + bcs + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/event_connection/no_filter.exp b/crates/sui-graphql-e2e-tests/tests/event_connection/no_filter.exp new file mode 100644 index 0000000000000..3d916fe5b9508 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/event_connection/no_filter.exp @@ -0,0 +1,254 @@ +processed 6 tasks + +init: +A: object(0,0) + +task 1, lines 6-25: +//# publish +created: object(1,0) +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 4970400, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 27: +//# run Test::M1::emit --sender A --args 0 +events: Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [0, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [1, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [2, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [3, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [4, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [5, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [6, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [7, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [8, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [9, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [10, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [11, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [12, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [13, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [14, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [15, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [16, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [17, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [18, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [19, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [20, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [21, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [22, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [23, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [24, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [25, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [26, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [27, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [28, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [29, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [30, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [31, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [32, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [33, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [34, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [35, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [36, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [37, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [38, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [39, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [40, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [41, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [42, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [43, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [44, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [45, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [46, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [47, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [48, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [49, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [50, 0, 0, 0, 0, 0, 0, 0] } +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 3, line 29: +//# create-checkpoint +Checkpoint created: 1 + +task 4, lines 31-44: +//# run-graphql +Response: { + "data": { + "events": { + "pageInfo": { + "hasPreviousPage": false, + "hasNextPage": true, + "startCursor": "eyJ0eCI6MiwiZSI6MCwiYyI6MX0", + "endCursor": "eyJ0eCI6MiwiZSI6MTksImMiOjF9" + }, + "nodes": [ + { + "json": { + "new_value": "0" + } + }, + { + "json": { + "new_value": "1" + } + }, + { + "json": { + "new_value": "2" + } + }, + { + "json": { + "new_value": "3" + } + }, + { + "json": { + "new_value": "4" + } + }, + { + "json": { + "new_value": "5" + } + }, + { + "json": { + "new_value": "6" + } + }, + { + "json": { + "new_value": "7" + } + }, + { + "json": { + "new_value": "8" + } + }, + { + "json": { + "new_value": "9" + } + }, + { + "json": { + "new_value": "10" + } + }, + { + "json": { + "new_value": "11" + } + }, + { + "json": { + "new_value": "12" + } + }, + { + "json": { + "new_value": "13" + } + }, + { + "json": { + "new_value": "14" + } + }, + { + "json": { + "new_value": "15" + } + }, + { + "json": { + "new_value": "16" + } + }, + { + "json": { + "new_value": "17" + } + }, + { + "json": { + "new_value": "18" + } + }, + { + "json": { + "new_value": "19" + } + } + ] + } + } +} + +task 5, lines 46-59: +//# run-graphql --cursors {"tx":2,"e":19,"c":1} +Response: { + "data": { + "events": { + "pageInfo": { + "hasPreviousPage": true, + "hasNextPage": true, + "startCursor": "eyJ0eCI6MiwiZSI6MjAsImMiOjF9", + "endCursor": "eyJ0eCI6MiwiZSI6MzksImMiOjF9" + }, + "nodes": [ + { + "json": { + "new_value": "20" + } + }, + { + "json": { + "new_value": "21" + } + }, + { + "json": { + "new_value": "22" + } + }, + { + "json": { + "new_value": "23" + } + }, + { + "json": { + "new_value": "24" + } + }, + { + "json": { + "new_value": "25" + } + }, + { + "json": { + "new_value": "26" + } + }, + { + "json": { + "new_value": "27" + } + }, + { + "json": { + "new_value": "28" + } + }, + { + "json": { + "new_value": "29" + } + }, + { + "json": { + "new_value": "30" + } + }, + { + "json": { + "new_value": "31" + } + }, + { + "json": { + "new_value": "32" + } + }, + { + "json": { + "new_value": "33" + } + }, + { + "json": { + "new_value": "34" + } + }, + { + "json": { + "new_value": "35" + } + }, + { + "json": { + "new_value": "36" + } + }, + { + "json": { + "new_value": "37" + } + }, + { + "json": { + "new_value": "38" + } + }, + { + "json": { + "new_value": "39" + } + } + ] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/event_connection/no_filter.move b/crates/sui-graphql-e2e-tests/tests/event_connection/no_filter.move new file mode 100644 index 0000000000000..aaca18be1a12a --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/event_connection/no_filter.move @@ -0,0 +1,59 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//# init --protocol-version 48 --addresses Test=0x0 --accounts A --simulator + +//# publish +module Test::M1 { + use sui::event; + + public struct EventA has copy, drop { + new_value: u64 + } + + public entry fun no_emit(value: u64): u64 { + value + } + + public entry fun emit(value: u64) { + let mut i = 0; + while (i < 51) { + event::emit(EventA { new_value: value + i }); + i = i + 1; + } + } +} + +//# run Test::M1::emit --sender A --args 0 + +//# create-checkpoint + +//# run-graphql +{ + events { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + nodes { + json + } + } +} + +//# run-graphql --cursors {"tx":2,"e":19,"c":1} +{ + events(after: "@{cursor_0}") { + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + nodes { + json + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/event_connection/tx_digest.exp b/crates/sui-graphql-e2e-tests/tests/event_connection/tx_digest.exp new file mode 100644 index 0000000000000..12f3b6c2ebbb3 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/event_connection/tx_digest.exp @@ -0,0 +1,326 @@ +processed 21 tasks + +init: +A: object(0,0), B: object(0,1) + +task 1, lines 10-26: +//# publish +created: object(1,0) +mutated: object(0,2) +gas summary: computation_cost: 1000000, storage_cost: 4795600, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 28: +//# run Test::M1::no_emit --sender A --args 0 +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 3, line 30: +//# run Test::M1::emit_2 --sender A --args 2 +events: Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [2, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [3, 0, 0, 0, 0, 0, 0, 0] } +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 4, line 32: +//# run Test::M1::emit_2 --sender B --args 4 +events: Event { package_id: Test, transaction_module: Identifier("M1"), sender: B, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [4, 0, 0, 0, 0, 0, 0, 0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: B, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [5, 0, 0, 0, 0, 0, 0, 0] } +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 5, line 34: +//# create-checkpoint +Checkpoint created: 1 + +task 6, lines 36-43: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "nodes": [ + { + "digest": "AXoD3PWjAdYov3o7FaWgAqJA8RmvQrjwxGxAi2MNEujz" + }, + { + "digest": "3nuQk9o2VVoqWbF6gS5vBTPwVLhMRbFJREDCvQJUavZ2" + }, + { + "digest": "5VAhspujQVcgJNvqe9Ed8BuFTeZdTRCCTzS6WwSZ9Dke" + }, + { + "digest": "8n1pk5fYM7v7tvsh4dcKxLRy8uf3he24FZCwdEKi9cSj" + }, + { + "digest": "4dqR1zeomDMNHbUAZjooSZXrosPEb67gvvsUFeUSet9v" + } + ] + } + } +} + +task 7, lines 45-55: +//# run-graphql +Response: { + "data": { + "events": { + "edges": [ + { + "cursor": "eyJ0eCI6MywiZSI6MCwiYyI6MX0", + "node": { + "json": { + "new_value": "2" + } + } + }, + { + "cursor": "eyJ0eCI6MywiZSI6MSwiYyI6MX0", + "node": { + "json": { + "new_value": "3" + } + } + } + ] + } + } +} + +task 8, lines 57-68: +//# run-graphql --cursors {"tx":3,"e":1,"c":1} +Response: { + "data": { + "events": { + "edges": [] + } + } +} + +task 9, lines 70-83: +//# run-graphql --cursors {"tx":1,"e":1,"c":1} +Response: { + "data": { + "events": { + "edges": [] + } + } +} + +task 10, lines 86-96: +//# run-graphql +Response: { + "data": { + "events": { + "edges": [ + { + "cursor": "eyJ0eCI6NCwiZSI6MCwiYyI6MX0", + "node": { + "json": { + "new_value": "4" + } + } + }, + { + "cursor": "eyJ0eCI6NCwiZSI6MSwiYyI6MX0", + "node": { + "json": { + "new_value": "5" + } + } + } + ] + } + } +} + +task 11, lines 98-108: +//# run-graphql --cursors {"tx":4,"e":0,"c":1} +Response: { + "data": { + "events": { + "edges": [ + { + "cursor": "eyJ0eCI6NCwiZSI6MSwiYyI6MX0", + "node": { + "json": { + "new_value": "5" + } + } + } + ] + } + } +} + +task 12, lines 111-121: +//# run-graphql +Response: { + "data": { + "events": { + "edges": [ + { + "cursor": "eyJ0eCI6MywiZSI6MCwiYyI6MX0", + "node": { + "json": { + "new_value": "2" + } + } + }, + { + "cursor": "eyJ0eCI6MywiZSI6MSwiYyI6MX0", + "node": { + "json": { + "new_value": "3" + } + } + } + ] + } + } +} + +task 13, lines 123-134: +//# run-graphql --cursors {"tx":3,"e":1,"c":1} +Response: { + "data": { + "events": { + "edges": [ + { + "cursor": "eyJ0eCI6MywiZSI6MCwiYyI6MX0", + "node": { + "json": { + "new_value": "2" + } + } + } + ] + } + } +} + +task 14, lines 136-149: +//# run-graphql --cursors {"tx":4,"e":1,"c":1} +Response: { + "data": { + "events": { + "edges": [] + } + } +} + +task 15, lines 152-162: +//# run-graphql +Response: { + "data": { + "events": { + "edges": [ + { + "cursor": "eyJ0eCI6NCwiZSI6MCwiYyI6MX0", + "node": { + "json": { + "new_value": "4" + } + } + }, + { + "cursor": "eyJ0eCI6NCwiZSI6MSwiYyI6MX0", + "node": { + "json": { + "new_value": "5" + } + } + } + ] + } + } +} + +task 16, lines 164-174: +//# run-graphql --cursors {"tx":4,"e":1,"c":1} +Response: { + "data": { + "events": { + "edges": [ + { + "cursor": "eyJ0eCI6NCwiZSI6MCwiYyI6MX0", + "node": { + "json": { + "new_value": "4" + } + } + } + ] + } + } +} + +task 17, lines 176-187: +//# run-graphql +Response: { + "data": { + "events": { + "edges": [ + { + "cursor": "eyJ0eCI6MywiZSI6MCwiYyI6MX0", + "node": { + "json": { + "new_value": "2" + } + } + }, + { + "cursor": "eyJ0eCI6MywiZSI6MSwiYyI6MX0", + "node": { + "json": { + "new_value": "3" + } + } + } + ] + } + } +} + +task 18, lines 189-200: +//# run-graphql +Response: { + "data": { + "events": { + "edges": [ + { + "cursor": "eyJ0eCI6NCwiZSI6MCwiYyI6MX0", + "node": { + "json": { + "new_value": "4" + } + } + }, + { + "cursor": "eyJ0eCI6NCwiZSI6MSwiYyI6MX0", + "node": { + "json": { + "new_value": "5" + } + } + } + ] + } + } +} + +task 19, lines 202-213: +//# run-graphql +Response: { + "data": { + "events": { + "edges": [] + } + } +} + +task 20, lines 215-226: +//# run-graphql +Response: { + "data": { + "events": { + "edges": [] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/event_connection/tx_digest.move b/crates/sui-graphql-e2e-tests/tests/event_connection/tx_digest.move new file mode 100644 index 0000000000000..58b17e5fd21d8 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/event_connection/tx_digest.move @@ -0,0 +1,226 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Tests that fetching events filtered on a tx digest that has no events correctly returns no nodes. +// Also tests that fetching events filtered on a tx digest that has events returns the correct +// number of page-limit-bound nodes. + +//# init --protocol-version 48 --addresses Test=0x0 --accounts A B --simulator + +//# publish +module Test::M1 { + use sui::event; + + public struct EventA has copy, drop { + new_value: u64 + } + + public entry fun no_emit(value: u64): u64 { + value + } + + public entry fun emit_2(value: u64) { + event::emit(EventA { new_value: value }); + event::emit(EventA { new_value: value + 1}) + } +} + +//# run Test::M1::no_emit --sender A --args 0 + +//# run Test::M1::emit_2 --sender A --args 2 + +//# run Test::M1::emit_2 --sender B --args 4 + +//# create-checkpoint + +//# run-graphql +{ + transactionBlocks { + nodes { + digest + } + } +} + +//# run-graphql +{ + events(filter: {transactionDigest: "8n1pk5fYM7v7tvsh4dcKxLRy8uf3he24FZCwdEKi9cSj"}) { + edges { + cursor + node { + json + } + } + } +} + +//# run-graphql --cursors {"tx":3,"e":1,"c":1} +# When the tx digest and after cursor are on the same tx, we'll use the after cursor's event sequence number +{ + events(after: "@{cursor_0}" filter: {transactionDigest: "8n1pk5fYM7v7tvsh4dcKxLRy8uf3he24FZCwdEKi9cSj"}) { + edges { + cursor + node { + json + } + } + } +} + +//# run-graphql --cursors {"tx":1,"e":1,"c":1} +# If the after cursor does not match the transaction digest's tx sequence number, +# we will get an empty response, since it's not possible to fetch an event +# that isn't of the same tx sequence number +{ + events(after: "@{cursor_0}" filter: {transactionDigest: "8n1pk5fYM7v7tvsh4dcKxLRy8uf3he24FZCwdEKi9cSj"}) { + edges { + cursor + node { + json + } + } + } +} + + +//# run-graphql +{ + events(filter: {transactionDigest: "4dqR1zeomDMNHbUAZjooSZXrosPEb67gvvsUFeUSet9v"}) { + edges { + cursor + node { + json + } + } + } +} + +//# run-graphql --cursors {"tx":4,"e":0,"c":1} +{ + events(after: "@{cursor_0}" filter: {transactionDigest: "4dqR1zeomDMNHbUAZjooSZXrosPEb67gvvsUFeUSet9v"}) { + edges { + cursor + node { + json + } + } + } +} + + +//# run-graphql +{ + events(last: 10 filter: {transactionDigest: "8n1pk5fYM7v7tvsh4dcKxLRy8uf3he24FZCwdEKi9cSj"}) { + edges { + cursor + node { + json + } + } + } +} + +//# run-graphql --cursors {"tx":3,"e":1,"c":1} +# When the tx digest and cursor are on the same tx, we'll use the cursor's event sequence number +{ + events(last: 10 before: "@{cursor_0}" filter: {transactionDigest: "8n1pk5fYM7v7tvsh4dcKxLRy8uf3he24FZCwdEKi9cSj"}) { + edges { + cursor + node { + json + } + } + } +} + +//# run-graphql --cursors {"tx":4,"e":1,"c":1} +# If the cursor does not match the transaction digest's tx sequence number, +# we will get an empty response, since it's not possible to fetch an event +# that isn't of the same tx sequence number +{ + events(last: 10 before: "@{cursor_0}" filter: {transactionDigest: "8n1pk5fYM7v7tvsh4dcKxLRy8uf3he24FZCwdEKi9cSj"}) { + edges { + cursor + node { + json + } + } + } +} + + +//# run-graphql +{ + events(last: 10 filter: {transactionDigest: "4dqR1zeomDMNHbUAZjooSZXrosPEb67gvvsUFeUSet9v"}) { + edges { + cursor + node { + json + } + } + } +} + +//# run-graphql --cursors {"tx":4,"e":1,"c":1} +{ + events(last: 10 before: "@{cursor_0}" filter: {transactionDigest: "4dqR1zeomDMNHbUAZjooSZXrosPEb67gvvsUFeUSet9v"}) { + edges { + cursor + node { + json + } + } + } +} + +//# run-graphql +# correct sender +{ + events(filter: {sender: "@{A}" transactionDigest: "8n1pk5fYM7v7tvsh4dcKxLRy8uf3he24FZCwdEKi9cSj"}) { + edges { + cursor + node { + json + } + } + } +} + +//# run-graphql +# correct sender +{ + events(filter: {sender: "@{B}" transactionDigest: "4dqR1zeomDMNHbUAZjooSZXrosPEb67gvvsUFeUSet9v"}) { + edges { + cursor + node { + json + } + } + } +} + +//# run-graphql +# incorrect sender +{ + events(filter: {sender: "@{B}" transactionDigest: "8n1pk5fYM7v7tvsh4dcKxLRy8uf3he24FZCwdEKi9cSj"}) { + edges { + cursor + node { + json + } + } + } +} + +//# run-graphql +# incorrect sender +{ + events(filter: {sender: "@{A}" transactionDigest: "4dqR1zeomDMNHbUAZjooSZXrosPEb67gvvsUFeUSet9v"}) { + edges { + cursor + node { + json + } + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/event_connection/type_filter.exp b/crates/sui-graphql-e2e-tests/tests/event_connection/type_filter.exp new file mode 100644 index 0000000000000..3afb31848780e --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/event_connection/type_filter.exp @@ -0,0 +1,211 @@ +processed 12 tasks + +init: +A: object(0,0), B: object(0,1) + +task 1, lines 6-34: +//# publish +created: object(1,0) +mutated: object(0,2) +gas summary: computation_cost: 1000000, storage_cost: 6604400, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 36: +//# run Test::M2::emit_emit_a --sender A --args 20 +events: Event { package_id: Test, transaction_module: Identifier("M2"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [] }, contents: [20, 0, 0, 0, 0, 0, 0, 0] } +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 3, line 38: +//# create-checkpoint +Checkpoint created: 1 + +task 4, lines 40-57: +//# run-graphql +Response: { + "data": { + "events": { + "nodes": [ + { + "sendingModule": { + "name": "M2" + }, + "type": { + "repr": "0x6edb181eb03cea19a3c4b09d2d6b5de8d0a741df186d072d18b2030eb36faee1::M1::EventA" + }, + "sender": { + "address": "0xfccc9a421bbb13c1a66a1aa98f0ad75029ede94857779c6915b44f94068b921e" + }, + "json": { + "new_value": "20" + }, + "bcs": "FAAAAAAAAAA=" + } + ] + } + } +} + +task 5, line 59: +//# run Test::M2::emit_b --sender A --args 42 +events: Event { package_id: Test, transaction_module: Identifier("M2"), sender: A, type_: StructTag { address: Test, module: Identifier("M2"), name: Identifier("EventB"), type_params: [] }, contents: [42, 0, 0, 0, 0, 0, 0, 0] } +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 6, line 61: +//# run Test::M2::emit_b --sender B --args 43 +events: Event { package_id: Test, transaction_module: Identifier("M2"), sender: B, type_: StructTag { address: Test, module: Identifier("M2"), name: Identifier("EventB"), type_params: [] }, contents: [43, 0, 0, 0, 0, 0, 0, 0] } +mutated: object(0,1) +gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 7, line 63: +//# create-checkpoint +Checkpoint created: 2 + +task 8, lines 65-82: +//# run-graphql +Response: { + "data": { + "events": { + "nodes": [ + { + "sendingModule": { + "name": "M2" + }, + "type": { + "repr": "0x6edb181eb03cea19a3c4b09d2d6b5de8d0a741df186d072d18b2030eb36faee1::M1::EventA" + }, + "sender": { + "address": "0xfccc9a421bbb13c1a66a1aa98f0ad75029ede94857779c6915b44f94068b921e" + }, + "json": { + "new_value": "20" + }, + "bcs": "FAAAAAAAAAA=" + } + ] + } + } +} + +task 9, lines 84-101: +//# run-graphql +Response: { + "data": { + "events": { + "nodes": [ + { + "sendingModule": { + "name": "M2" + }, + "type": { + "repr": "0x6edb181eb03cea19a3c4b09d2d6b5de8d0a741df186d072d18b2030eb36faee1::M2::EventB" + }, + "sender": { + "address": "0xfccc9a421bbb13c1a66a1aa98f0ad75029ede94857779c6915b44f94068b921e" + }, + "json": { + "new_value": "42" + }, + "bcs": "KgAAAAAAAAA=" + } + ] + } + } +} + +task 10, lines 103-120: +//# run-graphql +Response: { + "data": { + "events": { + "nodes": [ + { + "sendingModule": { + "name": "M2" + }, + "type": { + "repr": "0x6edb181eb03cea19a3c4b09d2d6b5de8d0a741df186d072d18b2030eb36faee1::M1::EventA" + }, + "sender": { + "address": "0xfccc9a421bbb13c1a66a1aa98f0ad75029ede94857779c6915b44f94068b921e" + }, + "json": { + "new_value": "20" + }, + "bcs": "FAAAAAAAAAA=" + }, + { + "sendingModule": { + "name": "M2" + }, + "type": { + "repr": "0x6edb181eb03cea19a3c4b09d2d6b5de8d0a741df186d072d18b2030eb36faee1::M2::EventB" + }, + "sender": { + "address": "0xfccc9a421bbb13c1a66a1aa98f0ad75029ede94857779c6915b44f94068b921e" + }, + "json": { + "new_value": "42" + }, + "bcs": "KgAAAAAAAAA=" + } + ] + } + } +} + +task 11, lines 122-139: +//# run-graphql +Response: { + "data": { + "events": { + "nodes": [ + { + "sendingModule": { + "name": "M2" + }, + "type": { + "repr": "0x6edb181eb03cea19a3c4b09d2d6b5de8d0a741df186d072d18b2030eb36faee1::M1::EventA" + }, + "sender": { + "address": "0xfccc9a421bbb13c1a66a1aa98f0ad75029ede94857779c6915b44f94068b921e" + }, + "json": { + "new_value": "20" + }, + "bcs": "FAAAAAAAAAA=" + }, + { + "sendingModule": { + "name": "M2" + }, + "type": { + "repr": "0x6edb181eb03cea19a3c4b09d2d6b5de8d0a741df186d072d18b2030eb36faee1::M2::EventB" + }, + "sender": { + "address": "0xfccc9a421bbb13c1a66a1aa98f0ad75029ede94857779c6915b44f94068b921e" + }, + "json": { + "new_value": "42" + }, + "bcs": "KgAAAAAAAAA=" + }, + { + "sendingModule": { + "name": "M2" + }, + "type": { + "repr": "0x6edb181eb03cea19a3c4b09d2d6b5de8d0a741df186d072d18b2030eb36faee1::M2::EventB" + }, + "sender": { + "address": "0xa7b032703878aa74c3126935789fd1d4d7e111d5911b09247d6963061c312b5a" + }, + "json": { + "new_value": "43" + }, + "bcs": "KwAAAAAAAAA=" + } + ] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/event_connection/type_filter.move b/crates/sui-graphql-e2e-tests/tests/event_connection/type_filter.move new file mode 100644 index 0000000000000..0ca546a3a94ef --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/event_connection/type_filter.move @@ -0,0 +1,140 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//# init --protocol-version 51 --addresses Test=0x0 --accounts A B --simulator + +//# publish +module Test::M1 { + use sui::event; + + public struct EventA has copy, drop { + new_value: u64 + } + + public fun emit_a(value: u64) { + event::emit(EventA { new_value: value }) + } +} + +module Test::M2 { + use sui::event; + use Test::M1; + + public struct EventB has copy, drop { + new_value: u64 + } + + public fun emit_emit_a(value: u64) { + M1::emit_a(value); + } + + public fun emit_b(value: u64) { + event::emit(EventB { new_value: value }) + } +} + +//# run Test::M2::emit_emit_a --sender A --args 20 + +//# create-checkpoint + +//# run-graphql +{ + events(filter: {sender: "@{A}", eventType: "@{Test}::M1::EventA"}) { + nodes { + sendingModule { + name + } + type { + repr + } + sender { + address + } + json + bcs + } + } +} + +//# run Test::M2::emit_b --sender A --args 42 + +//# run Test::M2::emit_b --sender B --args 43 + +//# create-checkpoint + +//# run-graphql +{ + events(filter: {sender: "@{A}", eventType: "@{Test}::M1"}) { + nodes { + sendingModule { + name + } + type { + repr + } + sender { + address + } + json + bcs + } + } +} + +//# run-graphql +{ + events(filter: {sender: "@{A}", eventType: "@{Test}::M2"}) { + nodes { + sendingModule { + name + } + type { + repr + } + sender { + address + } + json + bcs + } + } +} + +//# run-graphql +{ + events(filter: {sender: "@{A}", eventType: "@{Test}"}) { + nodes { + sendingModule { + name + } + type { + repr + } + sender { + address + } + json + bcs + } + } +} + +//# run-graphql +{ + events(filter: {eventType: "@{Test}"}) { + nodes { + sendingModule { + name + } + type { + repr + } + sender { + address + } + json + bcs + } + } +} + diff --git a/crates/sui-graphql-e2e-tests/tests/event_connection/type_param_filter.exp b/crates/sui-graphql-e2e-tests/tests/event_connection/type_param_filter.exp new file mode 100644 index 0000000000000..383af1455f8f3 --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/event_connection/type_param_filter.exp @@ -0,0 +1,182 @@ +processed 10 tasks + +init: +A: object(0,0), B: object(0,1) + +task 1, lines 6-29: +//# publish +created: object(1,0) +mutated: object(0,2) +gas summary: computation_cost: 1000000, storage_cost: 5996400, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, line 32: +//# run Test::M1::emit_T1 --sender A +events: Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [Struct(StructTag { address: Test, module: Identifier("M1"), name: Identifier("T1"), type_params: [] })] }, contents: [0] } +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 3, line 34: +//# run Test::M1::emit_T2 --sender A +events: Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [Struct(StructTag { address: Test, module: Identifier("M1"), name: Identifier("T2"), type_params: [] })] }, contents: [0] } +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 4, line 36: +//# run Test::M1::emit_both --sender A +events: Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [Struct(StructTag { address: Test, module: Identifier("M1"), name: Identifier("T1"), type_params: [] })] }, contents: [0] }, Event { package_id: Test, transaction_module: Identifier("M1"), sender: A, type_: StructTag { address: Test, module: Identifier("M1"), name: Identifier("EventA"), type_params: [Struct(StructTag { address: Test, module: Identifier("M1"), name: Identifier("T2"), type_params: [] })] }, contents: [0] } +mutated: object(0,0) +gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 978120, non_refundable_storage_fee: 9880 + +task 5, line 38: +//# create-checkpoint +Checkpoint created: 1 + +task 6, lines 40-47: +//# run-graphql +Response: { + "data": { + "transactionBlocks": { + "nodes": [ + { + "digest": "J7mHXcoa7LXwyjzZUWsk8zvYZjek359TM4d2hQK4LGHo" + }, + { + "digest": "Ch3i5cdtNPU5v8oSg3V5cdKtZDa3YjqjMd7Qh4NQLAx6" + }, + { + "digest": "6taTf6v2NQCFtd9A4nw3mBbkHwUw8RUoJjqQGSB5cBNt" + }, + { + "digest": "AEXpVZ7Vpsk7ZSTsR2QNPT7zhq8oqpJXRGgx3kAaTdn" + }, + { + "digest": "9nu1ivpL9hHcbJ9GwGfmD3Kuet5w74t2GBp8f1Ggy3UD" + } + ] + } + } +} + +task 7, lines 49-62: +//# run-graphql +Response: { + "data": { + "events": { + "nodes": [ + { + "type": { + "repr": "0xe722de9e58a9bab3a202b769b7518f91f852460d3d2c6d6743c301d08b9e614a::M1::EventA<0xe722de9e58a9bab3a202b769b7518f91f852460d3d2c6d6743c301d08b9e614a::M1::T1>" + }, + "sender": { + "address": "0xfccc9a421bbb13c1a66a1aa98f0ad75029ede94857779c6915b44f94068b921e" + }, + "json": { + "value": { + "dummy_field": false + } + } + }, + { + "type": { + "repr": "0xe722de9e58a9bab3a202b769b7518f91f852460d3d2c6d6743c301d08b9e614a::M1::EventA<0xe722de9e58a9bab3a202b769b7518f91f852460d3d2c6d6743c301d08b9e614a::M1::T2>" + }, + "sender": { + "address": "0xfccc9a421bbb13c1a66a1aa98f0ad75029ede94857779c6915b44f94068b921e" + }, + "json": { + "value": { + "dummy_field": false + } + } + }, + { + "type": { + "repr": "0xe722de9e58a9bab3a202b769b7518f91f852460d3d2c6d6743c301d08b9e614a::M1::EventA<0xe722de9e58a9bab3a202b769b7518f91f852460d3d2c6d6743c301d08b9e614a::M1::T1>" + }, + "sender": { + "address": "0xfccc9a421bbb13c1a66a1aa98f0ad75029ede94857779c6915b44f94068b921e" + }, + "json": { + "value": { + "dummy_field": false + } + } + }, + { + "type": { + "repr": "0xe722de9e58a9bab3a202b769b7518f91f852460d3d2c6d6743c301d08b9e614a::M1::EventA<0xe722de9e58a9bab3a202b769b7518f91f852460d3d2c6d6743c301d08b9e614a::M1::T2>" + }, + "sender": { + "address": "0xfccc9a421bbb13c1a66a1aa98f0ad75029ede94857779c6915b44f94068b921e" + }, + "json": { + "value": { + "dummy_field": false + } + } + } + ] + } + } +} + +task 8, lines 64-77: +//# run-graphql +Response: { + "data": { + "events": { + "nodes": [ + { + "type": { + "repr": "0xe722de9e58a9bab3a202b769b7518f91f852460d3d2c6d6743c301d08b9e614a::M1::EventA<0xe722de9e58a9bab3a202b769b7518f91f852460d3d2c6d6743c301d08b9e614a::M1::T1>" + }, + "sender": { + "address": "0xfccc9a421bbb13c1a66a1aa98f0ad75029ede94857779c6915b44f94068b921e" + }, + "json": { + "value": { + "dummy_field": false + } + } + }, + { + "type": { + "repr": "0xe722de9e58a9bab3a202b769b7518f91f852460d3d2c6d6743c301d08b9e614a::M1::EventA<0xe722de9e58a9bab3a202b769b7518f91f852460d3d2c6d6743c301d08b9e614a::M1::T1>" + }, + "sender": { + "address": "0xfccc9a421bbb13c1a66a1aa98f0ad75029ede94857779c6915b44f94068b921e" + }, + "json": { + "value": { + "dummy_field": false + } + } + } + ] + } + } +} + +task 9, lines 79-92: +//# run-graphql +Response: { + "data": { + "events": { + "nodes": [ + { + "type": { + "repr": "0xe722de9e58a9bab3a202b769b7518f91f852460d3d2c6d6743c301d08b9e614a::M1::EventA<0xe722de9e58a9bab3a202b769b7518f91f852460d3d2c6d6743c301d08b9e614a::M1::T2>" + }, + "sender": { + "address": "0xfccc9a421bbb13c1a66a1aa98f0ad75029ede94857779c6915b44f94068b921e" + }, + "json": { + "value": { + "dummy_field": false + } + } + } + ] + } + } +} diff --git a/crates/sui-graphql-e2e-tests/tests/event_connection/type_param_filter.move b/crates/sui-graphql-e2e-tests/tests/event_connection/type_param_filter.move new file mode 100644 index 0000000000000..128362999b50b --- /dev/null +++ b/crates/sui-graphql-e2e-tests/tests/event_connection/type_param_filter.move @@ -0,0 +1,92 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//# init --protocol-version 51 --addresses Test=0x0 --accounts A B --simulator + +//# publish +module Test::M1 { + use sui::event; + + public struct T1 has copy, drop {} + public struct T2 has copy, drop {} + + public struct EventA has copy, drop { + value: T + } + + public fun emit_T1() { + event::emit(EventA { value: T1 {} }) + } + + public fun emit_T2() { + event::emit(EventA { value: T2 {} }) + } + + public fun emit_both() { + event::emit(EventA { value: T1 {} }); + event::emit(EventA { value: T2 {} }) + } +} + + +//# run Test::M1::emit_T1 --sender A + +//# run Test::M1::emit_T2 --sender A + +//# run Test::M1::emit_both --sender A + +//# create-checkpoint + +//# run-graphql +{ + transactionBlocks { + nodes { + digest + } + } +} + +//# run-graphql +{ + events(filter: {eventType: "@{Test}::M1::EventA"}) { + nodes { + type { + repr + } + sender { + address + } + json + } + } +} + +//# run-graphql +{ + events(filter: {eventType: "@{Test}::M1::EventA<@{Test}::M1::T1>"}) { + nodes { + type { + repr + } + sender { + address + } + json + } + } +} + +//# run-graphql +{ + events(filter: {eventType: "@{Test}::M1::EventA<@{Test}::M1::T2>", transactionDigest: "9nu1ivpL9hHcbJ9GwGfmD3Kuet5w74t2GBp8f1Ggy3UD"}) { + nodes { + type { + repr + } + sender { + address + } + json + } + } +} diff --git a/crates/sui-graphql-rpc/schema.graphql b/crates/sui-graphql-rpc/schema.graphql index defc55cc329d2..faca90cc4e354 100644 --- a/crates/sui-graphql-rpc/schema.graphql +++ b/crates/sui-graphql-rpc/schema.graphql @@ -1272,6 +1272,8 @@ input EventFilter { PTB and emits an event. Modules can be filtered by their package, or package::module. + We currently do not support filtering by emitting module and event type + at the same time so if both are provided in one filter, the query will error. """ emittingModule: String """ @@ -3312,7 +3314,9 @@ type Query { """ transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ - The events that exist in the network. + Query events that are emitted in the network. + We currently do not support filtering by emitting module and event type + at the same time so if both are provided in one filter, the query will error. """ events(first: Int, after: String, last: Int, before: String, filter: EventFilter): EventConnection! """ diff --git a/crates/sui-graphql-rpc/src/types/event/cursor.rs b/crates/sui-graphql-rpc/src/types/event/cursor.rs new file mode 100644 index 0000000000000..9a64399f1fab6 --- /dev/null +++ b/crates/sui-graphql-rpc/src/types/event/cursor.rs @@ -0,0 +1,183 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + consistency::Checkpointed, + filter, + raw_query::RawQuery, + types::cursor::{self, Paginated, RawPaginated, ScanLimited, Target}, +}; +use diesel::{ + backend::Backend, + deserialize::{self, FromSql, QueryableByName}, + row::NamedRow, + BoolExpressionMethods, ExpressionMethods, QueryDsl, +}; +use serde::{Deserialize, Serialize}; +use sui_indexer::{models::events::StoredEvent, schema::events}; + +use super::Query; + +/// Contents of an Event's cursor. +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)] +pub(crate) struct EventKey { + /// Transaction Sequence Number + pub tx: u64, + + /// Event Sequence Number + pub e: u64, + + /// The checkpoint sequence number this was viewed at. + #[serde(rename = "c")] + pub checkpoint_viewed_at: u64, +} + +pub(crate) type Cursor = cursor::JsonCursor; + +/// Results from raw queries in Diesel can only be deserialized into structs that implement +/// `QueryableByName`. This struct is used to represent a row of `tx_sequence_number` and +/// `event_sequence_number` returned from subqueries against event lookup tables. +#[derive(Clone, Debug)] +pub struct EvLookup { + pub tx: i64, + pub ev: i64, +} + +impl Paginated for StoredEvent { + type Source = events::table; + + fn filter_ge(cursor: &Cursor, query: Query) -> Query { + use events::dsl::{event_sequence_number as event, tx_sequence_number as tx}; + query.filter( + tx.gt(cursor.tx as i64) + .or(tx.eq(cursor.tx as i64).and(event.ge(cursor.e as i64))), + ) + } + + fn filter_le(cursor: &Cursor, query: Query) -> Query { + use events::dsl::{event_sequence_number as event, tx_sequence_number as tx}; + query.filter( + tx.lt(cursor.tx as i64) + .or(tx.eq(cursor.tx as i64).and(event.le(cursor.e as i64))), + ) + } + + fn order(asc: bool, query: Query) -> Query { + use events::dsl; + if asc { + query + .order_by(dsl::tx_sequence_number.asc()) + .then_order_by(dsl::event_sequence_number.asc()) + } else { + query + .order_by(dsl::tx_sequence_number.desc()) + .then_order_by(dsl::event_sequence_number.desc()) + } + } +} + +impl RawPaginated for StoredEvent { + fn filter_ge(cursor: &Cursor, query: RawQuery) -> RawQuery { + filter!( + query, + format!( + "ROW(tx_sequence_number, event_sequence_number) >= ({}, {})", + cursor.tx, cursor.e + ) + ) + } + + fn filter_le(cursor: &Cursor, query: RawQuery) -> RawQuery { + filter!( + query, + format!( + "ROW(tx_sequence_number, event_sequence_number) <= ({}, {})", + cursor.tx, cursor.e + ) + ) + } + + fn order(asc: bool, query: RawQuery) -> RawQuery { + if asc { + query.order_by("tx_sequence_number ASC, event_sequence_number ASC") + } else { + query.order_by("tx_sequence_number DESC, event_sequence_number DESC") + } + } +} + +impl Target for StoredEvent { + fn cursor(&self, checkpoint_viewed_at: u64) -> Cursor { + Cursor::new(EventKey { + tx: self.tx_sequence_number as u64, + e: self.event_sequence_number as u64, + checkpoint_viewed_at, + }) + } +} + +impl Checkpointed for Cursor { + fn checkpoint_viewed_at(&self) -> u64 { + self.checkpoint_viewed_at + } +} + +impl ScanLimited for Cursor {} + +impl Target for EvLookup { + fn cursor(&self, checkpoint_viewed_at: u64) -> Cursor { + Cursor::new(EventKey { + tx: self.tx as u64, + e: self.ev as u64, + checkpoint_viewed_at, + }) + } +} + +impl RawPaginated for EvLookup { + fn filter_ge(cursor: &Cursor, query: RawQuery) -> RawQuery { + filter!( + query, + format!( + "ROW(tx_sequence_number, event_sequence_number) >= ({}, {})", + cursor.tx, cursor.e + ) + ) + } + + fn filter_le(cursor: &Cursor, query: RawQuery) -> RawQuery { + filter!( + query, + format!( + "ROW(tx_sequence_number, event_sequence_number) <= ({}, {})", + cursor.tx, cursor.e + ) + ) + } + + fn order(asc: bool, query: RawQuery) -> RawQuery { + if asc { + query.order_by("tx_sequence_number ASC, event_sequence_number ASC") + } else { + query.order_by("tx_sequence_number DESC, event_sequence_number DESC") + } + } +} + +/// `sql_query` raw queries require `QueryableByName`. The default implementation looks for a table +/// based on the struct name, and it also expects the struct's fields to reflect the table's +/// columns. We can override this behavior by implementing `QueryableByName` for our struct. For +/// `EvLookup`, its fields are derived from the common `tx_sequence_number` and +/// `event_sequence_number` columns for all events-related tables. +impl QueryableByName for EvLookup +where + DB: Backend, + i64: FromSql, +{ + fn build<'a>(row: &impl NamedRow<'a, DB>) -> deserialize::Result { + let tx = NamedRow::get::(row, "tx_sequence_number")?; + let ev = NamedRow::get::(row, "event_sequence_number")?; + + Ok(Self { tx, ev }) + } +} diff --git a/crates/sui-graphql-rpc/src/types/event/filter.rs b/crates/sui-graphql-rpc/src/types/event/filter.rs new file mode 100644 index 0000000000000..0a02a31be8969 --- /dev/null +++ b/crates/sui-graphql-rpc/src/types/event/filter.rs @@ -0,0 +1,44 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::types::{ + digest::Digest, + sui_address::SuiAddress, + type_filter::{ModuleFilter, TypeFilter}, +}; +use async_graphql::*; + +#[derive(InputObject, Clone, Default)] +pub(crate) struct EventFilter { + pub sender: Option, + pub transaction_digest: Option, + // Enhancement (post-MVP) + // after_checkpoint + // before_checkpoint + /// Events emitted by a particular module. An event is emitted by a + /// particular module if some function in the module is called by a + /// PTB and emits an event. + /// + /// Modules can be filtered by their package, or package::module. + /// We currently do not support filtering by emitting module and event type + /// at the same time so if both are provided in one filter, the query will error. + pub emitting_module: Option, + + /// This field is used to specify the type of event emitted. + /// + /// Events can be filtered by their type's package, package::module, + /// or their fully qualified type name. + /// + /// Generic types can be queried by either the generic type name, e.g. + /// `0x2::coin::Coin`, or by the full type name, such as + /// `0x2::coin::Coin<0x2::sui::SUI>`. + pub event_type: Option, + // Enhancement (post-MVP) + // pub start_time + // pub end_time + + // Enhancement (post-MVP) + // pub any + // pub all + // pub not +} diff --git a/crates/sui-graphql-rpc/src/types/event/lookups.rs b/crates/sui-graphql-rpc/src/types/event/lookups.rs new file mode 100644 index 0000000000000..93a9a55bf6e6b --- /dev/null +++ b/crates/sui-graphql-rpc/src/types/event/lookups.rs @@ -0,0 +1,158 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + data::pg::bytea_literal, + filter, query, + raw_query::RawQuery, + types::{ + cursor::Page, + digest::Digest, + sui_address::SuiAddress, + type_filter::{ModuleFilter, TypeFilter}, + }, +}; + +use std::fmt::Write; + +use super::Cursor; + +fn select_ev(sender: Option, from: &str) -> RawQuery { + let query = query!(format!( + "SELECT tx_sequence_number, event_sequence_number FROM {}", + from + )); + + if let Some(sender) = sender { + return query.filter(format!("sender = {}", bytea_literal(sender.as_slice()))); + } + + query +} + +pub(crate) fn select_sender(sender: SuiAddress) -> RawQuery { + select_ev(Some(sender), "event_senders") +} + +pub(crate) fn select_event_type(event_type: &TypeFilter, sender: Option) -> RawQuery { + match event_type { + TypeFilter::ByModule(ModuleFilter::ByPackage(p)) => { + filter!( + select_ev(sender, "event_struct_package"), + format!("package = {}", bytea_literal(p.as_slice())) + ) + } + TypeFilter::ByModule(ModuleFilter::ByModule(p, m)) => { + filter!( + select_ev(sender, "event_struct_module"), + format!( + "package = {} and module = {{}}", + bytea_literal(p.as_slice()) + ), + m + ) + } + TypeFilter::ByType(tag) => { + let package = tag.address; + let module = tag.module.to_string(); + let mut name = tag.name.as_str().to_owned(); + let (table, col_name) = if tag.type_params.is_empty() { + ("event_struct_name", "type_name") + } else { + let mut prefix = "<"; + for param in &tag.type_params { + name += prefix; + // SAFETY: write! to String always succeeds. + write!( + name, + "{}", + param.to_canonical_display(/* with_prefix */ true) + ) + .unwrap(); + prefix = ", "; + } + name += ">"; + ("event_struct_instantiation", "type_instantiation") + }; + + filter!( + select_ev(sender, table), + format!( + "package = {} and module = {{}} and {} = {{}}", + bytea_literal(package.as_slice()), + col_name + ), + module, + name + ) + } + } +} + +pub(crate) fn select_emit_module( + emit_module: &ModuleFilter, + sender: Option, +) -> RawQuery { + match emit_module { + ModuleFilter::ByPackage(p) => { + filter!( + select_ev(sender, "event_emit_package"), + format!("package = {}", bytea_literal(p.as_slice())) + ) + } + ModuleFilter::ByModule(p, m) => { + filter!( + select_ev(sender, "event_emit_module"), + format!( + "package = {} and module = {{}}", + bytea_literal(p.as_slice()) + ), + m + ) + } + } +} + +/// Adds filters to bound an events query from above and below based on cursors and filters. The +/// query will always at least be bounded by `tx_hi`, the current exclusive upperbound on +/// transaction sequence numbers, based on the consistency cursor. +pub(crate) fn add_bounds( + mut query: RawQuery, + tx_digest_filter: &Option, + page: &Page, + tx_hi: i64, +) -> RawQuery { + query = filter!(query, format!("tx_sequence_number < {}", tx_hi)); + + if let Some(after) = page.after() { + query = filter!( + query, + format!( + "ROW(tx_sequence_number, event_sequence_number) >= ({}, {})", + after.tx, after.e + ) + ); + } + + if let Some(before) = page.before() { + query = filter!( + query, + format!( + "ROW(tx_sequence_number, event_sequence_number) <= ({}, {})", + before.tx, before.e + ) + ); + } + + if let Some(digest) = tx_digest_filter { + query = filter!( + query, + format!( + "tx_sequence_number = (SELECT tx_sequence_number FROM tx_digests WHERE tx_digest = {})", + bytea_literal(digest.as_slice()), + ) + ); + } + + query +} diff --git a/crates/sui-graphql-rpc/src/types/event.rs b/crates/sui-graphql-rpc/src/types/event/mod.rs similarity index 50% rename from crates/sui-graphql-rpc/src/types/event.rs rename to crates/sui-graphql-rpc/src/types/event/mod.rs index cb558c2fba6c3..10c130c7f43c1 100644 --- a/crates/sui-graphql-rpc/src/types/event.rs +++ b/crates/sui-graphql-rpc/src/types/event/mod.rs @@ -3,28 +3,33 @@ use std::str::FromStr; -use super::cursor::{self, Page, Paginated, ScanLimited, Target}; -use super::digest::Digest; -use super::type_filter::{ModuleFilter, TypeFilter}; +use super::cursor::{Page, Target}; use super::{ address::Address, base64::Base64, date_time::DateTime, move_module::MoveModule, - move_value::MoveValue, sui_address::SuiAddress, + move_value::MoveValue, }; -use crate::consistency::Checkpointed; -use crate::data::{self, QueryExecutor}; +use crate::data::{self, DbConnection, QueryExecutor}; +use crate::query; use crate::{data::Db, error::Error}; use async_graphql::connection::{Connection, CursorType, Edge}; use async_graphql::*; -use diesel::{BoolExpressionMethods, ExpressionMethods, NullableExpressionMethods, QueryDsl}; -use serde::{Deserialize, Serialize}; +use cursor::EvLookup; +use diesel::{ExpressionMethods, QueryDsl}; +use lookups::{add_bounds, select_emit_module, select_event_type, select_sender}; use sui_indexer::models::{events::StoredEvent, transactions::StoredTransaction}; -use sui_indexer::schema::{events, transactions, tx_senders}; +use sui_indexer::schema::{checkpoints, events}; use sui_types::base_types::ObjectID; use sui_types::Identifier; use sui_types::{ base_types::SuiAddress as NativeSuiAddress, event::Event as NativeEvent, parse_sui_struct_tag, }; +mod cursor; +mod filter; +mod lookups; +pub(crate) use cursor::Cursor; +pub(crate) use filter::EventFilter; + /// A Sui node emits one of the following events: /// Move event /// Publish event @@ -40,56 +45,8 @@ pub(crate) struct Event { pub checkpoint_viewed_at: u64, } -/// Contents of an Event's cursor. -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)] -pub(crate) struct EventKey { - /// Transaction Sequence Number - tx: u64, - - /// Event Sequence Number - e: u64, - - /// The checkpoint sequence number this was viewed at. - #[serde(rename = "c")] - checkpoint_viewed_at: u64, -} - -pub(crate) type Cursor = cursor::JsonCursor; type Query = data::Query; -#[derive(InputObject, Clone, Default)] -pub(crate) struct EventFilter { - pub sender: Option, - pub transaction_digest: Option, - // Enhancement (post-MVP) - // after_checkpoint - // before_checkpoint - /// Events emitted by a particular module. An event is emitted by a - /// particular module if some function in the module is called by a - /// PTB and emits an event. - /// - /// Modules can be filtered by their package, or package::module. - pub emitting_module: Option, - - /// This field is used to specify the type of event emitted. - /// - /// Events can be filtered by their type's package, package::module, - /// or their fully qualified type name. - /// - /// Generic types can be queried by either the generic type name, e.g. - /// `0x2::coin::Coin`, or by the full type name, such as - /// `0x2::coin::Coin<0x2::sui::SUI>`. - pub event_type: Option, - // Enhancement (post-MVP) - // pub start_time - // pub end_time - - // Enhancement (post-MVP) - // pub any - // pub all - // pub not -} - #[Object] impl Event { /// The Move module containing some function that when called by @@ -158,64 +115,82 @@ impl Event { let cursor_viewed_at = page.validate_cursor_consistency()?; let checkpoint_viewed_at = cursor_viewed_at.unwrap_or(checkpoint_viewed_at); + // Construct tx and ev sequence number query with table-relevant filters, if they exist. The + // resulting query will look something like `SELECT tx_sequence_number, + // event_sequence_number FROM lookup_table WHERE ...`. If no filter is provided we don't + // need to use any lookup tables and can just query `events` table, as can be seen in the + // code below. + let query_constraint = match (filter.sender, &filter.emitting_module, &filter.event_type) { + (None, None, None) => None, + (Some(sender), None, None) => Some(select_sender(sender)), + (sender, None, Some(event_type)) => Some(select_event_type(event_type, sender)), + (sender, Some(module), None) => Some(select_emit_module(module, sender)), + (_, Some(_), Some(_)) => { + return Err(Error::Client( + "Filtering by both emitting module and event type is not supported".to_string(), + )) + } + }; + + use checkpoints::dsl; let (prev, next, results) = db .execute(move |conn| { - page.paginate_query::(conn, checkpoint_viewed_at, move || { - let mut query = events::dsl::events.into_boxed(); - - // Bound events by the provided `checkpoint_viewed_at`. From EXPLAIN - // ANALYZE, using the checkpoint sequence number directly instead of - // translating into a transaction sequence number bound is more efficient. - query = query.filter( - events::dsl::checkpoint_sequence_number.le(checkpoint_viewed_at as i64), - ); - - // The transactions table doesn't have an index on the senders column, so use - // `tx_senders`. - if let Some(sender) = &filter.sender { - query = query.filter( - events::dsl::tx_sequence_number.eq_any( - tx_senders::dsl::tx_senders - .select(tx_senders::dsl::tx_sequence_number) - .filter(tx_senders::dsl::sender.eq(sender.into_vec())), - ), - ) - } - - if let Some(digest) = &filter.transaction_digest { - // Since the event filter takes in a single tx_digest, we know that - // there will only be one corresponding transaction. We can use - // single_value() to tell the query planner that we expect only one - // instead of a range of values, which will subsequently speed up query - // execution time. - query = query.filter( - events::dsl::tx_sequence_number.nullable().eq( - transactions::dsl::transactions - .select(transactions::dsl::tx_sequence_number) - .filter( - transactions::dsl::transaction_digest.eq(digest.to_vec()), - ) - .single_value(), - ), - ) - } - - if let Some(module) = &filter.emitting_module { - query = module.apply(query, events::dsl::package, events::dsl::module); - } - - if let Some(type_) = &filter.event_type { - query = type_.apply( - query, - events::dsl::event_type, - events::dsl::event_type_package, - events::dsl::event_type_module, - events::dsl::event_type_name, - ); - } - - query - }) + let tx_hi: i64 = conn.first(move || { + dsl::checkpoints.select(dsl::network_total_transactions) + .filter(dsl::sequence_number.eq(checkpoint_viewed_at as i64)) + })?; + + let (prev, next, mut events): (bool, bool, Vec) = + if let Some(filter_query) = query_constraint { + let query = add_bounds(filter_query, &filter.transaction_digest, &page, tx_hi); + + let (prev, next, results) = + page.paginate_raw_query::(conn, checkpoint_viewed_at, query)?; + + let ev_lookups = results + .into_iter() + .map(|x| (x.tx, x.ev)) + .collect::>(); + + if ev_lookups.is_empty() { + return Ok::<_, diesel::result::Error>((prev, next, vec![])); + } + + // Unlike a multi-get on a single column which can be serviced by a query `IN + // (...)`, because events have a composite primary key, the query planner tends + // to perform a sequential scan when given a list of tuples to lookup. A query + // using `UNION ALL` allows us to leverage the index on the composite key. + let events = conn.results(move || { + // Diesel's DSL does not current support chained `UNION ALL`, so we have to turn + // to `RawQuery` here. + let query_string = ev_lookups.iter() + .map(|&(tx, ev)| { + format!("SELECT * FROM events WHERE tx_sequence_number = {} AND event_sequence_number = {}", tx, ev) + }) + .collect::>() + .join(" UNION ALL "); + + query!(query_string).into_boxed() + })?; + (prev, next, events) + } else { + // No filter is provided so we add bounds to the basic `SELECT * FROM + // events` query and call it a day. + let query = add_bounds(query!("SELECT * FROM events"), &filter.transaction_digest, &page, tx_hi); + let (prev, next, events_iter) = page.paginate_raw_query::(conn, checkpoint_viewed_at, query)?; + let events = events_iter.collect::>(); + (prev, next, events) + }; + + // UNION ALL does not guarantee order, so we need to sort the results. Whether + // `first` or `last, the result set is always sorted in ascending order. + events.sort_by(|a, b| { + a.tx_sequence_number.cmp(&b.tx_sequence_number) + .then_with(|| a.event_sequence_number.cmp(&b.event_sequence_number)) + }); + + + Ok::<_, diesel::result::Error>((prev, next, events)) }) .await?; @@ -256,7 +231,6 @@ impl Event { tx_sequence_number: stored_tx.tx_sequence_number, event_sequence_number: idx as i64, transaction_digest: stored_tx.transaction_digest.clone(), - checkpoint_sequence_number: stored_tx.checkpoint_sequence_number, #[cfg(feature = "postgres-feature")] senders: vec![Some(native_event.sender.to_vec())], package: native_event.package_id.to_vec(), @@ -264,9 +238,6 @@ impl Event { event_type: native_event .type_ .to_canonical_string(/* with_prefix */ true), - event_type_package: native_event.type_.address.to_vec(), - event_type_module: native_event.type_.module.to_string(), - event_type_name: native_event.type_.name.to_string(), bcs: native_event.contents.clone(), timestamp_ms: stored_tx.timestamp_ms, }; @@ -312,54 +283,3 @@ impl Event { }) } } - -impl Paginated for StoredEvent { - type Source = events::table; - - fn filter_ge(cursor: &Cursor, query: Query) -> Query { - use events::dsl::{event_sequence_number as event, tx_sequence_number as tx}; - query.filter( - tx.gt(cursor.tx as i64) - .or(tx.eq(cursor.tx as i64).and(event.ge(cursor.e as i64))), - ) - } - - fn filter_le(cursor: &Cursor, query: Query) -> Query { - use events::dsl::{event_sequence_number as event, tx_sequence_number as tx}; - query.filter( - tx.lt(cursor.tx as i64) - .or(tx.eq(cursor.tx as i64).and(event.le(cursor.e as i64))), - ) - } - - fn order(asc: bool, query: Query) -> Query { - use events::dsl; - if asc { - query - .order_by(dsl::tx_sequence_number.asc()) - .then_order_by(dsl::event_sequence_number.asc()) - } else { - query - .order_by(dsl::tx_sequence_number.desc()) - .then_order_by(dsl::event_sequence_number.desc()) - } - } -} - -impl Target for StoredEvent { - fn cursor(&self, checkpoint_viewed_at: u64) -> Cursor { - Cursor::new(EventKey { - tx: self.tx_sequence_number as u64, - e: self.event_sequence_number as u64, - checkpoint_viewed_at, - }) - } -} - -impl Checkpointed for Cursor { - fn checkpoint_viewed_at(&self) -> u64 { - self.checkpoint_viewed_at - } -} - -impl ScanLimited for Cursor {} diff --git a/crates/sui-graphql-rpc/src/types/query.rs b/crates/sui-graphql-rpc/src/types/query.rs index f403fbf8657b5..f6f4704b3009f 100644 --- a/crates/sui-graphql-rpc/src/types/query.rs +++ b/crates/sui-graphql-rpc/src/types/query.rs @@ -414,7 +414,9 @@ impl Query { .extend() } - /// The events that exist in the network. + /// Query events that are emitted in the network. + /// We currently do not support filtering by emitting module and event type + /// at the same time so if both are provided in one filter, the query will error. async fn events( &self, ctx: &Context<'_>, diff --git a/crates/sui-graphql-rpc/src/types/type_filter.rs b/crates/sui-graphql-rpc/src/types/type_filter.rs index f2028483989ff..16f9dd03181f1 100644 --- a/crates/sui-graphql-rpc/src/types/type_filter.rs +++ b/crates/sui-graphql-rpc/src/types/type_filter.rs @@ -2,18 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use super::{string_input::impl_string_input, sui_address::SuiAddress}; +use crate::filter; use crate::raw_query::RawQuery; -use crate::{ - data::{DieselBackend, Query}, - filter, -}; use async_graphql::*; -use diesel::{ - expression::{is_aggregate::No, ValidGrouping}, - query_builder::QueryFragment, - sql_types::{Binary, Text}, - AppearsOnTable, Expression, ExpressionMethods, QueryDsl, QuerySource, -}; use move_core_types::language_storage::StructTag; use std::{fmt, result::Result, str::FromStr}; use sui_types::{ @@ -67,89 +58,7 @@ pub(crate) enum Error { InvalidFormat(&'static str), } -/// Trait for a field that can be used in a query. -pub(crate) trait Field: - ExpressionMethods - + Expression - + QueryFragment - + AppearsOnTable - + ValidGrouping<(), IsAggregate = No> - + Send - + 'static -{ -} - -impl Field for T where - T: ExpressionMethods - + Expression - + QueryFragment - + AppearsOnTable - + ValidGrouping<(), IsAggregate = No> - + Send - + 'static -{ -} - impl TypeFilter { - /// Modify `query` to apply this filter to `type_field`, `package_field`, `module_field` - /// and `name_field`, where `type_field` stores the full type tag while the rest - /// store the package, module and name of the type tag respectively. The new query - /// after applying the filter is returned. - pub(crate) fn apply( - &self, - query: Query, - // Field storing the full type tag, including type parameters. - type_field: T, - package_field: P, - module_field: M, - // Name field only includes the name of the struct, like `Coin`, not including type parameters. - name_field: N, - ) -> Query - where - Query: QueryDsl, - T: Field, - P: Field, - M: Field, - N: Field, - QS: QuerySource, - { - match self { - TypeFilter::ByModule(ModuleFilter::ByPackage(p)) => { - query.filter(package_field.eq(p.into_vec())) - } - - TypeFilter::ByModule(ModuleFilter::ByModule(p, m)) => query - .filter(package_field.eq(p.into_vec())) - .filter(module_field.eq(m.clone())), - - // A type filter without type parameters is interpreted as either an exact match, or a - // match for all generic instantiations of the type so we check against only package, module - // and name fields. - TypeFilter::ByType(tag) if tag.type_params.is_empty() => { - let p = tag.address.to_vec(); - let m = tag.module.to_string(); - let n = tag.name.to_string(); - query - .filter(package_field.eq(p)) - .filter(module_field.eq(m)) - .filter(name_field.eq(n)) - } - - TypeFilter::ByType(tag) => { - let p = tag.address.to_vec(); - let m = tag.module.to_string(); - let n = tag.name.to_string(); - let exact = tag.to_canonical_string(/* with_prefix */ true); - // We check against the full type field for an exact match, including type parameters. - query - .filter(package_field.eq(p)) - .filter(module_field.eq(m)) - .filter(name_field.eq(n)) - .filter(type_field.eq(exact)) - } - } - } - /// Modify `query` to apply this filter to `field`, returning the new query. pub(crate) fn apply_raw( &self, @@ -295,28 +204,6 @@ impl FqNameFilter { } impl ModuleFilter { - /// Modify `query` to apply this filter, treating `package` as the column containing the package - /// address and `module` as the module containing the module name. - pub(crate) fn apply( - &self, - query: Query, - package: P, - module: M, - ) -> Query - where - Query: QueryDsl, - P: Field, - M: Field, - QS: QuerySource, - { - match self { - ModuleFilter::ByPackage(p) => query.filter(package.eq(p.into_vec())), - ModuleFilter::ByModule(p, m) => query - .filter(package.eq(p.into_vec())) - .filter(module.eq(m.clone())), - } - } - /// Try to create a filter whose results are the intersection of the results of the input /// filters (`self` and `other`). This may not be possible if the resulting filter is /// inconsistent (e.g. a filter that requires the module's package to be at two different diff --git a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap index fd04f186f34b6..ed3ec693362f4 100644 --- a/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap +++ b/crates/sui-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap @@ -1276,6 +1276,8 @@ input EventFilter { PTB and emits an event. Modules can be filtered by their package, or package::module. + We currently do not support filtering by emitting module and event type + at the same time so if both are provided in one filter, the query will error. """ emittingModule: String """ @@ -3316,7 +3318,9 @@ type Query { """ transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ - The events that exist in the network. + Query events that are emitted in the network. + We currently do not support filtering by emitting module and event type + at the same time so if both are provided in one filter, the query will error. """ events(first: Int, after: String, last: Int, before: String, filter: EventFilter): EventConnection! """ diff --git a/crates/sui-indexer/migrations/pg/2023-08-19-044020_events/up.sql b/crates/sui-indexer/migrations/pg/2023-08-19-044020_events/up.sql index dfbfa3ea14495..14aa6a098161f 100644 --- a/crates/sui-indexer/migrations/pg/2023-08-19-044020_events/up.sql +++ b/crates/sui-indexer/migrations/pg/2023-08-19-044020_events/up.sql @@ -4,7 +4,6 @@ CREATE TABLE events tx_sequence_number BIGINT NOT NULL, event_sequence_number BIGINT NOT NULL, transaction_digest bytea NOT NULL, - checkpoint_sequence_number bigint NOT NULL, -- array of SuiAddress in bytes. All signers of the transaction. senders bytea[] NOT NULL, -- bytes of the entry package ID. Notice that the package and module here @@ -15,11 +14,6 @@ CREATE TABLE events module text NOT NULL, -- StructTag in Display format, fully qualified including type parameters event_type text NOT NULL, - -- Components of the StructTag of the event type: package, module, - -- name (name of the struct, without type parameters) - event_type_package bytea NOT NULL, - event_type_module text NOT NULL, - event_type_name text NOT NULL, -- timestamp of the checkpoint when the event was emitted timestamp_ms BIGINT NOT NULL, -- bcs of the Event contents (Event.contents) @@ -30,5 +24,3 @@ CREATE TABLE events_partition_0 PARTITION OF events FOR VALUES FROM (0) TO (MAXV CREATE INDEX events_package ON events (package, tx_sequence_number, event_sequence_number); CREATE INDEX events_package_module ON events (package, module, tx_sequence_number, event_sequence_number); CREATE INDEX events_event_type ON events (event_type text_pattern_ops, tx_sequence_number, event_sequence_number); -CREATE INDEX events_type_package_module_name ON events (event_type_package, event_type_module, event_type_name, tx_sequence_number, event_sequence_number); -CREATE INDEX events_checkpoint_sequence_number ON events (checkpoint_sequence_number); diff --git a/crates/sui-indexer/src/models/events.rs b/crates/sui-indexer/src/models/events.rs index 455b22e1741fe..01f79c41d6ab2 100644 --- a/crates/sui-indexer/src/models/events.rs +++ b/crates/sui-indexer/src/models/events.rs @@ -31,9 +31,6 @@ pub struct StoredEvent { #[diesel(sql_type = diesel::sql_types::Binary)] pub transaction_digest: Vec, - #[diesel(sql_type = diesel::sql_types::BigInt)] - pub checkpoint_sequence_number: i64, - #[cfg(feature = "postgres-feature")] #[diesel(sql_type = diesel::sql_types::Array>)] pub senders: Vec>>, @@ -52,15 +49,6 @@ pub struct StoredEvent { #[diesel(sql_type = diesel::sql_types::Text)] pub event_type: String, - #[diesel(sql_type = diesel::sql_types::Binary)] - pub event_type_package: Vec, - - #[diesel(sql_type = diesel::sql_types::Text)] - pub event_type_module: String, - - #[diesel(sql_type = diesel::sql_types::Text)] - pub event_type_name: String, - #[diesel(sql_type = diesel::sql_types::BigInt)] pub timestamp_ms: i64, @@ -81,7 +69,6 @@ impl From for StoredEvent { tx_sequence_number: event.tx_sequence_number as i64, event_sequence_number: event.event_sequence_number as i64, transaction_digest: event.transaction_digest.into_inner().to_vec(), - checkpoint_sequence_number: event.checkpoint_sequence_number as i64, #[cfg(feature = "postgres-feature")] senders: event .senders @@ -94,9 +81,6 @@ impl From for StoredEvent { package: event.package.to_vec(), module: event.module.clone(), event_type: event.event_type.clone(), - event_type_package: event.event_type_package.to_vec(), - event_type_module: event.event_type_module.clone(), - event_type_name: event.event_type_name.clone(), bcs: event.bcs.clone(), timestamp_ms: event.timestamp_ms as i64, } diff --git a/crates/sui-indexer/src/schema/pg.rs b/crates/sui-indexer/src/schema/pg.rs index 2515c98d34c06..c1720c94b37a1 100644 --- a/crates/sui-indexer/src/schema/pg.rs +++ b/crates/sui-indexer/src/schema/pg.rs @@ -137,14 +137,10 @@ diesel::table! { tx_sequence_number -> Int8, event_sequence_number -> Int8, transaction_digest -> Bytea, - checkpoint_sequence_number -> Int8, senders -> Array>, package -> Bytea, module -> Text, event_type -> Text, - event_type_package -> Bytea, - event_type_module -> Text, - event_type_name -> Text, timestamp_ms -> Int8, bcs -> Bytea, } @@ -155,14 +151,10 @@ diesel::table! { tx_sequence_number -> Int8, event_sequence_number -> Int8, transaction_digest -> Bytea, - checkpoint_sequence_number -> Int8, senders -> Array>, package -> Bytea, module -> Text, event_type -> Text, - event_type_package -> Bytea, - event_type_module -> Text, - event_type_name -> Text, timestamp_ms -> Int8, bcs -> Bytea, } diff --git a/crates/sui-indexer/tests/ingestion_tests.rs b/crates/sui-indexer/tests/ingestion_tests.rs index af67a061bbde3..8eee88f8bd448 100644 --- a/crates/sui-indexer/tests/ingestion_tests.rs +++ b/crates/sui-indexer/tests/ingestion_tests.rs @@ -5,7 +5,6 @@ mod ingestion_tests { use diesel::ExpressionMethods; use diesel::{QueryDsl, RunQueryDsl}; - use move_core_types::language_storage::StructTag; use simulacrum::Simulacrum; use std::net::SocketAddr; use std::path::PathBuf; @@ -14,18 +13,14 @@ mod ingestion_tests { use sui_indexer::db::get_pool_connection; use sui_indexer::errors::Context; use sui_indexer::errors::IndexerError; - use sui_indexer::models::{ - events::StoredEvent, objects::StoredObject, transactions::StoredTransaction, - }; - use sui_indexer::schema::{events, objects, transactions}; + use sui_indexer::models::{objects::StoredObject, transactions::StoredTransaction}; + use sui_indexer::schema::{objects, transactions}; use sui_indexer::store::{indexer_store::IndexerStore, PgIndexerStore}; use sui_indexer::test_utils::{start_test_indexer, ReaderWriterConfig}; use sui_types::base_types::SuiAddress; use sui_types::effects::TransactionEffectsAPI; use sui_types::gas_coin::GasCoin; - use sui_types::{ - Identifier, SUI_FRAMEWORK_PACKAGE_ID, SUI_SYSTEM_ADDRESS, SUI_SYSTEM_PACKAGE_ID, - }; + use sui_types::SUI_FRAMEWORK_PACKAGE_ID; use tempfile::tempdir; use tokio::task::JoinHandle; @@ -93,24 +88,6 @@ mod ingestion_tests { Ok(()) } - /// Wait for the indexer to catch up to the given epoch id. - async fn wait_for_epoch( - pg_store: &PgIndexerStore, - epoch: u64, - ) -> Result<(), IndexerError> { - tokio::time::timeout(Duration::from_secs(10), async { - while { - let cp_opt = pg_store.get_latest_epoch_id().unwrap(); - cp_opt.is_none() || (cp_opt.unwrap() < epoch) - } { - tokio::time::sleep(Duration::from_secs(1)).await; - } - }) - .await - .expect("Timeout waiting for indexer to catchup to epoch"); - Ok(()) - } - #[tokio::test] pub async fn test_transaction_table() -> Result<(), IndexerError> { let mut sim = Simulacrum::new(); @@ -156,46 +133,6 @@ mod ingestion_tests { Ok(()) } - #[tokio::test] - pub async fn test_event_type() -> Result<(), IndexerError> { - let mut sim = Simulacrum::new(); - let data_ingestion_path = tempdir().unwrap().into_path(); - sim.set_data_ingestion_path(data_ingestion_path.clone()); - - // Advance the epoch to generate some events. - sim.advance_epoch(false); - - let (_, pg_store, _) = set_up(Arc::new(sim), data_ingestion_path).await; - - // Wait for the epoch to change so we can get some events. - wait_for_epoch(&pg_store, 1).await?; - - // Read the event from the database directly. - let db_event: StoredEvent = read_only_blocking!(&pg_store.blocking_cp(), |conn| { - events::table - .filter(events::event_type_name.eq("SystemEpochInfoEvent")) - .first::(conn) - }) - .context("Failed reading SystemEpochInfoEvent from PostgresDB")?; - - let event_type_tag = StructTag { - address: SUI_SYSTEM_ADDRESS, - module: Identifier::new("sui_system_state_inner").unwrap(), - name: Identifier::new("SystemEpochInfoEvent").unwrap(), - type_params: vec![], - }; - - // Check that the different components of the event type were stored correctly. - assert_eq!( - db_event.event_type, - event_type_tag.to_canonical_string(true) - ); - assert_eq!(db_event.event_type_package, SUI_SYSTEM_PACKAGE_ID.to_vec()); - assert_eq!(db_event.event_type_module, "sui_system_state_inner"); - assert_eq!(db_event.event_type_name, "SystemEpochInfoEvent"); - Ok(()) - } - #[tokio::test] pub async fn test_object_type() -> Result<(), IndexerError> { let mut sim = Simulacrum::new(); From 62c7d64c490e7f5e08b18e12235444e1d8478e32 Mon Sep 17 00:00:00 2001 From: Eugene Boguslavsky Date: Fri, 30 Aug 2024 15:08:33 +0000 Subject: [PATCH 225/232] Sui v1.32.1 Version Bump --- Cargo.lock | 116 +++++++++++++------------- Cargo.toml | 2 +- crates/sui-open-rpc/spec/openrpc.json | 2 +- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9cec723dbb5e7..40c61f3cb041f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1854,7 +1854,7 @@ checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" [[package]] name = "bin-version" -version = "1.32.0" +version = "1.32.1" dependencies = [ "const-str", "git-version", @@ -12434,7 +12434,7 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" [[package]] name = "sui" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anemo", "anyhow", @@ -12505,7 +12505,7 @@ dependencies = [ "sui-package-management", "sui-protocol-config", "sui-replay", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-simulator", "sui-source-validation", "sui-swarm", @@ -12649,7 +12649,7 @@ dependencies = [ [[package]] name = "sui-analytics-indexer" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "arrow", @@ -12701,7 +12701,7 @@ dependencies = [ [[package]] name = "sui-analytics-indexer-derive" -version = "1.32.0" +version = "1.32.1" dependencies = [ "proc-macro2 1.0.78", "quote 1.0.35", @@ -12710,7 +12710,7 @@ dependencies = [ [[package]] name = "sui-archival" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "byteorder", @@ -12818,7 +12818,7 @@ dependencies = [ "sui-macros", "sui-network", "sui-protocol-config", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-simulator", "sui-storage", "sui-surfer", @@ -12836,7 +12836,7 @@ dependencies = [ [[package]] name = "sui-bridge" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "arc-swap", @@ -12870,7 +12870,7 @@ dependencies = [ "sui-json-rpc-api", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-test-transaction-builder", "sui-types", "tap", @@ -12885,7 +12885,7 @@ dependencies = [ [[package]] name = "sui-bridge-cli" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "clap", @@ -12902,7 +12902,7 @@ dependencies = [ "sui-config", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-types", "telemetry-subscribers", "tokio", @@ -12933,7 +12933,7 @@ dependencies = [ "sui-data-ingestion-core", "sui-indexer-builder", "sui-json-rpc-types", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-test-transaction-builder", "sui-types", "tap", @@ -12945,7 +12945,7 @@ dependencies = [ [[package]] name = "sui-cluster-test" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "async-trait", @@ -12969,7 +12969,7 @@ dependencies = [ "sui-json", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-swarm", "sui-swarm-config", "sui-test-transaction-builder", @@ -13142,7 +13142,7 @@ dependencies = [ [[package]] name = "sui-data-ingestion" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "async-trait", @@ -13204,7 +13204,7 @@ dependencies = [ [[package]] name = "sui-e2e-tests" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "assert_cmd", @@ -13248,7 +13248,7 @@ dependencies = [ "sui-node", "sui-protocol-config", "sui-rest-api", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-simulator", "sui-storage", "sui-swarm", @@ -13321,7 +13321,7 @@ dependencies = [ [[package]] name = "sui-faucet" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "async-recursion", @@ -13340,7 +13340,7 @@ dependencies = [ "sui-config", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-types", "tap", "telemetry-subscribers", @@ -13377,7 +13377,7 @@ dependencies = [ [[package]] name = "sui-framework-snapshot" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "bcs", @@ -13440,7 +13440,7 @@ dependencies = [ [[package]] name = "sui-graphql-config" -version = "1.32.0" +version = "1.32.1" dependencies = [ "quote 1.0.35", "syn 1.0.107", @@ -13517,7 +13517,7 @@ dependencies = [ "sui-package-resolver", "sui-protocol-config", "sui-rest-api", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-swarm-config", "sui-test-transaction-builder", "sui-types", @@ -13557,7 +13557,7 @@ dependencies = [ [[package]] name = "sui-indexer" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "async-trait", @@ -13600,7 +13600,7 @@ dependencies = [ "sui-package-resolver", "sui-protocol-config", "sui-rest-api", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-test-transaction-builder", "sui-transaction-builder", "sui-types", @@ -13750,7 +13750,7 @@ dependencies = [ "sui-open-rpc", "sui-open-rpc-macros", "sui-protocol-config", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-simulator", "sui-swarm-config", "sui-test-transaction-builder", @@ -13811,7 +13811,7 @@ dependencies = [ [[package]] name = "sui-light-client" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "async-trait", @@ -13828,7 +13828,7 @@ dependencies = [ "sui-json-rpc-types", "sui-package-resolver", "sui-rest-api", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-types", "tokio", ] @@ -13845,7 +13845,7 @@ dependencies = [ [[package]] name = "sui-metric-checker" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "backoff", @@ -13866,7 +13866,7 @@ dependencies = [ [[package]] name = "sui-move" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "assert_cmd", @@ -13908,7 +13908,7 @@ dependencies = [ [[package]] name = "sui-move-build" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "datatest-stable", @@ -13932,7 +13932,7 @@ dependencies = [ [[package]] name = "sui-move-lsp" -version = "1.32.0" +version = "1.32.1" dependencies = [ "bin-version", "clap", @@ -14062,7 +14062,7 @@ dependencies = [ [[package]] name = "sui-node" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anemo", "anemo-tower", @@ -14115,7 +14115,7 @@ dependencies = [ [[package]] name = "sui-open-rpc" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "bcs", @@ -14151,7 +14151,7 @@ dependencies = [ [[package]] name = "sui-oracle" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "bcs", @@ -14171,7 +14171,7 @@ dependencies = [ "sui-json-rpc-types", "sui-keys", "sui-move-build", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-types", "tap", "telemetry-subscribers", @@ -14181,7 +14181,7 @@ dependencies = [ [[package]] name = "sui-package-dump" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "bcs", @@ -14198,14 +14198,14 @@ dependencies = [ [[package]] name = "sui-package-management" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "move-core-types", "move-package", "move-symbol-pool", "sui-json-rpc-types", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-types", "thiserror", "tracing", @@ -14348,7 +14348,7 @@ dependencies = [ "sui-json-rpc-api", "sui-json-rpc-types", "sui-protocol-config", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-storage", "sui-transaction-checks", "sui-types", @@ -14393,7 +14393,7 @@ dependencies = [ [[package]] name = "sui-rosetta" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "async-trait", @@ -14421,7 +14421,7 @@ dependencies = [ "sui-keys", "sui-move-build", "sui-node", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-swarm-config", "sui-types", "telemetry-subscribers", @@ -14435,7 +14435,7 @@ dependencies = [ [[package]] name = "sui-rpc-loadgen" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "async-trait", @@ -14453,7 +14453,7 @@ dependencies = [ "sui-json-rpc", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-types", "telemetry-subscribers", "test-cluster", @@ -14484,7 +14484,7 @@ dependencies = [ [[package]] name = "sui-sdk" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "async-recursion", @@ -14520,7 +14520,7 @@ dependencies = [ [[package]] name = "sui-security-watchdog" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "arrow-array", @@ -14567,7 +14567,7 @@ dependencies = [ [[package]] name = "sui-single-node-benchmark" -version = "1.32.0" +version = "1.32.1" dependencies = [ "async-trait", "bcs", @@ -14630,7 +14630,7 @@ dependencies = [ [[package]] name = "sui-source-validation" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "colored", @@ -14648,7 +14648,7 @@ dependencies = [ "sui-json-rpc-types", "sui-move-build", "sui-package-management", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-test-transaction-builder", "sui-types", "tar", @@ -14684,7 +14684,7 @@ dependencies = [ "sui-json-rpc-types", "sui-move", "sui-move-build", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-source-validation", "telemetry-subscribers", "tempfile", @@ -14755,7 +14755,7 @@ dependencies = [ [[package]] name = "sui-surfer" -version = "1.32.0" +version = "1.32.1" dependencies = [ "async-trait", "bcs", @@ -14853,13 +14853,13 @@ dependencies = [ "shared-crypto", "sui-genesis-builder", "sui-move-build", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-types", ] [[package]] name = "sui-test-validator" -version = "1.32.0" +version = "1.32.1" [[package]] name = "sui-tls" @@ -14884,7 +14884,7 @@ dependencies = [ [[package]] name = "sui-tool" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anemo", "anemo-cli", @@ -14918,7 +14918,7 @@ dependencies = [ "sui-package-dump", "sui-protocol-config", "sui-replay", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-snapshot", "sui-storage", "sui-types", @@ -15170,7 +15170,7 @@ dependencies = [ [[package]] name = "suins-indexer" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "async-trait", @@ -15204,7 +15204,7 @@ dependencies = [ [[package]] name = "suiop-cli" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "axum 0.7.5", @@ -15549,7 +15549,7 @@ dependencies = [ "sui-macros", "sui-node", "sui-protocol-config", - "sui-sdk 1.32.0", + "sui-sdk 1.32.1", "sui-simulator", "sui-swarm", "sui-swarm-config", @@ -17372,7 +17372,7 @@ dependencies = [ [[package]] name = "x" -version = "1.32.0" +version = "1.32.1" dependencies = [ "anyhow", "camino", diff --git a/Cargo.toml b/Cargo.toml index 2d175748d0d4e..f5f6e98767582 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -201,7 +201,7 @@ members = [ [workspace.package] # This version string will be inherited by sui-core, sui-faucet, sui-node, sui-tools, sui-sdk, sui-move-build, and sui crates. -version = "1.32.0" +version = "1.32.1" [profile.release] # debug = 1 means line charts only, which is minimum needed for good stack traces diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index f01df691e4369..85a4c2c11a5ba 100644 --- a/crates/sui-open-rpc/spec/openrpc.json +++ b/crates/sui-open-rpc/spec/openrpc.json @@ -12,7 +12,7 @@ "name": "Apache-2.0", "url": "https://raw.githubusercontent.com/MystenLabs/sui/main/LICENSE" }, - "version": "1.32.0" + "version": "1.32.1" }, "methods": [ { From 624300d58deda9aea058f2dac58bdfab1a87e13f Mon Sep 17 00:00:00 2001 From: mwtian <81660174+mwtian@users.noreply.github.com> Date: Fri, 30 Aug 2024 14:39:32 -0700 Subject: [PATCH 226/232] Cherrypick additional rocksdb metrics (#19172) ## Description #19112: this emits more metrics from rocksdb, to help with investigations into external fullnode write stalls. ## Test plan CI --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: ## Description Describe the changes or additions included in this PR. ## Test plan How did you test the new or updated feature? --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/typed-store/src/metrics.rs | 34 ++++++++++++++++++++++++++- crates/typed-store/src/rocks/mod.rs | 36 +++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/crates/typed-store/src/metrics.rs b/crates/typed-store/src/metrics.rs index 904ad5a31cf67..f7049d8d63768 100644 --- a/crates/typed-store/src/metrics.rs +++ b/crates/typed-store/src/metrics.rs @@ -77,6 +77,7 @@ impl SamplingInterval { pub struct ColumnFamilyMetrics { pub rocksdb_total_sst_files_size: IntGaugeVec, pub rocksdb_total_blob_files_size: IntGaugeVec, + pub rocksdb_current_size_active_mem_tables: IntGaugeVec, pub rocksdb_size_all_mem_tables: IntGaugeVec, pub rocksdb_num_snapshots: IntGaugeVec, pub rocksdb_oldest_snapshot_time: IntGaugeVec, @@ -86,13 +87,16 @@ pub struct ColumnFamilyMetrics { pub rocksdb_block_cache_usage: IntGaugeVec, pub rocksdb_block_cache_pinned_usage: IntGaugeVec, pub rocksdb_estimate_table_readers_mem: IntGaugeVec, + pub rocksdb_num_immutable_mem_tables: IntGaugeVec, pub rocksdb_mem_table_flush_pending: IntGaugeVec, pub rocksdb_compaction_pending: IntGaugeVec, + pub rocksdb_estimate_pending_compaction_bytes: IntGaugeVec, pub rocksdb_num_running_compactions: IntGaugeVec, pub rocksdb_num_running_flushes: IntGaugeVec, pub rocksdb_estimate_oldest_key_time: IntGaugeVec, pub rocksdb_background_errors: IntGaugeVec, pub rocksdb_estimated_num_keys: IntGaugeVec, + pub rocksdb_base_level: IntGaugeVec, } impl ColumnFamilyMetrics { @@ -112,6 +116,13 @@ impl ColumnFamilyMetrics { registry, ) .unwrap(), + rocksdb_current_size_active_mem_tables: register_int_gauge_vec_with_registry!( + "rocksdb_current_size_active_mem_tables", + "The current approximate size of active memtable (bytes).", + &["cf_name"], + registry, + ) + .unwrap(), rocksdb_size_all_mem_tables: register_int_gauge_vec_with_registry!( "rocksdb_size_all_mem_tables", "The memory size occupied by the column family's in-memory buffer", @@ -177,6 +188,13 @@ impl ColumnFamilyMetrics { registry, ) .unwrap(), + rocksdb_num_immutable_mem_tables: register_int_gauge_vec_with_registry!( + "rocksdb_num_immutable_mem_tables", + "The number of immutable memtables that have not yet been flushed.", + &["cf_name"], + registry, + ) + .unwrap(), rocksdb_mem_table_flush_pending: register_int_gauge_vec_with_registry!( "rocksdb_mem_table_flush_pending", "A 1 or 0 flag indicating whether a memtable flush is pending. @@ -198,6 +216,14 @@ impl ColumnFamilyMetrics { registry, ) .unwrap(), + rocksdb_estimate_pending_compaction_bytes: register_int_gauge_vec_with_registry!( + "rocksdb_estimate_pending_compaction_bytes", + "Estimated total number of bytes compaction needs to rewrite to get all levels down + to under target size. Not valid for other compactions than level-based.", + &["cf_name"], + registry, + ) + .unwrap(), rocksdb_num_running_compactions: register_int_gauge_vec_with_registry!( "rocksdb_num_running_compactions", "The number of compactions that are currently running for the column family.", @@ -234,7 +260,13 @@ impl ColumnFamilyMetrics { registry, ) .unwrap(), - + rocksdb_base_level: register_int_gauge_vec_with_registry!( + "rocksdb_base_level", + "The number of level to which L0 data will be compacted.", + &["cf_name"], + registry, + ) + .unwrap(), } } } diff --git a/crates/typed-store/src/rocks/mod.rs b/crates/typed-store/src/rocks/mod.rs index ca6505b9a448e..6bf550f4aa131 100644 --- a/crates/typed-store/src/rocks/mod.rs +++ b/crates/typed-store/src/rocks/mod.rs @@ -704,7 +704,7 @@ impl MetricConf { } } } -const CF_METRICS_REPORT_PERIOD_MILLIS: u64 = 1000; +const CF_METRICS_REPORT_PERIOD_SECS: u64 = 30; const METRICS_ERROR: i64 = -1; /// An interface to a rocksDB database, keyed by a columnfamily @@ -740,7 +740,7 @@ impl DBMap { if !is_deprecated { tokio::task::spawn(async move { let mut interval = - tokio::time::interval(Duration::from_millis(CF_METRICS_REPORT_PERIOD_MILLIS)); + tokio::time::interval(Duration::from_secs(CF_METRICS_REPORT_PERIOD_SECS)); loop { tokio::select! { _ = interval.tick() => { @@ -983,6 +983,14 @@ impl DBMap { Self::get_int_property(rocksdb, &cf, ROCKSDB_PROPERTY_TOTAL_BLOB_FILES_SIZE) .unwrap_or(METRICS_ERROR), ); + db_metrics + .cf_metrics + .rocksdb_current_size_active_mem_tables + .with_label_values(&[cf_name]) + .set( + Self::get_int_property(rocksdb, &cf, properties::CUR_SIZE_ACTIVE_MEM_TABLE) + .unwrap_or(METRICS_ERROR), + ); db_metrics .cf_metrics .rocksdb_size_all_mem_tables @@ -1063,6 +1071,14 @@ impl DBMap { Self::get_int_property(rocksdb, &cf, properties::ESTIMATE_NUM_KEYS) .unwrap_or(METRICS_ERROR), ); + db_metrics + .cf_metrics + .rocksdb_num_immutable_mem_tables + .with_label_values(&[cf_name]) + .set( + Self::get_int_property(rocksdb, &cf, properties::NUM_IMMUTABLE_MEM_TABLE) + .unwrap_or(METRICS_ERROR), + ); db_metrics .cf_metrics .rocksdb_mem_table_flush_pending @@ -1079,6 +1095,14 @@ impl DBMap { Self::get_int_property(rocksdb, &cf, properties::COMPACTION_PENDING) .unwrap_or(METRICS_ERROR), ); + db_metrics + .cf_metrics + .rocksdb_estimate_pending_compaction_bytes + .with_label_values(&[cf_name]) + .set( + Self::get_int_property(rocksdb, &cf, properties::ESTIMATE_PENDING_COMPACTION_BYTES) + .unwrap_or(METRICS_ERROR), + ); db_metrics .cf_metrics .rocksdb_num_running_compactions @@ -1111,6 +1135,14 @@ impl DBMap { Self::get_int_property(rocksdb, &cf, properties::BACKGROUND_ERRORS) .unwrap_or(METRICS_ERROR), ); + db_metrics + .cf_metrics + .rocksdb_base_level + .with_label_values(&[cf_name]) + .set( + Self::get_int_property(rocksdb, &cf, properties::BASE_LEVEL) + .unwrap_or(METRICS_ERROR), + ); } pub fn transaction(&self) -> Result, TypedStoreError> { From 32e55c681cacc4a324ea270eb7f251ac06d7a542 Mon Sep 17 00:00:00 2001 From: Eugene Boguslavsky Date: Tue, 3 Sep 2024 23:12:26 +0000 Subject: [PATCH 227/232] Sui v1.32.2 Version Bump --- Cargo.lock | 116 +++++++++++++------------- Cargo.toml | 2 +- crates/sui-open-rpc/spec/openrpc.json | 2 +- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 40c61f3cb041f..d47f271491979 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1854,7 +1854,7 @@ checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" [[package]] name = "bin-version" -version = "1.32.1" +version = "1.32.2" dependencies = [ "const-str", "git-version", @@ -12434,7 +12434,7 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" [[package]] name = "sui" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anemo", "anyhow", @@ -12505,7 +12505,7 @@ dependencies = [ "sui-package-management", "sui-protocol-config", "sui-replay", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-simulator", "sui-source-validation", "sui-swarm", @@ -12649,7 +12649,7 @@ dependencies = [ [[package]] name = "sui-analytics-indexer" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "arrow", @@ -12701,7 +12701,7 @@ dependencies = [ [[package]] name = "sui-analytics-indexer-derive" -version = "1.32.1" +version = "1.32.2" dependencies = [ "proc-macro2 1.0.78", "quote 1.0.35", @@ -12710,7 +12710,7 @@ dependencies = [ [[package]] name = "sui-archival" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "byteorder", @@ -12818,7 +12818,7 @@ dependencies = [ "sui-macros", "sui-network", "sui-protocol-config", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-simulator", "sui-storage", "sui-surfer", @@ -12836,7 +12836,7 @@ dependencies = [ [[package]] name = "sui-bridge" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "arc-swap", @@ -12870,7 +12870,7 @@ dependencies = [ "sui-json-rpc-api", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-test-transaction-builder", "sui-types", "tap", @@ -12885,7 +12885,7 @@ dependencies = [ [[package]] name = "sui-bridge-cli" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "clap", @@ -12902,7 +12902,7 @@ dependencies = [ "sui-config", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-types", "telemetry-subscribers", "tokio", @@ -12933,7 +12933,7 @@ dependencies = [ "sui-data-ingestion-core", "sui-indexer-builder", "sui-json-rpc-types", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-test-transaction-builder", "sui-types", "tap", @@ -12945,7 +12945,7 @@ dependencies = [ [[package]] name = "sui-cluster-test" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "async-trait", @@ -12969,7 +12969,7 @@ dependencies = [ "sui-json", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-swarm", "sui-swarm-config", "sui-test-transaction-builder", @@ -13142,7 +13142,7 @@ dependencies = [ [[package]] name = "sui-data-ingestion" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "async-trait", @@ -13204,7 +13204,7 @@ dependencies = [ [[package]] name = "sui-e2e-tests" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "assert_cmd", @@ -13248,7 +13248,7 @@ dependencies = [ "sui-node", "sui-protocol-config", "sui-rest-api", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-simulator", "sui-storage", "sui-swarm", @@ -13321,7 +13321,7 @@ dependencies = [ [[package]] name = "sui-faucet" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "async-recursion", @@ -13340,7 +13340,7 @@ dependencies = [ "sui-config", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-types", "tap", "telemetry-subscribers", @@ -13377,7 +13377,7 @@ dependencies = [ [[package]] name = "sui-framework-snapshot" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "bcs", @@ -13440,7 +13440,7 @@ dependencies = [ [[package]] name = "sui-graphql-config" -version = "1.32.1" +version = "1.32.2" dependencies = [ "quote 1.0.35", "syn 1.0.107", @@ -13517,7 +13517,7 @@ dependencies = [ "sui-package-resolver", "sui-protocol-config", "sui-rest-api", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-swarm-config", "sui-test-transaction-builder", "sui-types", @@ -13557,7 +13557,7 @@ dependencies = [ [[package]] name = "sui-indexer" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "async-trait", @@ -13600,7 +13600,7 @@ dependencies = [ "sui-package-resolver", "sui-protocol-config", "sui-rest-api", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-test-transaction-builder", "sui-transaction-builder", "sui-types", @@ -13750,7 +13750,7 @@ dependencies = [ "sui-open-rpc", "sui-open-rpc-macros", "sui-protocol-config", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-simulator", "sui-swarm-config", "sui-test-transaction-builder", @@ -13811,7 +13811,7 @@ dependencies = [ [[package]] name = "sui-light-client" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "async-trait", @@ -13828,7 +13828,7 @@ dependencies = [ "sui-json-rpc-types", "sui-package-resolver", "sui-rest-api", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-types", "tokio", ] @@ -13845,7 +13845,7 @@ dependencies = [ [[package]] name = "sui-metric-checker" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "backoff", @@ -13866,7 +13866,7 @@ dependencies = [ [[package]] name = "sui-move" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "assert_cmd", @@ -13908,7 +13908,7 @@ dependencies = [ [[package]] name = "sui-move-build" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "datatest-stable", @@ -13932,7 +13932,7 @@ dependencies = [ [[package]] name = "sui-move-lsp" -version = "1.32.1" +version = "1.32.2" dependencies = [ "bin-version", "clap", @@ -14062,7 +14062,7 @@ dependencies = [ [[package]] name = "sui-node" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anemo", "anemo-tower", @@ -14115,7 +14115,7 @@ dependencies = [ [[package]] name = "sui-open-rpc" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "bcs", @@ -14151,7 +14151,7 @@ dependencies = [ [[package]] name = "sui-oracle" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "bcs", @@ -14171,7 +14171,7 @@ dependencies = [ "sui-json-rpc-types", "sui-keys", "sui-move-build", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-types", "tap", "telemetry-subscribers", @@ -14181,7 +14181,7 @@ dependencies = [ [[package]] name = "sui-package-dump" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "bcs", @@ -14198,14 +14198,14 @@ dependencies = [ [[package]] name = "sui-package-management" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "move-core-types", "move-package", "move-symbol-pool", "sui-json-rpc-types", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-types", "thiserror", "tracing", @@ -14348,7 +14348,7 @@ dependencies = [ "sui-json-rpc-api", "sui-json-rpc-types", "sui-protocol-config", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-storage", "sui-transaction-checks", "sui-types", @@ -14393,7 +14393,7 @@ dependencies = [ [[package]] name = "sui-rosetta" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "async-trait", @@ -14421,7 +14421,7 @@ dependencies = [ "sui-keys", "sui-move-build", "sui-node", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-swarm-config", "sui-types", "telemetry-subscribers", @@ -14435,7 +14435,7 @@ dependencies = [ [[package]] name = "sui-rpc-loadgen" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "async-trait", @@ -14453,7 +14453,7 @@ dependencies = [ "sui-json-rpc", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-types", "telemetry-subscribers", "test-cluster", @@ -14484,7 +14484,7 @@ dependencies = [ [[package]] name = "sui-sdk" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "async-recursion", @@ -14520,7 +14520,7 @@ dependencies = [ [[package]] name = "sui-security-watchdog" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "arrow-array", @@ -14567,7 +14567,7 @@ dependencies = [ [[package]] name = "sui-single-node-benchmark" -version = "1.32.1" +version = "1.32.2" dependencies = [ "async-trait", "bcs", @@ -14630,7 +14630,7 @@ dependencies = [ [[package]] name = "sui-source-validation" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "colored", @@ -14648,7 +14648,7 @@ dependencies = [ "sui-json-rpc-types", "sui-move-build", "sui-package-management", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-test-transaction-builder", "sui-types", "tar", @@ -14684,7 +14684,7 @@ dependencies = [ "sui-json-rpc-types", "sui-move", "sui-move-build", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-source-validation", "telemetry-subscribers", "tempfile", @@ -14755,7 +14755,7 @@ dependencies = [ [[package]] name = "sui-surfer" -version = "1.32.1" +version = "1.32.2" dependencies = [ "async-trait", "bcs", @@ -14853,13 +14853,13 @@ dependencies = [ "shared-crypto", "sui-genesis-builder", "sui-move-build", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-types", ] [[package]] name = "sui-test-validator" -version = "1.32.1" +version = "1.32.2" [[package]] name = "sui-tls" @@ -14884,7 +14884,7 @@ dependencies = [ [[package]] name = "sui-tool" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anemo", "anemo-cli", @@ -14918,7 +14918,7 @@ dependencies = [ "sui-package-dump", "sui-protocol-config", "sui-replay", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-snapshot", "sui-storage", "sui-types", @@ -15170,7 +15170,7 @@ dependencies = [ [[package]] name = "suins-indexer" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "async-trait", @@ -15204,7 +15204,7 @@ dependencies = [ [[package]] name = "suiop-cli" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "axum 0.7.5", @@ -15549,7 +15549,7 @@ dependencies = [ "sui-macros", "sui-node", "sui-protocol-config", - "sui-sdk 1.32.1", + "sui-sdk 1.32.2", "sui-simulator", "sui-swarm", "sui-swarm-config", @@ -17372,7 +17372,7 @@ dependencies = [ [[package]] name = "x" -version = "1.32.1" +version = "1.32.2" dependencies = [ "anyhow", "camino", diff --git a/Cargo.toml b/Cargo.toml index f5f6e98767582..6c9976a5a554d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -201,7 +201,7 @@ members = [ [workspace.package] # This version string will be inherited by sui-core, sui-faucet, sui-node, sui-tools, sui-sdk, sui-move-build, and sui crates. -version = "1.32.1" +version = "1.32.2" [profile.release] # debug = 1 means line charts only, which is minimum needed for good stack traces diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index 85a4c2c11a5ba..fae22c2e71eea 100644 --- a/crates/sui-open-rpc/spec/openrpc.json +++ b/crates/sui-open-rpc/spec/openrpc.json @@ -12,7 +12,7 @@ "name": "Apache-2.0", "url": "https://raw.githubusercontent.com/MystenLabs/sui/main/LICENSE" }, - "version": "1.32.1" + "version": "1.32.2" }, "methods": [ { From a5eab1a75fa883f003a931bd71ef02987ffff7e9 Mon Sep 17 00:00:00 2001 From: Lu Zhang <8418040+longbowlu@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:31:22 -0700 Subject: [PATCH 228/232] [bridge] [cheerypick to 1.32] enable bridge on mainnet (#19199) ## Description enable bridge creation on mainnet by setting `bridge` is true. Before this change, the value true for testnet and devnet, but not mainnet. So this only applies to mainnet. ## Test plan it creates a new version 56 and enables bridge feature flag on mainnet. This change creates * [sui_protocol_config__test__Mainnet_version_56.snap](https://github.com/MystenLabs/sui/blob/4b2e1ab739cee34e4075717b52766eb3a07946df/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_56.snap) and * [sui_protocol_config__test__Testnet_version_56.snap](https://github.com/MystenLabs/sui/blob/4b2e1ab739cee34e4075717b52766eb3a07946df/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_56.snap) and it does NOT change * [sui_protocol_config__test__Testnet_version_55.snap](https://github.com/MystenLabs/sui/blob/4b2e1ab739cee34e4075717b52766eb3a07946df/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_55.snap) and [sui_protocol_config__test__Mainnet_version_55.snap](https://github.com/MystenLabs/sui/blob/4b2e1ab739cee34e4075717b52766eb3a07946df/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_55.snap) I did the following checks: * [sui_protocol_config__test__Mainnet_version_56.snap](https://github.com/MystenLabs/sui/blob/4b2e1ab739cee34e4075717b52766eb3a07946df/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_56.snap) is different from existing [sui_protocol_config__test__Mainnet_version_55.snap](https://github.com/MystenLabs/sui/blob/4b2e1ab739cee34e4075717b52766eb3a07946df/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_55.snap) for bridge: [diff](https://www.diffchecker.com/50VcWjj8/) * [sui_protocol_config__test__Testnet_version_56.snap](https://github.com/MystenLabs/sui/blob/4b2e1ab739cee34e4075717b52766eb3a07946df/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_56.snap) and [sui_protocol_config__test__Testnet_version_55.snap](https://github.com/MystenLabs/sui/blob/4b2e1ab739cee34e4075717b52766eb3a07946df/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_55.snap) are the same: [diff](https://www.diffchecker.com/6NbvoYmk/) --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [x] Protocol: - [x] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --- crates/sui-open-rpc/spec/openrpc.json | 2 +- crates/sui-protocol-config/src/lib.rs | 8 +- ...ocol_config__test__Mainnet_version_56.snap | 324 +++++++++++++++++ ...ocol_config__test__Testnet_version_56.snap | 324 +++++++++++++++++ ...sui_protocol_config__test__version_56.snap | 333 ++++++++++++++++++ ...ests__genesis_config_snapshot_matches.snap | 2 +- ..._populated_genesis_snapshot_matches-2.snap | 31 +- 7 files changed, 1006 insertions(+), 18 deletions(-) create mode 100644 crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_56.snap create mode 100644 crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_56.snap create mode 100644 crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_56.snap diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index fae22c2e71eea..fca82a4d4fe2b 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": "55", + "maxSupportedProtocolVersion": "56", "protocolVersion": "6", "featureFlags": { "accept_zklogin_in_multisig": false, diff --git a/crates/sui-protocol-config/src/lib.rs b/crates/sui-protocol-config/src/lib.rs index 370bb61286a09..5dd0524410fbc 100644 --- a/crates/sui-protocol-config/src/lib.rs +++ b/crates/sui-protocol-config/src/lib.rs @@ -16,7 +16,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 = 55; +const MAX_PROTOCOL_VERSION: u64 = 56; // Record history of protocol version allocations here: // @@ -169,6 +169,7 @@ const MAX_PROTOCOL_VERSION: u64 = 55; // Enable soft bundle on mainnet. // Version 55: Enable enums on mainnet. // Rethrow serialization type layout errors instead of converting them. +// Version 56: Enable bridge on mainnet. #[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct ProtocolVersion(u64); @@ -2683,6 +2684,11 @@ impl ProtocolConfig { cfg.feature_flags.rethrow_serialization_type_layout_errors = true; } + 56 => { + if chain == Chain::Mainnet { + cfg.feature_flags.bridge = true; + } + } // Use this template when making changes: // // // modify an existing constant. diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_56.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_56.snap new file mode 100644 index 0000000000000..235fecc0da919 --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Mainnet_version_56.snap @@ -0,0 +1,324 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 56 +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: TotalTxCount + 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 +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 +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: 1000 +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: 100 +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: false +max_accumulated_txn_cost_per_object_in_mysticeti_commit: 10 + diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_56.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_56.snap new file mode 100644 index 0000000000000..4a06336c6fde4 --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__Testnet_version_56.snap @@ -0,0 +1,324 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 56 +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: TotalTxCount + 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 +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 +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: 1000 +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: 100 +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: 10 + diff --git a/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_56.snap b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_56.snap new file mode 100644 index 0000000000000..a18a84ddf856b --- /dev/null +++ b/crates/sui-protocol-config/src/snapshots/sui_protocol_config__test__version_56.snap @@ -0,0 +1,333 @@ +--- +source: crates/sui-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +--- +version: 56 +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: TotalTxCount + 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 +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 +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: 1000 +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: 100 +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: 10 + 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 c780c2c68a94a..4bf528401e530 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: 55 + protocol_version: 56 allow_insertion_of_extra_objects: true epoch_duration_ms: 86400000 stake_subsidy_start_epoch: 0 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 8dd452630584e..5447c9d000d0c 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: 55 +protocol_version: 56 system_state_version: 1 validators: total_stake: 20000000000000000 @@ -240,13 +240,13 @@ validators: next_epoch_worker_address: ~ extra_fields: id: - id: "0x5c69dd788c7563c072656a524608262510732d402034ca53215f41e32dc3db49" + id: "0x9df15aec1e3398f9894d68a8b9e419d66ff924991a376081456866c6528a2c72" size: 0 voting_power: 10000 - operation_cap_id: "0x5fa2be1df8bd48729b78bf6edd46a0e9ea28fb6c770cb50ac47f5df471f753b1" + operation_cap_id: "0xbd965c0ebe2a666754e13a1b9812beb608097e78494013237388535f8a227929" gas_price: 1000 staking_pool: - id: "0x21201b029962e18d8d5564676500afd842684e1e0c4f288c6eaa90e0b9a3c3ef" + id: "0x5fa1b7daa7553ca1025c1f5168650d13bfb2b0f626ed186649649ec24a57813f" activation_epoch: 0 deactivation_epoch: ~ sui_balance: 20000000000000000 @@ -254,14 +254,14 @@ validators: value: 0 pool_token_balance: 20000000000000000 exchange_rates: - id: "0x2424c3e08b4a40a728acf962554669000c48b6ba7691bfa260bd424fcd0c0834" + id: "0xbf9aa86783f7120325e8723deaaf863c36581193509fd84b48ee372de6588523" size: 1 pending_stake: 0 pending_total_sui_withdraw: 0 pending_pool_token_withdraw: 0 extra_fields: id: - id: "0x7100ddb5358349900beb5e8e8e046939e1c03eedd9f4709ae492ff4d0e0cf66c" + id: "0x3f21afb58ceeb5e2ce9f90698dfa697ba8a339998107863131f1a170b781eefd" size: 0 commission_rate: 200 next_epoch_stake: 20000000000000000 @@ -269,27 +269,27 @@ validators: next_epoch_commission_rate: 200 extra_fields: id: - id: "0xd791c0d0ddc2928045f1a5b2da7911cbdd83ba737e96c03550f39ce8e5643536" + id: "0x4a1e374de1e8276992f7394e0f26b7b3e6f417900dd2b2a45ebb848230f070ac" size: 0 pending_active_validators: contents: - id: "0xc24dd20b74615566bb9e91c83348fdc1e874124387dcfa094b9aff49e46c26be" + id: "0xbc0069fc9fe1841e10b1b11bc17fbdd8ec32e6c82a6e0a485c6ee1aea0c7ea0f" size: 0 pending_removals: [] staking_pool_mappings: - id: "0x771b57d70042c92903066603056006cd575cd2da9b47442da606f118f1c3fb19" + id: "0x949ea822e8280160fb7c7451c8ed7fbcb3df16341128b7e2755fe5d50d5b2e23" size: 1 inactive_validators: - id: "0x4ef8c9e72a5c6323eea95dc1de5531f71b801ff57f61b4b02f58b3ae144cc702" + id: "0xf369d485f7548ed0b8f83a3e4b9dc73789a9e979a7c44399bb2e4d2e96963327" size: 0 validator_candidates: - id: "0xdd4d1f847759e67723cb7b35c69ec7f8a87ad81e9d539c3167b63d5fb7cb6346" + id: "0x46f59e9e59177fb0cf7aa3ecf9ba3559e91aea74638db0b208182d82cf523227" size: 0 at_risk_validators: contents: [] extra_fields: id: - id: "0x444fd2bcbb8bd50ee5d444002ba287a733589cd276214698581cc68543629c61" + id: "0x742a57b52a250f0e271b053f69701bb8584494295bf7125431f6d75418854063" size: 0 storage_fund: total_object_storage_rebates: @@ -306,7 +306,7 @@ parameters: validator_low_stake_grace_period: 7 extra_fields: id: - id: "0x9dbd68e6ef30badec9e13354817d491d3414a7df56e5f81763d9ced438d94379" + id: "0x6b9f36ccd31f88afae0f3b97a7b8cd7602ce503c6cca9e21d5def22afb4aeeca" size: 0 reference_gas_price: 1000 validator_report_records: @@ -320,7 +320,7 @@ stake_subsidy: stake_subsidy_decrease_rate: 1000 extra_fields: id: - id: "0xd5a6a44eb3ef3395304533553789eff6c70c6f8d8e9aec3f4af09f23782f203b" + id: "0xfe2aef7de84880d24134dcc31990be0b8876a1bcfe380200c74150503aa85c7f" size: 0 safe_mode: false safe_mode_storage_rewards: @@ -332,5 +332,6 @@ safe_mode_non_refundable_storage_fee: 0 epoch_start_timestamp_ms: 10 extra_fields: id: - id: "0xc2c89825a972146e7131722bde266cefc18277894ca57c91616745924c8ff7fe" + id: "0x1e140e4970cac0e58e7b7ed01adbc11b64072eb10e20b3295dd483e7bd196f27" size: 0 + From c7590b12dc9b2e08b7e5eb27183e908231122aff Mon Sep 17 00:00:00 2001 From: Eugene Boguslavsky Date: Fri, 6 Sep 2024 16:44:53 +0000 Subject: [PATCH 229/232] test macos platform --- .github/workflows/release.yml | 26 ++++-- Cargo.lock | 116 +++++++++++++------------- Cargo.toml | 2 +- crates/sui-open-rpc/spec/openrpc.json | 2 +- 4 files changed, 80 insertions(+), 66 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1ec80c79b9c12..7b67afecbf45c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,8 +1,9 @@ name: Attach Sui binaries to a release +run-name: Attach Sui binaries to a ${{ inputs.sui_tag }} release on: release: - types: [ published ] + types: [ published, prereleased ] workflow_dispatch: inputs: sui_tag: @@ -43,10 +44,10 @@ jobs: matrix: os: [ - ubuntu-ghcloud, # ubuntu-x86_64 - windows-ghcloud, # windows-x86_64 + # ubuntu-ghcloud, # ubuntu-x86_64 + # windows-ghcloud, # windows-x86_64 macos-latest-xl, # macos-x86_64 - macos-latest-xlarge # macos-arm64 + # macos-latest-xlarge # macos-arm64 ] fail-fast: false runs-on: ${{ matrix.os }} @@ -94,7 +95,7 @@ jobs: continue-on-error: true shell: bash run: | - echo "s3_archive_exist=$(curl -Is https://sui-releases.s3.us-east-1.amazonaws.com/releases/sui-${{ env.sui_tag }}-${{ env.os_type }}.tgz | head -n 1 | grep '200 OK')" >> $GITHUB_ENV + # echo "s3_archive_exist=$(curl -Is https://sui-releases.s3.us-east-1.amazonaws.com/releases/sui-${{ env.sui_tag }}-${{ env.os_type }}.tgz | head -n 1 | grep '200 OK')" >> $GITHUB_ENV - name: Download archive, if it exists if: ${{ env.s3_archive_exist != '' }} @@ -123,7 +124,7 @@ jobs: echo "PG_DATABASE_URL=postgres://postgres:root@localhost/" >> $GITHUB_ENV echo "PG_EXAMPLE_DATABASE_URL=postgres://postgres:root@localhost/diesel_example" >> $GITHUB_ENV - - name: Install postgres (Mac arm64) + - name: Install postgres (MacOS arm64) if: ${{ matrix.os == 'macos-latest-xlarge' && env.gcloud_archive_exist == '' }} shell: bash env: @@ -134,6 +135,19 @@ jobs: run: | brew install postgresql + - name: Remove unused apps (MacOS arm64) + if: ${{ startsWith(matrix.os, 'macos') && env.gcloud_archive_exist == '' }} + continue-on-error: true + shell: bash + run: | + # MacOS arm64 runner only has 14GB avaialble, which is too small for our builds, so removing unused softwared. + df -hI /dev/disk3s1s1 + sudo rm -rf /Applications/Xcode*.app + sudo rm -rf ~/Library/Developer/Xcode/DerivedData + sudo rm -rf ~/Library/Developer/CoreSimulator/Caches/* + sudo rm -rf ~/Library/Developer/Xcode/iOS\ DeviceSupport/* + df -hI /dev/disk3s1s1 + - name: Cargo build for ${{ matrix.os }} platform if: ${{ env.s3_archive_exist == '' }} shell: bash diff --git a/Cargo.lock b/Cargo.lock index d47f271491979..4118256759b7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1854,7 +1854,7 @@ checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" [[package]] name = "bin-version" -version = "1.32.2" +version = "1.32.3" dependencies = [ "const-str", "git-version", @@ -12434,7 +12434,7 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" [[package]] name = "sui" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anemo", "anyhow", @@ -12505,7 +12505,7 @@ dependencies = [ "sui-package-management", "sui-protocol-config", "sui-replay", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-simulator", "sui-source-validation", "sui-swarm", @@ -12649,7 +12649,7 @@ dependencies = [ [[package]] name = "sui-analytics-indexer" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "arrow", @@ -12701,7 +12701,7 @@ dependencies = [ [[package]] name = "sui-analytics-indexer-derive" -version = "1.32.2" +version = "1.32.3" dependencies = [ "proc-macro2 1.0.78", "quote 1.0.35", @@ -12710,7 +12710,7 @@ dependencies = [ [[package]] name = "sui-archival" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "byteorder", @@ -12818,7 +12818,7 @@ dependencies = [ "sui-macros", "sui-network", "sui-protocol-config", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-simulator", "sui-storage", "sui-surfer", @@ -12836,7 +12836,7 @@ dependencies = [ [[package]] name = "sui-bridge" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "arc-swap", @@ -12870,7 +12870,7 @@ dependencies = [ "sui-json-rpc-api", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-test-transaction-builder", "sui-types", "tap", @@ -12885,7 +12885,7 @@ dependencies = [ [[package]] name = "sui-bridge-cli" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "clap", @@ -12902,7 +12902,7 @@ dependencies = [ "sui-config", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-types", "telemetry-subscribers", "tokio", @@ -12933,7 +12933,7 @@ dependencies = [ "sui-data-ingestion-core", "sui-indexer-builder", "sui-json-rpc-types", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-test-transaction-builder", "sui-types", "tap", @@ -12945,7 +12945,7 @@ dependencies = [ [[package]] name = "sui-cluster-test" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "async-trait", @@ -12969,7 +12969,7 @@ dependencies = [ "sui-json", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-swarm", "sui-swarm-config", "sui-test-transaction-builder", @@ -13142,7 +13142,7 @@ dependencies = [ [[package]] name = "sui-data-ingestion" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "async-trait", @@ -13204,7 +13204,7 @@ dependencies = [ [[package]] name = "sui-e2e-tests" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "assert_cmd", @@ -13248,7 +13248,7 @@ dependencies = [ "sui-node", "sui-protocol-config", "sui-rest-api", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-simulator", "sui-storage", "sui-swarm", @@ -13321,7 +13321,7 @@ dependencies = [ [[package]] name = "sui-faucet" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "async-recursion", @@ -13340,7 +13340,7 @@ dependencies = [ "sui-config", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-types", "tap", "telemetry-subscribers", @@ -13377,7 +13377,7 @@ dependencies = [ [[package]] name = "sui-framework-snapshot" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "bcs", @@ -13440,7 +13440,7 @@ dependencies = [ [[package]] name = "sui-graphql-config" -version = "1.32.2" +version = "1.32.3" dependencies = [ "quote 1.0.35", "syn 1.0.107", @@ -13517,7 +13517,7 @@ dependencies = [ "sui-package-resolver", "sui-protocol-config", "sui-rest-api", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-swarm-config", "sui-test-transaction-builder", "sui-types", @@ -13557,7 +13557,7 @@ dependencies = [ [[package]] name = "sui-indexer" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "async-trait", @@ -13600,7 +13600,7 @@ dependencies = [ "sui-package-resolver", "sui-protocol-config", "sui-rest-api", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-test-transaction-builder", "sui-transaction-builder", "sui-types", @@ -13750,7 +13750,7 @@ dependencies = [ "sui-open-rpc", "sui-open-rpc-macros", "sui-protocol-config", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-simulator", "sui-swarm-config", "sui-test-transaction-builder", @@ -13811,7 +13811,7 @@ dependencies = [ [[package]] name = "sui-light-client" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "async-trait", @@ -13828,7 +13828,7 @@ dependencies = [ "sui-json-rpc-types", "sui-package-resolver", "sui-rest-api", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-types", "tokio", ] @@ -13845,7 +13845,7 @@ dependencies = [ [[package]] name = "sui-metric-checker" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "backoff", @@ -13866,7 +13866,7 @@ dependencies = [ [[package]] name = "sui-move" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "assert_cmd", @@ -13908,7 +13908,7 @@ dependencies = [ [[package]] name = "sui-move-build" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "datatest-stable", @@ -13932,7 +13932,7 @@ dependencies = [ [[package]] name = "sui-move-lsp" -version = "1.32.2" +version = "1.32.3" dependencies = [ "bin-version", "clap", @@ -14062,7 +14062,7 @@ dependencies = [ [[package]] name = "sui-node" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anemo", "anemo-tower", @@ -14115,7 +14115,7 @@ dependencies = [ [[package]] name = "sui-open-rpc" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "bcs", @@ -14151,7 +14151,7 @@ dependencies = [ [[package]] name = "sui-oracle" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "bcs", @@ -14171,7 +14171,7 @@ dependencies = [ "sui-json-rpc-types", "sui-keys", "sui-move-build", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-types", "tap", "telemetry-subscribers", @@ -14181,7 +14181,7 @@ dependencies = [ [[package]] name = "sui-package-dump" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "bcs", @@ -14198,14 +14198,14 @@ dependencies = [ [[package]] name = "sui-package-management" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "move-core-types", "move-package", "move-symbol-pool", "sui-json-rpc-types", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-types", "thiserror", "tracing", @@ -14348,7 +14348,7 @@ dependencies = [ "sui-json-rpc-api", "sui-json-rpc-types", "sui-protocol-config", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-storage", "sui-transaction-checks", "sui-types", @@ -14393,7 +14393,7 @@ dependencies = [ [[package]] name = "sui-rosetta" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "async-trait", @@ -14421,7 +14421,7 @@ dependencies = [ "sui-keys", "sui-move-build", "sui-node", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-swarm-config", "sui-types", "telemetry-subscribers", @@ -14435,7 +14435,7 @@ dependencies = [ [[package]] name = "sui-rpc-loadgen" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "async-trait", @@ -14453,7 +14453,7 @@ dependencies = [ "sui-json-rpc", "sui-json-rpc-types", "sui-keys", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-types", "telemetry-subscribers", "test-cluster", @@ -14484,7 +14484,7 @@ dependencies = [ [[package]] name = "sui-sdk" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "async-recursion", @@ -14520,7 +14520,7 @@ dependencies = [ [[package]] name = "sui-security-watchdog" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "arrow-array", @@ -14567,7 +14567,7 @@ dependencies = [ [[package]] name = "sui-single-node-benchmark" -version = "1.32.2" +version = "1.32.3" dependencies = [ "async-trait", "bcs", @@ -14630,7 +14630,7 @@ dependencies = [ [[package]] name = "sui-source-validation" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "colored", @@ -14648,7 +14648,7 @@ dependencies = [ "sui-json-rpc-types", "sui-move-build", "sui-package-management", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-test-transaction-builder", "sui-types", "tar", @@ -14684,7 +14684,7 @@ dependencies = [ "sui-json-rpc-types", "sui-move", "sui-move-build", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-source-validation", "telemetry-subscribers", "tempfile", @@ -14755,7 +14755,7 @@ dependencies = [ [[package]] name = "sui-surfer" -version = "1.32.2" +version = "1.32.3" dependencies = [ "async-trait", "bcs", @@ -14853,13 +14853,13 @@ dependencies = [ "shared-crypto", "sui-genesis-builder", "sui-move-build", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-types", ] [[package]] name = "sui-test-validator" -version = "1.32.2" +version = "1.32.3" [[package]] name = "sui-tls" @@ -14884,7 +14884,7 @@ dependencies = [ [[package]] name = "sui-tool" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anemo", "anemo-cli", @@ -14918,7 +14918,7 @@ dependencies = [ "sui-package-dump", "sui-protocol-config", "sui-replay", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-snapshot", "sui-storage", "sui-types", @@ -15170,7 +15170,7 @@ dependencies = [ [[package]] name = "suins-indexer" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "async-trait", @@ -15204,7 +15204,7 @@ dependencies = [ [[package]] name = "suiop-cli" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "axum 0.7.5", @@ -15549,7 +15549,7 @@ dependencies = [ "sui-macros", "sui-node", "sui-protocol-config", - "sui-sdk 1.32.2", + "sui-sdk 1.32.3", "sui-simulator", "sui-swarm", "sui-swarm-config", @@ -17372,7 +17372,7 @@ dependencies = [ [[package]] name = "x" -version = "1.32.2" +version = "1.32.3" dependencies = [ "anyhow", "camino", diff --git a/Cargo.toml b/Cargo.toml index 6c9976a5a554d..e81adee0b65b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -201,7 +201,7 @@ members = [ [workspace.package] # This version string will be inherited by sui-core, sui-faucet, sui-node, sui-tools, sui-sdk, sui-move-build, and sui crates. -version = "1.32.2" +version = "1.32.3" [profile.release] # debug = 1 means line charts only, which is minimum needed for good stack traces diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index fca82a4d4fe2b..ff9b82a332645 100644 --- a/crates/sui-open-rpc/spec/openrpc.json +++ b/crates/sui-open-rpc/spec/openrpc.json @@ -12,7 +12,7 @@ "name": "Apache-2.0", "url": "https://raw.githubusercontent.com/MystenLabs/sui/main/LICENSE" }, - "version": "1.32.2" + "version": "1.32.3" }, "methods": [ { From b1a66b66e5097a9d6e3cfbd4dbb9f4086248e388 Mon Sep 17 00:00:00 2001 From: Eugene Boguslavsky Date: Fri, 6 Sep 2024 16:45:21 +0000 Subject: [PATCH 230/232] fix name --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7b67afecbf45c..82e1dd641408d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -135,7 +135,7 @@ jobs: run: | brew install postgresql - - name: Remove unused apps (MacOS arm64) + - name: Remove unused apps (MacOS platform) if: ${{ startsWith(matrix.os, 'macos') && env.gcloud_archive_exist == '' }} continue-on-error: true shell: bash From 3576d332b0e58756a5a02c3933c4eb465632ecfe Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 6 Sep 2024 12:46:41 -0500 Subject: [PATCH 231/232] authority_aggregator: always request for events when asked (#19251) Change AuthorityAggregator to always pass through request_events flag, even to the validators we aren't sampling for objects. In addition, up the sample size of validators to 10 and log when we get unlucky and reach quorum without having recieved input or output objects. --- crates/sui-core/src/authority_aggregator.rs | 23 +++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/crates/sui-core/src/authority_aggregator.rs b/crates/sui-core/src/authority_aggregator.rs index 8365a2064fb3b..c5fc1e57d158f 100644 --- a/crates/sui-core/src/authority_aggregator.rs +++ b/crates/sui-core/src/authority_aggregator.rs @@ -104,6 +104,7 @@ pub struct AuthAggMetrics { pub cert_broadcasting_post_quorum_timeout: IntCounter, pub remaining_tasks_when_reaching_cert_quorum: Histogram, pub remaining_tasks_when_cert_broadcasting_post_quorum_timeout: Histogram, + pub quorum_reached_without_requested_objects: IntCounter, } impl AuthAggMetrics { @@ -188,7 +189,13 @@ impl AuthAggMetrics { "auth_agg_remaining_tasks_when_cert_broadcasting_post_quorum_timeout", "Number of remaining tasks when post quorum certificate broadcasting times out", registry, + ), + quorum_reached_without_requested_objects: register_int_counter_with_registry!( + "auth_agg_quorum_reached_without_requested_objects", + "Number of times quorum was reached without getting the requested objects back from at least 1 validator", + registry, ) + .unwrap(), } } @@ -454,6 +461,7 @@ struct ProcessCertificateState { input_objects: Option>, output_objects: Option>, auxiliary_data: Option>, + request: HandleCertificateRequestV3, } #[derive(Debug)] @@ -1497,13 +1505,14 @@ where input_objects: None, output_objects: None, auxiliary_data: None, + request: request.clone(), }; // create a set of validators that we should sample to request input/output objects from let validators_to_sample = if request.include_input_objects || request.include_output_objects { // Number of validators to request input/output objects from - const NUMBER_TO_SAMPLE: usize = 5; + const NUMBER_TO_SAMPLE: usize = 10; self.committee .choose_multiple_weighted_iter(NUMBER_TO_SAMPLE) @@ -1547,7 +1556,6 @@ where request_ref } else { HandleCertificateRequestV3 { - include_events: false, include_input_objects: false, include_output_objects: false, include_auxiliary_data: false, @@ -1583,6 +1591,7 @@ where // and return. match AuthorityAggregator::::handle_process_certificate_response( committee_clone, + &metrics, &tx_digest, &mut state, response, name) { Ok(Some(effects)) => ReduceOutput::Success(effects), @@ -1695,6 +1704,7 @@ where fn handle_process_certificate_response( committee: Arc, + metrics: &AuthAggMetrics, tx_digest: &TransactionDigest, state: &mut ProcessCertificateState, response: SuiResult, @@ -1758,6 +1768,15 @@ where signed_effects.into_data(), cert_sig, ); + + if (state.request.include_input_objects && state.input_objects.is_none()) + || (state.request.include_output_objects + && state.output_objects.is_none()) + { + metrics.quorum_reached_without_requested_objects.inc(); + debug!(?tx_digest, "Quorum Reached but requested input/output objects were not returned"); + } + ct.verify(&committee).map(|ct| { debug!(?tx_digest, "Got quorum for validators handle_certificate."); Some(QuorumDriverResponse { From f1c01f283b152107a3f6469f1e119e3eeee04ccb Mon Sep 17 00:00:00 2001 From: NB Date: Tue, 17 Sep 2024 09:05:22 +0200 Subject: [PATCH 232/232] temp --- Cargo.lock | 4865 +++++++--------- Cargo.toml | 20 +- .../Cargo.toml | 2 +- crates/sui-analytics-indexer/Cargo.toml | 4 +- crates/sui-cluster-test/Cargo.toml | 2 +- crates/sui-graphql-rpc/Cargo.toml | 4 +- crates/sui-indexer/logs.txt | 5000 +++++++++++++++++ .../src/handlers/checkpoint_handler.rs | 23 +- crates/sui-indexer/src/handlers/mod.rs | 4 +- crates/sui-indexer/src/indexer.rs | 1 + crates/sui-indexer/src/lib.rs | 2 +- crates/sui-indexer/src/schema/pg.rs | 2391 +++++++- .../sui-transactional-test-runner/Cargo.toml | 4 +- .../Cargo.toml | 2 +- fullnode.yaml | 2 +- 15 files changed, 9299 insertions(+), 3027 deletions(-) create mode 100644 crates/sui-indexer/logs.txt diff --git a/Cargo.lock b/Cargo.lock index a4305363ba311..43b41c5fc7206 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,11 +38,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aead" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c192eb8f11fc081b0fe4259ba5af04217d4e0faddd02417310a927911abd7c8" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", "generic-array", @@ -50,9 +56,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", @@ -61,9 +67,9 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", @@ -130,9 +136,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "android-tzdata" @@ -168,22 +174,17 @@ dependencies = [ "quinn", "quinn-proto", "rand 0.8.5", -<<<<<<< HEAD - "rcgen 0.13.1", - "ring 0.17.8", -======= "rcgen", - "ring 0.17.3", ->>>>>>> upstream/mainnet - "rustls 0.23.12", - "rustls-webpki 0.102.6", + "ring 0.17.8", + "rustls 0.23.13", + "rustls-webpki 0.102.8", "serde", "serde_json", - "socket2 0.5.6", + "socket2 0.5.7", "tap", "thiserror", "tokio", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.7.12", "tower", "tracing", "x509-parser", @@ -208,9 +209,9 @@ version = "0.0.0" source = "git+https://github.com/mystenlabs/anemo.git?rev=dbb5a074c2d25660525ab5d36d65ff0cb8051949#dbb5a074c2d25660525ab5d36d65ff0cb8051949" dependencies = [ "prettyplease", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -244,7 +245,7 @@ dependencies = [ "tokio", "tower", "tracing", - "uuid 1.2.2", + "uuid 1.10.0", ] [[package]] @@ -255,47 +256,48 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -303,27 +305,27 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356" dependencies = [ "backtrace", ] [[package]] name = "arbitrary" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" dependencies = [ "derive_arbitrary", ] [[package]] name = "arc-swap" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" dependencies = [ "serde", ] @@ -371,9 +373,9 @@ dependencies = [ [[package]] name = "ark-ec" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c60370a92f8e1a5f053cad73a862e1b99bc642333cd676fa11c0c39f80f4ac2" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" dependencies = [ "ark-ff", "ark-poly", @@ -399,7 +401,7 @@ dependencies = [ "derivative", "digest 0.10.7", "itertools 0.10.5", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-traits", "paste", "rustc_version", @@ -412,8 +414,8 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote 1.0.35", - "syn 1.0.107", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -422,11 +424,11 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-traits", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -446,9 +448,9 @@ dependencies = [ [[package]] name = "ark-poly" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6ec811462cabe265cfe1b102fcfe3df79d7d2929c2425673648ee9abfd0272" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" dependencies = [ "ark-ff", "ark-serialize", @@ -488,7 +490,7 @@ dependencies = [ "ark-serialize-derive", "ark-std", "digest 0.10.7", - "num-bigint 0.4.4", + "num-bigint 0.4.6", ] [[package]] @@ -497,9 +499,9 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -526,9 +528,9 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" [[package]] name = "arrayvec" @@ -538,9 +540,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "arrow" @@ -574,7 +576,7 @@ dependencies = [ "arrow-data", "arrow-schema", "chrono", - "half 2.4.1", + "half", "num", ] @@ -589,7 +591,7 @@ dependencies = [ "arrow-data", "arrow-schema", "chrono", - "half 2.4.1", + "half", "hashbrown 0.14.5", "num", ] @@ -601,7 +603,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c975484888fc95ec4a632cdc98be39c085b1bb518531b0c80c5d462063e5daa1" dependencies = [ "bytes", - "half 2.4.1", + "half", "num", ] @@ -619,7 +621,7 @@ dependencies = [ "atoi", "base64 0.22.1", "chrono", - "half 2.4.1", + "half", "lexical-core", "num", "ryu", @@ -652,7 +654,7 @@ checksum = "dd9d6f18c65ef7a2573ab498c374d8ae364b4a4edf67105357491c031f716ca5" dependencies = [ "arrow-buffer", "arrow-schema", - "half 2.4.1", + "half", "num", ] @@ -682,8 +684,8 @@ dependencies = [ "arrow-data", "arrow-schema", "chrono", - "half 2.4.1", - "indexmap 2.2.6", + "half", + "indexmap 2.5.0", "lexical-core", "num", "serde", @@ -701,7 +703,7 @@ dependencies = [ "arrow-data", "arrow-schema", "arrow-select", - "half 2.4.1", + "half", "num", ] @@ -716,7 +718,7 @@ dependencies = [ "arrow-buffer", "arrow-data", "arrow-schema", - "half 2.4.1", + "half", ] [[package]] @@ -757,8 +759,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "ascii-canvas" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -768,17 +768,10 @@ dependencies = [ ] [[package]] -name = "ascii_utils" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a" - -[[package]] ->>>>>>> upstream/mainnet name = "asn1-rs" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf6690c370453db30743b373a60ba498fc0d6d83b11f4abfd87a84a075db5dd4" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", @@ -796,9 +789,9 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", "synstructure", ] @@ -808,167 +801,50 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] name = "assert_cmd" -version = "2.0.7" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa3d466004a8b4cb1bc34044240a2fd29d17607e2e3bd613eb44fd48e8100da3" +checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" dependencies = [ + "anstyle", "bstr", "doc-comment", - "predicates", + "libc", + "predicates 3.1.2", "predicates-core", "predicates-tree", "wait-timeout", ] -[[package]] -name = "async-compression" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a116f46a969224200a0a97f29cfd4c50e7534e4b4826bd23ea2c3c533039c82c" -dependencies = [ - "brotli", - "flate2", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", -<<<<<<< HEAD -] - [[package]] name = "async-compression" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" dependencies = [ + "brotli", "flate2", "futures-core", "memchr", "pin-project-lite", "tokio", + "zstd 0.13.2", + "zstd-safe 7.2.1", ] [[package]] -======= - "zstd 0.13.0", - "zstd-safe 7.0.0", -] - -[[package]] -name = "async-graphql" -version = "7.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16926f97f683ff3b47b035cc79622f3d6a374730b07a5d9051e81e88b5f1904" -dependencies = [ - "async-graphql-derive", - "async-graphql-parser", - "async-graphql-value", - "async-stream", - "async-trait", - "base64 0.13.1", - "bytes", - "chrono", - "fast_chemail", - "fnv", - "futures-channel", - "futures-timer", - "futures-util", - "handlebars", - "http 1.1.0", - "indexmap 2.2.6", - "lru 0.7.8", - "mime", - "multer", - "num-traits", - "once_cell", - "opentelemetry 0.21.0", - "pin-project-lite", - "regex", - "serde", - "serde_json", - "serde_urlencoded", - "static_assertions_next", - "tempfile", - "thiserror", - "tracing", - "tracing-futures", -] - -[[package]] -name = "async-graphql-axum" -version = "7.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3415c9dbaf54397292da0bb81a907e2b989661ce068e4ccfebac33dc9e245e" -dependencies = [ - "async-graphql", - "async-trait", - "axum 0.7.5", - "bytes", - "futures-util", - "serde_json", - "tokio", - "tokio-stream", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tower-service", -] - -[[package]] -name = "async-graphql-derive" -version = "7.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a7349168b79030e3172a620f4f0e0062268a954604e41475eff082380fe505" -dependencies = [ - "Inflector", - "async-graphql-parser", - "darling 0.20.3", - "proc-macro-crate", - "proc-macro2 1.0.78", - "quote 1.0.35", - "strum 0.25.0", - "syn 2.0.48", - "thiserror", -] - -[[package]] -name = "async-graphql-parser" -version = "7.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fdc0adf9f53c2b65bb0ff5170cba1912299f248d0e48266f444b6f005deb1d" -dependencies = [ - "async-graphql-value", - "pest", - "serde", - "serde_json", -] - -[[package]] -name = "async-graphql-value" -version = "7.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cf4d4e86208f4f9b81a503943c07e6e7f29ad3505e6c9ce6431fe64dc241681" -dependencies = [ - "bytes", - "indexmap 2.2.6", - "serde", - "serde_json", -] - -[[package]] ->>>>>>> upstream/mainnet name = "async-lock" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ "event-listener", - "futures-lite", ] [[package]] @@ -984,13 +860,13 @@ dependencies = [ "nkeys", "nuid", "once_cell", - "portable-atomic 1.7.0", + "portable-atomic", "rand 0.8.5", "regex", "ring 0.17.8", - "rustls-native-certs 0.7.1", - "rustls-pemfile 2.1.2", - "rustls-webpki 0.102.6", + "rustls-native-certs 0.7.3", + "rustls-pemfile 2.1.3", + "rustls-webpki 0.102.8", "serde", "serde_json", "serde_nanos", @@ -1006,34 +882,35 @@ dependencies = [ [[package]] name = "async-recursion" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "async-stream" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", + "pin-project-lite", ] [[package]] name = "async-stream-impl" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -1043,13 +920,13 @@ source = "git+https://github.com/mystenmark/async-task?rev=4e45b26e11126b191701b [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -1109,21 +986,20 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7b2dbe9169059af0f821e811180fddc971fc210c776c133c7819ccd6e478db" dependencies = [ - "rustix 0.38.28", + "rustix", "tempfile", "windows-sys 0.52.0", ] [[package]] name = "auto_impl" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ - "proc-macro-error", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -1134,9 +1010,9 @@ checksum = "7460f7dd8e100147b82a63afca1a20eb6c231ee36b90ba7272e14951cb58af59" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "aws-config" @@ -1156,10 +1032,10 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.0.0", + "fastrand", "hex", - "http 0.2.9", - "hyper 0.14.26", + "http 0.2.12", + "hyper 0.14.30", "ring 0.16.20", "time", "tokio", @@ -1176,7 +1052,7 @@ checksum = "70a66ac8ef5fa9cf01c2d999f39d16812e90ec1467bd382cbbb74ba23ea86201" dependencies = [ "aws-smithy-async", "aws-smithy-types", - "fastrand 2.0.0", + "fastrand", "tokio", "tracing", "zeroize", @@ -1193,8 +1069,8 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "http 0.2.9", - "http-body 0.4.5", + "http 0.2.12", + "http-body 0.4.6", "lazy_static", "percent-encoding", "pin-project-lite", @@ -1216,11 +1092,11 @@ dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", "aws-types", - "fastrand 2.0.0", - "http 0.2.9", + "fastrand", + "http 0.2.12", "percent-encoding", "tracing", - "uuid 1.2.2", + "uuid 1.10.0", ] [[package]] @@ -1241,8 +1117,8 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.0.0", - "http 0.2.9", + "fastrand", + "http 0.2.12", "regex", "tokio-stream", "tracing", @@ -1267,8 +1143,8 @@ dependencies = [ "aws-smithy-types", "aws-smithy-xml", "aws-types", - "fastrand 2.0.0", - "http 0.2.9", + "fastrand", + "http 0.2.12", "regex", "tokio-stream", "tracing", @@ -1296,8 +1172,8 @@ dependencies = [ "aws-smithy-xml", "aws-types", "bytes", - "http 0.2.9", - "http-body 0.4.5", + "http 0.2.12", + "http-body 0.4.6", "once_cell", "percent-encoding", "regex", @@ -1324,7 +1200,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "http 0.2.9", + "http 0.2.12", "regex", "tokio-stream", "tracing", @@ -1349,7 +1225,7 @@ dependencies = [ "aws-smithy-types", "aws-smithy-xml", "aws-types", - "http 0.2.9", + "http 0.2.12", "regex", "tracing", ] @@ -1366,7 +1242,7 @@ dependencies = [ "form_urlencoded", "hex", "hmac 0.12.1", - "http 0.2.9", + "http 0.2.12", "once_cell", "percent-encoding", "regex", @@ -1399,8 +1275,8 @@ dependencies = [ "crc32c", "crc32fast", "hex", - "http 0.2.9", - "http-body 0.4.5", + "http 0.2.12", + "http-body 0.4.6", "md-5 0.10.6", "pin-project-lite", "sha1", @@ -1419,11 +1295,11 @@ dependencies = [ "aws-smithy-http-tower", "aws-smithy-types", "bytes", - "fastrand 2.0.0", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.26", - "hyper-rustls 0.24.0", + "fastrand", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", + "hyper-rustls 0.24.2", "lazy_static", "pin-project-lite", "rustls 0.21.12", @@ -1454,15 +1330,15 @@ dependencies = [ "bytes", "bytes-utils", "futures-core", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", "once_cell", "percent-encoding", "pin-project-lite", "pin-utils", "tokio", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.7.12", "tracing", ] @@ -1475,8 +1351,8 @@ dependencies = [ "aws-smithy-http", "aws-smithy-types", "bytes", - "http 0.2.9", - "http-body 0.4.5", + "http 0.2.12", + "http-body 0.4.6", "pin-project-lite", "tower", "tracing", @@ -1513,9 +1389,9 @@ dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", "bytes", - "fastrand 2.0.0", - "http 0.2.9", - "http-body 0.4.5", + "fastrand", + "http 0.2.12", + "http-body 0.4.6", "once_cell", "pin-project-lite", "pin-utils", @@ -1533,7 +1409,7 @@ dependencies = [ "aws-smithy-http", "aws-smithy-types", "bytes", - "http 0.2.9", + "http 0.2.12", "tokio", "tracing", ] @@ -1572,7 +1448,7 @@ dependencies = [ "aws-smithy-client", "aws-smithy-http", "aws-smithy-types", - "http 0.2.9", + "http 0.2.12", "rustc_version", "tracing", ] @@ -1588,11 +1464,11 @@ dependencies = [ "bitflags 1.3.2", "bytes", "futures-util", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", "itoa", - "matchit 0.7.0", + "matchit 0.7.3", "memchr", "mime", "percent-encoding", @@ -1622,7 +1498,7 @@ dependencies = [ "hyper 1.4.1", "hyper-util", "itoa", - "matchit 0.7.0", + "matchit 0.7.3", "memchr", "mime", "percent-encoding", @@ -1633,17 +1509,12 @@ dependencies = [ "serde_path_to_error", "serde_urlencoded", "sha1", -<<<<<<< HEAD - "sync_wrapper", -======= "sync_wrapper 1.0.1", ->>>>>>> upstream/mainnet "tokio", "tokio-tungstenite 0.21.0", "tower", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -1655,8 +1526,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 0.2.9", - "http-body 0.4.5", + "http 0.2.12", + "http-body 0.4.6", "mime", "rustversion", "tower-layer", @@ -1721,8 +1592,8 @@ dependencies = [ "hyper 1.4.1", "hyper-util", "pin-project-lite", - "rustls 0.23.12", - "rustls-pemfile 2.1.2", + "rustls 0.23.13", + "rustls-pemfile 2.1.3", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -1746,15 +1617,15 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.7.4", "object", "rustc-demangle", ] @@ -1816,9 +1687,9 @@ dependencies = [ [[package]] name = "base64-url" -version = "2.0.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c5b0a88aa36e9f095ee2e2b13fb8c5e4313e022783aedacc123328c0084916d" +checksum = "fb9fb9fb058cc3063b5fc88d9a21eefa2735871498a04e1650da76ed511c8569" dependencies = [ "base64 0.21.7", ] @@ -1836,7 +1707,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" dependencies = [ "blowfish", - "pbkdf2 0.12.1", + "pbkdf2 0.12.2", "sha2 0.10.8", ] @@ -1867,9 +1738,9 @@ dependencies = [ [[package]] name = "bellpepper" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b89c91b2463f99a3a527a16a5b6862f257ee8188d3cf1fbc53af06fb61c09f4f" +checksum = "9ae286c2cb403324ab644c7cc68dceb25fe52ca9429908a726d7ed272c1edf7b" dependencies = [ "bellpepper-core", "byteorder", @@ -1904,9 +1775,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3deeecb812ca5300b7d3f66f730cc2ebd3511c3d36c691dd79c165d5b19a26e3" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -1917,11 +1788,7 @@ checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" [[package]] name = "bin-version" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "const-str", "git-version", @@ -1949,12 +1816,12 @@ dependencies = [ "lazycell", "peeking_take_while", "prettyplease", - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] @@ -2006,9 +1873,9 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a539389a13af092cd345a2b47ae7dec12deb306d660b2223d25cd3419b253ebe" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -2034,9 +1901,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" dependencies = [ "serde", ] @@ -2090,8 +1957,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" dependencies = [ "arrayref", - "arrayvec 0.7.2", - "constant_time_eq 0.3.0", + "arrayvec 0.7.6", + "constant_time_eq 0.3.1", ] [[package]] @@ -2101,8 +1968,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94230421e395b9920d23df13ea5d77a20e1725331f90fbbf6df6040b33f756ae" dependencies = [ "arrayref", - "arrayvec 0.7.2", - "constant_time_eq 0.3.0", + "arrayvec 0.7.6", + "constant_time_eq 0.3.1", ] [[package]] @@ -2117,9 +1984,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] @@ -2132,9 +1999,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "block-padding" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a90ec2df9600c28a01c56c4784c9207a96d2451833aeceb8cc97e4c9548bb78" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ "generic-array", ] @@ -2151,9 +2018,9 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.11" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94087b935a822949d3291a9989ad2b2051ea141eda0fd4e478a75f6aa3e604b" +checksum = "4378725facc195f1a538864863f6de233b500a8862747e7f165078a419d5e874" dependencies = [ "cc", "glob", @@ -2185,9 +2052,9 @@ checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790" [[package]] name = "brotli" -version = "3.3.4" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -2196,9 +2063,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.3.2" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -2230,20 +2097,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", -<<<<<<< HEAD - "once_cell", - "regex-automata 0.1.10", -======= "regex-automata 0.4.7", ->>>>>>> upstream/mainnet "serde", ] [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byte-slice-cast" @@ -2253,15 +2115,15 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytecount" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytemuck" -version = "1.16.3" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" [[package]] name = "byteorder" @@ -2271,18 +2133,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" dependencies = [ "serde", ] [[package]] name = "bytes-utils" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47d3a8076e283f3acd27400535992edb3ba4b5bb72f8891ad8fbe7932a7d4b9" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" dependencies = [ "bytes", "either", @@ -2344,53 +2206,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e10ca87c81aaa3a949dbbe2b5e6c2c45dbc94ba4897e45ea31ff9ec5087be3dc" dependencies = [ "cached_proc_macro_types", - "darling 0.14.2", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "darling 0.14.4", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] name = "cached_proc_macro_types" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" +checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" [[package]] name = "camino" -<<<<<<< HEAD -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" -======= version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" ->>>>>>> upstream/mainnet dependencies = [ "serde", ] [[package]] name = "cargo-platform" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.14.2" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ - "camino", - "cargo-platform", - "semver 1.0.23", "serde", - "serde_json", ] [[package]] @@ -2401,7 +2244,7 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver 1.0.23", + "semver", "serde", "serde_json", "thiserror", @@ -2415,7 +2258,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver 1.0.23", + "semver", "serde", "serde_json", "thiserror", @@ -2444,12 +2287,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.94" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" +checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -2463,15 +2307,9 @@ dependencies = [ [[package]] name = "cfg-expr" -<<<<<<< HEAD -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" -======= -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345c78335be0624ed29012dc10c49102196c6882c12dde65d9f35b02da2aada8" ->>>>>>> upstream/mainnet +checksum = "d0890061c4d3223e7267f3bad2ec40b997d64faac1c2815a4a9d95018e2b9e9c" dependencies = [ "smallvec", "target-lexicon", @@ -2506,14 +2344,14 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.0", + "windows-targets 0.52.6", ] [[package]] name = "ciborium" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", @@ -2522,18 +2360,18 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", - "half 1.8.2", + "half", ] [[package]] @@ -2548,9 +2386,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.4.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", @@ -2559,9 +2397,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" dependencies = [ "clap_builder", "clap_derive", @@ -2569,9 +2407,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" dependencies = [ "anstream", "anstyle", @@ -2582,21 +2420,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "clipboard-win" @@ -2640,7 +2478,7 @@ dependencies = [ "coins-core", "digest 0.10.7", "hmac 0.12.1", - "k256 0.13.1", + "k256 0.13.3", "serde", "sha2 0.10.8", "thiserror", @@ -2656,7 +2494,7 @@ dependencies = [ "coins-bip32", "hmac 0.12.1", "once_cell", - "pbkdf2 0.12.1", + "pbkdf2 0.12.2", "rand 0.8.5", "sha2 0.10.8", "thiserror", @@ -2678,7 +2516,7 @@ dependencies = [ "serde", "serde_derive", "sha2 0.10.8", - "sha3 0.10.6", + "sha3 0.10.8", "thiserror", ] @@ -2717,26 +2555,25 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "colored" -version = "2.0.4" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" dependencies = [ - "is-terminal", "lazy_static", "windows-sys 0.48.0", ] [[package]] name = "combine" -version = "4.6.6" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", "memchr", @@ -2787,7 +2624,7 @@ dependencies = [ "futures", "http 1.1.0", "hyper 1.4.1", - "hyper-rustls 0.27.2", + "hyper-rustls 0.27.3", "hyper-util", "itertools 0.10.5", "mockall", @@ -2795,13 +2632,13 @@ dependencies = [ "mysten-metrics", "mysten-network", "nom", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "prometheus", - "prost 0.13.1", + "prost 0.13.2", "quinn-proto", "rand 0.8.5", "rstest", - "rustls 0.23.12", + "rustls 0.23.13", "serde", "shared-crypto", "strum_macros 0.24.3", @@ -2815,8 +2652,8 @@ dependencies = [ "tokio", "tokio-rustls 0.26.0", "tokio-stream", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tonic 0.12.1", + "tokio-util 0.7.12", + "tonic 0.12.2", "tonic-build", "tower", "tower-http", @@ -2826,15 +2663,15 @@ dependencies = [ [[package]] name = "console" -version = "0.15.4" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b6515d269224923b26b5febea2ed42b2d5f2ce37284a4dd670fedd6cb8347a" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode 0.3.6", "lazy_static", "libc", "unicode-width", - "windows-sys 0.42.0", + "windows-sys 0.52.0", ] [[package]] @@ -2844,9 +2681,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd326812b3fd01da5bb1af7d340d0d555fd3d4b641e7f1dfcf5962a902952787" dependencies = [ "futures-core", - "prost 0.12.3", - "prost-types 0.12.3", - "tonic 0.10.0", + "prost 0.12.6", + "prost-types 0.12.6", + "tonic 0.10.2", "tracing-core", ] @@ -2862,13 +2699,13 @@ dependencies = [ "futures-task", "hdrhistogram", "humantime", - "prost-types 0.12.3", + "prost-types 0.12.6", "serde", "serde_json", "thread_local", "tokio", "tokio-stream", - "tonic 0.10.0", + "tonic 0.10.2", "tracing", "tracing-core", "tracing-subscriber", @@ -2876,21 +2713,22 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.9.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c37be52ef5e3b394db27a2341010685ad5103c72ac15ce2e9420a7e8f93f342c" +checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" dependencies = [ "cfg-if", "cpufeatures", "hex", + "proptest", "serde", ] [[package]] name = "const-oid" -version = "0.9.2" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const-random" @@ -2914,9 +2752,9 @@ dependencies = [ [[package]] name = "const-str" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aca749d3d3f5b87a0d6100509879f9cf486ab510803a4a4e1001da1ff61c2bd6" +checksum = "3618cccc083bb987a415d85c02ca6c9994ea5b44731ec28b9ecf09658655fba9" [[package]] name = "constant_time_eq" @@ -2926,9 +2764,9 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "constant_time_eq" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "containers-api" @@ -2939,8 +2777,8 @@ dependencies = [ "chrono", "flate2", "futures-util", - "http 0.2.9", - "hyper 0.14.26", + "http 0.2.12", + "hyper 0.14.30", "hyperlocal", "log", "mime", @@ -2962,9 +2800,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -2972,9 +2810,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core2" @@ -2987,9 +2825,9 @@ dependencies = [ [[package]] name = "coset" -version = "0.3.4" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c765a4e852cef25c69a48e9fcd60995a7fecabf0134a0021e7181452c4a60f95" +checksum = "f4c8cc80f631f8307b887faca24dcc3abc427cd0367f6eb6188f6e8f5b7ad8fb" dependencies = [ "ciborium", "ciborium-io", @@ -3016,36 +2854,36 @@ dependencies = [ [[package]] name = "cpp_demangle" -version = "0.4.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b446fd40bcc17eddd6a4a78f24315eb90afdb3334999ddfd4909985c47722442" +checksum = "96e58d342ad113c2b878f16d5d034c03be492ae460cdbc02b7f0f2284d310c7d" dependencies = [ "cfg-if", ] [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] [[package]] name = "crc32c" -version = "0.6.4" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8f48d60e5b4d2c53d5c2b1d8a58c849a70ae5e5509b08a48d047e3b65714a74" +checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" dependencies = [ "rustc_version", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -3164,8 +3002,8 @@ dependencies = [ "bitflags 1.3.2", "crossterm_winapi", "libc", - "mio", - "parking_lot 0.12.1", + "mio 0.8.11", + "parking_lot 0.12.3", "signal-hook", "signal-hook-mio", "winapi", @@ -3180,8 +3018,8 @@ dependencies = [ "bitflags 1.3.2", "crossterm_winapi", "libc", - "mio", - "parking_lot 0.12.1", + "mio 0.8.11", + "parking_lot 0.12.3", "signal-hook", "signal-hook-mio", "winapi", @@ -3189,9 +3027,9 @@ dependencies = [ [[package]] name = "crossterm_winapi" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" dependencies = [ "winapi", ] @@ -3216,9 +3054,9 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.5.1" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -3249,9 +3087,9 @@ dependencies = [ [[package]] name = "csv" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b015497079b9a9d69c02ad25de6c0a6edef051ea6360a327d0bd05802ef64ad" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" dependencies = [ "csv-core", "itoa", @@ -3261,23 +3099,13 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ "memchr", ] -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote 1.0.35", - "syn 1.0.107", -] - [[package]] name = "ctr" version = "0.9.2" @@ -3309,9 +3137,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -3327,50 +3155,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "cxx" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2 1.0.78", - "quote 1.0.35", - "scratch", - "syn 1.0.107", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", -] - [[package]] name = "cynic" version = "3.7.3" @@ -3379,7 +3163,7 @@ checksum = "478c02b53607e3f21c374f024c2cfc2154e554905bba478e8e09409f10ce3726" dependencies = [ "cynic-proc-macros", "ref-cast", - "reqwest 0.12.5", + "reqwest 0.12.7", "serde", "serde_json", "static_assertions", @@ -3394,13 +3178,13 @@ checksum = "7c0ec86f960a00ce087e96ff6f073f6ff28b6876d69ce8caa06c03fb4143981c" dependencies = [ "counter", "cynic-parser", - "darling 0.20.3", + "darling 0.20.10", "once_cell", "ouroboros 0.18.4", - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "strsim 0.10.0", - "syn 2.0.48", + "syn 2.0.77", "thiserror", ] @@ -3410,7 +3194,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "718f6cd8c54ae5249fd42b0c86639df0100b8a86eea2e5f1b915cde2e1481453" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.5.0", "lalrpop-util", "logos", ] @@ -3422,9 +3206,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a69ecdf4aa110fed1c0c8de290bc8ccb2835388733cf2f418f0abdf6ff3899" dependencies = [ "cynic-codegen", - "darling 0.20.3", - "quote 1.0.35", - "syn 2.0.48", + "darling 0.20.10", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -3439,12 +3223,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ - "darling_core 0.14.2", - "darling_macro 0.14.2", + "darling_core 0.14.4", + "darling_macro 0.14.4", ] [[package]] @@ -3465,24 +3249,24 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "strsim 0.10.0", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "darling_core" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "strsim 0.10.0", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -3493,10 +3277,10 @@ checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "strsim 0.11.1", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] @@ -3506,19 +3290,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core 0.13.4", - "quote 1.0.35", - "syn 1.0.107", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] name = "darling_macro" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ - "darling_core 0.14.2", - "quote 1.0.35", - "syn 1.0.107", + "darling_core 0.14.4", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -3528,8 +3312,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", - "quote 1.0.35", - "syn 2.0.48", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -3542,20 +3326,20 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.9", + "parking_lot_core 0.9.10", ] [[package]] name = "data-encoding" -version = "2.3.3" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "data-encoding-macro" -version = "0.1.12" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86927b7cd2fe88fa698b87404b287ab98d1a0063a34071d92e575b72d3029aca" +checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -3563,12 +3347,12 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.10" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5bbed42daaa95e780b60a50546aa345b8413a1e46f9a40a12907d3598f038db" +checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f" dependencies = [ "data-encoding", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -3593,7 +3377,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" dependencies = [ - "uuid 1.2.2", + "uuid 1.10.0", ] [[package]] @@ -3609,9 +3393,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.5" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e58dffcdcc8ee7b22f0c1f71a69243d7c2d9ad87b5a14361f2424a1565c219" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "pem-rfc7468 0.7.0", @@ -3620,14 +3404,14 @@ dependencies = [ [[package]] name = "der-parser" -version = "8.1.0" +version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" dependencies = [ "asn1-rs", "displaydoc", "nom", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-traits", "rusticata-macros", ] @@ -3648,9 +3432,9 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -3659,20 +3443,20 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] name = "derive_arbitrary" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cdeb9ec472d588e539a818b2dee436825730da08ad0017c4b1a17676bdc8b7" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -3690,10 +3474,10 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" dependencies = [ - "darling 0.14.2", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "darling 0.14.4", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -3703,20 +3487,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" dependencies = [ "derive_builder_core", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case", - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "rustc_version", - "syn 1.0.107", + "syn 2.0.77", ] [[package]] @@ -3738,11 +3522,11 @@ dependencies = [ [[package]] name = "diesel" -version = "2.2.3" +version = "2.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e13bab2796f412722112327f3e575601a3e9cdcbe426f0d30dbf43f3f5dc71" +checksum = "158fe8e2e68695bd615d7e4f3227c0727b151330d3e253b525086c348d055d5e" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "byteorder", "chrono", "diesel_derives", @@ -3757,14 +3541,14 @@ dependencies = [ [[package]] name = "diesel-derive-enum" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b10c03b954333d05bfd5be1d8a74eae2c9ca77b86e0f1c3a1ea29c49da1d6c2" +checksum = "81c5131a2895ef64741dad1d483f358c2a229a3a2d1b256778cdc5e146db64d4" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -3775,9 +3559,9 @@ checksum = "e7f2c3de51e2ba6bf2a648285696137aaf0f5f487bcbea93972fe8a364e131a4" dependencies = [ "diesel_table_macro_syntax", "dsl_auto_type", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -3797,7 +3581,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" dependencies = [ - "syn 2.0.48", + "syn 2.0.77", ] [[package]] @@ -3833,7 +3617,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e616e59155c92257e84970156f506287853355f58cd4a6eb167385722c32b790" dependencies = [ - "nu-ansi-term", + "nu-ansi-term 0.46.0", +] + +[[package]] +name = "diffy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d3041965b7a63e70447ec818a46b1e5297f7fcae3058356d226c02750c4e6cb" +dependencies = [ + "nu-ansi-term 0.50.1", ] [[package]] @@ -3851,7 +3644,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.3", + "block-buffer 0.10.4", "const-oid", "crypto-common", "subtle", @@ -3921,20 +3714,20 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "dissimilar" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5f0c7e4bd266b8ab2550e6238d2e74977c23c15536ac7be45e9c95e2e3fbbb" +checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" [[package]] name = "doc-comment" @@ -3956,8 +3749,8 @@ dependencies = [ "containers-api", "docker-api-stubs", "futures-util", - "http 0.2.9", - "hyper 0.14.26", + "http 0.2.12", + "hyper 0.14.30", "log", "paste", "serde", @@ -3976,7 +3769,7 @@ dependencies = [ "chrono", "serde", "serde_json", - "serde_with 2.1.0", + "serde_with 2.3.3", ] [[package]] @@ -3993,9 +3786,9 @@ checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "downcast-rs" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dsl_auto_type" @@ -4003,19 +3796,19 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607" dependencies = [ - "darling 0.20.3", + "darling 0.20.10", "either", "heck 0.5.0", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "dunce" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "duration-str" @@ -4033,9 +3826,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "ecdsa" @@ -4051,15 +3844,16 @@ dependencies = [ [[package]] name = "ecdsa" -version = "0.16.6" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a48e5d537b8a30c0b023116d981b16334be1485af7ca68db3a2b7024cbc957fd" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der 0.7.5", + "der 0.7.9", "digest 0.10.7", "elliptic-curve 0.13.8", "rfc6979 0.4.0", - "signature 2.0.0", + "signature 2.2.0", + "spki 0.7.3", ] [[package]] @@ -4080,7 +3874,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8 0.10.2", - "signature 2.0.0", + "signature 2.2.0", ] [[package]] @@ -4108,21 +3902,17 @@ dependencies = [ "ed25519 2.2.3", "rand_core 0.6.4", "serde", -<<<<<<< HEAD - "sha2 0.10.6", - "signature 2.0.0", - "subtle", -======= "sha2 0.10.8", ->>>>>>> upstream/mainnet + "signature 2.2.0", + "subtle", "zeroize", ] [[package]] name = "either" -version = "1.8.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elliptic-curve" @@ -4151,7 +3941,7 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct 0.2.0", "base64ct", - "crypto-bigint 0.5.1", + "crypto-bigint 0.5.5", "digest 0.10.7", "ff 0.13.0", "generic-array", @@ -4159,7 +3949,7 @@ dependencies = [ "pem-rfc7468 0.7.0", "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1 0.7.1", + "sec1 0.7.3", "serde_json", "serdect", "subtle", @@ -4189,9 +3979,9 @@ checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -4211,32 +4001,44 @@ dependencies = [ "base64 0.21.7", "bytes", "hex", - "k256 0.13.1", + "k256 0.13.3", "log", "rand 0.8.5", "rlp", "serde", - "sha3 0.10.6", + "sha3 0.10.8", "zeroize", ] +[[package]] +name = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck 0.5.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + [[package]] name = "enum-compat-util" version = "0.1.0" dependencies = [ - "serde_yaml", + "serde_yaml 0.8.26", ] [[package]] name = "enum_dispatch" -version = "0.3.9" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1693044dcf452888dd3a6a6a0dab67f0652094e3920dfe029a54d2f37d9b7394" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" dependencies = [ "once_cell", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -4289,23 +4091,14 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", ] -[[package]] -name = "error-chain" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" -dependencies = [ - "version_check", -] - [[package]] name = "error-code" version = "2.3.1" @@ -4333,7 +4126,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.8", - "sha3 0.10.6", + "sha3 0.10.8", "thiserror", "uuid 0.8.2", ] @@ -4350,7 +4143,7 @@ dependencies = [ "regex", "serde", "serde_json", - "sha3 0.10.6", + "sha3 0.10.8", "thiserror", "uint", ] @@ -4445,21 +4238,15 @@ dependencies = [ "ethers-core", "ethers-etherscan", "eyre", -<<<<<<< HEAD - "getrandom 0.2.15", - "hex", - "prettyplease 0.1.25", -======= "prettyplease", ->>>>>>> upstream/mainnet - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "regex", - "reqwest 0.11.20", + "reqwest 0.11.27", "serde", "serde_json", - "syn 2.0.48", - "toml 0.8.16", + "syn 2.0.77", + "toml 0.8.19", "walkdir", ] @@ -4473,10 +4260,10 @@ dependencies = [ "const-hex", "ethers-contract-abigen", "ethers-core", - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "serde_json", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] @@ -4485,7 +4272,7 @@ version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82d80cc6ad30b14a48ab786523af33b37f28a8623fc06afd55324816ef18fb1f" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.6", "bytes", "cargo_metadata 0.18.1", "chrono", @@ -4493,12 +4280,7 @@ dependencies = [ "elliptic-curve 0.13.8", "ethabi", "generic-array", -<<<<<<< HEAD - "getrandom 0.2.15", - "hex", -======= ->>>>>>> upstream/mainnet - "k256 0.13.1", + "k256 0.13.3", "num_enum 0.7.3", "once_cell", "open-fastrlp", @@ -4507,11 +4289,11 @@ dependencies = [ "serde", "serde_json", "strum 0.26.3", - "syn 2.0.48", + "syn 2.0.77", "tempfile", "thiserror", "tiny-keccak", - "unicode-xid 0.2.4", + "unicode-xid 0.2.5", ] [[package]] @@ -4522,12 +4304,8 @@ checksum = "e79e5973c26d4baf0ce55520bd732314328cabe53193286671b47144145b9649" dependencies = [ "chrono", "ethers-core", -<<<<<<< HEAD - "getrandom 0.2.15", -======= ->>>>>>> upstream/mainnet - "reqwest 0.11.20", - "semver 1.0.23", + "reqwest 0.11.27", + "semver", "serde", "serde_json", "thiserror", @@ -4551,7 +4329,7 @@ dependencies = [ "futures-locks", "futures-util", "instant", - "reqwest 0.11.20", + "reqwest 0.11.27", "serde", "serde_json", "thiserror", @@ -4578,17 +4356,13 @@ dependencies = [ "futures-core", "futures-timer", "futures-util", -<<<<<<< HEAD - "getrandom 0.2.15", -======= ->>>>>>> upstream/mainnet "hashers", - "http 0.2.9", + "http 0.2.12", "instant", - "jsonwebtoken", + "jsonwebtoken 8.3.0", "once_cell", "pin-project", - "reqwest 0.11.20", + "reqwest 0.11.27", "serde", "serde_json", "thiserror", @@ -4641,7 +4415,7 @@ dependencies = [ "path-slash", "rayon", "regex", - "semver 1.0.23", + "semver", "serde", "serde_json", "solang-parser", @@ -4656,9 +4430,9 @@ dependencies = [ [[package]] name = "ethnum" -version = "1.3.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0198b9d0078e0f30dedc7acbb21c974e838fc8fae3ee170128658a98cb2c1c04" +checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" [[package]] name = "event-listener" @@ -4668,9 +4442,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "expect-test" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3" +checksum = "9e0be0a561335815e06dab7c62e50353134c796e7a6155402a64bcff66b6a5e0" dependencies = [ "dissimilar", "once_cell", @@ -4678,9 +4452,9 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.8" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ "indenter", "once_cell", @@ -4720,7 +4494,7 @@ dependencies = [ "curve25519-dalek-ng", "derive_more", "digest 0.10.7", - "ecdsa 0.16.6", + "ecdsa 0.16.9", "ed25519-consensus", "elliptic-curve 0.13.8", "fastcrypto-derive", @@ -4729,7 +4503,7 @@ dependencies = [ "hex-literal 0.4.1", "hkdf", "lazy_static", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "once_cell", "p256", "rand 0.8.5", @@ -4742,8 +4516,8 @@ dependencies = [ "serde_json", "serde_with 3.9.0", "sha2 0.10.8", - "sha3 0.10.6", - "signature 2.0.0", + "sha3 0.10.8", + "signature 2.2.0", "static_assertions", "thiserror", "tokio", @@ -4756,8 +4530,8 @@ name = "fastcrypto-derive" version = "0.1.3" source = "git+https://github.com/MystenLabs/fastcrypto?rev=5f2c63266a065996d53f98156f0412782b468597#5f2c63266a065996d53f98156f0412782b468597" dependencies = [ - "quote 1.0.35", - "syn 1.0.107", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -4772,7 +4546,7 @@ dependencies = [ "itertools 0.10.5", "rand 0.8.5", "serde", - "sha3 0.10.6", + "sha3 0.10.8", "tap", "tracing", "typenum", @@ -4787,7 +4561,7 @@ dependencies = [ "bcs", "fastcrypto", "lazy_static", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-integer", "num-prime", "num-traits", @@ -4818,10 +4592,10 @@ dependencies = [ "itertools 0.12.1", "lazy_static", "neptune", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "once_cell", "regex", - "reqwest 0.12.4", + "reqwest 0.12.7", "schemars", "serde", "serde_json", @@ -4830,18 +4604,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" -dependencies = [ - "instant", -] - -[[package]] -name = "fastrand" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fcm" @@ -4852,7 +4617,7 @@ dependencies = [ "chrono", "erased-serde", "log", - "reqwest 0.11.20", + "reqwest 0.11.27", "serde", "serde_json", ] @@ -4864,7 +4629,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" dependencies = [ "cfg-if", - "rustix 0.38.34", + "rustix", "windows-sys 0.48.0", ] @@ -4911,9 +4676,9 @@ dependencies = [ "num-bigint 0.3.3", "num-integer", "num-traits", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -4929,21 +4694,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cca4fdab1b9b7e274e7de51202e37f9cfa542b28c77f8d09b817d77a726b4807" dependencies = [ "darling 0.13.4", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", + "libredox", + "windows-sys 0.59.0", ] [[package]] @@ -4964,7 +4729,7 @@ version = "0.1.0" source = "git+ssh://git@github.com/nightly-labs/alexandria.git?rev=bd401bc3807bba5bb5203f42cc7b8cf836be83f5#bd401bc3807bba5bb5203f42cc7b8cf836be83f5" dependencies = [ "fcm", - "reqwest 0.12.4", + "reqwest 0.12.7", "serde", "serde_json", "structs", @@ -5019,12 +4784,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.8.0", ] [[package]] @@ -5059,9 +4824,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -5111,9 +4876,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -5126,9 +4891,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -5136,15 +4901,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -5153,24 +4918,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" - -[[package]] -name = "futures-lite" -version = "1.12.0" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" -dependencies = [ - "fastrand 1.8.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-locks" @@ -5184,32 +4934,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" dependencies = [ "gloo-timers", "send_wrapper 0.4.0", @@ -5217,9 +4967,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -5280,9 +5030,9 @@ dependencies = [ [[package]] name = "ghash" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ "opaque-debug", "polyval", @@ -5296,24 +5046,22 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "git-version" -version = "0.3.5" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b0decc02f4636b9ccad390dcbe77b722a77efedfa393caf8379a51d5c61899" +checksum = "1ad568aa3db0fcbc81f2f116137f263d7304f512a1209b35b85150d3ef88ad19" dependencies = [ "git-version-macro", - "proc-macro-hack", ] [[package]] name = "git-version-macro" -version = "0.3.5" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe69f1cbdb6e28af2bac214e943b99ce8a0a06b447d15d3e61161b0423139f3f" +checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ - "proc-macro-hack", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -5324,15 +5072,15 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" dependencies = [ "aho-corasick", "bstr", "log", "regex-automata 0.4.7", - "regex-syntax 0.8.2", + "regex-syntax 0.8.4", ] [[package]] @@ -5349,9 +5097,9 @@ dependencies = [ [[package]] name = "governor" -version = "0.6.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "821239e5672ff23e2a7060901fa622950bbd80b649cdaadd78d1c1767ed14eb4" +checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b" dependencies = [ "cfg-if", "dashmap", @@ -5359,10 +5107,12 @@ dependencies = [ "futures-timer", "no-std-compat", "nonzero_ext", - "parking_lot 0.12.1", + "parking_lot 0.12.3", + "portable-atomic", "quanta", "rand 0.8.5", "smallvec", + "spinning_top", ] [[package]] @@ -5403,14 +5153,14 @@ dependencies = [ "fixedbitset 0.4.2", "guppy-summaries", "guppy-workspace-hack", - "indexmap 2.2.6", + "indexmap 2.5.0", "itertools 0.13.0", "nested", "once_cell", "pathdiff", "petgraph 0.6.5", "rayon", - "semver 1.0.23", + "semver", "serde", "serde_json", "smallvec", @@ -5429,7 +5179,7 @@ dependencies = [ "cfg-if", "diffus", "guppy-workspace-hack", - "semver 1.0.23", + "semver", "serde", "toml 0.5.11", ] @@ -5451,19 +5201,19 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 0.2.9", - "indexmap 2.2.6", + "http 0.2.12", + "indexmap 2.5.0", "slab", "tokio", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.7.12", "tracing", ] [[package]] name = "h2" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ "atomic-waker", "bytes", @@ -5471,24 +5221,18 @@ dependencies = [ "futures-core", "futures-sink", "http 1.1.0", - "indexmap 2.2.6", + "indexmap 2.5.0", "slab", "tokio", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.7.12", "tracing", ] [[package]] name = "hakari" -<<<<<<< HEAD -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af0223111b69beda15417ad6a960bffb093c916f0eaa559036c7036efa2d199" -======= -version = "0.17.3" +version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bd2b14c094d2793daf279eb7624f4525e26f555fbc1647613756cf83f44755" ->>>>>>> upstream/mainnet +checksum = "f3db4569d65cb4bc06dbcd78e4dd9771e266e75e27de868fa369e995fbb8c267" dependencies = [ "ahash 0.8.11", "atomicwrites", @@ -5496,12 +5240,12 @@ dependencies = [ "camino", "cfg-if", "debug-ignore", - "diffy", + "diffy 0.4.0", "guppy", "guppy-workspace-hack", "include_dir", "indenter", - "itertools 0.12.1", + "itertools 0.13.0", "owo-colors 3.5.0", "pathdiff", "rayon", @@ -5513,12 +5257,6 @@ dependencies = [ "twox-hash", ] -[[package]] -name = "half" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" - [[package]] name = "half" version = "2.4.1" @@ -5569,11 +5307,11 @@ dependencies = [ [[package]] name = "hdrhistogram" -version = "7.5.2" +version = "7.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f19b9f54f7c7f55e31401bb647626ce0cf0f67b0004982ce815b3ee72a02aa8" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" dependencies = [ - "base64 0.13.1", + "base64 0.21.7", "byteorder", "crossbeam-channel", "flate2", @@ -5619,9 +5357,15 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "hex" @@ -5646,9 +5390,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "hkdf" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ "hmac 0.12.1", ] @@ -5689,9 +5433,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -5711,12 +5455,12 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http 0.2.9", + "http 0.2.12", "pin-project-lite", ] @@ -5751,15 +5495,15 @@ checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" @@ -5769,22 +5513,22 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", "h2 0.3.26", - "http 0.2.9", - "http-body 0.4.5", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2 0.5.7", "tokio", "tower-service", "tracing", @@ -5800,7 +5544,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.5", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "httparse", @@ -5818,11 +5562,11 @@ version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ - "http 0.2.9", - "hyper 0.14.26", + "http 0.2.12", + "hyper 0.14.30", "log", "rustls 0.20.9", - "rustls-native-certs 0.6.2", + "rustls-native-certs 0.6.3", "tokio", "tokio-rustls 0.23.4", "webpki-roots 0.22.6", @@ -5830,34 +5574,37 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ - "http 0.2.9", - "hyper 0.14.26", + "futures-util", + "http 0.2.12", + "hyper 0.14.30", "log", "rustls 0.21.12", - "rustls-native-certs 0.6.2", + "rustls-native-certs 0.6.3", "tokio", "tokio-rustls 0.24.1", ] [[package]] name = "hyper-rustls" -version = "0.26.0" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", "hyper 1.4.1", "hyper-util", - "rustls 0.22.4", + "rustls 0.23.13", + "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.0", "tower-service", + "webpki-roots 0.26.5", ] [[package]] @@ -5866,17 +5613,13 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper 0.14.26", + "hyper 0.14.30", "pin-project-lite", "tokio", "tokio-io-timeout", ] [[package]] -<<<<<<< HEAD -name = "hyper-tls" -version = "0.5.0" -======= name = "hyper-timeout" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5890,14 +5633,13 @@ dependencies = [ ] [[package]] -name = "hyper-util" -version = "0.1.6" ->>>>>>> upstream/mainnet +name = "hyper-tls" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper 0.14.26", + "hyper 0.14.30", "native-tls", "tokio", "tokio-native-tls", @@ -5921,9 +5663,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" dependencies = [ "bytes", "futures-channel", @@ -5932,7 +5674,7 @@ dependencies = [ "http-body 1.0.1", "hyper 1.4.1", "pin-project-lite", - "socket2 0.5.6", + "socket2 0.5.7", "tokio", "tower", "tower-service", @@ -5947,33 +5689,32 @@ checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c" dependencies = [ "futures-util", "hex", - "hyper 0.14.26", + "hyper 0.14.30", "pin-project", "tokio", ] [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows-core", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] @@ -5982,16 +5723,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.5.0" @@ -6037,7 +5768,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" dependencies = [ - "parity-scale-codec 3.6.5", + "parity-scale-codec 3.6.12", ] [[package]] @@ -6073,9 +5804,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -6094,8 +5825,8 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", ] [[package]] @@ -6117,9 +5848,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown 0.14.5", @@ -6128,13 +5859,14 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.2" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4295cbb7573c16d310e99e713cf9e75101eb190ab31fccd35f2d2691b4352b19" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" dependencies = [ "console", + "instant", "number_prefix", - "portable-atomic 0.3.19", + "portable-atomic", "unicode-width", ] @@ -6164,7 +5896,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "block-padding 0.3.2", + "block-padding 0.3.3", "generic-array", ] @@ -6186,9 +5918,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.26.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f0f08b46e4379744de2ab67aa8f7de3ffd1da3e275adc41fcc82053ede46ff" +checksum = "6593a41c7a73841868772495db7dc1e8ecab43bb5c0b6da2059246c4b506ab60" dependencies = [ "console", "lazy_static", @@ -6197,14 +5929,13 @@ dependencies = [ "pest_derive", "serde", "similar", - "yaml-rust", ] [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", "js-sys", @@ -6218,21 +5949,11 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" -[[package]] -name = "io-lifetimes" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" -dependencies = [ - "libc", - "windows-sys 0.42.0", -] - [[package]] name = "ipnet" -version = "2.7.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" [[package]] name = "ipnetwork" @@ -6245,9 +5966,9 @@ dependencies = [ [[package]] name = "iri-string" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f5f6c2df22c009ac44f6f1499308e7a3ac7ba42cd2378475cc691510e1eef1b" +checksum = "3e0f755bd3806e06ad4f366f92639415d99a339a2c7ecf8c26ccea2097c11cb6" dependencies = [ "memchr", "serde", @@ -6264,14 +5985,13 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.7" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ - "hermit-abi", - "io-lifetimes", - "rustix 0.37.7", - "windows-sys 0.48.0", + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", ] [[package]] @@ -6290,6 +6010,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -6328,15 +6054,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jemalloc-ctl" -version = "0.5.0" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1891c671f3db85d8ea8525dd43ab147f9977041911d24a03e5a36187a7bfde9" +checksum = "7cffc705424a344c054e135d12ee591402f4539245e8bbd64e6c9eaa9458b63c" dependencies = [ "jemalloc-sys", "libc", @@ -6345,29 +6071,28 @@ dependencies = [ [[package]] name = "jemalloc-sys" -version = "0.5.2+5.3.0-patched" +version = "0.5.4+5.3.0-patched" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134163979b6eed9564c98637b710b40979939ba351f59952708234ea11b5f3f8" +checksum = "ac6c1946e1cea1788cbfde01c993b52a10e2da07f4bac608228d1bed20bfebf2" dependencies = [ "cc", - "fs_extra", "libc", ] [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -6418,16 +6143,16 @@ version = "0.16.2" source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" dependencies = [ "futures-util", - "http 0.2.9", + "http 0.2.12", "jsonrpsee-core", "jsonrpsee-types", "pin-project", - "rustls-native-certs 0.6.2", + "rustls-native-certs 0.6.3", "soketto", "thiserror", "tokio", "tokio-rustls 0.23.4", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.7.12", "tracing", "webpki-roots 0.22.6", ] @@ -6438,7 +6163,7 @@ version = "0.16.2" source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" dependencies = [ "anyhow", - "arrayvec 0.7.2", + "arrayvec 0.7.6", "async-lock", "async-trait", "beef", @@ -6446,9 +6171,9 @@ dependencies = [ "futures-timer", "futures-util", "globset", - "hyper 0.14.26", + "hyper 0.14.30", "jsonrpsee-types", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "rand 0.8.5", "rustc-hash 1.1.0", "serde", @@ -6465,7 +6190,7 @@ version = "0.16.2" source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" dependencies = [ "async-trait", - "hyper 0.14.26", + "hyper 0.14.30", "hyper-rustls 0.23.2", "jsonrpsee-core", "jsonrpsee-types", @@ -6483,10 +6208,10 @@ version = "0.16.2" source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" dependencies = [ "heck 0.4.1", - "proc-macro-crate", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro-crate 1.1.3", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -6496,8 +6221,8 @@ source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcd dependencies = [ "futures-channel", "futures-util", - "http 0.2.9", - "hyper 0.14.26", + "http 0.2.12", + "hyper 0.14.30", "jsonrpsee-core", "jsonrpsee-types", "serde", @@ -6505,7 +6230,7 @@ dependencies = [ "soketto", "tokio", "tokio-stream", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.7.12", "tower", "tracing", ] @@ -6528,28 +6253,36 @@ name = "jsonrpsee-ws-client" version = "0.16.2" source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" dependencies = [ - "http 0.2.9", + "http 0.2.12", "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-types", ] +[[package]] +name = "jsonwebtoken" +version = "8.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" +dependencies = [ + "base64 0.21.7", + "pem 1.1.1", + "ring 0.16.20", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "jsonwebtoken" version = "9.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" dependencies = [ -<<<<<<< HEAD - "base64 0.21.2", + "base64 0.21.7", "js-sys", "pem 3.0.4", "ring 0.17.8", -======= - "base64 0.21.7", - "pem 1.1.0", - "ring 0.16.20", ->>>>>>> upstream/mainnet "serde", "serde_json", "simple_asn1", @@ -6565,28 +6298,28 @@ dependencies = [ "ecdsa 0.14.8", "elliptic-curve 0.12.3", "sha2 0.10.8", - "sha3 0.10.6", + "sha3 0.10.8", ] [[package]] name = "k256" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ "cfg-if", - "ecdsa 0.16.6", + "ecdsa 0.16.9", "elliptic-curve 0.13.8", "once_cell", "sha2 0.10.8", - "signature 2.0.0", + "signature 2.2.0", ] [[package]] name = "keccak" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures", ] @@ -6624,11 +6357,11 @@ dependencies = [ "lalrpop-util", "petgraph 0.6.5", "regex", - "regex-syntax 0.8.2", + "regex-syntax 0.8.4", "string_cache", "term", "tiny-keccak", - "unicode-xid 0.2.4", + "unicode-xid 0.2.5", "walkdir", ] @@ -6643,11 +6376,11 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.5.2", + "spin 0.9.8", ] [[package]] @@ -6728,25 +6461,36 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libloading" -version = "0.7.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "winapi", + "windows-targets 0.52.6", ] [[package]] name = "libm" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall 0.5.4", +] [[package]] name = "librocksdb-sys" @@ -6777,24 +6521,15 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.8" +version = "1.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" dependencies = [ "cc", "pkg-config", "vcpkg", ] -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - [[package]] name = "linked-hash-map" version = "0.5.6" @@ -6803,21 +6538,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" - -[[package]] -name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -6825,9 +6554,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" dependencies = [ "serde", ] @@ -6850,10 +6579,10 @@ dependencies = [ "beef", "fnv", "lazy_static", - "proc-macro2 1.0.78", - "quote 1.0.35", - "regex-syntax 0.8.2", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "regex-syntax 0.8.4", + "syn 2.0.77", ] [[package]] @@ -6867,27 +6596,27 @@ dependencies = [ [[package]] name = "lru" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03f1160296536f10c833a82dca22267d5486734230d47bf00bf435885814ba1e" +checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" dependencies = [ "hashbrown 0.13.2", ] [[package]] name = "lru" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ "hashbrown 0.14.5", ] [[package]] name = "lsp-server" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248f65b78f6db5d8e1b1604b4098a28b43d21a8eb1deeca22b1c421b276c7095" +checksum = "550446e84739dcaf6d48a4a093973850669e13e8a34d8f8d64851041be267cd9" dependencies = [ "crossbeam-channel", "log", @@ -6910,23 +6639,14 @@ dependencies = [ [[package]] name = "lz4-sys" -version = "1.9.4" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +checksum = "109de74d5d2353660401699a4174a4ff23fcc649caf553df71933c7fb45ad868" dependencies = [ "cc", "libc", ] -[[package]] -name = "mach2" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" -dependencies = [ - "libc", -] - [[package]] name = "maplit" version = "1.0.2" @@ -6956,9 +6676,9 @@ checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" [[package]] name = "matchit" -version = "0.7.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "md-5" @@ -6995,9 +6715,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" -version = "0.5.8" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" dependencies = [ "libc", ] @@ -7021,7 +6741,7 @@ dependencies = [ "backtrace-ext", "cfg-if", "miette-derive", - "owo-colors 4.0.0", + "owo-colors 4.1.0", "supports-color", "supports-hyperlinks", "supports-unicode", @@ -7037,9 +6757,9 @@ version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -7049,7 +6769,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff" dependencies = [ "serde", - "toml 0.8.16", + "toml 0.8.19", ] [[package]] @@ -7059,8 +6779,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd" dependencies = [ "migrations_internals", - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", ] [[package]] @@ -7071,9 +6791,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ "mime", "unicase", @@ -7087,13 +6807,22 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + [[package]] name = "mio" version = "0.8.11" @@ -7107,17 +6836,29 @@ dependencies = [ ] [[package]] -name = "mockall" -version = "0.11.4" +name = "mio" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "mockall" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" dependencies = [ "cfg-if", "downcast", "fragile", "lazy_static", "mockall_derive", - "predicates", + "predicates 2.1.5", "predicates-tree", ] @@ -7128,29 +6869,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] name = "moka" -version = "0.12.5" +version = "0.12.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1911e88d5831f748a4097a43862d129e3c6fca831eecac9b8db6d01d93c9de2" +checksum = "32cf62eb4dd975d2dde76432fb1075c49e3ee2331cf36f1f8fd4b66550d32b6f" dependencies = [ "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "rustc_version", - "skeptic", "smallvec", "tagptr", "thiserror", "triomphe", - "uuid 1.2.2", + "uuid 1.10.0", ] [[package]] @@ -7358,7 +7098,7 @@ dependencies = [ "move-vm-runtime", "move-vm-test-utils", "move-vm-types", - "serde_yaml", + "serde_yaml 0.8.26", "tempfile", "toml_edit 0.14.4", "walkdir", @@ -7375,7 +7115,7 @@ dependencies = [ "hex", "move-binary-format", "move-core-types", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "once_cell", "serde", "sha2 0.9.9", @@ -7581,7 +7321,7 @@ dependencies = [ "petgraph 0.5.1", "regex", "serde", - "serde_yaml", + "serde_yaml 0.8.26", "sha2 0.9.9", "tempfile", "toml 0.5.11", @@ -7597,8 +7337,8 @@ name = "move-proc-macros" version = "0.1.0" dependencies = [ "enum-compat-util", - "quote 1.0.35", - "syn 2.0.48", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -7892,9 +7632,9 @@ dependencies = [ "rand 0.8.5", "real_tokio", "serde", - "socket2 0.4.9", + "socket2 0.4.10", "tap", - "tokio-util 0.7.10 (git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e47aafebf98e9c1734a8848a1876d5946c44bdd1)", + "tokio-util 0.7.10", "toml 0.5.11", "tracing", "tracing-subscriber", @@ -7905,41 +7645,22 @@ name = "msim-macros" version = "0.1.0" source = "git+https://github.com/MystenLabs/mysten-sim.git?rev=b320996d8dfb99b273fe31c0222c659332283c99#b320996d8dfb99b273fe31c0222c659332283c99" dependencies = [ - "darling 0.14.2", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", -] - -[[package]] -<<<<<<< HEAD -======= -name = "multer" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" -dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http 1.1.0", - "httparse", - "memchr", - "mime", - "spin 0.9.8", - "version_check", + "darling 0.14.4", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] ->>>>>>> upstream/mainnet name = "multiaddr" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b53e0cc5907a5c216ba6584bf74be8ab47d6d6289f72793b2dddbf15dc3bf8c" +checksum = "2b36f567c7099511fa8612bbbb52dda2419ce0bdbacf31714e3a5ffdb766d3bd" dependencies = [ "arrayref", "byteorder", "data-encoding", + "log", "multibase", "multihash", "percent-encoding", @@ -7977,25 +7698,25 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro-error", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", "synstructure", ] [[package]] name = "multimap" -version = "0.8.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" [[package]] name = "mysqlclient-sys" -version = "0.2.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61b381528ba293005c42a409dd73d034508e273bf90481f17ec2e964a6e969b" +checksum = "478e2040dbc35c73927b77a2be91a496de19deab376a6982ed61e89592434619" dependencies = [ "pkg-config", "vcpkg", @@ -8006,7 +7727,7 @@ name = "mysten-common" version = "0.1.0" dependencies = [ "futures", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "tokio", ] @@ -8019,7 +7740,7 @@ dependencies = [ "dashmap", "futures", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "prometheus", "prometheus-closure-metric", "scopeguard", @@ -8027,7 +7748,7 @@ dependencies = [ "tap", "tokio", "tracing", - "uuid 1.2.2", + "uuid 1.10.0", ] [[package]] @@ -8046,7 +7767,7 @@ dependencies = [ "snap", "tokio", "tokio-stream", - "tonic 0.12.1", + "tonic 0.12.2", "tonic-health", "tower", "tower-http", @@ -8080,10 +7801,10 @@ dependencies = [ "fastcrypto-tbls", "hashbrown 0.12.3", "impl-trait-for-tuples", - "indexmap 2.2.6", + "indexmap 2.5.0", "mysten-util-mem-derive", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "roaring", "smallvec", ] @@ -8092,8 +7813,8 @@ dependencies = [ name = "mysten-util-mem-derive" version = "0.1.0" dependencies = [ - "proc-macro2 1.0.78", - "syn 1.0.107", + "proc-macro2 1.0.86", + "syn 1.0.109", "synstructure", ] @@ -8111,7 +7832,7 @@ checksum = "40a3eb6b7c682b65d1f631ec3176829d72ab450b3aacdd3f719bf220822e59ac" dependencies = [ "libc", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "thiserror", "widestring", "winapi", @@ -8163,7 +7884,7 @@ dependencies = [ "bytes", "fastcrypto", "futures", - "indexmap 2.2.6", + "indexmap 2.5.0", "mockall", "mysten-metrics", "narwhal-config", @@ -8181,7 +7902,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", - "tonic 0.12.1", + "tonic 0.12.2", "tracing", "typed-store", ] @@ -8205,7 +7926,7 @@ dependencies = [ "narwhal-crypto", "narwhal-test-utils", "narwhal-types", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "prometheus", "quinn-proto", "rand 0.8.5", @@ -8243,9 +7964,9 @@ dependencies = [ "pretty_assertions", "prometheus", "rand 0.8.5", - "reqwest 0.12.4", + "reqwest 0.12.7", "serde-reflection", - "serde_yaml", + "serde_yaml 0.8.26", "sui-keys", "sui-protocol-config", "sui-types", @@ -8277,7 +7998,7 @@ dependencies = [ "fastcrypto", "futures", "governor", - "indexmap 2.2.6", + "indexmap 2.5.0", "itertools 0.10.5", "mockall", "mysten-common", @@ -8293,11 +8014,11 @@ dependencies = [ "narwhal-types", "narwhal-worker", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "prometheus", "proptest", "rand 0.8.5", - "reqwest 0.12.4", + "reqwest 0.12.7", "sui-macros", "sui-protocol-config", "tap", @@ -8318,13 +8039,13 @@ dependencies = [ "fastcrypto", "fastcrypto-tbls", "futures", - "lru 0.10.0", + "lru 0.10.1", "mysten-common", "mysten-metrics", "narwhal-config", "narwhal-test-utils", "narwhal-types", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "prometheus", "sui-macros", "tap", @@ -8341,7 +8062,7 @@ dependencies = [ "anemo", "fastcrypto", "fdlimit", - "indexmap 2.2.6", + "indexmap 2.5.0", "itertools 0.10.5", "mysten-metrics", "mysten-network", @@ -8361,7 +8082,7 @@ dependencies = [ "telemetry-subscribers", "tempfile", "tokio", - "tonic 0.12.1", + "tonic 0.12.2", "tracing", "typed-store", ] @@ -8381,7 +8102,7 @@ dependencies = [ "enum_dispatch", "fastcrypto", "futures", - "indexmap 2.2.6", + "indexmap 2.5.0", "mockall", "mysten-common", "mysten-metrics", @@ -8394,7 +8115,7 @@ dependencies = [ "prometheus", "proptest", "proptest-derive", - "prost 0.13.1", + "prost 0.13.2", "rand 0.8.5", "roaring", "serde", @@ -8403,7 +8124,7 @@ dependencies = [ "sui-protocol-config", "thiserror", "tokio", - "tonic 0.12.1", + "tonic 0.12.2", "tonic-build", "tracing", "typed-store", @@ -8437,14 +8158,14 @@ dependencies = [ "narwhal-types", "prometheus", "rand 0.8.5", - "reqwest 0.12.4", + "reqwest 0.12.7", "sui-protocol-config", "tap", "telemetry-subscribers", "tempfile", "thiserror", "tokio", - "tonic 0.12.1", + "tonic 0.12.2", "tower", "tracing", "typed-store", @@ -8529,7 +8250,7 @@ source = "git+https://github.com/nextest-rs/nexlint.git?rev=7ce56bd591242a57660e dependencies = [ "anyhow", "camino", - "diffy", + "diffy 0.3.0", "globset", "guppy", "nexlint", @@ -8635,7 +8356,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -8643,7 +8364,7 @@ dependencies = [ "kqueue", "libc", "log", - "mio", + "mio 0.8.11", "walkdir", "windows-sys 0.48.0", ] @@ -8659,9 +8380,9 @@ dependencies = [ [[package]] name = "ntest" -version = "0.9.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da8ec6d2b73d45307e926f5af46809768581044384637af6b3f3fe7c3c88f512" +checksum = "fb183f0a1da7a937f672e5ee7b7edb727bf52b8a52d531374ba8ebb9345c0330" dependencies = [ "ntest_test_cases", "ntest_timeout", @@ -8669,25 +8390,25 @@ dependencies = [ [[package]] name = "ntest_test_cases" -version = "0.9.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be7d33be719c6f4d09e64e27c1ef4e73485dc4cc1f4d22201f89860a7fe22e22" +checksum = "16d0d3f2a488592e5368ebbe996e7f1d44aa13156efad201f5b4d84e150eaa93" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] name = "ntest_timeout" -version = "0.9.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "066b468120587a402f0b47d8f80035c921f6a46f8209efd0632a89a16f5188a4" +checksum = "fcc7c92f190c97f79b4a332f5e81dcf68c8420af2045c936c9be0bc9de6f63b5" dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro-crate 3.2.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -8700,6 +8421,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "nu-ansi-term" +version = "0.50.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "nuid" version = "0.5.0" @@ -8711,11 +8441,11 @@ dependencies = [ [[package]] name = "num" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-complex", "num-integer", "num-iter", @@ -8736,11 +8466,10 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", "rand 0.8.5", @@ -8748,9 +8477,9 @@ dependencies = [ [[package]] name = "num-bigint-dig" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2399c9463abc5f909349d8aa9ba080e0b88b3ce2885389b60b993f39b1a56905" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" dependencies = [ "byteorder", "lazy_static", @@ -8765,9 +8494,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] @@ -8784,26 +8513,25 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -8816,7 +8544,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64a5fe11d4135c3bcdf3a95b18b194afa9608a5f6ff034f5d857bc9a27fb0119" dependencies = [ - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-integer", "num-traits", ] @@ -8829,8 +8557,8 @@ checksum = "e238432a7881ec7164503ccc516c014bf009be7984cde1ba56837862543bdec3" dependencies = [ "bitvec 1.0.1", "either", - "lru 0.12.3", - "num-bigint 0.4.4", + "lru 0.12.4", + "num-bigint 0.4.6", "num-integer", "num-modular", "num-traits", @@ -8839,21 +8567,20 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -8865,7 +8592,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] @@ -8893,37 +8620,25 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro-crate 1.1.3", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] -<<<<<<< HEAD -======= name = "num_enum_derive" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", -] - -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", + "proc-macro-crate 3.2.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] ->>>>>>> upstream/mainnet name = "number_prefix" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -8953,11 +8668,11 @@ dependencies = [ "hyper 1.4.1", "itertools 0.13.0", "md-5 0.10.6", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "percent-encoding", "quick-xml", "rand 0.8.5", - "reqwest 0.12.4", + "reqwest 0.12.7", "ring 0.17.8", "serde", "serde_json", @@ -8979,7 +8694,7 @@ dependencies = [ "futures", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.9.0", "structs", "tokio", "tracing", @@ -9003,15 +8718,15 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" -version = "11.1.3" +version = "11.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "open" @@ -9030,7 +8745,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.6", "auto_impl", "bytes", "ethereum-types", @@ -9044,9 +8759,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" dependencies = [ "bytes", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -9054,7 +8769,7 @@ name = "openapiv3" version = "2.0.0" source = "git+https://github.com/bmwill/openapiv3.git?rev=ca4b4845b7c159a39f5c68ad8f7f76cb6f4d6963#ca4b4845b7c159a39f5c68ad8f7f76cb6f4d6963" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.5.0", "schemars", "serde", "serde_json", @@ -9066,7 +8781,7 @@ version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -9081,9 +8796,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -9093,7 +8808,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] -<<<<<<< HEAD name = "openssl-sys" version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -9106,8 +8820,6 @@ dependencies = [ ] [[package]] -======= ->>>>>>> upstream/mainnet name = "opentelemetry" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -9115,25 +8827,6 @@ checksum = "9591d937bc0e6d2feb6f71a559540ab300ea49955229c347a517a28d27784c54" dependencies = [ "opentelemetry_api", "opentelemetry_sdk", -<<<<<<< HEAD -======= -] - -[[package]] -name = "opentelemetry" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" -dependencies = [ - "futures-core", - "futures-sink", - "indexmap 2.2.6", - "js-sys", - "once_cell", - "pin-project-lite", - "thiserror", - "urlencoding", ->>>>>>> upstream/mainnet ] [[package]] @@ -9144,7 +8837,7 @@ checksum = "7e5e5a5c4135864099f3faafbe939eb4d7f9b80ebf68a8448da961b32a7c1275" dependencies = [ "async-trait", "futures-core", - "http 0.2.9", + "http 0.2.12", "opentelemetry-proto", "opentelemetry-semantic-conventions", "opentelemetry_api", @@ -9173,11 +8866,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73c9f9340ad135068800e7f1b24e9e09ed9e7143f5bf8518ded3d3ec69789269" dependencies = [ -<<<<<<< HEAD "opentelemetry", -======= - "opentelemetry 0.20.0", ->>>>>>> upstream/mainnet ] [[package]] @@ -9209,11 +8898,7 @@ dependencies = [ "futures-util", "once_cell", "opentelemetry_api", -<<<<<<< HEAD "ordered-float", -======= - "ordered-float 3.9.1", ->>>>>>> upstream/mainnet "percent-encoding", "rand 0.8.5", "regex", @@ -9231,9 +8916,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "ordered-float" -version = "3.9.1" +version = "3.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a54938017eacd63036332b4ae5c8a49fc8c0c1d6d629893057e4f13609edd06" +checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" dependencies = [ "num-traits", ] @@ -9268,9 +8953,9 @@ checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -9281,19 +8966,10 @@ checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd" dependencies = [ "heck 0.4.1", "itertools 0.12.1", - "proc-macro2 1.0.78", + "proc-macro2 1.0.86", "proc-macro2-diagnostics", - "quote 1.0.35", - "syn 2.0.48", -] - -[[package]] -name = "output_vt100" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" -dependencies = [ - "winapi", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -9316,9 +8992,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "owo-colors" -version = "4.0.0" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" +checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" [[package]] name = "p256" @@ -9326,7 +9002,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ - "ecdsa 0.16.6", + "ecdsa 0.16.9", "elliptic-curve 0.13.8", "primeorder", "sha2 0.10.8", @@ -9358,7 +9034,7 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.6", "bitvec 0.20.4", "byte-slice-cast", "impl-trait-for-tuples", @@ -9368,15 +9044,15 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.5" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.6", "bitvec 1.0.1", "byte-slice-cast", "impl-trait-for-tuples", - "parity-scale-codec-derive 3.6.5", + "parity-scale-codec-derive 3.6.12", "serde", ] @@ -9386,30 +9062,24 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro-crate 1.1.3", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] name = "parity-scale-codec-derive" -version = "3.6.5" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro-crate 3.2.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] -[[package]] -name = "parking" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" - [[package]] name = "parking_lot" version = "0.11.2" @@ -9423,12 +9093,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core 0.9.9", + "parking_lot_core 0.9.10", ] [[package]] @@ -9447,54 +9117,18 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall 0.5.4", "smallvec", - "windows-targets 0.48.0", -] - -[[package]] -<<<<<<< HEAD -======= -name = "parquet" -version = "52.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f22ba0d95db56dde8685e3fadcb915cdaadda31ab8abbe3ff7f0ad1ef333267" -dependencies = [ - "ahash 0.8.11", - "arrow-array", - "arrow-buffer", - "arrow-cast", - "arrow-data", - "arrow-ipc", - "arrow-schema", - "arrow-select", - "base64 0.22.1", - "brotli 6.0.0", - "bytes", - "chrono", - "flate2", - "half 2.3.1", - "hashbrown 0.14.1", - "lz4_flex", - "num", - "num-bigint 0.4.4", - "paste", - "seq-macro", - "snap", - "thrift", - "twox-hash", - "zstd 0.13.0", - "zstd-sys", + "windows-targets 0.52.6", ] [[package]] ->>>>>>> upstream/mainnet name = "passkey-authenticator" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -9516,7 +9150,7 @@ checksum = "f14d42b14749cc7927add34a9932b3b3cc5349a633384850baa67183061439dd" dependencies = [ "ciborium", "coset", - "idna 0.5.0", + "idna", "passkey-authenticator", "passkey-types", "public-suffix", @@ -9532,11 +9166,11 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "499cff8432e71c5f8784d9645aac0f9fca604d67f59b68a606170b5e229c6538" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "ciborium", "coset", "data-encoding", - "indexmap 2.2.6", + "indexmap 2.5.0", "rand 0.8.5", "serde", "serde_json", @@ -9575,9 +9209,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "path-slash" @@ -9608,9 +9242,9 @@ dependencies = [ [[package]] name = "pbkdf2" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ "digest 0.10.7", "hmac 0.12.1", @@ -9624,9 +9258,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pem" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c64931a1a212348ec4f3b4362585eca7159d0d09cbdf4a7f74f02173596fd4" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" dependencies = [ "base64 0.13.1", ] @@ -9661,15 +9295,15 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.11" +version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea" dependencies = [ "memchr", "thiserror", @@ -9678,9 +9312,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.2" +version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96504449aa860c8dcde14f9fba5c58dc6658688ca1fe363589d6327b8662c603" +checksum = "664d22978e2815783adbdd2c588b455b1bd625299ce36b2a99881ac9627e6d8d" dependencies = [ "pest", "pest_generator", @@ -9688,26 +9322,26 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.2" +version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "798e0220d1111ae63d66cb66a5dcb3fc2d986d520b98e49e1852bfdb11d7c5e7" +checksum = "a2d5487022d5d33f4c30d91c22afa240ce2a644e87fe08caad974d4eab6badbe" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "pest_meta" -version = "2.5.2" +version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "984298b75898e30a843e278a9f2452c31e349a073a0ce6fd950a12a74464e065" +checksum = "0091754bbd0ea592c4deb3a122ce8ecbb0753b738aa82bc055fcc2eccc8d8174" dependencies = [ "once_cell", "pest", - "sha1", + "sha2 0.10.8", ] [[package]] @@ -9727,7 +9361,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.2.6", + "indexmap 2.5.0", ] [[package]] @@ -9742,35 +9376,35 @@ dependencies = [ [[package]] name = "phf" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ "phf_macros", - "phf_shared 0.11.1", + "phf_shared 0.11.2", ] [[package]] name = "phf_generator" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ - "phf_shared 0.11.1", + "phf_shared 0.11.2", "rand 0.8.5", ] [[package]] name = "phf_macros" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" dependencies = [ "phf_generator", - "phf_shared 0.11.1", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "phf_shared 0.11.2", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -9784,9 +9418,9 @@ dependencies = [ [[package]] name = "phf_shared" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" dependencies = [ "siphasher", ] @@ -9806,16 +9440,16 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -9841,9 +9475,9 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der 0.7.5", + "der 0.7.9", "pkcs8 0.10.2", - "spki 0.7.1", + "spki 0.7.3", ] [[package]] @@ -9862,21 +9496,21 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.5", - "spki 0.7.1", + "der 0.7.9", + "spki 0.7.3", ] [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plotters" -version = "0.3.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", @@ -9887,15 +9521,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" -version = "0.3.3" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] @@ -9913,9 +9547,9 @@ dependencies = [ [[package]] name = "polyval" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", "cpufeatures", @@ -9923,12 +9557,6 @@ dependencies = [ "universal-hash", ] -[[package]] -name = "portable-atomic" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26f6a7b87c2e435a3241addceeeff740ff8b7e76b74c13bf9acb17fa454ea00b" - [[package]] name = "portable-atomic" version = "1.7.0" @@ -9954,7 +9582,7 @@ dependencies = [ "log", "nix 0.26.4", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "smallvec", "symbolic-demangle", "tempfile", @@ -9963,15 +9591,18 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "pq-sys" -version = "0.4.7" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b845d6d8ec554f972a2c5298aad68953fd64e7441e846075450b44656a016d1" +checksum = "a92c30dd81695321846d4dfe348da67b1752ebb61cd1549d203a7b57e323c435" dependencies = [ "vcpkg", ] @@ -9996,17 +9627,28 @@ dependencies = [ "regex", ] +[[package]] +name = "predicates" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +dependencies = [ + "anstyle", + "difflib", + "predicates-core", +] + [[package]] name = "predicates-core" -version = "1.0.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" [[package]] name = "predicates-tree" -version = "1.0.7" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" dependencies = [ "predicates-core", "termtree", @@ -10014,24 +9656,22 @@ dependencies = [ [[package]] name = "pretty_assertions" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "ctor", "diff", - "output_vt100", "yansi 0.5.1", ] [[package]] name = "prettyplease" -version = "0.2.17" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ - "proc-macro2 1.0.78", - "syn 2.0.48", + "proc-macro2 1.0.86", + "syn 2.0.77", ] [[package]] @@ -10050,9 +9690,9 @@ dependencies = [ [[package]] name = "primeorder" -version = "0.13.0" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7613fdcc0831c10060fa69833ea8fa2caa94b6456f51e25356a885b530a2e3d0" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" dependencies = [ "elliptic-curve 0.13.8", ] @@ -10093,6 +9733,15 @@ dependencies = [ "toml 0.5.11", ] +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit 0.22.20", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -10100,9 +9749,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", "version_check", ] @@ -10112,17 +9761,11 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "version_check", ] -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - [[package]] name = "proc-macro2" version = "0.4.30" @@ -10134,9 +9777,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -10147,24 +9790,24 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", "version_check", "yansi 1.0.1", ] [[package]] name = "prometheus" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" dependencies = [ "cfg-if", "fnv", "lazy_static", "memchr", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "protobuf", "thiserror", ] @@ -10178,6 +9821,20 @@ dependencies = [ "protobuf", ] +[[package]] +name = "prometheus-http-query" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcebfa99f03ae51220778316b37d24981e36322c82c24848f48c5bd0f64cbdb" +dependencies = [ + "enum-as-inner", + "mime", + "reqwest 0.12.7", + "serde", + "time", + "url", +] + [[package]] name = "prometheus-parse" version = "0.2.3" @@ -10191,20 +9848,19 @@ dependencies = [ [[package]] name = "proptest" -version = "1.1.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f1b898011ce9595050a68e60f90bad083ff2987a695a42357134c8381fba70" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", - "bitflags 1.3.2", - "byteorder", + "bit-vec", + "bitflags 2.6.0", "lazy_static", "num-traits", - "quick-error 2.0.1", "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.6.28", + "regex-syntax 0.8.4", "rusty-fork", "tempfile", "unarray", @@ -10233,29 +9889,29 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.3" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ "bytes", - "prost-derive 0.12.3", + "prost-derive 0.12.6", ] [[package]] name = "prost" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc" +checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995" dependencies = [ "bytes", - "prost-derive 0.13.1", + "prost-derive 0.13.2", ] [[package]] name = "prost-build" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1" +checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" dependencies = [ "bytes", "heck 0.5.0", @@ -10265,10 +9921,10 @@ dependencies = [ "once_cell", "petgraph 0.6.5", "prettyplease", - "prost 0.13.1", - "prost-types 0.13.1", + "prost 0.13.2", + "prost-types 0.13.2", "regex", - "syn 2.0.48", + "syn 2.0.77", "tempfile", ] @@ -10280,53 +9936,53 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools 0.10.5", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] name = "prost-derive" -version = "0.12.3" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.11.0", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "itertools 0.12.1", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "prost-derive" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca" +checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" dependencies = [ "anyhow", "itertools 0.13.0", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "prost-types" -version = "0.12.3" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" dependencies = [ - "prost 0.12.3", + "prost 0.12.6", ] [[package]] name = "prost-types" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2" +checksum = "60caa6738c7369b940c3d49246a8d1749323674c65cb13010134f5c9bad5b519" dependencies = [ - "prost 0.13.1", + "prost 0.13.2", ] [[package]] @@ -10340,9 +9996,9 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" dependencies = [ "cc", ] @@ -10353,26 +10009,14 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca346b8ff0739660876c8d96a6f9de5cd9b4cd87500bb0ce92485318c674afe" -[[package]] -name = "pulldown-cmark" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" -dependencies = [ - "bitflags 2.4.1", - "memchr", - "unicase", -] - [[package]] name = "quanta" -version = "0.11.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17e662a7a8291a865152364c20c7abc5e60486ab2001e8ec10b24862de0b9ab" +checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" dependencies = [ "crossbeam-utils", "libc", - "mach2", "once_cell", "raw-cpuid", "wasi 0.11.0+wasi-snapshot-preview1", @@ -10386,12 +10030,6 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - [[package]] name = "quick-xml" version = "0.36.1" @@ -10404,9 +10042,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.3" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b22d8e7369034b9a7132bc2008cac12f2013c8132b45e0554e6e20e2617f2156" +checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" dependencies = [ "bytes", "futures-io", @@ -10414,8 +10052,8 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.0.0", - "rustls 0.23.12", - "socket2 0.5.6", + "rustls 0.23.13", + "socket2 0.5.7", "thiserror", "tokio", "tracing", @@ -10423,15 +10061,15 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba92fb39ec7ad06ca2582c0ca834dfeadcaf06ddfc8e635c80aa7e1c05315fdd" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" dependencies = [ "bytes", "rand 0.8.5", "ring 0.17.8", "rustc-hash 2.0.0", - "rustls 0.23.12", + "rustls 0.23.13", "slab", "thiserror", "tinyvec", @@ -10440,15 +10078,15 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285" +checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" dependencies = [ "libc", "once_cell", - "socket2 0.5.6", + "socket2 0.5.7", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -10462,11 +10100,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ - "proc-macro2 1.0.78", + "proc-macro2 1.0.86", ] [[package]] @@ -10476,7 +10114,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" dependencies = [ "log", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "scheduled-thread-pool", ] @@ -10593,11 +10231,11 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "10.6.0" +version = "11.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6823ea29436221176fe662da99998ad3b4db2c7f31e7b6f5fe43adccd6320bb" +checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] @@ -10618,21 +10256,6 @@ checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", -<<<<<<< HEAD -] - -[[package]] -name = "rcgen" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" -dependencies = [ - "pem 1.1.0", - "ring 0.16.20", - "time", - "yasna", -======= ->>>>>>> upstream/mainnet ] [[package]] @@ -10650,13 +10273,13 @@ dependencies = [ [[package]] name = "readonly" -version = "0.2.3" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78725e4e53781014168628ef49b2dc2fc6ae8d01a08769a5064685d34ee116c" +checksum = "a25d631e41bfb5fdcde1d4e2215f62f7f0afa3ff11e26563765bd6ea1d229aeb" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -10667,13 +10290,13 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio", + "mio 0.8.11", "num_cpus", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.6", - "tokio-macros 2.2.0 (git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e47aafebf98e9c1734a8848a1876d5946c44bdd1)", + "socket2 0.5.7", + "tokio-macros 2.2.0", "windows-sys 0.48.0", ] @@ -10688,30 +10311,21 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.4.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom 0.2.15", - "redox_syscall 0.2.16", + "libredox", "thiserror", ] @@ -10730,32 +10344,21 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "regex" -<<<<<<< HEAD -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" -======= version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" ->>>>>>> upstream/mainnet dependencies = [ "aho-corasick", "memchr", -<<<<<<< HEAD - "regex-automata 0.3.7", - "regex-syntax 0.7.5", -======= "regex-automata 0.4.7", - "regex-syntax 0.8.2", ->>>>>>> upstream/mainnet + "regex-syntax 0.8.4", ] [[package]] @@ -10764,35 +10367,25 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax 0.6.28", + "regex-syntax 0.6.29", ] [[package]] name = "regex-automata" -<<<<<<< HEAD -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" -dependencies = [ - "aho-corasick 1.0.2", - "memchr", - "regex-syntax 0.7.5", -======= version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", ->>>>>>> upstream/mainnet + "regex-syntax 0.8.4", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" @@ -10808,9 +10401,9 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.11.20" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "base64 0.21.7", "bytes", @@ -10818,9 +10411,10 @@ dependencies = [ "futures-core", "futures-util", "h2 0.3.26", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", + "hyper-rustls 0.24.2", "hyper-tls 0.5.0", "ipnet", "js-sys", @@ -10830,46 +10424,44 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration 0.5.1", "tokio", -<<<<<<< HEAD "tokio-native-tls", -======= "tokio-rustls 0.24.1", ->>>>>>> upstream/mainnet "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg 0.50.0", + "webpki-roots 0.25.4", + "winreg", ] [[package]] name = "reqwest" -version = "0.12.4" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" dependencies = [ -<<<<<<< HEAD - "async-compression 0.4.12", -======= "async-compression", ->>>>>>> upstream/mainnet "base64 0.22.1", "bytes", "encoding_rs", "futures-channel", "futures-core", "futures-util", - "h2 0.4.5", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "http-body-util", "hyper 1.4.1", - "hyper-rustls 0.26.0", + "hyper-rustls 0.27.3", "hyper-tls 0.6.0", "hyper-util", "ipnet", @@ -10880,27 +10472,28 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.22.4", - "rustls-native-certs 0.7.1", - "rustls-pemfile 2.1.2", + "quinn", + "rustls 0.23.13", + "rustls-native-certs 0.7.3", + "rustls-pemfile 2.1.3", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", - "system-configuration", + "sync_wrapper 1.0.1", + "system-configuration 0.6.1", "tokio", "tokio-native-tls", - "tokio-rustls 0.25.0", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-rustls 0.26.0", + "tokio-util 0.7.12", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.26.3", - "winreg 0.52.0", + "webpki-roots 0.26.5", + "windows-registry", ] [[package]] @@ -10912,7 +10505,7 @@ dependencies = [ "anyhow", "async-trait", "http 1.1.0", - "reqwest 0.12.4", + "reqwest 0.12.7", "serde", "thiserror", "tower-service", @@ -10932,7 +10525,7 @@ dependencies = [ "http 1.1.0", "hyper 1.4.1", "parking_lot 0.11.2", - "reqwest 0.12.4", + "reqwest 0.12.7", "reqwest-middleware", "retry-policies", "tokio", @@ -11028,9 +10621,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -11059,8 +10652,8 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ - "base64 0.21.2", - "bitflags 2.4.1", + "base64 0.21.7", + "bitflags 2.6.0", "serde", "serde_derive", ] @@ -11081,28 +10674,27 @@ dependencies = [ "pkcs8 0.9.0", "rand_core 0.6.4", "sha2 0.10.8", - "signature 2.0.0", + "signature 2.2.0", "subtle", "zeroize", ] [[package]] name = "rsa" -version = "0.9.1" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f1471dbb4be5de45050e8ef7040625298ccb9efe941419ac2697088715925f" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" dependencies = [ - "byteorder", "const-oid", "digest 0.10.7", "num-bigint-dig", "num-integer", - "num-iter", "num-traits", "pkcs1 0.7.5", "pkcs8 0.10.2", "rand_core 0.6.4", - "signature 2.0.0", + "signature 2.2.0", + "spki 0.7.3", "subtle", "zeroize", ] @@ -11126,10 +10718,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7229b505ae0706e64f37ffc54a9c163e11022a6636d58fe1f3f52018257ff9f7" dependencies = [ "cfg-if", - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "rustc_version", - "syn 1.0.107", + "syn 1.0.109", "unicode-ident", ] @@ -11144,8 +10736,8 @@ dependencies = [ "bytes", "crc32fast", "futures", - "http 0.2.9", - "hyper 0.14.26", + "http 0.2.12", + "hyper 0.14.30", "hyper-rustls 0.23.2", "lazy_static", "log", @@ -11168,7 +10760,7 @@ dependencies = [ "chrono", "dirs-next", "futures", - "hyper 0.14.26", + "hyper 0.14.30", "serde", "serde_json", "shlex", @@ -11203,8 +10795,8 @@ dependencies = [ "futures", "hex", "hmac 0.11.0", - "http 0.2.9", - "hyper 0.14.26", + "http 0.2.12", + "hyper 0.14.30", "log", "md-5 0.9.1", "percent-encoding", @@ -11225,7 +10817,7 @@ dependencies = [ "aes", "aes-gcm", "async-trait", - "bitflags 2.4.1", + "bitflags 2.6.0", "byteorder", "chacha20", "ctr", @@ -11237,7 +10829,7 @@ dependencies = [ "hex-literal 0.4.1", "hmac 0.12.1", "log", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "once_cell", "poly1305", "rand 0.8.5", @@ -11248,7 +10840,7 @@ dependencies = [ "subtle", "thiserror", "tokio", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.7.12", ] [[package]] @@ -11271,7 +10863,7 @@ dependencies = [ "async-trait", "bcrypt-pbkdf", "bit-vec", - "block-padding 0.3.2", + "block-padding 0.3.3", "byteorder", "cbc", "ctr", @@ -11283,7 +10875,7 @@ dependencies = [ "inout", "log", "md5", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-integer", "pbkdf2 0.11.0", "rand 0.7.3", @@ -11299,19 +10891,19 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.35.0" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1790d1c4c0ca81211399e0e0af16333276f375209e71a37b67698a373db5b47a" +checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.6", "num-traits", ] [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -11333,11 +10925,11 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.23", + "semver", ] [[package]] @@ -11351,28 +10943,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.1", - "windows-sys 0.45.0", -] - -[[package]] -name = "rustix" -version = "0.38.34" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "errno", "libc", - "linux-raw-sys 0.4.12", + "linux-raw-sys", "windows-sys 0.52.0", ] @@ -11402,53 +10980,52 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.4" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ "log", + "once_cell", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.6", + "rustls-webpki 0.102.8", "subtle", "zeroize", ] [[package]] -name = "rustls" -version = "0.23.12" +name = "rustls-native-certs" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ - "log", - "once_cell", - "ring 0.17.8", - "rustls-pki-types", - "rustls-webpki 0.102.6", - "subtle", - "zeroize", + "openssl-probe", + "rustls-pemfile 1.0.4", + "schannel", + "security-framework", ] [[package]] name = "rustls-native-certs" -version = "0.6.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" dependencies = [ "openssl-probe", - "rustls-pemfile 1.0.2", + "rustls-pemfile 2.1.3", + "rustls-pki-types", "schannel", "security-framework", ] [[package]] name = "rustls-native-certs" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba" +checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "rustls-pki-types", "schannel", "security-framework", @@ -11456,18 +11033,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ "base64 0.21.7", ] [[package]] name = "rustls-pemfile" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" dependencies = [ "base64 0.22.1", "rustls-pki-types", @@ -11475,9 +11052,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustls-webpki" @@ -11491,9 +11068,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.6" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring 0.17.8", "rustls-pki-types", @@ -11502,9 +11079,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.11" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rusty-fork" @@ -11513,7 +11090,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" dependencies = [ "fnv", - "quick-error 1.2.3", + "quick-error", "tempfile", "wait-timeout", ] @@ -11548,9 +11125,9 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "107c3d5d7f370ac09efa62a78375f94d94b8a33c61d8c278b96683fb4dbf2d8d" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -11579,44 +11156,44 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.10.0" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" +checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" dependencies = [ "cfg-if", "derive_more", - "parity-scale-codec 3.6.5", + "parity-scale-codec 3.6.12", "scale-info-derive", ] [[package]] name = "scale-info-derive" -version = "2.10.0" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" +checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro-crate 3.2.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.59.0", ] [[package]] name = "scheduled-thread-pool" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "977a7519bff143a44f842fd07e80ad1329295bd71686457f18e496736f4bf9bf" +checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" dependencies = [ - "parking_lot 0.12.1", + "parking_lot 0.12.3", ] [[package]] @@ -11638,23 +11215,17 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "serde_derive_internals", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scratch" -version = "1.0.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scrypt" @@ -11670,12 +11241,12 @@ dependencies = [ [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -11693,12 +11264,12 @@ dependencies = [ [[package]] name = "sec1" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48518a2b5775ba8ca5b46596aae011caa431e6ce7e4a67ead66d92f08884220e" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct 0.2.0", - "der 0.7.5", + "der 0.7.9", "generic-array", "pkcs8 0.10.2", "serdect", @@ -11737,11 +11308,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -11750,23 +11321,14 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", ] -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.23" @@ -11776,15 +11338,6 @@ dependencies = [ "serde", ] -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - [[package]] name = "send_wrapper" version = "0.4.0" @@ -11799,9 +11352,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.208" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -11829,22 +11382,22 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.9" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.208" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -11853,19 +11406,20 @@ version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.5.0", "itoa", + "memchr", "ryu", "serde", ] @@ -11881,10 +11435,11 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.9" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b04f22b563c91331a10074bda3dd5492e3cc39d56bd557e91c0af42b6c7341" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" dependencies = [ + "itoa", "serde", ] @@ -11894,9 +11449,9 @@ version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -11910,9 +11465,9 @@ dependencies = [ [[package]] name = "serde_test" -version = "1.0.152" +version = "1.0.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3611210d2d67e3513204742004d6ac6f589e521861dabb0f649b070eea8bed9e" +checksum = "7f901ee573cab6b3060453d2d5f0bae4e6d628c23c0a962ff9b5f1d7c8d4f1ed" dependencies = [ "serde", ] @@ -11931,9 +11486,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "2.1.0" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bf4a5a814902cd1014dbccfa4d4560fb8432c779471e96e035602519f82eef" +checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" dependencies = [ "base64 0.13.1", "chrono", @@ -11941,7 +11496,7 @@ dependencies = [ "indexmap 1.9.3", "serde", "serde_json", - "serde_with_macros 2.1.0", + "serde_with_macros 2.3.3", "time", ] @@ -11955,7 +11510,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.2.6", + "indexmap 2.5.0", "serde", "serde_derive", "serde_json", @@ -11965,14 +11520,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "2.1.0" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3452b4c0f6c1e357f73fdb87cd1efabaa12acf328c7a528e252893baeb3f4aa" +checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ - "darling 0.14.2", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "darling 0.20.10", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -11982,9 +11537,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" dependencies = [ "darling 0.20.10", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -11999,6 +11554,19 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.5.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "serdect" version = "0.2.0" @@ -12035,9 +11603,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", @@ -12082,9 +11650,9 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ "digest 0.10.7", "keccak", @@ -12092,9 +11660,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -12122,7 +11690,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" dependencies = [ - "dirs 4.0.0", + "dirs 5.0.1", ] [[package]] @@ -12133,9 +11701,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook" -version = "0.3.14" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", @@ -12143,20 +11711,20 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", - "mio", + "mio 0.8.11", "signal-hook", ] [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -12169,7 +11737,7 @@ checksum = "c1e303f8205714074f6068773f0e29527e0453937fe837c9717d066635b65f31" dependencies = [ "pkcs8 0.10.2", "rand_core 0.6.4", - "signature 2.0.0", + "signature 2.2.0", "zeroize", ] @@ -12185,9 +11753,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.0.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", "rand_core 0.6.4", @@ -12195,9 +11763,9 @@ dependencies = [ [[package]] name = "similar" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" [[package]] name = "simple-server-timing-header" @@ -12211,7 +11779,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-traits", "thiserror", "time", @@ -12260,9 +11828,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "sized-chunks" @@ -12274,26 +11842,11 @@ dependencies = [ "typenum", ] -[[package]] -name = "skeptic" -version = "0.13.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8" -dependencies = [ - "bytecount", - "cargo_metadata 0.14.2", - "error-chain", - "glob", - "pulldown-cmark", - "tempfile", - "walkdir", -] - [[package]] name = "slab" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] @@ -12321,9 +11874,9 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "snafu" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0656e7e3ffb70f6c39b3c2a86332bb74aa3c679da781642590f3c1118c5045" +checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" dependencies = [ "doc-comment", "snafu-derive", @@ -12331,21 +11884,21 @@ dependencies = [ [[package]] name = "snafu-derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "475b3bbe5245c26f2d8a6f62d67c1f30eb9fffeccee721c45d162c3ebbdf81b2" +checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] name = "snap" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" [[package]] name = "snowflake-api" @@ -12362,7 +11915,7 @@ dependencies = [ "log", "object_store", "regex", - "reqwest 0.12.4", + "reqwest 0.12.7", "reqwest-middleware", "reqwest-retry", "serde", @@ -12371,7 +11924,7 @@ dependencies = [ "thiserror", "tokio", "url", - "uuid 1.2.2", + "uuid 1.10.0", ] [[package]] @@ -12380,13 +11933,9 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f1c0c8818bb7400e821b166075ec771c8e8a012af41a8982b34eefaad4739fd" dependencies = [ -<<<<<<< HEAD "base64 0.22.1", -======= - "base64 0.21.7", ->>>>>>> upstream/mainnet - "jsonwebtoken", - "rsa 0.9.1", + "jsonwebtoken 9.3.0", + "rsa 0.9.6", "serde", "sha2 0.10.8", "thiserror", @@ -12395,9 +11944,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", @@ -12405,9 +11954,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", @@ -12422,7 +11971,7 @@ dependencies = [ "base64 0.13.1", "bytes", "futures", - "http 0.2.9", + "http 0.2.12", "httparse", "log", "rand 0.8.5", @@ -12440,7 +11989,7 @@ dependencies = [ "lalrpop-util", "phf", "thiserror", - "unicode-xid 0.2.4", + "unicode-xid 0.2.5", ] [[package]] @@ -12466,6 +12015,15 @@ dependencies = [ "strum 0.24.1", ] +[[package]] +name = "spinning_top" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.6.0" @@ -12478,12 +12036,12 @@ dependencies = [ [[package]] name = "spki" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der 0.7.5", + "der 0.7.9", ] [[package]] @@ -12494,15 +12052,15 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "stacker" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" dependencies = [ "cc", "cfg-if", "libc", "psm", - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -12511,12 +12069,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "static_assertions_next" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7beae5182595e9a8b683fa98c4317f956c9a2dec3b9716990d20023cc60c766" - [[package]] name = "str-buf" version = "1.0.6" @@ -12531,7 +12083,7 @@ checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ "new_debug_unreachable", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "phf_shared 0.10.0", "precomputed-hash", ] @@ -12568,7 +12120,7 @@ dependencies = [ "rayon", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.9.0", "strum 0.26.3", "typeshare", ] @@ -12588,7 +12140,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" dependencies = [ - "strum_macros 0.25.2", + "strum_macros 0.25.3", ] [[package]] @@ -12607,23 +12159,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "rustversion", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "strum_macros" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "rustversion", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] @@ -12633,10 +12185,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "rustversion", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] @@ -12651,9 +12203,9 @@ dependencies = [ [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "subtle-ng" @@ -12663,11 +12215,7 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" [[package]] name = "sui" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anemo", "anyhow", @@ -12705,18 +12253,18 @@ dependencies = [ "move-vm-config", "move-vm-profiler", "msim", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "prometheus", "rand 0.8.5", "regex", - "reqwest 0.12.4", + "reqwest 0.12.7", "rusoto_core", "rusoto_kms", "rustyline", "rustyline-derive", "serde", "serde_json", - "serde_yaml", + "serde_yaml 0.8.26", "shared-crypto", "shell-words", "shlex", @@ -12736,11 +12284,7 @@ dependencies = [ "sui-package-management", "sui-protocol-config", "sui-replay", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-simulator", "sui-source-validation", "sui-swarm", @@ -12755,13 +12299,13 @@ dependencies = [ "test-cluster", "thiserror", "tokio", - "toml 0.7.4", + "toml 0.7.8", "tower", "tower-http", "tracing", "unescape", "url", - "uuid 1.2.2", + "uuid 1.10.0", ] [[package]] @@ -12782,7 +12326,7 @@ dependencies = [ "move-vm-runtime", "move-vm-types", "mysten-metrics", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "serde", "sui-macros", "sui-move-natives-latest", @@ -12810,7 +12354,7 @@ dependencies = [ "move-vm-runtime-v0", "move-vm-types", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "serde", "sui-macros", "sui-move-natives-v0", @@ -12837,7 +12381,7 @@ dependencies = [ "move-vm-profiler", "move-vm-runtime-v1", "move-vm-types", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "serde", "sui-macros", "sui-move-natives-v1", @@ -12864,7 +12408,7 @@ dependencies = [ "move-vm-profiler", "move-vm-runtime-v2", "move-vm-types", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "serde", "sui-macros", "sui-move-natives-v2", @@ -12874,75 +12418,9 @@ dependencies = [ "tracing", ] -[[package]] -<<<<<<< HEAD -name = "sui-archival" -version = "1.30.1" -======= -name = "sui-analytics-indexer" -version = "1.32.3" -dependencies = [ - "anyhow", - "arrow", - "arrow-array", - "async-trait", - "axum 0.7.5", - "bcs", - "byteorder", - "bytes", - "chrono", - "clap", - "csv", - "eyre", - "fastcrypto", - "gcp-bigquery-client", - "move-binary-format", - "move-bytecode-utils", - "move-core-types", - "mysten-metrics", - "num_enum 0.6.1", - "object_store", - "parquet", - "prometheus", - "serde", - "serde_json", - "simulacrum", - "snowflake-api", - "strum 0.24.1", - "strum_macros 0.24.3", - "sui-analytics-indexer-derive", - "sui-config", - "sui-data-ingestion-core", - "sui-indexer", - "sui-json-rpc-types", - "sui-package-resolver", - "sui-rest-api", - "sui-storage", - "sui-types", - "tap", - "telemetry-subscribers", - "tempfile", - "thiserror", - "tokio", - "tokio-stream", - "tracing", - "typed-store", - "url", -] - -[[package]] -name = "sui-analytics-indexer-derive" -version = "1.32.3" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", -] - [[package]] name = "sui-archival" version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "byteorder", @@ -13002,7 +12480,7 @@ dependencies = [ "narwhal-config", "prettytable-rs", "prometheus-parse", - "reqwest 0.12.4", + "reqwest 0.12.7", "russh", "russh-keys", "serde", @@ -13050,11 +12528,7 @@ dependencies = [ "sui-macros", "sui-network", "sui-protocol-config", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-simulator", "sui-storage", "sui-surfer", @@ -13065,18 +12539,14 @@ dependencies = [ "telemetry-subscribers", "test-cluster", "tokio", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.7.12", "tracing", "typed-store", ] [[package]] name = "sui-bridge" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "arc-swap", @@ -13092,7 +12562,7 @@ dependencies = [ "fastcrypto", "futures", "hex-literal 0.3.4", - "lru 0.10.0", + "lru 0.10.1", "maplit", "move-core-types", "mysten-metrics", @@ -13100,12 +12570,7 @@ dependencies = [ "once_cell", "prometheus", "rand 0.8.5", -<<<<<<< HEAD - "reqwest 0.12.4", - "rocksdb", -======= - "reqwest 0.12.5", ->>>>>>> upstream/mainnet + "reqwest 0.12.7", "serde", "serde_json", "serde_with 3.9.0", @@ -13115,11 +12580,7 @@ dependencies = [ "sui-json-rpc-api", "sui-json-rpc-types", "sui-keys", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-test-transaction-builder", "sui-types", "tap", @@ -13134,11 +12595,7 @@ dependencies = [ [[package]] name = "sui-bridge-cli" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "clap", @@ -13146,7 +12603,7 @@ dependencies = [ "fastcrypto", "futures", "move-core-types", - "reqwest 0.12.4", + "reqwest 0.12.7", "serde", "serde_json", "serde_with 3.9.0", @@ -13155,11 +12612,7 @@ dependencies = [ "sui-config", "sui-json-rpc-types", "sui-keys", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-types", "telemetry-subscribers", "tokio", @@ -13184,17 +12637,13 @@ dependencies = [ "prometheus", "rayon", "serde", - "serde_yaml", + "serde_yaml 0.8.26", "sui-bridge", "sui-config", "sui-data-ingestion-core", "sui-indexer-builder", "sui-json-rpc-types", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-test-transaction-builder", "sui-types", "tap", @@ -13205,74 +12654,27 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= -name = "sui-cluster-test" -version = "1.32.3" +name = "sui-config" +version = "0.0.0" dependencies = [ + "anemo", "anyhow", - "async-trait", + "bcs", "clap", - "derivative", - "diesel", + "consensus-config", + "csv", + "dirs 4.0.0", "fastcrypto", - "futures", - "jsonrpsee", - "move-core-types", - "prometheus", - "regex", - "reqwest 0.12.5", - "serde_json", - "shared-crypto", - "sui-config", - "sui-core", - "sui-faucet", - "sui-graphql-rpc", - "sui-indexer", - "sui-json", - "sui-json-rpc-types", - "sui-keys", - "sui-sdk 1.32.3", - "sui-swarm", - "sui-swarm-config", - "sui-test-transaction-builder", - "sui-types", - "telemetry-subscribers", - "tempfile", - "test-cluster", - "tokio", - "tracing", - "uuid 1.2.2", -] - -[[package]] ->>>>>>> upstream/mainnet -name = "sui-config" -version = "0.0.0" -dependencies = [ - "anemo", - "anyhow", - "bcs", - "clap", - "consensus-config", - "csv", - "dirs 4.0.0", - "fastcrypto", - "insta", - "narwhal-config", - "object_store", - "once_cell", + "insta", + "narwhal-config", + "object_store", + "once_cell", "prometheus", "rand 0.8.5", - "reqwest 0.12.4", + "reqwest 0.12.7", "serde", -<<<<<<< HEAD - "serde_with 3.8.1", - "serde_yaml", -======= "serde_with 3.9.0", "serde_yaml 0.8.26", ->>>>>>> upstream/mainnet "sui-keys", "sui-protocol-config", "sui-types", @@ -13298,7 +12700,7 @@ dependencies = [ "count-min-sketch", "criterion", "dashmap", - "diffy", + "diffy 0.3.0", "either", "enum_dispatch", "expect-test", @@ -13309,10 +12711,10 @@ dependencies = [ "fs_extra", "futures", "im", - "indexmap 2.2.6", + "indexmap 2.5.0", "itertools 0.10.5", "jsonrpsee", - "lru 0.10.0", + "lru 0.10.1", "mockall", "moka", "more-asserts", @@ -13333,30 +12735,25 @@ dependencies = [ "narwhal-types", "narwhal-worker", "nonempty", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num_cpus", "object_store", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "pprof", "pretty_assertions", "prometheus", "rand 0.8.5", "rayon", - "reqwest 0.12.4", + "reqwest 0.12.7", "roaring", "rstest", "scopeguard", "serde", "serde-reflection", "serde_json", -<<<<<<< HEAD - "serde_with 3.8.1", - "serde_yaml", -======= "serde_with 3.9.0", "serde_yaml 0.8.26", ->>>>>>> upstream/mainnet "shared-crypto", "signature 1.6.4", "static_assertions", @@ -13416,11 +12813,7 @@ dependencies = [ [[package]] name = "sui-data-ingestion" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "async-trait", @@ -13440,7 +12833,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "serde_yaml", + "serde_yaml 0.8.26", "sui-archival", "sui-data-ingestion-core", "sui-storage", @@ -13483,11 +12876,7 @@ dependencies = [ [[package]] name = "sui-e2e-tests" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "assert_cmd", @@ -13500,7 +12889,7 @@ dependencies = [ "fastcrypto-zkp", "fs_extra", "futures", - "indexmap 2.2.6", + "indexmap 2.5.0", "insta", "jsonrpsee", "move-binary-format", @@ -13531,11 +12920,7 @@ dependencies = [ "sui-node", "sui-protocol-config", "sui-rest-api", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-simulator", "sui-storage", "sui-swarm", @@ -13555,7 +12940,7 @@ dependencies = [ name = "sui-enum-compat-util" version = "0.1.0" dependencies = [ - "serde_yaml", + "serde_yaml 0.8.26", ] [[package]] @@ -13602,17 +12987,13 @@ dependencies = [ "expect-test", "tempfile", "thiserror", - "toml 0.7.4", - "toml_edit 0.19.10", + "toml 0.7.8", + "toml_edit 0.19.15", ] [[package]] name = "sui-faucet" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "async-recursion", @@ -13623,7 +13004,7 @@ dependencies = [ "futures", "http 1.1.0", "mysten-metrics", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "prometheus", "scopeguard", "serde", @@ -13631,11 +13012,7 @@ dependencies = [ "sui-config", "sui-json-rpc-types", "sui-keys", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-types", "tap", "telemetry-subscribers", @@ -13648,7 +13025,7 @@ dependencies = [ "tracing", "ttl_cache", "typed-store", - "uuid 1.2.2", + "uuid 1.10.0", ] [[package]] @@ -13672,11 +13049,7 @@ dependencies = [ [[package]] name = "sui-framework-snapshot" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "bcs", @@ -13723,13 +13096,8 @@ dependencies = [ "prometheus", "rand 0.8.5", "serde", -<<<<<<< HEAD - "serde_with 3.8.1", - "serde_yaml", -======= "serde_with 3.9.0", "serde_yaml 0.8.26", ->>>>>>> upstream/mainnet "shared-crypto", "sui-config", "sui-execution", @@ -13742,131 +13110,9 @@ dependencies = [ "tracing", ] -[[package]] -<<<<<<< HEAD -name = "sui-indexer" -version = "1.30.1" -======= -name = "sui-graphql-config" -version = "1.32.3" -dependencies = [ - "quote 1.0.35", - "syn 1.0.107", -] - -[[package]] -name = "sui-graphql-e2e-tests" -version = "0.1.0" -dependencies = [ - "datatest-stable", - "msim", - "sui-graphql-rpc", - "sui-transactional-test-runner", - "tokio", -] - -[[package]] -name = "sui-graphql-rpc" -version = "2024.7.0" -dependencies = [ - "anyhow", - "async-graphql", - "async-graphql-axum", - "async-graphql-value", - "async-trait", - "axum 0.7.5", - "axum-extra", - "bcs", - "bin-version", - "chrono", - "clap", - "const-str", - "diesel", - "downcast", - "either", - "expect-test", - "fastcrypto", - "fastcrypto-zkp", - "futures", - "hex", - "http 1.1.0", - "hyper 1.4.1", - "im", - "insta", - "itertools 0.10.5", - "lru 0.10.0", - "move-binary-format", - "move-bytecode-utils", - "move-core-types", - "move-disassembler", - "move-ir-types", - "mysten-metrics", - "mysten-network", - "once_cell", - "prometheus", - "rand 0.8.5", - "regex", - "reqwest 0.12.5", - "serde", - "serde_json", - "serde_with 3.9.0", - "serde_yaml 0.8.26", - "serial_test", - "shared-crypto", - "similar", - "simulacrum", - "sui-framework", - "sui-graphql-config", - "sui-graphql-rpc-client", - "sui-graphql-rpc-headers", - "sui-indexer", - "sui-json-rpc", - "sui-json-rpc-types", - "sui-package-resolver", - "sui-protocol-config", - "sui-rest-api", - "sui-sdk 1.32.3", - "sui-swarm-config", - "sui-test-transaction-builder", - "sui-types", - "tap", - "telemetry-subscribers", - "tempfile", - "test-cluster", - "thiserror", - "tokio", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.7.4", - "tower", - "tower-http", - "tracing", - "uuid 1.2.2", -] - -[[package]] -name = "sui-graphql-rpc-client" -version = "0.1.0" -dependencies = [ - "async-graphql", - "axum 0.7.5", - "hyper 1.4.1", - "reqwest 0.12.5", - "serde_json", - "sui-graphql-rpc-headers", - "thiserror", -] - -[[package]] -name = "sui-graphql-rpc-headers" -version = "0.1.0" -dependencies = [ - "axum 0.7.5", -] - [[package]] name = "sui-indexer" version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "async-trait", @@ -13910,11 +13156,7 @@ dependencies = [ "sui-package-resolver", "sui-protocol-config", "sui-rest-api", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-test-transaction-builder", "sui-transaction-builder", "sui-types", @@ -13924,7 +13166,7 @@ dependencies = [ "test-cluster", "thiserror", "tokio", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.7.12", "tracing", "url", ] @@ -13978,9 +13220,9 @@ dependencies = [ "eyre", "fastcrypto", "futures", - "http-body 0.4.5", + "http-body 0.4.6", "hyper 1.4.1", - "indexmap 2.2.6", + "indexmap 2.5.0", "itertools 0.10.5", "jsonrpsee", "mockall", @@ -13989,11 +13231,8 @@ dependencies = [ "move-core-types", "move-package", "mysten-metrics", -<<<<<<< HEAD - "odin", -======= "mysten-service", ->>>>>>> upstream/mainnet + "odin", "once_cell", "prometheus", "serde", @@ -14016,7 +13255,7 @@ dependencies = [ "telemetry-subscribers", "thiserror", "tokio", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.7.12", "tower", "tower-http", "tracing", @@ -14055,7 +13294,7 @@ dependencies = [ "move-package", "prometheus", "rand 0.8.5", - "reqwest 0.12.4", + "reqwest 0.12.7", "sui-config", "sui-core", "sui-json", @@ -14068,11 +13307,7 @@ dependencies = [ "sui-open-rpc", "sui-open-rpc-macros", "sui-protocol-config", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-simulator", "sui-swarm-config", "sui-test-transaction-builder", @@ -14133,11 +13368,7 @@ dependencies = [ [[package]] name = "sui-light-client" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "async-trait", @@ -14148,17 +13379,13 @@ dependencies = [ "move-core-types", "serde", "serde_json", - "serde_yaml", + "serde_yaml 0.8.26", "sui-config", "sui-json", "sui-json-rpc-types", "sui-package-resolver", "sui-rest-api", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-types", "tokio", ] @@ -14174,10 +13401,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -name = "sui-move" -version = "1.30.1" -======= name = "sui-metric-checker" version = "1.32.3" dependencies = [ @@ -14189,9 +13412,9 @@ dependencies = [ "humantime", "once_cell", "prometheus-http-query", - "reqwest 0.12.5", + "reqwest 0.12.7", "serde", - "serde_yaml 0.9.21", + "serde_yaml 0.9.34+deprecated", "strum_macros 0.24.3", "telemetry-subscribers", "tokio", @@ -14201,7 +13424,6 @@ dependencies = [ [[package]] name = "sui-move" version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "assert_cmd", @@ -14226,7 +13448,7 @@ dependencies = [ "prometheus", "rand 0.8.5", "serde_json", - "serde_yaml", + "serde_yaml 0.8.26", "sui-core", "sui-macros", "sui-move-build", @@ -14243,11 +13465,7 @@ dependencies = [ [[package]] name = "sui-move-build" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "datatest-stable", @@ -14271,11 +13489,7 @@ dependencies = [ [[package]] name = "sui-move-lsp" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "bin-version", "clap", @@ -14292,7 +13506,7 @@ dependencies = [ "fastcrypto", "fastcrypto-vdf", "fastcrypto-zkp", - "indexmap 2.2.6", + "indexmap 2.5.0", "move-binary-format", "move-core-types", "move-stdlib-natives", @@ -14353,7 +13567,7 @@ dependencies = [ "better_any", "fastcrypto", "fastcrypto-zkp", - "indexmap 2.2.6", + "indexmap 2.5.0", "move-binary-format", "move-core-types", "move-stdlib-natives-v2", @@ -14397,7 +13611,7 @@ dependencies = [ "telemetry-subscribers", "tempfile", "tokio", - "tonic 0.12.1", + "tonic 0.12.2", "tonic-build", "tower", "tracing", @@ -14405,11 +13619,7 @@ dependencies = [ [[package]] name = "sui-node" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anemo", "anemo-tower", @@ -14431,9 +13641,9 @@ dependencies = [ "mysten-service", "narwhal-network", "narwhal-worker", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "prometheus", - "reqwest 0.12.4", + "reqwest 0.12.7", "serde", "snap", "sui-archival", @@ -14462,11 +13672,7 @@ dependencies = [ [[package]] name = "sui-open-rpc" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "bcs", @@ -14494,19 +13700,15 @@ version = "0.1.0" dependencies = [ "derive-syn-parse", "itertools 0.10.5", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", "unescape", ] [[package]] name = "sui-oracle" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "bcs", @@ -14518,7 +13720,7 @@ dependencies = [ "once_cell", "prometheus", "rand 0.8.5", - "reqwest 0.12.4", + "reqwest 0.12.7", "serde", "serde_json", "shared-crypto", @@ -14526,11 +13728,7 @@ dependencies = [ "sui-json-rpc-types", "sui-keys", "sui-move-build", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-types", "tap", "telemetry-subscribers", @@ -14548,7 +13746,7 @@ dependencies = [ "cynic-codegen", "fastcrypto", "move-core-types", - "reqwest 0.12.5", + "reqwest 0.12.7", "serde", "serde_json", "sui-types", @@ -14557,22 +13755,14 @@ dependencies = [ [[package]] name = "sui-package-management" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "move-core-types", "move-package", "move-symbol-pool", "sui-json-rpc-types", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-types", "thiserror", "tracing", @@ -14587,7 +13777,7 @@ dependencies = [ "eyre", "hyper 1.4.1", "insta", - "lru 0.10.0", + "lru 0.10.1", "move-binary-format", "move-command-line-common", "move-compiler", @@ -14607,10 +13797,10 @@ name = "sui-proc-macros" version = "0.7.0" dependencies = [ "msim-macros", - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "sui-enum-compat-util", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] @@ -14631,9 +13821,9 @@ dependencies = [ name = "sui-protocol-config-macros" version = "0.1.0" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -14658,27 +13848,17 @@ dependencies = [ "mysten-metrics", "once_cell", "prometheus", - "prost 0.13.1", + "prost 0.13.2", "prost-build", "protobuf", "rand 0.8.5", -<<<<<<< HEAD - "reqwest 0.12.4", - "rustls 0.21.12", - "rustls-pemfile 1.0.2", - "serde", - "serde_json", - "serde_with 3.8.1", - "serde_yaml", -======= - "reqwest 0.12.5", - "rustls 0.23.12", - "rustls-pemfile 2.1.2", + "reqwest 0.12.7", + "rustls 0.23.13", + "rustls-pemfile 2.1.3", "serde", "serde_json", "serde_with 3.9.0", "serde_yaml 0.8.26", ->>>>>>> upstream/mainnet "snap", "sui-tls", "sui-types", @@ -14701,24 +13881,19 @@ dependencies = [ "futures", "http 1.1.0", "jsonrpsee", - "lru 0.10.0", + "lru 0.10.1", "move-binary-format", "move-bytecode-utils", "move-core-types", "move-vm-config", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "prometheus", "rand 0.8.5", "regex", "serde", "serde_json", -<<<<<<< HEAD - "serde_with 3.8.1", - "serde_yaml", -======= "serde_with 3.9.0", "serde_yaml 0.8.26", ->>>>>>> upstream/mainnet "shared-crypto", "shellexpand", "similar", @@ -14730,11 +13905,7 @@ dependencies = [ "sui-json-rpc-api", "sui-json-rpc-types", "sui-protocol-config", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-storage", "sui-transaction-checks", "sui-types", @@ -14742,7 +13913,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.7.12", "tracing", ] @@ -14754,7 +13925,7 @@ dependencies = [ "async-trait", "axum 0.7.5", "bcs", - "diffy", + "diffy 0.3.0", "fastcrypto", "itertools 0.10.5", "mime", @@ -14762,17 +13933,12 @@ dependencies = [ "openapiv3", "prometheus", "rand 0.8.5", - "reqwest 0.12.4", + "reqwest 0.12.7", "schemars", "serde", "serde_json", -<<<<<<< HEAD - "serde_with 3.8.1", - "serde_yaml", -======= "serde_with 3.9.0", "serde_yaml 0.8.26", ->>>>>>> upstream/mainnet "sui-protocol-config", "sui-sdk 0.0.0", "sui-types", @@ -14784,11 +13950,7 @@ dependencies = [ [[package]] name = "sui-rosetta" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "async-trait", @@ -14804,7 +13966,7 @@ dependencies = [ "mysten-metrics", "once_cell", "rand 0.8.5", - "reqwest 0.12.4", + "reqwest 0.12.7", "serde", "serde_json", "shared-crypto", @@ -14816,11 +13978,7 @@ dependencies = [ "sui-keys", "sui-move-build", "sui-node", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-swarm-config", "sui-types", "telemetry-subscribers", @@ -14834,11 +13992,7 @@ dependencies = [ [[package]] name = "sui-rpc-loadgen" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "async-trait", @@ -14856,16 +14010,12 @@ dependencies = [ "sui-json-rpc", "sui-json-rpc-types", "sui-keys", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-types", "telemetry-subscribers", "test-cluster", "tokio", - "tonic 0.12.1", + "tonic 0.12.2", "tracing", ] @@ -14891,11 +14041,7 @@ dependencies = [ [[package]] name = "sui-sdk" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "async-recursion", @@ -14911,7 +14057,7 @@ dependencies = [ "jsonrpsee", "move-core-types", "rand 0.8.5", - "reqwest 0.12.4", + "reqwest 0.12.7", "serde", "serde_json", "serde_with 3.9.0", @@ -14931,11 +14077,7 @@ dependencies = [ [[package]] name = "sui-security-watchdog" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "arrow-array", @@ -14946,7 +14088,7 @@ dependencies = [ "lexical-util", "mysten-metrics", "prometheus", - "reqwest 0.12.4", + "reqwest 0.12.7", "serde", "serde_json", "snowflake-api", @@ -14954,7 +14096,7 @@ dependencies = [ "tokio", "tokio-cron-scheduler", "tracing", - "uuid 1.2.2", + "uuid 1.10.0", ] [[package]] @@ -14965,7 +14107,7 @@ dependencies = [ "anemo-tower", "bcs", "fastcrypto", - "lru 0.10.0", + "lru 0.10.1", "move-package", "msim", "narwhal-network", @@ -14982,11 +14124,7 @@ dependencies = [ [[package]] name = "sui-single-node-benchmark" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "async-trait", "bcs", @@ -15049,11 +14187,7 @@ dependencies = [ [[package]] name = "sui-source-validation" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "colored", @@ -15070,12 +14204,8 @@ dependencies = [ "rand 0.8.5", "sui-json-rpc-types", "sui-move-build", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-package-management", "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-test-transaction-builder", "sui-types", "tar", @@ -15105,23 +14235,19 @@ dependencies = [ "move-symbol-pool", "mysten-metrics", "prometheus", - "reqwest 0.12.4", + "reqwest 0.12.7", "serde", "sui", "sui-json-rpc-types", "sui-move", "sui-move-build", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-source-validation", "telemetry-subscribers", "tempfile", "test-cluster", "tokio", - "toml 0.7.4", + "toml 0.7.8", "tower", "tower-http", "tracing", @@ -15147,11 +14273,11 @@ dependencies = [ "fastcrypto", "futures", "hyper 1.4.1", - "hyper-rustls 0.27.2", + "hyper-rustls 0.27.3", "indicatif", "integer-encoding", "itertools 0.10.5", - "lru 0.10.0", + "lru 0.10.1", "move-binary-format", "move-bytecode-utils", "move-core-types", @@ -15160,16 +14286,11 @@ dependencies = [ "num_enum 0.6.1", "object_store", "once_cell", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "percent-encoding", "pretty_assertions", "prometheus", -<<<<<<< HEAD - "reqwest 0.12.4", - "rocksdb", -======= - "reqwest 0.12.5", ->>>>>>> upstream/mainnet + "reqwest 0.12.7", "serde", "serde_json", "sui-config", @@ -15186,22 +14307,18 @@ dependencies = [ "tracing", "typed-store", "url", - "zstd", + "zstd 0.12.4", ] [[package]] name = "sui-surfer" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "async-trait", "bcs", "clap", "futures", - "indexmap 2.2.6", + "indexmap 2.5.0", "move-binary-format", "move-core-types", "move-package", @@ -15260,13 +14377,8 @@ dependencies = [ "prometheus", "rand 0.8.5", "serde", -<<<<<<< HEAD - "serde_with 3.8.1", - "serde_yaml", -======= "serde_with 3.9.0", "serde_yaml 0.8.26", ->>>>>>> upstream/mainnet "shared-crypto", "sui-config", "sui-execution", @@ -15283,7 +14395,7 @@ dependencies = [ name = "sui-telemetry" version = "0.1.0" dependencies = [ - "reqwest 0.12.4", + "reqwest 0.12.7", "serde", "sui-core", "tracing", @@ -15298,21 +14410,13 @@ dependencies = [ "shared-crypto", "sui-genesis-builder", "sui-move-build", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-types", ] [[package]] name = "sui-test-validator" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet [[package]] name = "sui-tls" @@ -15325,17 +14429,10 @@ dependencies = [ "fastcrypto", "pkcs8 0.9.0", "rand 0.8.5", -<<<<<<< HEAD - "rcgen 0.9.3", - "reqwest 0.12.4", - "rustls 0.21.12", - "rustls-webpki 0.101.7", -======= "rcgen", - "reqwest 0.12.5", - "rustls 0.23.12", - "rustls-webpki 0.102.6", ->>>>>>> upstream/mainnet + "reqwest 0.12.7", + "rustls 0.23.13", + "rustls-webpki 0.102.8", "tokio", "tokio-rustls 0.26.0", "tower-layer", @@ -15344,11 +14441,7 @@ dependencies = [ [[package]] name = "sui-tool" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anemo", "anemo-cli", @@ -15382,11 +14475,7 @@ dependencies = [ "sui-package-dump", "sui-protocol-config", "sui-replay", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-snapshot", "sui-storage", "sui-types", @@ -15428,56 +14517,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= -name = "sui-transactional-test-runner" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "bcs", - "bimap", - "clap", - "criterion", - "eyre", - "fastcrypto", - "futures", - "move-binary-format", - "move-bytecode-utils", - "move-command-line-common", - "move-compiler", - "move-core-types", - "move-stdlib", - "move-symbol-pool", - "move-transactional-test-runner", - "move-vm-runtime", - "msim", - "once_cell", - "rand 0.8.5", - "regex", - "serde_json", - "simulacrum", - "sui-config", - "sui-core", - "sui-framework", - "sui-framework-snapshot", - "sui-graphql-rpc", - "sui-json-rpc", - "sui-json-rpc-api", - "sui-json-rpc-types", - "sui-protocol-config", - "sui-rest-api", - "sui-storage", - "sui-swarm-config", - "sui-types", - "telemetry-subscribers", - "tempfile", - "tokio", - "typed-store", -] - -[[package]] ->>>>>>> upstream/mainnet name = "sui-types" version = "0.1.0" dependencies = [ @@ -15501,10 +14540,10 @@ dependencies = [ "fastcrypto-tbls", "fastcrypto-zkp", "im", - "indexmap 2.2.6", + "indexmap 2.5.0", "itertools 0.10.5", "jsonrpsee", - "lru 0.10.0", + "lru 0.10.1", "move-binary-format", "move-bytecode-utils", "move-command-line-common", @@ -15519,13 +14558,13 @@ dependencies = [ "narwhal-config", "narwhal-crypto", "nonempty", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-traits", "num_enum 0.6.1", "odin", "once_cell", "p256", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "passkey-authenticator", "passkey-client", "passkey-types", @@ -15538,13 +14577,8 @@ dependencies = [ "serde", "serde-name", "serde_json", -<<<<<<< HEAD - "serde_with 3.8.1", - "serde_yaml", -======= "serde_with 3.9.0", "serde_yaml 0.8.26", ->>>>>>> upstream/mainnet "shared-crypto", "signature 1.6.4", "static_assertions", @@ -15557,7 +14591,7 @@ dependencies = [ "tap", "thiserror", "tokio", - "tonic 0.12.1", + "tonic 0.12.2", "tracing", "typed-store-error", "url", @@ -15593,6 +14627,13 @@ dependencies = [ "sui-types", ] +[[package]] +name = "sui-verifier-transactional-tests" +version = "0.1.0" +dependencies = [ + "datatest-stable", +] + [[package]] name = "sui-verifier-v0" version = "0.1.0" @@ -15639,11 +14680,7 @@ dependencies = [ [[package]] name = "suins-indexer" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "async-trait", @@ -15663,7 +14700,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "serde_yaml", + "serde_yaml 0.8.26", "sui-data-ingestion-core", "sui-json-rpc", "sui-storage", @@ -15694,36 +14731,27 @@ dependencies = [ "prettytable-rs", "rand 0.8.5", "regex", -<<<<<<< HEAD - "reqwest 0.12.4", - "semver 1.0.16", - "serde", - "serde_json", - "serde_yaml", - "sha2 0.10.6", -======= - "reqwest 0.12.5", - "semver 1.0.23", + "reqwest 0.12.7", + "semver", "serde", "serde_json", "serde_yaml 0.8.26", "sha2 0.10.8", ->>>>>>> upstream/mainnet "spinners", "strum 0.24.1", "tabled", "tempfile", "tokio", - "toml_edit 0.19.10", + "toml_edit 0.19.15", "tracing", "tracing-subscriber", ] [[package]] name = "supports-color" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9829b314621dfc575df4e409e79f9d6a66a3bd707ab73f23cb4aa3a854ac854f" +checksum = "8775305acf21c96926c900ad056abeef436701108518cf890020387236ac5a77" dependencies = [ "is_ci", ] @@ -15750,8 +14778,8 @@ dependencies = [ "fs2", "hex", "once_cell", - "reqwest 0.11.20", - "semver 1.0.23", + "reqwest 0.11.27", + "semver", "serde", "serde_json", "sha2 0.10.8", @@ -15762,21 +14790,21 @@ dependencies = [ [[package]] name = "symbolic-common" -version = "12.1.1" +version = "12.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26212dc7aeb75abb4cc84320a50dd482977089402f7b4043b454d6d79d8536e7" +checksum = "9c1db5ac243c7d7f8439eb3b8f0357888b37cf3732957e91383b0ad61756374e" dependencies = [ "debugid", "memmap2", "stable_deref_trait", - "uuid 1.2.2", + "uuid 1.10.0", ] [[package]] name = "symbolic-demangle" -version = "12.1.1" +version = "12.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f1b0155f588568b2df0d693b30aeedb59360b647b85fc3c23942e81e8cc97a" +checksum = "ea26e430c27d4a8a5dea4c4b81440606c7c1a415bd611451ef6af8c81416afc3" dependencies = [ "cpp_demangle", "rustc-demangle", @@ -15796,23 +14824,23 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.48" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "unicode-ident", ] @@ -15822,16 +14850,25 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", - "unicode-xid 0.2.4", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", + "unicode-xid 0.2.5", ] [[package]] @@ -15857,7 +14894,18 @@ checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", "core-foundation", - "system-configuration-sys", + "system-configuration-sys 0.5.0", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "system-configuration-sys 0.6.0", ] [[package]] @@ -15870,6 +14918,16 @@ dependencies = [ "libc", ] +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tabled" version = "0.12.2" @@ -15889,9 +14947,9 @@ checksum = "99f688a08b54f4f02f0a3c382aefdb7884d3d69609f785bd253dc033243e3fe4" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -15935,15 +14993,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "target-spec" -<<<<<<< HEAD -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf4306559bd50cb358e7af5692694d6f6fad95cf2c0bea2571dd419f5298e12" -======= -version = "3.2.1" +version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "419ccf3482090c626619fa2574290aaa00b696f9ab73af08fbf48260565431bf" ->>>>>>> upstream/mainnet +checksum = "4c5743abbf7bc7d5296ae61368b50cd218ac09432281cb5d48b97308d5c27909" dependencies = [ "cfg-expr", "guppy-workspace-hack", @@ -15981,15 +15033,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", - "fastrand 2.0.0", - "redox_syscall 0.3.5", - "rustix 0.38.34", - "windows-sys 0.48.0", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", ] [[package]] @@ -16018,15 +15070,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "rustix 0.38.34", + "rustix", "windows-sys 0.48.0", ] [[package]] name = "termtree" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "test-cluster" @@ -16052,11 +15104,7 @@ dependencies = [ "sui-macros", "sui-node", "sui-protocol-config", -<<<<<<< HEAD - "sui-sdk 1.30.1", -======= "sui-sdk 1.32.3", ->>>>>>> upstream/mainnet "sui-simulator", "sui-swarm", "sui-swarm-config", @@ -16068,9 +15116,9 @@ dependencies = [ [[package]] name = "test-fuzz" -version = "3.0.5" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd4a3a7f00909d5a1d1f83b86b65d91e4c94f80b0c2d0ae37e2ef44da7b7a0a0" +checksum = "857e884d611b1f26e63f00559975d348491e66b1271a8144a9806c9bd4a791cf" dependencies = [ "serde", "test-fuzz-internal", @@ -16080,40 +15128,40 @@ dependencies = [ [[package]] name = "test-fuzz-internal" -version = "3.0.5" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9186daca5c58cb307d09731e0ba06b13fd6c036c90672b9bfc31cecf76cf689" +checksum = "48db3bbc562408b2111f3a0c96ec416ffa3ab66f8a6ab42579b608b9f74744e1" dependencies = [ "cargo_metadata 0.15.4", - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "serde", "strum_macros 0.24.3", ] [[package]] name = "test-fuzz-macro" -version = "3.0.5" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d187b450bfb5b7939f82f9747dc1ebb15a7a9c4a93cd304a41aece7149608b" +checksum = "e17cd05c077c7b9c5d0045c539f9c5e911187bf65cd1b407d28239efc7b54880" dependencies = [ - "darling 0.14.2", + "darling 0.20.10", "if_chain", + "itertools 0.10.5", "lazy_static", - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "subprocess", - "syn 1.0.107", + "syn 2.0.77", "test-fuzz-internal", "toolchain_find", - "unzip-n", ] [[package]] name = "test-fuzz-runtime" -version = "3.0.5" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a0d69068569b9b7311095823fe0e49eedfd05ad4277eb64fc334cf1a5bc5116" +checksum = "53d68728ca22d1a96a71645fd2327e37821b8979a7e75e44ad24a72e8051513d" dependencies = [ "bincode", "hex", @@ -16136,30 +15184,31 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ + "cfg-if", "once_cell", ] @@ -16180,12 +15229,7 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", -<<<<<<< HEAD -======= - "libc", "num-conv", - "num_threads", ->>>>>>> upstream/mainnet "powerfmt", "serde", "time-core", @@ -16248,37 +15292,36 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.36.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", "libc", - "mio", - "num_cpus", - "parking_lot 0.12.1", + "mio 1.0.2", + "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.6", - "tokio-macros 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.5.7", + "tokio-macros 2.4.0", "tracing", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -16293,7 +15336,7 @@ dependencies = [ "num-traits", "tokio", "tracing", - "uuid 1.2.2", + "uuid 1.10.0", ] [[package]] @@ -16309,22 +15352,22 @@ dependencies = [ [[package]] name = "tokio-macros" version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +source = "git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e47aafebf98e9c1734a8848a1876d5946c44bdd1#e47aafebf98e9c1734a8848a1876d5946c44bdd1" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "tokio-macros" -version = "2.2.0" -source = "git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e47aafebf98e9c1734a8848a1876d5946c44bdd1#e47aafebf98e9c1734a8848a1876d5946c44bdd1" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -16369,38 +15412,27 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-rustls" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" -dependencies = [ - "rustls 0.22.4", - "rustls-pki-types", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.12", + "rustls 0.23.13", "rustls-pki-types", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" dependencies = [ "futures-core", "pin-project-lite", "tokio", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.7.12", ] [[package]] @@ -16415,7 +15447,7 @@ dependencies = [ "tokio", "tokio-rustls 0.24.1", "tungstenite 0.20.1", - "webpki-roots 0.25.2", + "webpki-roots 0.25.4", ] [[package]] @@ -16433,8 +15465,7 @@ dependencies = [ [[package]] name = "tokio-util" version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +source = "git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e47aafebf98e9c1734a8848a1876d5946c44bdd1#e47aafebf98e9c1734a8848a1876d5946c44bdd1" dependencies = [ "bytes", "futures-core", @@ -16443,14 +15474,16 @@ dependencies = [ "futures-util", "hashbrown 0.14.5", "pin-project-lite", - "tokio", + "real_tokio", + "slab", "tracing", ] [[package]] name = "tokio-util" -version = "0.7.10" -source = "git+https://github.com/mystenmark/tokio-madsim-fork.git?rev=e47aafebf98e9c1734a8848a1876d5946c44bdd1#e47aafebf98e9c1734a8848a1876d5946c44bdd1" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", @@ -16459,9 +15492,7 @@ dependencies = [ "futures-util", "hashbrown 0.14.5", "pin-project-lite", - "real_tokio", - "slab", - "tracing", + "tokio", ] [[package]] @@ -16476,27 +15507,27 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ - "indexmap 1.9.3", + "indexmap 2.5.0", "serde", "serde_spanned", "toml_datetime 0.6.8", - "toml_edit 0.19.10", + "toml_edit 0.19.15", ] [[package]] name = "toml" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime 0.6.8", - "toml_edit 0.22.17", + "toml_edit 0.22.20", ] [[package]] @@ -16540,24 +15571,24 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.10" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 1.9.3", + "indexmap 2.5.0", "serde", "serde_spanned", "toml_datetime 0.6.8", - "winnow 0.4.6", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.22.17" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.5.0", "serde", "serde_spanned", "toml_datetime 0.6.8", @@ -16577,9 +15608,9 @@ dependencies = [ "futures-core", "futures-util", "h2 0.3.26", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", "hyper-timeout 0.4.1", "percent-encoding", "pin-project", @@ -16594,9 +15625,9 @@ dependencies = [ [[package]] name = "tonic" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5469afaf78a11265c343a88969045c1568aa8ecc6c787dbf756e92e70f199861" +checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" dependencies = [ "async-stream", "async-trait", @@ -16604,13 +15635,13 @@ dependencies = [ "base64 0.21.7", "bytes", "h2 0.3.26", - "http 0.2.9", - "http-body 0.4.5", - "hyper 0.14.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", "hyper-timeout 0.4.1", "percent-encoding", "pin-project", - "prost 0.12.3", + "prost 0.12.6", "tokio", "tokio-stream", "tower", @@ -16621,16 +15652,16 @@ dependencies = [ [[package]] name = "tonic" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38659f4a91aba8598d27821589f5db7dddd94601e7a01b1e485a50e5484c7401" +checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad" dependencies = [ "async-stream", "async-trait", "axum 0.7.5", "base64 0.22.1", "bytes", - "h2 0.4.5", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "http-body-util", @@ -16639,8 +15670,8 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project", - "prost 0.13.1", - "socket2 0.5.6", + "prost 0.13.2", + "socket2 0.5.7", "tokio", "tokio-stream", "tower", @@ -16651,40 +15682,40 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "568392c5a2bd0020723e3f387891176aabafe36fd9fcd074ad309dfa0c8eb964" +checksum = "fe4ee8877250136bd7e3d2331632810a4df4ea5e004656990d8d66d2f5ee8a67" dependencies = [ "prettyplease", - "proc-macro2 1.0.78", + "proc-macro2 1.0.86", "prost-build", - "quote 1.0.35", - "syn 2.0.48", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "tonic-health" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e10e6a96ee08b6ce443487d4368442d328d0e746f3681f81127f7dc41b4955" +checksum = "ec0a34e6f706bae26b2b490e1da5c3f6a6ff87cae442bcbc7c881bab9631b5a7" dependencies = [ "async-stream", - "prost 0.13.1", + "prost 0.13.2", "tokio", "tokio-stream", - "tonic 0.12.1", + "tonic 0.12.2", ] [[package]] name = "toolchain_find" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e85654a10e7a07a47c6f19d93818f3f343e22927f2fa280c84f7c8042743413" +checksum = "fa8c746c4e8d786ff304a6a73d7b406420d9a7c7d8292e090979dc85213113ed" dependencies = [ "home", - "lazy_static", + "once_cell", "regex", - "semver 0.11.0", + "semver", "walkdir", ] @@ -16703,7 +15734,7 @@ dependencies = [ "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.7.12", "tower-layer", "tower-service", "tracing", @@ -16717,7 +15748,7 @@ checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ "async-compression", "base64 0.21.7", - "bitflags 2.4.1", + "bitflags 2.6.0", "bytes", "futures-core", "futures-util", @@ -16732,25 +15763,25 @@ dependencies = [ "percent-encoding", "pin-project-lite", "tokio", - "tokio-util 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.7.12", "tower", "tower-layer", "tower-service", "tracing", - "uuid 1.2.2", + "uuid 1.10.0", ] [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -16766,11 +15797,12 @@ dependencies = [ [[package]] name = "tracing-appender" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", + "thiserror", "time", "tracing-subscriber", ] @@ -16781,9 +15813,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -16818,12 +15850,23 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" dependencies = [ - "lazy_static", "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", "tracing-core", ] @@ -16834,16 +15877,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75327c6b667828ddc28f5e3f169036cb793c3f588d83bf0f262a7f062ffed3c8" dependencies = [ "once_cell", -<<<<<<< HEAD "opentelemetry", -======= - "opentelemetry 0.20.0", ->>>>>>> upstream/mainnet "opentelemetry_sdk", "smallvec", "tracing", "tracing-core", - "tracing-log", + "tracing-log 0.1.4", "tracing-subscriber", ] @@ -16859,12 +15898,12 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", - "nu-ansi-term", + "nu-ansi-term 0.46.0", "once_cell", "regex", "serde", @@ -16875,7 +15914,7 @@ dependencies = [ "time", "tracing", "tracing-core", - "tracing-log", + "tracing-log 0.2.0", "tracing-serde", ] @@ -16885,9 +15924,9 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b79e2e9c9ab44c6d7c20d5976961b47e8f49ac199154daa514b77cd1ab536625" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -16921,9 +15960,9 @@ checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tryhard" @@ -16967,7 +16006,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 0.2.9", + "http 0.2.12", "httparse", "log", "rand 0.8.5", @@ -17023,15 +16062,15 @@ dependencies = [ "msim", "once_cell", "ouroboros 0.17.2", - "proc-macro2 1.0.78", + "proc-macro2 1.0.86", "prometheus", - "quote 1.0.35", + "quote 1.0.37", "rand 0.8.5", "rocksdb", "rstest", "serde", "sui-macros", - "syn 1.0.107", + "syn 1.0.109", "tap", "tempfile", "thiserror", @@ -17048,9 +16087,9 @@ name = "typed-store-derive" version = "0.3.0" dependencies = [ "itertools 0.10.5", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -17070,19 +16109,19 @@ dependencies = [ "libc", "memchr", "nom", - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "regex", - "regex-syntax 0.7.2", - "syn 2.0.48", + "regex-syntax 0.7.5", + "syn 2.0.77", "zstd-sys", ] [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typeshare" @@ -17102,15 +16141,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a615d6c2764852a2e88a4f16e9ce1ea49bb776b5872956309e170d63a042a34f" dependencies = [ - "quote 1.0.35", - "syn 2.0.48", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "uint" @@ -17138,9 +16177,9 @@ checksum = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e" [[package]] name = "unicase" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ "version_check", ] @@ -17153,9 +16192,9 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-linebreak" @@ -17165,24 +16204,24 @@ checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" @@ -17192,9 +16231,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" [[package]] name = "universal-hash" @@ -17207,19 +16246,16 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "unsafe-libyaml" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] ->>>>>>> upstream/mainnet name = "unsigned-varint" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" +checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" [[package]] name = "untrusted" @@ -17233,54 +16269,39 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "unzip-n" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7e85a0596447f0f2ac090e16bc4c516c6fe91771fb0c0ccf7fa3dae896b9c" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", -] - [[package]] name = "ureq" version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" dependencies = [ -<<<<<<< HEAD "base64 0.22.1", -======= - "base64 0.21.7", ->>>>>>> upstream/mainnet "flate2", "log", "once_cell", - "rustls 0.23.12", + "rustls 0.23.13", "rustls-pki-types", "url", - "webpki-roots 0.26.3", + "webpki-roots 0.26.5", ] [[package]] name = "url" -version = "2.3.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna 0.3.0", + "idna", "percent-encoding", "serde", ] [[package]] name = "urlencoding" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "utf-8" @@ -17290,9 +16311,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" @@ -17306,9 +16327,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.2.2" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom 0.2.15", "rand 0.8.5", @@ -17326,8 +16347,8 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae2faf80ac463422992abf4de234731279c058aaf33171ca70277c98406b124" dependencies = [ - "quote 1.0.35", - "syn 1.0.107", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] @@ -17338,9 +16359,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "versions" @@ -17381,8 +16402,8 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", ] [[package]] @@ -17394,12 +16415,6 @@ dependencies = [ "libc", ] -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - [[package]] name = "walkdir" version = "2.5.0" @@ -17412,11 +16427,10 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -17458,17 +16472,17 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -17482,7 +16496,7 @@ version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ - "quote 1.0.35", + "quote 1.0.37", "wasm-bindgen-macro-support", ] @@ -17492,9 +16506,9 @@ version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -17535,9 +16549,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -17549,7 +16563,7 @@ version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring 0.17.3", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -17564,35 +16578,26 @@ dependencies = [ [[package]] name = "webpki-roots" -<<<<<<< HEAD -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" -dependencies = [ - "rustls-webpki 0.100.3", -] -======= -version = "0.25.2" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" ->>>>>>> upstream/mainnet +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.26.3" +version = "0.26.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a" dependencies = [ "rustls-pki-types", ] [[package]] name = "whoami" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fec781d48b41f8163426ed18e8fc2864c12937df9ce54c88ede7bd47270893e" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ - "redox_syscall 0.4.1", + "redox_syscall 0.5.4", "wasite", "web-sys", ] @@ -17621,11 +16626,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -17635,27 +16640,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-sys" -version = "0.42.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.52.6", ] [[package]] -name = "windows-sys" -version = "0.45.0" +name = "windows-registry" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ - "windows-targets 0.42.2", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", ] [[package]] @@ -17664,7 +16684,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.48.5", ] [[package]] @@ -17673,185 +16693,144 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.6", ] [[package]] -name = "windows-targets" -version = "0.42.2" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.52.6", ] [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] -name = "windows_i686_gnu" -version = "0.52.0" +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.4.6" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] @@ -17875,16 +16854,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -17921,11 +16890,7 @@ dependencies = [ [[package]] name = "x" -<<<<<<< HEAD -version = "1.30.1" -======= version = "1.32.3" ->>>>>>> upstream/mainnet dependencies = [ "anyhow", "camino", @@ -17959,21 +16924,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" dependencies = [ "libc", - "linux-raw-sys 0.4.12", - "rustix 0.38.34", + "linux-raw-sys", + "rustix", ] [[package]] name = "xml-rs" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a77ee7c0de333dcc6da69b177380a0b81e0dacfa4f7344c465a36871ee601" +checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" [[package]] name = "xmlparser" -version = "0.13.5" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] name = "yaml-rust" @@ -18003,7 +16968,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ "bit-vec", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "time", ] @@ -18013,6 +16978,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] @@ -18022,50 +16988,29 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.3.3" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.107", - "synstructure", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -18099,11 +17044,20 @@ dependencies = [ [[package]] name = "zstd" -version = "0.12.3+zstd.1.5.2" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" dependencies = [ - "zstd-safe", + "zstd-safe 6.0.6", +] + +[[package]] +name = "zstd" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +dependencies = [ + "zstd-safe 7.2.1", ] [[package]] @@ -18118,14 +17072,23 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "6.0.5+zstd.1.5.4" +version = "6.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" dependencies = [ "libc", "zstd-sys", ] +[[package]] +name = "zstd-safe" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +dependencies = [ + "zstd-sys", +] + [[package]] name = "zstd-sys" version = "2.0.12+zstd.1.5.6" diff --git a/Cargo.toml b/Cargo.toml index 17acd58aae594..3256120421a86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,9 +83,9 @@ members = [ "crates/shared-crypto", "crates/simulacrum", "crates/sui", - "crates/sui-adapter-transactional-tests", - "crates/sui-analytics-indexer", - "crates/sui-analytics-indexer-derive", + # "crates/sui-adapter-transactional-tests", + # "crates/sui-analytics-indexer", + # "crates/sui-analytics-indexer-derive", "crates/sui-archival", "crates/sui-authority-aggregation", "crates/sui-aws-orchestrator", @@ -93,7 +93,7 @@ members = [ "crates/sui-bridge", "crates/sui-bridge-cli", "crates/sui-bridge-indexer", - "crates/sui-cluster-test", + # "crates/sui-cluster-test", "crates/sui-config", "crates/sui-core", "crates/sui-cost", @@ -106,11 +106,11 @@ members = [ "crates/sui-framework-snapshot", "crates/sui-framework-tests", "crates/sui-genesis-builder", - "crates/sui-graphql-config", - "crates/sui-graphql-e2e-tests", - "crates/sui-graphql-rpc", - "crates/sui-graphql-rpc-client", - "crates/sui-graphql-rpc-headers", + # "crates/sui-graphql-config", + # "crates/sui-graphql-e2e-tests", + # "crates/sui-graphql-rpc", + # "crates/sui-graphql-rpc-client", + # "crates/sui-graphql-rpc-headers", "crates/sui-indexer", "crates/sui-indexer-builder", "crates/sui-json", @@ -159,7 +159,7 @@ members = [ "crates/sui-tool", "crates/sui-transaction-builder", "crates/sui-transaction-checks", - "crates/sui-transactional-test-runner", + # "crates/sui-transactional-test-runner", "crates/sui-types", "crates/sui-upgrade-compatibility-transactional-tests", "crates/sui-verifier-transactional-tests", diff --git a/crates/sui-adapter-transactional-tests/Cargo.toml b/crates/sui-adapter-transactional-tests/Cargo.toml index 25538480c9d2e..2b1797440573e 100644 --- a/crates/sui-adapter-transactional-tests/Cargo.toml +++ b/crates/sui-adapter-transactional-tests/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" [dev-dependencies] datatest-stable.workspace = true -sui-transactional-test-runner.workspace = true +# sui-transactional-test-runner.workspace = true [[test]] name = "tests" diff --git a/crates/sui-analytics-indexer/Cargo.toml b/crates/sui-analytics-indexer/Cargo.toml index 227d38b32315a..7a21cc191b6ec 100644 --- a/crates/sui-analytics-indexer/Cargo.toml +++ b/crates/sui-analytics-indexer/Cargo.toml @@ -34,7 +34,7 @@ parquet.workspace = true arrow-array.workspace = true fastcrypto = { workspace = true, features = ["copy_key"] } mysten-metrics.workspace = true -sui-analytics-indexer-derive.workspace = true +# sui-analytics-indexer-derive.workspace = true sui-data-ingestion-core.workspace = true sui-indexer.workspace = true eyre.workspace = true @@ -51,7 +51,7 @@ sui-json-rpc-types.workspace = true sui-package-resolver.workspace = true simulacrum.workspace = true arrow.workspace = true -gcp-bigquery-client = "0.18.0" +# gcp-bigquery-client = "0.18.0" snowflake-api.workspace = true tap.workspace = true diff --git a/crates/sui-cluster-test/Cargo.toml b/crates/sui-cluster-test/Cargo.toml index 9186b9ea56db2..ed9b467c1a3ce 100644 --- a/crates/sui-cluster-test/Cargo.toml +++ b/crates/sui-cluster-test/Cargo.toml @@ -28,7 +28,7 @@ regex.workspace = true sui-indexer.workspace = true sui-faucet.workspace = true -sui-graphql-rpc.workspace = true +# sui-graphql-rpc.workspace = true sui-swarm.workspace = true sui-swarm-config.workspace = true sui-json-rpc-types.workspace = true diff --git a/crates/sui-graphql-rpc/Cargo.toml b/crates/sui-graphql-rpc/Cargo.toml index 6284038ea56fe..78a7bc5aef2ae 100644 --- a/crates/sui-graphql-rpc/Cargo.toml +++ b/crates/sui-graphql-rpc/Cargo.toml @@ -70,8 +70,8 @@ im.workspace = true downcast = "0.11.0" sui-graphql-config.workspace = true -sui-graphql-rpc-headers.workspace = true -sui-graphql-rpc-client.workspace = true +# sui-graphql-rpc-headers.workspace = true +# sui-graphql-rpc-client.workspace = true # TODO: put these behind feature flag to prevent leakage diff --git a/crates/sui-indexer/logs.txt b/crates/sui-indexer/logs.txt new file mode 100644 index 0000000000000..87a45b8b9affd --- /dev/null +++ b/crates/sui-indexer/logs.txt @@ -0,0 +1,5000 @@ +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456213Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95141 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456216Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95142 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456217Z INFO sui_types::nats_queue: Received checkpoint with seq number 95124 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456218Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95133 for workflow primary in 67.296µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456218Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456219Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456219Z INFO sui_types::nats_queue: Received checkpoint with seq number 95133 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456220Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456221Z INFO sui_types::nats_queue: Received checkpoint with seq number 95124 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456223Z INFO sui_types::nats_queue: Received checkpoint with seq number 95133 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456223Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95137 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456224Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456226Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456224Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95140 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456226Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95138 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456226Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456228Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95131 with time now 1726138825456 and checkpoint time 1681502931527 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456229Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95127 with time now 1726138825456 and checkpoint time 1681502927526 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456229Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456232Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95138: 44635886918 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456232Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456232Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95140: 44635884975 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456233Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95138 with time now 1726138825456 and checkpoint time 1681502938538 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456234Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95127 for workflow primary in 73.728µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456234Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95140 with time now 1726138825456 and checkpoint time 1681502940481 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456237Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95131 for workflow primary in 86.982µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456238Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95138 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456239Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456240Z INFO sui_types::nats_queue: Received checkpoint with seq number 95127 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456242Z INFO sui_types::nats_queue: Received checkpoint with seq number 95131 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456242Z INFO sui_types::nats_queue: Received checkpoint with seq number 95127 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456244Z INFO sui_types::nats_queue: Received checkpoint with seq number 95131 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456243Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95140 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456244Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456245Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95136 with time now 1726138825456 and checkpoint time 1681502936516 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456247Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456247Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95134 with time now 1726138825456 and checkpoint time 1681502934570 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456248Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95139 with time now 1726138825456 and checkpoint time 1681502939570 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456249Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456250Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95135 with time now 1726138825456 and checkpoint time 1681502935535 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456253Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95132 with time now 1726138825456 and checkpoint time 1681502932529 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456253Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95129 with time now 1726138825456 and checkpoint time 1681502929565 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456255Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456256Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95136 for workflow primary in 70.271µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456258Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95139 for workflow primary in 58.489µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456261Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95135 for workflow primary in 74.569µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456262Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456264Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95145 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456264Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95134 for workflow primary in 117.981µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456266Z INFO sui_types::nats_queue: Received checkpoint with seq number 95136 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456265Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95132 for workflow primary in 111.167µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456265Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95129 for workflow primary in 100.749µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456267Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95145: 44635879878 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456267Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95130 with time now 1726138825456 and checkpoint time 1681502930569 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456268Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95145 with time now 1726138825456 and checkpoint time 1681502945578 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456268Z INFO sui_types::nats_queue: Received checkpoint with seq number 95139 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456269Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95142 with time now 1726138825456 and checkpoint time 1681502942502 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456270Z INFO sui_types::nats_queue: Received checkpoint with seq number 95135 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456270Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95147 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456271Z INFO sui_types::nats_queue: Received checkpoint with seq number 95134 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456268Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95149 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456272Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95146 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456273Z INFO sui_types::nats_queue: Received checkpoint with seq number 95129 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456274Z INFO sui_types::nats_queue: Received checkpoint with seq number 95132 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456275Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95147: 44635877902 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456275Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95148 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456276Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95147 with time now 1726138825456 and checkpoint time 1681502947554 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456277Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95146: 44635878913 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456274Z INFO sui_types::nats_queue: Received checkpoint with seq number 95136 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456277Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456277Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95149: 44635875941 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456278Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95146 with time now 1726138825456 and checkpoint time 1681502946543 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456279Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95145 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456280Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95149 with time now 1726138825456 and checkpoint time 1681502949515 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456280Z INFO sui_types::nats_queue: Received checkpoint with seq number 95139 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456280Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95147 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456281Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95143 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456281Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95148: 44635876902 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456282Z INFO sui_types::nats_queue: Received checkpoint with seq number 95135 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456283Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95148 with time now 1726138825456 and checkpoint time 1681502948554 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456283Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456284Z INFO sui_types::nats_queue: Received checkpoint with seq number 95134 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456284Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95142 for workflow primary in 79.869µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456285Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95143: 44635881898 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456285Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95137 with time now 1726138825456 and checkpoint time 1681502937556 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456285Z INFO sui_types::nats_queue: Received checkpoint with seq number 95129 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456286Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95143 with time now 1726138825456 and checkpoint time 1681502943558 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456286Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95146 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456285Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95141 with time now 1726138825456 and checkpoint time 1681502941536 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456287Z INFO sui_types::nats_queue: Received checkpoint with seq number 95132 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456286Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95130 for workflow primary in 111.709µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456286Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456288Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95138 with time now 1726138825456 and checkpoint time 1681502938538 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456289Z INFO sui_types::nats_queue: Received checkpoint with seq number 95142 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456291Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95151 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456292Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95144 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456290Z INFO sui_types::nats_queue: Received checkpoint with seq number 95130 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456293Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95149 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456294Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456294Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95151: 44635873831 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456294Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95148 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456295Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95143 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456296Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95151 with time now 1726138825456 and checkpoint time 1681502951625 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456296Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95150 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456298Z INFO sui_types::nats_queue: Received checkpoint with seq number 95142 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456299Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456299Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456299Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95144: 44635880909 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456300Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95137 for workflow primary in 109.645µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456300Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95138 for workflow primary in 72.185µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456300Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95144 with time now 1726138825456 and checkpoint time 1681502944547 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456301Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456301Z INFO sui_types::nats_queue: Received checkpoint with seq number 95130 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456303Z INFO sui_types::nats_queue: Received checkpoint with seq number 95137 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456304Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95151 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456304Z INFO sui_types::nats_queue: Received checkpoint with seq number 95138 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456302Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95141 for workflow primary in 105.066µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456304Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456304Z INFO sui_types::nats_queue: Received checkpoint with seq number 95138 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456303Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95150: 44635874847 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456305Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95144 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456306Z INFO sui_types::nats_queue: Received checkpoint with seq number 95141 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456306Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456306Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95150 with time now 1726138825456 and checkpoint time 1681502950609 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456307Z INFO sui_types::nats_queue: Received checkpoint with seq number 95137 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456309Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456309Z INFO sui_types::nats_queue: Received checkpoint with seq number 95141 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456310Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95152 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456312Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456312Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456313Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95145 with time now 1726138825456 and checkpoint time 1681502945578 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456313Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456314Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95150 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456315Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95152: 44635872905 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456317Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95140 with time now 1726138825456 and checkpoint time 1681502940481 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456317Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95152 with time now 1726138825456 and checkpoint time 1681502952551 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456319Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95145 for workflow primary in 54.682µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456323Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95155 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456325Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456326Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95147 with time now 1726138825456 and checkpoint time 1681502947554 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456326Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95155: 44635869912 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456327Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456328Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95155 with time now 1726138825456 and checkpoint time 1681502955544 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456327Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456327Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95152 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456328Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456329Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456330Z INFO sui_types::nats_queue: Received checkpoint with seq number 95145 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456332Z INFO sui_types::nats_queue: Received checkpoint with seq number 95140 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456332Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95140 for workflow primary in 104.135µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456336Z INFO sui_types::nats_queue: Received checkpoint with seq number 95145 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456336Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95147 for workflow primary in 64.751µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456337Z INFO sui_types::nats_queue: Received checkpoint with seq number 95140 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456337Z INFO sui_types::nats_queue: Received checkpoint with seq number 95147 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456337Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456338Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95155 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456339Z INFO sui_types::nats_queue: Received checkpoint with seq number 95147 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456338Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95154 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456340Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95153 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456344Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95151 with time now 1726138825456 and checkpoint time 1681502951625 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456344Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95153: 44635871867 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456345Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95153 with time now 1726138825456 and checkpoint time 1681502953589 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456344Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456347Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95154: 44635870852 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456349Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95154 with time now 1726138825456 and checkpoint time 1681502954604 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456349Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95143 with time now 1726138825456 and checkpoint time 1681502943558 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456351Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95156 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456351Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95146 with time now 1726138825456 and checkpoint time 1681502946543 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456352Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95153 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456352Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95157 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456352Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95151 for workflow primary in 60.253µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456352Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95158 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456355Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95143 for workflow primary in 72.275µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456354Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95154 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456355Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456355Z INFO sui_types::nats_queue: Received checkpoint with seq number 95151 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456356Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95156: 44635868920 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456356Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95157: 44635865405 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456356Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456357Z INFO sui_types::nats_queue: Received checkpoint with seq number 95143 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456358Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95156 with time now 1726138825456 and checkpoint time 1681502956536 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456358Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95157 with time now 1726138825456 and checkpoint time 1681502960051 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456358Z INFO sui_types::nats_queue: Received checkpoint with seq number 95151 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456358Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456357Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95158: 44635863443 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456358Z INFO sui_types::nats_queue: Received checkpoint with seq number 95146 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456360Z INFO sui_types::nats_queue: Received checkpoint with seq number 95143 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456358Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95146 for workflow primary in 84.197µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456360Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95158 with time now 1726138825456 and checkpoint time 1681502962013 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456361Z INFO sui_types::nats_queue: Received checkpoint with seq number 95146 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456360Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95144 with time now 1726138825456 and checkpoint time 1681502944547 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456362Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456365Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95157 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456367Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95149 with time now 1726138825456 and checkpoint time 1681502949515 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456369Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456372Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95156 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456372Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95159 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456373Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95158 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456374Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95159: 44635862999 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456374Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456375Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95159 with time now 1726138825456 and checkpoint time 1681502962457 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456376Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456376Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456376Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95144 for workflow primary in 82.986µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456376Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95150 with time now 1726138825456 and checkpoint time 1681502950609 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456379Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456378Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95160 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456380Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456380Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95148 with time now 1726138825456 and checkpoint time 1681502948554 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456381Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95159 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456383Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95160: 44635861956 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456385Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95160 with time now 1726138825456 and checkpoint time 1681502963500 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456384Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95149 for workflow primary in 110.657µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456386Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95153 with time now 1726138825456 and checkpoint time 1681502953589 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456386Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456386Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95150 for workflow primary in 87.974µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456389Z INFO sui_types::nats_queue: Received checkpoint with seq number 95144 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456389Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95160 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456390Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456391Z INFO sui_types::nats_queue: Received checkpoint with seq number 95149 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456390Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456390Z INFO sui_types::nats_queue: Received checkpoint with seq number 95144 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456392Z INFO sui_types::nats_queue: Received checkpoint with seq number 95150 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456392Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95153 for workflow primary in 51.526µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456393Z INFO sui_types::nats_queue: Received checkpoint with seq number 95153 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456393Z INFO sui_types::nats_queue: Received checkpoint with seq number 95148 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456393Z INFO sui_types::nats_queue: Received checkpoint with seq number 95149 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456393Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95148 for workflow primary in 115.456µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456395Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456395Z INFO sui_types::nats_queue: Received checkpoint with seq number 95150 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456395Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456394Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95152 with time now 1726138825456 and checkpoint time 1681502952551 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456396Z INFO sui_types::nats_queue: Received checkpoint with seq number 95148 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456397Z INFO sui_types::nats_queue: Received checkpoint with seq number 95153 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456400Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456400Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95161 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456404Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95163 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456405Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95162 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456405Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456405Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95161: 44635859592 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456407Z INFO sui_types::nats_queue: Received checkpoint with seq number 95152 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456406Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95152 for workflow primary in 93.424µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456407Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95161 with time now 1726138825456 and checkpoint time 1681502965864 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456408Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95163: 44635858089 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456409Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95163 with time now 1726138825456 and checkpoint time 1681502967367 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456410Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95156 with time now 1726138825456 and checkpoint time 1681502956536 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456410Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95162: 44635859171 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456411Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95162 with time now 1726138825456 and checkpoint time 1681502966285 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456416Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95157 with time now 1726138825456 and checkpoint time 1681502960051 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456417Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95165 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456417Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95156 for workflow primary in 65.563µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456417Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95161 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456419Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95165: 44635855454 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456418Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95162 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456419Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95165 with time now 1726138825456 and checkpoint time 1681502970002 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456418Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95154 with time now 1726138825456 and checkpoint time 1681502954604 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456418Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95163 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456420Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95155 with time now 1726138825456 and checkpoint time 1681502955544 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456421Z INFO sui_types::nats_queue: Received checkpoint with seq number 95156 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456423Z INFO sui_types::nats_queue: Received checkpoint with seq number 95157 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456422Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95157 for workflow primary in 69.37µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456423Z INFO sui_types::nats_queue: Received checkpoint with seq number 95152 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456424Z INFO sui_types::nats_queue: Received checkpoint with seq number 95156 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456425Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95164 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456425Z INFO sui_types::nats_queue: Received checkpoint with seq number 95157 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456427Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95164: 44635855873 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456428Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95164 with time now 1726138825456 and checkpoint time 1681502969583 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456430Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95165 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456431Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95164 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456431Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456430Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456431Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95159 with time now 1726138825456 and checkpoint time 1681502962457 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456431Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456433Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95160 with time now 1726138825456 and checkpoint time 1681502963500 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456433Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95158 with time now 1726138825456 and checkpoint time 1681502962013 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456435Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456435Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95167 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456437Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456436Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95154 for workflow primary in 95.528µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456437Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95167: 44635853490 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456436Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95155 for workflow primary in 111.528µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456439Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95167 with time now 1726138825456 and checkpoint time 1681502971966 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456439Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95160 for workflow primary in 58.9µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456438Z INFO sui_types::nats_queue: Received checkpoint with seq number 95154 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456441Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95158 for workflow primary in 86.702µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456441Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95159 for workflow primary in 68.098µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456443Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95167 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456442Z INFO sui_types::nats_queue: Received checkpoint with seq number 95155 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456444Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456444Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456445Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456445Z INFO sui_types::nats_queue: Received checkpoint with seq number 95154 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456445Z INFO sui_types::nats_queue: Received checkpoint with seq number 95160 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456446Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95166 for workflow primary +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456447Z INFO sui_types::nats_queue: Received checkpoint with seq number 95155 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456448Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456447Z INFO sui_types::nats_queue: Received checkpoint with seq number 95159 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456449Z INFO sui_types::nats_queue: Received checkpoint with seq number 95160 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456449Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 95166: 44635854479 ms +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456450Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456450Z INFO sui_types::nats_queue: Received checkpoint with seq number 95158 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456450Z INFO sui_types::nats_queue: Received checkpoint with seq number 95159 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456450Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 95166 with time now 1726138825456 and checkpoint time 1681502970977 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456451Z INFO sui_types::nats_queue: Received checkpoint with seq number 95158 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456463Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456467Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95165 with time now 1726138825456 and checkpoint time 1681502970002 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456468Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95164 with time now 1726138825456 and checkpoint time 1681502969583 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456469Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456473Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95164 for workflow primary in 47.749µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456473Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=95166 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456474Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95165 for workflow primary in 56.746µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456478Z INFO sui_types::nats_queue: Received checkpoint with seq number 95165 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456480Z INFO sui_types::nats_queue: Received checkpoint with seq number 95164 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456481Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456484Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95167 with time now 1726138825456 and checkpoint time 1681502971966 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456483Z INFO sui_types::nats_queue: Received checkpoint with seq number 95165 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456486Z INFO sui_types::nats_queue: Received checkpoint with seq number 95164 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456487Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95162 with time now 1726138825456 and checkpoint time 1681502966285 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456489Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95163 with time now 1726138825456 and checkpoint time 1681502967367 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456490Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95161 with time now 1726138825456 and checkpoint time 1681502965864 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456491Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95167 for workflow primary in 55.023µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456492Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95162 for workflow primary in 85.901µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456496Z INFO sui_types::nats_queue: Received checkpoint with seq number 95167 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456497Z INFO sui_types::nats_queue: Received checkpoint with seq number 95162 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456497Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95163 for workflow primary in 90.169µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456497Z INFO sui_types::nats_queue: Received checkpoint with seq number 95167 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456498Z INFO sui_types::nats_queue: Received checkpoint with seq number 95163 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456497Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456498Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95161 for workflow primary in 95.959µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456499Z INFO sui_types::nats_queue: Received checkpoint with seq number 95161 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456500Z INFO sui_types::nats_queue: Received checkpoint with seq number 95162 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456501Z INFO sui_types::nats_queue: Received checkpoint with seq number 95163 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456502Z INFO sui_types::nats_queue: Received checkpoint with seq number 95161 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456521Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: indexed checkpoint 95166 with time now 1726138825456 and checkpoint time 1681502970977 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456532Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95166 for workflow primary in 84.758µs +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456538Z INFO sui_types::nats_queue: Received checkpoint with seq number 95166 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.456542Z INFO sui_types::nats_queue: Received checkpoint with seq number 95166 but expected 85168 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.557136Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.658171Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.759153Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.860336Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:25 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:25.961510Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:26 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:26.061741Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:26 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:26.162947Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:26 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:26.264135Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:26 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:26.365341Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:26 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:26.466503Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:26 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:26.567684Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:26 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:26.668848Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:26 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:26.770019Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:26 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:26.871217Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:26 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:26.972378Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:27 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:27.073559Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:27 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:27.173716Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:27 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:27.274886Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:27 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:27.376079Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:27 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:27.477236Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:27 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:27.578110Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:27 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:27.679262Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:27 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:27.780439Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:27 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:27.881609Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:27 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:27.981775Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:28 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:28.082951Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:28 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:28.184108Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:28 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:28.285306Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:28 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:28.350510Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 85168: 44647223649 ms +Sep 12 13:00:28 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:28.350515Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 85168 with time now 1726138828350 and checkpoint time 1681491604701 +Sep 12 13:00:28 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:28.350544Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=85168 +Sep 12 13:00:28 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:28.351801Z INFO sui_data_ingestion_core::worker_pool: transient worker execution error `Failed to get network total transactions in epoch`: `Indexer failed to read PostgresDB with error: `Record not found`` for checkpoint 85168 +Sep 12 13:00:28 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:28.385906Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:28 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:28.487124Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:28 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:28.588317Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:28 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:28.689537Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:28 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:28.789791Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:28 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:28.891005Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:28 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:28.992246Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:29 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:29.093478Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:29 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:29.194711Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:29 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:29.295968Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:29 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:29.397134Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:29 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:29.498360Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:29 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:29.599553Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:29 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:29.699786Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:29 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:29.801005Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:29 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:29.902233Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:30 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:30.003500Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:30 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:30.104713Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:30 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:30.205960Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:30 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:30.307176Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:30 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:30.408410Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:30 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:30.509611Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:30 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:30.609805Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:30 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:30.711027Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:30 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:30.812217Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:30 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:30.913448Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:31 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:31.014670Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:31 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:31.116190Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:31 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:31.217448Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:31 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:31.318704Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:31 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:31.420102Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:31 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:31.521285Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:31 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:31.622527Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:31 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:31.722752Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:31 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:31.823957Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:31 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:31.925206Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:32 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:32.026422Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:32 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:32.127665Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:32 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:32.228888Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:32 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:32.330125Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:32 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:32.431388Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:32 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:32.532589Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:32 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:32.633685Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:32 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:32.734871Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:32 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:32.836105Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:32 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:32.937323Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:33 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:33.038551Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:33 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:33.138805Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:33 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:33.240025Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:33 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:33.341272Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:33 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:33.442495Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:33 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:33.543709Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:33 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:33.644906Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:33 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:33.746415Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:33 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:33.847637Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:33 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:33.948821Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:34 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:34.050033Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:34 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:34.151223Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:34 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:34.252490Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:34 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:34.352832Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:34 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:34.454055Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:34 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:34.555278Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:34 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:34.656471Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:34 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:34.757686Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:34 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:34.858880Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:34 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:34.960077Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:35 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:35.061301Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:35 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:35.162489Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:35 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:35.263736Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:35 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:35.364952Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:35 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:35.466165Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:35 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:35.567417Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:35 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:35.668640Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:35 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:35.770273Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:35 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:35.871503Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:35 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:35.971745Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:36 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:36.072973Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:36 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:36.174206Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:36 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:36.275480Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:36 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:36.376714Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:36 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:36.477941Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:36 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:36.579130Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:36 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:36.680364Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:36 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:36.781591Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:36 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:36.881818Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:36 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:36.983071Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:37 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:37.084288Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:37 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:37.185533Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:37 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:37.285755Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:37 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:37.386989Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:37 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:37.488225Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:37 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:37.589255Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:37 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:37.690487Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:37 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:37.791689Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:37 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:37.892927Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:37 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:37.994150Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:38 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:38.095380Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:38 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:38.196640Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:38 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:38.297856Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:38 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:38.399100Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:38 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:38.500296Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:38 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:38.601500Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:38 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:38.673721Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 85168: 44647233972 ms +Sep 12 13:00:38 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:38.673728Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 85168 with time now 1726138838673 and checkpoint time 1681491604701 +Sep 12 13:00:38 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:38.673775Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=85168 +Sep 12 13:00:38 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:38.674779Z INFO sui_data_ingestion_core::worker_pool: transient worker execution error `Failed to get network total transactions in epoch`: `Indexer failed to read PostgresDB with error: `Record not found`` for checkpoint 85168 +Sep 12 13:00:38 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:38.701823Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:38 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:38.803002Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:38 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:38.904199Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:39 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:39.005354Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:39 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:39.106545Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:39 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:39.207705Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:39 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:39.308880Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:39 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:39.409975Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:39 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:39.511156Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:39 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:39.612346Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:39 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:39.713499Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:39 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:39.814686Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:39 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:39.915857Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:40 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:40.017026Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:40 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:40.118216Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:40 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:40.219371Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:40 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:40.320553Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:40 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:40.420707Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:40 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:40.521880Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:40 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:40.623076Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:40 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:40.724238Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:40 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:40.825432Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:40 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:40.926588Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:41 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:41.026768Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:41 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:41.127927Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:41 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:41.229099Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:41 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:41.330326Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:41 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:41.431513Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:41 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:41.531732Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:41 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:41.632923Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:41 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:41.734159Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:41 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:41.835384Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:41 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:41.936615Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:42 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:42.036865Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:42 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:42.138079Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:42 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:42.239325Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:42 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:42.340537Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:42 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:42.440849Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:42 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:42.542017Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:42 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:42.642904Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:42 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:42.744153Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:42 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:42.845369Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:42 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:42.946614Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:43 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:43.046773Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:43 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:43.147937Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:43 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:43.249115Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:43 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:43.350300Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:43 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:43.451528Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:43 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:43.551766Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:43 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:43.652949Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:43 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:43.754129Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:43 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:43.855315Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:43 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:43.956531Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:44 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:44.057429Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:44 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:44.158610Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:44 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:44.258760Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:44 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:44.360684Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:44 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:44.461824Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:44 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:44.563014Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:44 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:44.664233Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:44 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:44.765385Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:44 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:44.866554Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:44 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:44.967715Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:45 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:45.068917Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:45 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:45.170143Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:45 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:45.271665Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:45 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:45.372908Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:45 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:45.474302Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:45 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:45.574857Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:45 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:45.676057Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:45 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:45.777243Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:45 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:45.878799Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:45 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:45.980792Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:46 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:46.082054Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:46 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:46.183274Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:46 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:46.284487Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:46 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:46.385059Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:46 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:46.486224Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:46 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:46.586927Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:46 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:46.688114Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:46 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:46.789312Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:46 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:46.890492Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:46 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:46.991679Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:47 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:47.092912Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:47 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:47.194652Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:47 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:47.295871Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:47 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:47.397061Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:47 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:47.498270Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:47 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:47.599020Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:47 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:47.700219Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:47 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:47.801444Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:47 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:47.902643Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:48 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:48.003857Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:48 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:48.105064Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:48 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:48.206298Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:48 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:48.307558Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:48 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:48.407782Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:48 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:48.509190Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:48 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:48.610684Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:48 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:48.712302Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:48 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:48.813483Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:48 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:48.914665Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:49 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:49.015873Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:49 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:49.117043Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:49 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:49.218241Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:49 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:49.319414Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:49 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:49.420367Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:49 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:49.521581Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:49 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:49.621760Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:49 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:49.722964Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:49 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:49.824135Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:49 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:49.925349Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:50 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:50.025944Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:50 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:50.127131Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:50 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:50.228343Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:50 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:50.329518Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:50 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:50.429720Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:50 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:50.530891Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:50 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:50.632138Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:50 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:50.733325Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:50 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:50.834859Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:50 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:50.936083Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:51 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:51.037271Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:51 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:51.138487Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:51 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:51.239678Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:51 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:51.340881Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:51 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:51.442112Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:51 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:51.543305Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:51 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:51.644535Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:51 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:51.744713Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:51 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:51.846302Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:51 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:51.946914Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:52 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:52.048114Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:52 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:52.149324Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:52 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:52.250497Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:52 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:52.351697Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:52 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:52.452867Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:52 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:52.554052Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:52 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:52.654971Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:52 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:52.756154Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:52 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:52.856790Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:52 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:52.957969Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:53 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:53.059242Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:53 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:53.160433Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:53 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:53.261635Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:53 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:53.362864Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:53 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:53.464057Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:53 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:53.565274Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:53 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:53.666448Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:53 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:53.767667Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:53 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:53.868848Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:53 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:53.970028Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:54 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:54.071234Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:54 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:54.172403Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:54 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:54.273598Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:54 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:54.374544Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:54 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:54.474748Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:54 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:54.575969Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:54 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:54.677155Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:54 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:54.778381Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:54 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:54.879566Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:54 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:54.979810Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:55 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:55.081055Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:55 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:55.182268Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:55 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:55.283495Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:55 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:55.325634Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 85168: 44647250624 ms +Sep 12 13:00:55 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:55.325640Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 85168 with time now 1726138855325 and checkpoint time 1681491604701 +Sep 12 13:00:55 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:55.325676Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=85168 +Sep 12 13:00:55 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:55.326629Z INFO sui_data_ingestion_core::worker_pool: transient worker execution error `Failed to get network total transactions in epoch`: `Indexer failed to read PostgresDB with error: `Record not found`` for checkpoint 85168 +Sep 12 13:00:55 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:55.383778Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:55 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:55.485289Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:55 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:55.586509Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:55 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:55.686751Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:55 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:55.787978Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:55 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:55.889206Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:55 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:55.990474Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:56 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:56.091691Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:56 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:56.192938Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:56 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:56.294146Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:56 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:56.395382Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:56 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:56.496618Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:56 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:56.597802Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:56 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:56.699055Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:56 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:56.799741Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:56 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:56.901413Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:57 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:57.002635Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:57 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:57.103865Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:57 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:57.205123Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:57 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:57.306343Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:57 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:57.407590Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:57 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:57.507791Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:57 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:57.608813Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:57 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:57.710319Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:57 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:57.811515Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:57 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:57.911741Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:58 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:58.013019Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:58 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:58.114414Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:58 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:58.215782Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:58 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:58.317185Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:58 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:58.418617Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:58 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:58.520004Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:58 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:58.621427Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:58 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:58.722709Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:58 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:58.823976Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:58 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:58.925179Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:59 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:59.026398Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:59 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:59.126826Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:59 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:59.228219Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:59 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:59.329633Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:59 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:59.430932Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:59 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:59.532331Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:59 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:59.633766Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:59 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:59.735162Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:59 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:59.836588Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:00:59 sui-mainnet-node cargo[3499927]: 2024-09-12T11:00:59.936968Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:00 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:00.038374Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:00 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:00.139765Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:00 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:00.241165Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:00 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:00.342593Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:00 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:00.442899Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:00 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:00.544191Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:00 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:00.645579Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:00 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:00.745994Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:00 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:00.847261Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:00 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:00.948674Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:01 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:01.050101Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:01 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:01.151401Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:01 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:01.252815Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:01 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:01.354218Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:01 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:01.455621Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:01 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:01.557033Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:01 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:01.658334Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:01 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:01.737621Z INFO sui_indexer::db::setup_postgres: DB connection pool size: 100, with idle conn: 100. +Sep 12 13:01:01 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:01.758844Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:01 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:01.860146Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:01 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:01.961555Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:02 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:02.062827Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:02 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:02.164137Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:02 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:02.265391Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:02 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:02.366754Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:02 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:02.468148Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:02 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:02.569534Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:02 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:02.669857Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:02 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:02.771291Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:02 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:02.872559Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:02 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:02.972980Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:03 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:03.074365Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:03 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:03.175688Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:03 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:03.277088Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:03 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:03.378501Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:03 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:03.478833Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:03 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:03.580220Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:03 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:03.681634Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:03 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:03.783029Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:03 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:03.884435Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:03 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:03.984871Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:04 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:04.086263Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:04 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:04.187690Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:04 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:04.289078Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:04 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:04.390494Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:04 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:04.490873Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:04 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:04.592247Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:04 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:04.693651Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:04 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:04.795038Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:04 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:04.896451Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:04 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:04.996814Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:05 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:05.098221Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:05 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:05.199600Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:05 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:05.299997Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:05 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:05.401419Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:05 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:05.502789Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:05 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:05.604195Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:05 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:05.705588Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:05 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:05.805909Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:05 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:05.907336Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:06 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:06.008645Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:06 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:06.109975Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:06 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:06.211363Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:06 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:06.312687Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:06 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:06.413948Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:06 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:06.515255Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:06 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:06.616681Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:06 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:06.718073Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:06 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:06.819324Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:06 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:06.920638Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:07 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:07.022207Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:07 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:07.123555Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:07 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:07.223952Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:07 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:07.325376Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:07 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:07.426766Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:07 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:07.528092Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:07 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:07.629440Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:07 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:07.729852Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:07 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:07.831277Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:07 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:07.932670Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:08 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:08.034058Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:08 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:08.135472Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:08 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:08.235882Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:08 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:08.337316Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:08 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:08.438712Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:08 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:08.540127Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:08 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:08.641514Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:08 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:08.741926Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:08 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:08.843325Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:08 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:08.944727Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:09 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:09.046158Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:09 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:09.147550Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:09 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:09.247969Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:09 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:09.349357Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:09 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:09.450496Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:09 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:09.550741Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:09 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:09.652130Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:09 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:09.753557Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:09 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:09.853945Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:09 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:09.939399Z INFO sui_indexer::handlers::checkpoint_handler: checkpoint download lag for cp 85168: 44647265238 ms +Sep 12 13:01:09 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:09.939408Z INFO sui_indexer::handlers::checkpoint_handler: Indexer lag: downloaded checkpoint 85168 with time now 1726138869939 and checkpoint time 1681491604701 +Sep 12 13:01:09 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:09.939487Z INFO sui_indexer::handlers::checkpoint_handler: Indexing checkpoint data blob checkpoint_seq=85168 +Sep 12 13:01:09 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:09.940990Z INFO sui_data_ingestion_core::worker_pool: transient worker execution error `Failed to get network total transactions in epoch`: `Indexer failed to read PostgresDB with error: `Record not found`` for checkpoint 85168 +Sep 12 13:01:09 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:09.955124Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:10 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:10.056519Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:10 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:10.156920Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:10 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:10.258178Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:10 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:10.359572Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:10 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:10.459990Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:10 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:10.561371Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:10 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:10.662770Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:10 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:10.764166Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:10 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:10.865363Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:10 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:10.966588Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:11 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:11.066861Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:11 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:11.168252Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:11 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:11.269618Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:11 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:11.369998Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:11 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:11.471406Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:11 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:11.572778Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:11 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:11.673998Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:11 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:11.775372Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:11 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:11.876590Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:11 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:11.976913Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:12 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:12.078108Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:12 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:12.179365Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:12 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:12.280676Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:12 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:12.382092Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:12 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:12.483495Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:12 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:12.584671Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:12 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:12.685844Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:12 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:12.787240Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:12 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:12.888665Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:12 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:12.990051Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:13 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:13.091476Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:13 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:13.191864Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:13 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:13.293267Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:13 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:13.394695Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:13 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:13.496090Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:13 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:13.597509Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:13 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:13.698807Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:13 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:13.800216Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:13 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:13.901601Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:14 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:14.001911Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:14 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:14.103334Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:14 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:14.204704Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:14 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:14.306116Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:14 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:14.407156Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:14 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:14.508561Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:14 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:14.608918Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:14 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:14.710308Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:14 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:14.811732Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:14 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:14.913119Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:15 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:15.014531Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:15 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:15.114923Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:15 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:15.216497Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:15 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:15.316839Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:15 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:15.418234Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:15 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:15.519665Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:15 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:15.621054Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:15 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:15.722473Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:15 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:15.822740Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:15 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:15.924139Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:16 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:16.025563Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:16 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:16.125950Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:16 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:16.227365Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:16 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:16.328757Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:16 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:16.430159Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:16 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:16.531595Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:16 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:16.631993Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:16 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:16.733413Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:16 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:16.834692Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:16 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:16.935890Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:17 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:17.037121Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:17 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:17.138431Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:17 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:17.238857Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:17 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:17.340157Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:17 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:17.441572Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:17 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:17.541961Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:17 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:17.643518Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:17 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:17.743946Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:17 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:17.845343Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:17 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:17.946764Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:18 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:18.048147Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:18 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:18.149558Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:18 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:18.249950Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:18 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:18.351353Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:18 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:18.452781Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:18 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:18.554174Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:18 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:18.655505Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:18 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:18.755844Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:18 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:18.857249Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:18 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:18.958513Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:19 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:19.058911Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:19 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:19.160335Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:19 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:19.261718Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:19 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:19.363117Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:19 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:19.464510Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:19 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:19.564846Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:19 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:19.666272Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:19 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:19.767578Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:19 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:19.868000Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:19 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:19.969383Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:20 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:20.070792Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:20 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:20.172187Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:20 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:20.273586Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:20 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:20.374013Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:20 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:20.475404Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:20 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:20.576818Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:20 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:20.678205Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:20 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:20.779607Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:20 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:20.880038Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:20 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:20.981435Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:21 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:21.081857Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:21 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:21.183242Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:21 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:21.284667Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:21 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:21.386059Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:21 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:21.487476Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:21 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:21.587903Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:21 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:21.689292Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:21 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:21.790709Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:21 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:21.892095Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:21 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:21.993347Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:22 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:22.094543Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:22 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:22.194783Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:22 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:22.296046Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:22 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:22.397434Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:22 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:22.497845Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:22 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:22.599075Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:22 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:22.700494Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:22 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:22.800923Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:22 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:22.902316Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:23 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:23.003651Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:23 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:23.105038Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:23 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:23.206361Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:23 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:23.307604Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:23 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:23.408159Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:23 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:23.509585Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:23 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:23.609982Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:23 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:23.711397Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:23 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:23.812786Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:23 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:23.914195Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:24 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:24.015630Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:24 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:24.117022Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:24 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:24.218440Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:24 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:24.318842Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:24 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:24.420130Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:24 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:24.521522Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:24 sui-mainnet-node cargo[3499927]: 2024-09-12T11:01:24.621923Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 95168, pruning watermark: 85168, new updates: 0 +Sep 12 13:01:24 sui-mainnet-node systemd[1]: Stopping sui-indexer.service - Sui indexer... +Sep 12 13:01:24 sui-mainnet-node systemd[1]: sui-indexer.service: Deactivated successfully. +Sep 12 13:01:24 sui-mainnet-node systemd[1]: Stopped sui-indexer.service - Sui indexer. +Sep 12 13:01:24 sui-mainnet-node systemd[1]: sui-indexer.service: Consumed 57.035s CPU time. +Sep 12 13:25:37 sui-mainnet-node systemd[1]: Started sui-indexer.service - Sui indexer. +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: /root/sui/crates/sui-indexer/Cargo.toml: unused manifest key: dependencies.odin.version +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: /root/sui/crates/sui-data-ingestion-core/Cargo.toml: unused manifest key: dependencies.odin.version +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: /root/sui/crates/sui-types/Cargo.toml: unused manifest key: dependencies.odin.version +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: /root/sui/crates/sui-json-rpc/Cargo.toml: unused manifest key: dependencies.odin.version +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: unused macro definition: `debug_print_format` +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: --> external-crates/move/crates/move-compiler/src/shared/string_utils.rs:99:14 +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 99 | macro_rules! debug_print_format { +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: = note: `#[warn(unused_macros)]` on by default +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: unused import: `debug_print_format` +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: --> external-crates/move/crates/move-compiler/src/shared/string_utils.rs:117:16 +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 117 | pub(crate) use debug_print_format; +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: = note: `#[warn(unused_imports)]` on by default +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: unused macro definition: `debug_print_internal` +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: --> external-crates/move/crates/move-compiler/src/shared/string_utils.rs:127:14 +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 127 | macro_rules! debug_print_internal { +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: unused import: `debug_print_internal` +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: --> external-crates/move/crates/move-compiler/src/shared/string_utils.rs:168:16 +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 168 | pub(crate) use debug_print_internal; +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: unused import: `AstDebug` +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: --> external-crates/move/crates/move-compiler/src/typing/expand.rs:12:61 +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 12 | shared::{ide::IDEAnnotation, string_utils::debug_print, AstDebug}, +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: unused import: `ast_debug::AstDebug` +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: --> external-crates/move/crates/move-compiler/src/typing/match_analysis.rs:11:9 +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 11 | ast_debug::AstDebug, +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: fields `match_variant_translation`, `match_translation`, `match_specialization`, `match_work_queue`, `function_translation`, and `eval_order` are never read +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: --> external-crates/move/crates/move-compiler/src/hlir/translate.rs:122:16 +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 121 | pub(super) struct HLIRDebugFlags { +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | -------------- fields in this struct +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 122 | pub(super) match_variant_translation: bool, +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 123 | pub(super) match_translation: bool, +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 124 | pub(super) match_specialization: bool, +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 125 | pub(super) match_work_queue: bool, +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 126 | pub(super) function_translation: bool, +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 127 | pub(super) eval_order: bool, +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: = note: `#[warn(dead_code)]` on by default +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: field `debug` is never read +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: --> external-crates/move/crates/move-compiler/src/hlir/translate.rs:133:9 +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 130 | pub(super) struct Context<'env> { +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ------- field in this struct +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: ... +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 133 | pub debug: HLIRDebugFlags, +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: fields `match_counterexample`, `autocomplete_resolution`, `function_translation`, and `type_elaboration` are never read +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: --> external-crates/move/crates/move-compiler/src/typing/core.rs:85:16 +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 84 | pub(super) struct TypingDebugFlags { +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ---------------- fields in this struct +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 85 | pub(super) match_counterexample: bool, +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 86 | pub(super) autocomplete_resolution: bool, +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 87 | pub(super) function_translation: bool, +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 88 | pub(super) type_elaboration: bool, +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: field `debug` is never read +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: --> external-crates/move/crates/move-compiler/src/typing/core.rs:95:16 +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 91 | pub struct Context<'env> { +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ------- field in this struct +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: ... +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 95 | pub(super) debug: TypingDebugFlags, +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: `move-compiler` (lib) generated 10 warnings (run `cargo fix --lib -p move-compiler` to apply 4 suggestions) +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: unused import: `ClientOptions` +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: --> crates/sui-config/src/object_storage_config.rs:8:20 +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 8 | use object_store::{ClientOptions, DynObjectStore}; +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: = note: `#[warn(unused_imports)]` on by default +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: unused imports: `HeaderMap`, `HeaderName`, and `HeaderValue` +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: --> crates/sui-config/src/object_storage_config.rs:9:23 +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 9 | use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: `sui-config` (lib) generated 2 warnings (run `cargo fix --lib -p sui-config` to apply 2 suggestions) +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: use of deprecated method `tonic::transport::server::Router::::into_router`: Use `Routes::into_axum_router` instead. +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: --> consensus/core/src/network/tonic_network.rs:696:14 +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 696 | .into_router(); +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: = note: `#[warn(deprecated)]` on by default +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: `consensus-core` (lib) generated 1 warning +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: unused import: `sui_types::nats_queue::NatsQueueSender` +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: --> crates/sui-data-ingestion-core/src/executor.rs:19:5 +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 19 | use sui_types::nats_queue::NatsQueueSender; +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: = note: `#[warn(unused_imports)]` on by default +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: `sui-data-ingestion-core` (lib) generated 1 warning (run `cargo fix --lib -p sui-data-ingestion-core` to apply 1 suggestion) +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: unused variable: `ccp_digest` +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: --> crates/sui-core/src/checkpoints/mod.rs:1106:22 +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: 1106 | if let Some((ccp_digest, ccp_effects)) = consensus_commit_prologue { +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_ccp_digest` +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: = note: `#[warn(unused_variables)]` on by default +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: warning: `sui-core` (lib) generated 1 warning +Sep 12 13:25:37 sui-mainnet-node cargo[3519529]: Compiling sui-indexer v1.32.3 (/root/sui/crates/sui-indexer) +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: warning: unused import: `sui_package_resolver::PackageStore` +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: --> crates/sui-indexer/src/handlers/tx_processor.rs:14:5 +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: 14 | use sui_package_resolver::PackageStore; +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: = note: `#[warn(unused_imports)]` on by default +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: warning: unused import: `sui_package_resolver::Resolver` +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: --> crates/sui-indexer/src/handlers/tx_processor.rs:15:5 +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: 15 | use sui_package_resolver::Resolver; +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: warning: unused import: `sui_types::base_types::SuiAddress` +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: --> crates/sui-indexer/src/handlers/tx_processor.rs:17:5 +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: | +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: 17 | use sui_types::base_types::SuiAddress; +Sep 12 13:25:46 sui-mainnet-node cargo[3519529]: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:31:48 sui-mainnet-node cargo[3519529]: warning: `sui-indexer` (lib) generated 3 warnings (run `cargo fix --lib -p sui-indexer` to apply 3 suggestions) +Sep 12 13:35:04 sui-mainnet-node systemd[1]: Stopping sui-indexer.service - Sui indexer... +Sep 12 13:35:04 sui-mainnet-node systemd[1]: sui-indexer.service: Deactivated successfully. +Sep 12 13:35:04 sui-mainnet-node systemd[1]: Stopped sui-indexer.service - Sui indexer. +Sep 12 13:35:04 sui-mainnet-node systemd[1]: sui-indexer.service: Consumed 9min 28.040s CPU time. +Sep 12 13:35:04 sui-mainnet-node systemd[1]: Started sui-indexer.service - Sui indexer. +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: /root/sui/crates/sui-data-ingestion-core/Cargo.toml: unused manifest key: dependencies.odin.version +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: /root/sui/crates/sui-types/Cargo.toml: unused manifest key: dependencies.odin.version +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: /root/sui/crates/sui-indexer/Cargo.toml: unused manifest key: dependencies.odin.version +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: /root/sui/crates/sui-json-rpc/Cargo.toml: unused manifest key: dependencies.odin.version +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: unused macro definition: `debug_print_format` +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> external-crates/move/crates/move-compiler/src/shared/string_utils.rs:99:14 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 99 | macro_rules! debug_print_format { +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: = note: `#[warn(unused_macros)]` on by default +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: unused import: `debug_print_format` +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> external-crates/move/crates/move-compiler/src/shared/string_utils.rs:117:16 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 117 | pub(crate) use debug_print_format; +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: = note: `#[warn(unused_imports)]` on by default +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: unused macro definition: `debug_print_internal` +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> external-crates/move/crates/move-compiler/src/shared/string_utils.rs:127:14 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 127 | macro_rules! debug_print_internal { +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: unused import: `debug_print_internal` +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> external-crates/move/crates/move-compiler/src/shared/string_utils.rs:168:16 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 168 | pub(crate) use debug_print_internal; +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: unused import: `AstDebug` +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> external-crates/move/crates/move-compiler/src/typing/expand.rs:12:61 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 12 | shared::{ide::IDEAnnotation, string_utils::debug_print, AstDebug}, +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: unused import: `ast_debug::AstDebug` +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> external-crates/move/crates/move-compiler/src/typing/match_analysis.rs:11:9 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 11 | ast_debug::AstDebug, +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: fields `match_variant_translation`, `match_translation`, `match_specialization`, `match_work_queue`, `function_translation`, and `eval_order` are never read +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> external-crates/move/crates/move-compiler/src/hlir/translate.rs:122:16 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 121 | pub(super) struct HLIRDebugFlags { +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | -------------- fields in this struct +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 122 | pub(super) match_variant_translation: bool, +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 123 | pub(super) match_translation: bool, +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 124 | pub(super) match_specialization: bool, +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 125 | pub(super) match_work_queue: bool, +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 126 | pub(super) function_translation: bool, +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 127 | pub(super) eval_order: bool, +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: = note: `#[warn(dead_code)]` on by default +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: field `debug` is never read +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> external-crates/move/crates/move-compiler/src/hlir/translate.rs:133:9 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 130 | pub(super) struct Context<'env> { +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ------- field in this struct +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: ... +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 133 | pub debug: HLIRDebugFlags, +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: fields `match_counterexample`, `autocomplete_resolution`, `function_translation`, and `type_elaboration` are never read +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> external-crates/move/crates/move-compiler/src/typing/core.rs:85:16 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 84 | pub(super) struct TypingDebugFlags { +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ---------------- fields in this struct +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 85 | pub(super) match_counterexample: bool, +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 86 | pub(super) autocomplete_resolution: bool, +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 87 | pub(super) function_translation: bool, +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 88 | pub(super) type_elaboration: bool, +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: field `debug` is never read +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> external-crates/move/crates/move-compiler/src/typing/core.rs:95:16 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 91 | pub struct Context<'env> { +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ------- field in this struct +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: ... +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 95 | pub(super) debug: TypingDebugFlags, +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: `move-compiler` (lib) generated 10 warnings (run `cargo fix --lib -p move-compiler` to apply 4 suggestions) +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: unused import: `ClientOptions` +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> crates/sui-config/src/object_storage_config.rs:8:20 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 8 | use object_store::{ClientOptions, DynObjectStore}; +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: = note: `#[warn(unused_imports)]` on by default +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: unused imports: `HeaderMap`, `HeaderName`, and `HeaderValue` +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> crates/sui-config/src/object_storage_config.rs:9:23 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 9 | use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: `sui-config` (lib) generated 2 warnings (run `cargo fix --lib -p sui-config` to apply 2 suggestions) +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: use of deprecated method `tonic::transport::server::Router::::into_router`: Use `Routes::into_axum_router` instead. +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> consensus/core/src/network/tonic_network.rs:696:14 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 696 | .into_router(); +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: = note: `#[warn(deprecated)]` on by default +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: `consensus-core` (lib) generated 1 warning +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: unused import: `sui_types::nats_queue::NatsQueueSender` +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> crates/sui-data-ingestion-core/src/executor.rs:19:5 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 19 | use sui_types::nats_queue::NatsQueueSender; +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: = note: `#[warn(unused_imports)]` on by default +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: `sui-data-ingestion-core` (lib) generated 1 warning (run `cargo fix --lib -p sui-data-ingestion-core` to apply 1 suggestion) +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: unused variable: `ccp_digest` +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> crates/sui-core/src/checkpoints/mod.rs:1106:22 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 1106 | if let Some((ccp_digest, ccp_effects)) = consensus_commit_prologue { +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_ccp_digest` +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: = note: `#[warn(unused_variables)]` on by default +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: `sui-core` (lib) generated 1 warning +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: unused import: `sui_package_resolver::PackageStore` +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> crates/sui-indexer/src/handlers/tx_processor.rs:14:5 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 14 | use sui_package_resolver::PackageStore; +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: = note: `#[warn(unused_imports)]` on by default +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: unused import: `sui_package_resolver::Resolver` +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> crates/sui-indexer/src/handlers/tx_processor.rs:15:5 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 15 | use sui_package_resolver::Resolver; +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: unused import: `sui_types::base_types::SuiAddress` +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: --> crates/sui-indexer/src/handlers/tx_processor.rs:17:5 +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: 17 | use sui_types::base_types::SuiAddress; +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: warning: `sui-indexer` (lib) generated 3 warnings (run `cargo fix --lib -p sui-indexer` to apply 3 suggestions) +Sep 12 13:35:05 sui-mainnet-node cargo[3519948]: Compiling sui-indexer v1.32.3 (/root/sui/crates/sui-indexer) +Sep 12 13:37:40 sui-mainnet-node systemd[1]: Stopping sui-indexer.service - Sui indexer... +Sep 12 13:37:41 sui-mainnet-node systemd[1]: sui-indexer.service: Deactivated successfully. +Sep 12 13:37:41 sui-mainnet-node systemd[1]: Stopped sui-indexer.service - Sui indexer. +Sep 12 13:37:41 sui-mainnet-node systemd[1]: sui-indexer.service: Consumed 2min 36.407s CPU time. +Sep 12 13:37:41 sui-mainnet-node systemd[1]: Started sui-indexer.service - Sui indexer. +Sep 12 13:37:41 sui-mainnet-node cargo[3520474]: warning: /root/sui/crates/sui-json-rpc/Cargo.toml: unused manifest key: dependencies.odin.version +Sep 12 13:37:41 sui-mainnet-node cargo[3520474]: warning: /root/sui/crates/sui-data-ingestion-core/Cargo.toml: unused manifest key: dependencies.odin.version +Sep 12 13:37:41 sui-mainnet-node cargo[3520474]: warning: /root/sui/crates/sui-types/Cargo.toml: unused manifest key: dependencies.odin.version +Sep 12 13:37:41 sui-mainnet-node cargo[3520474]: warning: /root/sui/crates/sui-indexer/Cargo.toml: unused manifest key: dependencies.odin.version +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: unused macro definition: `debug_print_format` +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> external-crates/move/crates/move-compiler/src/shared/string_utils.rs:99:14 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 99 | macro_rules! debug_print_format { +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: = note: `#[warn(unused_macros)]` on by default +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: unused import: `debug_print_format` +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> external-crates/move/crates/move-compiler/src/shared/string_utils.rs:117:16 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 117 | pub(crate) use debug_print_format; +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: = note: `#[warn(unused_imports)]` on by default +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: unused macro definition: `debug_print_internal` +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> external-crates/move/crates/move-compiler/src/shared/string_utils.rs:127:14 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 127 | macro_rules! debug_print_internal { +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: unused import: `debug_print_internal` +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> external-crates/move/crates/move-compiler/src/shared/string_utils.rs:168:16 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 168 | pub(crate) use debug_print_internal; +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: unused import: `AstDebug` +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> external-crates/move/crates/move-compiler/src/typing/expand.rs:12:61 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 12 | shared::{ide::IDEAnnotation, string_utils::debug_print, AstDebug}, +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: unused import: `ast_debug::AstDebug` +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> external-crates/move/crates/move-compiler/src/typing/match_analysis.rs:11:9 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 11 | ast_debug::AstDebug, +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: fields `match_variant_translation`, `match_translation`, `match_specialization`, `match_work_queue`, `function_translation`, and `eval_order` are never read +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> external-crates/move/crates/move-compiler/src/hlir/translate.rs:122:16 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 121 | pub(super) struct HLIRDebugFlags { +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | -------------- fields in this struct +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 122 | pub(super) match_variant_translation: bool, +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 123 | pub(super) match_translation: bool, +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 124 | pub(super) match_specialization: bool, +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 125 | pub(super) match_work_queue: bool, +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 126 | pub(super) function_translation: bool, +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 127 | pub(super) eval_order: bool, +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: = note: `#[warn(dead_code)]` on by default +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: field `debug` is never read +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> external-crates/move/crates/move-compiler/src/hlir/translate.rs:133:9 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 130 | pub(super) struct Context<'env> { +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ------- field in this struct +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: ... +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 133 | pub debug: HLIRDebugFlags, +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: fields `match_counterexample`, `autocomplete_resolution`, `function_translation`, and `type_elaboration` are never read +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> external-crates/move/crates/move-compiler/src/typing/core.rs:85:16 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 84 | pub(super) struct TypingDebugFlags { +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ---------------- fields in this struct +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 85 | pub(super) match_counterexample: bool, +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 86 | pub(super) autocomplete_resolution: bool, +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 87 | pub(super) function_translation: bool, +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 88 | pub(super) type_elaboration: bool, +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: field `debug` is never read +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> external-crates/move/crates/move-compiler/src/typing/core.rs:95:16 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 91 | pub struct Context<'env> { +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ------- field in this struct +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: ... +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 95 | pub(super) debug: TypingDebugFlags, +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: `move-compiler` (lib) generated 10 warnings (run `cargo fix --lib -p move-compiler` to apply 4 suggestions) +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: unused import: `ClientOptions` +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> crates/sui-config/src/object_storage_config.rs:8:20 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 8 | use object_store::{ClientOptions, DynObjectStore}; +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: = note: `#[warn(unused_imports)]` on by default +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: unused imports: `HeaderMap`, `HeaderName`, and `HeaderValue` +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> crates/sui-config/src/object_storage_config.rs:9:23 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 9 | use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: `sui-config` (lib) generated 2 warnings (run `cargo fix --lib -p sui-config` to apply 2 suggestions) +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: use of deprecated method `tonic::transport::server::Router::::into_router`: Use `Routes::into_axum_router` instead. +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> consensus/core/src/network/tonic_network.rs:696:14 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 696 | .into_router(); +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: = note: `#[warn(deprecated)]` on by default +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: `consensus-core` (lib) generated 1 warning +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: unused import: `sui_types::nats_queue::NatsQueueSender` +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> crates/sui-data-ingestion-core/src/executor.rs:19:5 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 19 | use sui_types::nats_queue::NatsQueueSender; +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: = note: `#[warn(unused_imports)]` on by default +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: `sui-data-ingestion-core` (lib) generated 1 warning (run `cargo fix --lib -p sui-data-ingestion-core` to apply 1 suggestion) +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: unused variable: `ccp_digest` +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> crates/sui-core/src/checkpoints/mod.rs:1106:22 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 1106 | if let Some((ccp_digest, ccp_effects)) = consensus_commit_prologue { +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_ccp_digest` +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: = note: `#[warn(unused_variables)]` on by default +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: `sui-core` (lib) generated 1 warning +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: unused import: `sui_package_resolver::PackageStore` +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> crates/sui-indexer/src/handlers/tx_processor.rs:14:5 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 14 | use sui_package_resolver::PackageStore; +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: = note: `#[warn(unused_imports)]` on by default +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: unused import: `sui_package_resolver::Resolver` +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> crates/sui-indexer/src/handlers/tx_processor.rs:15:5 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 15 | use sui_package_resolver::Resolver; +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: unused import: `sui_types::base_types::SuiAddress` +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: --> crates/sui-indexer/src/handlers/tx_processor.rs:17:5 +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: 17 | use sui_types::base_types::SuiAddress; +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: warning: `sui-indexer` (lib) generated 3 warnings (run `cargo fix --lib -p sui-indexer` to apply 3 suggestions) +Sep 12 13:37:42 sui-mainnet-node cargo[3520474]: Compiling sui-indexer v1.32.3 (/root/sui/crates/sui-indexer) +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: Finished `release` profile [optimized + debuginfo] target(s) in 3m 51s +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: Running `/root/sui/target/release/sui-indexer --db-url 'postgres://postgres:postgres@localhost/sui_indexer_v2' --rpc-client-url 'http://0.0.0.0:9000' --fullnode-sync-worker --reset-db` +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:32.951608Z WARN sui_indexer: WARNING: Sui indexer is still experimental and we expect occasional breaking changes that require backfills. +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:32.951715Z INFO sui_indexer: Parsed indexer config: IndexerConfig { +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: db_url: Some( +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: Secret([REDACTED alloc::string::String]), +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: ), +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: db_user_name: None, +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: db_password: None, +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: db_host: None, +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: db_port: None, +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: db_name: None, +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: rpc_client_url: "http://0.0.0.0:9000", +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: remote_store_url: Some( +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: "https://checkpoints.mainnet.sui.io", +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: ), +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: client_metric_host: "0.0.0.0", +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: client_metric_port: 9184, +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: rpc_server_url: "0.0.0.0", +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: rpc_server_port: 9000, +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: reset_db: true, +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: fullnode_sync_worker: true, +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: rpc_server_worker: false, +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: data_ingestion_path: None, +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: name_service_package_address: None, +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: name_service_registry_id: None, +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: name_service_reverse_registry_id: None, +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: } +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:32.951800Z WARN sui_indexer::metrics: Failed to convert full node url http://0.0.0.0:9000 to a shorter version +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:32.951810Z INFO sui_indexer::metrics: Starting prometheus server with labels: {"indexer_fullnode": "unknown_url"} +Sep 12 13:41:32 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:32.954521Z INFO async_nats: event: connected +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.339265Z INFO sui_indexer::db::setup_postgres: Postgres database connection pool is created at postgres://postgres:postgres@localhost/sui_indexer_v2 +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.339727Z INFO sui_indexer::db::setup_postgres: Resetting PG database ... +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.436418Z INFO sui_indexer::db::setup_postgres: Dropped all tables. +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.437832Z INFO sui_indexer::db::setup_postgres: Dropped all procedures. +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.438450Z INFO sui_indexer::db::setup_postgres: Dropped all functions. +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.440543Z INFO sui_indexer::db::setup_postgres: Created __diesel_schema_migrations table. +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.767319Z INFO sui_indexer::db::setup_postgres: Reset database complete. +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.767328Z INFO sui_indexer::db::setup_postgres: Reset Postgres database complete. +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.767540Z INFO sui_indexer::db::setup_postgres: DB connection pool size: 100, with idle conn: 99. +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.769394Z INFO sui_indexer::store::pg_partition_manager: Found 4 tables with partitions : [{"events": (0, 0), "objects_history": (0, 0), "objects_version": (0, 99), "transactions": (0, 0)}] +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.769477Z INFO sui_indexer::indexer: Sui Indexer Writer (version "1.32.3") started... +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.769482Z INFO sui_indexer::handlers::objects_snapshot_processor: Starting object snapshot processor... +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.770452Z INFO sui_indexer::indexer: Starting indexer pruner with epochs to keep: 1 +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.770490Z INFO sui_indexer::handlers::objects_snapshot_processor: Starting objects snapshot committer... +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.771603Z INFO sui_indexer::store::pg_partition_manager: Found 4 tables with partitions : [{"events": (0, 0), "objects_history": (0, 0), "objects_version": (0, 99), "transactions": (0, 0)}] +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.771964Z INFO sui_types::nats_queue: Nats queue init checkpoint: 56614153 +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.771968Z INFO sui_types::nats_queue: Nats notifications queue init checkpoint: 56614153 +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.771974Z INFO sui_indexer::indexer: Starting data ingestion executor... +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.771984Z INFO sui_indexer::handlers::committer: Indexer checkpoint commit task started... +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.771986Z INFO sui_indexer::handlers::committer: Using checkpoint commit batch size 100 +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.772204Z INFO sui_data_ingestion_core::worker_pool: Starting indexing pipeline primary with concurrency 200. Current watermark is 56614153. +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.772204Z INFO sui_data_ingestion_core::worker_pool: Starting indexing pipeline object_snapshot with concurrency 200. Current watermark is 0. +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.772353Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 0 +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.894569Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 0, pruning watermark: 0, new updates: 0 +Sep 12 13:41:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:33.995586Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 0, pruning watermark: 0, new updates: 0 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.096850Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 0, pruning watermark: 0, new updates: 0 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.197631Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 0, pruning watermark: 0, new updates: 0 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.299487Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 0, pruning watermark: 0, new updates: 4 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.301078Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 3 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.301080Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 0 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.301089Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 1 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.301126Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.301126Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.301124Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 2 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.301141Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.302371Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 9. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.302531Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 3 for workflow object_snapshot in 1.447546ms +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.303290Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 2 for workflow object_snapshot in 2.158915ms +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.303423Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 1 for workflow object_snapshot in 2.328532ms +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.304723Z INFO sui_types::object::bounded_visitor: Using default value for 'MAX_ANNOTATED_VALUE_SIZE' -- max bound: 1048576 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.330787Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 0 for workflow object_snapshot in 29.699977ms +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.331033Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 4 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.432620Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 4, pruning watermark: 4, new updates: 32 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433068Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 7 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433086Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433087Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 22 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433099Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433105Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 7 for workflow object_snapshot in 29.344µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433111Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 4 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433119Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433120Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 22 for workflow object_snapshot in 28.312µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433135Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 4 for workflow object_snapshot in 22.232µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433139Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 8 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433143Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 6 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433147Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433145Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 5 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433150Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433159Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 8 for workflow object_snapshot in 17.683µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433161Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 6 for workflow object_snapshot in 16.18µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433163Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 9 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433165Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 24 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433167Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433171Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433174Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433176Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 9 for workflow object_snapshot in 11.742µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433178Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 24 for workflow object_snapshot in 11.061µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433173Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 26 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433183Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 23 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433185Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 10 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433188Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433189Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433190Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433196Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 23 for workflow object_snapshot in 11.041µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433195Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 35 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433201Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 10 for workflow object_snapshot in 14.568µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433199Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 5 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433202Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 30 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433201Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 5 for workflow object_snapshot in 50.775µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433208Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433208Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 26 for workflow object_snapshot in 27.25µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433211Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 11 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433211Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 25 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433212Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433212Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 32 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433216Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 33 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433217Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433217Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 30 for workflow object_snapshot in 13.355µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433222Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 27 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433223Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433223Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433228Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433228Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433229Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 11 for workflow object_snapshot in 15.539µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433233Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 35 for workflow object_snapshot in 33.302µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433235Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 12 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433235Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 11 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433238Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 27 for workflow object_snapshot in 14.117µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433241Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433242Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 28 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433242Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 32 for workflow object_snapshot in 27.151µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433243Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 31 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433247Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433246Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 33 for workflow object_snapshot in 28.444µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433246Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 25 for workflow object_snapshot in 32.16µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433249Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 12 for workflow object_snapshot in 11.992µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433250Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433250Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 12 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433253Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 13 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433249Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 34 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433254Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 17 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433253Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 29 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433256Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 28 for workflow object_snapshot in 13.024µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433257Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433259Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433260Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433262Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 19 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433263Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 31 for workflow object_snapshot in 17.213µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433263Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 13 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433263Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 15 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433264Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433267Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433269Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 29 for workflow object_snapshot in 13.736µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433269Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 17 for workflow object_snapshot in 14.096µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433269Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 18 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433270Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 13 for workflow object_snapshot in 15.73µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433274Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433275Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 16 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433275Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 20 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433275Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 19 for workflow object_snapshot in 11.932µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433276Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433279Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 21 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433280Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433282Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 14 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433284Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 34 for workflow object_snapshot in 28.714µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433287Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433287Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433289Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 18 for workflow object_snapshot in 17.373µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433290Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433291Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 20 for workflow object_snapshot in 14.397µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433295Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 15 for workflow object_snapshot in 29.365µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433296Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 16 for workflow object_snapshot in 19.617µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433296Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 14 for workflow object_snapshot in 13.245µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433296Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 14 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433308Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 21 for workflow object_snapshot in 27.331µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433363Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 21 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.433382Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 36 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.533770Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 36, pruning watermark: 36, new updates: 0 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.635620Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 36, pruning watermark: 36, new updates: 200 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636014Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 56 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636027Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 45 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636033Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 57 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636041Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636041Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636043Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636048Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 79 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636056Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 45 for workflow object_snapshot in 24.796µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636057Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636063Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 36 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636065Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 57 for workflow object_snapshot in 28.654µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636069Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 80 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636072Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636074Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636075Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 79 for workflow object_snapshot in 22.312µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636078Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 58 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636075Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 90 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636082Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636083Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 80 for workflow object_snapshot in 12.213µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636081Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 56 for workflow object_snapshot in 57.998µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636085Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 91 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636086Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636088Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 58 for workflow object_snapshot in 8.916µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636089Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636091Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 46 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636093Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 59 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636094Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 36 for workflow object_snapshot in 30.106µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636096Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 91 for workflow object_snapshot in 9.187µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636096Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636099Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 37 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636099Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 92 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636099Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 51 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636102Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 59 for workflow object_snapshot in 7.895µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636103Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636104Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636103Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 90 for workflow object_snapshot in 23.654µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636107Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636107Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 60 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636109Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 81 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636110Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636115Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636115Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 92 for workflow object_snapshot in 14.687µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636118Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 93 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636118Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 60 for workflow object_snapshot in 10.23µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636119Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 37 for workflow object_snapshot in 18.535µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636122Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636124Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 38 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636124Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 81 for workflow object_snapshot in 14.066µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636127Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 61 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636127Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 82 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636126Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636129Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636126Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 53 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636128Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 46 for workflow object_snapshot in 34.284µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636130Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636130Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636132Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 93 for workflow object_snapshot in 12.935µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636136Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 61 for workflow object_snapshot in 7.764µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636136Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 94 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636135Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 54 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636136Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636138Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 62 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636138Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 82 for workflow object_snapshot in 10.259µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636138Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 38 for workflow object_snapshot in 13.024µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636139Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636141Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636143Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 83 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636143Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636146Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 94 for workflow object_snapshot in 8.746µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636146Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636148Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 95 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636149Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 62 for workflow object_snapshot in 9.838µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636151Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636152Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 63 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636152Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 53 for workflow object_snapshot in 22.112µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636151Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 51 for workflow object_snapshot in 48.841µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636153Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 83 for workflow object_snapshot in 9.387µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636152Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 55 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636155Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636156Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 39 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636157Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 95 for workflow object_snapshot in 7.634µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636157Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 52 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636162Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636162Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636164Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 54 for workflow object_snapshot in 26.199µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636164Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 47 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636170Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 52 for workflow object_snapshot in 11.01µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636170Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 39 for workflow object_snapshot in 12.133µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636169Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 84 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636171Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 63 for workflow object_snapshot in 18.113µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636173Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 98 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636173Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636175Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 72 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636175Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636175Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 67 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636175Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 40 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636179Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636180Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636181Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636181Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636182Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 84 for workflow object_snapshot in 11.12µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636185Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 85 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636186Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636187Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 40 for workflow object_snapshot in 10.289µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636188Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 67 for workflow object_snapshot in 11.592µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636188Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636189Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 72 for workflow object_snapshot in 13.264µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636190Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 41 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636189Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 47 for workflow object_snapshot in 23.373µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636192Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 73 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636192Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 66 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636194Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636194Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636194Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 98 for workflow object_snapshot in 18.745µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636196Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 85 for workflow object_snapshot in 9.588µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636198Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636200Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 73 for workflow object_snapshot in 6.633µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636200Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 41 for workflow object_snapshot in 8.416µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636200Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 86 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636200Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 96 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636196Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 71 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636202Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 74 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636203Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636200Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 65 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636206Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636206Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 55 for workflow object_snapshot in 49.944µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636207Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 66 for workflow object_snapshot in 12.974µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636209Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 86 for workflow object_snapshot in 7.975µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636209Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636211Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 74 for workflow object_snapshot in 6.532µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636210Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 49 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636212Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 42 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636213Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 75 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636213Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 50 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636215Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636217Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636218Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636219Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 87 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636219Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636221Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 42 for workflow object_snapshot in 8.205µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636222Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636220Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 64 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636219Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636224Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 43 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636223Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 96 for workflow object_snapshot in 19.756µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636225Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 75 for workflow object_snapshot in 10.79µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636226Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 49 for workflow object_snapshot in 13.605µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636228Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 87 for workflow object_snapshot in 7.764µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636229Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636228Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 50 for workflow object_snapshot in 13.595µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636231Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 48 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636231Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 69 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636227Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636234Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 88 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636235Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 78 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636235Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 43 for workflow object_snapshot in 10.439µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636235Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 77 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636237Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636238Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636239Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 44 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636241Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636242Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636243Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636239Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 97 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636243Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 78 for workflow object_snapshot in 6.803µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636243Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 88 for workflow object_snapshot in 7.885µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636245Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636244Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 65 for workflow object_snapshot in 39.964µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636246Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 89 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636245Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636250Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636255Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 44 for workflow object_snapshot in 14.848µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636257Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 89 for workflow object_snapshot in 9.488µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636257Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 77 for workflow object_snapshot in 19.787µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636260Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 69 for workflow object_snapshot in 26.82µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636260Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 70 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636263Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 76 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636266Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 68 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636267Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 48 for workflow object_snapshot in 34.825µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636268Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 71 for workflow object_snapshot in 64.19µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636263Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636273Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 37 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636273Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 64 for workflow object_snapshot in 48.741µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636275Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636275Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636281Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636287Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 68 for workflow object_snapshot in 19.667µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636288Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 76 for workflow object_snapshot in 23.615µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636300Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 97 for workflow object_snapshot in 55.374µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636302Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 38 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636308Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 70 for workflow object_snapshot in 44.673µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636310Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 99 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636328Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636334Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 39 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636340Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 123 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636343Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 99 for workflow object_snapshot in 27.822µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636344Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636348Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 136 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636348Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 132 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636347Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 135 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636349Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 133 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636349Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 129 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636351Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 134 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636352Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 123 for workflow object_snapshot in 10.23µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636352Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 141 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636355Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636355Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 40 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636355Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 100 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636357Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636359Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636360Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636359Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636360Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636361Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636363Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636364Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 100 for workflow object_snapshot in 7.734µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636366Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 136 for workflow object_snapshot in 16.762µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636368Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 101 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636369Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 142 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636370Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 134 for workflow object_snapshot in 17.433µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636371Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636372Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 41 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636373Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 135 for workflow object_snapshot in 21.77µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636374Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636374Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 138 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636375Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 141 for workflow object_snapshot in 21.38µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636375Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 133 for workflow object_snapshot in 23.594µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636377Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 101 for workflow object_snapshot in 8.125µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636377Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 132 for workflow object_snapshot in 26.79µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636379Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636379Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 139 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636379Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 102 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636380Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 144 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636381Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 130 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636382Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636383Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636383Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 142 for workflow object_snapshot in 12.864µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636385Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636386Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636387Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 138 for workflow object_snapshot in 11.732µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636388Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 102 for workflow object_snapshot in 7.173µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636386Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 129 for workflow object_snapshot in 34.084µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636390Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 103 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636392Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 137 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636393Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636392Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 139 for workflow object_snapshot in 11.812µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636394Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 144 for workflow object_snapshot in 12.934µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636395Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 124 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636396Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 130 for workflow object_snapshot in 13.576µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636396Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 140 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636397Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636398Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 103 for workflow object_snapshot in 7.414µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636398Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 143 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636399Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 131 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636401Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 104 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636401Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636403Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636404Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636403Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636404Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636410Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 104 for workflow object_snapshot in 8.496µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636411Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 140 for workflow object_snapshot in 12.433µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636414Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 114 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636415Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 143 for workflow object_snapshot in 14.888µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636416Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 137 for workflow object_snapshot in 22.852µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636418Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 131 for workflow object_snapshot in 17.753µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636419Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 109 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636419Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 124 for workflow object_snapshot in 21.39µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636421Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636412Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 105 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636425Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 125 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636430Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636431Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 114 for workflow object_snapshot in 15.72µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636432Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636434Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636435Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 147 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636436Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 110 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636432Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 148 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636438Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 112 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636440Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636440Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636440Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 109 for workflow object_snapshot in 19.747µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636443Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 150 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636439Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 152 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636443Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 145 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636444Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 107 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636445Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 108 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636444Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 105 for workflow object_snapshot in 20.418µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636447Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636447Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 125 for workflow object_snapshot in 20.408µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636450Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636450Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636449Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 115 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636451Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 147 for workflow object_snapshot in 14.668µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636451Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636452Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636466Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 150 for workflow object_snapshot in 21.85µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636451Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 110 for workflow object_snapshot in 13.656µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636468Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636468Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 113 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636470Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 149 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636470Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636470Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 126 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636470Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 119 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636472Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 42 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636472Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 106 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636472Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 108 for workflow object_snapshot in 25.608µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636451Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 151 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636475Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636469Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 146 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636476Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 107 for workflow object_snapshot in 29.395µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636475Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 111 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636478Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636478Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636478Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636479Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636487Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 149 for workflow object_snapshot in 15.288µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636488Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636490Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 127 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636489Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 148 for workflow object_snapshot in 50.595µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636490Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 145 for workflow object_snapshot in 43.611µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636492Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636494Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636493Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636493Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 152 for workflow object_snapshot in 47.759µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636495Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 113 for workflow object_snapshot in 24.075µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636495Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 106 for workflow object_snapshot in 21.139µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636497Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 112 for workflow object_snapshot in 55.484µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636500Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 128 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636501Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 165 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636501Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 161 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636500Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 126 for workflow object_snapshot in 27.912µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636501Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 111 for workflow object_snapshot in 22.963µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636503Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 163 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636499Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636504Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 43 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636505Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636505Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 164 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636507Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636506Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 115 for workflow object_snapshot in 54.492µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636507Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636507Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 119 for workflow object_snapshot in 33.843µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636508Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 127 for workflow object_snapshot in 16.321µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636509Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636509Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636508Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 159 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636507Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 156 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636511Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 116 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636511Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 120 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636516Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 165 for workflow object_snapshot in 14.166µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636517Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636512Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 160 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636517Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 44 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636518Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636517Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636519Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 161 for workflow object_snapshot in 16.05µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636518Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 151 for workflow object_snapshot in 42.82µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636519Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 164 for workflow object_snapshot in 12.734µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636520Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 128 for workflow object_snapshot in 18.204µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636518Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 158 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636520Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 166 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636523Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 157 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636525Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 169 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636525Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 173 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636525Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 117 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636521Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 163 for workflow object_snapshot in 17.002µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636526Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 171 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636527Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636528Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636529Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 170 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636529Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636530Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636530Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636531Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 48 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636531Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636531Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 162 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636532Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 172 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636532Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 116 for workflow object_snapshot in 18.956µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636533Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636533Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 159 for workflow object_snapshot in 21.51µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636534Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636534Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 167 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636537Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636537Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 118 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636537Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 120 for workflow object_snapshot in 23.334µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636536Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636539Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 169 for workflow object_snapshot in 12.563µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636539Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 168 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636540Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 154 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636541Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 121 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636542Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636542Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 64 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636542Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636538Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636543Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636546Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 117 for workflow object_snapshot in 19.296µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636546Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 166 for workflow object_snapshot in 23.774µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636547Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636547Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636548Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 155 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636548Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636548Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 156 for workflow object_snapshot in 36.028µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636548Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 146 for workflow object_snapshot in 69.74µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636551Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 122 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636552Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 174 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636552Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 173 for workflow object_snapshot in 25.147µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636554Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636556Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636556Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 153 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636556Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 171 for workflow object_snapshot in 28.083µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636557Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 68 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636558Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 178 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636558Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 177 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636560Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636562Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 176 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636564Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636562Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 167 for workflow object_snapshot in 25.297µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636566Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 168 for workflow object_snapshot in 25.217µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636566Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 118 for workflow object_snapshot in 27.702µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636568Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636569Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 70 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636569Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 155 for workflow object_snapshot in 18.915µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636569Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 121 for workflow object_snapshot in 26.259µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636569Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636570Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 157 for workflow object_snapshot in 45.175µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636569Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 170 for workflow object_snapshot in 38.822µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636568Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 179 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636570Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 175 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636574Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636572Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 180 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636569Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 162 for workflow object_snapshot in 35.606µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636573Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 160 for workflow object_snapshot in 53.791µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636575Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 172 for workflow object_snapshot in 41.838µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636577Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 174 for workflow object_snapshot in 24.526µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636579Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 181 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636579Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 153 for workflow object_snapshot in 20.618µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636580Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 154 for workflow object_snapshot in 37.801µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636581Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 99 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636581Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 158 for workflow object_snapshot in 58.85µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636582Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 183 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636582Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 182 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636584Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 184 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636584Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 122 for workflow object_snapshot in 31.579µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636585Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636587Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636587Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 178 for workflow object_snapshot in 27.712µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636587Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636588Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 176 for workflow object_snapshot in 24.225µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636591Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 186 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636591Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636590Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 177 for workflow object_snapshot in 29.876µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636592Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636593Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636595Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 100 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636597Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636598Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 185 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636595Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636601Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 179 for workflow object_snapshot in 28.753µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636602Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 184 for workflow object_snapshot in 16.581µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636604Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636605Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 188 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636605Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 183 for workflow object_snapshot in 21.821µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636605Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 187 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636607Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 189 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636607Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 101 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636609Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636609Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 186 for workflow object_snapshot in 16.841µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636610Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 190 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636610Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 181 for workflow object_snapshot in 28.563µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636611Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636613Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 180 for workflow object_snapshot in 36.979µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636612Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 191 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636614Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636615Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 188 for workflow object_snapshot in 9.658µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636615Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 185 for workflow object_snapshot in 15.399µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636616Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636614Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 182 for workflow object_snapshot in 29.155µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636617Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 192 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636617Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 189 for workflow object_snapshot in 9.218µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636617Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 175 for workflow object_snapshot in 42.519µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636619Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 193 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636621Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636622Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 102 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636623Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 187 for workflow object_snapshot in 15.549µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636624Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636625Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 190 for workflow object_snapshot in 14.577µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636625Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636630Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 194 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636633Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 195 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636632Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 191 for workflow object_snapshot in 17.242µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636634Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 196 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636634Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 103 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636636Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 192 for workflow object_snapshot in 17.343µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636638Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636639Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636640Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636640Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 193 for workflow object_snapshot in 18.956µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636640Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 198 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636642Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 199 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636644Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 196 for workflow object_snapshot in 9.377µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636644Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 197 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636645Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636645Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 104 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636646Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 200 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636646Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636649Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 201 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636651Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636651Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636652Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 198 for workflow object_snapshot in 9.928µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636652Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 195 for workflow object_snapshot in 17.884µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636653Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 199 for workflow object_snapshot in 10.449µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636653Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 202 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636657Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 105 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636657Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636658Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 200 for workflow object_snapshot in 11.332µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636660Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636662Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 197 for workflow object_snapshot in 16.21µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636664Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 194 for workflow object_snapshot in 31.338µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636668Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 106 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636669Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 202 for workflow object_snapshot in 13.726µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636672Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 204 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636673Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 201 for workflow object_snapshot in 21.741µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636678Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 111 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636682Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 203 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636682Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636686Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 115 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636686Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 205 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636687Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636688Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 210 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636690Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636691Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 207 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636694Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 116 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636694Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636695Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 203 for workflow object_snapshot in 11.452µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636695Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 204 for workflow object_snapshot in 20.198µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636699Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 213 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636702Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636697Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636704Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 117 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636703Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 210 for workflow object_snapshot in 14.076µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636702Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 208 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636709Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 205 for workflow object_snapshot in 21.861µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636711Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 211 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636712Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 207 for workflow object_snapshot in 19.146µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636712Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 213 for workflow object_snapshot in 12.533µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636712Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636715Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 209 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636712Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 212 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636717Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636718Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 206 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636718Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 214 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636720Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636722Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636722Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636722Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 215 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636725Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636725Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 208 for workflow object_snapshot in 20.438µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636727Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 216 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636728Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 211 for workflow object_snapshot in 14.727µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636729Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 214 for workflow object_snapshot in 9.689µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636729Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636730Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 212 for workflow object_snapshot in 14.046µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636731Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636731Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 206 for workflow object_snapshot in 12.103µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636734Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 217 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636734Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 209 for workflow object_snapshot in 18.505µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636735Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 218 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636740Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636741Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 219 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636741Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636740Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 215 for workflow object_snapshot in 15.96µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636741Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 216 for workflow object_snapshot in 13.005µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636744Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 220 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636745Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636747Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 221 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636749Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636750Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 217 for workflow object_snapshot in 14.747µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636750Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 218 for workflow object_snapshot in 13.265µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636752Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 219 for workflow object_snapshot in 10.059µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636752Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636753Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 222 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636757Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 220 for workflow object_snapshot in 12.283µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636759Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 223 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636762Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636762Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 221 for workflow object_snapshot in 13.976µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636765Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636773Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 224 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636775Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 223 for workflow object_snapshot in 14.748µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636777Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636779Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 222 for workflow object_snapshot in 22.902µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636786Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 224 for workflow object_snapshot in 11.641µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636808Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 118 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636852Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 121 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636859Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 225 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636865Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636865Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 226 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636871Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636875Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 122 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636876Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 225 for workflow object_snapshot in 14.888µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636880Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 227 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636880Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 226 for workflow object_snapshot in 13.294µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636884Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636884Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 228 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636887Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 175 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636888Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636889Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 227 for workflow object_snapshot in 7.915µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636889Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 231 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636892Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 230 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636895Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 228 for workflow object_snapshot in 9.828µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636895Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636896Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636896Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 229 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636902Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 230 for workflow object_snapshot in 8.847µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636906Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636907Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 231 for workflow object_snapshot in 15.92µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636926Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 187 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636926Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 233 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636926Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 229 for workflow object_snapshot in 26.159µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636930Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 232 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636930Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636935Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636939Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 233 for workflow object_snapshot in 10.96µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636944Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 232 for workflow object_snapshot in 12.834µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636945Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 235 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636948Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 190 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636951Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 234 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636953Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636957Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636962Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 191 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636968Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 234 for workflow object_snapshot in 14.728µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636968Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 235 for workflow object_snapshot in 20.629µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636972Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 192 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636985Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 193 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.636996Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 194 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637007Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 201 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637036Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 203 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637052Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 204 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637062Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 205 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637071Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 206 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637083Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 209 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637093Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 215 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637120Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 216 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637133Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 217 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637143Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 218 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637153Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 219 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637165Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 220 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637175Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 221 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637185Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 222 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637201Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 224 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637211Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 225 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637223Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 226 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637234Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 227 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637243Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 228 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637255Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 229 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637263Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 232 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637272Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 234 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637280Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 235 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.637289Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 236 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.738075Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 236, pruning watermark: 236, new updates: 2 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.738144Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 237 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.738152Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 236 for workflow object_snapshot +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.738155Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.738160Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.738171Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 237 for workflow object_snapshot in 21.81µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.738176Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 236 for workflow object_snapshot in 22.011µs +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.738201Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 238 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.838970Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 238, pruning watermark: 238, new updates: 0 +Sep 12 13:41:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:34.939905Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 238, pruning watermark: 238, new updates: 0 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041165Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 238, pruning watermark: 238, new updates: 200 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041258Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 239 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041258Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 238 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041273Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 245 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041274Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041275Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041282Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041301Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 238 for workflow object_snapshot in 36.538µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041301Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 245 for workflow object_snapshot in 24.456µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041304Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 239 for workflow object_snapshot in 39.414µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041308Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 240 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041310Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 242 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041312Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 241 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041317Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041317Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041321Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041327Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 243 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041333Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 240 for workflow object_snapshot in 22.162µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041333Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 242 for workflow object_snapshot in 19.867µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041331Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 239 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041339Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 241 for workflow object_snapshot in 24.927µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041340Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 244 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041347Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041346Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041346Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 253 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041355Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 249 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041363Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041363Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 244 for workflow object_snapshot in 21.039µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041364Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041363Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 251 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041370Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 252 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041373Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 243 for workflow object_snapshot in 40.566µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041377Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041379Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 249 for workflow object_snapshot in 21.29µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041381Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 240 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041385Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 246 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041385Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 253 for workflow object_snapshot in 35.327µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041389Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 252 for workflow object_snapshot in 16.41µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041394Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041396Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 254 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041396Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 247 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041398Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 250 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041404Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041405Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041406Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 246 for workflow object_snapshot in 18.735µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041417Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 254 for workflow object_snapshot in 18.896µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041418Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041419Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 247 for workflow object_snapshot in 20.308µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041424Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 248 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041429Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 257 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041430Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 258 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041431Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041432Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 256 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041437Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041437Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 250 for workflow object_snapshot in 34.675µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041438Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041440Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041444Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 248 for workflow object_snapshot in 18.024µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041446Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 259 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041446Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041451Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 257 for workflow object_snapshot in 19.296µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041452Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041465Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 258 for workflow object_snapshot in 32.33µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041468Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 261 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041471Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 241 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041472Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 262 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041473Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 263 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041474Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 264 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041477Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041479Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 259 for workflow object_snapshot in 31.97µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041477Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 260 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041479Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 256 for workflow object_snapshot in 41.958µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041481Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041480Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 251 for workflow object_snapshot in 112.089µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041485Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041488Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 243 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041487Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 255 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041491Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 261 for workflow object_snapshot in 19.978µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041491Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041494Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041493Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 265 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041494Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 263 for workflow object_snapshot in 18.986µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041499Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041501Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 246 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041503Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 264 for workflow object_snapshot in 25.688µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041505Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 255 for workflow object_snapshot in 15.579µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041509Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 266 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041510Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 260 for workflow object_snapshot in 28.704µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041511Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041514Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 247 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041516Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 262 for workflow object_snapshot in 42.229µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041518Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041526Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 268 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041525Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 267 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041528Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 270 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041529Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 248 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041531Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 266 for workflow object_snapshot in 19.556µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041533Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041534Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041535Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041535Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 265 for workflow object_snapshot in 38.992µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041534Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 269 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041544Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 251 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041544Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 268 for workflow object_snapshot in 16.391µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041547Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 270 for workflow object_snapshot in 16.1µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041547Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 267 for workflow object_snapshot in 18.996µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041547Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041553Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 271 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041555Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 272 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041560Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041562Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 255 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041565Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 269 for workflow object_snapshot in 26.9µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041573Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041575Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 271 for workflow object_snapshot in 19.006µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041581Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 273 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041586Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 275 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041588Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041590Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 272 for workflow object_snapshot in 32.461µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041594Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041597Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 274 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041597Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 273 for workflow object_snapshot in 14.076µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041604Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 275 for workflow object_snapshot in 15.89µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041607Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 260 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041610Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041609Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 276 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041611Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 279 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041611Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 278 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041612Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 277 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041618Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041619Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041621Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041621Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041621Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 274 for workflow object_snapshot in 21.971µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041624Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 262 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041626Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 279 for workflow object_snapshot in 13.936µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041634Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 280 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041634Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 278 for workflow object_snapshot in 20.318µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041637Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 265 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041637Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 276 for workflow object_snapshot in 25.879µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041637Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 277 for workflow object_snapshot in 22.783µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041640Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041651Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 280 for workflow object_snapshot in 15.018µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041653Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 267 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041661Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 283 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041661Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 281 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041666Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041668Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 269 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041669Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041677Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 283 for workflow object_snapshot in 14.117µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041679Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 281 for workflow object_snapshot in 16.05µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041680Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 282 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041681Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 271 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041686Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041686Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 284 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041689Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 285 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041691Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041696Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041697Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 272 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041699Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 282 for workflow object_snapshot in 17.012µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041702Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 284 for workflow object_snapshot in 14.517µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041706Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 285 for workflow object_snapshot in 15.739µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041707Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 287 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041713Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 273 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041714Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 288 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041715Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041722Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 286 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041723Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041726Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 289 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041727Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 274 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041729Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041729Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 290 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041730Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 287 for workflow object_snapshot in 20.889µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041731Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041742Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 276 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041741Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 291 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041742Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 289 for workflow object_snapshot in 15.179µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041738Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 288 for workflow object_snapshot in 20.699µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041746Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 286 for workflow object_snapshot in 22.121µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041752Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041757Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041759Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 280 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041766Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 291 for workflow object_snapshot in 21.289µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041772Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 281 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041772Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 292 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041775Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 290 for workflow object_snapshot in 43.401µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041779Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041784Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 295 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041786Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 282 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041790Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 292 for workflow object_snapshot in 15.419µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041790Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041795Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 293 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041799Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 294 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041799Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 295 for workflow object_snapshot in 13.736µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041800Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 284 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041803Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041805Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041811Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 294 for workflow object_snapshot in 10.89µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041816Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 285 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041816Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 296 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041820Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 293 for workflow object_snapshot in 22.612µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041824Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 297 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041825Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041828Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 299 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041828Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 286 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041831Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041834Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041834Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 298 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041834Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 301 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041836Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 296 for workflow object_snapshot in 18.554µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041839Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 300 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041841Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 290 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041841Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 297 for workflow object_snapshot in 14.788µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041841Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 299 for workflow object_snapshot in 11.442µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041842Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041843Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041853Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 292 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041854Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 302 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041856Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041856Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 298 for workflow object_snapshot in 19.376µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041858Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 301 for workflow object_snapshot in 21.149µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041862Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041868Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 293 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041871Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 302 for workflow object_snapshot in 14.998µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041873Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 300 for workflow object_snapshot in 31.959µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041883Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 296 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041890Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 303 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041895Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041895Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 306 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041900Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 297 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041903Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 303 for workflow object_snapshot in 12.143µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041909Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041909Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 305 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041913Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 304 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041915Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041922Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041925Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 305 for workflow object_snapshot in 13.886µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041935Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 306 for workflow object_snapshot in 36.729µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041936Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 298 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041936Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 304 for workflow object_snapshot in 20.398µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041943Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 307 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041954Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 300 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041954Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041969Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 308 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041972Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 303 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041974Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 310 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041975Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 307 for workflow object_snapshot in 29.305µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041976Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 311 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041978Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041986Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 304 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041986Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041988Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041992Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 308 for workflow object_snapshot in 21.34µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041994Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 315 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041996Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 314 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.041999Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 313 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042001Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 307 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042001Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 310 for workflow object_snapshot in 25.457µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042003Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042004Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042004Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 311 for workflow object_snapshot in 24.946µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042008Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042009Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 312 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042012Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 320 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042014Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 308 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042016Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 314 for workflow object_snapshot in 18.164µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042018Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042019Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 317 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042018Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 315 for workflow object_snapshot in 21.04µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042014Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 319 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042020Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 313 for workflow object_snapshot in 18.665µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042022Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042026Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 309 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042028Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042030Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 318 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042032Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 312 for workflow object_snapshot in 20.308µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042033Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 316 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042037Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042037Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 320 for workflow object_snapshot in 22.812µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042039Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 309 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042040Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042043Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 322 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042044Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042045Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 318 for workflow object_snapshot in 12.644µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042048Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 317 for workflow object_snapshot in 27.391µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042046Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 321 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042050Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042050Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042052Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 316 for workflow object_snapshot in 17.493µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042053Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 309 for workflow object_snapshot in 12.754µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042062Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042067Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 322 for workflow object_snapshot in 22.222µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042071Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 319 for workflow object_snapshot in 49.823µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042074Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 321 for workflow object_snapshot in 24.105µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042080Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 323 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042085Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 324 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042089Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 325 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042089Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042090Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042097Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042098Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 316 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042100Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 324 for workflow object_snapshot in 12.814µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042100Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 326 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042105Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 323 for workflow object_snapshot in 21.801µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042107Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 325 for workflow object_snapshot in 15.068µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042108Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 327 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042109Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042112Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042120Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 327 for workflow object_snapshot in 10.67µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042127Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 326 for workflow object_snapshot in 24.796µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042151Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 319 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042158Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 331 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042165Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 328 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042166Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 329 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042167Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042168Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 321 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042172Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042172Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042181Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 323 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042181Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 330 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042181Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 331 for workflow object_snapshot in 20.539µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042182Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 328 for workflow object_snapshot in 15.289µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042182Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 329 for workflow object_snapshot in 14.948µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042188Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042193Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 325 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042205Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 326 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042206Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 330 for workflow object_snapshot in 22.612µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042208Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 334 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042207Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 332 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042214Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 333 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042220Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 328 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042220Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042220Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042220Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042222Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 335 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042226Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042231Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 333 for workflow object_snapshot in 14.948µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042232Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 336 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042232Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 329 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042235Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 335 for workflow object_snapshot in 12.483µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042236Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 332 for workflow object_snapshot in 24.767µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042239Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042236Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 334 for workflow object_snapshot in 24.676µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042241Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 338 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042243Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 337 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042246Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 330 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042246Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042245Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 340 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042253Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042255Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042255Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 338 for workflow object_snapshot in 12.383µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042258Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 343 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042257Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 336 for workflow object_snapshot in 22.932µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042259Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 332 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042265Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042264Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 341 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042265Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 339 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042270Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 337 for workflow object_snapshot in 24.556µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042270Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 340 for workflow object_snapshot in 22.252µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042272Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042272Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 342 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042275Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 334 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042275Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 343 for workflow object_snapshot in 16.16µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042279Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042281Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 339 for workflow object_snapshot in 14.107µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042281Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042280Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 345 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042285Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 344 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042288Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042291Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042293Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 336 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042297Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 346 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042297Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 341 for workflow object_snapshot in 29.815µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042302Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 344 for workflow object_snapshot in 14.687µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042302Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 342 for workflow object_snapshot in 28.493µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042304Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042306Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 337 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042305Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 345 for workflow object_snapshot in 22.111µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042311Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 347 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042316Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 346 for workflow object_snapshot in 16.521µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042316Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042320Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 339 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042324Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 348 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042328Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 347 for workflow object_snapshot in 15.108µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042329Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042335Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 349 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042334Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 341 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042337Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 348 for workflow object_snapshot in 11.893µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042340Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042348Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 349 for workflow object_snapshot in 11.611µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042349Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 342 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042357Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 350 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042362Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 345 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042367Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042374Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 346 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042381Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 350 for workflow object_snapshot in 21.871µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042387Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 347 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042388Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 351 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042394Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042396Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 355 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042399Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 348 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042399Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 352 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042402Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 351 for workflow object_snapshot in 11.521µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042402Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 354 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042404Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042407Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 353 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042408Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042409Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042410Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 349 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042413Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042419Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 355 for workflow object_snapshot in 19.907µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042420Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 352 for workflow object_snapshot in 17.914µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042422Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 354 for workflow object_snapshot in 17.733µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042422Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 350 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042426Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 353 for workflow object_snapshot in 17.573µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042435Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 351 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042435Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 356 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042447Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 357 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042448Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 352 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042452Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042476Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 353 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042477Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 359 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042476Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 357 for workflow object_snapshot in 27.681µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042484Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042485Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042487Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 358 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042489Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 356 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042494Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042495Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 359 for workflow object_snapshot in 15.719µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042494Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 361 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042504Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042504Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 356 for workflow object_snapshot in 66.153µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042510Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 360 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042517Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 358 for workflow object_snapshot in 27.381µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042517Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 361 for workflow object_snapshot in 19.767µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042519Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042525Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 363 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042530Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042532Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 365 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042532Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 360 for workflow object_snapshot in 19.927µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042535Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 358 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042537Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042540Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 363 for workflow object_snapshot in 12.944µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042542Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 362 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042543Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 364 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042546Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 365 for workflow object_snapshot in 12.343µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042552Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042553Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042560Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 367 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042566Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 362 for workflow object_snapshot in 20.418µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042566Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 366 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042569Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042568Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 364 for workflow object_snapshot in 23.033µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042577Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042580Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 367 for workflow object_snapshot in 17.833µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042583Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 360 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042589Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 368 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042592Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 366 for workflow object_snapshot in 23.373µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042595Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042600Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 369 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042603Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 362 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042604Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 372 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042613Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042615Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042617Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 364 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042621Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 371 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042622Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 370 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042622Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 374 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042624Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 368 for workflow object_snapshot in 32.982µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042626Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042629Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042628Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 372 for workflow object_snapshot in 19.967µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042629Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042631Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 366 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042631Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 369 for workflow object_snapshot in 27.221µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042635Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 371 for workflow object_snapshot in 12.383µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042637Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 373 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042640Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 370 for workflow object_snapshot in 16.481µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042645Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 374 for workflow object_snapshot in 20.348µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042646Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042646Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 375 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042647Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 376 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042651Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 368 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042652Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042655Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 373 for workflow object_snapshot in 16.2µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042655Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042657Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 377 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042661Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 375 for workflow object_snapshot in 13.104µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042665Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042667Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 376 for workflow object_snapshot in 18.164µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042670Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 378 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042671Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 369 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042676Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042681Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 377 for workflow object_snapshot in 21.45µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042686Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 379 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042690Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 370 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042692Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042693Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 378 for workflow object_snapshot in 22.452µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042700Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 379 for workflow object_snapshot in 11.512µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042708Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 373 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042716Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 380 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042726Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042728Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 375 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042727Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 382 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042728Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 383 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042734Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042736Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042739Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 380 for workflow object_snapshot in 20.599µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042741Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 381 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042741Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 384 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042744Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 383 for workflow object_snapshot in 13.685µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042746Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 376 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042749Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042751Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042753Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 382 for workflow object_snapshot in 22.793µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042758Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 384 for workflow object_snapshot in 14.007µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042761Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 381 for workflow object_snapshot in 17.663µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042765Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 377 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042776Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 385 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042781Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042780Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 386 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042783Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 378 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042785Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042793Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 386 for workflow object_snapshot in 10.59µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042793Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 387 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042801Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042800Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 385 for workflow object_snapshot in 22.451µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042804Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 379 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042809Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 389 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042813Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042815Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 387 for workflow object_snapshot in 19.376µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042820Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 389 for workflow object_snapshot in 9.487µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042824Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 380 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042826Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 390 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042826Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 391 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042827Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 388 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042832Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042834Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 392 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042836Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042836Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042844Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042843Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 381 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042847Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 390 for workflow object_snapshot in 18.856µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042848Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 388 for workflow object_snapshot in 18.444µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042849Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 394 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042849Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 391 for workflow object_snapshot in 19.828µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042850Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 393 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042857Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042858Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 392 for workflow object_snapshot in 21.52µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042862Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 385 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042866Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 394 for workflow object_snapshot in 14.808µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042873Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042878Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 387 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042882Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 395 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042887Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042891Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 393 for workflow object_snapshot in 34.244µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042894Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 396 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042896Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 395 for workflow object_snapshot in 12.052µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042896Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 388 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042896Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 397 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042896Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 399 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042904Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042903Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042908Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 398 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042910Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042915Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 397 for workflow object_snapshot in 16.04µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042914Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 391 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042917Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042918Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 396 for workflow object_snapshot in 21.531µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042922Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 399 for workflow object_snapshot in 21.32µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042926Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 400 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042928Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 401 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042930Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 398 for workflow object_snapshot in 20.108µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042934Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042934Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 392 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042936Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 402 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042936Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042944Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042944Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 401 for workflow object_snapshot in 15.068µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042948Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 400 for workflow object_snapshot in 19.997µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042949Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 403 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042953Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 393 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042955Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 402 for workflow object_snapshot in 18.074µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042956Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042966Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 403 for workflow object_snapshot in 15.379µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042971Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 395 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.042988Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 396 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043004Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 398 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043005Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 405 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043008Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 408 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043008Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 407 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043015Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043015Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043017Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043023Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 400 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043025Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 408 for workflow object_snapshot in 14.748µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043028Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 406 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043029Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 409 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043032Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 404 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043032Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 405 for workflow object_snapshot in 24.846µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043033Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 407 for workflow object_snapshot in 23.574µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043035Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043035Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043042Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 413 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043042Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043042Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 402 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043045Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 406 for workflow object_snapshot in 14.598µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043047Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043046Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 411 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043047Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 409 for workflow object_snapshot in 16.892µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043051Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 412 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043049Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 410 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043056Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043055Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 404 for workflow object_snapshot in 19.466µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043056Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 413 for workflow object_snapshot in 12.594µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043056Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043064Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 403 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043064Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043064Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 414 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043064Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 412 for workflow object_snapshot in 11.632µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043065Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 415 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043070Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043072Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043072Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 411 for workflow object_snapshot in 23.354µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043074Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 416 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043080Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 414 for workflow object_snapshot in 14.226µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043081Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043078Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 417 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043080Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 410 for workflow object_snapshot in 26.94µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043084Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 404 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043086Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 415 for workflow object_snapshot in 18.705µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043090Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043091Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 416 for workflow object_snapshot in 15.278µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043103Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 410 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043104Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 417 for workflow object_snapshot in 21.61µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043117Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 418 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043123Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043128Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 421 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043131Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 418 for workflow object_snapshot in 12.323µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043130Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 415 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043134Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043149Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 424 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043150Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 421 for workflow object_snapshot in 20.187µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043151Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 422 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043156Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 419 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043159Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043160Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043162Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043168Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 416 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043171Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 419 for workflow object_snapshot in 14.026µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043172Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 425 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043175Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 422 for workflow object_snapshot in 21.099µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043178Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 426 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043178Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 424 for workflow object_snapshot in 26.86µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043180Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 423 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043181Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043185Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043185Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 427 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043186Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 417 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043187Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043187Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 428 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043192Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 420 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043193Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043194Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 429 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043195Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 425 for workflow object_snapshot in 20.038µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043197Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 423 for workflow object_snapshot in 14.377µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043199Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043199Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 426 for workflow object_snapshot in 19.918µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043201Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043204Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 418 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043203Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043206Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 427 for workflow object_snapshot in 19.416µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043207Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 429 for workflow object_snapshot in 11.732µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043216Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 430 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043218Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 432 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043219Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 419 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043219Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 431 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043220Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043221Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 420 for workflow object_snapshot in 25.939µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043223Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 428 for workflow object_snapshot in 30.306µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043227Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043227Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043228Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 430 for workflow object_snapshot in 11.341µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043234Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 420 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043234Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 433 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043240Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043241Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 431 for workflow object_snapshot in 19.116µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043246Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 432 for workflow object_snapshot in 26.659µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043249Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 433 for workflow object_snapshot in 12.624µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043249Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 428 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043255Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 434 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043257Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 435 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043262Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043265Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 430 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043268Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043270Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 435 for workflow object_snapshot in 11.311µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043278Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 437 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043281Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 431 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043282Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 434 for workflow object_snapshot in 24.095µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043281Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 436 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043285Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043294Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043296Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 437 for workflow object_snapshot in 16.791µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043297Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 432 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043305Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 436 for workflow object_snapshot in 20.208µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043312Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 433 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043327Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 434 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043341Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 436 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.043356Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 438 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.143600Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 438, pruning watermark: 438, new updates: 0 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245363Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 438, pruning watermark: 438, new updates: 19 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245472Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 438 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245484Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 445 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245488Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245496Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245506Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 438 for workflow object_snapshot in 27.261µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245520Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 441 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245523Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 439 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245533Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 450 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245533Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 445 for workflow object_snapshot in 46.487µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245539Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245539Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245540Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 442 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245552Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245559Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 443 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245563Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 439 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245563Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 441 for workflow object_snapshot in 38.272µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245570Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 450 for workflow object_snapshot in 34.574µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245570Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 440 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245574Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 446 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245575Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245577Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245577Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 442 for workflow object_snapshot in 35.035µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245579Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245583Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 444 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245585Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 456 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245588Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 446 for workflow object_snapshot in 13.004µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245590Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 440 for workflow object_snapshot in 18.695µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245593Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245595Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 449 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245595Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 447 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245594Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245598Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245600Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245600Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245601Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 439 for workflow object_snapshot in 34.975µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245598Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 448 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245608Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 444 for workflow object_snapshot in 23.223µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245610Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 449 for workflow object_snapshot in 14.086µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245614Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 447 for workflow object_snapshot in 17.603µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245614Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 451 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245615Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 452 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245618Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 443 for workflow object_snapshot in 53.801µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245619Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 455 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245622Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245621Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 456 for workflow object_snapshot in 33.102µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245622Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 454 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245624Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245626Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 443 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245628Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245629Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245631Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 452 for workflow object_snapshot in 14.857µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245635Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245636Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 451 for workflow object_snapshot in 20.589µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245590Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 453 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245645Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 455 for workflow object_snapshot in 23.684µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245654Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 448 for workflow object_snapshot in 44.032µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245654Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 454 for workflow object_snapshot in 29.084µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245662Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245670Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 448 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245674Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 453 for workflow object_snapshot in 29.535µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245685Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 453 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.245697Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 457 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.347571Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 457, pruning watermark: 457, new updates: 72 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348081Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 459 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348094Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 468 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348097Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348117Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 459 for workflow object_snapshot in 28.433µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348119Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348130Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 457 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348135Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 458 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348143Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 463 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348144Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348146Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348150Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 468 for workflow object_snapshot in 50.865µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348160Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348167Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 457 for workflow object_snapshot in 32.721µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348170Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 464 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348175Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 458 for workflow object_snapshot in 37.6µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348180Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348183Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 463 for workflow object_snapshot in 37.249µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348184Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 466 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348188Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 477 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348189Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 478 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348193Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 458 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348193Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348199Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348205Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 464 for workflow object_snapshot in 32.941µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348209Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348209Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 466 for workflow object_snapshot in 22.692µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348211Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 467 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348216Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 465 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348221Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 478 for workflow object_snapshot in 30.627µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348222Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348223Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 460 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348226Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348223Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 473 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348228Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 475 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348236Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348239Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 465 for workflow object_snapshot in 20.779µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348238Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 476 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348246Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 475 for workflow object_snapshot in 15.94µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348246Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 460 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348246Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 467 for workflow object_snapshot in 32.641µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348248Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348250Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 461 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348254Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 462 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348255Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348255Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348255Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348255Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 474 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348260Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 477 for workflow object_snapshot in 68.889µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348263Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348267Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 470 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348269Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 461 for workflow object_snapshot in 18.084µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348273Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 460 for workflow object_snapshot in 24.235µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348272Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 473 for workflow object_snapshot in 43.321µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348274Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 481 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348274Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348276Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 476 for workflow object_snapshot in 31.799µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348281Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348280Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 471 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348281Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348283Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 469 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348285Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 462 for workflow object_snapshot in 29.926µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348285Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 482 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348289Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348289Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 472 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348293Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 481 for workflow object_snapshot in 17.523µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348294Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348295Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348296Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 484 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348300Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 474 for workflow object_snapshot in 40.035µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348301Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 487 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348302Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 470 for workflow object_snapshot in 33.041µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348303Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 471 for workflow object_snapshot in 20.078µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348303Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348309Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 482 for workflow object_snapshot in 21.75µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348311Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 486 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348311Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 469 for workflow object_snapshot in 25.477µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348312Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348311Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348313Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 479 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348320Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348320Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348321Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 485 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348324Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 472 for workflow object_snapshot in 30.266µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348324Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 480 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348327Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 483 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348329Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348331Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 479 for workflow object_snapshot in 16.26µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348332Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 488 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348332Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 487 for workflow object_snapshot in 27.381µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348335Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348337Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348337Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 486 for workflow object_snapshot in 23.333µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348339Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348340Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 485 for workflow object_snapshot in 17.223µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348340Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 489 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348342Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 484 for workflow object_snapshot in 44.703µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348347Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 483 for workflow object_snapshot in 18.315µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348349Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 488 for workflow object_snapshot in 15.559µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348349Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 462 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348350Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 490 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348351Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348356Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 491 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348357Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 492 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348359Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 480 for workflow object_snapshot in 30.697µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348360Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348364Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 489 for workflow object_snapshot in 21.37µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348372Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348373Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 493 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348374Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348377Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348377Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 490 for workflow object_snapshot in 24.716µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348380Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 469 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348385Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 491 for workflow object_snapshot in 26.74µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348388Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 493 for workflow object_snapshot in 13.715µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348390Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 492 for workflow object_snapshot in 30.176µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348399Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 472 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348410Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 495 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348415Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348415Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 479 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348417Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 494 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348423Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 495 for workflow object_snapshot in 11.912µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348426Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348434Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 480 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348438Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 496 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348441Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 494 for workflow object_snapshot in 22.031µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348443Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348451Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 496 for workflow object_snapshot in 11.892µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348464Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 497 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348465Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 498 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348467Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 489 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348469Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 499 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348472Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348475Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348475Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 500 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348476Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 502 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348478Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348482Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348484Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 498 for workflow object_snapshot in 16.911µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348485Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 497 for workflow object_snapshot in 18.745µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348485Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348488Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 490 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348491Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 499 for workflow object_snapshot in 20.067µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348494Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 501 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348500Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 503 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348500Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 500 for workflow object_snapshot in 22.722µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348504Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 502 for workflow object_snapshot in 26.109µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348508Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348509Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348512Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 491 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348511Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 504 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348520Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348521Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 503 for workflow object_snapshot in 19.757µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348524Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 505 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348528Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 509 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348529Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 492 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348530Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348530Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 507 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348530Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 501 for workflow object_snapshot in 32.911µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348535Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348535Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 504 for workflow object_snapshot in 20.949µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348537Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 508 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348538Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348540Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 505 for workflow object_snapshot in 14.206µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348544Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 506 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348546Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348544Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 509 for workflow object_snapshot in 14.567µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348547Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 494 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348549Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 507 for workflow object_snapshot in 17.353µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348553Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348555Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 510 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348562Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 512 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348562Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 508 for workflow object_snapshot in 21.871µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348564Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 496 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348565Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 506 for workflow object_snapshot in 18.775µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348569Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 511 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348570Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348572Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348576Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348578Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 513 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348581Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 497 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348585Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348586Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 510 for workflow object_snapshot in 27.462µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348588Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 511 for workflow object_snapshot in 16.791µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348590Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 512 for workflow object_snapshot in 25.538µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348599Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 499 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348598Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 513 for workflow object_snapshot in 18.525µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348605Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 514 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348613Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 515 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348615Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348617Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 500 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348624Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348626Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 517 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348631Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348630Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 514 for workflow object_snapshot in 21.891µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348634Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 501 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348641Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 515 for workflow object_snapshot in 23.925µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348647Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 517 for workflow object_snapshot in 18.976µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348650Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 516 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348651Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 504 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348656Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 518 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348658Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 519 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348658Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 520 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348660Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348665Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348666Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 521 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348668Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348667Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348672Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 505 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348673Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348673Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 516 for workflow object_snapshot in 21.32µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348678Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 518 for workflow object_snapshot in 19.276µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348680Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 519 for workflow object_snapshot in 19.185µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348680Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 520 for workflow object_snapshot in 19.035µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348681Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 523 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348685Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 521 for workflow object_snapshot in 17.102µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348689Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 522 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348689Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 506 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348698Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348705Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 510 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348706Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 522 for workflow object_snapshot in 14.988µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348713Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 524 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348712Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348714Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 526 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348716Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 511 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348717Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348718Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 527 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348719Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348723Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348724Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 524 for workflow object_snapshot in 10.41µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348730Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 523 for workflow object_snapshot in 47.188µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348730Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 528 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348732Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 527 for workflow object_snapshot in 12.012µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348732Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 512 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348732Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 525 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348733Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 526 for workflow object_snapshot in 17.893µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348741Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348748Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 513 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348757Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 525 for workflow object_snapshot in 22.262µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348763Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 514 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348771Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348779Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 515 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348783Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 528 for workflow object_snapshot in 50.615µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348791Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 516 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348802Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 518 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348813Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 519 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348824Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 520 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348835Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 521 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348846Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 522 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348857Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 523 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348867Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 525 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348878Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 528 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.348888Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 529 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.450075Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 529, pruning watermark: 529, new updates: 0 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.550926Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 529, pruning watermark: 529, new updates: 200 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551007Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 533 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551022Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551046Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 533 for workflow object_snapshot in 32.49µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551102Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 549 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551125Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551144Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 549 for workflow object_snapshot in 36.117µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551155Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 534 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551166Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551179Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 534 for workflow object_snapshot in 21.12µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551186Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 535 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551198Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551214Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 535 for workflow object_snapshot in 26.199µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551233Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 536 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551247Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551265Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 536 for workflow object_snapshot in 29.926µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551274Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 529 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551283Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 537 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551293Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551294Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551306Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 537 for workflow object_snapshot in 19.567µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551305Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 529 for workflow object_snapshot in 29.615µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551326Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 530 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551334Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551346Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 530 for workflow object_snapshot in 18.425µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551353Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 531 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551359Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551370Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 531 for workflow object_snapshot in 15.018µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551370Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 538 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551387Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 532 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551387Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551390Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 544 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551394Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551404Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551404Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 538 for workflow object_snapshot in 31.86µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551405Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 532 for workflow object_snapshot in 16.06µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551423Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 544 for workflow object_snapshot in 28.574µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551428Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 548 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551429Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 539 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551426Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 547 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551436Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551443Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551445Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 539 for workflow object_snapshot in 14.718µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551445Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551449Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 540 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551469Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551473Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 548 for workflow object_snapshot in 43.411µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551478Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 540 for workflow object_snapshot in 27.19µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551483Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 541 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551483Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 542 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551483Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 547 for workflow object_snapshot in 52.078µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551488Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551486Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 545 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551492Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 530 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551493Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551503Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 541 for workflow object_snapshot in 19.176µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551506Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 542 for workflow object_snapshot in 20.278µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551507Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 543 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551513Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551513Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551516Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 546 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551523Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 543 for workflow object_snapshot in 13.996µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551527Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551532Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 556 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551533Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 545 for workflow object_snapshot in 42.8µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551537Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551537Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 531 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551534Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 552 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551541Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 560 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551545Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 556 for workflow object_snapshot in 11.632µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551545Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 546 for workflow object_snapshot in 25.377µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551549Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551545Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 554 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551549Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551553Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 532 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551560Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 560 for workflow object_snapshot in 17.022µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551565Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 539 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551565Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 555 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551566Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 557 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551568Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 552 for workflow object_snapshot in 28.263µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551569Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551571Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551572Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551573Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 553 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551577Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 540 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551580Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 555 for workflow object_snapshot in 13.556µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551582Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 557 for workflow object_snapshot in 14.046µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551588Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 558 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551589Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 541 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551588Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551590Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 550 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551592Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 554 for workflow object_snapshot in 41.497µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551594Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551592Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 559 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551598Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 551 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551599Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551601Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 542 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551602Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 568 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551602Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 558 for workflow object_snapshot in 11.993µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551604Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551608Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 573 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551609Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551608Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 553 for workflow object_snapshot in 32.541µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551613Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 543 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551614Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551613Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 551 for workflow object_snapshot in 13.575µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551614Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 550 for workflow object_snapshot in 22.102µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551616Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 566 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551617Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551623Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 568 for workflow object_snapshot in 19.617µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551624Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 573 for workflow object_snapshot in 14.968µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551624Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 545 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551625Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 565 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551631Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551631Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 562 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551632Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 569 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551633Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551637Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551638Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 546 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551640Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551646Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 562 for workflow object_snapshot in 12.714µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551610Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 564 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551650Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 569 for workflow object_snapshot in 16.701µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551649Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 565 for workflow object_snapshot in 21.981µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551651Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 550 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551654Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 561 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551652Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 559 for workflow object_snapshot in 53.911µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551651Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 563 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551654Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 566 for workflow object_snapshot in 35.837µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551659Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 570 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551659Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551659Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551663Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 567 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551665Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551658Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 581 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551669Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 561 for workflow object_snapshot in 13.666µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551671Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551674Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 570 for workflow object_snapshot in 13.485µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551674Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 571 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551676Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 564 for workflow object_snapshot in 28.032µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551677Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 577 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551678Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 582 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551679Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 575 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551682Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551682Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 572 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551682Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551688Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551689Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551690Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 567 for workflow object_snapshot in 25.598µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551691Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 579 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551692Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 580 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551693Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551696Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 576 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551695Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 571 for workflow object_snapshot in 18.725µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551697Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 572 for workflow object_snapshot in 12.403µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551697Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 589 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551697Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551697Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551700Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 585 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551701Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551703Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551703Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 587 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551704Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551700Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 574 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551707Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551707Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 577 for workflow object_snapshot in 27.622µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551709Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551714Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 589 for workflow object_snapshot in 15.218µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551715Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551716Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 588 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551715Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 581 for workflow object_snapshot in 47.138µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551717Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 580 for workflow object_snapshot in 22.402µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551718Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 575 for workflow object_snapshot in 36.829µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551719Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 582 for workflow object_snapshot in 37.781µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551720Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 592 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551719Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 563 for workflow object_snapshot in 62.216µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551720Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 587 for workflow object_snapshot in 15.338µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551720Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551721Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 585 for workflow object_snapshot in 19.116µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551722Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 576 for workflow object_snapshot in 24.105µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551722Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 586 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551724Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551725Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551727Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 590 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551733Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551733Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 574 for workflow object_snapshot in 27.001µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551734Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 594 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551735Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 592 for workflow object_snapshot in 14.056µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551735Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551738Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 591 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551741Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 588 for workflow object_snapshot in 23.303µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551741Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 579 for workflow object_snapshot in 47.739µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551744Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 593 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551743Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551745Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551746Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 578 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551749Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551749Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 590 for workflow object_snapshot in 19.136µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551749Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 583 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551750Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 584 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551754Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 591 for workflow object_snapshot in 14.498µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551755Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551756Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 595 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551758Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 593 for workflow object_snapshot in 12.614µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551758Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551759Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551760Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 594 for workflow object_snapshot in 23.534µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551765Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551767Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 578 for workflow object_snapshot in 18.274µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551768Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 597 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551768Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 586 for workflow object_snapshot in 41.828µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551763Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 596 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551773Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 584 for workflow object_snapshot in 21.019µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551772Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 583 for workflow object_snapshot in 20.709µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551774Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 598 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551776Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 599 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551779Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551779Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551779Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 595 for workflow object_snapshot in 20.088µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551781Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 600 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551785Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551786Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 601 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551787Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 559 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551788Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 598 for workflow object_snapshot in 12.283µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551790Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551792Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 597 for workflow object_snapshot in 22.412µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551795Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 602 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551795Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551797Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551800Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551801Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 603 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551803Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 604 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551803Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 600 for workflow object_snapshot in 19.486µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551804Z INFO sui_data_ingestion_core::worker_pool: finished checkpoint processing 596 for workflow object_snapshot in 32.24µs +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551808Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551812Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 606 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551814Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551819Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551822Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 607 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551823Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 608 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551824Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 561 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551811Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 605 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551829Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551830Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551835Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 609 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551839Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 563 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551838Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551844Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551851Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 611 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551853Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 612 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551853Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 610 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551855Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 613 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551858Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551859Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551860Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 615 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551861Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551862Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551862Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 616 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551866Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551867Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 617 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551868Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551868Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 618 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551869Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 614 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551874Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551874Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551874Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 619 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551875Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551877Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 620 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551882Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551885Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 621 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551886Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551891Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 574 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551892Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 623 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551895Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 622 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551899Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551898Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 624 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551901Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551900Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551905Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551910Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 625 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551916Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551916Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 626 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551919Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 578 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551926Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551928Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 628 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551928Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 627 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551934Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 583 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551934Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551933Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 629 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551936Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551940Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 630 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551943Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551944Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 631 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551947Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 584 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551947Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 632 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551950Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551951Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551953Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 633 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551955Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551958Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551965Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 634 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551970Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 595 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551973Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551976Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 635 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551981Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 636 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551982Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551982Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 637 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551989Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551990Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551995Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 639 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551995Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 641 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551997Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 638 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.551999Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 648 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552002Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552005Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552006Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552005Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 596 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552005Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 644 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552006Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552010Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 640 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552011Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 642 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552014Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552016Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552016Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552018Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 645 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552019Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 643 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552022Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 649 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552023Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552026Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 650 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552026Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552026Z INFO sui_data_ingestion_core::reader: cleaning processed files, watermark is 599 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552027Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 651 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552027Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 652 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552030Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 647 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552031Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552031Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 646 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552032Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552034Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552037Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552038Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552040Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552049Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 653 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552052Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 654 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552053Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 656 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552053Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 655 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552057Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552058Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552059Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552060Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552073Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 658 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552073Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 657 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552078Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552080Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552084Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 659 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552091Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552100Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 661 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552102Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 660 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552106Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552109Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552125Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 662 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552132Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552132Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 666 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552139Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552143Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 664 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552144Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 663 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552148Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552149Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 665 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552150Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 669 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552154Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552155Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552164Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552164Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 667 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552166Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 671 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552167Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 670 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552167Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 668 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552169Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552170Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552171Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552172Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552181Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 673 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552182Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 672 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552185Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552186Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 674 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552187Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552191Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552195Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 675 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552199Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552199Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 676 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552202Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552207Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 677 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552212Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552215Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 678 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552220Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552223Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 680 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552225Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 679 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552227Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552231Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552235Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 682 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552237Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 681 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552240Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552242Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552247Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 683 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552248Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 684 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552252Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552254Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552255Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 685 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552257Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552261Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 686 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552265Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552267Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 687 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552270Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552278Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 689 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552280Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 688 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552282Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552285Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552294Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 691 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552295Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 690 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552296Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552298Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552302Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 692 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552304Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552310Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 693 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552313Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552324Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 694 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552328Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552335Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 695 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552339Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552347Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 697 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552353Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552354Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 696 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552358Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 698 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552361Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552363Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552370Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 699 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552372Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552375Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 700 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552379Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 701 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552382Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552383Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552384Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 702 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552387Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552389Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 703 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552389Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 705 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552391Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552392Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 704 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552394Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552394Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552412Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 706 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552412Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 709 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552416Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552418Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552425Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 707 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552426Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 710 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552427Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 708 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552428Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 712 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552431Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552431Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552431Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552437Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 711 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552439Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552442Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552444Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 713 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552449Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 714 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552449Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 715 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552452Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552452Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552461Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552486Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 718 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552494Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552494Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 716 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552496Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 721 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552499Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 717 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552502Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552502Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 722 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552504Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552506Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552507Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552508Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 719 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552510Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 724 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552510Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 723 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552511Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552512Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552515Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552518Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 720 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552520Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 726 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552521Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 725 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552525Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 727 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552524Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552525Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552525Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552527Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552537Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 728 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.552539Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653388Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 729, pruning watermark: 599, new updates: 41 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653508Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 740 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653519Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 742 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653522Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653521Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 734 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653526Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653534Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653537Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 735 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653540Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 741 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653541Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653546Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653549Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 729 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653550Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 736 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653554Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653555Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653559Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 743 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653559Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 745 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653562Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 730 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653565Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653567Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653569Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 746 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653572Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 731 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653572Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 737 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653572Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 744 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653573Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653575Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653577Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653578Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653581Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653582Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 732 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653585Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653589Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 747 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653592Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 733 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653592Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 738 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653593Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 739 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653593Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 748 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653594Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653597Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653599Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653599Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653598Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 751 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653599Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653605Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653609Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 750 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653612Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 753 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653613Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 752 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653615Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653608Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 749 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653619Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 754 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653619Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653621Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653624Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653627Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653648Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 759 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653650Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 755 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653648Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 756 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653653Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 757 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653656Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653659Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653661Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653662Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653666Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 758 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653672Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653671Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 763 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653672Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 761 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653677Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653678Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653678Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 760 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653683Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653684Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 762 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653688Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 764 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653690Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653692Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 765 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653692Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 766 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653693Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653697Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653699Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653711Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 767 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653713Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 769 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653716Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653717Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 768 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653721Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.653722Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.753697Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 770, pruning watermark: 599, new updates: 0 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.855439Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 770, pruning watermark: 599, new updates: 0 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956261Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 770, pruning watermark: 599, new updates: 200 +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956338Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 772 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956349Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956370Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 770 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956378Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956374Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 777 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956388Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956389Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 771 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956394Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956406Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 774 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956410Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 775 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956412Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956412Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 782 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956417Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956420Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 773 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956425Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956425Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956432Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 776 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956437Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956437Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 783 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956443Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956441Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 785 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956448Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 778 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956452Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956465Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956471Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 780 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956470Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 784 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956474Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 786 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956476Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956473Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 788 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956479Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 781 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956480Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956484Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956483Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956486Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956487Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 779 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956495Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956496Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 789 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956502Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956503Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 791 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956505Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 790 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956504Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 787 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956508Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956510Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956510Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 792 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956519Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956521Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 794 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956523Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 795 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956523Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 793 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956526Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956529Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956532Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956536Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 797 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956536Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 798 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956537Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956538Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 796 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956544Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956548Z INFO sui_data_ingestion_core::worker_pool: received checkpoint for processing 799 for workflow object_snapshot +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956549Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956553Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:35.956554Z INFO sui_indexer::handlers::checkpoint_handler: Resolving Move struct layouts for struct tags of size 1. +Sep 12 13:41:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:36.057196Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 970, pruning watermark: 599, new updates: 5 +Sep 12 13:41:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:36.158435Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 975, pruning watermark: 599, new updates: 27 +Sep 12 13:41:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:36.259421Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 1002, pruning watermark: 599, new updates: 58 +Sep 12 13:41:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:36.360395Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 1060, pruning watermark: 599, new updates: 151 +Sep 12 13:41:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:36.460846Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 1211, pruning watermark: 599, new updates: 49 +Sep 12 13:41:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:36.562153Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 1260, pruning watermark: 599, new updates: 113 +Sep 12 13:41:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:36.663250Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 1373, pruning watermark: 599, new updates: 96 +Sep 12 13:41:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:36.763647Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 1469, pruning watermark: 599, new updates: 107 +Sep 12 13:41:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:36.865045Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 1576, pruning watermark: 599, new updates: 67 +Sep 12 13:41:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:36.966365Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 1643, pruning watermark: 599, new updates: 101 +Sep 12 13:41:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:37.066983Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 1744, pruning watermark: 599, new updates: 144 +Sep 12 13:41:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:37.168314Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 1888, pruning watermark: 599, new updates: 102 +Sep 12 13:41:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:37.269400Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 1990, pruning watermark: 599, new updates: 157 +Sep 12 13:41:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:37.370348Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 2147, pruning watermark: 599, new updates: 76 +Sep 12 13:41:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:37.471198Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 2223, pruning watermark: 599, new updates: 0 +Sep 12 13:41:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:37.572193Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 2223, pruning watermark: 599, new updates: 0 +Sep 12 13:41:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:37.673099Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 2223, pruning watermark: 599, new updates: 200 +Sep 12 13:41:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:37.774117Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 2423, pruning watermark: 599, new updates: 0 +Sep 12 13:41:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:37.875434Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 2423, pruning watermark: 599, new updates: 4 +Sep 12 13:41:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:37.976574Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 2427, pruning watermark: 599, new updates: 196 +Sep 12 13:41:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:38.077568Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 2623, pruning watermark: 599, new updates: 61 +Sep 12 13:41:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:38.179251Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 2684, pruning watermark: 599, new updates: 145 +Sep 12 13:41:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:38.280416Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 2829, pruning watermark: 599, new updates: 47 +Sep 12 13:41:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:38.380641Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 2876, pruning watermark: 599, new updates: 153 +Sep 12 13:41:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:38.482202Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 3029, pruning watermark: 599, new updates: 90 +Sep 12 13:41:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:38.583082Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 3119, pruning watermark: 599, new updates: 117 +Sep 12 13:41:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:38.683784Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 3236, pruning watermark: 599, new updates: 111 +Sep 12 13:41:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:38.784885Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 3347, pruning watermark: 599, new updates: 109 +Sep 12 13:41:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:38.885824Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 3456, pruning watermark: 599, new updates: 125 +Sep 12 13:41:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:38.986659Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 3581, pruning watermark: 599, new updates: 112 +Sep 12 13:41:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:39.088532Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 3693, pruning watermark: 599, new updates: 17 +Sep 12 13:41:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:39.190056Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 3710, pruning watermark: 599, new updates: 153 +Sep 12 13:41:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:39.290694Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 3863, pruning watermark: 599, new updates: 30 +Sep 12 13:41:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:39.391676Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 3893, pruning watermark: 599, new updates: 105 +Sep 12 13:41:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:39.493002Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 3998, pruning watermark: 599, new updates: 85 +Sep 12 13:41:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:39.593916Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 4083, pruning watermark: 599, new updates: 6 +Sep 12 13:41:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:39.695469Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 4089, pruning watermark: 599, new updates: 200 +Sep 12 13:41:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:39.796750Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 4289, pruning watermark: 599, new updates: 5 +Sep 12 13:41:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:39.897853Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 4294, pruning watermark: 599, new updates: 119 +Sep 12 13:41:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:39.999029Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 4413, pruning watermark: 599, new updates: 81 +Sep 12 13:41:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:40.099742Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 4494, pruning watermark: 599, new updates: 24 +Sep 12 13:41:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:40.200601Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 4518, pruning watermark: 599, new updates: 97 +Sep 12 13:41:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:40.301651Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 4615, pruning watermark: 599, new updates: 105 +Sep 12 13:41:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:40.403040Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 4720, pruning watermark: 599, new updates: 41 +Sep 12 13:41:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:40.504238Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 4761, pruning watermark: 599, new updates: 87 +Sep 12 13:41:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:40.605122Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 4848, pruning watermark: 599, new updates: 105 +Sep 12 13:41:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:40.706105Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 4953, pruning watermark: 599, new updates: 97 +Sep 12 13:41:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:40.806745Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 5050, pruning watermark: 599, new updates: 103 +Sep 12 13:41:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:40.908475Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 5153, pruning watermark: 599, new updates: 167 +Sep 12 13:41:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:41.009712Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 5320, pruning watermark: 599, new updates: 143 +Sep 12 13:41:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:41.110733Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 5463, pruning watermark: 599, new updates: 9 +Sep 12 13:41:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:41.212271Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 5472, pruning watermark: 599, new updates: 99 +Sep 12 13:41:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:41.312672Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 5571, pruning watermark: 599, new updates: 108 +Sep 12 13:41:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:41.413576Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 5679, pruning watermark: 599, new updates: 97 +Sep 12 13:41:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:41.515013Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 5776, pruning watermark: 599, new updates: 154 +Sep 12 13:41:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:41.615557Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 5930, pruning watermark: 599, new updates: 81 +Sep 12 13:41:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:41.716553Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 6011, pruning watermark: 599, new updates: 0 +Sep 12 13:41:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:41.818476Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 6011, pruning watermark: 599, new updates: 0 +Sep 12 13:41:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:41.919158Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 6011, pruning watermark: 599, new updates: 200 +Sep 12 13:41:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:42.020096Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 6211, pruning watermark: 599, new updates: 0 +Sep 12 13:41:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:42.120827Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 6211, pruning watermark: 599, new updates: 12 +Sep 12 13:41:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:42.222418Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 6223, pruning watermark: 599, new updates: 5 +Sep 12 13:41:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:42.323390Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 6228, pruning watermark: 599, new updates: 200 +Sep 12 13:41:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:42.423655Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 6428, pruning watermark: 599, new updates: 3 +Sep 12 13:41:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:42.524797Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 6431, pruning watermark: 599, new updates: 199 +Sep 12 13:41:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:42.626339Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 6630, pruning watermark: 599, new updates: 129 +Sep 12 13:41:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:42.727408Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 6759, pruning watermark: 599, new updates: 78 +Sep 12 13:41:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:42.827955Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 6837, pruning watermark: 599, new updates: 196 +Sep 12 13:41:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:42.928570Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 7033, pruning watermark: 599, new updates: 35 +Sep 12 13:41:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:43.030548Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 7068, pruning watermark: 599, new updates: 138 +Sep 12 13:41:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:43.131741Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 7206, pruning watermark: 599, new updates: 70 +Sep 12 13:41:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:43.233546Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 7276, pruning watermark: 599, new updates: 187 +Sep 12 13:41:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:43.334627Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 7463, pruning watermark: 599, new updates: 44 +Sep 12 13:41:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:43.435798Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 7507, pruning watermark: 599, new updates: 172 +Sep 12 13:41:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:43.536713Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 7679, pruning watermark: 599, new updates: 114 +Sep 12 13:41:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:43.638263Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 7793, pruning watermark: 599, new updates: 0 +Sep 12 13:41:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:43.739308Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 7793, pruning watermark: 599, new updates: 187 +Sep 12 13:41:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:43.840115Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 7980, pruning watermark: 599, new updates: 19 +Sep 12 13:41:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:43.940624Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 7999, pruning watermark: 599, new updates: 196 +Sep 12 13:41:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:44.041719Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 8195, pruning watermark: 599, new updates: 52 +Sep 12 13:41:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:44.143439Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 8247, pruning watermark: 599, new updates: 0 +Sep 12 13:41:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:44.243737Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 8247, pruning watermark: 599, new updates: 200 +Sep 12 13:41:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:44.344942Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 8447, pruning watermark: 599, new updates: 43 +Sep 12 13:41:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:44.446489Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 8490, pruning watermark: 599, new updates: 174 +Sep 12 13:41:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:44.548310Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 8664, pruning watermark: 599, new updates: 128 +Sep 12 13:41:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:44.649491Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 8792, pruning watermark: 599, new updates: 99 +Sep 12 13:41:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:44.751218Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 8891, pruning watermark: 599, new updates: 0 +Sep 12 13:41:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:44.851795Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 8891, pruning watermark: 599, new updates: 200 +Sep 12 13:41:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:44.952652Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 9091, pruning watermark: 599, new updates: 81 +Sep 12 13:41:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:45.053748Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 9172, pruning watermark: 599, new updates: 124 +Sep 12 13:41:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:45.154759Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 9296, pruning watermark: 599, new updates: 197 +Sep 12 13:41:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:45.256047Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 9493, pruning watermark: 599, new updates: 9 +Sep 12 13:41:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:45.357619Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 9502, pruning watermark: 599, new updates: 200 +Sep 12 13:41:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:45.459008Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 9702, pruning watermark: 599, new updates: 51 +Sep 12 13:41:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:45.560018Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 9753, pruning watermark: 599, new updates: 58 +Sep 12 13:41:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:45.660802Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 9811, pruning watermark: 599, new updates: 0 +Sep 12 13:41:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:45.762231Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 9811, pruning watermark: 599, new updates: 200 +Sep 12 13:41:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:45.863395Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10011, pruning watermark: 599, new updates: 200 +Sep 12 13:41:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:45.964099Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10211, pruning watermark: 599, new updates: 6 +Sep 12 13:41:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:46.065603Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10217, pruning watermark: 599, new updates: 194 +Sep 12 13:41:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:46.166559Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10411, pruning watermark: 599, new updates: 6 +Sep 12 13:41:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:46.268530Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10417, pruning watermark: 599, new updates: 182 +Sep 12 13:41:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:46.369671Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:46.471295Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:46.571776Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:46.672772Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:46.774179Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:46.875549Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:46.976964Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:47.078264Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:47.179675Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:47.281069Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:47.382484Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:47.482823Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:47.584212Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:47.685607Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:47.786922Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:47.888305Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:47.988563Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:48.089964Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:48.191304Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:48.292690Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:48.394101Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:48.495494Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:48.595894Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:48.697302Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:48.798542Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:48.899959Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:49.001344Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:49.101752Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:49.203061Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:49.304474Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:49.404862Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:49.506088Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:49.607439Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:49.707677Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:49.809079Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:49.910511Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:50.011905Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:50.113322Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:50.214705Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:50.316118Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:50.417512Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:50.518826Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:50.620233Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:50.721598Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:50.822993Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:50.924364Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:51.024748Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:51.126159Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:51.227535Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:51.328936Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:51.430301Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:51.531692Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:51.633068Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:51.734443Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:51.834864Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:51.936249Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:52.037670Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:52.139063Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:52.240489Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:52.340755Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:52.442152Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:52.543580Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:52.644952Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:52.746339Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:52.846616Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:52.947994Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:53.049397Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:53.149765Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:53.251160Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:53.352471Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:53.452874Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:53.554269Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:53.655647Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:53.757064Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:53.858305Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:53.958719Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:54.060108Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:54.161511Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:54.262941Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:54.364335Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:54.465562Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:54.566770Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:54.668078Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:54.769392Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:54.869796Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:54.971141Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:55.072533Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:55.173955Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:55.275345Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:55.375756Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:55.477190Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:55.578590Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:55.679998Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:55.781363Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:55.881755Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:55.983148Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:56.084487Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:56.184922Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:56.286318Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:56.386748Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:56.488138Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:56.589550Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:56.690931Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:56.792310Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:56.892716Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:56.994014Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:57.095408Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:57.195793Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:57.297201Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:57.398606Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:57.499941Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:57.601513Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:57.702874Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:57.804272Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:57.905679Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:58.007077Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:58.108501Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:58.208802Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:58.310227Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:58.411614Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:58.513017Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:58.614433Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:58.714805Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:58.816297Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:58.917661Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:59.019071Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:59.120481Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:59.220797Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:59.322225Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:59.423614Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:59.524819Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:59.625982Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:59.727364Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:59.828545Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:41:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:41:59.929943Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:00.031365Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:00.131749Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:00.233162Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:00.334551Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:00.435951Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:00.537380Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:00.637755Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:00.739147Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:00.840508Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:00.941905Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:01.043301Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:01.144698Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:01.246122Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:01.347503Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:01.447916Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:01.549306Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:01.650596Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:01.752002Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:01.853371Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:01.953777Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:02.055161Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:02.156569Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:02.257964Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:02.359363Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:02.459789Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:02.561091Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:02.662488Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:02.762852Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:02.864232Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:02.965583Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:03.066970Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:03.168391Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:03.268777Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:03.370100Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:03.471491Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:03.571813Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:03.673133Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:03.774519Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:03.875844Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:03.977144Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:04.078480Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:04.178877Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:04.280274Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:04.381615Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:04.482915Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:04.584226Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:04.685681Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:04.787084Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:04.888349Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:04.988744Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:05.090164Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:05.191554Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:05.292962Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:05.394355Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:05.494757Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:05.596183Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:05.697564Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:05.799137Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:05.900547Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:06.001741Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:06.103175Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:06.204575Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:06.306096Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:06.407136Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:06.508044Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:06.609344Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:06.709746Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:06.811178Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:06.912566Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:07.013820Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:07.115130Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:07.216471Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:07.316733Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:07.418129Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:07.519555Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:07.620923Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:07.722311Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:07.823680Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:07.925059Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:08.026499Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:08.126895Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:08.228314Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:08.329700Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:08.431105Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:08.532371Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:08.632660Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:08.734057Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:08.834857Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:08.936278Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:09.037669Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:09.139073Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:09.240421Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:09.340813Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:09.442144Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:09.543426Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:09.643836Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:09.745059Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:09.846480Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:09.946907Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:10.048304Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:10.148720Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:10.250104Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:10.351432Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:10.452715Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:10.554108Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:10.655532Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:10.756916Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:10.858327Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:10.958674Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:11.060000Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:11.161426Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:11.261819Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:11.363237Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:11.464622Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:11.566029Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:11.667426Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:11.767823Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:11.869247Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:11.970609Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:12.072031Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:12.173333Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:12.273736Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:12.375165Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:12.476559Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:12.577876Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:12.679236Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:12.780643Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:12.882039Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:12.983352Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:13.083785Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:13.185106Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:13.286520Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:13.387909Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:13.489312Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:13.589654Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:13.691050Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:13.792028Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:13.893393Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:13.993961Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:14.095350Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:14.195755Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:14.297188Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:14.398579Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:14.499995Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:14.601380Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:14.701778Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:14.803022Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:14.904426Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:15.004690Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:15.106080Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:15.207498Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:15.307887Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:15.409204Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:15.510634Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:15.611924Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:15.713319Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:15.814678Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:15.915976Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:16.017360Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:16.117759Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:16.219186Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:16.320576Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:16.421906Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:16.523193Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:16.624569Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:16.725976Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:16.827259Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:16.928658Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:17.030038Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:17.131452Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:17.231859Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:17.333259Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:17.434686Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:17.535988Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:17.637386Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:17.737749Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:17.839132Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:17.940377Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:18.040765Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:18.142187Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:18.243574Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:18.344985Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:18.446375Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:18.546777Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:18.648010Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:18.749290Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:18.849791Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:18.951150Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:19.052504Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:19.153907Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:19.255131Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:19.356555Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:19.457943Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:19.559356Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:19.659723Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:19.761101Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:19.862336Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:19.963665Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:20.065083Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:20.166388Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:20.266800Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:20.368192Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:20.469590Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:20.571015Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:20.672399Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:20.772814Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:20.874202Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:20.975609Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:21.077041Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:21.178434Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:21.278860Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:21.380246Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:21.481585Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:21.582977Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:21.684371Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:21.784799Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:21.886190Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:21.987531Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:22.088916Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:22.190390Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:22.290786Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:22.392184Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:22.493598Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:22.594983Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:22.696389Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:22.796776Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:22.898178Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:22.999609Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:23.101000Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:23.202420Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:23.302802Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:23.404210Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:23.505604Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:23.607002Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:23.708405Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:23.809132Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:23.910555Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:24.011945Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:24.113356Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:24.213784Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:24.315179Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:24.416600Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:24.517984Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:24.619394Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:24.720613Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:24.822013Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:24.923255Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:25.024566Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:25.125832Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:25.227131Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:25.328537Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:25.429967Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:25.531362Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:25.631783Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:25.733167Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:25.834579Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:25.935971Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:26.037292Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:26.137723Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:26.239021Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:26.340437Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:26.440837Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:26.542244Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:26.643641Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:26.745035Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:26.846471Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:26.946860Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:27 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:27.048274Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:27 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:27.149680Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:27 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:27.251084Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:27 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:27.352514Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:27 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:27.453905Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:27 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:27.555323Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:27 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:27.656708Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:27 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:27.758118Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:27 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:27.859514Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:27 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:27.960911Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:28 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:28.062340Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:28 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:28.162724Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:28 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:28.264137Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:28 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:28.365526Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:28 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:28.466926Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:28 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:28.568354Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:28 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:28.668747Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:28 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:28.770161Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:28 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:28.871630Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:28 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:28.973040Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:29 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:29.074439Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:29 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:29.174848Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:29 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:29.276272Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:29 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:29.377673Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:29 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:29.478999Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:29 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:29.580389Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:29 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:29.680792Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:29 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:29.782048Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:29 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:29.883427Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:29 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:29.983673Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:30 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:30.084901Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:30 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:30.186315Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:30 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:30.287707Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:30 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:30.389271Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:30 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:30.490587Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:30 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:30.591946Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:30 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:30.693245Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:30 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:30.794627Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:30 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:30.896030Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:30 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:30.997475Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:31 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:31.097741Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:31 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:31.199078Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:31 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:31.300483Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:31 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:31.400899Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:31 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:31.502290Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:31 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:31.603627Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:31 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:31.705056Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:31 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:31.806450Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:31 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:31.906885Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:32 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:32.008269Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:32 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:32.109581Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:32 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:32.210893Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:32 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:32.312204Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:32 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:32.413609Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:32 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:32.514908Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:32 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:32.616143Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:32 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:32.717356Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:32 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:32.817759Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:32 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:32.919190Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:33.020559Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:33.121959Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:33.223318Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:33.323699Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:33.425069Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:33.526477Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:33.626899Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:33.728285Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:33.769639Z INFO sui_indexer::db::setup_postgres: DB connection pool size: 100, with idle conn: 100. +Sep 12 13:42:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:33.829103Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:33 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:33.930507Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:34.031912Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:34.133340Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:34.233633Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:34.335058Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:34.436438Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:34.536867Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:34.638256Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:34.739572Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:34.840999Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:34 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:34.942391Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:35.043577Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:35.144804Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:35.245850Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:35.347246Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:35.448646Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:35.550041Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:35.651426Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:35.751841Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:35.853232Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:35 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:35.954635Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:36.055893Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:36.157285Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:36.258703Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:36.360088Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:36.461496Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:36.561890Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:36.663289Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:36.764665Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:36.866052Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:36 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:36.967486Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:37.067688Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:37.169068Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:37.270485Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:37.370853Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:37.472232Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:37.573583Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:37.674967Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:37.776336Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:37.876727Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:37 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:37.978155Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:38.079544Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:38.180960Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:38.282346Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:38.382752Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:38.484006Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:38.585403Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:38.685826Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:38.787207Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:38.887851Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:38 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:38.989239Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:39.090643Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:39.192070Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:39.293376Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:39.393708Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:39.495092Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:39.596501Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:39.696893Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:39.798290Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:39 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:39.898716Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:40.000105Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:40.101329Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:40.202618Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:40.303940Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:40.405374Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:40.505770Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:40.607191Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:40.708578Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:40.809988Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:40 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:40.911378Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:41.011782Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:41.113208Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:41.214515Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:41.315931Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:41.417229Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:41.518638Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:41.619903Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:41.721299Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:41.821636Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:41 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:41.923022Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:42.024436Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:42.124764Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:42.226170Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:42.327604Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:42.428996Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:42.530328Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:42.631708Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:42.733117Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:42.834510Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:42 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:42.935908Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:43.037331Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:43.137720Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:43.239134Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:43.340523Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:43.441927Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:43.543358Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:43.643750Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:43.745171Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:43.846635Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:43 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:43.948044Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:44.049438Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:44.149856Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:44.251109Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:44.352499Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:44.452915Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:44.554284Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:44.655697Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:44.757124Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:44.858522Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:44 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:44.959746Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:45.061119Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:45.162411Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:45.262639Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:45.364171Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:45.465600Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:45.566994Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:45.668392Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:45.768766Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:45.870088Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:45 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:45.971504Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:46.071904Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:46.173335Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:46.273723Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:46.375143Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:46.476531Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:46.577938Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:46.679342Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:46.779713Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:46.881113Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:46 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:46.982485Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:47.082896Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:47.184207Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:47.285412Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:47.385819Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:47.487188Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:47.588600Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:47.689968Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:47.791278Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:47.892548Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:47 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:47.993946Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:48.095375Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:48.195766Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:48.297181Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:48.398490Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:48.498895Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:48.600327Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:48.701593Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:48.803337Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:48 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:48.903719Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:49.005127Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:49.106521Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:49.207924Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:49.309351Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:49.409739Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:49.511153Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:49.612543Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:49.713924Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:49.815356Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:49 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:49.915748Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:50.017006Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:50.118321Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:50.219565Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:50.320767Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:50.422164Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:50.523512Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:50.624884Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:50.726289Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:50.827671Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:50 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:50.929073Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:51.030507Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:51.131902Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:51.233324Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:51.334705Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:51.436028Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:51.537335Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:51.637715Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:51.739034Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:51.840398Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:51 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:51.940790Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:52.042165Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:52.143569Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:52.245004Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:52.346396Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:52.446817Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:52.548115Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:52.649521Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:52.750825Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:52.852222Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:52 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:52.953565Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:53.054957Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:53.156374Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:53.256763Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:53.358169Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:53.459566Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:53.560874Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:53.662277Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:53.763666Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:53.865451Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:53 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:53.965860Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:54.067267Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:54.168702Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:54.269989Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:54.371311Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:54.472697Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:54.574106Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:54.675492Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:54.775882Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:54.877300Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:54 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:54.978602Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:55.080021Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:55.181415Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:55.281685Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:55.382953Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:55.484346Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:55.585578Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:55.686944Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:55.788328Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:55.889608Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:55 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:55.990983Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:56.092357Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:56.192744Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:56.294161Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:56.395546Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:56.496949Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:56.598299Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:56.699584Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:56.800928Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:56 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:56.902287Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:57.003672Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:57.105059Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:57.206449Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:57.306895Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:57.408284Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:57.509566Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:57.610830Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:57.712201Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:57.813650Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:57 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:57.915013Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:58.016398Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:58.116762Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:58.218155Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:58.319523Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:58.420902Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:58.522310Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:58.623678Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:58.725072Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:58.826666Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:58 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:58.928070Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:59.029599Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:59.130989Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:59.232411Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:59.332709Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:59.434118Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:59.535505Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:59.636886Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:59.738286Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:59.839663Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:42:59 sui-mainnet-node cargo[3520474]: 2024-09-12T11:42:59.941079Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:00.042482Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:00.142888Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:00.244148Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:00.345470Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:00.445737Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:00.547133Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:00.648368Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:00.748740Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:00.850151Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:00 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:00.951578Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:01.052970Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:01.154391Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:01.254773Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:01.356179Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:01.457571Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:01.558881Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:01.660285Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:01.761649Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:01.863037Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:01 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:01.964401Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:02.064799Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:02.166227Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:02.267622Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:02.369040Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:02.470314Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:02.570723Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:02.672096Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:02.773479Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:02.873880Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:02 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:02.975246Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:03.076672Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:03.178059Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:03.279476Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:03.380736Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:03.482045Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:03.583479Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:03.683775Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:03.785179Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:03.886685Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:03 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:03.988091Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:04.089570Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:04.190946Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:04.292262Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:04.393653Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:04.495060Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:04.596325Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:04.696614Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:04.798014Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:04.899378Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:04 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:04.999698Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:05.101093Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:05.202502Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:05.302931Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:05.404221Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:05.505488Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:05.605881Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:05.707298Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:05.808694Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:05 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:05.909922Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:06.011349Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:06.111745Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:06.213167Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:06.314555Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:06.415963Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:06.517381Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:06.617765Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:06.719117Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:06.820504Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:06 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:06.920915Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:07.022274Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:07.123601Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:07.224993Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:07.326383Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:07.426805Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:07.528057Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:07.629470Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:07.729840Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:07.831141Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:07 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:07.932568Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:08.033872Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:08.135206Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:08.236598Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:08.338005Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:08.439436Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:08.539847Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:08.641250Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:08.742629Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:08.844272Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:08 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:08.945679Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:09.047075Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:09.148498Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:09.248886Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:09.350304Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:09.451693Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:09.553099Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:09.654498Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:09.754867Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:09.856263Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:09 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:09.957625Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:10.059028Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:10.160423Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:10.260830Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:10.362258Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:10.463664Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:10.564888Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:10.666291Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:10.767616Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:10.869042Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:10 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:10.970440Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:11.070878Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:11.172267Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:11.273677Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:11.375066Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:11.476485Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:11.576911Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:11.678287Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:11.779681Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:11.880940Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:11 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:11.982324Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:12.083706Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:12.185104Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:12.286527Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:12.387912Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:12.489235Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:12.590541Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:12.691931Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:12.793358Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:12.893653Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:12 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:12.995075Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:13.096475Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:13.196885Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:13.298194Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:13.399594Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:13.501023Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:13.602410Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:13.702809Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:13.804183Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:13 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:13.904814Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:14.006247Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:14.107649Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:14.208995Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:14.310376Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:14.410790Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:14.512185Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:14.613584Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:14.714992Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:14.816380Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:14 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:14.916798Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:15.018186Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:15.119597Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:15.221033Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:15.322433Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:15.422994Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:15.524325Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:15.625548Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:15.726927Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:15.828224Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:15 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:15.929670Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:16.031061Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:16.132498Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:16.232802Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:16.334209Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:16.435521Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:16.536923Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:16.638329Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:16.739693Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:16.841101Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:16 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:16.942495Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:17.042899Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:17.144333Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:17.244718Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:17.346139Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:17.447524Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:17.548933Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:17.650298Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:17.751674Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:17.853093Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:17 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:17.954497Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:18.054721Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:18.156100Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:18.257419Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:18.357850Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:18.459248Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:18.560672Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:18.662045Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:18.763447Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:18.863849Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:18 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:18.965247Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:19.066674Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:19.167898Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:19.269231Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:19.370621Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:19.471939Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:19.573370Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:19.673745Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:19.775143Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:19.876504Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:19 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:19.976895Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:20.078276Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:20.179681Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:20.281110Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:20.382499Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:20.482919Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:20.584273Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:20.685499Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:20.785814Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:20.887216Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:20 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:20.988641Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:21.090029Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:21.191446Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:21.291775Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:21.393178Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:21.494612Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:21.596006Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:21.697324Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:21.798683Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:21 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:21.900067Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:22.001435Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:22.101828Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:22.203257Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:22.304649Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:22.406063Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:22.507453Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:22.607872Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:22.709303Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:22.810669Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:22 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:22.912093Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:23.013405Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:23.113731Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:23.215124Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:23.316524Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:23.417950Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:23.519345Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:23.619892Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:23.721251Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:23.821802Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:23 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:23.923198Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:24.024599Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:24.126026Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:24.227415Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:24.327753Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:24.429146Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:24.530478Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:24.630890Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:24.732261Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:24.833659Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:24 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:24.935037Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:25.036447Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:25.136772Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:25.238175Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:25.339604Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:25.440996Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:25.542243Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:25.643619Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:25.744856Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:25.846298Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:25 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:25.947628Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:26.048973Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:26.150362Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:26.250776Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:26.352171Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:26.453573Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:26.555005Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:26.656383Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:26.756780Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:26.858143Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:26 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:26.959525Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:27 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:27.060937Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:27 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:27.162224Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:27 sui-mainnet-node cargo[3520474]: 2024-09-12T11:43:27.263624Z INFO sui_data_ingestion_core::reader: Read from remote. Current checkpoint number: 10599, pruning watermark: 599, new updates: 0 +Sep 12 13:43:27 sui-mainnet-node systemd[1]: Stopping sui-indexer.service - Sui indexer... +Sep 12 13:43:27 sui-mainnet-node systemd[1]: sui-indexer.service: Deactivated successfully. +Sep 12 13:43:27 sui-mainnet-node systemd[1]: Stopped sui-indexer.service - Sui indexer. +Sep 12 13:43:27 sui-mainnet-node systemd[1]: sui-indexer.service: Consumed 3min 55.279s CPU time. diff --git a/crates/sui-indexer/src/handlers/checkpoint_handler.rs b/crates/sui-indexer/src/handlers/checkpoint_handler.rs index 2d1deb98978d1..009df30c1cfe3 100644 --- a/crates/sui-indexer/src/handlers/checkpoint_handler.rs +++ b/crates/sui-indexer/src/handlers/checkpoint_handler.rs @@ -5,25 +5,35 @@ use std::collections::{BTreeMap, HashMap}; use std::sync::{Arc, Mutex}; use async_trait::async_trait; +use chrono::Utc; use diesel::r2d2::R2D2Connection; use itertools::Itertools; +use odin::structs::sui_notifications::{ + CoinReceived, CoinSent, CoinSwap, NftBurned, NftMinted, NftReceived, NftSent, + SuiIndexerNotification, +}; +use odin::sui_ws::{ + AccountObjectsUpdate, CoinCreated, CoinMutated, CoinObjectUpdateStatus, ObjectChangeUpdate, + ObjectUpdateStatus, SuiWsApiMsg, TokenBalanceUpdate, TokenUpdate, +}; +use sui_types::gas_coin::GAS; use tap::tap::TapFallible; use tokio::sync::watch; use tokio_util::sync::CancellationToken; -use tracing::{info, warn}; +use tracing::{error, info, warn}; use move_core_types::annotated_value::{MoveStructLayout, MoveTypeLayout}; use move_core_types::language_storage::{StructTag, TypeTag}; use mysten_metrics::{get_metrics, spawn_monitored_task}; use sui_data_ingestion_core::Worker; -use sui_json_rpc_types::SuiMoveValue; +use sui_json_rpc_types::{ObjectStatus, SuiMoveValue}; use sui_package_resolver::{PackageStore, PackageStoreWithLruCache, Resolver}; use sui_rest_api::{CheckpointData, CheckpointTransaction}; use sui_types::base_types::ObjectID; use sui_types::dynamic_field::DynamicFieldInfo; use sui_types::dynamic_field::DynamicFieldName; use sui_types::dynamic_field::DynamicFieldType; -use sui_types::effects::TransactionEffectsAPI; +use sui_types::effects::{TransactionEffects, TransactionEffectsAPI}; use sui_types::event::SystemEpochInfoEvent; use sui_types::messages_checkpoint::{ CertifiedCheckpointSummary, CheckpointContents, CheckpointSequenceNumber, @@ -44,15 +54,16 @@ use crate::models::display::StoredDisplay; use crate::store::package_resolver::{IndexerStorePackageResolver, InterimPackageResolver}; use crate::store::{IndexerStore, PgIndexerStore}; use crate::types::{ - EventIndex, IndexedCheckpoint, IndexedDeletedObject, IndexedEpochInfo, IndexedEvent, - IndexedObject, IndexedPackage, IndexedTransaction, IndexerResult, TransactionKind, TxIndex, + CustomIndexedTransaction, EventIndex, IndexedCheckpoint, IndexedDeletedObject, + IndexedEpochInfo, IndexedEvent, IndexedObject, IndexedPackage, IndexerResult, TransactionKind, + TxIndex, }; use super::tx_processor::EpochEndIndexingObjectStore; use super::tx_processor::TxChangesProcessor; -use super::CheckpointDataToCommit; use super::EpochToCommit; use super::TransactionObjectChangesToCommit; +use super::{CheckpointDataToCommit, CustomCheckpointDataToCommit}; const CHECKPOINT_QUEUE_SIZE: usize = 100; diff --git a/crates/sui-indexer/src/handlers/mod.rs b/crates/sui-indexer/src/handlers/mod.rs index ce2d0d07cc64d..376a74043abb7 100644 --- a/crates/sui-indexer/src/handlers/mod.rs +++ b/crates/sui-indexer/src/handlers/mod.rs @@ -6,8 +6,8 @@ use std::collections::BTreeMap; use crate::{ models::display::StoredDisplay, types::{ - EventIndex, IndexedCheckpoint, IndexedDeletedObject, IndexedEpochInfo, IndexedEvent, - IndexedObject, IndexedPackage, IndexedTransaction, TxIndex, + CustomIndexedTransaction, EventIndex, IndexedCheckpoint, IndexedDeletedObject, + IndexedEpochInfo, IndexedEvent, IndexedObject, IndexedPackage, IndexedTransaction, TxIndex, }, }; diff --git a/crates/sui-indexer/src/indexer.rs b/crates/sui-indexer/src/indexer.rs index fff48c69ac843..32435cf8e98ba 100644 --- a/crates/sui-indexer/src/indexer.rs +++ b/crates/sui-indexer/src/indexer.rs @@ -82,6 +82,7 @@ impl Indexer { .expect("Failed to get latest tx checkpoint sequence number from DB") .map(|seq| seq + 1) .unwrap_or_default(); + // let primary_watermark = 56614153; let download_queue_size = env::var("DOWNLOAD_QUEUE_SIZE") .unwrap_or_else(|_| DOWNLOAD_QUEUE_SIZE.to_string()) .parse::() diff --git a/crates/sui-indexer/src/lib.rs b/crates/sui-indexer/src/lib.rs index 6c1794c5f340a..c2d6b4e91e4a7 100644 --- a/crates/sui-indexer/src/lib.rs +++ b/crates/sui-indexer/src/lib.rs @@ -1,6 +1,6 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -#![recursion_limit = "256"] +#![recursion_limit = "512"] use std::net::SocketAddr; use std::time::Duration; diff --git a/crates/sui-indexer/src/schema/pg.rs b/crates/sui-indexer/src/schema/pg.rs index c1720c94b37a1..cbb24fcf8d6ce 100644 --- a/crates/sui-indexer/src/schema/pg.rs +++ b/crates/sui-indexer/src/schema/pg.rs @@ -1,5 +1,3 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 // @generated automatically by Diesel CLI. diesel::table! { @@ -27,7 +25,7 @@ diesel::table! { validator_signature -> Bytea, end_of_epoch_data -> Nullable, min_tx_sequence_number -> Nullable, - max_tx_sequence_number -> Nullable + max_tx_sequence_number -> Nullable, } } @@ -268,10 +266,2050 @@ diesel::table! { } diesel::table! { - protocol_configs (protocol_version, config_name) { - protocol_version -> Int8, - config_name -> Text, - config_value -> Nullable, + objects_version_00 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_01 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_02 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_03 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_04 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_05 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_06 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_07 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_08 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_09 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_0a (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_0b (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_0c (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_0d (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_0e (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_0f (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_10 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_11 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_12 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_13 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_14 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_15 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_16 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_17 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_18 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_19 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_1a (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_1b (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_1c (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_1d (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_1e (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_1f (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_20 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_21 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_22 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_23 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_24 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_25 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_26 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_27 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_28 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_29 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_2a (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_2b (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_2c (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_2d (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_2e (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_2f (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_30 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_31 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_32 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_33 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_34 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_35 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_36 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_37 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_38 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_39 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_3a (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_3b (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_3c (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_3d (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_3e (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_3f (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_40 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_41 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_42 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_43 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_44 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_45 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_46 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_47 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_48 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_49 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_4a (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_4b (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_4c (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_4d (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_4e (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_4f (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_50 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_51 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_52 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_53 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_54 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_55 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_56 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_57 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_58 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_59 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_5a (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_5b (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_5c (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_5d (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_5e (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_5f (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_60 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_61 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_62 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_63 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_64 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_65 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_66 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_67 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_68 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_69 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_6a (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_6b (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_6c (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_6d (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_6e (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_6f (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_70 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_71 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_72 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_73 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_74 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_75 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_76 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_77 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_78 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_79 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_7a (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_7b (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_7c (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_7d (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_7e (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_7f (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_80 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_81 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_82 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_83 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_84 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_85 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_86 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_87 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_88 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_89 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_8a (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_8b (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_8c (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_8d (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_8e (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_8f (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_90 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_91 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_92 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_93 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_94 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_95 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_96 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_97 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_98 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_99 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_9a (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_9b (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_9c (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_9d (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_9e (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_9f (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_a0 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_a1 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_a2 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_a3 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_a4 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_a5 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_a6 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_a7 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_a8 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_a9 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_aa (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_ab (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_ac (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_ad (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_ae (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_af (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_b0 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_b1 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_b2 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_b3 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_b4 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_b5 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_b6 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_b7 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_b8 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_b9 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_ba (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_bb (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_bc (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_bd (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_be (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_bf (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_c0 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_c1 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_c2 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_c3 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_c4 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_c5 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_c6 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_c7 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_c8 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_c9 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_ca (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_cb (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_cc (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_cd (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_ce (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_cf (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_d0 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_d1 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_d2 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_d3 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_d4 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_d5 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_d6 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_d7 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_d8 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_d9 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_da (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_db (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_dc (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_dd (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_de (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_df (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_e0 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_e1 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_e2 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_e3 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_e4 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_e5 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_e6 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_e7 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_e8 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_e9 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_ea (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_eb (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_ec (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_ed (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_ee (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_ef (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_f0 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_f1 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_f2 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_f3 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_f4 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_f5 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_f6 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_f7 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_f8 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_f9 (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_fa (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_fb (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_fc (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_fd (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_fe (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, + } +} + +diesel::table! { + objects_version_ff (object_id, object_version) { + object_id -> Bytea, + object_version -> Int8, + cp_sequence_number -> Int8, } } @@ -285,6 +2323,14 @@ diesel::table! { } } +diesel::table! { + protocol_configs (protocol_version, config_name) { + protocol_version -> Int8, + config_name -> Text, + config_value -> Nullable, + } +} + diesel::table! { pruner_cp_watermark (checkpoint_sequence_number) { checkpoint_sequence_number -> Int8, @@ -397,43 +2443,294 @@ diesel::table! { } } -#[macro_export] -macro_rules! for_all_tables { - ($action:path) => { - $action!( - chain_identifier, - checkpoints, - display, - epochs, - event_emit_module, - event_emit_package, - event_senders, - event_struct_instantiation, - event_struct_module, - event_struct_name, - event_struct_package, - events, - feature_flags, - objects_history, - objects_snapshot, - objects_version, - packages, - protocol_configs, - pruner_cp_watermark, - transactions, - tx_calls_fun, - tx_calls_mod, - tx_calls_pkg, - tx_changed_objects, - tx_digests, - tx_input_objects, - tx_kinds, - tx_recipients, - tx_senders - ); - }; -} - -pub use for_all_tables; - -for_all_tables!(diesel::allow_tables_to_appear_in_same_query); +diesel::allow_tables_to_appear_in_same_query!( + chain_identifier, + checkpoints, + display, + epochs, + event_emit_module, + event_emit_package, + event_senders, + event_struct_instantiation, + event_struct_module, + event_struct_name, + event_struct_package, + events, + events_partition_0, + feature_flags, + objects, + objects_history, + objects_history_partition_0, + objects_snapshot, + objects_version, + objects_version_00, + objects_version_01, + objects_version_02, + objects_version_03, + objects_version_04, + objects_version_05, + objects_version_06, + objects_version_07, + objects_version_08, + objects_version_09, + objects_version_0a, + objects_version_0b, + objects_version_0c, + objects_version_0d, + objects_version_0e, + objects_version_0f, + objects_version_10, + objects_version_11, + objects_version_12, + objects_version_13, + objects_version_14, + objects_version_15, + objects_version_16, + objects_version_17, + objects_version_18, + objects_version_19, + objects_version_1a, + objects_version_1b, + objects_version_1c, + objects_version_1d, + objects_version_1e, + objects_version_1f, + objects_version_20, + objects_version_21, + objects_version_22, + objects_version_23, + objects_version_24, + objects_version_25, + objects_version_26, + objects_version_27, + objects_version_28, + objects_version_29, + objects_version_2a, + objects_version_2b, + objects_version_2c, + objects_version_2d, + objects_version_2e, + objects_version_2f, + objects_version_30, + objects_version_31, + objects_version_32, + objects_version_33, + objects_version_34, + objects_version_35, + objects_version_36, + objects_version_37, + objects_version_38, + objects_version_39, + objects_version_3a, + objects_version_3b, + objects_version_3c, + objects_version_3d, + objects_version_3e, + objects_version_3f, + objects_version_40, + objects_version_41, + objects_version_42, + objects_version_43, + objects_version_44, + objects_version_45, + objects_version_46, + objects_version_47, + objects_version_48, + objects_version_49, + objects_version_4a, + objects_version_4b, + objects_version_4c, + objects_version_4d, + objects_version_4e, + objects_version_4f, + objects_version_50, + objects_version_51, + objects_version_52, + objects_version_53, + objects_version_54, + objects_version_55, + objects_version_56, + objects_version_57, + objects_version_58, + objects_version_59, + objects_version_5a, + objects_version_5b, + objects_version_5c, + objects_version_5d, + objects_version_5e, + objects_version_5f, + objects_version_60, + objects_version_61, + objects_version_62, + objects_version_63, + objects_version_64, + objects_version_65, + objects_version_66, + objects_version_67, + objects_version_68, + objects_version_69, + objects_version_6a, + objects_version_6b, + objects_version_6c, + objects_version_6d, + objects_version_6e, + objects_version_6f, + objects_version_70, + objects_version_71, + objects_version_72, + objects_version_73, + objects_version_74, + objects_version_75, + objects_version_76, + objects_version_77, + objects_version_78, + objects_version_79, + objects_version_7a, + objects_version_7b, + objects_version_7c, + objects_version_7d, + objects_version_7e, + objects_version_7f, + objects_version_80, + objects_version_81, + objects_version_82, + objects_version_83, + objects_version_84, + objects_version_85, + objects_version_86, + objects_version_87, + objects_version_88, + objects_version_89, + objects_version_8a, + objects_version_8b, + objects_version_8c, + objects_version_8d, + objects_version_8e, + objects_version_8f, + objects_version_90, + objects_version_91, + objects_version_92, + objects_version_93, + objects_version_94, + objects_version_95, + objects_version_96, + objects_version_97, + objects_version_98, + objects_version_99, + objects_version_9a, + objects_version_9b, + objects_version_9c, + objects_version_9d, + objects_version_9e, + objects_version_9f, + objects_version_a0, + objects_version_a1, + objects_version_a2, + objects_version_a3, + objects_version_a4, + objects_version_a5, + objects_version_a6, + objects_version_a7, + objects_version_a8, + objects_version_a9, + objects_version_aa, + objects_version_ab, + objects_version_ac, + objects_version_ad, + objects_version_ae, + objects_version_af, + objects_version_b0, + objects_version_b1, + objects_version_b2, + objects_version_b3, + objects_version_b4, + objects_version_b5, + objects_version_b6, + objects_version_b7, + objects_version_b8, + objects_version_b9, + objects_version_ba, + objects_version_bb, + objects_version_bc, + objects_version_bd, + objects_version_be, + objects_version_bf, + objects_version_c0, + objects_version_c1, + objects_version_c2, + objects_version_c3, + objects_version_c4, + objects_version_c5, + objects_version_c6, + objects_version_c7, + objects_version_c8, + objects_version_c9, + objects_version_ca, + objects_version_cb, + objects_version_cc, + objects_version_cd, + objects_version_ce, + objects_version_cf, + objects_version_d0, + objects_version_d1, + objects_version_d2, + objects_version_d3, + objects_version_d4, + objects_version_d5, + objects_version_d6, + objects_version_d7, + objects_version_d8, + objects_version_d9, + objects_version_da, + objects_version_db, + objects_version_dc, + objects_version_dd, + objects_version_de, + objects_version_df, + objects_version_e0, + objects_version_e1, + objects_version_e2, + objects_version_e3, + objects_version_e4, + objects_version_e5, + objects_version_e6, + objects_version_e7, + objects_version_e8, + objects_version_e9, + objects_version_ea, + objects_version_eb, + objects_version_ec, + objects_version_ed, + objects_version_ee, + objects_version_ef, + objects_version_f0, + objects_version_f1, + objects_version_f2, + objects_version_f3, + objects_version_f4, + objects_version_f5, + objects_version_f6, + objects_version_f7, + objects_version_f8, + objects_version_f9, + objects_version_fa, + objects_version_fb, + objects_version_fc, + objects_version_fd, + objects_version_fe, + objects_version_ff, + packages, + protocol_configs, + pruner_cp_watermark, + transactions, + transactions_partition_0, + tx_calls_fun, + tx_calls_mod, + tx_calls_pkg, + tx_changed_objects, + tx_digests, + tx_input_objects, + tx_kinds, + tx_recipients, + tx_senders, +); diff --git a/crates/sui-transactional-test-runner/Cargo.toml b/crates/sui-transactional-test-runner/Cargo.toml index d602aea50457a..fed0ee7ef631c 100644 --- a/crates/sui-transactional-test-runner/Cargo.toml +++ b/crates/sui-transactional-test-runner/Cargo.toml @@ -40,14 +40,14 @@ move-stdlib = { path = "../../external-crates/move/crates/move-stdlib" } move-vm-runtime = { path = "../../external-crates/move/crates/move-vm-runtime" } simulacrum.workspace = true -sui-graphql-rpc.workspace = true +# sui-graphql-rpc.workspace = true sui-rest-api.workspace = true sui-swarm-config.workspace = true sui-config.workspace = true sui-core = { workspace = true, features = ["test-utils"] } sui-framework.workspace = true sui-protocol-config.workspace = true -sui-types = { workspace = true, features = ["test-utils"]} +sui-types = { workspace = true, features = ["test-utils"] } sui-json-rpc-types.workspace = true sui-json-rpc.workspace = true sui-json-rpc-api.workspace = true diff --git a/crates/sui-verifier-transactional-tests/Cargo.toml b/crates/sui-verifier-transactional-tests/Cargo.toml index 12d9944ec1e9a..302b64e93c033 100644 --- a/crates/sui-verifier-transactional-tests/Cargo.toml +++ b/crates/sui-verifier-transactional-tests/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" [dev-dependencies] datatest-stable.workspace = true -sui-transactional-test-runner.workspace = true +# sui-transactional-test-runner.workspace = true [[test]] name = "tests" diff --git a/fullnode.yaml b/fullnode.yaml index 54be2d3192e3a..18f0be5f16fea 100644 --- a/fullnode.yaml +++ b/fullnode.yaml @@ -28,7 +28,7 @@ genesis: genesis-file-location: "/root/sui/genesis.blob" authority-store-pruning-config: - num-latest-epoch-dbs-to-retain: 3 + num-latest-epoch-dbs-to-retain: 0 epoch-db-pruning-period-secs: 3600 num-epochs-to-retain: 0 max-checkpoints-in-batch: 10

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¨BJEC?wjO(w` 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 YeY(pFthzDiIzh{ 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