diff --git a/.cargo/config.toml b/.cargo/config.toml index b2a8f5068..2d88f8d29 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,5 +1,5 @@ [alias] clarinet-install = "install --path components/clarinet-cli --locked --force" -tst = "nextest run --workspace --locked --exclude clarinet-sdk-wasm --exclude clarity-jupyter-kernel" +tst = "nextest run --workspace --no-fail-fast --locked --exclude clarinet-sdk-wasm --exclude clarity-jupyter-kernel" cov = "llvm-cov nextest --workspace --locked --exclude clarinet-sdk-wasm --exclude clarity-jupyter-kernel --lcov --output-path lcov.info" cov-dev = "llvm-cov nextest --workspace --locked --exclude clarinet-sdk-wasm --exclude clarity-jupyter-kernel --html" diff --git a/.github/workflows/ci-sdk.yaml b/.github/workflows/ci-sdk.yaml index a9f3324e3..d61638b49 100644 --- a/.github/workflows/ci-sdk.yaml +++ b/.github/workflows/ci-sdk.yaml @@ -32,7 +32,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' - name: Setup cache keys run: | @@ -50,12 +50,15 @@ jobs: - name: Install wasm-pack run: npm install -g wasm-pack - - name: Run wasm-bindgen-test - run: wasm-pack test --node components/clarinet-sdk-wasm - - name: Build Wasm packages run: npm run build:sdk-wasm + - name: Install npm dependencies + run: npm ci + + - name: Run wasm-bindgen-test + run: wasm-pack test --node components/clarinet-sdk-wasm + - name: Upload Wasm artifacts uses: actions/upload-artifact@v4 with: @@ -76,7 +79,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' - name: Download Wasm artifacts uses: actions/download-artifact@v4 diff --git a/.vscode/settings.json b/.vscode/settings.json index d2bf76f02..dafbcf295 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,10 @@ { "rust-analyzer.check.command": "clippy", - "rust-analyzer.check.workspace": true, + // rust-analyzer hack: + // manually set --workspace in extraArgs so that it's always set only one time + "rust-analyzer.check.workspace": false, "rust-analyzer.check.extraArgs": [ + "--workspace", "--exclude=clarinet-sdk-wasm", "--exclude=clarity-jupyter-kernel" ] diff --git a/Cargo.lock b/Cargo.lock index 9831357e6..38c6ef81b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -159,6 +159,16 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -178,7 +188,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -189,7 +199,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -207,6 +217,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "atty" version = "0.2.14" @@ -465,7 +481,7 @@ dependencies = [ "serde_json", "serde_repr", "serde_urlencoded", - "thiserror", + "thiserror 1.0.63", "tokio", "tokio-util", "tower-service", @@ -660,7 +676,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -701,7 +717,6 @@ dependencies = [ "clarity-repl", "crossbeam-channel", "crossterm", - "dirs", "fwdansi", "hiro-system-kit 0.1.0", "lazy_static", @@ -754,6 +769,7 @@ dependencies = [ "clarinet-utils", "clarity", "clarity-repl", + "dirs", "js-sys", "lazy_static", "libsecp256k1 0.7.1", @@ -872,9 +888,11 @@ dependencies = [ "getrandom 0.2.8", "hiro-system-kit 0.1.0", "httparse", + "js-sys", "lazy_static", "log", "memchr", + "mockito", "pico-args", "pox-locking", "prettytable-rs", @@ -882,12 +900,15 @@ dependencies = [ "reqwest", "rustyline", "serde", + "serde-wasm-bindgen", "serde_derive", "serde_json", "sha2 0.10.8", "test-case", "tokio", "tokio-util", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -1289,7 +1310,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -1419,7 +1440,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -1463,9 +1484,9 @@ dependencies = [ [[package]] name = "dirs" -version = "4.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ "dirs-sys", ] @@ -1482,13 +1503,14 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.3.7" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", - "redox_users", - "winapi 0.3.9", + "option-ext", + "redox_users 0.5.0", + "windows-sys 0.59.0", ] [[package]] @@ -1498,7 +1520,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.3", "winapi 0.3.9", ] @@ -1510,7 +1532,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -1535,7 +1557,7 @@ checksum = "7bdb5411188f7f878a17964798c1264b6b0a9f915bd39b20bf99193c923e1b4e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -1777,7 +1799,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -1970,6 +1992,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "h2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.2.3", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -2232,7 +2273,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.8", "http-body 0.4.5", "httparse", @@ -2255,6 +2296,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2 0.4.7", "http 1.1.0", "http-body 1.0.0", "httparse", @@ -2473,7 +2515,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -2692,6 +2734,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.4.0", + "libc", +] + [[package]] name = "libsecp256k1" version = "0.3.5" @@ -3076,6 +3128,30 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "mockito" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "652cd6d169a36eaf9d1e6bce1a221130439a966d7f27858af66a33a66e9c4ee2" +dependencies = [ + "assert-json-diff", + "bytes", + "colored", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "log", + "rand 0.8.5", + "regex", + "serde_json", + "serde_urlencoded", + "similar", + "tokio", +] + [[package]] name = "multer" version = "2.1.0" @@ -3338,6 +3414,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "overload" version = "0.1.1" @@ -3416,7 +3498,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -3547,9 +3629,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -3562,7 +3644,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", "version_check", "yansi", ] @@ -3579,7 +3661,7 @@ dependencies = [ "memchr", "parking_lot", "protobuf", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -3610,7 +3692,7 @@ dependencies = [ "rustc-hash 2.0.0", "rustls", "socket2 0.5.5", - "thiserror", + "thiserror 1.0.63", "tokio", "tracing", ] @@ -3627,7 +3709,7 @@ dependencies = [ "rustc-hash 2.0.0", "rustls", "slab", - "thiserror", + "thiserror 1.0.63", "tinyvec", "tracing", ] @@ -3808,7 +3890,18 @@ checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom 0.2.8", "redox_syscall 0.2.16", - "thiserror", + "thiserror 1.0.63", +] + +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom 0.2.8", + "libredox", + "thiserror 2.0.11", ] [[package]] @@ -3828,7 +3921,7 @@ checksum = "a930b010d9effee5834317bb7ff406b76af7724348fd572b38705b4bd099fa92" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -4010,7 +4103,7 @@ dependencies = [ "proc-macro2", "quote", "rocket_http", - "syn 2.0.82", + "syn 2.0.96", "unicode-xid", "version_check", ] @@ -4137,9 +4230,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.13" +version = "0.23.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" dependencies = [ "once_cell", "ring", @@ -4161,9 +4254,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" [[package]] name = "rustls-webpki" @@ -4309,7 +4402,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror", + "thiserror 1.0.63", "time", ] @@ -4373,7 +4466,7 @@ checksum = "54be4f245ce16bc58d57ef2716271d0d4519e0f6defa147f6e081005bcb278ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -4729,7 +4822,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" dependencies = [ "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -4803,6 +4896,7 @@ dependencies = [ name = "stacks-devnet-js" version = "2.12.0" dependencies = [ + "bip39", "clarinet-deployments", "clarinet-files", "error-chain", @@ -4971,7 +5065,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -4999,9 +5093,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.82" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -5025,7 +5119,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -5101,7 +5195,7 @@ dependencies = [ "cfg-if 1.0.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -5112,7 +5206,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", "test-case-core", ] @@ -5122,7 +5216,16 @@ version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.63", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", ] [[package]] @@ -5133,7 +5236,18 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] @@ -5272,7 +5386,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -5696,7 +5810,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", "wasm-bindgen-shared", ] @@ -5730,7 +5844,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5764,7 +5878,7 @@ checksum = "c97b2ef2c8d627381e51c071c2ab328eac606d3f69dd82bcbca20a9e389d95f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -5885,7 +5999,7 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", "wasmtime-component-util", "wasmtime-wit-bindgen", "wit-parser", @@ -5915,7 +6029,7 @@ dependencies = [ "log", "object 0.32.1", "target-lexicon", - "thiserror", + "thiserror 1.0.63", "wasmparser 0.116.1", "wasmtime-cranelift-shared", "wasmtime-environ", @@ -5953,7 +6067,7 @@ dependencies = [ "serde", "serde_derive", "target-lexicon", - "thiserror", + "thiserror 1.0.63", "wasmparser 0.116.1", "wasmtime-types", ] @@ -6061,7 +6175,7 @@ dependencies = [ "cranelift-entity", "serde", "serde_derive", - "thiserror", + "thiserror 1.0.63", "wasmparser 0.116.1", ] @@ -6073,7 +6187,7 @@ checksum = "f50f51f8d79bfd2aa8e9d9a0ae7c2d02b45fe412e62ff1b87c0c81b07c738231" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -6516,7 +6630,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", "synstructure", ] @@ -6537,7 +6651,7 @@ checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] @@ -6557,7 +6671,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", "synstructure", ] @@ -6586,7 +6700,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.96", ] [[package]] diff --git a/components/clarinet-cli/Cargo.toml b/components/clarinet-cli/Cargo.toml index d88174d09..f40a13a31 100644 --- a/components/clarinet-cli/Cargo.toml +++ b/components/clarinet-cli/Cargo.toml @@ -26,7 +26,6 @@ serde_derive = "1" tokio = { version = "1.35.1", features = ["full"] } lazy_static = { workspace = true } atty = "0.2.14" -dirs = { version = "4.0.0" } crossterm = "0.27.0" ratatui = { version = "0.27.0", default-features = false, features = ["crossterm"] } segment = { version = "0.2.4", optional = true } diff --git a/components/clarinet-cli/src/bin.rs b/components/clarinet-cli/src/bin.rs index eb118d87b..d31233d65 100644 --- a/components/clarinet-cli/src/bin.rs +++ b/components/clarinet-cli/src/bin.rs @@ -1,8 +1,5 @@ extern crate serde; -#[macro_use] -extern crate serde_derive; - #[macro_use] extern crate serde_json; diff --git a/components/clarinet-cli/src/frontend/cli.rs b/components/clarinet-cli/src/frontend/cli.rs index 3d5b22d6b..7763b383e 100644 --- a/components/clarinet-cli/src/frontend/cli.rs +++ b/components/clarinet-cli/src/frontend/cli.rs @@ -22,6 +22,7 @@ use clarinet_deployments::types::{DeploymentGenerationArtifacts, DeploymentSpeci use clarinet_deployments::{ get_default_deployment_path, load_deployment, setup_session_with_deployment, }; +use clarinet_files::clarinetrc::ClarinetRC; use clarinet_files::StacksNetwork; use clarinet_files::{ get_manifest_location, FileLocation, NetworkManifest, ProjectManifest, ProjectManifestFile, @@ -34,6 +35,7 @@ use clarity_repl::clarity::vm::types::QualifiedContractIdentifier; use clarity_repl::clarity::ClarityVersion; use clarity_repl::frontend::terminal::print_clarity_wasm_warning; use clarity_repl::repl::diagnostic::output_diagnostic; +use clarity_repl::repl::settings::{ApiUrl, RemoteDataSettings}; use clarity_repl::repl::{ClarityCodeSource, ClarityContract, ContractDeployer, DEFAULT_EPOCH}; use clarity_repl::{analysis, repl, Terminal}; use stacks_network::{self, DevnetOrchestrator}; @@ -43,8 +45,6 @@ use std::io::prelude::*; use std::{env, process}; use toml; -use super::clarinetrc::GlobalSettings; - #[cfg(feature = "telemetry")] use super::telemetry::{telemetry_report_event, DeveloperUsageDigest, DeveloperUsageEvent}; /// Clarinet is a command line tool for Clarity smart contract development. @@ -334,6 +334,7 @@ struct Console { /// Path to Clarinet.toml #[clap(long = "manifest-path", short = 'm')] pub manifest_path: Option, + /// If specified, use this deployment file #[clap(long = "deployment-plan-path", short = 'p')] pub deployment_plan_path: Option, @@ -351,6 +352,17 @@ struct Console { conflicts_with = "use_on_disk_deployment_plan" )] pub use_computed_deployment_plan: bool, + + /// Enable remote data fetching from mainnet or a testnet + #[clap(long = "enable-remote-data", short = 'r')] + pub enable_remote_data: bool, + /// Set a custom Stacks Blockchain API URL for remote data fetching + #[clap(long = "remote-data-api-url", short = 'a')] + pub remote_data_api_url: Option, + /// Initial remote Stacks block height + #[clap(long = "remote-data-initial-height", short = 'b')] + pub remote_data_initial_height: Option, + /// Allow the Clarity Wasm preview to run in parallel with the Clarity interpreter (beta) #[clap(long = "enable-clarity-wasm")] pub enable_clarity_wasm: bool, @@ -454,7 +466,7 @@ pub fn main() { } }; - let global_settings = GlobalSettings::from_global_file(); + let clarinetrc = ClarinetRC::from_rc_file(); match opts.command { Command::Completions(cmd) => { @@ -520,7 +532,7 @@ pub fn main() { if project_opts.disable_telemetry { false } else { - match global_settings.enable_telemetry { + match clarinetrc.enable_telemetry { Some(enable) => enable, _ => { println!("{}", yellow!("Send usage data to Hiro.")); @@ -533,7 +545,7 @@ pub fn main() { "{}", blue!(format!( " $ mkdir -p ~/.clarinet; echo \"enable_telemetry = true\" >> {}", - GlobalSettings::get_settings_file_path() + ClarinetRC::get_settings_file_path() )) ); // TODO(lgalabru): once we have a privacy policy available, add a link @@ -578,7 +590,7 @@ pub fn main() { if !execute_changes(changes) { std::process::exit(1); } - if global_settings.enable_hints.unwrap_or(true) { + if clarinetrc.enable_hints.unwrap_or(true) { display_contract_new_hint(Some(project_opts.name.as_str())); } if telemetry_enabled { @@ -879,7 +891,7 @@ pub fn main() { if !execute_changes(changes) { std::process::exit(1); } - if global_settings.enable_hints.unwrap_or(true) { + if clarinetrc.enable_hints.unwrap_or(true) { display_post_check_hint(); } } @@ -910,7 +922,7 @@ pub fn main() { if !execute_changes(changes) { std::process::exit(1); } - if global_settings.enable_hints.unwrap_or(true) { + if clarinetrc.enable_hints.unwrap_or(true) { display_post_check_hint(); } } @@ -935,12 +947,18 @@ pub fn main() { if !execute_changes(vec![Changes::EditTOML(change)]) { std::process::exit(1); } - if global_settings.enable_hints.unwrap_or(true) { + if clarinetrc.enable_hints.unwrap_or(true) { display_post_check_hint(); } } }, Command::Console(cmd) => { + let remote_data_settings = RemoteDataSettings { + enabled: cmd.enable_remote_data, + api_url: cmd.remote_data_api_url.unwrap_or_default(), + initial_height: cmd.remote_data_initial_height, + }; + // Loop to handle `::reload` command loop { let manifest = load_manifest_or_warn(cmd.manifest_path.clone()); @@ -988,7 +1006,8 @@ pub fn main() { } } None => { - let settings = repl::SessionSettings::default(); + let mut settings = repl::SessionSettings::default(); + settings.repl_settings.remote_data = remote_data_settings.clone(); if cmd.enable_clarity_wasm { let mut settings_wasm = repl::SessionSettings::default(); settings_wasm.repl_settings.clarity_wasm_mode = true; @@ -1035,7 +1054,7 @@ pub fn main() { } } - if global_settings.enable_hints.unwrap_or(true) { + if clarinetrc.enable_hints.unwrap_or(true) { display_contract_new_hint(None); } } @@ -1104,7 +1123,8 @@ pub fn main() { } } Command::Check(cmd) => { - let manifest = load_manifest_or_exit(cmd.manifest_path); + let mut manifest = load_manifest_or_exit(cmd.manifest_path); + manifest.repl_settings.remote_data.enabled = false; let (deployment, _, artifacts) = load_deployment_and_artifacts_or_exit( &manifest, &cmd.deployment_plan_path, @@ -1154,7 +1174,7 @@ pub fn main() { false => 1, }; - if global_settings.enable_hints.unwrap_or(true) { + if clarinetrc.enable_hints.unwrap_or(true) { display_post_check_hint(); } if manifest.project.telemetry { @@ -1170,7 +1190,7 @@ pub fn main() { "{}", format_warn!("This command is deprecated. Use 'clarinet devnet start' instead"), ); - devnet_start(cmd, global_settings) + devnet_start(cmd, clarinetrc) } Command::LSP => run_lsp(), Command::DAP => match super::dap::run_dap() { @@ -1188,7 +1208,7 @@ pub fn main() { process::exit(1); } } - Devnet::DevnetStart(cmd) => devnet_start(cmd, global_settings), + Devnet::DevnetStart(cmd) => devnet_start(cmd, clarinetrc), }, }; } @@ -1695,14 +1715,14 @@ fn display_hint_footer() { "{}", yellow!(format!( "These hints can be disabled in the {} file.", - GlobalSettings::get_settings_file_path() + ClarinetRC::get_settings_file_path() )) ); println!( "{}", blue!(format!( " $ mkdir -p ~/.clarinet; echo \"enable_hints = false\" >> {}", - GlobalSettings::get_settings_file_path() + ClarinetRC::get_settings_file_path() )) ); display_separator(); @@ -1786,7 +1806,7 @@ fn display_deploy_hint() { display_hint_footer(); } -fn devnet_start(cmd: DevnetStart, global_settings: GlobalSettings) { +fn devnet_start(cmd: DevnetStart, clarinetrc: ClarinetRC) { let manifest = load_manifest_or_exit(cmd.manifest_path); println!("Computing deployment plan"); let result = match cmd.deployment_plan_path { @@ -1885,7 +1905,7 @@ fn devnet_start(cmd: DevnetStart, global_settings: GlobalSettings) { process::exit(1); } Ok(_) => { - if global_settings.enable_hints.unwrap_or(true) { + if clarinetrc.enable_hints.unwrap_or(true) { display_deploy_hint(); } process::exit(0); diff --git a/components/clarinet-cli/src/frontend/mod.rs b/components/clarinet-cli/src/frontend/mod.rs index 29136d2af..ac917a0ef 100644 --- a/components/clarinet-cli/src/frontend/mod.rs +++ b/components/clarinet-cli/src/frontend/mod.rs @@ -1,5 +1,3 @@ -mod clarinetrc; - pub mod cli; pub mod dap; #[cfg(feature = "telemetry")] diff --git a/components/clarinet-cli/src/lib.rs b/components/clarinet-cli/src/lib.rs index 1a93819f4..12995d25f 100644 --- a/components/clarinet-cli/src/lib.rs +++ b/components/clarinet-cli/src/lib.rs @@ -1,8 +1,5 @@ extern crate serde; -#[macro_use] -extern crate serde_derive; - #[macro_use] extern crate serde_json; diff --git a/components/clarinet-deployments/src/lib.rs b/components/clarinet-deployments/src/lib.rs index 4a1e94cdc..6ccbcacb3 100644 --- a/components/clarinet-deployments/src/lib.rs +++ b/components/clarinet-deployments/src/lib.rs @@ -106,6 +106,9 @@ fn update_session_with_genesis_accounts( deployment: &DeploymentSpecification, ) { if let Some(ref spec) = deployment.genesis { + let addresses: Vec<_> = spec.wallets.iter().map(|w| w.address.clone()).collect(); + session.interpreter.save_genesis_accounts(addresses); + for wallet in spec.wallets.iter() { let _ = session.interpreter.mint_stx_balance( wallet.address.clone().into(), @@ -126,14 +129,16 @@ pub fn update_session_with_deployment_plan( ) -> UpdateSessionExecutionResult { update_session_with_genesis_accounts(session, deployment); - let boot_contracts_data = BOOT_CONTRACTS_DATA.clone(); - let mut boot_contracts = BTreeMap::new(); - for (contract_id, (boot_contract, ast)) in boot_contracts_data { - let result = session - .interpreter - .run(&boot_contract, Some(&ast), false, None); - boot_contracts.insert(contract_id, result); + if !session.settings.repl_settings.remote_data.enabled { + let boot_contracts_data = BOOT_CONTRACTS_DATA.clone(); + + for (contract_id, (boot_contract, ast)) in boot_contracts_data { + let result = session + .interpreter + .run(&boot_contract, Some(&ast), false, None); + boot_contracts.insert(contract_id, result); + } } let mut contracts = BTreeMap::new(); @@ -339,21 +344,28 @@ pub async fn generate_default_deployment( let mut requirements_data = BTreeMap::new(); let mut requirements_deps = BTreeMap::new(); + let mut repl_settings = manifest.repl_settings.clone(); + repl_settings.remote_data.enabled = false; let settings = SessionSettings { - repl_settings: manifest.repl_settings.clone(), + repl_settings, ..Default::default() }; - let session = Session::new(settings.clone()); - let boot_contracts_data = BOOT_CONTRACTS_DATA.clone(); + let simnet_remote_data = + matches!(network, StacksNetwork::Simnet) && manifest.repl_settings.remote_data.enabled; + let mut boot_contracts_ids = BTreeSet::new(); - let mut boot_contracts_asts = BTreeMap::new(); - for (id, (contract, ast)) in boot_contracts_data { - boot_contracts_ids.insert(id.clone()); - boot_contracts_asts.insert(id, (contract.clarity_version, ast)); + + if !simnet_remote_data { + let boot_contracts_data = BOOT_CONTRACTS_DATA.clone(); + let mut boot_contracts_asts = BTreeMap::new(); + for (id, (contract, ast)) in boot_contracts_data { + boot_contracts_ids.insert(id.clone()); + boot_contracts_asts.insert(id, (contract.clarity_version, ast)); + } + requirements_data.append(&mut boot_contracts_asts); } - requirements_data.append(&mut boot_contracts_asts); let mut queue = VecDeque::new(); @@ -422,14 +434,17 @@ pub async fn generate_default_deployment( // Build the struct representing the requirement in the deployment if matches!(network, StacksNetwork::Simnet) { - let data = EmulatedContractPublishSpecification { - contract_name: contract_id.name.clone(), - emulated_sender: contract_id.issuer.clone(), - source: source.clone(), - location: contract_location, - clarity_version, - }; - emulated_contracts_publish.insert(contract_id.clone(), data); + if !simnet_remote_data { + let data = EmulatedContractPublishSpecification { + contract_name: contract_id.name.clone(), + emulated_sender: contract_id.issuer.clone(), + source: source.clone(), + location: contract_location, + clarity_version, + }; + + emulated_contracts_publish.insert(contract_id.clone(), data); + } } else if matches!(network, StacksNetwork::Devnet | StacksNetwork::Testnet) { let mut remap_principals = BTreeMap::new(); remap_principals @@ -533,7 +548,7 @@ pub async fn generate_default_deployment( } // Avoid listing requirements as deployment transactions to the deployment specification on Mainnet - if !matches!(network, StacksNetwork::Mainnet) { + if !matches!(network, StacksNetwork::Mainnet) && !simnet_remote_data { let mut ordered_contracts_ids = match ASTDependencyDetector::order_contracts( &requirements_deps, &contract_epochs, diff --git a/components/clarinet-deployments/src/requirements.rs b/components/clarinet-deployments/src/requirements.rs index f696bc414..708ca630f 100644 --- a/components/clarinet-deployments/src/requirements.rs +++ b/components/clarinet-deployments/src/requirements.rs @@ -4,7 +4,7 @@ use clarity_repl::{ chainstate::StacksAddress, vm::types::QualifiedContractIdentifier, Address, ClarityVersion, StacksEpochId, }, - repl::{DEFAULT_CLARITY_VERSION, DEFAULT_EPOCH}, + repl::{remote_data::epoch_for_height, DEFAULT_CLARITY_VERSION, DEFAULT_EPOCH}, }; use reqwest; @@ -121,79 +121,6 @@ pub async fn retrieve_contract( Ok((contract.source, epoch, clarity_version, contract_location)) } -pub const MAINNET_20_START_HEIGHT: u32 = 1; -pub const MAINNET_2_05_START_HEIGHT: u32 = 40_607; -pub const MAINNET_21_START_HEIGHT: u32 = 99_113; -pub const MAINNET_22_START_HEIGHT: u32 = 103_900; -pub const MAINNET_23_START_HEIGHT: u32 = 104_359; -pub const MAINNET_24_START_HEIGHT: u32 = 107_055; -pub const MAINNET_25_START_HEIGHT: u32 = 147_290; -pub const MAINNET_30_START_HEIGHT: u32 = 171_833; -pub const MAINNET_31_START_HEIGHT: u32 = 340_555; - -// the current primary testnet starts directly in epoch 2.5 (pox-4 deployment) -pub const TESTNET_20_START_HEIGHT: u32 = 1; -pub const TESTNET_2_05_START_HEIGHT: u32 = 1; -pub const TESTNET_21_START_HEIGHT: u32 = 1; -pub const TESTNET_22_START_HEIGHT: u32 = 1; -pub const TESTNET_23_START_HEIGHT: u32 = 1; -pub const TESTNET_24_START_HEIGHT: u32 = 1; -pub const TESTNET_25_START_HEIGHT: u32 = 1; -pub const TESTNET_30_START_HEIGHT: u32 = 320; -pub const TESTNET_31_START_HEIGHT: u32 = 814; - -fn epoch_for_height(is_mainnet: bool, height: u32) -> StacksEpochId { - if is_mainnet { - epoch_for_mainnet_height(height) - } else { - epoch_for_testnet_height(height) - } -} - -fn epoch_for_mainnet_height(height: u32) -> StacksEpochId { - if height < MAINNET_2_05_START_HEIGHT { - StacksEpochId::Epoch20 - } else if height < MAINNET_21_START_HEIGHT { - StacksEpochId::Epoch2_05 - } else if height < MAINNET_22_START_HEIGHT { - StacksEpochId::Epoch21 - } else if height < MAINNET_23_START_HEIGHT { - StacksEpochId::Epoch22 - } else if height < MAINNET_24_START_HEIGHT { - StacksEpochId::Epoch23 - } else if height < MAINNET_25_START_HEIGHT { - StacksEpochId::Epoch24 - } else if height < MAINNET_30_START_HEIGHT { - StacksEpochId::Epoch25 - } else if height < MAINNET_31_START_HEIGHT { - StacksEpochId::Epoch30 - } else { - StacksEpochId::Epoch31 - } -} - -fn epoch_for_testnet_height(height: u32) -> StacksEpochId { - if height < TESTNET_2_05_START_HEIGHT { - StacksEpochId::Epoch20 - } else if height < TESTNET_21_START_HEIGHT { - StacksEpochId::Epoch2_05 - } else if height < TESTNET_22_START_HEIGHT { - StacksEpochId::Epoch21 - } else if height < TESTNET_23_START_HEIGHT { - StacksEpochId::Epoch22 - } else if height < TESTNET_24_START_HEIGHT { - StacksEpochId::Epoch23 - } else if height < TESTNET_25_START_HEIGHT { - StacksEpochId::Epoch24 - } else if height < TESTNET_30_START_HEIGHT { - StacksEpochId::Epoch25 - } else if height < TESTNET_31_START_HEIGHT { - StacksEpochId::Epoch30 - } else { - StacksEpochId::Epoch31 - } -} - #[allow(dead_code)] #[derive(Deserialize, Debug, Default, Clone)] struct Contract { diff --git a/components/clarinet-files/Cargo.toml b/components/clarinet-files/Cargo.toml index c38e3aba4..1c1ce6d4b 100644 --- a/components/clarinet-files/Cargo.toml +++ b/components/clarinet-files/Cargo.toml @@ -15,6 +15,7 @@ url = { version = "2.2.2", features = ["serde"] } tiny-hderive = "0.3.0" bitcoin = { version = "0.31.2", optional = true } lazy_static = { workspace = true} +dirs = "6.0" clarity = { workspace = true } diff --git a/components/clarinet-cli/src/frontend/clarinetrc.rs b/components/clarinet-files/src/clarinetrc.rs similarity index 54% rename from components/clarinet-cli/src/frontend/clarinetrc.rs rename to components/clarinet-files/src/clarinetrc.rs index 03e499dde..e9f8e3a50 100644 --- a/components/clarinet-cli/src/frontend/clarinetrc.rs +++ b/components/clarinet-files/src/clarinetrc.rs @@ -1,40 +1,30 @@ -use std::fs::{self}; - use std::env; -#[derive(Serialize, Deserialize, Default)] -pub struct GlobalSettings { +#[derive(Serialize, Deserialize, Default, Debug)] +pub struct ClarinetRC { pub enable_hints: Option, pub enable_telemetry: Option, } -impl GlobalSettings { +impl ClarinetRC { pub fn get_settings_file_path() -> &'static str { "~/.clarinet/clarinetrc.toml" } - pub fn from_global_file() -> Self { + pub fn from_rc_file() -> Self { let home_dir = dirs::home_dir(); if let Some(path) = home_dir.map(|home_dir| home_dir.join(".clarinet/clarinetrc.toml")) { if path.exists() { - match fs::read_to_string(path) { - Ok(content) => match toml::from_str::(&content) { + match std::fs::read_to_string(path) { + Ok(content) => match toml::from_str::(&content) { Ok(res) => return res, Err(_) => { - println!( - "{} {}", - format_warn!("unable to parse"), - Self::get_settings_file_path() - ); + println!("unable to parse {}", Self::get_settings_file_path()); } }, Err(_) => { - println!( - "{} {}", - format_warn!("unable to read file"), - Self::get_settings_file_path() - ); + println!("unable to read file {}", Self::get_settings_file_path()); } } } diff --git a/components/clarinet-files/src/lib.rs b/components/clarinet-files/src/lib.rs index d6e396cb3..db565d13f 100644 --- a/components/clarinet-files/src/lib.rs +++ b/components/clarinet-files/src/lib.rs @@ -3,8 +3,7 @@ extern crate serde; #[macro_use] extern crate serde_derive; -pub extern crate bip39; -pub extern crate url; +pub mod clarinetrc; mod network_manifest; mod project_manifest; diff --git a/components/clarinet-files/src/project_manifest.rs b/components/clarinet-files/src/project_manifest.rs index 895c629f3..4968b0d2d 100644 --- a/components/clarinet-files/src/project_manifest.rs +++ b/components/clarinet-files/src/project_manifest.rs @@ -26,13 +26,6 @@ pub struct ClarityContractMetadata { pub epoch: StacksEpochId, } -#[derive(Serialize, Deserialize, Debug)] -pub struct ProjectManifestFile { - project: ProjectConfigFile, - contracts: Option, - repl: Option, -} - #[derive(Serialize, Deserialize, Debug)] pub struct ProjectConfigFile { name: String, @@ -48,6 +41,13 @@ pub struct ProjectConfigFile { cache_dir: Option, } +#[derive(Serialize, Deserialize, Debug)] +pub struct ProjectManifestFile { + project: ProjectConfigFile, + contracts: Option, + repl: Option, +} + #[derive(Deserialize, Serialize, Debug, Clone)] pub struct ProjectManifest { pub project: ProjectConfig, diff --git a/components/clarinet-sdk-wasm/build.mjs b/components/clarinet-sdk-wasm/build.mjs index e06992ffb..f07d04c5a 100644 --- a/components/clarinet-sdk-wasm/build.mjs +++ b/components/clarinet-sdk-wasm/build.mjs @@ -8,9 +8,17 @@ import path from "node:path"; const rootDir = new URL(".", import.meta.url).pathname; /** - * build + * build sdk js script */ -async function build() { +async function build_wasm_js_scripts() { + const dir = path.join(rootDir, "../clarity-repl/js"); + await execCommand("npm", ["install"], dir); +} + +/** + * build clarinet-sdk-wasm + */ +async function build_wasm_sdk() { console.log("Deleting pkg-node"); await rmIfExists(path.join(rootDir, "pkg-node")); console.log("Deleting pkg-browser"); @@ -40,6 +48,8 @@ async function build() { ]); await updatePackageName(); + await updatePackageJson("pkg-node/package.json"); + await updatePackageJson("pkg-browser/package.json"); } /** @@ -48,11 +58,10 @@ async function build() { * @param {string[]} args * @returns */ -export const execCommand = async (command, args) => { - console.log(`Building ${args[5]}`); +export const execCommand = async (command, args, cwd = rootDir) => { return new Promise((resolve, reject) => { const childProcess = spawn(command, args, { - cwd: rootDir, + cwd, }); childProcess.stdout.on("data", (data) => { process.stdout.write(data.toString()); @@ -99,11 +108,28 @@ async function updatePackageName() { '"name": "@hirosystems/clarinet-sdk-wasm-browser"', ); await fs.writeFile(filePath, updatedData, "utf-8"); - console.log("āœ… Package name updated successfully."); + console.log("āœ… pkg-browser/package.json name updated"); +} + +/** + * updatePackagesIncludedFiles + * Include snippets/ files and add the sync-request dependency + * @param {string} path + */ +async function updatePackageJson(file) { + const filePath = path.join(rootDir, file); + + const fileData = JSON.parse(await fs.readFile(filePath, "utf-8")); + fileData.files.push("snippets/"); + + fileData.dependencies = { "sync-request": "6.1.0" }; + await fs.writeFile(filePath, JSON.stringify(fileData, null, 2), "utf-8"); + console.log(`āœ… ${file} updated`); } try { - await build(); + await build_wasm_js_scripts(); + await build_wasm_sdk(); console.log("\nāœ… Project successfully built.\nšŸš€ Ready to publish."); console.log("Run the following commands to publish"); console.log("\n```"); diff --git a/components/clarinet-sdk-wasm/src/core.rs b/components/clarinet-sdk-wasm/src/core.rs index 66868e91f..d065dfc4a 100644 --- a/components/clarinet-sdk-wasm/src/core.rs +++ b/components/clarinet-sdk-wasm/src/core.rs @@ -21,6 +21,7 @@ use clarity_repl::clarity::{ }; use clarity_repl::repl::clarity_values::{uint8_to_string, uint8_to_value}; use clarity_repl::repl::session::{CostsReport, BOOT_CONTRACTS_DATA}; +use clarity_repl::repl::settings::RemoteDataSettings; use clarity_repl::repl::{ clarity_values, ClarityCodeSource, ClarityContract, ContractDeployer, Session, SessionSettings, DEFAULT_CLARITY_VERSION, DEFAULT_EPOCH, @@ -96,7 +97,7 @@ impl CallFnArgs { Self { contract, method, - args: args.iter().map(|a| a.to_vec()).collect(), + args: args.into_iter().map(|a| a.to_vec()).collect(), sender, } } @@ -106,27 +107,24 @@ impl CallFnArgs { Because it's JSON, the Uint8Array arguments are passed as Map instead of Vec. This method transform the Map back into a Vec. */ - fn from_json_args( - CallContractArgsJSON { - contract, - method, - args_maps, - sender, - }: CallContractArgsJSON, - ) -> Self { - let mut args: Vec> = vec![]; - for arg in args_maps { - let mut parsed_arg: Vec = vec![0; arg.len()]; - for (i, v) in arg.iter() { - parsed_arg[*i] = *v; - } - args.push(parsed_arg); - } + fn from_json_args(json_args: CallContractArgsJSON) -> Self { + let args = json_args + .args_maps + .into_iter() + .map(|arg| { + let mut parsed_arg = vec![0; arg.len()]; + for (i, v) in arg { + parsed_arg[i] = v; + } + parsed_arg + }) + .collect(); + Self { - contract, - method, + contract: json_args.contract, + method: json_args.method, args, - sender, + sender: json_args.sender, } } } @@ -166,7 +164,6 @@ pub struct DeployContractArgs { options: ContractOptions, sender: String, } - #[wasm_bindgen] impl DeployContractArgs { #[wasm_bindgen(constructor)] @@ -300,7 +297,7 @@ impl SDK { pub fn new(fs_request: JsFunction, options: Option) -> Self { panic::set_hook(Box::new(console_error_panic_hook::hook)); - let fs = Box::new(WASMFileSystemAccessor::new(fs_request)); + let file_accessor = Box::new(WASMFileSystemAccessor::new(fs_request)); let track_coverage = options.as_ref().map_or(false, |o| o.track_coverage); let track_costs = options.as_ref().map_or(false, |o| o.track_costs); @@ -312,7 +309,7 @@ impl SDK { contracts_interfaces: HashMap::new(), contracts_locations: HashMap::new(), session: None, - file_accessor: fs, + file_accessor, options: SDKOptions { track_coverage, track_costs, @@ -342,16 +339,27 @@ impl SDK { #[wasm_bindgen(js_name=getDefaultClarityVersionForCurrentEpoch)] pub fn default_clarity_version_for_current_epoch(&self) -> ClarityVersionString { let session = self.get_session(); + let current_epoch = session.interpreter.datastore.get_current_epoch(); ClarityVersionString { - obj: ClarityVersion::default_for_epoch(session.current_epoch) + obj: ClarityVersion::default_for_epoch(current_epoch) .to_string() .into(), } } #[wasm_bindgen(js_name=initEmptySession)] - pub async fn init_empty_session(&mut self) -> Result<(), String> { - let session = Session::new(SessionSettings::default()); + pub async fn init_empty_session( + &mut self, + remote_data_settings: JsValue, + ) -> Result<(), String> { + let config: Option = + serde_wasm_bindgen::from_value(remote_data_settings) + .map_err(|e| format!("Failed to parse remote data settings: {}", e))?; + + let mut settings = SessionSettings::default(); + settings.repl_settings.remote_data = config.unwrap_or_default(); + let session = Session::new(settings); + self.session = Some(session); Ok(()) } @@ -600,11 +608,16 @@ impl SDK { #[wasm_bindgen(getter, js_name=currentEpoch)] pub fn current_epoch(&mut self) -> String { let session = self.get_session_mut(); - session.current_epoch.to_string() + session + .interpreter + .datastore + .get_current_epoch() + .to_string() } #[wasm_bindgen(js_name=setEpoch)] pub fn set_epoch(&mut self, epoch: EpochString) { + // @todo: unwrap to default epoch?? DEFAULT_EPOCH.to_string() let epoch = epoch.as_string().unwrap_or("2.4".into()); let epoch = match epoch.as_str() { "2.0" => StacksEpochId::Epoch20, @@ -676,6 +689,7 @@ impl SDK { .get_data_var(&contract_id, var_name) .ok_or("value not found".into()) } + #[wasm_bindgen(js_name=getBlockTime)] pub fn get_block_time(&mut self) -> u64 { self.get_session_mut().interpreter.get_block_time() @@ -780,9 +794,10 @@ impl SDK { #[wasm_bindgen(js_name=callReadOnlyFn)] pub fn call_read_only_fn(&mut self, args: &CallFnArgs) -> Result { - let interface = self.get_function_interface(&args.contract, &args.method)?; - if interface.access != ContractInterfaceFunctionAccess::read_only { - return Err(format!("{} is not a read-only function", &args.method)); + if let Ok(interface) = self.get_function_interface(&args.contract, &args.method) { + if interface.access != ContractInterfaceFunctionAccess::read_only { + return Err(format!("{} is not a read-only function", &args.method)); + } } self.call_contract_fn(args, false) } @@ -792,9 +807,10 @@ impl SDK { args: &CallFnArgs, advance_chain_tip: bool, ) -> Result { - let interface = self.get_function_interface(&args.contract, &args.method)?; - if interface.access != ContractInterfaceFunctionAccess::public { - return Err(format!("{} is not a public function", &args.method)); + if let Ok(interface) = self.get_function_interface(&args.contract, &args.method) { + if interface.access != ContractInterfaceFunctionAccess::public { + return Err(format!("{} is not a public function", &args.method)); + } } if advance_chain_tip { @@ -809,9 +825,10 @@ impl SDK { args: &CallFnArgs, advance_chain_tip: bool, ) -> Result { - let interface = self.get_function_interface(&args.contract, &args.method)?; - if interface.access != ContractInterfaceFunctionAccess::private { - return Err(format!("{} is not a private function", &args.method)); + if let Ok(interface) = self.get_function_interface(&args.contract, &args.method) { + if interface.access != ContractInterfaceFunctionAccess::private { + return Err(format!("{} is not a private function", &args.method)); + } } if advance_chain_tip { let session = self.get_session_mut(); @@ -857,13 +874,14 @@ impl SDK { if advance_chain_tip { session.advance_chain_tip(1); } + let current_epoch = session.interpreter.datastore.get_current_epoch(); let contract = ClarityContract { code_source: ClarityCodeSource::ContractInMemory(args.content.clone()), name: args.name.clone(), deployer: ContractDeployer::Address(args.sender.to_string()), clarity_version: args.options.clarity_version, - epoch: session.current_epoch, + epoch: current_epoch, }; match session.deploy_contract(&contract, false, None) { @@ -1025,6 +1043,19 @@ impl SDK { session.handle_command(&snippet) } + #[wasm_bindgen(js_name=setLocalAccounts)] + pub fn set_local_accounts(&mut self, addresses: Vec) { + let principals = addresses + .into_iter() + .map(|a| StandardPrincipalData::from(StacksAddress::from_string(&a).unwrap())) + .collect(); + let session = self.get_session_mut(); + session + .interpreter + .clarity_datastore + .save_local_account(principals); + } + #[wasm_bindgen(js_name=mintSTX)] pub fn mint_stx(&mut self, recipient: String, amount: u64) -> Result { let session = self.get_session_mut(); diff --git a/components/clarinet-sdk-wasm/src/test_wasm.rs b/components/clarinet-sdk-wasm/src/test_wasm.rs index afa5ba379..637223185 100644 --- a/components/clarinet-sdk-wasm/src/test_wasm.rs +++ b/components/clarinet-sdk-wasm/src/test_wasm.rs @@ -1,13 +1,18 @@ use super::core::DeployContractArgs; + use crate::core::{CallFnArgs, ContractOptions, EpochString, TransactionRes, SDK}; + use clarity::vm::Value as ClarityValue; +use clarity_repl::repl::settings::{ApiUrl, RemoteDataSettings}; +use gloo_utils::format::JsValueSerdeExt; use js_sys::Function as JsFunction; +use wasm_bindgen::JsValue; use wasm_bindgen_test::*; async fn init_sdk() -> SDK { let js_noop = JsFunction::new_no_args("return"); let mut sdk = SDK::new(js_noop, None); - let _ = sdk.init_empty_session().await; + let _ = sdk.init_empty_session(JsValue::undefined()).await; sdk.set_epoch(EpochString::new("3.0")); sdk } @@ -24,7 +29,7 @@ fn deploy_basic_contract(sdk: &mut SDK) -> TransactionRes { } #[wasm_bindgen_test] -async fn it_cn_execute_clarity_code() { +async fn it_can_execute_clarity_code() { let mut sdk = init_sdk().await; let tx = sdk.execute("(+ u41 u1)".into()).unwrap(); let expected = format!("0x{}", ClarityValue::UInt(42).serialize_to_hex().unwrap()); @@ -61,3 +66,29 @@ async fn it_can_call_a_private_function() { let expected = format!("0x{}", ClarityValue::UInt(2).serialize_to_hex().unwrap()); assert_eq!(tx.result, expected); } + +#[wasm_bindgen_test] +async fn it_can_call_remote_data() { + let js_noop = JsFunction::new_no_args("return"); + let mut sdk = SDK::new(js_noop, None); + let options = RemoteDataSettings { + enabled: true, + api_url: ApiUrl("https://api.testnet.hiro.so".to_string()), + initial_height: Some(42000), + }; + let _ = sdk + .init_empty_session(JsValue::from_serde(&options).unwrap()) + .await; + + assert_eq!(sdk.current_epoch(), "3.1"); + + let tx = sdk.call_public_fn(&CallFnArgs::new( + "STJCAB2T9TR2EJM7YS4DM2CGBBVTF7BV237Y8KNV.counter".into(), + "get-count".into(), + vec![], + "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM".into(), + )); + + let expected = format!("0x{}", ClarityValue::UInt(0).serialize_to_hex().unwrap()); + assert_eq!(tx.unwrap().result, expected); +} diff --git a/components/clarinet-sdk/browser/package.json b/components/clarinet-sdk/browser/package.json index 61b1c070a..9b34feca8 100644 --- a/components/clarinet-sdk/browser/package.json +++ b/components/clarinet-sdk/browser/package.json @@ -28,7 +28,7 @@ "license": "GPL-3.0", "readme": "./README.md", "dependencies": { - "@hirosystems/clarinet-sdk-wasm-browser": "^2.12.0", + "@hirosystems/clarinet-sdk-wasm-browser": "2.12.0", "@stacks/transactions": "^6.13.0" } } diff --git a/components/clarinet-sdk/browser/src/index.ts b/components/clarinet-sdk/browser/src/index.ts index 1c53a9e4c..bafce41c8 100644 --- a/components/clarinet-sdk/browser/src/index.ts +++ b/components/clarinet-sdk/browser/src/index.ts @@ -23,7 +23,6 @@ export { defaultVfs, defaultFileStore } from "./defaultVfs.js"; export const initSimnet = async (virtualFileSystem?: Function) => { await init(); - const vfs = virtualFileSystem ? virtualFileSystem : defaultVfs; return new Proxy(new SDK(vfs), getSessionProxy()) as unknown as Simnet; }; diff --git a/components/clarinet-sdk/node/package.json b/components/clarinet-sdk/node/package.json index 1e6233afd..14bc2d3f0 100644 --- a/components/clarinet-sdk/node/package.json +++ b/components/clarinet-sdk/node/package.json @@ -61,7 +61,7 @@ "license": "GPL-3.0", "readme": "./README.md", "dependencies": { - "@hirosystems/clarinet-sdk-wasm": "^2.12.0", + "@hirosystems/clarinet-sdk-wasm": "2.12.0", "@stacks/transactions": "^6.13.0", "kolorist": "^1.8.0", "prompts": "^2.4.2", diff --git a/components/clarinet-sdk/node/src/index.ts b/components/clarinet-sdk/node/src/index.ts index 26fa0ff2b..3cb4a8ea5 100644 --- a/components/clarinet-sdk/node/src/index.ts +++ b/components/clarinet-sdk/node/src/index.ts @@ -22,6 +22,16 @@ BigInt.prototype.toJSON = function () { return this.toString(); }; +type Options = { trackCosts: boolean; trackCoverage: boolean }; + +export async function getSDK(options?: Options): Promise { + const module = await wasmModule; + let sdkOptions = new SDKOptions(!!options?.trackCosts, !!options?.trackCoverage); + + const simnet = new Proxy(new module.SDK(vfs, sdkOptions), getSessionProxy()) as unknown as Simnet; + return simnet; +} + // load wasm only once and memoize it function memoizedInit() { let simnet: Simnet | null = null; @@ -32,9 +42,7 @@ function memoizedInit() { options?: { trackCosts: boolean; trackCoverage: boolean }, ) => { if (noCache || !simnet) { - const module = await wasmModule; - let sdkOptions = new SDKOptions(!!options?.trackCosts, !!options?.trackCoverage); - simnet = new Proxy(new module.SDK(vfs, sdkOptions), getSessionProxy()) as unknown as Simnet; + simnet = await getSDK(options); } // start a new simnet session diff --git a/components/clarinet-sdk/node/tests/remote-data.test.ts b/components/clarinet-sdk/node/tests/remote-data.test.ts new file mode 100644 index 000000000..d39682e55 --- /dev/null +++ b/components/clarinet-sdk/node/tests/remote-data.test.ts @@ -0,0 +1,77 @@ +import fs from "node:fs"; +import path from "node:path"; +import { Cl } from "@stacks/transactions"; +import { describe, expect, it, beforeEach, afterEach } from "vitest"; + +// test the built package and not the source code +// makes it simpler to handle wasm build +import { getSDK } from ".."; + +const api_url = "https://api.testnet.hiro.so"; +const counterAddress = "STJCAB2T9TR2EJM7YS4DM2CGBBVTF7BV237Y8KNV.counter"; +const sender = "ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG"; + +const deploymentPlanPath = path.join( + process.cwd(), + "tests/fixtures/deployments/default.simnet-plan.yaml", +); + +function deleteExistingDeploymentPlan() { + if (fs.existsSync(deploymentPlanPath)) { + fs.unlinkSync(deploymentPlanPath); + } +} + +beforeEach(async () => { + deleteExistingDeploymentPlan(); +}); + +afterEach(() => { + deleteExistingDeploymentPlan(); +}); + +describe("simnet remote interactions", async () => { + const simnet = await getSDK(); + + it("can call a remote contract", async () => { + await simnet.initEmptySession({ + enabled: true, + api_url: "https://api.testnet.hiro.so", + initial_height: 56230, + }); + const { result } = simnet.callReadOnlyFn(counterAddress, "get-count", [], sender); + expect(result).toStrictEqual(Cl.uint(0)); + }); + + it("can call a remote contract", async () => { + await simnet.initEmptySession({ + enabled: true, + api_url: "https://api.testnet.hiro.so", + initial_height: 57000, + }); + const { result } = simnet.callReadOnlyFn(counterAddress, "get-count", [], sender); + expect(result).toStrictEqual(Cl.uint(1)); + }); + + it("can use at-block", async () => { + await simnet.initEmptySession({ + enabled: true, + api_url: "https://api.testnet.hiro.so", + initial_height: 57000, + }); + const { result: resultAt56230 } = simnet.callReadOnlyFn( + counterAddress, + "get-count-at-block", + [Cl.uint(56230)], + sender, + ); + expect(resultAt56230).toStrictEqual(Cl.ok(Cl.uint(0))); + const { result: resultAt56300 } = simnet.callReadOnlyFn( + counterAddress, + "get-count-at-block", + [Cl.uint(56300)], + sender, + ); + expect(resultAt56300).toStrictEqual(Cl.ok(Cl.uint(1))); + }); +}); diff --git a/components/clarinet-sdk/node/tests/simnet-usage.test.ts b/components/clarinet-sdk/node/tests/simnet-usage.test.ts index 63db3368e..60eff9dac 100644 --- a/components/clarinet-sdk/node/tests/simnet-usage.test.ts +++ b/components/clarinet-sdk/node/tests/simnet-usage.test.ts @@ -50,6 +50,7 @@ describe("basic simnet interactions", () => { simnet.mineEmptyBlocks(4); expect(simnet.blockHeight).toBe(blockHeight + 5); }); + it("can not mine empty stacks block in pre-3.0", () => { expect(() => simnet.mineEmptyStacksBlock()).toThrowError( "use mineEmptyBurnBlock in epoch lower than 3.0", @@ -237,13 +238,13 @@ describe("simnet can call contracts function", () => { it("can not call a public function with callPrivateFn", () => { expect(() => { simnet.callPrivateFn("counter", "increment", [], address1); - }).toThrow("increment is not a private function"); + }).toThrow(/^increment is not a private function$/); }); it("can not call a private function with callPublicFn", () => { expect(() => { simnet.callPublicFn("counter", "inner-increment", [], address1); - }).toThrow("increment is not a public function"); + }).toThrow(/^inner-increment is not a public function$/); }); it("can get updated assets map", () => { diff --git a/components/clarinet-sdk/node/vitest.config.mjs b/components/clarinet-sdk/node/vitest.config.mjs index 245c7fc29..64b44d287 100644 --- a/components/clarinet-sdk/node/vitest.config.mjs +++ b/components/clarinet-sdk/node/vitest.config.mjs @@ -3,10 +3,8 @@ import { defineConfig } from "vite"; export default defineConfig({ test: { - // https://vitest.dev/guide/common-errors.html#failed-to-terminate-worker pool: "forks", poolOptions: { - threads: { singleThread: true }, forks: { singleFork: true }, }, include: ["./tests/**/*.test.ts", "./vitest-helpers/tests/**/*.test.ts"], diff --git a/components/clarity-jupyter-kernel/Cargo.toml b/components/clarity-jupyter-kernel/Cargo.toml index be106709f..36bf81dce 100644 --- a/components/clarity-jupyter-kernel/Cargo.toml +++ b/components/clarity-jupyter-kernel/Cargo.toml @@ -28,5 +28,5 @@ uuid = { version = "1.0.0", features = ["v4"] } hmac = { version = "0.7.1" } hex = { version = "0.3.2" } colored = { version = "1.8.0" } -dirs = { version = "4.0.0" } +dirs = { version = "6.0.0" } chrono = { version = "0.4.31" } diff --git a/components/clarity-repl/Cargo.toml b/components/clarity-repl/Cargo.toml index a60519f1c..1310b3088 100644 --- a/components/clarity-repl/Cargo.toml +++ b/components/clarity-repl/Cargo.toml @@ -38,8 +38,14 @@ clar2wasm = { git = "https://github.com/stacks-network/clarity-wasm.git", branch pox-locking = { git = "https://github.com/stacks-network/stacks-core.git", branch="feat/clarity-wasm-develop", optional = true, default-features = false } prettytable-rs = { version = "0.10.0" } +# wasm +wasm-bindgen = { workspace = true, optional = true } +serde-wasm-bindgen = { version = "0.6.4", optional = true } +web-sys = { workspace = true, optional = true, features = ["XmlHttpRequest"]} +js-sys = { version = "0.3", optional = true } + # DAP Debugger -tokio = { version = "1.35.1", features = ["full"], optional = true } +tokio = { version = "1.35.1", features = ["full"], optional = true} tokio-util = { version = "0.7.10", features = ["codec"], optional = true } futures = { version = "0.3.12", optional = true } debug_types = { version = "1.0.0", optional = true } @@ -52,11 +58,12 @@ memchr = { version = "2.4.1", optional = true } pico-args = { version = "0.5.0", optional = true } rustyline = { version = "14.0.0", optional = true } hiro_system_kit = { version = "0.1.0", package = "hiro-system-kit", path = "../hiro-system-kit", default-features = false } -reqwest = { workspace = true } +reqwest = { workspace = true, features = ["blocking"] } [dev-dependencies] test-case = "*" divan = "0.1" +mockito = "1.6" [lib] name = "clarity_repl" @@ -78,7 +85,7 @@ sdk = [ "clarity/devtools", "clarity/log", "hiro_system_kit/tokio_helpers", - "pox-locking/default" + "pox-locking/default", ] cli = [ "sdk", @@ -100,5 +107,9 @@ wasm = [ "clarity/wasm", "clarity/developer-mode", "clarity/devtools", - "pox-locking/wasm" + "pox-locking/wasm", + "wasm-bindgen", + "serde-wasm-bindgen", + "web-sys", + "js-sys", ] diff --git a/components/clarity-repl/js/index.mjs b/components/clarity-repl/js/index.mjs new file mode 100644 index 000000000..ca0e18860 --- /dev/null +++ b/components/clarity-repl/js/index.mjs @@ -0,0 +1,39 @@ +// @ts-check + +import request from "sync-request"; + +const encoder = new TextEncoder(); + +export function getHiroApiKey() { + const isNode = typeof process !== "undefined" && process.env != null; + if (!isNode) return undefined; + return process.env.HIRO_API_KEY; +} + +/** + * httpClient + * @param {import("sync-request").HttpVerb} method + * @param {string} path + * @returns {{ status: number, body: Uint8Array }} + */ +export function httpClient(method, path) { + const options = { + headers: { + "x-hiro-product": "clarinet-sdk", + }, + }; + const apiKey = getHiroApiKey(); + if (apiKey) { + options.headers["x-api-key"] = apiKey; + } + + const response = request(method, path, options); + if (typeof response.body === "string") { + return { status: response.statusCode, body: encoder.encode(response.body) }; + } + + return { + status: response.statusCode, + body: new Uint8Array(response.body), + }; +} diff --git a/components/clarity-repl/js/package.json b/components/clarity-repl/js/package.json new file mode 100644 index 000000000..edfa7e996 --- /dev/null +++ b/components/clarity-repl/js/package.json @@ -0,0 +1,12 @@ +{ + "name": "@hirosystems/clarity-repl-js-lib", + "version": "1.0.0", + "private": true, + "type": "module", + "author": "hirosystems", + "license": "GPL-3.0", + "readme": "./README.md", + "dependencies": { + "sync-request": "6.1.0" + } +} diff --git a/components/clarity-repl/src/bin.rs b/components/clarity-repl/src/bin.rs index d126b0555..b834e452f 100644 --- a/components/clarity-repl/src/bin.rs +++ b/components/clarity-repl/src/bin.rs @@ -20,6 +20,9 @@ extern crate prettytable; #[macro_use] extern crate hiro_system_kit; +#[macro_use] +mod uprint; + pub mod analysis; pub mod frontend; pub mod repl; diff --git a/components/clarity-repl/src/lib.rs b/components/clarity-repl/src/lib.rs index ce7646a46..7f341f240 100644 --- a/components/clarity-repl/src/lib.rs +++ b/components/clarity-repl/src/lib.rs @@ -11,6 +11,9 @@ extern crate serde_derive; #[macro_use] extern crate hiro_system_kit; +#[macro_use] +mod uprint; + pub mod analysis; pub mod clarity { diff --git a/components/clarity-repl/src/repl/datastore.rs b/components/clarity-repl/src/repl/datastore.rs index 8c56cf6a9..de78a0399 100644 --- a/components/clarity-repl/src/repl/datastore.rs +++ b/components/clarity-repl/src/repl/datastore.rs @@ -1,26 +1,27 @@ -use std::collections::HashMap; - -use clarity::types::chainstate::BlockHeaderHash; -use clarity::types::chainstate::BurnchainHeaderHash; -use clarity::types::chainstate::ConsensusHash; -use clarity::types::chainstate::SortitionId; -use clarity::types::chainstate::StacksAddress; -use clarity::types::chainstate::StacksBlockId; -use clarity::types::chainstate::TrieHash; -use clarity::types::chainstate::VRFSeed; +use std::cell::RefCell; +use std::collections::{BTreeMap, HashMap}; +use std::rc::Rc; + +use super::remote_data::{epoch_for_height, Block, HttpClient}; +use clarity::types::chainstate::{ + BlockHeaderHash, BurnchainHeaderHash, ConsensusHash, SortitionId, StacksAddress, StacksBlockId, + TrieHash, VRFSeed, +}; use clarity::types::StacksEpochId; use clarity::util::hash::Sha512Trunc256Sum; use clarity::vm::analysis::AnalysisDatabase; use clarity::vm::database::BurnStateDB; use clarity::vm::database::{ClarityBackingStore, HeadersDB}; use clarity::vm::errors::InterpreterResult as Result; -use clarity::vm::types::QualifiedContractIdentifier; -use clarity::vm::types::TupleData; +use clarity::vm::types::{ + PrincipalData, QualifiedContractIdentifier, StandardPrincipalData, TupleData, +}; use clarity::vm::StacksEpoch; use pox_locking::handle_contract_call_special_cases; use sha2::{Digest, Sha512_256}; use super::interpreter::BLOCK_LIMIT_MAINNET; +use super::settings::RemoteNetworkInfo; const SECONDS_BETWEEN_BURN_BLOCKS: u64 = 600; const SECONDS_BETWEEN_STACKS_BLOCKS: u64 = 10; @@ -41,31 +42,63 @@ fn epoch_to_peer_version(epoch: StacksEpochId) -> u8 { } } -#[derive(Clone, Debug)] -struct StoreEntry(StacksBlockId, String); - -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct ClarityDatastore { open_chain_tip: StacksBlockId, - current_chain_tip: StacksBlockId, - store: HashMap>, + current_chain_tip: Rc>, + store: HashMap>, metadata: HashMap<(String, String), String>, - block_id_lookup: HashMap, height_at_chain_tip: HashMap, + chain_tip_at_height: HashMap, + + remote_network_info: Option, + remote_block_info_cache: Rc>>, + local_accounts: Vec, + + client: HttpClient, +} + +impl Clone for ClarityDatastore { + fn clone(&self) -> Self { + // for performance optimization, a simnet session can be stored and cached + // when cloning the session (and the datastore), we do not want to keep the + // current_chain_tip RefCell value, but rather sync it with the open_chain_tip + *self.current_chain_tip.borrow_mut() = self.open_chain_tip; + Self { + open_chain_tip: self.open_chain_tip, + current_chain_tip: Rc::clone(&self.current_chain_tip), + store: self.store.clone(), + metadata: self.metadata.clone(), + height_at_chain_tip: self.height_at_chain_tip.clone(), + chain_tip_at_height: self.chain_tip_at_height.clone(), + remote_network_info: self.remote_network_info.clone(), + remote_block_info_cache: Rc::clone(&self.remote_block_info_cache), + local_accounts: self.local_accounts.clone(), + client: self.client.clone(), + } + } +} + +struct BurnBlockHashes { + header_hash: BurnchainHeaderHash, + consensus_hash: ConsensusHash, + vrf_seed: VRFSeed, + sortition_id: SortitionId, } #[derive(Clone, Debug)] struct BurnBlockInfo { + consensus_hash: ConsensusHash, + vrf_seed: VRFSeed, + sortition_id: SortitionId, burn_block_time: u64, - burn_block_height: u32, + burn_chain_height: u32, } #[derive(Clone, Debug)] pub struct StacksBlockInfo { block_header_hash: BlockHeaderHash, burn_block_header_hash: BurnchainHeaderHash, - consensus_hash: ConsensusHash, - vrf_seed: VRFSeed, stacks_block_time: u64, } @@ -77,14 +110,28 @@ pub struct StacksConstants { pub pox_rejection_fraction: u64, } +impl Default for StacksConstants { + fn default() -> Self { + StacksConstants { + burn_start_height: 0, + pox_prepare_length: 50, + pox_reward_cycle_length: 1050, + pox_rejection_fraction: 0, + } + } +} + #[derive(Clone, Debug)] pub struct Datastore { genesis_id: StacksBlockId, + burn_chain_tip: BurnchainHeaderHash, burn_chain_height: u32, + current_chain_tip: Rc>, + remote_block_info_cache: Rc>>, burn_blocks: HashMap, stacks_chain_height: u32, stacks_blocks: HashMap, - sortition_lookup: HashMap, + sortition_lookup: HashMap, tenure_blocks_height: HashMap, consensus_hash_lookup: HashMap, current_epoch: StacksEpochId, @@ -100,37 +147,80 @@ fn height_to_hashed_bytes(height: u32) -> [u8; 32] { hash.0 } -fn height_to_id(height: u32) -> StacksBlockId { - StacksBlockId(height_to_hashed_bytes(height)) -} - -fn height_to_burn_block_header_hash(height: u32) -> BurnchainHeaderHash { - let mut bytes = height_to_hashed_bytes(height); - bytes[0] = 2; - BurnchainHeaderHash(bytes) -} - -impl Default for ClarityDatastore { - fn default() -> Self { - Self::new() +impl BurnBlockHashes { + fn from_height(height: u32) -> Self { + let bytes = height_to_hashed_bytes(height); + let header_hash = { + let mut buffer = bytes; + buffer[0] = 2; + BurnchainHeaderHash::from_bytes(&buffer[0..32]).unwrap() + }; + let consensus_hash = { + let mut buffer = bytes; + buffer[0] = 3; + ConsensusHash::from_bytes(&buffer[0..20]).unwrap() + }; + let vrf_seed = { + let mut buffer = bytes; + buffer[0] = 4; + VRFSeed(buffer) + }; + Self { + header_hash, + consensus_hash, + vrf_seed, + sortition_id: SortitionId(bytes), + } } } impl ClarityDatastore { - pub fn new() -> Self { - let id = height_to_id(0); + pub fn new(remote_network_info: Option, client: HttpClient) -> Self { + if let Some(remote_network_info) = remote_network_info { + return Self::new_with_remote_data(remote_network_info, client); + } + + let height = 0; + let id = StacksBlockId(height_to_hashed_bytes(height)); + Self { open_chain_tip: id, - current_chain_tip: id, + current_chain_tip: Rc::new(RefCell::new(id)), store: HashMap::new(), metadata: HashMap::new(), - block_id_lookup: HashMap::from([(id, id)]), - height_at_chain_tip: HashMap::from([(id, 0)]), + height_at_chain_tip: HashMap::from([(id, height)]), + chain_tip_at_height: HashMap::from([(height, id)]), + + remote_network_info: None, + remote_block_info_cache: Rc::new(RefCell::new(HashMap::new())), + local_accounts: Vec::new(), + + client, } } - pub fn open(_path_str: &str, _miner_tip: Option<&StacksBlockId>) -> Result { - Ok(ClarityDatastore::new()) + fn new_with_remote_data(remote_network_info: RemoteNetworkInfo, client: HttpClient) -> Self { + let height = remote_network_info.initial_height; + let path = format!("/extended/v2/blocks/{}", height); + let block = client.fetch_block(&path); + let cache = HashMap::from([(block.index_block_hash, block.clone())]); + + let id = block.index_block_hash; + + Self { + open_chain_tip: id, + current_chain_tip: Rc::new(RefCell::new(id)), + store: HashMap::new(), + metadata: HashMap::new(), + height_at_chain_tip: HashMap::from([(id, height)]), + chain_tip_at_height: HashMap::from([(height, id)]), + + remote_network_info: Some(remote_network_info), + remote_block_info_cache: Rc::new(RefCell::new(cache)), + local_accounts: Vec::new(), + + client, + } } pub fn as_analysis_db(&mut self) -> AnalysisDatabase<'_> { @@ -180,31 +270,83 @@ impl ClarityDatastore { // .expect("ERROR: Failed to commit MARF block"); } - fn put(&mut self, key: &str, value: &str) { - if let Some(entries) = self.store.get_mut(key) { - entries.push(StoreEntry(self.open_chain_tip, value.to_string())); - } else { - self.store.insert( - key.to_string(), - vec![StoreEntry(self.open_chain_tip, value.to_string())], - ); + pub fn save_local_account(&mut self, local_accounts: Vec) { + self.local_accounts = local_accounts; + } + + fn is_key_from_local_account(&mut self, key: &str) -> bool { + let parts: Vec<&str> = key.split("::").collect(); + if let Ok(principal) = PrincipalData::parse(parts[1]) { + let standard_principal = match principal { + PrincipalData::Contract(contract) => contract.issuer, + PrincipalData::Standard(standard) => standard, + }; + return self.local_accounts.contains(&standard_principal); } + false + } + + fn put(&mut self, key: &str, value: &str) { + let height = self.get_current_block_height(); + self.store + .entry(key.to_string()) + .or_default() + .insert(height, value.to_string()); + } + + fn fetch_block(&mut self, url: &str) -> Block { + let block = self.client.fetch_block(url); + self.remote_block_info_cache + .borrow_mut() + .insert(block.index_block_hash, block.clone()); + self.height_at_chain_tip + .insert(block.index_block_hash, block.height); + self.chain_tip_at_height + .insert(block.height, block.index_block_hash); + block } - fn get_latest_data(&self, data: &[StoreEntry]) -> Option { - let StoreEntry(tip, value) = data.last()?; + fn get_remote_block_info_from_height(&mut self, height: u32) -> Block { + if let Some(hash) = self.chain_tip_at_height.get(&height) { + return self.get_remote_block_info_from_hash(&hash.clone()); + } + self.fetch_block(&format!("/extended/v2/blocks/{}", height)) + } - if self.height_at_chain_tip.get(tip)? - <= self.height_at_chain_tip.get(&self.current_chain_tip)? - { - Some(value.clone()) - } else { - self.get_latest_data(&data[..data.len() - 1]) + fn get_remote_block_info_from_hash(&mut self, hash: &StacksBlockId) -> Block { + if let Some(cached) = self.remote_block_info_cache.borrow().get(hash) { + return cached.clone(); } + self.fetch_block(&format!("/extended/v2/blocks/{}", hash)) + } + + fn get_remote_chaintip(&mut self) -> String { + let initial_height = self.remote_network_info.as_ref().unwrap().initial_height; + let height = self.get_current_block_height().min(initial_height); + let block_info = self.get_remote_block_info_from_height(height); + block_info.index_block_hash.to_string() } - pub fn make_contract_hash_key(contract: &QualifiedContractIdentifier) -> String { - format!("clarity-contract::{}", contract) + fn fetch_clarity_marf_value(&mut self, key: &str) -> Result> { + let key_hash = TrieHash::from_key(key); + let tip = self.get_remote_chaintip(); + let url = format!("/v2/clarity/marf/{}?tip={}&proof=false", key_hash, tip); + self.client.fetch_clarity_data(&url) + } + + fn fetch_clarity_metadata( + &mut self, + contract: &QualifiedContractIdentifier, + key: &str, + ) -> Result> { + let addr = contract.issuer.to_string(); + let contract = contract.name.to_string(); + let tip = { self.get_remote_chaintip() }; + let url = format!( + "/v2/clarity/metadata/{}/{}/{}?tip={}", + addr, contract, key, tip + ); + self.client.fetch_clarity_data(&url) } } @@ -218,10 +360,42 @@ impl ClarityBackingStore for ClarityDatastore { /// fetch K-V out of the committed datastore fn get_data(&mut self, key: &str) -> Result> { - match self.store.get(key) { - Some(data) => Ok(self.get_latest_data(data)), - None => Ok(None), + let current_height = self.get_current_block_height(); + let fetch_remote_data = + self.remote_network_info.is_some() && !self.is_key_from_local_account(key); + + let values_map = self.store.get(key); + + if fetch_remote_data { + // if the value for the exact current_chain_tip is present, return it + if let Some(data) = values_map.and_then(|data| data.get(¤t_height)) { + return Ok(Some(data.clone())); + } + + let initial_height = self.remote_network_info.as_ref().unwrap().initial_height; + if current_height > initial_height { + if let Some((_, value)) = values_map.and_then(|data| { + data.iter() + .rev() + .find(|(height, _)| height > &&initial_height && height <= &¤t_height) + }) { + return Ok(Some(value.clone())); + } + } + + let data = self.fetch_clarity_marf_value(key); + if let Ok(Some(value)) = &data { + self.put(key, value); + } + return data; } + + Ok(values_map.and_then(|data| { + data.iter() + .rev() + .find(|(height, _)| height <= &¤t_height) + .map(|(_, value)| value.clone()) + })) } fn get_data_from_path(&mut self, _hash: &TrieHash) -> Result> { @@ -248,22 +422,45 @@ impl ClarityBackingStore for ClarityDatastore { /// returns the previous block header hash on success fn set_block_hash(&mut self, bhh: StacksBlockId) -> Result { let prior_tip = self.open_chain_tip; - self.current_chain_tip = bhh; + if self.remote_network_info.is_some() { + #[allow(clippy::map_entry)] + if !self.height_at_chain_tip.contains_key(&bhh) { + let block_info = self.get_remote_block_info_from_hash(&bhh); + self.height_at_chain_tip.insert(bhh, block_info.height); + self.chain_tip_at_height.insert(block_info.height, bhh); + } + } + *self.current_chain_tip.borrow_mut() = bhh; Ok(prior_tip) } fn get_block_at_height(&mut self, height: u32) -> Option { - Some(height_to_id(height)) + if let Some(remote_network_info) = &self.remote_network_info { + if height <= remote_network_info.initial_height { + let block_info = self.get_remote_block_info_from_height(height); + return Some(block_info.index_block_hash); + } + } + self.chain_tip_at_height.get(&height).copied() } /// this function returns the current block height, as viewed by this marfed-kv structure, /// i.e., it changes on time-shifted evaluation. the open_chain_tip functions always /// return data about the chain tip that is currently open for writing. fn get_current_block_height(&mut self) -> u32 { - *self - .height_at_chain_tip - .get(&self.current_chain_tip) - .unwrap_or(&u32::MAX) + let current_chain_tip = *self.current_chain_tip.borrow(); + if let Some(&height) = self.height_at_chain_tip.get(¤t_chain_tip) { + return height; + } + + if let Some(initial_height) = self.remote_network_info.as_ref().map(|d| d.initial_height) { + let block_info = self.get_remote_block_info_from_hash(¤t_chain_tip); + if block_info.height <= initial_height { + return block_info.height; + } + } + + u32::MAX } fn get_open_chain_tip_height(&mut self) -> u32 { @@ -299,12 +496,18 @@ impl ClarityBackingStore for ClarityDatastore { contract: &QualifiedContractIdentifier, key: &str, ) -> Result> { - let key = &(contract.to_string(), key.to_string()); - - match self.metadata.get(key) { - Some(result) => Ok(Some(result.to_string())), - None => Ok(None), + let metadata = self.metadata.get(&(contract.to_string(), key.to_string())); + if metadata.is_some() { + return Ok(metadata.cloned()); } + if self.remote_network_info.is_some() && !self.local_accounts.contains(&contract.issuer) { + let data = self.fetch_clarity_metadata(contract, key); + if let Ok(Some(value)) = &data { + let _ = self.insert_metadata(contract, key, value); + } + return data; + } + Ok(None) } fn get_contract_hash( @@ -333,56 +536,131 @@ impl ClarityBackingStore for ClarityDatastore { } } -impl Default for Datastore { - fn default() -> Self { - Self::new(StacksConstants { - burn_start_height: 0, - pox_prepare_length: 50, - pox_reward_cycle_length: 1050, - pox_rejection_fraction: 0, - }) - } -} - impl Datastore { - pub fn new(constants: StacksConstants) -> Self { - let bytes = height_to_hashed_bytes(0); + pub fn new(clarity_datastore: &ClarityDatastore, constants: StacksConstants) -> Self { + if clarity_datastore.remote_network_info.is_some() { + return Self::new_with_remote_data(clarity_datastore, constants); + } + + let stacks_chain_height = 0; + let burn_chain_height = 0; + let bytes = height_to_hashed_bytes(stacks_chain_height); let id = StacksBlockId(bytes); - let sortition_id = SortitionId(bytes); let genesis_time = chrono::Utc::now().timestamp() as u64; - let first_burn_block_header_hash = BurnchainHeaderHash([0x00; 32]); + let burn_block_hashes = BurnBlockHashes::from_height(burn_chain_height); + let burn_block_header_hash = burn_block_hashes.header_hash; - let genesis_burn_block = BurnBlockInfo { + let burn_block = BurnBlockInfo { + consensus_hash: burn_block_hashes.consensus_hash, + vrf_seed: burn_block_hashes.vrf_seed, + sortition_id: burn_block_hashes.sortition_id, burn_block_time: genesis_time, - burn_block_height: 0, + burn_chain_height, }; - let genesis_block = StacksBlockInfo { - block_header_hash: BlockHeaderHash([0x00; 32]), - burn_block_header_hash: first_burn_block_header_hash, - consensus_hash: ConsensusHash([0x00; 20]), - vrf_seed: VRFSeed([0x00; 32]), + let stacks_block = StacksBlockInfo { + block_header_hash: BlockHeaderHash(bytes), + burn_block_header_hash: burn_block_hashes.header_hash, stacks_block_time: genesis_time + SECONDS_BETWEEN_STACKS_BLOCKS, }; - let sortition_lookup = HashMap::from([(sortition_id, id)]); - let consensus_hash_lookup = HashMap::from([(genesis_block.consensus_hash, sortition_id)]); + let sortition_lookup = HashMap::from([(burn_block.sortition_id, burn_block_header_hash)]); + let consensus_hash_lookup = + HashMap::from([(burn_block.consensus_hash, burn_block.sortition_id)]); let tenure_blocks_height = HashMap::from([(0, 0)]); - let burn_blocks = HashMap::from([(first_burn_block_header_hash, genesis_burn_block)]); - let stacks_blocks = HashMap::from([(id, genesis_block)]); + let burn_blocks = HashMap::from([(burn_block_header_hash, burn_block)]); + let stacks_blocks = HashMap::from([(id, stacks_block)]); Datastore { genesis_id: id, - burn_chain_height: 0, + burn_chain_tip: burn_block_header_hash, + burn_chain_height, + current_chain_tip: Rc::clone(&clarity_datastore.current_chain_tip), + remote_block_info_cache: Rc::clone(&clarity_datastore.remote_block_info_cache), burn_blocks, - stacks_chain_height: 0, + stacks_chain_height, stacks_blocks, sortition_lookup, consensus_hash_lookup, tenure_blocks_height, current_epoch: StacksEpochId::Epoch2_05, - current_epoch_start_height: 0, + current_epoch_start_height: stacks_chain_height, + constants, + } + } + + fn new_with_remote_data( + clarity_datastore: &ClarityDatastore, + constants: StacksConstants, + ) -> Self { + let current_chain_tip = clarity_datastore.current_chain_tip.borrow(); + let stacks_chain_height = clarity_datastore + .height_at_chain_tip + .get(¤t_chain_tip) + .unwrap(); + + let block = { + let cache = clarity_datastore.remote_block_info_cache.borrow(); + cache.get(¤t_chain_tip).unwrap().clone() + }; + + let is_mainnet = clarity_datastore + .remote_network_info + .as_ref() + .unwrap() + .is_mainnet; + + let burn_chain_height = block.burn_block_height; + let id = block.index_block_hash; + let burn_block_header_hash = block.burn_block_hash; + let block_header_hash = block.hash; + + let sortition = clarity_datastore + .client + .fetch_sortition(&burn_block_header_hash); + let sortition_id = sortition.sortition_id; + let consensus_hash = sortition.consensus_hash; + + let vrf_seed = sortition.vrf_seed.unwrap_or_else(|| { + let bytes = height_to_hashed_bytes(burn_chain_height); + VRFSeed(bytes) + }); + + let burn_block = BurnBlockInfo { + consensus_hash, + vrf_seed, + sortition_id, + burn_block_time: block.burn_block_time, + burn_chain_height, + }; + + let stacks_block = StacksBlockInfo { + block_header_hash, + burn_block_header_hash, + stacks_block_time: block.block_time, + }; + + let sortition_lookup = HashMap::from([(sortition_id, burn_block_header_hash)]); + let consensus_hash_lookup = HashMap::from([(burn_block.consensus_hash, sortition_id)]); + let tenure_blocks_height = HashMap::from([(burn_chain_height, block.tenure_height)]); + let burn_blocks = HashMap::from([(burn_block_header_hash, burn_block)]); + let stacks_blocks = HashMap::from([(id, stacks_block)]); + + Datastore { + genesis_id: id, + burn_chain_tip: burn_block_header_hash, + burn_chain_height, + current_chain_tip: Rc::clone(&clarity_datastore.current_chain_tip), + remote_block_info_cache: Rc::clone(&clarity_datastore.remote_block_info_cache), + burn_blocks, + stacks_chain_height: *stacks_chain_height, + stacks_blocks, + sortition_lookup, + consensus_hash_lookup, + tenure_blocks_height, + current_epoch: epoch_for_height(is_mainnet, *stacks_chain_height), + current_epoch_start_height: *stacks_chain_height, constants, } } @@ -400,48 +678,32 @@ impl Datastore { } fn build_next_stacks_block(&self, clarity_datastore: &ClarityDatastore) -> StacksBlockInfo { - let burn_chain_height = self.burn_chain_height; let stacks_block_height = self.stacks_chain_height; - let last_stacks_block = self + let previous_stacks_block = self .stacks_blocks - .get(&clarity_datastore.current_chain_tip) + .get(&clarity_datastore.open_chain_tip) .expect("current chain tip missing in stacks block table"); let last_burn_block = self .burn_blocks - .get(&height_to_burn_block_header_hash(burn_chain_height)) + .get(&self.burn_chain_tip) .expect("burn block missing in burn block table"); let last_block_time = std::cmp::max( - last_stacks_block.stacks_block_time, + previous_stacks_block.stacks_block_time, last_burn_block.burn_block_time, ); - let bytes = height_to_hashed_bytes(stacks_block_height); - let block_header_hash = { - let mut buffer = bytes; + let mut buffer = height_to_hashed_bytes(stacks_block_height); buffer[0] = 1; BlockHeaderHash(buffer) }; - let burn_block_header_hash = height_to_burn_block_header_hash(burn_chain_height); - let consensus_hash = { - let mut buffer = bytes; - buffer[0] = 3; - ConsensusHash::from_bytes(&buffer[0..20]).unwrap() - }; - let vrf_seed = { - let mut buffer = bytes; - buffer[0] = 4; - VRFSeed(buffer) - }; let stacks_block_time: u64 = last_block_time + SECONDS_BETWEEN_STACKS_BLOCKS; StacksBlockInfo { block_header_hash, - burn_block_header_hash, - consensus_hash, - vrf_seed, + burn_block_header_hash: self.burn_chain_tip, stacks_block_time, } } @@ -452,30 +714,53 @@ impl Datastore { count: u32, ) -> u32 { for _ in 1..=count { - let last_stacks_block = self - .stacks_blocks - .get(&clarity_datastore.current_chain_tip) - .unwrap(); - let last_burn_block = self - .burn_blocks - .get(&last_stacks_block.burn_block_header_hash) - .unwrap(); - - let mut next_burn_block_time = - last_burn_block.burn_block_time + SECONDS_BETWEEN_BURN_BLOCKS; - if last_stacks_block.stacks_block_time > next_burn_block_time { - next_burn_block_time = - last_stacks_block.stacks_block_time + SECONDS_BETWEEN_STACKS_BLOCKS; - } + let next_burn_block_time = { + let last_stacks_block = self + .stacks_blocks + .get(&clarity_datastore.open_chain_tip) + .unwrap_or_else(|| { + panic!( + "current chain tip missing in stacks_blocks table: {}", + clarity_datastore.open_chain_tip + ) + }); + let last_burn_block = + self.burn_blocks + .get(&self.burn_chain_tip) + .unwrap_or_else(|| { + panic!( + "burn block missing in burn_blocks table: {}", + self.burn_chain_tip + ) + }); + + let mut next_burn_block_time = + last_burn_block.burn_block_time + SECONDS_BETWEEN_BURN_BLOCKS; + if last_stacks_block.stacks_block_time > next_burn_block_time { + next_burn_block_time = + last_stacks_block.stacks_block_time + SECONDS_BETWEEN_STACKS_BLOCKS; + } + next_burn_block_time + }; let height = self.burn_chain_height + 1; - let hash = height_to_burn_block_header_hash(height); - let burn_block_info = BurnBlockInfo { + let burn_block_hashes = BurnBlockHashes::from_height(height); + let burn_block_header_hash = burn_block_hashes.header_hash; + + let burn_block = BurnBlockInfo { + consensus_hash: burn_block_hashes.consensus_hash, + vrf_seed: burn_block_hashes.vrf_seed, + sortition_id: burn_block_hashes.sortition_id, burn_block_time: next_burn_block_time, - burn_block_height: height, + burn_chain_height: height, }; - self.burn_blocks.insert(hash, burn_block_info); + self.consensus_hash_lookup + .insert(burn_block.consensus_hash, burn_block.sortition_id); + self.sortition_lookup + .insert(burn_block.sortition_id, burn_block_header_hash); + self.burn_chain_tip = burn_block_header_hash; + self.burn_blocks.insert(burn_block_header_hash, burn_block); self.burn_chain_height = height; self.advance_stacks_chain_tip(clarity_datastore, 1); @@ -491,34 +776,22 @@ impl Datastore { clarity_datastore: &mut ClarityDatastore, count: u32, ) -> u32 { - let current_lookup_id = *clarity_datastore - .block_id_lookup - .get(&clarity_datastore.open_chain_tip) - .expect("Open chain tip missing in block id lookup table"); - for _ in 1..=count { self.stacks_chain_height += 1; - let bytes = height_to_hashed_bytes(self.stacks_chain_height); let id = StacksBlockId(bytes); - let sortition_id = SortitionId(bytes); let block_info = self.build_next_stacks_block(clarity_datastore); - - self.sortition_lookup.insert(sortition_id, id); - self.consensus_hash_lookup - .insert(block_info.consensus_hash, sortition_id); self.stacks_blocks.insert(id, block_info); - - clarity_datastore - .block_id_lookup - .entry(id) - .or_insert(current_lookup_id); clarity_datastore .height_at_chain_tip .entry(id) .or_insert(self.stacks_chain_height); - clarity_datastore.open_chain_tip = height_to_id(self.stacks_chain_height); - clarity_datastore.current_chain_tip = clarity_datastore.open_chain_tip; + clarity_datastore + .chain_tip_at_height + .entry(self.stacks_chain_height) + .or_insert(id); + clarity_datastore.open_chain_tip = id; + *clarity_datastore.current_chain_tip.borrow_mut() = id; } self.stacks_chain_height @@ -548,9 +821,18 @@ impl HeadersDB for Datastore { id_bhh: &StacksBlockId, _epoch_id: &StacksEpochId, ) -> Option { - self.stacks_blocks + if let Some(hash) = self + .stacks_blocks .get(id_bhh) .map(|id| id.block_header_hash) + { + return Some(hash); + }; + + self.remote_block_info_cache + .borrow() + .get(id_bhh) + .map(|block| block.hash) } fn get_burn_header_hash_for_block( @@ -567,7 +849,11 @@ impl HeadersDB for Datastore { id_bhh: &StacksBlockId, _epoch_id: &StacksEpochId, ) -> Option { - self.stacks_blocks.get(id_bhh).map(|id| id.consensus_hash) + self.stacks_blocks + .get(id_bhh) + .map(|block| block.burn_block_header_hash) + .and_then(|hash| self.burn_blocks.get(&hash)) + .map(|b| b.consensus_hash) } fn get_vrf_seed_for_block( @@ -575,13 +861,26 @@ impl HeadersDB for Datastore { id_bhh: &StacksBlockId, _epoch_id: &StacksEpochId, ) -> Option { - self.stacks_blocks.get(id_bhh).map(|id| id.vrf_seed) + self.stacks_blocks + .get(id_bhh) + .map(|block| block.burn_block_header_hash) + .and_then(|hash| self.burn_blocks.get(&hash)) + .map(|b| b.vrf_seed) } fn get_stacks_block_time_for_block(&self, id_bhh: &StacksBlockId) -> Option { - self.stacks_blocks + if let Some(time) = self + .stacks_blocks .get(id_bhh) .map(|id| id.stacks_block_time) + { + return Some(time); + }; + + self.remote_block_info_cache + .borrow() + .get(id_bhh) + .map(|block| block.block_time) } fn get_burn_block_time_for_block( @@ -595,9 +894,18 @@ impl HeadersDB for Datastore { } fn get_burn_block_height_for_block(&self, id_bhh: &StacksBlockId) -> Option { - self.get_burn_header_hash_for_block(id_bhh) + if let Some(height) = self + .get_burn_header_hash_for_block(id_bhh) .and_then(|hash| self.burn_blocks.get(&hash)) - .map(|b| b.burn_block_height) + .map(|b| b.burn_chain_height) + { + return Some(height); + } + + self.remote_block_info_cache + .borrow() + .get(id_bhh) + .map(|block| block.burn_block_height) } fn get_stacks_height_for_tenure_height( @@ -681,23 +989,31 @@ impl BurnStateDB for Datastore { } fn get_tip_burn_block_height(&self) -> Option { - Some(self.burn_chain_height) + let current_chain_tip = self.current_chain_tip.borrow(); + if let Some(height) = self.get_burn_block_height_for_block(¤t_chain_tip) { + return Some(height); + } + + return self + .remote_block_info_cache + .borrow() + .get(¤t_chain_tip) + .map(|block| block.burn_block_height); } fn get_tip_sortition_id(&self) -> Option { - let bytes = height_to_hashed_bytes(self.stacks_chain_height); - let sortition_id = SortitionId(bytes); - Some(sortition_id) + let current_chain_tip = self.current_chain_tip.borrow(); + self.get_burn_header_hash_for_block(¤t_chain_tip) + .and_then(|hash| self.burn_blocks.get(&hash)) + .map(|block| block.sortition_id) } /// Returns the *burnchain block height* for the `sortition_id` is associated with. fn get_burn_block_height(&self, sortition_id: &SortitionId) -> Option { self.sortition_lookup .get(sortition_id) - .and_then(|id| self.stacks_blocks.get(id)) - .map(|stacks_block_info| stacks_block_info.burn_block_header_hash) - .and_then(|hash| self.burn_blocks.get(&hash)) - .map(|burn_block_info| burn_block_info.burn_block_height) + .and_then(|hash| self.burn_blocks.get(hash)) + .map(|burn_block_info| burn_block_info.burn_chain_height) } /// Returns the height of the burnchain when the Stacks chain started running. @@ -719,16 +1035,13 @@ impl BurnStateDB for Datastore { /// Returns the burnchain header hash for the given burn block height, as queried from the given SortitionId. /// - /// Returns Some if `self.get_burn_start_height() <= height < self.get_burn_block_height(sorition_id)`, and None otherwise. + /// Returns Some if `self.get_burn_start_height() <= height < self.get_burn_block_height(sortition_id)`, and None otherwise. fn get_burn_header_hash( &self, _height: u32, sortition_id: &SortitionId, ) -> Option { - self.sortition_lookup - .get(sortition_id) - .and_then(|id| self.stacks_blocks.get(id)) - .map(|block_info| block_info.burn_block_header_hash) + self.sortition_lookup.get(sortition_id).copied() } /// Lookup a `SortitionId` keyed to a `ConsensusHash`. @@ -779,20 +1092,85 @@ impl BurnStateDB for Datastore { mod tests { use clarity::types::StacksEpoch; + use crate::repl::settings::ApiUrl; + use super::*; + fn get_datastores() -> (ClarityDatastore, Datastore) { + let client = HttpClient::new(ApiUrl("https://api.tesnet.hiro.so".to_string())); + let constants = StacksConstants::default(); + let clarity_datastore = ClarityDatastore::new(None, client); + let datastore = Datastore::new(&clarity_datastore, constants); + (clarity_datastore, datastore) + } + + fn get_datastores_with_remote_data() -> (ClarityDatastore, Datastore) { + let mut server = mockito::Server::new(); + let _ = server + .mock("GET", "/extended/v2/blocks/10") + .with_status(200) + .with_header("content-type", "application/json") + .with_body( + r#"{ + "canonical": true, + "height": 10, + "hash": "0xaff3b535a135348ed00023ec1bdc3da9005253a9ce80a4906ade03ea6685d342", + "block_time": 1735934294, + "block_time_iso": "2025-01-03T19:58:14.000Z", + "tenure_height": 10, + "index_block_hash": "0x201cf66636e693d95998b40ddd0cbe038432806046eed11866052f15a9fa8fc5", + "parent_block_hash": "0x94c3d8f56ed2e1093f26089572af9cc5d5b097d461dcc184196f1ee2070de063", + "parent_index_block_hash": "0x1969bdddb9902162f5fdd2ff49cabb30300a9819c89bedd4c27fed82f8c9cf4b", + "burn_block_time": 1735451504, + "burn_block_time_iso": "2024-12-29T05:51:44.000Z", + "burn_block_hash": "0x57f3e2bd4519e4263353bf6b7614a9cee7f2d36fe61409852d42e41afe5e6cad", + "burn_block_height": 798, + "miner_txid": "0x5fb426cf9eb4577b545bd731634886d5bd5c9d40d573e2cdb95100f483913491", + "tx_count": 2 + }"#, + ) + .create(); + let _ = server + .mock("GET", "/v3/sortitions/burn/57f3e2bd4519e4263353bf6b7614a9cee7f2d36fe61409852d42e41afe5e6cad") + .with_status(200) + .with_header("content-type", "application/json") + .with_body( + r#"[{ + "burn_block_hash": "0x57f3e2bd4519e4263353bf6b7614a9cee7f2d36fe61409852d42e41afe5e6cad", + "burn_block_height":798, + "burn_header_timestamp": 1735451504, + "sortition_id": "0x71e332329133c0f331c6b5e9b21a415ea0c32aa300a0e94a88e7c30d4aaf78c6", + "parent_sortition_id": "0xd6bffd8c4cd86428d5404ef36867319976008e84047d2acbae9079fd918c4de9", + "consensus_hash": "0x44f6511f569d3ed78d437af619d529b6c66a4fa2" + }]"#, + ) + .create(); + let client = HttpClient::new(ApiUrl(server.url())); + let constants = StacksConstants::default(); + let clarity_datastore = ClarityDatastore::new( + Some(RemoteNetworkInfo { + initial_height: 10, + is_mainnet: false, + api_url: ApiUrl(server.url().to_string()), + network_id: 2147483648, + stacks_tip_height: 10, + }), + client, + ); + let datastore = Datastore::new(&clarity_datastore, constants); + (clarity_datastore, datastore) + } + #[test] fn test_advance_chain_tip() { - let mut datastore = Datastore::default(); - let mut clarity_datastore = ClarityDatastore::new(); + let (mut clarity_datastore, mut datastore) = get_datastores(); datastore.advance_burn_chain_tip(&mut clarity_datastore, 5); assert_eq!(datastore.stacks_chain_height, 5); } #[test] fn test_set_current_epoch() { - let mut datastore = Datastore::default(); - let mut clarity_datastore = ClarityDatastore::new(); + let (mut clarity_datastore, mut datastore) = get_datastores(); let epoch_id = StacksEpochId::Epoch25; datastore.set_current_epoch(&mut clarity_datastore, epoch_id); assert_eq!(datastore.current_epoch, epoch_id); @@ -800,38 +1178,37 @@ mod tests { #[test] fn test_get_v1_unlock_height() { - let datastore = Datastore::default(); + let (_, datastore) = get_datastores(); assert_eq!(datastore.get_v1_unlock_height(), 0); } #[test] fn test_get_v2_unlock_height() { - let datastore = Datastore::default(); + let (_, datastore) = get_datastores(); assert_eq!(datastore.get_v2_unlock_height(), 0); } #[test] fn test_get_v3_unlock_height() { - let datastore = Datastore::default(); + let (_, datastore) = get_datastores(); assert_eq!(datastore.get_v3_unlock_height(), 0); } #[test] fn test_get_pox_3_activation_height() { - let datastore = Datastore::default(); + let (_, datastore) = get_datastores(); assert_eq!(datastore.get_pox_3_activation_height(), 0); } #[test] fn test_get_pox_4_activation_height() { - let datastore = Datastore::default(); + let (_, datastore) = get_datastores(); assert_eq!(datastore.get_pox_4_activation_height(), 0); } #[test] fn test_get_tip_burn_block_height() { - let mut datastore = Datastore::default(); - let mut clarity_datastore = ClarityDatastore::new(); + let (mut clarity_datastore, mut datastore) = get_datastores(); let chain_height = 10; datastore.advance_burn_chain_tip(&mut clarity_datastore, 10); let tip_burn_block_height = datastore.get_tip_burn_block_height(); @@ -840,31 +1217,31 @@ mod tests { #[test] fn test_get_burn_start_height() { - let datastore = Datastore::default(); + let (_, datastore) = get_datastores(); assert_eq!(datastore.get_burn_start_height(), 0); } #[test] fn test_get_pox_prepare_length() { - let datastore = Datastore::default(); + let (_, datastore) = get_datastores(); assert_eq!(datastore.get_pox_prepare_length(), 50); } #[test] fn test_get_pox_reward_cycle_length() { - let datastore = Datastore::default(); + let (_, datastore) = get_datastores(); assert_eq!(datastore.get_pox_reward_cycle_length(), 1050); } #[test] fn test_get_pox_rejection_fraction() { - let datastore = Datastore::default(); + let (_, datastore) = get_datastores(); assert_eq!(datastore.get_pox_rejection_fraction(), 0); } #[test] fn test_get_stacks_epoch() { - let datastore = Datastore::default(); + let (_, datastore) = get_datastores(); let height = 10; let epoch = datastore.get_stacks_epoch(height); assert_eq!( @@ -881,7 +1258,7 @@ mod tests { #[test] fn test_get_stacks_epoch_by_epoch_id() { - let datastore = Datastore::default(); + let (_, datastore) = get_datastores(); let epoch_id = StacksEpochId::Epoch2_05; let epoch = datastore.get_stacks_epoch_by_epoch_id(&epoch_id); assert_eq!( @@ -895,4 +1272,37 @@ mod tests { }) ); } + + #[test] + fn test_get_current_block_height() { + let (mut clarity_datastore, _) = get_datastores(); + let height = clarity_datastore.get_current_block_height(); + assert_eq!(height, 0); + } + + #[test] + fn test_get_current_block_heigth_with_remote_data() { + let (mut clarity_datastore, _datastore) = get_datastores_with_remote_data(); + let height = clarity_datastore.get_current_block_height(); + assert_eq!(height, 10); + } + + // make sure that when a ClarityDatastore is clones, the current_chain_tip is reset + #[test] + fn test_clarity_datastore_caching() { + let (mut clarity_datastore, mut datastore) = get_datastores(); + + let initial_tip = *clarity_datastore.current_chain_tip.borrow(); + + let cache = clarity_datastore.clone(); + + datastore.advance_burn_chain_tip(&mut clarity_datastore, 10); + + let current_tip = *clarity_datastore.current_chain_tip.borrow(); + assert_ne!(current_tip, initial_tip); + + let clarity_datastore = cache.clone(); + let current_tip = *clarity_datastore.current_chain_tip.borrow(); + assert_eq!(current_tip, initial_tip); + } } diff --git a/components/clarity-repl/src/repl/interpreter.rs b/components/clarity-repl/src/repl/interpreter.rs index 60188751e..370def049 100644 --- a/components/clarity-repl/src/repl/interpreter.rs +++ b/components/clarity-repl/src/repl/interpreter.rs @@ -3,10 +3,9 @@ use std::collections::{btree_map::Entry, BTreeMap, BTreeSet}; use crate::analysis::annotation::{Annotation, AnnotationKind}; use crate::analysis::ast_dependency_detector::{ASTDependencyDetector, Dependency}; use crate::analysis::{self}; -use crate::repl::datastore::ClarityDatastore; -use crate::repl::datastore::Datastore; +use crate::repl::datastore::{ClarityDatastore, Datastore}; use crate::repl::Settings; -use clarity::consts::CHAIN_ID_TESTNET; +use clarity::consts::{CHAIN_ID_MAINNET, CHAIN_ID_TESTNET}; use clarity::types::StacksEpochId; use clarity::vm::analysis::ContractAnalysis; use clarity::vm::ast::{build_ast_with_diagnostics, ContractAST}; @@ -28,6 +27,9 @@ use clarity::vm::{events::*, ClarityVersion}; use clarity::vm::{ContractEvaluationResult, EvalHook}; use clarity::vm::{CostSynthesis, ExecutionResult, ParsedContract}; +use super::datastore::StacksConstants; +use super::remote_data::HttpClient; +use super::settings::{ApiUrl, RemoteNetworkInfo}; use super::{ClarityContract, DEFAULT_EPOCH}; pub const BLOCK_LIMIT_MAINNET: ExecutionCost = ExecutionCost { @@ -43,6 +45,7 @@ pub struct ClarityInterpreter { pub clarity_datastore: ClarityDatastore, pub datastore: Datastore, pub repl_settings: Settings, + remote_network_info: Option, tx_sender: StandardPrincipalData, accounts: BTreeSet, tokens: BTreeMap>, @@ -53,13 +56,30 @@ pub struct Txid(pub [u8; 32]); impl ClarityInterpreter { pub fn new(tx_sender: StandardPrincipalData, repl_settings: Settings) -> Self { + let remote_data_settings = repl_settings.remote_data.clone(); + + let client = HttpClient::new(ApiUrl(remote_data_settings.api_url.to_string())); + let remote_network_info = if remote_data_settings.enabled { + Some( + remote_data_settings + .get_initial_remote_network_info(&client) + .unwrap(), + ) + } else { + None + }; + + let clarity_datastore = ClarityDatastore::new(remote_network_info.clone(), client); + let datastore = Datastore::new(&clarity_datastore, StacksConstants::default()); + Self { tx_sender, repl_settings, - clarity_datastore: ClarityDatastore::new(), + remote_network_info, + clarity_datastore, + datastore, accounts: BTreeSet::new(), tokens: BTreeMap::new(), - datastore: Datastore::default(), } } @@ -356,43 +376,71 @@ impl ClarityInterpreter { Some(format!("0x{value_hex}")) } - fn execute( + fn get_global_context( &mut self, - contract: &ClarityContract, - contract_ast: &ContractAST, - analysis: ContractAnalysis, + epoch: StacksEpochId, cost_track: bool, - eval_hooks: Option>, - ) -> Result { - let contract_id = contract.expect_resolved_contract_identifier(Some(&self.tx_sender)); - let snippet = contract.expect_in_memory_code_source(); - let mut contract_context = - ContractContext::new(contract_id.clone(), contract.clarity_version); + ) -> Result { + let is_mainnet = self + .remote_network_info + .as_ref() + .map_or(false, |data| data.is_mainnet); + let chain_id = if is_mainnet { + CHAIN_ID_MAINNET + } else { + CHAIN_ID_TESTNET + }; let mut conn = ClarityDatabase::new( &mut self.clarity_datastore, &self.datastore, &self.datastore, ); - let tx_sender: PrincipalData = self.tx_sender.clone().into(); conn.begin(); - conn.set_clarity_epoch_version(contract.epoch) + conn.set_clarity_epoch_version(epoch) .map_err(|e| e.to_string())?; conn.commit().map_err(|e| e.to_string())?; let cost_tracker = if cost_track { LimitedCostTracker::new( - false, - CHAIN_ID_TESTNET, + is_mainnet, + chain_id, BLOCK_LIMIT_MAINNET.clone(), &mut conn, - contract.epoch, + epoch, ) .map_err(|e| format!("failed to initialize cost tracker: {e}"))? } else { LimitedCostTracker::new_free() }; - let mut global_context = - GlobalContext::new(false, CHAIN_ID_TESTNET, conn, cost_tracker, contract.epoch); + + Ok(GlobalContext::new( + is_mainnet, + chain_id, + conn, + cost_tracker, + epoch, + )) + } + + fn execute( + &mut self, + contract: &ClarityContract, + contract_ast: &ContractAST, + analysis: ContractAnalysis, + cost_track: bool, + eval_hooks: Option>, + ) -> Result { + let contract_id = contract.expect_resolved_contract_identifier(Some(&self.tx_sender)); + let snippet = contract.expect_in_memory_code_source(); + let mut contract_context = + ContractContext::new(contract_id.clone(), contract.clarity_version); + + #[cfg(not(feature = "wasm"))] + let show_timings = self.repl_settings.show_timings; + + let tx_sender: PrincipalData = self.tx_sender.clone().into(); + + let mut global_context = self.get_global_context(contract.epoch, cost_track)?; if let Some(mut in_hooks) = eval_hooks { let mut hooks: Vec<&mut dyn EvalHook> = Vec::new(); @@ -402,9 +450,6 @@ impl ClarityInterpreter { global_context.eval_hooks = Some(hooks); } - #[cfg(not(feature = "wasm"))] - let show_timings = self.repl_settings.show_timings; - global_context.begin(); let result = global_context.execute(|g| { if contract_ast.expressions.len() == 1 && !snippet.contains("(define-") { @@ -439,7 +484,7 @@ impl ClarityInterpreter { args.push(evaluated_arg); } - #[cfg(not(feature = "wasm"))] + #[cfg(not(target_arch = "wasm32"))] let start = std::time::Instant::now(); let args: Vec = args @@ -448,9 +493,9 @@ impl ClarityInterpreter { .collect(); let res = env.execute_contract(&contract_id, &method, &args, false)?; - #[cfg(not(feature = "wasm"))] + #[cfg(not(target_arch = "wasm32"))] if show_timings { - println!("execution time: {:?}Ī¼s", start.elapsed().as_micros()); + println!("execution time: {:?}", start.elapsed()); } return Ok(Some(res)); @@ -458,14 +503,14 @@ impl ClarityInterpreter { } }; - #[cfg(not(feature = "wasm"))] + #[cfg(not(target_arch = "wasm32"))] let start = std::time::Instant::now(); let result = eval(&contract_ast.expressions[0], &mut env, &context); - #[cfg(not(feature = "wasm"))] + #[cfg(not(target_arch = "wasm32"))] if show_timings { - println!("execution time: {:?}Ī¼s", start.elapsed().as_micros()); + println!("execution time: {:?}", start.elapsed()); } return result.map(Some); @@ -598,30 +643,10 @@ impl ClarityInterpreter { let mut contract_context = ContractContext::new(contract_id.clone(), contract.clarity_version); - let mut conn = ClarityDatabase::new( - &mut self.clarity_datastore, - &self.datastore, - &self.datastore, - ); + let show_timings = self.repl_settings.show_timings; let tx_sender: PrincipalData = self.tx_sender.clone().into(); - conn.begin(); - conn.set_clarity_epoch_version(contract.epoch) - .expect("failed to set epoch"); - conn.commit().expect("failed to commit"); - let cost_tracker = if cost_track { - LimitedCostTracker::new( - false, - CHAIN_ID_TESTNET, - BLOCK_LIMIT_MAINNET.clone(), - &mut conn, - contract.epoch, - ) - .map_err(|e| format!("failed to initialize cost tracker: {e}"))? - } else { - LimitedCostTracker::new_free() - }; - let mut global_context = - GlobalContext::new(false, CHAIN_ID_TESTNET, conn, cost_tracker, contract.epoch); + + let mut global_context = self.get_global_context(contract.epoch, cost_track)?; if let Some(mut in_hooks) = eval_hooks { let mut hooks: Vec<&mut dyn EvalHook> = Vec::new(); @@ -631,8 +656,6 @@ impl ClarityInterpreter { global_context.eval_hooks = Some(hooks); } - let show_timings = self.repl_settings.show_timings; - global_context.begin(); let result = global_context.execute(|g| { if contract_ast.expressions.len() == 1 && !snippet.contains("(define-") { @@ -834,31 +857,9 @@ impl ClarityInterpreter { allow_private: bool, mut eval_hooks: Vec<&mut dyn EvalHook>, ) -> Result { - let mut conn = ClarityDatabase::new( - &mut self.clarity_datastore, - &self.datastore, - &self.datastore, - ); let tx_sender: PrincipalData = self.tx_sender.clone().into(); - conn.begin(); - conn.set_clarity_epoch_version(epoch) - .map_err(|e| e.to_string())?; - conn.commit().map_err(|e| e.to_string())?; - let cost_tracker = if track_costs { - LimitedCostTracker::new( - false, - CHAIN_ID_TESTNET, - BLOCK_LIMIT_MAINNET.clone(), - &mut conn, - epoch, - ) - .map_err(|e| format!("failed to initialize cost tracker: {e}"))? - } else { - LimitedCostTracker::new_free() - }; - let mut global_context = - GlobalContext::new(false, CHAIN_ID_TESTNET, conn, cost_tracker, epoch); + let mut global_context = self.get_global_context(epoch, track_costs)?; let mut hooks: Vec<&mut dyn EvalHook> = Vec::new(); for hook in eval_hooks.drain(..) { @@ -1041,25 +1042,18 @@ impl ClarityInterpreter { (events, accounts_to_credit, accounts_to_debit) } + pub fn save_genesis_accounts(&mut self, addresses: Vec) { + self.clarity_datastore.save_local_account(addresses); + } + pub fn mint_stx_balance( &mut self, recipient: PrincipalData, amount: u64, ) -> Result { let final_balance = { - let conn = ClarityDatabase::new( - &mut self.clarity_datastore, - &self.datastore, - &self.datastore, - ); + let mut global_context = self.get_global_context(DEFAULT_EPOCH, false)?; - let mut global_context = GlobalContext::new( - false, - CHAIN_ID_TESTNET, - conn, - LimitedCostTracker::new_free(), - DEFAULT_EPOCH, - ); global_context.begin(); let mut cur_balance = global_context .database @@ -1199,6 +1193,7 @@ impl ClarityInterpreter { mod tests { use super::*; use crate::analysis::Settings as AnalysisSettings; + use crate::repl::settings::RemoteDataSettings; use crate::{ repl::session::BOOT_CONTRACTS_DATA, test_fixtures::clarity_contract::ClarityContractBuilder, }; @@ -1207,6 +1202,14 @@ mod tests { vm::{self, types::TupleData, ClarityVersion}, }; + #[track_caller] + fn get_interpreter(settings: Option) -> ClarityInterpreter { + ClarityInterpreter::new( + StandardPrincipalData::transient(), + settings.unwrap_or_default(), + ) + } + #[track_caller] fn deploy_contract( interpreter: &mut ClarityInterpreter, @@ -1259,8 +1262,7 @@ mod tests { #[test] fn test_get_tx_sender() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let tx_sender = StandardPrincipalData::transient(); interpreter.set_tx_sender(tx_sender.clone()); assert_eq!(interpreter.get_tx_sender(), tx_sender); @@ -1268,8 +1270,7 @@ mod tests { #[test] fn test_set_tx_sender() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let addr = StacksAddress::from_string("ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5").unwrap(); let tx_sender = StandardPrincipalData::from(addr); @@ -1279,23 +1280,20 @@ mod tests { #[test] fn test_get_block_time() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let bt = interpreter.get_block_time(); assert_ne!(bt, 0); // TODO placeholder } #[test] fn test_get_block_height() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); assert_eq!(interpreter.get_block_height(), 0); } #[test] fn test_advance_stacks_chain_tip_pre_epoch_3() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); interpreter.set_current_epoch(StacksEpochId::Epoch2_05); let count = 5; let initial_block_height = interpreter.get_burn_block_height(); @@ -1308,11 +1306,11 @@ mod tests { fn test_advance_stacks_chain_tip() { let wasm_settings = Settings { analysis: AnalysisSettings::default(), + remote_data: RemoteDataSettings::default(), clarity_wasm_mode: true, show_timings: false, }; - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), wasm_settings); + let mut interpreter = get_interpreter(Some(wasm_settings)); interpreter.set_current_epoch(StacksEpochId::Epoch30); interpreter.advance_burn_chain_tip(1); let count = 5; @@ -1327,8 +1325,7 @@ mod tests { #[test] fn test_advance_chain_tip_pre_epoch3() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); interpreter.set_current_epoch(StacksEpochId::Epoch2_05); let count = 5; let initial_block_height = interpreter.get_block_height(); @@ -1342,8 +1339,7 @@ mod tests { #[test] fn test_advance_chain_tip() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); interpreter.set_current_epoch(StacksEpochId::Epoch30); let count = 5; let initial_block_height = interpreter.get_block_height(); @@ -1357,8 +1353,7 @@ mod tests { #[test] fn test_get_assets_maps() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let addr = "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5"; let amount = 1000; interpreter.credit_token(addr.into(), "STX".into(), amount); @@ -1375,8 +1370,7 @@ mod tests { #[test] fn test_get_tokens() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let addr = "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5"; interpreter.credit_token(addr.into(), "STX".into(), 1000); @@ -1386,8 +1380,7 @@ mod tests { #[test] fn test_get_accounts() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let addr = "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5"; interpreter.credit_token(addr.into(), "STX".into(), 1000); @@ -1397,8 +1390,7 @@ mod tests { #[test] fn test_get_balance_for_account() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let addr = "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5"; let amount = 1000; @@ -1410,8 +1402,7 @@ mod tests { #[test] fn test_credit_any_token() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let addr = "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5"; let amount = 1000; @@ -1423,8 +1414,7 @@ mod tests { #[test] fn test_mint_stx_balance() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let recipient = PrincipalData::Standard(StandardPrincipalData::transient()); let amount = 1000; @@ -1437,8 +1427,7 @@ mod tests { #[test] fn test_run_valid_contract() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let contract = ClarityContract::fixture(); let result = interpreter.run_interpreter(&contract, None, false, None); assert!(result.is_ok()); @@ -1447,8 +1436,7 @@ mod tests { #[test] fn test_run_invalid_contract() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let snippet = "(define-public (add) (ok (+ u1 1)))"; // ^ should be uint @@ -1463,8 +1451,7 @@ mod tests { #[test] fn test_run_runtime_error() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let snippet = "(/ u1 u0)"; let contract = ClarityContractBuilder::default() @@ -1490,8 +1477,7 @@ mod tests { #[test] fn test_build_ast() { - let interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let interpreter = get_interpreter(None); let contract = ClarityContract::fixture(); let (_ast, diagnostics, success) = interpreter.build_ast(&contract); assert!(success); @@ -1500,8 +1486,7 @@ mod tests { #[test] fn test_execute() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let contract = ClarityContract::fixture(); let result = deploy_contract(&mut interpreter, &contract); @@ -1517,8 +1502,7 @@ mod tests { #[test] fn test_call_contract_fn() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let contract = ClarityContract::fixture(); let source = contract.expect_in_memory_code_source(); @@ -1542,8 +1526,7 @@ mod tests { #[test] fn test_run_both() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let contract = ClarityContract::fixture(); let _ = deploy_contract(&mut interpreter, &contract); @@ -1556,8 +1539,7 @@ mod tests { #[test] fn test_get_data_var() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let contract = ClarityContractBuilder::default() .code_source(["(define-data-var count uint u9)"].join("\n")) .build(); @@ -1579,8 +1561,7 @@ mod tests { #[test] fn test_get_map_entry() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let contract = ClarityContractBuilder::default() .code_source( [ @@ -1606,8 +1587,7 @@ mod tests { #[test] fn test_execute_stx_events() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let account = PrincipalData::parse("S1G2081040G2081040G2081040G208105NK8PE5").unwrap(); let _ = interpreter.mint_stx_balance(account, 100000); @@ -1657,8 +1637,7 @@ mod tests { #[test] fn test_execute_ft_events() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let contract = ClarityContractBuilder::default() .code_source( @@ -1703,8 +1682,7 @@ mod tests { #[test] fn test_execute_nft_events() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let contract = ClarityContractBuilder::default() .code_source( @@ -1758,8 +1736,7 @@ mod tests { clarity_wasm_mode: false, ..Default::default() }; - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), repl_settings); + let mut interpreter = get_interpreter(Some(repl_settings)); let boot_contracts_data = BOOT_CONTRACTS_DATA.clone(); @@ -1777,8 +1754,7 @@ mod tests { #[test] fn block_height_support_in_clarity2_epoch2() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let snippet = [ "(define-read-only (get-height)", @@ -1847,8 +1823,7 @@ mod tests { #[test] fn block_height_support_in_clarity2_epoch3() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); interpreter.advance_burn_chain_tip(1); @@ -1900,8 +1875,7 @@ mod tests { #[test] fn block_height_support_in_clarity3_epoch3() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); interpreter.advance_burn_chain_tip(1); @@ -1989,8 +1963,7 @@ mod tests { #[test] fn burn_block_time_is_realistic_in_epoch_3_0() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); interpreter.set_current_epoch(StacksEpochId::Epoch30); interpreter.advance_burn_chain_tip(3); @@ -2019,8 +1992,7 @@ mod tests { #[test] fn first_stacks_block_time_in_a_tenure() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); interpreter.set_current_epoch(StacksEpochId::Epoch30); let _ = interpreter.advance_burn_chain_tip(2); @@ -2049,8 +2021,7 @@ mod tests { #[test] fn stacks_block_time_is_realistic_in_epoch_3_0() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); interpreter.set_current_epoch(StacksEpochId::Epoch30); let _ = interpreter.advance_stacks_chain_tip(3); @@ -2079,8 +2050,7 @@ mod tests { #[test] fn burn_block_time_after_many_stacks_blocks_is_realistic_in_epoch_3_0() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); interpreter.set_current_epoch(StacksEpochId::Epoch30); // by advancing stacks_chain_tip by 101, we are getting a tenure of more than 600 seconds @@ -2137,8 +2107,7 @@ mod tests { #[test] fn can_call_a_public_function() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let contract = ClarityContractBuilder::default() .code_source("(define-public (public-func) (ok true))".into()) @@ -2168,8 +2137,7 @@ mod tests { #[test] fn can_call_a_private_function() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let contract = ClarityContractBuilder::default() .code_source("(define-private (private-func) true)".into()) @@ -2199,8 +2167,7 @@ mod tests { #[test] fn can_not_call_a_private_function_without_allow_private() { - let mut interpreter = - ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + let mut interpreter = get_interpreter(None); let contract = ClarityContractBuilder::default() .code_source("(define-private (private-func) true)".into()) diff --git a/components/clarity-repl/src/repl/mod.rs b/components/clarity-repl/src/repl/mod.rs index 516881a08..631d55a0a 100644 --- a/components/clarity-repl/src/repl/mod.rs +++ b/components/clarity-repl/src/repl/mod.rs @@ -3,6 +3,7 @@ pub mod clarity_values; pub mod datastore; pub mod diagnostic; pub mod interpreter; +pub mod remote_data; pub mod session; pub mod settings; pub mod tracer; diff --git a/components/clarity-repl/src/repl/remote_data.rs b/components/clarity-repl/src/repl/remote_data.rs new file mode 100644 index 000000000..665c24293 --- /dev/null +++ b/components/clarity-repl/src/repl/remote_data.rs @@ -0,0 +1,448 @@ +use clarity::types::{ + chainstate::{ + BlockHeaderHash, BurnchainHeaderHash, ConsensusHash, SortitionId, StacksBlockId, VRFSeed, + }, + StacksEpochId, +}; +use clarity::vm::errors::InterpreterResult; +use serde::de::Error as SerdeError; +use serde::{de::DeserializeOwned, Deserialize, Deserializer}; + +#[cfg(target_arch = "wasm32")] +use js_sys::JsString; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; +#[cfg(target_arch = "wasm32")] +#[wasm_bindgen] +#[derive(Deserialize)] +struct JsHttpClientResponse { + status: u16, + body: Vec, +} + +#[cfg(target_arch = "wasm32")] +#[wasm_bindgen(module = "/js/index.mjs")] +extern "C" { + #[wasm_bindgen(js_name = httpClient)] + fn http_client(method: &JsString, path: &JsString) -> JsValue; +} + +use crate::repl::settings::ApiUrl; + +pub const MAINNET_20_START_HEIGHT: u32 = 1; +pub const MAINNET_2_05_START_HEIGHT: u32 = 40_607; +pub const MAINNET_21_START_HEIGHT: u32 = 99_113; +pub const MAINNET_22_START_HEIGHT: u32 = 103_900; +pub const MAINNET_23_START_HEIGHT: u32 = 104_359; +pub const MAINNET_24_START_HEIGHT: u32 = 107_055; +pub const MAINNET_25_START_HEIGHT: u32 = 147_290; +pub const MAINNET_30_START_HEIGHT: u32 = 171_833; +pub const MAINNET_31_START_HEIGHT: u32 = 340_555; + +// the current primary testnet starts directly in epoch 2.5 (pox-4 deployment) +pub const TESTNET_20_START_HEIGHT: u32 = 1; +pub const TESTNET_2_05_START_HEIGHT: u32 = 1; +pub const TESTNET_21_START_HEIGHT: u32 = 1; +pub const TESTNET_22_START_HEIGHT: u32 = 1; +pub const TESTNET_23_START_HEIGHT: u32 = 1; +pub const TESTNET_24_START_HEIGHT: u32 = 1; +pub const TESTNET_25_START_HEIGHT: u32 = 1; +pub const TESTNET_30_START_HEIGHT: u32 = 320; +pub const TESTNET_31_START_HEIGHT: u32 = 814; + +pub fn epoch_for_height(is_mainnet: bool, height: u32) -> StacksEpochId { + if is_mainnet { + epoch_for_mainnet_height(height) + } else { + epoch_for_testnet_height(height) + } +} + +fn epoch_for_mainnet_height(height: u32) -> StacksEpochId { + if height < MAINNET_2_05_START_HEIGHT { + StacksEpochId::Epoch20 + } else if height < MAINNET_21_START_HEIGHT { + StacksEpochId::Epoch2_05 + } else if height < MAINNET_22_START_HEIGHT { + StacksEpochId::Epoch21 + } else if height < MAINNET_23_START_HEIGHT { + StacksEpochId::Epoch22 + } else if height < MAINNET_24_START_HEIGHT { + StacksEpochId::Epoch23 + } else if height < MAINNET_25_START_HEIGHT { + StacksEpochId::Epoch24 + } else if height < MAINNET_30_START_HEIGHT { + StacksEpochId::Epoch25 + } else if height < MAINNET_31_START_HEIGHT { + StacksEpochId::Epoch30 + } else { + StacksEpochId::Epoch31 + } +} + +fn epoch_for_testnet_height(height: u32) -> StacksEpochId { + if height < TESTNET_2_05_START_HEIGHT { + StacksEpochId::Epoch20 + } else if height < TESTNET_21_START_HEIGHT { + StacksEpochId::Epoch2_05 + } else if height < TESTNET_22_START_HEIGHT { + StacksEpochId::Epoch21 + } else if height < TESTNET_23_START_HEIGHT { + StacksEpochId::Epoch22 + } else if height < TESTNET_24_START_HEIGHT { + StacksEpochId::Epoch23 + } else if height < TESTNET_25_START_HEIGHT { + StacksEpochId::Epoch24 + } else if height < TESTNET_30_START_HEIGHT { + StacksEpochId::Epoch25 + } else if height < TESTNET_31_START_HEIGHT { + StacksEpochId::Epoch30 + } else { + StacksEpochId::Epoch31 + } +} + +#[derive(Deserialize)] +pub struct ClarityDataResponse { + pub data: String, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct Info { + pub network_id: u32, + pub stacks_tip_height: u32, +} + +fn deserialize_burnchain_header_hash<'de, D>( + deserializer: D, +) -> Result +where + D: Deserializer<'de>, +{ + let s: String = String::deserialize(deserializer)?; + BurnchainHeaderHash::from_hex(s.trim_start_matches("0x")).map_err(SerdeError::custom) +} + +fn deserialize_consensus_hash<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let s: String = String::deserialize(deserializer)?; + ConsensusHash::from_hex(s.trim_start_matches("0x")).map_err(SerdeError::custom) +} + +fn deserialize_sortition_id<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let s: String = String::deserialize(deserializer)?; + SortitionId::from_hex(s.trim_start_matches("0x")).map_err(SerdeError::custom) +} + +fn deserialize_stacks_block_id<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let s: String = String::deserialize(deserializer)?; + StacksBlockId::from_hex(s.trim_start_matches("0x")).map_err(SerdeError::custom) +} + +fn deserialize_block_header_hash<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let s: String = String::deserialize(deserializer)?; + BlockHeaderHash::from_hex(s.trim_start_matches("0x")).map_err(SerdeError::custom) +} + +fn deserialize_vrf_seed<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let s: Option = Option::deserialize(deserializer)?; + match s { + Some(s) => VRFSeed::from_hex(s.trim_start_matches("0x")) + .map_err(SerdeError::custom) + .map(Some), + None => Ok(None), + } +} + +#[derive(Clone, Debug, Deserialize)] +pub struct Sortition { + #[serde(deserialize_with = "deserialize_burnchain_header_hash")] + pub burn_block_hash: BurnchainHeaderHash, + pub burn_block_height: u32, + #[serde(deserialize_with = "deserialize_consensus_hash")] + pub consensus_hash: ConsensusHash, + #[serde(deserialize_with = "deserialize_sortition_id")] + pub sortition_id: SortitionId, + #[serde(deserialize_with = "deserialize_sortition_id")] + pub parent_sortition_id: SortitionId, + // @todo: remove serde(default) and Option<> when stacks-network#5772 is merged + #[serde(default, deserialize_with = "deserialize_vrf_seed")] + pub vrf_seed: Option, +} + +#[derive(Clone, Debug, Deserialize)] +#[allow(dead_code)] +pub struct Block { + pub height: u32, + pub burn_block_height: u32, + pub tenure_height: u32, + pub block_time: u64, + pub burn_block_time: u64, + #[serde(deserialize_with = "deserialize_block_header_hash")] + pub hash: BlockHeaderHash, + #[serde(deserialize_with = "deserialize_stacks_block_id")] + pub index_block_hash: StacksBlockId, + #[serde(deserialize_with = "deserialize_burnchain_header_hash")] + pub burn_block_hash: BurnchainHeaderHash, +} + +#[derive(Clone, Debug)] +pub struct HttpClient { + url: ApiUrl, + #[allow(dead_code)] + api_key: Option, +} + +impl HttpClient { + pub fn new(url: ApiUrl) -> Self { + #[cfg(not(target_arch = "wasm32"))] + let api_key = std::env::var("HIRO_API_KEY").ok(); + #[cfg(target_arch = "wasm32")] + let api_key = None; + + HttpClient { url, api_key } + } + + #[cfg(not(target_arch = "wasm32"))] + fn get(&self, path: &str) -> Result { + let url = format!("{}{}", self.url, path); + let client = reqwest::blocking::Client::new(); + let mut request = client.get(&url).header("x-hiro-product", "clarinet-cli"); + if let Some(ref api_key) = self.api_key { + request = request.header("x-api-key", api_key); + } + let response = request.send().map_err(|e| e.to_string())?; + if response.status() != 200 { + return Err(format!( + "http error - status: {} - message: {}", + response.status(), + response.text().unwrap() + )); + } + response.json::().map_err(|e| e.to_string()) + } + + #[cfg(target_arch = "wasm32")] + fn get(&self, path: &str) -> Result { + let url = JsString::from(format!("{}{}", self.url, path)); + let raw_response = http_client(&JsString::from("GET"), &url); + let response: JsHttpClientResponse = + serde_wasm_bindgen::from_value(raw_response).map_err(|e| e.to_string())?; + let body = std::str::from_utf8(&response.body).unwrap(); + if response.status != 200 { + return Err(format!( + "http error - status: {} - message: {}", + response.status, body + )); + } + serde_json::from_str::(body).map_err(|e| e.to_string()) + } + + pub fn fetch_info(&self) -> Info { + self.get::("/v2/info").unwrap() + } + + pub fn fetch_sortition(&self, burn_block_hash: &BurnchainHeaderHash) -> Sortition { + let url = format!("/v3/sortitions/burn/{}", burn_block_hash); + let sortitions = self.get::>(&url).unwrap(); + sortitions.into_iter().next().unwrap() + } + + pub fn fetch_block(&self, url: &str) -> Block { + self.get::(url).unwrap() + } + + pub fn fetch_clarity_data(&self, path: &str) -> InterpreterResult> { + match self.get::(path) { + Ok(data) => Ok(Some(data.data.trim_start_matches("0x").to_string())), + Err(_) => Ok(None), + } + } +} +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_http_client_fetch_info() { + let mut server = mockito::Server::new(); + let _ = server + .mock("GET", "/v2/info") + .with_status(200) + .with_header("content-type", "application/json") + .with_body(r#"{ + "peer_version": 402653196, + "pox_consensus": "0ce291b675bb0148b435a884e250aafc3fd6bc86", + "burn_block_height": 882262, + "stable_pox_consensus": "f517f5aced5be836f9fe10980ff06108a6a2acec", + "stable_burn_block_height": 882255, + "network_id": 1, + "parent_network_id": 3652501241, + "stacks_tip_height": 556946, + "stacks_tip": "70526983b920b31d5e0d65750033a4dc2f328f31a3ffeb1f8780bfb164d50502", + "stacks_tip_consensus_hash": "0ce291b675bb0148b435a884e250aafc3fd6bc86", + "genesis_chainstate_hash": "74237aa39aa50a83de11a4f53e9d3bb7d43461d1de9873f402e5453ae60bc59b", + "unanchored_tip": null, + "unanchored_seq": null, + "tenure_height": 184037, + "is_fully_synced": true, + "node_public_key": "02e0ce39375d699d164f90cc815427943c5acccca02069e394f9ed28d2c2bca317", + "node_public_key_hash": "d5b1f3c7f9b2ffa8ac610170d1352550d240197c", + "stackerdbs": [] + }"#) + .create(); + + let client = HttpClient::new(ApiUrl(server.url())); + let info = client.fetch_info(); + assert_eq!(info.network_id, 1); + assert_eq!(info.stacks_tip_height, 556946); + } + + #[test] + fn test_http_client_fetch_sortition() { + let mut server = mockito::Server::new(); + + let _ = server + .mock("GET", "/v3/sortitions/burn/000000000000000000012f34a6727bf7dc9ceae203022cb14a3b37fe8de0e6ad") + .with_status(200) + .with_header("content-type", "application/json") + .with_body( + r#"[{ + "burn_block_hash": "0x000000000000000000012f34a6727bf7dc9ceae203022cb14a3b37fe8de0e6ad", + "burn_block_height": 882262, + "burn_header_timestamp": 1738666756, + "sortition_id": "0x6e79b604db6d97b9289f04f446e78ec871a6b16972b02674bc3ea2bdec200fb9", + "parent_sortition_id": "0x8b2dedebf5b8c72c1e8ede00abd1f417d755ac7f513dbf3c3d007494404115d3", + "consensus_hash": "0x0ce291b675bb0148b435a884e250aafc3fd6bc86", + "was_sortition": true, + "miner_pk_hash160": "0x37e79a837b4071a1fc6c1b49208e7d2141a25905", + "stacks_parent_ch": "0xcd18600459e4da24ede6662cc4df6bcece61b5f9", + "last_sortition_ch": "0xcd18600459e4da24ede6662cc4df6bcece61b5f9", + "committed_block_hash": "0xbf7e26ee22b18461dfed70cc114372a0f8a61249de2f20b120e6fe63da5a45e4" + }]"#, + ) + .create(); + + let client = HttpClient::new(ApiUrl(server.url())); + let sortition = client.fetch_sortition( + &BurnchainHeaderHash::from_hex( + "000000000000000000012f34a6727bf7dc9ceae203022cb14a3b37fe8de0e6ad", + ) + .unwrap(), + ); + + assert_eq!(sortition.burn_block_height, 882262); + assert_eq!( + sortition.consensus_hash, + ConsensusHash::from_hex("0ce291b675bb0148b435a884e250aafc3fd6bc86").unwrap() + ); + assert_eq!( + sortition.sortition_id, + SortitionId::from_hex( + "6e79b604db6d97b9289f04f446e78ec871a6b16972b02674bc3ea2bdec200fb9" + ) + .unwrap() + ); + assert_eq!( + sortition.parent_sortition_id, + SortitionId::from_hex( + "8b2dedebf5b8c72c1e8ede00abd1f417d755ac7f513dbf3c3d007494404115d3" + ) + .unwrap() + ); + } + + #[test] + fn test_http_client_fetch_block() { + let mut server = mockito::Server::new(); + let _ = server + .mock("GET", "/extended/v2/blocks/556946") + .with_status(200) + .with_header("content-type", "application/json") + .with_body( + r#"{ + "canonical": true, + "height": 556946, + "hash": "0x70526983b920b31d5e0d65750033a4dc2f328f31a3ffeb1f8780bfb164d50502", + "block_time": 1738667305, + "block_time_iso": "2025-02-04T11:08:25.000Z", + "tenure_height": 184037, + "index_block_hash": "0xa246be7256de49aa6923074a53507a839b2ba356f8809f8e7448c87b5c1891e9", + "parent_block_hash": "0x06dd38d5315c133b08cefdedb5c51f2e91fd8a0474e07b3d1a740c19bc21842e", + "parent_index_block_hash": "0x1d39f5eb45aa0e78cc256ea6ed180dcb9e8c87bea11ecf8102ef9bced5f3f73b", + "burn_block_time": 1738666756, + "burn_block_time_iso": "2025-02-04T10:59:16.000Z", + "burn_block_hash": "0x000000000000000000012f34a6727bf7dc9ceae203022cb14a3b37fe8de0e6ad", + "burn_block_height": 882262, + "miner_txid": "0x2ca4c7f6d36f32f3c2f1c5ebae3816690a9ba2258c38ef6a4494d315873a0448", + "tx_count": 1, + "execution_cost_read_count": 0, + "execution_cost_read_length": 0, + "execution_cost_runtime": 0, + "execution_cost_write_count": 0, + "execution_cost_write_length": 0 + }"#, + ) + .create(); + + let client = HttpClient::new(ApiUrl(server.url())); + let block = client.fetch_block("/extended/v2/blocks/556946"); + assert_eq!(block.height, 556946); + assert_eq!(block.burn_block_height, 882262); + assert_eq!(block.tenure_height, 184037); + assert_eq!(block.block_time, 1738667305); + assert_eq!(block.burn_block_time, 1738666756); + assert_eq!( + block.hash, + BlockHeaderHash::from_hex( + "70526983b920b31d5e0d65750033a4dc2f328f31a3ffeb1f8780bfb164d50502" + ) + .unwrap() + ); + assert_eq!( + block.index_block_hash, + StacksBlockId::from_hex( + "a246be7256de49aa6923074a53507a839b2ba356f8809f8e7448c87b5c1891e9" + ) + .unwrap() + ); + assert_eq!( + block.burn_block_hash, + BurnchainHeaderHash::from_hex( + "000000000000000000012f34a6727bf7dc9ceae203022cb14a3b37fe8de0e6ad" + ) + .unwrap() + ); + } + + // we should better handle network errors. tracked in #1646 + #[test] + #[should_panic] + fn test_http_client_error() { + let mut server = mockito::Server::new(); + let _ = server + .mock("GET", "/extended/v2/blocks/9999999999999") + .with_status(404) + .with_header("content-type", "application/json") + .with_body(r#"{"statusCode":404,"error":"Not Found","message":"Block not found"}"#) + .create(); + + let client = HttpClient::new(ApiUrl(server.url())); + + let _block = client.fetch_block("/extended/v2/blocks/9999999999999"); + } +} diff --git a/components/clarity-repl/src/repl/session.rs b/components/clarity-repl/src/repl/session.rs index 9af5eb058..5225e235f 100644 --- a/components/clarity-repl/src/repl/session.rs +++ b/components/clarity-repl/src/repl/session.rs @@ -96,7 +96,6 @@ pub struct CostsReport { #[derive(Clone, Debug)] pub struct Session { pub settings: SessionSettings, - pub current_epoch: StacksEpochId, pub contracts: BTreeMap, pub interpreter: ClarityInterpreter, api_reference: HashMap, @@ -120,7 +119,6 @@ impl Session { Self { interpreter: ClarityInterpreter::new(tx_sender, settings.repl_settings.clone()), - current_epoch: settings.epoch_id.unwrap_or(StacksEpochId::Epoch2_05), contracts: BTreeMap::new(), api_reference: build_api_reference(), show_costs: false, @@ -551,12 +549,13 @@ impl Session { cost_track: bool, ast: Option<&ContractAST>, ) -> Result> { - if contract.epoch != self.current_epoch { + let current_epoch = self.interpreter.datastore.get_current_epoch(); + if contract.epoch != current_epoch { let diagnostic = Diagnostic { level: Level::Error, message: format!( "contract epoch ({}) does not match current epoch ({})", - contract.epoch, self.current_epoch + contract.epoch, current_epoch ), spans: vec![], suggestion: None, @@ -620,12 +619,13 @@ impl Session { hooks.push(coverage_hook); } + let current_epoch = self.interpreter.datastore.get_current_epoch(); let execution = match self.interpreter.call_contract_fn( &QualifiedContractIdentifier::parse(&contract_id_str).unwrap(), method, args, - self.current_epoch, - ClarityVersion::default_for_epoch(self.current_epoch), + current_epoch, + ClarityVersion::default_for_epoch(current_epoch), track_costs, allow_private, hooks, @@ -651,12 +651,13 @@ impl Session { snippet: String, cost_track: bool, ) -> Result> { + let current_epoch = self.interpreter.datastore.get_current_epoch(); let contract = ClarityContract { code_source: ClarityCodeSource::ContractInMemory(snippet), name: format!("contract-{}", self.contracts.len()), deployer: ContractDeployer::DefaultDeployer, - clarity_version: ClarityVersion::default_for_epoch(self.current_epoch), - epoch: self.current_epoch, + clarity_version: ClarityVersion::default_for_epoch(current_epoch), + epoch: current_epoch, }; let contract_identifier = contract.expect_resolved_contract_identifier(Some(&self.interpreter.get_tx_sender())); @@ -690,12 +691,13 @@ impl Session { eval_hooks: Option>, cost_track: bool, ) -> Result> { + let current_epoch = self.interpreter.datastore.get_current_epoch(); let contract = ClarityContract { code_source: ClarityCodeSource::ContractInMemory(snippet), name: format!("contract-{}", self.contracts.len()), deployer: ContractDeployer::DefaultDeployer, - clarity_version: ClarityVersion::default_for_epoch(self.current_epoch), - epoch: self.current_epoch, + clarity_version: ClarityVersion::default_for_epoch(current_epoch), + epoch: current_epoch, }; let contract_identifier = contract.expect_resolved_contract_identifier(Some(&self.interpreter.get_tx_sender())); @@ -999,7 +1001,8 @@ impl Session { } pub fn get_epoch(&mut self) -> String { - format!("Current epoch: {}", self.current_epoch) + let current_epoch = self.interpreter.datastore.get_current_epoch(); + format!("Current epoch: {}", current_epoch) } pub fn set_epoch(&mut self, cmd: &str) -> String { @@ -1024,7 +1027,6 @@ impl Session { } pub fn update_epoch(&mut self, epoch: StacksEpochId) { - self.current_epoch = epoch; self.interpreter.set_current_epoch(epoch); if epoch >= StacksEpochId::Epoch30 { self.interpreter.set_tenure_height(); @@ -1660,6 +1662,10 @@ mod tests { .1[0], "u1".green().to_string() ); + + assert_eq!(session.process_console_input("(at-block (unwrap-panic (get-block-info? id-header-hash u0)) burn-block-height)").1[0], "u0".green().to_string()); + assert_eq!(session.process_console_input("(at-block (unwrap-panic (get-block-info? id-header-hash u5000)) burn-block-height)").1[0], "u4999".green().to_string()); + assert_eq!(session.process_console_input("(at-block (unwrap-panic (get-block-info? id-header-hash u0)) (contract-call? .contract get-x))").1[0], "u0".green().to_string()); assert_eq!(session.process_console_input("(at-block (unwrap-panic (get-block-info? id-header-hash u5000)) (contract-call? .contract get-x))").1[0], "u0".green().to_string()); @@ -1682,6 +1688,13 @@ mod tests { "u2".green().to_string() ); assert_eq!(session.process_console_input("(at-block (unwrap-panic (get-block-info? id-header-hash u10000)) (contract-call? .contract get-x))").1[0], "u1".green().to_string()); + + session.handle_command("::set_epoch 3.1"); + + let _ = session.advance_burn_chain_tip(10000); + + assert_eq!(session.process_console_input("(at-block (unwrap-panic (get-stacks-block-info? id-header-hash u19000)) burn-block-height)").1[0], "u18999".green().to_string()); + assert_eq!(session.process_console_input("(at-block (unwrap-panic (get-stacks-block-info? id-header-hash u20000)) burn-block-height)").1[0], "u19999".green().to_string()); } #[test] @@ -1744,8 +1757,6 @@ mod tests { let contract = ClarityContractBuilder::default().build(); let _ = session.deploy_contract(&contract, false, None); - dbg!(&contract); - let result = session.call_contract_fn( "contract", "incr", diff --git a/components/clarity-repl/src/repl/settings.rs b/components/clarity-repl/src/repl/settings.rs index 3ef9feaf9..3ab504e25 100644 --- a/components/clarity-repl/src/repl/settings.rs +++ b/components/clarity-repl/src/repl/settings.rs @@ -1,10 +1,15 @@ use std::convert::TryInto; +use std::fmt; +use std::str::FromStr; -use crate::analysis; use clarity::types::chainstate::StacksAddress; use clarity::types::StacksEpochId; use clarity::vm::types::{PrincipalData, QualifiedContractIdentifier, StandardPrincipalData}; +use crate::analysis; + +use super::remote_data::HttpClient; + #[derive(Clone, Debug)] pub struct InitialContract { pub code: String, @@ -40,22 +45,42 @@ pub struct Account { #[derive(Clone, Debug, Default)] pub struct SessionSettings { - pub node: String, pub include_boot_contracts: Vec, pub include_costs: bool, - pub initial_contracts: Vec, pub initial_accounts: Vec, pub initial_deployer: Option, - pub scoping_contract: Option, - pub lazy_initial_contracts_interpretation: bool, pub disk_cache_enabled: bool, pub repl_settings: Settings, pub epoch_id: Option, } +#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)] +pub struct ApiUrl(pub String); +impl Default for ApiUrl { + fn default() -> Self { + ApiUrl("https://api.hiro.so".to_string()) + } +} + +impl FromStr for ApiUrl { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(ApiUrl(s.to_string())) + } +} + +impl fmt::Display for ApiUrl { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + #[derive(Debug, Default, Clone, Deserialize, Serialize)] pub struct Settings { pub analysis: analysis::Settings, + #[serde(skip_serializing)] + pub remote_data: RemoteDataSettings, #[serde(skip_serializing, skip_deserializing)] pub clarity_wasm_mode: bool, #[serde(skip_serializing, skip_deserializing)] @@ -64,20 +89,87 @@ pub struct Settings { #[derive(Debug, Default, Clone, Deserialize, Serialize)] pub struct SettingsFile { - pub analysis: Option, + analysis: Option, + remote_data: Option, } impl From for Settings { fn from(file: SettingsFile) -> Self { - let analysis = if let Some(analysis) = file.analysis { - analysis::Settings::from(analysis) - } else { - analysis::Settings::default() - }; + let analysis = file + .analysis + .map(analysis::Settings::from) + .unwrap_or_default(); + + let remote_data = file + .remote_data + .map(RemoteDataSettings::from) + .unwrap_or_default(); + Self { analysis, + remote_data, clarity_wasm_mode: false, show_timings: false, } } } + +#[derive(Debug, Default, Clone, Deserialize, Serialize)] +pub struct RemoteDataSettingsFile { + enabled: Option, + api_url: Option, + initial_height: Option, +} + +#[derive(Debug, Default, Clone, Deserialize, Serialize)] +pub struct RemoteDataSettings { + pub enabled: bool, + pub api_url: ApiUrl, + pub initial_height: Option, +} + +#[derive(Debug, Default, Clone, Deserialize, Serialize)] +pub struct RemoteNetworkInfo { + pub api_url: ApiUrl, + pub initial_height: u32, + pub network_id: u32, + pub stacks_tip_height: u32, + pub is_mainnet: bool, +} + +impl From for RemoteDataSettings { + fn from(file: RemoteDataSettingsFile) -> Self { + Self { + enabled: file.enabled.unwrap_or_default(), + api_url: file.api_url.unwrap_or_default(), + initial_height: file.initial_height, + } + } +} + +impl RemoteDataSettings { + pub fn get_initial_remote_network_info( + &self, + client: &HttpClient, + ) -> Result { + let info = client.fetch_info(); + + let initial_height = match self.initial_height { + Some(initial_height) => { + if initial_height > info.stacks_tip_height { + return Err("Initial height is greater than the current tip height".to_string()); + } + initial_height + } + None => info.stacks_tip_height, + }; + + Ok(RemoteNetworkInfo { + api_url: self.api_url.clone(), + initial_height, + network_id: info.network_id, + stacks_tip_height: info.stacks_tip_height, + is_mainnet: info.network_id == 1, + }) + } +} diff --git a/components/clarity-repl/src/uprint.rs b/components/clarity-repl/src/uprint.rs new file mode 100644 index 000000000..cf8cdc5a4 --- /dev/null +++ b/components/clarity-repl/src/uprint.rs @@ -0,0 +1,17 @@ +// universal print macro + +#[macro_export] +#[cfg(not(target_arch = "wasm32"))] +macro_rules! uprint { + ( $( $t:tt )* ) => { + println!($( $t )* ); + } +} + +#[cfg(target_arch = "wasm32")] +#[macro_export] +macro_rules! uprint { + ( $( $t:tt )* ) => { + web_sys::console::log_1(&format!( $( $t )* ).into()); + } +} diff --git a/components/clarity-repl/tests/session_with_remote_data.rs b/components/clarity-repl/tests/session_with_remote_data.rs new file mode 100644 index 000000000..4de0793a6 --- /dev/null +++ b/components/clarity-repl/tests/session_with_remote_data.rs @@ -0,0 +1,198 @@ +use clarity::{ + types::{ + chainstate::{BlockHeaderHash, StacksBlockId}, + StacksEpochId, + }, + vm::{EvaluationResult, Value}, +}; +use clarity_repl::repl::{ + settings::{ApiUrl, RemoteDataSettings}, + Session, SessionSettings, +}; + +#[track_caller] +fn eval_snippet(session: &mut Session, snippet: &str) -> Value { + let execution_res = session.eval(snippet.to_string(), false).unwrap(); + match execution_res.result { + EvaluationResult::Contract(_) => unreachable!(), + EvaluationResult::Snippet(res) => res.result, + } +} + +fn init_session(initial_heigth: u32) -> Session { + let mut settings = SessionSettings::default(); + settings.repl_settings.remote_data = RemoteDataSettings { + enabled: true, + api_url: ApiUrl("https://api.testnet.hiro.so".to_string()), + initial_height: Some(initial_heigth), + }; + Session::new(settings) +} + +// the counter contract is delpoyed on testnet at height #41613 +// the initial count value is 0 and is incremented by 1 at #56232 +const COUNTER_ADDR: &str = "STJCAB2T9TR2EJM7YS4DM2CGBBVTF7BV237Y8KNV.counter"; + +#[test] +fn it_starts_in_the_right_epoch() { + let session = init_session(42000); + assert_eq!( + session.interpreter.datastore.get_current_epoch(), + StacksEpochId::Epoch31 + ); +} + +#[test] +fn it_can_fetch_remote() { + // count at block 42000 is 0 + let mut session = init_session(42000); + let snippet = format!("(contract-call? '{} get-count)", COUNTER_ADDR); + let result = eval_snippet(&mut session, &snippet); + assert_eq!(result, Value::UInt(0)); + + // count at block 57000 is 1 + let mut session = init_session(57000); + let snippet = format!("(contract-call? '{} get-count)", COUNTER_ADDR); + let result = eval_snippet(&mut session, &snippet); + assert_eq!(result, Value::UInt(1)); +} + +#[test] +fn it_can_get_stacks_block_info() { + let mut session = init_session(57000); + + let snippet = "(get-stacks-block-info? id-header-hash u42000)"; + let result = eval_snippet(&mut session, snippet); + let hash = + StacksBlockId::from_hex("b4678e059aa9b82b1473597087876ef61a5c6a0c35910cd4b797201d6b423a07") + .unwrap(); + assert_eq!( + result, + Value::some(Value::buff_from(hash.to_bytes().to_vec()).unwrap()).unwrap() + ); + + let snippet = "(get-stacks-block-info? header-hash u42000)"; + let result = eval_snippet(&mut session, snippet); + let hash = BlockHeaderHash::from_hex( + "eabe9273e76a55384be01866e01a72564a1a1772aa1c2d578c4e918875575840", + ) + .unwrap(); + assert_eq!( + result, + Value::some(Value::buff_from(hash.to_bytes().to_vec()).unwrap()).unwrap() + ); + + let snippet = "(get-stacks-block-info? time u42000)"; + let result = eval_snippet(&mut session, snippet); + + assert_eq!(result, Value::some(Value::UInt(1736792439)).unwrap()); +} + +#[test] +fn it_can_fork_state() { + let mut session = init_session(57000); + let snippet_get_count = format!("(contract-call? '{} get-count)", COUNTER_ADDR); + + let result = eval_snippet(&mut session, &snippet_get_count); + assert_eq!(result, Value::UInt(1)); + + session.advance_burn_chain_tip(1); + let snippet = format!("(contract-call? '{} increment)", COUNTER_ADDR); + let _ = eval_snippet(&mut session, &snippet); + + let result = eval_snippet(&mut session, &snippet_get_count); + assert_eq!(result, Value::UInt(2)); +} + +#[test] +fn it_handles_at_block() { + let mut session = init_session(60000); + + // block 42000 hash + let hash = "0xb4678e059aa9b82b1473597087876ef61a5c6a0c35910cd4b797201d6b423a07"; + + let snippet = format!("(at-block {} stacks-block-height)", hash); + let result = eval_snippet(&mut session, &snippet); + assert_eq!(result, Value::UInt(42000)); + + let snippet_get_count_at_10k = format!( + "(contract-call? '{} get-count-at-block u10000)", + COUNTER_ADDR + ); + let result = eval_snippet(&mut session, &snippet_get_count_at_10k); + assert_eq!(result, Value::okay(Value::none()).unwrap()); + + let snippet_get_count_at_42k = format!( + "(contract-call? '{} get-count-at-block u42000)", + COUNTER_ADDR + ); + let result = eval_snippet(&mut session, &snippet_get_count_at_42k); + assert_eq!(result, Value::okay(Value::UInt(0)).unwrap()); + + let snippet_get_count_at_57k = format!( + "(contract-call? '{} get-count-at-block u57000)", + COUNTER_ADDR + ); + let result = eval_snippet(&mut session, &snippet_get_count_at_57k); + assert_eq!(result, Value::okay(Value::UInt(1)).unwrap()); +} + +#[test] +fn it_can_get_heights() { + let mut session = init_session(57000); + + let result = eval_snippet(&mut session, "stacks-block-height"); + assert_eq!(result, Value::UInt(57000)); + let result = eval_snippet(&mut session, "burn-block-height"); + assert_eq!(result, Value::UInt(6603)); + let result = eval_snippet(&mut session, "tenure-height"); + assert_eq!(result, Value::UInt(4251)); + + let hash = "0xb4678e059aa9b82b1473597087876ef61a5c6a0c35910cd4b797201d6b423a07"; + let snippet = format!("(at-block {hash} stacks-block-height)"); + let result = eval_snippet(&mut session, &snippet); + assert_eq!(result, Value::UInt(42000)); + let snippet = format!("(at-block {hash} burn-block-height)"); + let result = eval_snippet(&mut session, &snippet); + assert_eq!(result, Value::UInt(5560)); + let snippet = format!("(at-block {hash} tenure-height)"); + let result = eval_snippet(&mut session, &snippet); + assert_eq!(result, Value::UInt(3302)); +} + +#[test] +fn it_keeps_track_of_historical_data() { + let mut session = init_session(57000); + + let snippet = format!( + "(contract-call? '{} get-count-at-block u42000)", + COUNTER_ADDR + ); + let result = eval_snippet(&mut session, &snippet); + assert_eq!(result, Value::okay(Value::UInt(0)).unwrap()); + + let snippet = format!("(contract-call? '{} get-count)", COUNTER_ADDR); + let result = eval_snippet(&mut session, &snippet); + assert_eq!(result, Value::UInt(1)); +} + +#[test] +fn it_handles_chain_constants() { + let mut session = init_session(57000); + let result = eval_snippet(&mut session, "is-in-mainnet"); + assert_eq!(result, Value::Bool(false)); + let result = eval_snippet(&mut session, "chain-id"); + assert_eq!(result, Value::UInt(2147483648)); + + let mut settings = SessionSettings::default(); + settings.repl_settings.remote_data = RemoteDataSettings { + enabled: true, + api_url: ApiUrl("https://api.hiro.so".to_string()), + initial_height: Some(535000), + }; + let mut session = Session::new(settings); + let result = eval_snippet(&mut session, "is-in-mainnet"); + assert_eq!(result, Value::Bool(true)); + let result = eval_snippet(&mut session, "chain-id"); + assert_eq!(result, Value::UInt(1)); +} diff --git a/components/stacks-devnet-js/Cargo.toml b/components/stacks-devnet-js/Cargo.toml index d5f9b8f30..af54fac3f 100644 --- a/components/stacks-devnet-js/Cargo.toml +++ b/components/stacks-devnet-js/Cargo.toml @@ -9,6 +9,7 @@ exclude = ["index.node"] crate-type = ["cdylib"] [dependencies] +bip39 = { version = "1.0.1", default-features = false } serde = "1" error-chain = "0.12" clarinet-files = { path = "../clarinet-files" } diff --git a/components/stacks-devnet-js/src/lib.rs b/components/stacks-devnet-js/src/lib.rs index f00a64f58..57e8dcf52 100644 --- a/components/stacks-devnet-js/src/lib.rs +++ b/components/stacks-devnet-js/src/lib.rs @@ -4,8 +4,8 @@ extern crate error_chain; mod serde; +use bip39::{Language, Mnemonic}; use clarinet_deployments::{get_default_deployment_path, load_deployment}; -use clarinet_files::bip39::{Language, Mnemonic}; use clarinet_files::{ compute_addresses, AccountConfig, DevnetConfigFile, FileLocation, PoxStackingOrder, ProjectManifest, StacksNetwork, DEFAULT_DERIVATION_PATH, diff --git a/components/stacks-network/Cargo.toml b/components/stacks-network/Cargo.toml index 490dc11eb..c1a34caf7 100644 --- a/components/stacks-network/Cargo.toml +++ b/components/stacks-network/Cargo.toml @@ -25,7 +25,7 @@ chrono = "0.4.31" futures = "0.3.12" base58 = "0.2.0" tokio = { version = "1.35.1", features = ["full"] } -dirs = { version = "4.0.0" } +dirs = { version = "6.0.0" } clap = { version = "4.4.8", features = ["derive"] } serde_yaml = "0.8.23" diff --git a/package-lock.json b/package-lock.json index 42b4b4d53..1851ff246 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "name": "clarinet-sdk-workspace", "license": "GPL-3.0", "workspaces": [ + "components/clarity-repl/js", "components/clarinet-sdk-wasm/pkg-node", "components/clarinet-sdk-wasm/pkg-browser", "components/clarinet-sdk/common", @@ -14,7 +15,7 @@ "components/clarinet-sdk/browser" ], "devDependencies": { - "@types/node": "^20.4.5", + "@types/node": "22.12.0", "@types/prompts": "^2.4.5", "@types/yargs": "^17.0.24", "prettier": "^3.3.3", @@ -26,19 +27,25 @@ "components/clarinet-sdk-wasm/pkg-browser": { "name": "@hirosystems/clarinet-sdk-wasm-browser", "version": "2.12.0", - "license": "GPL-3.0" + "license": "GPL-3.0", + "dependencies": { + "sync-request": "6.1.0" + } }, "components/clarinet-sdk-wasm/pkg-node": { "name": "@hirosystems/clarinet-sdk-wasm", "version": "2.12.0", - "license": "GPL-3.0" + "license": "GPL-3.0", + "dependencies": { + "sync-request": "6.1.0" + } }, "components/clarinet-sdk/browser": { "name": "@hirosystems/clarinet-sdk-browser", "version": "2.12.0", "license": "GPL-3.0", "dependencies": { - "@hirosystems/clarinet-sdk-wasm-browser": "^2.12.0", + "@hirosystems/clarinet-sdk-wasm-browser": "2.12.0", "@stacks/transactions": "^6.13.0" } }, @@ -51,7 +58,7 @@ "version": "2.12.0", "license": "GPL-3.0", "dependencies": { - "@hirosystems/clarinet-sdk-wasm": "^2.12.0", + "@hirosystems/clarinet-sdk-wasm": "2.12.0", "@stacks/transactions": "^6.13.0", "kolorist": "^1.8.0", "prompts": "^2.4.2", @@ -70,6 +77,14 @@ "node": ">=18.0.0" } }, + "components/clarity-repl/js": { + "name": "@hirosystems/clarity-repl-js-lib", + "version": "1.0.0", + "license": "GPL-3.0", + "dependencies": { + "sync-request": "6.1.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -458,6 +473,10 @@ "resolved": "components/clarinet-sdk-wasm/pkg-browser", "link": true }, + "node_modules/@hirosystems/clarity-repl-js-lib": { + "resolved": "components/clarity-repl/js", + "link": true + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -581,9 +600,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz", - "integrity": "sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.2.tgz", + "integrity": "sha512-6Fyg9yQbwJR+ykVdT9sid1oc2ewejS6h4wzQltmJfSW53N60G/ah9pngXGANdy9/aaE/TcUFpWosdm7JXS1WTQ==", "cpu": [ "arm" ], @@ -594,9 +613,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.1.tgz", - "integrity": "sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.2.tgz", + "integrity": "sha512-K5GfWe+vtQ3kyEbihrimM38UgX57UqHp+oME7X/EX9Im6suwZfa7Hsr8AtzbJvukTpwMGs+4s29YMSO3rwWtsw==", "cpu": [ "arm64" ], @@ -607,9 +626,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.1.tgz", - "integrity": "sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.2.tgz", + "integrity": "sha512-PSN58XG/V/tzqDb9kDGutUruycgylMlUE59f40ny6QIRNsTEIZsrNQTJKUN2keMMSmlzgunMFqyaGLmly39sug==", "cpu": [ "arm64" ], @@ -620,9 +639,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.1.tgz", - "integrity": "sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.2.tgz", + "integrity": "sha512-gQhK788rQJm9pzmXyfBB84VHViDERhAhzGafw+E5mUpnGKuxZGkMVDa3wgDFKT6ukLC5V7QTifzsUKdNVxp5qQ==", "cpu": [ "x64" ], @@ -633,9 +652,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.1.tgz", - "integrity": "sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.2.tgz", + "integrity": "sha512-eiaHgQwGPpxLC3+zTAcdKl4VsBl3r0AiJOd1Um/ArEzAjN/dbPK1nROHrVkdnoE6p7Svvn04w3f/jEZSTVHunA==", "cpu": [ "arm64" ], @@ -646,9 +665,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.1.tgz", - "integrity": "sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.2.tgz", + "integrity": "sha512-lhdiwQ+jf8pewYOTG4bag0Qd68Jn1v2gO1i0mTuiD+Qkt5vNfHVK/jrT7uVvycV8ZchlzXp5HDVmhpzjC6mh0g==", "cpu": [ "x64" ], @@ -659,9 +678,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.1.tgz", - "integrity": "sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.2.tgz", + "integrity": "sha512-lfqTpWjSvbgQP1vqGTXdv+/kxIznKXZlI109WkIFPbud41bjigjNmOAAKoazmRGx+k9e3rtIdbq2pQZPV1pMig==", "cpu": [ "arm" ], @@ -672,9 +691,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.1.tgz", - "integrity": "sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.2.tgz", + "integrity": "sha512-RGjqULqIurqqv+NJTyuPgdZhka8ImMLB32YwUle2BPTDqDoXNgwFjdjQC59FbSk08z0IqlRJjrJ0AvDQ5W5lpw==", "cpu": [ "arm" ], @@ -685,9 +704,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.1.tgz", - "integrity": "sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.2.tgz", + "integrity": "sha512-ZvkPiheyXtXlFqHpsdgscx+tZ7hoR59vOettvArinEspq5fxSDSgfF+L5wqqJ9R4t+n53nyn0sKxeXlik7AY9Q==", "cpu": [ "arm64" ], @@ -698,9 +717,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.1.tgz", - "integrity": "sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.2.tgz", + "integrity": "sha512-UlFk+E46TZEoxD9ufLKDBzfSG7Ki03fo6hsNRRRHF+KuvNZ5vd1RRVQm8YZlGsjcJG8R252XFK0xNPay+4WV7w==", "cpu": [ "arm64" ], @@ -711,9 +730,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.28.1.tgz", - "integrity": "sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.2.tgz", + "integrity": "sha512-hJhfsD9ykx59jZuuoQgYT1GEcNNi3RCoEmbo5OGfG8RlHOiVS7iVNev9rhLKh7UBYq409f4uEw0cclTXx8nh8Q==", "cpu": [ "loong64" ], @@ -724,9 +743,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.1.tgz", - "integrity": "sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.2.tgz", + "integrity": "sha512-g/O5IpgtrQqPegvqopvmdCF9vneLE7eqYfdPWW8yjPS8f63DNam3U4ARL1PNNB64XHZDHKpvO2Giftf43puB8Q==", "cpu": [ "ppc64" ], @@ -737,9 +756,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.1.tgz", - "integrity": "sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.2.tgz", + "integrity": "sha512-bSQijDC96M6PuooOuXHpvXUYiIwsnDmqGU8+br2U7iPoykNi9JtMUpN7K6xml29e0evK0/g0D1qbAUzWZFHY5Q==", "cpu": [ "riscv64" ], @@ -750,9 +769,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.1.tgz", - "integrity": "sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.2.tgz", + "integrity": "sha512-49TtdeVAsdRuiUHXPrFVucaP4SivazetGUVH8CIxVsNsaPHV4PFkpLmH9LeqU/R4Nbgky9lzX5Xe1NrzLyraVA==", "cpu": [ "s390x" ], @@ -763,9 +782,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.1.tgz", - "integrity": "sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.2.tgz", + "integrity": "sha512-j+jFdfOycLIQ7FWKka9Zd3qvsIyugg5LeZuHF6kFlXo6MSOc6R1w37YUVy8VpAKd81LMWGi5g9J25P09M0SSIw==", "cpu": [ "x64" ], @@ -776,9 +795,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.1.tgz", - "integrity": "sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.2.tgz", + "integrity": "sha512-aDPHyM/D2SpXfSNCVWCxyHmOqN9qb7SWkY1+vaXqMNMXslZYnwh9V/UCudl6psyG0v6Ukj7pXanIpfZwCOEMUg==", "cpu": [ "x64" ], @@ -789,9 +808,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.1.tgz", - "integrity": "sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.2.tgz", + "integrity": "sha512-LQRkCyUBnAo7r8dbEdtNU08EKLCJMgAk2oP5H3R7BnUlKLqgR3dUjrLBVirmc1RK6U6qhtDw29Dimeer8d5hzQ==", "cpu": [ "arm64" ], @@ -802,9 +821,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.1.tgz", - "integrity": "sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.2.tgz", + "integrity": "sha512-wt8OhpQUi6JuPFkm1wbVi1BByeag87LDFzeKSXzIdGcX4bMLqORTtKxLoCbV57BHYNSUSOKlSL4BYYUghainYA==", "cpu": [ "ia32" ], @@ -815,9 +834,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.1.tgz", - "integrity": "sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.2.tgz", + "integrity": "sha512-rUrqINax0TvrPBXrFKg0YbQx18NpPN3NNrgmaao9xRNbTwek7lOXObhx8tQy8gelmQ/gLaGy1WptpU2eKJZImg==", "cpu": [ "x64" ], @@ -871,9 +890,9 @@ } }, "node_modules/@stacks/common/node_modules/@types/node": { - "version": "18.19.68", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.68.tgz", - "integrity": "sha512-QGtpFH1vB99ZmTa63K4/FU8twThj4fuVSBkGddTp7uIL/cuoLWIUSL2RcOaigBhfR+hg5pgGkBnkoOxrTVBMKw==", + "version": "18.19.75", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.75.tgz", + "integrity": "sha512-UIksWtThob6ZVSyxcOqCLOUNg/dyO1Qvx4McgeuhrEtHTLFTf7BBhEazaE4K806FGTPtzd/2sE90qn4fVr7cyw==", "license": "MIT", "dependencies": { "undici-types": "~5.26.4" @@ -904,9 +923,9 @@ } }, "node_modules/@stacks/encryption/node_modules/@types/node": { - "version": "18.19.68", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.68.tgz", - "integrity": "sha512-QGtpFH1vB99ZmTa63K4/FU8twThj4fuVSBkGddTp7uIL/cuoLWIUSL2RcOaigBhfR+hg5pgGkBnkoOxrTVBMKw==", + "version": "18.19.75", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.75.tgz", + "integrity": "sha512-UIksWtThob6ZVSyxcOqCLOUNg/dyO1Qvx4McgeuhrEtHTLFTf7BBhEazaE4K806FGTPtzd/2sE90qn4fVr7cyw==", "dev": true, "license": "MIT", "dependencies": { @@ -990,6 +1009,15 @@ "@types/node": "*" } }, + "node_modules/@types/concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -1020,6 +1048,15 @@ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "license": "MIT" }, + "node_modules/@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1029,12 +1066,12 @@ "peer": true }, "node_modules/@types/node": { - "version": "20.17.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.10.tgz", - "integrity": "sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==", + "version": "22.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.12.0.tgz", + "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==", "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.20.0" } }, "node_modules/@types/prompts": { @@ -1048,6 +1085,12 @@ "kleur": "^3.0.3" } }, + "node_modules/@types/qs": { + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", + "license": "MIT" + }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -1066,13 +1109,13 @@ "license": "MIT" }, "node_modules/@vitest/expect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", - "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.1.tgz", + "integrity": "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==", "license": "MIT", "dependencies": { - "@vitest/spy": "1.6.0", - "@vitest/utils": "1.6.0", + "@vitest/spy": "1.6.1", + "@vitest/utils": "1.6.1", "chai": "^4.3.10" }, "funding": { @@ -1080,12 +1123,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", - "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.1.tgz", + "integrity": "sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==", "license": "MIT", "dependencies": { - "@vitest/utils": "1.6.0", + "@vitest/utils": "1.6.1", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -1094,9 +1137,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", - "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.1.tgz", + "integrity": "sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==", "license": "MIT", "dependencies": { "magic-string": "^0.30.5", @@ -1108,9 +1151,9 @@ } }, "node_modules/@vitest/spy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", - "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.1.tgz", + "integrity": "sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==", "license": "MIT", "dependencies": { "tinyspy": "^2.2.0" @@ -1120,9 +1163,9 @@ } }, "node_modules/@vitest/utils": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", - "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.1.tgz", + "integrity": "sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==", "license": "MIT", "dependencies": { "diff-sequences": "^29.6.3", @@ -1452,6 +1495,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -1461,6 +1510,12 @@ "node": "*" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1519,9 +1574,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", - "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, "funding": [ { @@ -1566,9 +1621,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "devOptional": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/c32check": { "version": "2.0.0", @@ -1592,10 +1645,39 @@ "node": ">=8" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { - "version": "1.0.30001689", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001689.tgz", - "integrity": "sha512-CmeR2VBycfa+5/jOfnp/NpWPGd06nf1XYiefUvhXFfZE4GkRc9jv+eGPS4nT558WS/8lYCzV8SlANCIPvbWP1g==", + "version": "1.0.30001697", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001697.tgz", + "integrity": "sha512-GwNPlWJin8E+d7Gxq96jxM6w0w+VFeyyXRsjU58emtkYqnbwHqXm5uT2uCmO0RQE9htWknOP4xtBlLmM/gWxvQ==", "dev": true, "funding": [ { @@ -1614,6 +1696,12 @@ "license": "CC-BY-4.0", "peer": true }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "license": "Apache-2.0" + }, "node_modules/chai": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", @@ -1762,6 +1850,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -1770,19 +1870,40 @@ "license": "MIT", "peer": true }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, "node_modules/confbox": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", "license": "MIT" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/cross-fetch": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", - "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", "license": "MIT", "dependencies": { - "node-fetch": "^2.6.12" + "node-fetch": "^2.7.0" } }, "node_modules/cross-spawn": { @@ -1828,6 +1949,15 @@ "node": ">=6" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -1837,6 +1967,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -1845,9 +1989,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.74", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.74.tgz", - "integrity": "sha512-ck3//9RC+6oss/1Bh9tiAVFy5vfSKbRHAFh7Z3/eTRkEqJeWgymloShB17Vg3Z4nmDNp35vAd1BZ6CMW4Wt6Iw==", + "version": "1.5.91", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.91.tgz", + "integrity": "sha512-sNSHHyq048PFmZY4S90ax61q+gLCs0X0YmcOII9wG9S2XwbVr+h4VW2wWhnbp/Eys3cCwTxVF292W3qPaxIapQ==", "dev": true, "license": "ISC", "peer": true @@ -1860,9 +2004,9 @@ "license": "MIT" }, "node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", "dev": true, "license": "MIT", "dependencies": { @@ -1873,14 +2017,44 @@ "node": ">=10.13.0" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", - "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", "dev": true, "license": "MIT", "peer": true }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", @@ -2039,10 +2213,20 @@ "peer": true }, "node_modules/fast-uri": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", - "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "BSD-3-Clause", "peer": true }, @@ -2076,6 +2260,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2090,6 +2289,15 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2108,6 +2316,52 @@ "node": "*" } }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", @@ -2121,9 +2375,9 @@ } }, "node_modules/glob": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", - "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", + "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", "dev": true, "license": "ISC", "dependencies": { @@ -2152,6 +2406,18 @@ "license": "BSD-2-Clause", "peer": true }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -2169,6 +2435,60 @@ "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-basic": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", + "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", + "license": "MIT", + "dependencies": { + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "license": "MIT", + "dependencies": { + "@types/node": "^10.0.3" + } + }, + "node_modules/http-response-object/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "license": "MIT" + }, "node_modules/human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", @@ -2178,6 +2498,12 @@ "node": ">=16.17.0" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -2209,6 +2535,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2362,6 +2694,15 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -2386,9 +2727,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -2397,9 +2736,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "license": "MIT", - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -2446,17 +2783,23 @@ } }, "node_modules/mlly": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.3.tgz", - "integrity": "sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", + "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", "license": "MIT", "dependencies": { "acorn": "^8.14.0", - "pathe": "^1.1.2", - "pkg-types": "^1.2.1", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", "ufo": "^1.5.4" } }, + "node_modules/mlly/node_modules/pathe": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.2.tgz", + "integrity": "sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2544,6 +2887,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/onetime": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", @@ -2581,6 +2936,11 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==" + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2642,20 +3002,26 @@ } }, "node_modules/pkg-types": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.1.tgz", - "integrity": "sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", "license": "MIT", "dependencies": { "confbox": "^0.1.8", - "mlly": "^1.7.2", - "pathe": "^1.1.2" + "mlly": "^1.7.4", + "pathe": "^2.0.1" } }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.2.tgz", + "integrity": "sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==", + "license": "MIT" + }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "funding": [ { "type": "opencollective", @@ -2672,7 +3038,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -2722,6 +3088,21 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "license": "MIT", + "dependencies": { + "asap": "~2.0.6" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -2746,6 +3127,21 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -2763,6 +3159,27 @@ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "license": "MIT" }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -2813,9 +3230,9 @@ } }, "node_modules/rollup": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.28.1.tgz", - "integrity": "sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==", + "version": "4.34.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.2.tgz", + "integrity": "sha512-sBDUoxZEaqLu9QeNalL8v3jw6WjPku4wfZGyTU7l7m1oC+rpRihXc/n/H+4148ZkGz5Xli8CHMns//fFGKvpIQ==", "license": "MIT", "dependencies": { "@types/estree": "1.0.6" @@ -2828,25 +3245,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.28.1", - "@rollup/rollup-android-arm64": "4.28.1", - "@rollup/rollup-darwin-arm64": "4.28.1", - "@rollup/rollup-darwin-x64": "4.28.1", - "@rollup/rollup-freebsd-arm64": "4.28.1", - "@rollup/rollup-freebsd-x64": "4.28.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.28.1", - "@rollup/rollup-linux-arm-musleabihf": "4.28.1", - "@rollup/rollup-linux-arm64-gnu": "4.28.1", - "@rollup/rollup-linux-arm64-musl": "4.28.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.28.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.28.1", - "@rollup/rollup-linux-riscv64-gnu": "4.28.1", - "@rollup/rollup-linux-s390x-gnu": "4.28.1", - "@rollup/rollup-linux-x64-gnu": "4.28.1", - "@rollup/rollup-linux-x64-musl": "4.28.1", - "@rollup/rollup-win32-arm64-msvc": "4.28.1", - "@rollup/rollup-win32-ia32-msvc": "4.28.1", - "@rollup/rollup-win32-x64-msvc": "4.28.1", + "@rollup/rollup-android-arm-eabi": "4.34.2", + "@rollup/rollup-android-arm64": "4.34.2", + "@rollup/rollup-darwin-arm64": "4.34.2", + "@rollup/rollup-darwin-x64": "4.34.2", + "@rollup/rollup-freebsd-arm64": "4.34.2", + "@rollup/rollup-freebsd-x64": "4.34.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.2", + "@rollup/rollup-linux-arm-musleabihf": "4.34.2", + "@rollup/rollup-linux-arm64-gnu": "4.34.2", + "@rollup/rollup-linux-arm64-musl": "4.34.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.2", + "@rollup/rollup-linux-riscv64-gnu": "4.34.2", + "@rollup/rollup-linux-s390x-gnu": "4.34.2", + "@rollup/rollup-linux-x64-gnu": "4.34.2", + "@rollup/rollup-linux-x64-musl": "4.34.2", + "@rollup/rollup-win32-arm64-msvc": "4.34.2", + "@rollup/rollup-win32-ia32-msvc": "4.34.2", + "@rollup/rollup-win32-x64-msvc": "4.34.2", "fsevents": "~2.3.2" } }, @@ -2854,7 +3271,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -2892,9 +3308,9 @@ } }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -2936,6 +3352,78 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", @@ -3014,6 +3502,21 @@ "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", "license": "MIT" }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -3155,6 +3658,29 @@ "node": ">=8" } }, + "node_modules/sync-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", + "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", + "license": "MIT", + "dependencies": { + "http-response-object": "^3.0.1", + "sync-rpc": "^1.2.1", + "then-request": "^6.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/sync-rpc": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", + "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", + "license": "MIT", + "dependencies": { + "get-port": "^3.1.0" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -3282,6 +3808,34 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/then-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", + "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", + "license": "MIT", + "dependencies": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/then-request/node_modules/@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", + "license": "MIT" + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -3326,9 +3880,9 @@ "license": "MIT" }, "node_modules/ts-loader": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", - "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", + "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", "dev": true, "license": "MIT", "dependencies": { @@ -3355,10 +3909,16 @@ "node": ">=4" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -3376,15 +3936,15 @@ "license": "MIT" }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "license": "MIT" }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "dev": true, "funding": [ { @@ -3404,7 +3964,7 @@ "peer": true, "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -3424,6 +3984,12 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/varuint-bitcoin": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz", @@ -3435,9 +4001,9 @@ } }, "node_modules/vite": { - "version": "5.4.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", - "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", + "version": "5.4.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", + "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", "license": "MIT", "dependencies": { "esbuild": "^0.21.3", @@ -3494,9 +4060,9 @@ } }, "node_modules/vite-node": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", - "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.1.tgz", + "integrity": "sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==", "license": "MIT", "dependencies": { "cac": "^6.7.14", @@ -3516,16 +4082,16 @@ } }, "node_modules/vitest": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", - "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.1.tgz", + "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==", "license": "MIT", "dependencies": { - "@vitest/expect": "1.6.0", - "@vitest/runner": "1.6.0", - "@vitest/snapshot": "1.6.0", - "@vitest/spy": "1.6.0", - "@vitest/utils": "1.6.0", + "@vitest/expect": "1.6.1", + "@vitest/runner": "1.6.1", + "@vitest/snapshot": "1.6.1", + "@vitest/spy": "1.6.1", + "@vitest/utils": "1.6.1", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -3539,7 +4105,7 @@ "tinybench": "^2.5.1", "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.6.0", + "vite-node": "1.6.1", "why-is-node-running": "^2.2.2" }, "bin": { @@ -3554,8 +4120,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.6.0", - "@vitest/ui": "1.6.0", + "@vitest/browser": "1.6.1", + "@vitest/ui": "1.6.1", "happy-dom": "*", "jsdom": "*" }, diff --git a/package.json b/package.json index 96340d86e..eb25afe83 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "author": "hirosystems", "license": "GPL-3.0", "workspaces": [ + "components/clarity-repl/js", "components/clarinet-sdk-wasm/pkg-node", "components/clarinet-sdk-wasm/pkg-browser", "components/clarinet-sdk/common", @@ -19,7 +20,7 @@ "publish:sdk": "npm publish -w components/clarinet-sdk/node -w components/clarinet-sdk/browser --tag beta" }, "devDependencies": { - "@types/node": "^20.4.5", + "@types/node": "22.12.0", "@types/prompts": "^2.4.5", "@types/yargs": "^17.0.24", "prettier": "^3.3.3",