diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 72dc27e81..dce814dad 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -52,7 +52,9 @@ jobs: strategy: matrix: clarity_version: [1, 2, 3] - name: Clarity::V${{ matrix.clarity_version }} Artifacts + cost_tracking: [false, true] + + name: Clarity::V${{ matrix.clarity_version }} Artifacts${{ matrix.cost_tracking && ' (cost-tracking)' || ''}} steps: - name: Checkout PR uses: actions/checkout@v4 @@ -69,21 +71,21 @@ jobs: - name: Create archive of test binaries run: | cargo llvm-cov nextest-archive \ - --features test-clarity-v${{ matrix.clarity_version }} \ + --features test-clarity-v${{ matrix.clarity_version }}${{ matrix.cost_tracking && ',test-cost-tracking' || ''}} \ --workspace \ - --archive-file nextest-archive-v${{ matrix.clarity_version }}.tar.zst + --archive-file nextest-archive-v${{ matrix.clarity_version }}-${{ matrix.cost_tracking }}.tar.zst - name: Upload test binaries uses: actions/upload-artifact@v4 with: - name: nextest-archive-v${{ matrix.clarity_version }} - path: nextest-archive-v${{ matrix.clarity_version }}.tar.zst + name: nextest-archive-v${{ matrix.clarity_version }}-${{ matrix.cost_tracking }} + path: nextest-archive-v${{ matrix.clarity_version }}-${{ matrix.cost_tracking }}.tar.zst if-no-files-found: error - name: Upload standard.wasm file uses: actions/upload-artifact@v4 with: - name: standard-v${{ matrix.clarity_version }}.wasm + name: standard-v${{ matrix.clarity_version }}-${{ matrix.cost_tracking }}.wasm path: ${{github.workspace}}/clar2wasm/src/standard/standard.wasm if-no-files-found: error @@ -96,7 +98,8 @@ jobs: fail-fast: false matrix: clarity_version: [1, 2, 3] - name: Clarity::V${{ matrix.clarity_version }} Tests + cost_tracking: [false, true] + name: Clarity::V${{ matrix.clarity_version }} Tests${{ matrix.cost_tracking && ' (cost-tracking)' || ''}} steps: - name: Checkout PR uses: actions/checkout@v4 @@ -113,27 +116,27 @@ jobs: - name: Download standard.wasm file uses: actions/download-artifact@v4 with: - name: standard-v${{ matrix.clarity_version }}.wasm + name: standard-v${{ matrix.clarity_version }}-${{ matrix.cost_tracking }}.wasm path: ${{github.workspace}}/clar2wasm/src/standard/ - name: Download archive uses: actions/download-artifact@v4 with: - name: nextest-archive-v${{ matrix.clarity_version }} + name: nextest-archive-v${{ matrix.clarity_version }}-${{ matrix.cost_tracking }} - name: Run tests and output coverage shell: bash run: | cargo llvm-cov nextest \ - --archive-file nextest-archive-v${{ matrix.clarity_version }}.tar.zst \ + --archive-file nextest-archive-v${{ matrix.clarity_version }}-${{ matrix.cost_tracking }}.tar.zst \ --codecov \ - --output-path codecov-v${{ matrix.clarity_version }}.json + --output-path codecov-v${{ matrix.clarity_version }}-${{ matrix.cost_tracking }}.json - name: Upload codecov.json uses: actions/upload-artifact@v4 with: - name: code-coverage-v${{ matrix.clarity_version }} - path: codecov-v${{ matrix.clarity_version }}.json + name: code-coverage-v${{ matrix.clarity_version }}-${{ matrix.cost_tracking }} + path: codecov-v${{ matrix.clarity_version }}-${{ matrix.cost_tracking }}.json if-no-files-found: error # Code coverage diff --git a/clar2wasm/Cargo.toml b/clar2wasm/Cargo.toml index cbb1bea39..4076ce511 100644 --- a/clar2wasm/Cargo.toml +++ b/clar2wasm/Cargo.toml @@ -23,7 +23,10 @@ wat = "1.0.74" [features] flamegraph = [] pb = [] + # Test-specific features + +# Clarity version to test - default v2 test-clarity-v1 = [] test-clarity-v2 = [] test-clarity-v3 = [] diff --git a/clar2wasm/src/cost.rs b/clar2wasm/src/cost.rs index e5fc70218..8b85a2cdd 100644 --- a/clar2wasm/src/cost.rs +++ b/clar2wasm/src/cost.rs @@ -8,8 +8,11 @@ mod clar2; mod clar3; use std::fmt; +use std::ops::{AddAssign, SubAssign}; -use clarity::vm::{ClarityName, ClarityVersion}; +use clarity::types::StacksEpochId; +use clarity::vm::costs::ExecutionCost; +use clarity::vm::ClarityName; use walrus::ir::{BinaryOp, Instr, UnaryOp, Unop}; use walrus::{FunctionId, GlobalId, InstrSeqBuilder, LocalId, Module}; use wasmtime::{AsContextMut, Extern, Global, Mutability, Val, ValType}; @@ -23,11 +26,88 @@ type Result = std::result::Result; /// Values of the cost globals #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] pub struct CostMeter { - pub runtime: u64, - pub read_count: u64, - pub read_length: u64, - pub write_count: u64, - pub write_length: u64, + pub runtime: i64, + pub read_count: i64, + pub read_length: i64, + pub write_count: i64, + pub write_length: i64, +} + +impl CostMeter { + pub const INIT: Self = Self::MAX; + pub const ZERO: Self = Self::MIN; + + pub const MAX: Self = Self { + runtime: i64::MAX, + read_count: i64::MAX, + read_length: i64::MAX, + write_count: i64::MAX, + write_length: i64::MAX, + }; + + pub const MIN: Self = Self { + runtime: 0, + read_count: 0, + read_length: 0, + write_count: 0, + write_length: 0, + }; +} + +impl AddAssign<&CostMeter> for CostMeter { + fn add_assign(&mut self, rhs: &CostMeter) { + self.runtime.add_assign(rhs.runtime); + self.read_count.add_assign(rhs.read_count); + self.read_length.add_assign(rhs.read_length); + self.write_count.add_assign(rhs.write_count); + self.write_length.add_assign(rhs.write_length); + } +} + +impl AddAssign for CostMeter { + fn add_assign(&mut self, rhs: CostMeter) { + self.add_assign(&rhs); + } +} + +impl SubAssign<&CostMeter> for CostMeter { + fn sub_assign(&mut self, rhs: &CostMeter) { + self.runtime.sub_assign(rhs.runtime); + self.read_count.sub_assign(rhs.read_count); + self.read_length.sub_assign(rhs.read_length); + self.write_count.sub_assign(rhs.write_count); + self.write_length.sub_assign(rhs.write_length); + } +} + +impl SubAssign for CostMeter { + fn sub_assign(&mut self, rhs: CostMeter) { + self.sub_assign(&rhs); + } +} + +impl From for ExecutionCost { + fn from(meter: CostMeter) -> Self { + Self { + write_length: meter.write_length as _, + write_count: meter.write_count as _, + read_length: meter.read_length as _, + read_count: meter.read_count as _, + runtime: meter.runtime as _, + } + } +} + +impl From for CostMeter { + fn from(meter: ExecutionCost) -> Self { + Self { + write_length: meter.write_length as _, + write_count: meter.write_count as _, + read_length: meter.read_length as _, + read_count: meter.read_count as _, + runtime: meter.runtime as _, + } + } } /// Globals used for cost tracking @@ -115,11 +195,31 @@ impl CostLinker for wasmtime::Linker { ) -> wasmtime::Result<()> { let mut store = store.as_context_mut(); - define_cost_global_import(self, &mut store, "cost-runtime", 0)?; - define_cost_global_import(self, &mut store, "cost-read-count", 0)?; - define_cost_global_import(self, &mut store, "cost-read-length", 0)?; - define_cost_global_import(self, &mut store, "cost-write-count", 0)?; - define_cost_global_import(self, &mut store, "cost-write-length", 0)?; + define_cost_global_import(self, &mut store, "cost-runtime", CostMeter::INIT.runtime)?; + define_cost_global_import( + self, + &mut store, + "cost-read-count", + CostMeter::INIT.read_count, + )?; + define_cost_global_import( + self, + &mut store, + "cost-read-length", + CostMeter::INIT.read_length, + )?; + define_cost_global_import( + self, + &mut store, + "cost-write-count", + CostMeter::INIT.read_length, + )?; + define_cost_global_import( + self, + &mut store, + "cost-write-length", + CostMeter::INIT.read_length, + )?; Ok(()) } @@ -129,7 +229,7 @@ fn define_cost_global_import( linker: &mut wasmtime::Linker, mut store: impl AsContextMut, name: &str, - value: u64, + value: i64, ) -> wasmtime::Result<()> { use wasmtime::{Global, GlobalType}; @@ -180,6 +280,16 @@ pub trait AccessCostMeter: CostLinker { }) } + /// Returns the amount used in the cost meter - i.e. [`CostMeter::INIT`].sub(get_cost_meter()) + fn get_used_cost(&self, mut store: impl AsContextMut) -> wasmtime::Result { + let curr = self.get_cost_meter(&mut store)?; + + let mut used = CostMeter::INIT; + used.sub_assign(&curr); + + Ok(used) + } + /// Set the value of the cost meter. fn set_cost_meter( &self, @@ -190,21 +300,19 @@ pub trait AccessCostMeter: CostLinker { let globals = self.get_cost_globals(&mut store)?; - globals - .runtime - .set(&mut store, Val::I64(meter.runtime as _))?; + globals.runtime.set(&mut store, Val::I64(meter.runtime))?; globals .read_count - .set(&mut store, Val::I64(meter.read_count as _))?; + .set(&mut store, Val::I64(meter.read_count))?; globals .read_length - .set(&mut store, Val::I64(meter.read_length as _))?; + .set(&mut store, Val::I64(meter.read_length))?; globals .write_count - .set(&mut store, Val::I64(meter.write_count as _))?; + .set(&mut store, Val::I64(meter.write_count))?; globals .write_length - .set(&mut store, Val::I64(meter.write_length as _))?; + .set(&mut store, Val::I64(meter.write_length))?; Ok(()) } @@ -260,18 +368,12 @@ pub trait ChargeGenerator { let n = n.into(); if let Some((ctx, module)) = self.cost_context() { - let maybe_word_cost = match ctx.clarity_version { - ClarityVersion::Clarity1 => clar1::WORD_COSTS.get(&word_name), - ClarityVersion::Clarity2 => clar2::WORD_COSTS.get(&word_name), - ClarityVersion::Clarity3 => clar3::WORD_COSTS.get(&word_name), - }; - - match maybe_word_cost { + match ctx.word_cost(&word_name) { Some(cost) => ctx.emit(instrs, module, cost, n)?, None => { return Err(GeneratorError::InternalError(format!( - "'{}' do not exists in costs table for {}", - word_name, ctx.clarity_version + "'{word_name}' does not exist in costs table for epoch '{}'", + ctx.epoch ))) } } @@ -339,7 +441,7 @@ impl ScalarGet for InstrSeqBuilder<'_> { /// Context required from a generator to emit cost tracking code. pub struct ChargeContext { - pub clarity_version: ClarityVersion, + pub epoch: StacksEpochId, pub runtime: GlobalId, pub read_count: GlobalId, pub read_length: GlobalId, @@ -348,6 +450,23 @@ pub struct ChargeContext { pub runtime_error: FunctionId, } +impl ChargeContext { + fn word_cost(&self, name: &ClarityName) -> Option<&WordCost> { + match self.epoch { + StacksEpochId::Epoch10 => panic!("clarity did not exist in epoch 1"), + StacksEpochId::Epoch20 => clar1::WORD_COSTS.get(name), + StacksEpochId::Epoch2_05 => clar2::WORD_COSTS.get(name), + StacksEpochId::Epoch21 + | StacksEpochId::Epoch22 + | StacksEpochId::Epoch23 + | StacksEpochId::Epoch24 + | StacksEpochId::Epoch25 + | StacksEpochId::Epoch30 + | StacksEpochId::Epoch31 => clar3::WORD_COSTS.get(name), + } + } +} + #[derive(Debug, Clone, Copy)] struct WordCost { runtime: Caf, @@ -665,7 +784,7 @@ fn caf_nlogn( } #[cfg(test)] -mod test_caf { +mod caf { //! The code in this module tests that the code generation in the `caf_*` functions is correct, //! *not* that the code generation of each word is correct. @@ -682,7 +801,7 @@ mod test_caf { assert_eq!( final_cost_val, - initial_cost_val - cost as u64, + initial_cost_val - cost as i64, "should decrement accurately" ); } @@ -711,7 +830,7 @@ mod test_caf { assert_eq!( final_val, - initial_val - cost as u64, + initial_val - cost as i64, "should decrement accurately" ); } @@ -742,7 +861,7 @@ mod test_caf { assert_eq!( final_val, - initial_val - cost as u64, + initial_val - cost as i64, "should decrement accurately" ); } @@ -773,7 +892,7 @@ mod test_caf { assert_eq!( final_val, - initial_val - cost as u64, + initial_val - cost as i64, "should decrement accurately" ); } @@ -794,9 +913,9 @@ mod test_caf { fn execute_with_caf>( arg: i32, - initial: u64, + initial: i64, caf: impl FnOnce(LocalId) -> (Caf, S), - ) -> Result { + ) -> Result { use wasmtime::{Engine, Linker, Module, Store}; let engine = Engine::default(); @@ -918,3 +1037,673 @@ mod test_caf { module.emit_wasm() } } + +#[cfg(test)] +mod word { + use super::*; + + use clarity::vm::ClarityVersion; + + use crate::tools::TestEnvironment; + + #[inline(always)] + fn execute_snippet( + epoch: StacksEpochId, + version: ClarityVersion, + snippet: &str, + expected_cost: CostMeter, + ) { + let mut env = TestEnvironment::new_with_cost(epoch, version); + + env.init_contract_with_snippet("snippet", snippet) + .expect("init_contract should succeed"); + let cost_tracker = env.cost_tracker; + + let cost = CostMeter::from(cost_tracker.get_total()); + assert_eq!(cost, expected_cost, "'cost' should match 'expected_cost'"); + } + + macro_rules! epoch_for_version { + (1) => { + StacksEpochId::Epoch20 + }; + (2) => { + StacksEpochId::Epoch2_05 + }; + (3) => { + StacksEpochId::Epoch31 + }; + } + + macro_rules! decl_test { + ($version:literal, $name:literal, $snippet:literal, $expected_cost:expr) => { + paste::paste! { + #[test] + fn [<$name _ v $version >]() { + let epoch = epoch_for_version!($version); + let version = ClarityVersion::default_for_epoch(epoch); + execute_snippet(epoch, version, $snippet, $expected_cost); + } + } + }; + } + + macro_rules! decl_tests { + ($name:literal, $snippet:literal, { $($version:literal => $cost:expr),* $(,)? }) => { + $( + decl_test!($version, $name, $snippet, $cost); + )* + } + } + + decl_tests!("add", "(+ 1 2 3)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 158, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("sub", "(- 10 9 1)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 158, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("mul", "(* 2 5 10)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("div", "(/ 10 5 2)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + + decl_tests!("log2", "(log2 1000)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 133, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("mod", "(mod 2 3)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 141, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("pow", "(pow 2 3)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 143, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("sqrti", "(sqrti 11)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 142, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("bitwise_and", "(bit-and 24 16)", { + 3 => CostMeter { runtime: 369, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("bitwise_or", "(bit-or 24 16)", { + 3 => CostMeter { runtime: 369, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("bitwise_xor", "(bit-xor 1 2)", { + 3 => CostMeter { runtime: 369, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("bitwise_not", "(bit-not 3)", { + 3 => CostMeter { runtime: 147, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("bitwise_lshift", "(bit-shift-left 2 u1)", { + 3 => CostMeter { runtime: 167, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("bitwise_rshift", "(bit-shift-right 2 u1)", { + 3 => CostMeter { runtime: 167, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("buf_to_int_be", "(buff-to-int-be 0x01)", { + 3 => CostMeter { runtime: 141, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("buf_to_int_le", "(buff-to-int-le 0x01)", { + 3 => CostMeter { runtime: 141, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("buf_to_uint_be", "(buff-to-uint-be 0x01)", { + 3 => CostMeter { runtime: 141, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("buf_to_uint_le", "(buff-to-uint-le 0x01)", { + 3 => CostMeter { runtime: 141, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("gt_int", "(> 1 2)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 240, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("gte_int", "(>= 1 2)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 240, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("lt_int", "(< 1 2)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 240, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("lte_int", "(<= 1 2)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 240, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("gt_buf", "(> 0xffff 0x4242)", { + 3 => CostMeter { runtime: 142, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("gte_buf", "(>= 0xffff 0x4242)", { + 3 => CostMeter { runtime: 142, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("lt_buf", "(< 0xffff 0x4242)", { + 3 => CostMeter { runtime: 142, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("lte_buf", "(<= 0xffff 0x4242)", { + 3 => CostMeter { runtime: 142, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("or", "(or true false)", { + 1 => CostMeter { runtime: 3000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 155, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 126, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("and", "(and true false)", { + 1 => CostMeter { runtime: 3000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 155, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 126, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("not", "(not true)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 138, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("to_int", "(to-int u238)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 135, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("to_uint", "(to-uint 238)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 135, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("int_to_ascii", "(int-to-ascii 1)", { + 3 => CostMeter { runtime: 147, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("int_to_utf8", "(int-to-utf8 1)", { + 3 => CostMeter { runtime: 181, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("string_to_int", "(string-to-int? \"1\")", { + 3 => CostMeter { runtime: 168, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("string_to_uint", "(string-to-uint? \"1\")", { + 3 => CostMeter { runtime: 168, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("hash160_int", "(hash160 0)", { + 1 => CostMeter { runtime: 17000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 217, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 204, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("keccak256_int", "(keccak256 0)", { + 1 => CostMeter { runtime: 17000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 237, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 143, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("sha256_int", "(sha256 0)", { + 1 => CostMeter { runtime: 17000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 116, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 116, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("sha512_int", "(sha512 0)", { + 1 => CostMeter { runtime: 17000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 192, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 192, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("sha512_256_int", "(sha512/256 0)", { + 1 => CostMeter { runtime: 17000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 204, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 72, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("hash160_buf", "(hash160 0xffff)", { + 1 => CostMeter { runtime: 3000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 203, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 190, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("keccak256_buf", "(keccak256 0xffff)", { + 1 => CostMeter { runtime: 3000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 223, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 129, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("sha256_buf", "(sha256 0xffff)", { + 1 => CostMeter { runtime: 3000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 102, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 102, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("sha512_buf", "(sha512 0xffff)", { + 1 => CostMeter { runtime: 3000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 178, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 178, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("sha512_256_buf", "(sha512/256 0xffff)", { + 1 => CostMeter { runtime: 3000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 190, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 58, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("stx_burn", "(stx-burn? u100 'S1G2081040G2081040G2081040G208105NK8PE5)", { + 1 => CostMeter { runtime: 1000, read_count: 2, read_length: 1, write_count: 2, write_length: 1 }, + 2 => CostMeter { runtime: 612, read_count: 2, read_length: 1, write_count: 2, write_length: 1 }, + 3 => CostMeter { runtime: 549, read_count: 2, read_length: 1, write_count: 2, write_length: 1 }, + }); + decl_tests!("stx_get_balance", "(stx-get-balance 'S1G2081040G2081040G2081040G208105NK8PE5)", { + 1 => CostMeter { runtime: 1000, read_count: 1, read_length: 1, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 1385, read_count: 1, read_length: 1, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 4294, read_count: 1, read_length: 1, write_count: 0, write_length: 0 }, + }); + decl_tests!("stx_get_account", "(stx-account 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)", { + 3 => CostMeter { runtime: 4654, read_count: 1, read_length: 1, write_count: 0, write_length: 0 }, + }); + decl_tests!("principal_construct", "(principal-construct? 0x1a 0xfa6bf38ed557fe417333710d6033e9419391a320)", { + 3 => CostMeter { runtime: 398, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("principal_destruct", "(principal-destruct? 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6)", { + 3 => CostMeter { runtime: 314, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + + decl_tests!("let", "(let ((a 42) (b 24)) a)", { + 1 => CostMeter { runtime: 3000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 1154, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 412, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("at_block", "(at-block 0x0000000000000000000000000000000000000000000000000000000000000000 1)", { + 1 => CostMeter { runtime: 1000, read_count: 1, read_length: 1, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 210, read_count: 1, read_length: 1, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 1327, read_count: 1, read_length: 1, write_count: 0, write_length: 0 }, + }); + decl_tests!("get_block_info", "(get-block-info? time u0)", { + 1 => CostMeter { runtime: 1000, read_count: 1, read_length: 1, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 6321, read_count: 1, read_length: 1, write_count: 0, write_length: 0 }, + }); + decl_tests!("get_burn_block_info", "(get-burn-block-info? header-hash u677050)", { + 3 => CostMeter { runtime: 96479, read_count: 1, read_length: 1, write_count: 0, write_length: 0 }, + }); + decl_tests!("get_stacks_block_info", "(get-stacks-block-info? time u0)", { + 3 => CostMeter { runtime: 6321, read_count: 1, read_length: 1, write_count: 0, write_length: 0 }, + }); + decl_tests!("get_tenure_info", "(get-tenure-info? time u0)", { + 3 => CostMeter { runtime: 0, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("asserts", "(asserts! true 1)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 128, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("filter", "(filter not (list true false true false))", { + 1 => CostMeter { runtime: 10000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 1394, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 1179, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("if", "(if true 1 2)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 200, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 168, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("match", "(match (some 1) value 1 2)", { + 1 => CostMeter { runtime: 2000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 517, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 463, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("try", "(try! (some 1))", { + 1 => CostMeter { runtime: 2000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 517, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 439, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("unwrap", "(unwrap! (ok 1) 1)", { + 1 => CostMeter { runtime: 2000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 517, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 451, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("unwrap_err", "(unwrap-err! (err 1) false)", { + 1 => CostMeter { runtime: 2000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 517, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 447, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("from_consensus_buff", "(from-consensus-buff? int 0x0000000000000000000000000000000001)", { + 3 => CostMeter { runtime: 389, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("to_consensus_buff", "(to-consensus-buff? 1)", { + 3 => CostMeter { runtime: 250, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("as_contract", "(as-contract 1)", { + 3 => CostMeter { runtime: 138, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + // decl_tests!("contract_call", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + decl_tests!("begin", "(begin 1)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 202, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 151, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("unwrap_err_panic", "(unwrap-err-panic (err 1))", { + 1 => CostMeter { runtime: 2000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 569, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 501, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("unwrap_panic", "(unwrap-panic (some 1))", { + 1 => CostMeter { runtime: 2000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 460, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 473, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("get_data_var", "(define-data-var i int 0) \ + (var-get i)", { + 1 => CostMeter { runtime: 33000, read_count: 1, read_length: 33, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 575, read_count: 1, read_length: 33, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 183, read_count: 1, read_length: 33, write_count: 0, write_length: 0 }, + }); + decl_tests!("set_data_var", "(define-data-var i int 0) \ + (var-set i 1)", { + 1 => CostMeter { runtime: 17000, read_count: 0, read_length: 0, write_count: 1, write_length: 17 }, + 2 => CostMeter { runtime: 771, read_count: 0, read_length: 0, write_count: 1, write_length: 17 }, + 3 => CostMeter { runtime: 735, read_count: 0, read_length: 0, write_count: 1, write_length: 17 }, + }); + decl_tests!("default_to", "(default-to 0 none)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 287, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 268, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("err", "(err true)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 230, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("ok", "(ok true)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 230, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("some", "(some true)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 230, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("index_of_list", "(index-of (list 1 2 3) 2)", { + 1 => CostMeter { runtime: 8000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 486, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 420, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("index_of_string_utf8", r#"(index-of u"hello" u"l")"#, { + 1 => CostMeter { runtime: 6000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 248, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 216, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("index_of_buff", r#"(index-of 0x1234567890 0x34)"#, { + 1 => CostMeter { runtime: 6000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 248, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 216, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + // TODO: need serialization of values for computation + decl_tests!("is_eq", "(is-eq 1 1)", { + 1 => CostMeter { runtime: 0, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 0, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 0, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("map_definition", "(define-map squares { x: int } { y: int })", { + 1 => CostMeter { runtime: 0, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 0, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 0, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + // TODO: need a change in the cost computation + decl_tests!("map_delete", "(define-map squares { x: int } { y: int }) \ + (map-delete squares { x: 1 })", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + // TODO: need a change in the cost computation + decl_tests!("map_get", "(define-map squares { x: int } { y: int }) \ + (map-get? squares { x: 1 })", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + // TODO: need a change in the cost computation + decl_tests!("map_insert", "(define-map squares { x: int } { y: int }) \ + (map-insert squares { x: 1 } { y: 1 })", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + // TODO: need a change in the cost computation + decl_tests!("map_set", "(define-map squares { x: int } { y: int }) \ + (map-set squares { x: 1 } { y: 1 })", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + // decl_tests!("contract_of", "(contract-of contract)", { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + decl_tests!("is_none", "(is-none none)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("is_some", "(is-some (some 1))", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("is_standard", "(is-standard 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6)", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("principal_of", "(principal-of? 0x03adb8de4bfb65db2cfd6120d55c6526ae9c52e675db7e47308636534ba7786110)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("print", "(print 0x1234567890)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("is_err", "(is-err (err 1))", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("is_ok", "(is-ok (ok 1))", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("recover", "(secp256k1-recover? 0xde5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f04 0x8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a1301)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("verify", "(secp256k1-verify 0xde5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f04 0x8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a1301 0x03adb8de4bfb65db2cfd6120d55c6526ae9c52e675db7e47308636534ba7786110)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("append", "(append (list 1 2 3 4) 5)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("as_max_len", "(as-max-len? 0x1234567890 u2)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("concat", "(concat 0x0102 0x0304)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("element_at", "(element-at? 0x1234567890 u2)", { + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("fold", "(fold * (list 2 2 2) 1)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("len", "(len 0x010203)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("list_cons", "(list 1 2 3)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("map", "(define-private (zero-or-one (char (buff 1))) \ + (if (is-eq char 0x00) 0x00 0x01)) \ + (map zero-or-one 0x000102)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("replace_at", "(replace-at? 0x00112233 u2 0x44)", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("slice", "(slice? 0x1234567890 u1 u3)", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("stx_transfer", "(stx-burn? u100 'S1G2081040G2081040G2081040G208105NK8PE5)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("stx_transfer_memo", "(stx-transfer-memo? u100 'S1G2081040G2081040G2081040G208105NK8PE5 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM 0x12345678)", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("ft_burn", "(define-fungible-token st) \ + (ft-mint? st u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("nft_burn", "(define-non-fungible-token st int) \ + (nft-mint? st 1 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("ft_get_balance", "(define-fungible-token st) \ + (ft-get-balance st 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("nft_get_owner", "(define-non-fungible-token st int) \ + (nft-mint? st 1 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) \ + (nft-get-owner? st 1)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("ft_get_supply", "(define-fungible-token st) \ + (ft-get-supply st)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("ft_mint", "(define-fungible-token st) \ + (ft-mint? st u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("nft_mint", "(define-non-fungible-token st int) \ + (nft-mint? st 1 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("ft_transfer", "(define-fungible-token st) \ + (ft-mint? st u100 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR) \ + (ft-transfer? st u50 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) \ + (ft-transfer? st u60 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("nft_transfer", "(define-non-fungible-token st int) \ + (nft-mint? st 1 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR) \ + (nft-transfer? st 1 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + // decl_tests!("impl_trait", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + // decl_tests!("use_trait", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + decl_tests!("tuple_cons", "(tuple (b 0x0102) (id 1337))", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("tuple_get", "(get id (tuple (b 0x0102) (id 1337)))", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("tuple_merge", "(merge {a: 1} {b: 2})", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + + // decl_tests!("define_constant", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + // decl_tests!("define_data_var", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + // decl_tests!("define_private_function", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + // decl_tests!("define_public_function", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + // decl_tests!("define_readonly_function", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + // decl_tests!("ft_define", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + // decl_tests!("nft_define", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + // decl_tests!("define_trait", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); +} diff --git a/clar2wasm/src/cost.rs~ b/clar2wasm/src/cost.rs~ new file mode 100644 index 000000000..98c6f8f16 --- /dev/null +++ b/clar2wasm/src/cost.rs~ @@ -0,0 +1,1708 @@ +//! Functionality to track the costs of running Clarity. +//! +//! The cost computations in this module are meant to be a full match with the interpreter +//! implementation of the Clarity runtime. + +mod clar1; +mod clar2; +mod clar3; + +use std::fmt; +use std::ops::{AddAssign, SubAssign}; + +use clarity::types::StacksEpochId; +use clarity::vm::costs::ExecutionCost; +use clarity::vm::ClarityName; +use walrus::ir::{BinaryOp, Instr, UnaryOp, Unop}; +use walrus::{FunctionId, GlobalId, InstrSeqBuilder, LocalId, Module}; +use wasmtime::{AsContextMut, Extern, Global, Mutability, Val, ValType}; + +use crate::error_mapping::ErrorMap; +use crate::wasm_generator::{GeneratorError, WasmGenerator}; +use crate::words::Word; + +type Result = std::result::Result; + +/// Values of the cost globals +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +pub struct CostMeter { + pub runtime: i64, + pub read_count: i64, + pub read_length: i64, + pub write_count: i64, + pub write_length: i64, +} + +impl CostMeter { + pub const INIT: Self = Self::MAX; + pub const ZERO: Self = Self::MIN; + + pub const MAX: Self = Self { + runtime: i64::MAX, + read_count: i64::MAX, + read_length: i64::MAX, + write_count: i64::MAX, + write_length: i64::MAX, + }; + + pub const MIN: Self = Self { + runtime: 0, + read_count: 0, + read_length: 0, + write_count: 0, + write_length: 0, + }; +} + +impl AddAssign<&CostMeter> for CostMeter { + fn add_assign(&mut self, rhs: &CostMeter) { + self.runtime.add_assign(rhs.runtime); + self.read_count.add_assign(rhs.read_count); + self.read_length.add_assign(rhs.read_length); + self.write_count.add_assign(rhs.write_count); + self.write_length.add_assign(rhs.write_length); + } +} + +impl AddAssign for CostMeter { + fn add_assign(&mut self, rhs: CostMeter) { + self.add_assign(&rhs); + } +} + +impl SubAssign<&CostMeter> for CostMeter { + fn sub_assign(&mut self, rhs: &CostMeter) { + self.runtime.sub_assign(rhs.runtime); + self.read_count.sub_assign(rhs.read_count); + self.read_length.sub_assign(rhs.read_length); + self.write_count.sub_assign(rhs.write_count); + self.write_length.sub_assign(rhs.write_length); + } +} + +impl SubAssign for CostMeter { + fn sub_assign(&mut self, rhs: CostMeter) { + self.sub_assign(&rhs); + } +} + +impl From for ExecutionCost { + fn from(meter: CostMeter) -> Self { + Self { + write_length: meter.write_length as _, + write_count: meter.write_count as _, + read_length: meter.read_length as _, + read_count: meter.read_count as _, + runtime: meter.runtime as _, + } + } +} + +impl From for CostMeter { + fn from(meter: ExecutionCost) -> Self { + Self { + write_length: meter.write_length as _, + write_count: meter.write_count as _, + read_length: meter.read_length as _, + read_count: meter.read_count as _, + runtime: meter.runtime as _, + } + } +} + +/// Globals used for cost tracking +#[derive(Debug, Clone, Copy)] +pub struct CostGlobals { + pub runtime: Global, + pub read_count: Global, + pub read_length: Global, + pub write_count: Global, + pub write_length: Global, +} + +/// Trait for a `Linker` that can be used to retrieve the cost globals. +pub trait CostLinker { + /// Get the cost globals. + fn get_cost_globals(&self, store: impl AsContextMut) + -> wasmtime::Result; + /// Define the cost globals. + fn define_cost_globals(&mut self, store: impl AsContextMut) -> wasmtime::Result<()>; +} + +/// Convenience to use the same error string in multiple places +#[derive(Debug)] +enum GetCostGlobalsError { + Runtime, + ReadCount, + ReadLength, + WriteCount, + WriteLength, +} + +impl fmt::Display for GetCostGlobalsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use GetCostGlobalsError::*; + + match self { + Runtime => write!(f, "missing `cost-runtime` global"), + ReadCount => write!(f, "missing `cost-read-count` global"), + ReadLength => write!(f, "missing `cost-read-length` global"), + WriteCount => write!(f, "missing `cost-write-count` global"), + WriteLength => write!(f, "missing `cost-write-length` global"), + } + } +} + +impl std::error::Error for GetCostGlobalsError {} + +impl CostLinker for wasmtime::Linker { + fn get_cost_globals( + &self, + mut store: impl AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + + let runtime = self.get(&mut store, "clarity", "cost-runtime"); + let read_count = self.get(&mut store, "clarity", "cost-read-count"); + let read_length = self.get(&mut store, "clarity", "cost-read-length"); + let write_count = self.get(&mut store, "clarity", "cost-write-count"); + let write_length = self.get(&mut store, "clarity", "cost-write-length"); + + use GetCostGlobalsError::*; + + fn unwrap_global_or( + ext: Option, + err: GetCostGlobalsError, + ) -> Result { + match ext { + Some(Extern::Global(global)) => Ok(global), + _ => Err(err), + } + } + + Ok(CostGlobals { + runtime: unwrap_global_or(runtime, Runtime)?, + read_count: unwrap_global_or(read_count, ReadCount)?, + read_length: unwrap_global_or(read_length, ReadLength)?, + write_count: unwrap_global_or(write_count, WriteCount)?, + write_length: unwrap_global_or(write_length, WriteLength)?, + }) + } + + fn define_cost_globals( + &mut self, + mut store: impl AsContextMut, + ) -> wasmtime::Result<()> { + let mut store = store.as_context_mut(); + + define_cost_global_import(self, &mut store, "cost-runtime", CostMeter::INIT.runtime)?; + define_cost_global_import( + self, + &mut store, + "cost-read-count", + CostMeter::INIT.read_count, + )?; + define_cost_global_import( + self, + &mut store, + "cost-read-length", + CostMeter::INIT.read_length, + )?; + define_cost_global_import( + self, + &mut store, + "cost-write-count", + CostMeter::INIT.read_length, + )?; + define_cost_global_import( + self, + &mut store, + "cost-write-length", + CostMeter::INIT.read_length, + )?; + + Ok(()) + } +} + +fn define_cost_global_import( + linker: &mut wasmtime::Linker, + mut store: impl AsContextMut, + name: &str, + value: i64, +) -> wasmtime::Result<()> { + use wasmtime::{Global, GlobalType}; + + let mut store = store.as_context_mut(); + + let global = Global::new( + &mut store, + GlobalType::new(ValType::I64, Mutability::Var), + Val::I64(value as _), + )?; + + linker.define(&mut store, "clarity", name, global)?; + + Ok(()) +} + +/// Trait to manipulate the values of a cost meter. +pub trait AccessCostMeter: CostLinker { + /// Get the current value of the cost meter. + fn get_cost_meter( + &self, + mut store: impl AsContextMut, + ) -> wasmtime::Result { + let mut store = store.as_context_mut(); + + let globals = self.get_cost_globals(&mut store)?; + + use GetCostGlobalsError::*; + + Ok(CostMeter { + runtime: globals.runtime.get(&mut store).i64().ok_or(Runtime)? as _, + read_count: globals.read_count.get(&mut store).i64().ok_or(ReadCount)? as _, + read_length: globals + .read_length + .get(&mut store) + .i64() + .ok_or(ReadLength)? as _, + write_count: globals + .write_count + .get(&mut store) + .i64() + .ok_or(WriteCount)? as _, + write_length: globals + .write_length + .get(&mut store) + .i64() + .ok_or(WriteLength)? as _, + }) + } + + /// Returns the amount used in the cost meter - i.e. [`CostMeter::INIT`].sub(get_cost_meter()) + fn get_used_cost(&self, mut store: impl AsContextMut) -> wasmtime::Result { + let curr = self.get_cost_meter(&mut store)?; + + let mut used = CostMeter::INIT; + used.sub_assign(&curr); + + Ok(used) + } + + /// Set the value of the cost meter. + fn set_cost_meter( + &self, + mut store: impl AsContextMut, + meter: CostMeter, + ) -> wasmtime::Result<()> { + let mut store = store.as_context_mut(); + + let globals = self.get_cost_globals(&mut store)?; + + globals.runtime.set(&mut store, Val::I64(meter.runtime))?; + globals + .read_count + .set(&mut store, Val::I64(meter.read_count))?; + globals + .read_length + .set(&mut store, Val::I64(meter.read_length))?; + globals + .write_count + .set(&mut store, Val::I64(meter.write_count))?; + globals + .write_length + .set(&mut store, Val::I64(meter.write_length))?; + + Ok(()) + } +} + +impl> AccessCostMeter for T {} + +/// Extension trait allowing for words to generate cost tracking code +/// during traversal. +pub trait WordCharge { + /// Generate cost tracking code for this word. + /// + /// See [`ChargeGenerator::charge`] for more details. + fn charge( + &self, + generator: &C, + instrs: &mut InstrSeqBuilder, + n: impl Into, + ) -> Result<()>; +} + +impl WordCharge for W { + fn charge( + &self, + generator: &C, + instrs: &mut InstrSeqBuilder, + n: impl Into, + ) -> Result<()> { + generator.charge(instrs, self.name(), n) + } +} + +/// Generators of cost tracking code. +pub trait ChargeGenerator { + /// The cost tracking context. Only present if charging code should be emitted. + fn cost_context(&self) -> Option<(&ChargeContext, &Module)>; + + /// Generate code that charges the appropriate cost for the given word. + /// + /// `n` is a scaling factor that depends on the word being charged, but can only be known + /// during traversal. The value *must* be either a `u32` or a `LocalId` representing a local + /// with type `I32`. + /// If the word has a constant cost, the value will be ignored. This is useful in words where + /// the cost is known to be constant during traversal. + /// + /// Code will be generated iff [`cost_context`] returns `Some`. + fn charge( + &self, + instrs: &mut InstrSeqBuilder, + word_name: ClarityName, + n: impl Into, + ) -> Result<()> { + let n = n.into(); + + if let Some((ctx, module)) = self.cost_context() { + match ctx.word_cost(&word_name) { + Some(cost) => ctx.emit(instrs, module, cost, n)?, + None => { + return Err(GeneratorError::InternalError(format!( + "'{word_name}' does not exist in costs table for epoch '{}'", + ctx.epoch + ))) + } + } + } + + Ok(()) + } +} + +impl ChargeGenerator for WasmGenerator { + fn cost_context(&self) -> Option<(&ChargeContext, &Module)> { + self.cost_context.as_ref().map(|ctx| (ctx, &self.module)) + } +} + +/// A 32-bit unsigned integer to be resolved at either compile-time or run-time. +#[derive(Clone, Copy)] +pub enum Scalar { + Compile(u32), + Run(LocalId), +} + +impl From for Scalar { + fn from(n: u32) -> Self { + Self::Compile(n) + } +} + +impl From for Scalar { + fn from(n: LocalId) -> Self { + Self::Run(n) + } +} + +/// Trait for allowing us to not repeat ourselves in resolving a scalar. +trait ScalarGet { + fn scalar_get(&mut self, module: &Module, scalar: Scalar) -> Result<&mut Self>; +} + +impl ScalarGet for InstrSeqBuilder<'_> { + fn scalar_get(&mut self, module: &Module, scalar: Scalar) -> Result<&mut Self> { + Ok(match scalar { + Scalar::Compile(c) => self.i64_const(c as _), + Scalar::Run(l) => { + let local = module.locals.get(l); + + match local.ty() { + walrus::ValType::I32 => {} + ty => { + return Err(GeneratorError::InternalError(format!( + "cost local should be of type i32 but is of type {ty}" + ))) + } + } + + self.local_get(l) + // this is so we don't have to repeat this code in the `caf` functions + .instr(Instr::Unop(Unop { + op: UnaryOp::I64ExtendUI32, + })) + } + }) + } +} + +/// Context required from a generator to emit cost tracking code. +pub struct ChargeContext { + pub epoch: StacksEpochId, + pub runtime: GlobalId, + pub read_count: GlobalId, + pub read_length: GlobalId, + pub write_count: GlobalId, + pub write_length: GlobalId, + pub runtime_error: FunctionId, +} + +impl ChargeContext { + fn word_cost(&self, name: &ClarityName) -> Option<&WordCost> { + match self.epoch { + StacksEpochId::Epoch10 => panic!("clarity did not exist in epoch 1"), + StacksEpochId::Epoch20 => clar1::WORD_COSTS.get(name), + StacksEpochId::Epoch2_05 => clar2::WORD_COSTS.get(name), + StacksEpochId::Epoch21 + | StacksEpochId::Epoch22 + | StacksEpochId::Epoch23 + | StacksEpochId::Epoch24 + | StacksEpochId::Epoch25 + | StacksEpochId::Epoch30 + | StacksEpochId::Epoch31 => clar3::WORD_COSTS.get(name), + } + } +} + +#[derive(Debug, Clone, Copy)] +struct WordCost { + runtime: Caf, + read_count: Caf, + read_length: Caf, + write_count: Caf, + write_length: Caf, +} + +/// Cost assessment function +#[derive(Debug, Clone, Copy)] +enum Caf { + /// Constant cost + Constant(u32), + /// Linear cost, scaling with `n` + /// + /// a * n + b + Linear { a: u64, b: u64 }, + /// Logarithmic cost, scaling with `n` + /// + /// a * log2(n) + b + LogN { a: u64, b: u64 }, + /// Linear logarithmic cost, scaling with `n` + /// + /// a * n * log2(n) + b + NLogN { a: u64, b: u64 }, + /// Zero cost - equivalent to `Constant(0)` + None, +} + +impl ChargeContext { + fn emit( + &self, + instrs: &mut InstrSeqBuilder, + module: &Module, + cost: &WordCost, + n: Scalar, + ) -> Result<()> { + self.emit_with_caf( + instrs, + module, + cost.runtime, + self.runtime, + ErrorMap::CostOverrunRuntime as _, + n, + )?; + self.emit_with_caf( + instrs, + module, + cost.read_count, + self.read_count, + ErrorMap::CostOverrunReadCount as _, + n, + )?; + self.emit_with_caf( + instrs, + module, + cost.read_length, + self.read_length, + ErrorMap::CostOverrunReadLength as _, + n, + )?; + self.emit_with_caf( + instrs, + module, + cost.write_count, + self.write_count, + ErrorMap::CostOverrunWriteCount as _, + n, + )?; + self.emit_with_caf( + instrs, + module, + cost.write_length, + self.write_length, + ErrorMap::CostOverrunWriteLength as _, + n, + )?; + + Ok(()) + } + + fn emit_with_caf( + &self, + instrs: &mut InstrSeqBuilder, + module: &Module, + params: Caf, + global: GlobalId, + err_code: i32, + n: impl Into, + ) -> Result<()> { + match params { + Caf::Constant(cost) => { + caf_const(instrs, module, global, self.runtime_error, err_code, cost) + } + Caf::Linear { a, b } => caf_linear( + instrs, + module, + global, + self.runtime_error, + err_code, + n, + a, + b, + ), + Caf::LogN { a, b } => caf_logn( + instrs, + module, + global, + self.runtime_error, + err_code, + n, + a, + b, + ), + Caf::NLogN { a, b } => caf_nlogn( + instrs, + module, + global, + self.runtime_error, + err_code, + n, + a, + b, + ), + Caf::None => Ok(()), + } + } +} + +fn caf_const( + instrs: &mut InstrSeqBuilder, + module: &Module, + global: GlobalId, + error: FunctionId, + err_code: i32, + cost: impl Into, +) -> Result<()> { + let cost = cost.into(); + + // global pushed onto the stack to subtract from later + instrs.global_get(global); + + // cost + instrs.scalar_get(module, cost)?; + + // global -= cost + instrs + .binop(BinaryOp::I64Sub) + .global_set(global) + .global_get(global) + .i64_const(0) + .binop(BinaryOp::I64LtS) + .if_else( + None, + |builder| { + builder.i32_const(err_code); + builder.call(error); + }, + |_| {}, + ); + + Ok(()) +} + +#[allow(clippy::too_many_arguments)] +fn caf_linear( + instrs: &mut InstrSeqBuilder, + module: &Module, + global: GlobalId, + error: FunctionId, + err_code: i32, + n: impl Into, + a: u64, + b: u64, +) -> Result<()> { + let n = n.into(); + + // global pushed onto the stack to subtract from later + instrs.global_get(global); + + // cost = a * n + b + instrs + // n + .scalar_get(module, n)? + // a * + .i64_const(a as _) + .binop(BinaryOp::I64Mul) + // b + + .i64_const(b as _) + .binop(BinaryOp::I64Add); + + // global -= cost + instrs + .binop(BinaryOp::I64Sub) + .global_set(global) + .global_get(global) + .i64_const(0) + .binop(BinaryOp::I64LtS) + .if_else( + None, + |builder| { + builder.i32_const(err_code); + builder.call(error); + }, + |_| {}, + ); + + Ok(()) +} + +#[allow(clippy::too_many_arguments)] +fn caf_logn( + instrs: &mut InstrSeqBuilder, + module: &Module, + global: GlobalId, + error: FunctionId, + err_code: i32, + n: impl Into, + a: u64, + b: u64, +) -> Result<()> { + let n = n.into(); + + // global pushed onto the stack to subtract from later + instrs.global_get(global); + + // cost = a * log2(n) + b + instrs + // log2(n) + // 63 minus leading zeros in `n` + // n *must* be larger than 0 + .i64_const(63) + .scalar_get(module, n)? + .unop(UnaryOp::I64Clz) + .binop(BinaryOp::I64Sub) + // a * + .i64_const(a as _) + .binop(BinaryOp::I64Mul) + // b + + .i64_const(b as _) + .binop(BinaryOp::I64Add); + + // global -= cost + instrs + .binop(BinaryOp::I64Sub) + .global_set(global) + .global_get(global) + .i64_const(0) + .binop(BinaryOp::I64LtS) + .if_else( + None, + |builder| { + builder.i32_const(err_code); + builder.call(error); + }, + |_| {}, + ); + + Ok(()) +} + +#[allow(clippy::too_many_arguments)] +fn caf_nlogn( + instrs: &mut InstrSeqBuilder, + module: &Module, + global: GlobalId, + error: FunctionId, + err_code: i32, + n: impl Into, + a: u64, + b: u64, +) -> Result<()> { + let n = n.into(); + + // global pushed onto the stack to subtract from later + instrs.global_get(global); + + // cost = a * n * log2(n) + b + instrs + // log2(n) + // 63 minus leading zeros in `n` + // n *must* be larger than 0 + .i64_const(63) + .scalar_get(module, n)? + .unop(UnaryOp::I64Clz) + .binop(BinaryOp::I64Sub) + // n * + .scalar_get(module, n)? + .binop(BinaryOp::I64Mul) + // a * + .i64_const(a as _) + .binop(BinaryOp::I64Mul) + // b + + .i64_const(b as _) + .binop(BinaryOp::I64Add); + + // global -= cost + instrs + .binop(BinaryOp::I64Sub) + .global_set(global) + .global_get(global) + .i64_const(0) + .binop(BinaryOp::I64LtS) + .if_else( + None, + |builder| { + builder.i32_const(err_code); + builder.call(error); + }, + |_| {}, + ); + + Ok(()) +} + +#[cfg(test)] +mod caf { + //! The code in this module tests that the code generation in the `caf_*` functions is correct, + //! *not* that the code generation of each word is correct. + + use super::*; + + #[test] + fn constant() { + let initial_cost_val = 1000000; + + for cost in 1..100 { + let final_cost_val = + execute_with_caf(0, initial_cost_val, |local| (Caf::Constant(cost), local)) + .expect("execution with enough fuel should succeed"); + + assert_eq!( + final_cost_val, + initial_cost_val - cost as i64, + "should decrement accurately" + ); + } + } + + #[test] + fn linear() { + let initial_val = 1000000; + + let a = 2; + let b = 3; + + for n in 0..100 { + let cost = a * n + b; + + let final_val = execute_with_caf(n, initial_val, |local| { + ( + Caf::Linear { + a: a as _, + b: b as _, + }, + local, + ) + }) + .expect("execution with enough fuel should succeed"); + + assert_eq!( + final_val, + initial_val - cost as i64, + "should decrement accurately" + ); + } + } + + #[test] + fn logn() { + let initial_val = 1000000; + + let a = 2; + let b = 3; + + // cost = (+ (* a (log2 n)) b)) + + for n in 1..100u32 { + let cost = a * n.ilog2() + b; + + let final_val = execute_with_caf(n as _, initial_val, |local| { + ( + Caf::LogN { + a: a as _, + b: b as _, + }, + local, + ) + }) + .expect("execution with enough fuel should succeed"); + + assert_eq!( + final_val, + initial_val - cost as i64, + "should decrement accurately" + ); + } + } + + #[test] + fn nlogn() { + let initial_val = 1000000; + + let a = 2; + let b = 3; + + // cost = (+ (* a (* n (log2 n))) b)) + + for n in 1..100u32 { + let cost = a * n * n.ilog2() + b; + + let final_val = execute_with_caf(n as _, initial_val, |local| { + ( + Caf::NLogN { + a: a as _, + b: b as _, + }, + local, + ) + }) + .expect("execution with enough fuel should succeed"); + + assert_eq!( + final_val, + initial_val - cost as i64, + "should decrement accurately" + ); + } + } + + #[test] + fn none() { + let initial_val = 2; + let fn_arg = 0; + + let final_val = execute_with_caf(fn_arg, initial_val, |local| (Caf::None, local)) + .expect("execution with enough fuel should succeed"); + + assert_eq!(final_val, initial_val, "none caf should not cost"); + } + + const ERR_CODE: i32 = -42; + + fn execute_with_caf>( + arg: i32, + initial: i64, + caf: impl FnOnce(LocalId) -> (Caf, S), + ) -> Result { + use wasmtime::{Engine, Linker, Module, Store}; + + let engine = Engine::default(); + let binary = module_with_caf(caf); + let module = Module::from_binary(&engine, &binary).unwrap(); + + let mut linker = Linker::<()>::new(&engine); + let mut store = Store::new(&engine, ()); + + linker.define_cost_globals(&mut store).unwrap(); + linker + .set_cost_meter( + &mut store, + CostMeter { + runtime: initial, + read_count: 0, + read_length: 0, + write_count: 0, + write_length: 0, + }, + ) + .unwrap(); + + let instance = linker.instantiate(&mut store, &module).unwrap(); + + let func = instance + .get_typed_func::(&mut store, "identity") + .unwrap(); + let err_code = instance.get_global(&mut store, "err-code").unwrap(); + + match func.call(&mut store, arg) { + Ok(_) => Ok(linker.get_cost_meter(&mut store).unwrap().runtime), + Err(_) => Err(err_code.get(&mut store).unwrap_i64()), + } + } + + // The functions generated here is extremely simple (a: i32) -> a, but still allows for + // understanding the runtime characteristics of any `Caf`. + fn module_with_caf>(caf: impl FnOnce(LocalId) -> (Caf, S)) -> Vec { + use walrus::ir::Value; + use walrus::{FunctionBuilder, InitExpr, Module, ValType}; + + let mut module = Module::default(); + + // we put in all the globals, but we only use `cost-runtime` + let (cost_global, _) = + module.add_import_global("clarity", "cost-runtime", ValType::I64, true); + module.add_import_global("clarity", "cost-read-count", ValType::I64, true); + module.add_import_global("clarity", "cost-read-length", ValType::I64, true); + module.add_import_global("clarity", "cost-write-count", ValType::I64, true); + module.add_import_global("clarity", "cost-write-length", ValType::I64, true); + + let error_global = + module + .globals + .add_local(ValType::I32, true, InitExpr::Value(Value::I32(0))); + + let arg = module.locals.add(ValType::I32); + + // runtime error that takes an I32 and traps, similar to the stdlib + let mut error = FunctionBuilder::new(&mut module.types, &[ValType::I32], &[]); + let mut body = error.func_body(); + body.local_get(arg); + body.global_set(error_global); + body.unreachable(); + let error = error.finish(vec![arg], &mut module.funcs); + + let mut identity = + FunctionBuilder::new(&mut module.types, &[ValType::I32], &[ValType::I32]); + + let mut body = identity.func_body(); + + let (caf, scalar) = caf(arg); + match caf { + Caf::Constant(n) => { + caf_const(&mut body, &module, cost_global, error, ERR_CODE, n).unwrap() + } + Caf::Linear { a, b } => caf_linear( + &mut body, + &module, + cost_global, + error, + ERR_CODE, + scalar, + a, + b, + ) + .unwrap(), + Caf::LogN { a, b } => caf_logn( + &mut body, + &module, + cost_global, + error, + ERR_CODE, + scalar, + a, + b, + ) + .unwrap(), + Caf::NLogN { a, b } => caf_nlogn( + &mut body, + &module, + cost_global, + error, + ERR_CODE, + scalar, + a, + b, + ) + .unwrap(), + Caf::None => {} + } + body.local_get(arg); + let identity = identity.finish(vec![arg], &mut module.funcs); + + module.exports.add("identity", identity); + module.exports.add("err-code", error_global); + + module.emit_wasm() + } +} + +#[cfg(test)] +mod word { + use super::*; + + use clarity::vm::ClarityVersion; + + use crate::tools::TestEnvironment; + + #[inline(always)] + fn execute_snippet( + epoch: StacksEpochId, + version: ClarityVersion, + snippet: &str, + expected_cost: CostMeter, + ) { + let mut env = TestEnvironment::new_with_cost(epoch, version); + + env.init_contract_with_snippet("snippet", snippet) + .expect("init_contract should succeed"); + let cost_tracker = env.cost_tracker; + + let cost = CostMeter::from(cost_tracker.get_total()); + assert_eq!(cost, expected_cost, "'cost' should match 'expected_cost'"); + } + + macro_rules! epoch_for_version { + (1) => { + StacksEpochId::Epoch20 + }; + (2) => { + StacksEpochId::Epoch21 + }; + (3) => { + StacksEpochId::Epoch31 + }; + } + + macro_rules! decl_test { + ($version:literal, $name:literal, $snippet:literal, $expected_cost:expr) => { + paste::paste! { + #[test] + fn [<$name _ v $version >]() { + let epoch = epoch_for_version!($version); + let version = ClarityVersion::default_for_epoch(epoch); + execute_snippet(epoch, version, $snippet, $expected_cost); + } + } + }; + } + + macro_rules! decl_tests { + ($name:literal, $snippet:literal, { $($version:literal => $cost:expr),* $(,)? }) => { + $( + decl_test!($version, $name, $snippet, $cost); + )* + } + } + + decl_tests!("add", "(+ 1 2 3)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 158, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("sub", "(- 10 9 1)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 158, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("mul", "(* 2 5 10)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("div", "(/ 10 5 2)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + + // TODO: + + decl_tests!("log2", "(log2 1000)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 133, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("mod", "(mod 2 3)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 141, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("pow", "(pow 2 3)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 143, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("sqrti", "(sqrti 11)", { + 1 => CostMeter { runtime: 1000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 142, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("bitwise_and", "(bit-and 24 16)", { + 3 => CostMeter { runtime: 142, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("bitwise_or", "(bit-or 24 16)", { + 3 => CostMeter { runtime: 142, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("bitwise_xor", "(bit-xor 1 2)", { + 3 => CostMeter { runtime: 142, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("bitwise_not", "(bit-not 3)", { + 3 => CostMeter { runtime: 142, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("bitwise_lshift", "(bit-shift-left 2 u1)", { + 3 => CostMeter { runtime: 142, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("bitwise_rshift", "(bit-shift-right 2 u1)", { + 3 => CostMeter { runtime: 142, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("buf_to_int_be", "(buff-to-int-be 0x01)", { + 3 => CostMeter { runtime: 142, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("buf_to_int_le", "(buff-to-int-le 0x01)", { + 3 => CostMeter { runtime: 142, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("buf_to_uint_be", "(buff-to-uint-be 0x01)", { + 3 => CostMeter { runtime: 142, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("buf_to_uint_le", "(buff-to-uint-le 0x01)", { + 3 => CostMeter { runtime: 142, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("gt_int", "(> 1 2)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("gte_int", "(>= 1 2)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("lt_int", "(< 1 2)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("lte_int", "(<= 1 2)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("gt_buf", "(> 0xffff 0x4242)", { + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("gte_buf", "(>= 0xffff 0x4242)", { + 2 => CostMeter { runtime: 170, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("lt_buf", "(< 0xffff 0x4242)", { + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("lte_buf", "(<= 0xffff 0x4242)", { + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("or", "(or true false)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("and", "(and true false)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("not", "(not true)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("to_int", "(to-int u238)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("to_uint", "(to-uint 238)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("int_to_ascii", "(int-to-ascii 1)", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("int_to_utf8", "(int-to-utf8 1)", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("string_to_int", "(string-to-int? \"1\")", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("string_to_uint", "(string-to-uint? \"1\")", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("hash160_int", "(hash160 0)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("keccak256_int", "(keccak256 0)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("sha256_int", "(sha256 0)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("sha512_int", "(sha512 0)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("sha512_256_int", "(sha512/256 0)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("hash160_buf", "(hash160 0xffff)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("keccak256_buf", "(keccak256 0xffff)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("sha256_buf", "(sha256 0xffff)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("sha512_buf", "(sha512 0xffff)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("sha512_256_buf", "(sha512/256 0xffff)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("stx_burn", "(stx-burn? u100 'S1G2081040G2081040G2081040G208105NK8PE5)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("stx_get_balance", "(stx-get-balance 'S1G2081040G2081040G2081040G208105NK8PE5)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("stx_get_account", "(stx-account 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("principal_construct", "(principal-construct? 0x1a 0xfa6bf38ed557fe417333710d6033e9419391a320)", { + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("principal_destruct", "(principal-destruct? 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6)", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + + decl_tests!("let", "(let ((a 42)) a)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("at_block", "(at-block 0x0000000000000000000000000000000000000000000000000000000000000000 1)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("get_block_info", "(get-block-info? time u0)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("get_burn_block_info", "(get-burn-block-info? header-hash u677050)", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("get_stacks_block_info", "(get-stacks-block-info? time u0)", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("get_tenure_info", "(get-tenure-info? time u0)", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("asserts", "(asserts! true 1)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("filter", "(filter not (list true false true false))", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("if", "(if true 1 2)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("match", "(match (some 1) value 1 2)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("try", "(try! (some 1))", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("unwrap", "(unwrap! (ok 1) 1)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("unwrap_err", "(unwrap-err! (err 1) false)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("from_consensus_buff", "(from-consensus-buff? int 0x0000000000000000000000000000000001)", { + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("to_consensus_buff", "(to-consensus-buff? 1)", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("as_contract", "(as-contract 1)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + // decl_tests!("contract_call", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + decl_tests!("begin", "(begin 1)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("unwrap_err_panic", "(unwrap-err-panic (err 1))", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("unwrap_panic", "(unwrap-panic (some 1))", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("get_data_var", "(define-data-var i int 0) \ + (var-get i)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("set_data_var", "(define-data-var i int 0) \ + (var-set i 1)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("default_to", "(default-to 0 none)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("err", "(err true)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("ok", "(ok true)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("some", "(some true)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("index_of", "(index-of? 0xfb01 0x01) ", { + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("is_eq", "(is-eq 1 1)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("map_definition", "(define-map squares { x: int } { y: int })", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("map_delete", "(define-map squares { x: int } { y: int }) \ + (map-delete squares { x: 1 })", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("map_get", "(define-map squares { x: int } { y: int }) \ + (map-get? squares { x: 1 })", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("map_insert", "(define-map squares { x: int } { y: int }) \ + (map-insert squares { x: 1 } { y: 1 })", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("map_set", "(define-map squares { x: int } { y: int }) \ + (map-set squares { x: 1 } { y: 1 })", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + // decl_tests!("contract_of", "(contract-of contract)", { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + decl_tests!("is_none", "(is-none none)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("is_some", "(is-some (some 1))", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("is_standard", "(is-standard 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6)", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("principal_construct", "(principal-construct? 0x1a 0xfa6bf38ed557fe417333710d6033e9419391a320)", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("principal_of", "(principal-of? 0x03adb8de4bfb65db2cfd6120d55c6526ae9c52e675db7e47308636534ba7786110)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("print", "(print 0x1234567890)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("is_err", "(is-err (err 1))", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("is_ok", "(is-ok (ok 1))", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("recover", "(secp256k1-recover? 0xde5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f04 0x8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a1301)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("verify", "(secp256k1-verify 0xde5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f04 0x8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a1301 0x03adb8de4bfb65db2cfd6120d55c6526ae9c52e675db7e47308636534ba7786110)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("append", "(append (list 1 2 3 4) 5)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("as_max_len", "(as-max-len? 0x1234567890 u2)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("concat", "(concat 0x0102 0x0304)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("element_at", "(element-at? 0x1234567890 u2)", { + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("fold", "(fold * (list 2 2 2) 1)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("len", "(len 0x010203)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("list_cons", "(list 1 2 3)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("map", "(define-private (zero-or-one (char (buff 1))) \ + (if (is-eq char 0x00) 0x00 0x01)) \ + (map zero-or-one 0x000102)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("replace_at", "(replace-at? 0x00112233 u2 0x44)", { + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("slice", "(slice? 0x1234567890 u1 u3)", { + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("stx_transfer", "(stx-burn? u100 'S1G2081040G2081040G2081040G208105NK8PE5)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("stx_transfer_memo", "(stx-transfer-memo? u100 'S1G2081040G2081040G2081040G208105NK8PE5 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM 0x12345678)", { + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("ft_burn", "(define-fungible-token st) \ + (ft-mint? st u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("nft_burn", "(define-non-fungible-token st int) \ + (nft-mint? st 1 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("ft_get_balance", "(define-fungible-token st) \ + (ft-get-balance st 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("nft_get_owner", "(define-non-fungible-token st int) \ + (nft-mint? st 1 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) \ + (nft-get-owner? st 1)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("ft_get_supply", "(define-fungible-token st) \ + (ft-get-supply st)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("ft_mint", "(define-fungible-token st) \ + (ft-mint? st u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("nft_mint", "(define-non-fungible-token st int) \ + (nft-mint? st 1 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("ft_transfer", "(define-fungible-token st) \ + (ft-mint? st u100 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR) \ + (ft-transfer? st u50 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) \ + (ft-transfer? st u60 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("nft_transfer", "(define-non-fungible-token st int) \ + (nft-mint? st 1 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR) \ + (nft-transfer? st 1 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + // decl_tests!("impl_trait", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + // decl_tests!("use_trait", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + decl_tests!("tuple_cons", "(tuple (b 0x0102) (id 1337))", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("tuple_get", "(get id (tuple (b 0x0102) (id 1337)))", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + decl_tests!("tuple_merge", "(merge {a: 1} {b: 2})", { + 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + }); + + // decl_tests!("define_constant", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + // decl_tests!("define_data_var", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + // decl_tests!("define_private_function", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + // decl_tests!("define_public_function", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + // decl_tests!("define_readonly_function", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + // decl_tests!("ft_define", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + // decl_tests!("nft_define", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); + // decl_tests!("define_trait", todo!(), { + // 1 => CostMeter { runtime: 4000, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 2 => CostMeter { runtime: 199, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // 3 => CostMeter { runtime: 164, read_count: 0, read_length: 0, write_count: 0, write_length: 0 }, + // }); +} diff --git a/clar2wasm/src/cost/clar1.rs b/clar2wasm/src/cost/clar1.rs index d24ebe6e8..7ed7f9fa5 100644 --- a/clar2wasm/src/cost/clar1.rs +++ b/clar2wasm/src/cost/clar1.rs @@ -9,7 +9,7 @@ use crate::words::bindings::Let; use crate::words::blockinfo::{AtBlock, GetBlockInfo, GetStacksBlockInfo, GetTenureInfo}; use crate::words::comparison::{CmpGeq, CmpGreater, CmpLeq, CmpLess}; use crate::words::conditionals::{And, Asserts, Filter, If, Match, Or, Try, Unwrap, UnwrapErr}; -use crate::words::contract::{AsContract, ContractCall}; +use crate::words::contract::ContractCall; use crate::words::control_flow::{Begin, UnwrapErrPanic, UnwrapPanic}; use crate::words::data_vars::{GetDataVar, SetDataVar}; use crate::words::default_to::DefaultTo; @@ -228,7 +228,7 @@ lazy_static! { map.insert( Keccak256.name(), WordCost { - runtime: Linear { a: 1, b: 127 }, + runtime: Linear { a: 1000, b: 1000 }, read_count: None, read_length: None, write_count: None, @@ -265,15 +265,14 @@ lazy_static! { write_length: None, }, ); - // TODO: check if this indeed costs nothing (SUSPICIOUS) map.insert( StxBurn.name(), WordCost { - runtime: None, - read_count: None, - read_length: None, - write_count: None, - write_length: None, + runtime: Constant(1000), + read_count: Constant(2), + read_length: Constant(1), + write_count: Constant(2), + write_length: Constant(1), }, ); map.insert( @@ -431,16 +430,6 @@ lazy_static! { write_length: None, }, ); - map.insert( - AsContract.name(), - WordCost { - runtime: Constant(138), - read_count: None, - read_length: None, - write_count: None, - write_length: None, - }, - ); map.insert( ContractCall.name(), WordCost { diff --git a/clar2wasm/src/cost/clar2.rs b/clar2wasm/src/cost/clar2.rs index 929a7ef90..c8e4c6cf6 100644 --- a/clar2wasm/src/cost/clar2.rs +++ b/clar2wasm/src/cost/clar2.rs @@ -9,7 +9,7 @@ use crate::words::bindings::Let; use crate::words::blockinfo::{AtBlock, GetBlockInfo, GetStacksBlockInfo, GetTenureInfo}; use crate::words::comparison::{CmpGeq, CmpGreater, CmpLeq, CmpLess}; use crate::words::conditionals::{And, Asserts, Filter, If, Match, Or, Try, Unwrap, UnwrapErr}; -use crate::words::contract::{AsContract, ContractCall}; +use crate::words::contract::ContractCall; use crate::words::control_flow::{Begin, UnwrapErrPanic, UnwrapPanic}; use crate::words::data_vars::{GetDataVar, SetDataVar}; use crate::words::default_to::DefaultTo; @@ -20,11 +20,13 @@ use crate::words::logical::Not; use crate::words::maps::{MapDelete, MapGet, MapInsert, MapSet}; use crate::words::noop::{ContractOf, ToInt, ToUint}; use crate::words::options::{IsNone, IsSome}; -use crate::words::principal::{IsStandard, PrincipalOf}; +use crate::words::principal::PrincipalOf; use crate::words::print::Print; use crate::words::responses::{IsErr, IsOk}; use crate::words::secp256k1::{Recover, Verify}; -use crate::words::sequences::{Append, AsMaxLen, Concat, ElementAt, Fold, Len, ListCons, Map}; +use crate::words::sequences::{ + Append, AsMaxLen, Concat, ElementAt, Fold, Len, ListCons, Map, Slice, +}; use crate::words::stx::{StxBurn, StxGetBalance, StxTransfer}; use crate::words::tokens::{ BurnFungibleToken, BurnNonFungibleToken, GetBalanceOfFungibleToken, GetOwnerOfNonFungibleToken, @@ -128,7 +130,7 @@ lazy_static! { map.insert( CmpGreater.name(), WordCost { - runtime: Constant(170), + runtime: Linear { a: 7, b: 128 }, read_count: None, read_length: None, write_count: None, @@ -138,7 +140,7 @@ lazy_static! { map.insert( CmpGeq.name(), WordCost { - runtime: Constant(170), + runtime: Linear { a: 7, b: 128 }, read_count: None, read_length: None, write_count: None, @@ -148,7 +150,7 @@ lazy_static! { map.insert( CmpLess.name(), WordCost { - runtime: Constant(170), + runtime: Linear { a: 7, b: 128 }, read_count: None, read_length: None, write_count: None, @@ -158,7 +160,7 @@ lazy_static! { map.insert( CmpLeq.name(), WordCost { - runtime: Constant(170), + runtime: Linear { a: 7, b: 128 }, read_count: None, read_length: None, write_count: None, @@ -265,15 +267,14 @@ lazy_static! { write_length: None, }, ); - // TODO: check if this indeed costs nothing (SUSPICIOUS) map.insert( StxBurn.name(), WordCost { - runtime: None, - read_count: None, - read_length: None, - write_count: None, - write_length: None, + runtime: Constant(612), + read_count: Constant(2), + read_length: Constant(1), + write_count: Constant(2), + write_length: Constant(1), }, ); map.insert( @@ -431,16 +432,6 @@ lazy_static! { write_length: None, }, ); - map.insert( - AsContract.name(), - WordCost { - runtime: Constant(138), - read_count: None, - read_length: None, - write_count: None, - write_length: None, - }, - ); map.insert( ContractCall.name(), WordCost { @@ -551,6 +542,16 @@ lazy_static! { write_length: None, }, ); + map.insert( + Slice.name(), + WordCost { + runtime: Constant(498), + read_count: None, + read_length: None, + write_count: None, + write_length: None, + }, + ); map.insert( MapGet.name(), WordCost { @@ -623,16 +624,6 @@ lazy_static! { write_length: None, }, ); - map.insert( - IsStandard.name(), - WordCost { - runtime: Constant(127), - read_count: None, - read_length: None, - write_count: None, - write_length: None, - }, - ); map.insert( PrincipalOf.name(), WordCost { diff --git a/clar2wasm/src/cost/clar3.rs b/clar2wasm/src/cost/clar3.rs index 8a8150a20..69fa5b01c 100644 --- a/clar2wasm/src/cost/clar3.rs +++ b/clar2wasm/src/cost/clar3.rs @@ -415,6 +415,16 @@ lazy_static! { write_length: None, }, ); + map.insert( + Construct.name(), + WordCost { + runtime: Constant(398), + read_count: None, + read_length: None, + write_count: None, + write_length: None, + }, + ); map.insert( Destruct.name(), WordCost { @@ -439,11 +449,11 @@ lazy_static! { map.insert( StxBurn.name(), WordCost { - runtime: None, - read_count: None, - read_length: None, - write_count: None, - write_length: None, + runtime: Constant(549), + read_count: Constant(2), + read_length: Constant(1), + write_count: Constant(2), + write_length: Constant(1), }, ); map.insert( @@ -833,16 +843,6 @@ lazy_static! { write_length: None, }, ); - map.insert( - Construct.name(), - WordCost { - runtime: Constant(398), - read_count: None, - read_length: None, - write_count: None, - write_length: None, - }, - ); map.insert( PrincipalOf.name(), WordCost { diff --git a/clar2wasm/src/error_mapping.rs b/clar2wasm/src/error_mapping.rs index ed533b0fb..4f8af5c82 100644 --- a/clar2wasm/src/error_mapping.rs +++ b/clar2wasm/src/error_mapping.rs @@ -219,6 +219,8 @@ fn from_runtime_error_code( ) -> Error { let runtime_error_code = get_global_i32(&instance, &mut store, "runtime-error-code"); + println!("error code: {runtime_error_code}"); + match ErrorMap::from(runtime_error_code) { ErrorMap::NotClarityError => Error::Wasm(WasmError::Runtime(e)), ErrorMap::ArithmeticOverflow => { diff --git a/clar2wasm/src/initialize.rs b/clar2wasm/src/initialize.rs index b25acb97d..3f556aa12 100644 --- a/clar2wasm/src/initialize.rs +++ b/clar2wasm/src/initialize.rs @@ -7,9 +7,10 @@ use clarity::vm::{CallStack, ContractContext, Value}; use stacks_common::types::chainstate::StacksBlockId; use wasmtime::{Linker, Module, Store}; +use crate::cost::{CostLinker, CostMeter}; use crate::linker::link_host_functions; use crate::wasm_utils::*; -use crate::{error_mapping, CostLinker}; +use crate::{error_mapping, AccessCostMeter}; // The context used when making calls into the Wasm module. pub struct ClarityWasmContext<'a, 'b> { @@ -312,6 +313,16 @@ impl<'a, 'b> ClarityWasmContext<'a, 'b> { } } +/// Successful return of a contract initialization +/// +/// Contains the result of the execution of the top-level expressions, and the cost of executing +/// them. +#[derive(Debug, PartialEq)] +pub struct ContractInit { + pub ret: Option, + pub cost: CostMeter, +} + /// Initialize a contract, executing all of the top-level expressions and /// registering all of the definitions in the context. Returns the value /// returned from the last top-level expression. @@ -320,7 +331,7 @@ pub fn initialize_contract( contract_context: &mut ContractContext, sponsor: Option, contract_analysis: &ContractAnalysis, -) -> Result, Error> { +) -> Result { let publisher: PrincipalData = contract_context.contract_identifier.issuer.clone().into(); let mut call_stack = CallStack::new(); @@ -407,15 +418,21 @@ pub fn initialize_contract( .and_then(|type_map| type_map.get_type_expected(expr)) }); - if let Some(return_type) = return_type { + let ret = if let Some(return_type) = return_type { let memory = instance .get_memory(&mut store, "memory") .ok_or(Error::Wasm(WasmError::MemoryNotFound))?; wasm_to_clarity_value(return_type, 0, &results, memory, &mut &mut store, epoch) - .map(|(val, _offset)| val) + .map(|(val, _offset)| val)? } else { - Ok(None) - } + None + }; + + let cost = linker + .get_used_cost(&mut store) + .map_err(|_| Error::Wasm(WasmError::GlobalNotFound("cost-*".to_string())))?; + + Ok(ContractInit { ret, cost }) } #[cfg(test)] diff --git a/clar2wasm/src/linker.rs b/clar2wasm/src/linker.rs index ed090eed6..b232fa2ea 100644 --- a/clar2wasm/src/linker.rs +++ b/clar2wasm/src/linker.rs @@ -15,6 +15,7 @@ use stacks_common::util::hash::{Keccak256Hash, Sha512Sum, Sha512Trunc256Sum}; use stacks_common::util::secp256k1::{secp256k1_recover, secp256k1_verify, Secp256k1PublicKey}; use wasmtime::{Caller, Engine, Instance, Linker, Memory, Module, Store}; +use crate::cost::CostLinker; use crate::initialize::ClarityWasmContext; use crate::wasm_utils::*; @@ -5903,7 +5904,10 @@ pub fn load_stdlib() -> Result<(Instance, Store<()>), wasmtime::Error> { let standard_lib = include_str!("standard/standard.wat"); let engine = Engine::default(); let mut store = Store::new(&engine, ()); - let linker = dummy_linker(&engine)?; + + let mut linker = dummy_linker(&engine)?; + linker.define_cost_globals(&mut store)?; + let module = Module::new(&engine, standard_lib)?; let instance = linker.instantiate(&mut store, &module)?; Ok((instance, store)) diff --git a/clar2wasm/src/tools.rs b/clar2wasm/src/tools.rs index 19f8e714d..083279ade 100644 --- a/clar2wasm/src/tools.rs +++ b/clar2wasm/src/tools.rs @@ -6,13 +6,14 @@ use std::collections::HashMap; use std::sync::LazyLock; +use clarity::boot_util::boot_code_id; use clarity::consts::{CHAIN_ID_MAINNET, CHAIN_ID_TESTNET}; use clarity::types::StacksEpochId; use clarity::vm::analysis::run_analysis; use clarity::vm::ast::build_ast; use clarity::vm::contexts::{EventBatch, GlobalContext}; use clarity::vm::contracts::Contract; -use clarity::vm::costs::LimitedCostTracker; +use clarity::vm::costs::{CostTracker, ExecutionCost, LimitedCostTracker}; use clarity::vm::database::ClarityDatabase; use clarity::vm::errors::{CheckErrors, Error, WasmError}; use clarity::vm::events::{SmartContractEventData, StacksTransactionEvent}; @@ -21,9 +22,12 @@ use clarity::vm::{eval_all, ClarityVersion, ContractContext, ContractName, Value use regex::Regex; use crate::compile; +use crate::cost::CostMeter; use crate::datastore::{BurnDatastore, Datastore, StacksConstants}; use crate::initialize::initialize_contract; +const DEFAULT_ENV_AMOUNT: u128 = 1_000_000_000; + #[derive(Clone)] pub struct TestEnvironment { contract_contexts: HashMap, @@ -31,23 +35,47 @@ pub struct TestEnvironment { pub version: ClarityVersion, datastore: Datastore, burn_datastore: BurnDatastore, - cost_tracker: LimitedCostTracker, events: Vec, - network: Network, + is_mainnet: bool, + chain_id: u32, + emit_cost_code: bool, + pub cost_tracker: LimitedCostTracker, } impl TestEnvironment { - pub fn new_with_amount(amount: u128, epoch: StacksEpochId, version: ClarityVersion) -> Self { + fn new_full( + amount: u128, + epoch: StacksEpochId, + version: ClarityVersion, + network: Network, + emit_cost_code: bool, + ) -> Self { + let (epoch, version) = if !Self::epoch_and_clarity_match(epoch, version) { + let valid_version = ClarityVersion::default_for_epoch(epoch); + println!( + "[WARN] Provided epoch ({epoch}) and Clarity version ({version}) do not match. \ + Defaulting to epoch ({epoch}) and Clarity version ({valid_version})." + ); + (epoch, valid_version) + } else { + (epoch, version) + }; + let constants = StacksConstants::default(); let burn_datastore = BurnDatastore::new(constants.clone()); let mut datastore = Datastore::new(); - let cost_tracker = LimitedCostTracker::new_free(); - let mut db = ClarityDatabase::new(&mut datastore, &burn_datastore, &burn_datastore); - db.begin(); - db.set_clarity_epoch_version(epoch) + let (is_mainnet, chain_id) = match network { + Network::Mainnet => (true, CHAIN_ID_MAINNET), + Network::Testnet => (false, CHAIN_ID_TESTNET), + }; + + let mut conn = ClarityDatabase::new(&mut datastore, &burn_datastore, &burn_datastore); + + conn.begin(); + conn.set_clarity_epoch_version(epoch) .expect("Failed to set epoch version."); - db.commit().expect("Failed to commit."); + conn.commit().expect("Failed to commit."); // Give one account a starting balance, to be used for testing. let recipient = PrincipalData::Standard(StandardPrincipalData::transient()); @@ -60,16 +88,49 @@ impl TestEnvironment { }) .expect("Failed to increment liquid supply."); - Self { + let mut env = Self { contract_contexts: HashMap::new(), epoch, version, datastore, burn_datastore, - cost_tracker, events: vec![], - network: Network::Testnet, + is_mainnet, + chain_id, + emit_cost_code, + cost_tracker: LimitedCostTracker::new_free(), + }; + + if env.emit_cost_code { + // we only load boot contracts if we need to track cost + for contract in BOOT_CONTRACTS { + let _ = env + .inner_init_contract_with_snippet( + contract.name, + true, + contract.code, + contract.version, + contract.epoch, + ) + .unwrap_or_else(|err| { + panic!( + "could not interpret boot contract: {}\nreason: {err}", + contract.name + ) + }); + } + + let limit = ExecutionCost::from(CostMeter::INIT); + + let mut conn = + ClarityDatabase::new(&mut env.datastore, &env.burn_datastore, &env.burn_datastore); + + env.cost_tracker = + LimitedCostTracker::new(env.is_mainnet, env.chain_id, limit, &mut conn, env.epoch) + .expect("Creating cost tracker should succeed") } + + env } /// Creates a new environment instance with the specified epoch and Clarity version. @@ -92,20 +153,24 @@ impl TestEnvironment { /// # Returns /// /// An instance of the environment configured with the validated epoch and Clarity version. - /// pub fn new(epoch: StacksEpochId, version: ClarityVersion) -> Self { - let (epoch, version) = if !Self::epoch_and_clarity_match(epoch, version) { - let valid_version = ClarityVersion::default_for_epoch(epoch); - println!( - "[WARN] Provided epoch ({epoch}) and Clarity version ({version}) do not match. \ - Defaulting to epoch ({epoch}) and Clarity version ({valid_version})." - ); - (epoch, valid_version) - } else { - (epoch, version) - }; + Self::new_full(DEFAULT_ENV_AMOUNT, epoch, version, Network::Testnet, false) + } + + pub fn new_with_amount(amount: u128, epoch: StacksEpochId, version: ClarityVersion) -> Self { + Self::new_full(amount, epoch, version, Network::Testnet, false) + } + + pub fn new_with_network( + epoch: StacksEpochId, + version: ClarityVersion, + network: Network, + ) -> Self { + Self::new_full(DEFAULT_ENV_AMOUNT, epoch, version, network, false) + } - Self::new_with_amount(1_000_000_000, epoch, version) + pub fn new_with_cost(epoch: StacksEpochId, version: ClarityVersion) -> Self { + Self::new_full(DEFAULT_ENV_AMOUNT, epoch, version, Network::Testnet, true) } /// Checks whether the given epoch and Clarity version are compatible. @@ -143,25 +208,35 @@ impl TestEnvironment { } } - pub fn new_with_network( - epoch: StacksEpochId, - version: ClarityVersion, - network: Network, - ) -> Self { - let mut env = Self::new(epoch, version); - env.network = network; - env + pub fn init_contract_with_snippet( + &mut self, + contract_name: &str, + snippet: &str, + ) -> Result, Error> { + self.inner_init_contract_with_snippet( + contract_name, + false, + snippet, + self.version, + self.epoch, + ) } - pub fn init_contract_with_snippet( + fn inner_init_contract_with_snippet( &mut self, contract_name: &str, + is_boot_contract: bool, snippet: &str, + version: ClarityVersion, + epoch: StacksEpochId, ) -> Result, Error> { - let contract_id = QualifiedContractIdentifier::new( - StandardPrincipalData::transient(), - (*contract_name).into(), - ); + let contract_id = match is_boot_contract { + false => QualifiedContractIdentifier::new( + StandardPrincipalData::transient(), + (*contract_name).into(), + ), + true => boot_code_id(contract_name, self.is_mainnet), + }; let mut compile_result = self .datastore @@ -171,10 +246,10 @@ impl TestEnvironment { snippet, &contract_id, LimitedCostTracker::new_free(), - self.version, - self.epoch, + version, + epoch, analysis_db, - false, + !is_boot_contract && self.emit_cost_code, ) .map_err(|e| CheckErrors::Expects(format!("Compilation failure {:?}", e))) }) @@ -200,13 +275,8 @@ impl TestEnvironment { &self.burn_datastore, ); - let (is_mainnet, chain_id) = match self.network { - Network::Mainnet => (true, CHAIN_ID_MAINNET), - Network::Testnet => (false, CHAIN_ID_TESTNET), - }; - let mut global_context = - GlobalContext::new(is_mainnet, chain_id, conn, cost_tracker, self.epoch); + GlobalContext::new(self.is_mainnet, self.chain_id, conn, cost_tracker, epoch); global_context.begin(); global_context .execute(|g| g.database.insert_contract_hash(&contract_id, snippet)) @@ -235,12 +305,16 @@ impl TestEnvironment { if let Some(events) = events { self.events.push(events); } - self.cost_tracker = global_context.cost_track; self.contract_contexts - .insert(contract_name.to_string(), contract_context); + .insert(contract_id.name.to_string(), contract_context); - Ok(return_val) + self.cost_tracker = global_context.cost_track; + self.cost_tracker + .add_cost(ExecutionCost::from(return_val.cost)) + .expect("Adding cost should succeed"); + + Ok(return_val.ret) } pub fn evaluate(&mut self, snippet: &str) -> Result, Error> { @@ -270,15 +344,14 @@ impl TestEnvironment { (*contract_name).into(), ); - let mut cost_tracker = LimitedCostTracker::new_free(); - std::mem::swap(&mut self.cost_tracker, &mut cost_tracker); + let contract_analysis = self.datastore.as_analysis_db().execute(|analysis_db| { + let mut cost_tracker = LimitedCostTracker::new_free(); - let mut contract_analysis = self.datastore.as_analysis_db().execute(|analysis_db| { // Parse the contract let ast = build_ast( &contract_id, snippet, - &mut self.cost_tracker, + &mut cost_tracker, self.version, self.epoch, ) @@ -311,16 +384,14 @@ impl TestEnvironment { &self.burn_datastore, ); - let (is_mainnet, chain_id) = match self.network { - Network::Mainnet => (true, CHAIN_ID_MAINNET), - Network::Testnet => (false, CHAIN_ID_TESTNET), - }; + let mut cost_tracker = LimitedCostTracker::new_free(); + std::mem::swap(&mut self.cost_tracker, &mut cost_tracker); let mut global_context = GlobalContext::new( - is_mainnet, - chain_id, + self.is_mainnet, + self.chain_id, conn, - contract_analysis.cost_track.take().unwrap(), + cost_tracker, self.epoch, ); global_context.begin(); @@ -352,11 +423,11 @@ impl TestEnvironment { if let Some(events) = events { self.events.push(events); } - self.cost_tracker = global_context.cost_track; self.contract_contexts .insert(contract_name.to_owned(), contract_context); + self.cost_tracker = global_context.cost_track; Ok(result) } @@ -516,6 +587,7 @@ impl CrossEvalResult { "Compiled and interpreted results diverge! {snippet}\ncompiled: {:?}\ninterpreted: {:?}", self.compiled, self.interpreted ); + compare_events( self.env_interpreted.get_events(), self.env_compiled.get_events(), @@ -859,6 +931,70 @@ pub fn crosscheck_with_network( ); } +// Represents a boot contract on disk +struct BootContract { + // The name of the contract + name: &'static str, + // The code of the contract + code: &'static str, + // Clarity version of the contract + version: ClarityVersion, + // Stacks epoch of deployment of the contract + epoch: StacksEpochId, +} + +macro_rules! boot_contract_code { + ($name:literal) => { + include_str!(concat!( + "../tests/contracts/boot-contracts/", + $name, + ".clar" + )) + }; +} + +macro_rules! boot_contract { + ($name:literal, $version:expr, $epoch:expr) => { + BootContract { + name: $name, + code: boot_contract_code!($name), + version: $version, + epoch: $epoch, + } + }; + ($base:literal, $name:literal, $version:literal, $epoch:literal) => { + BootContract { + name: $name, + code: concat!(boot_contract_code!($base), "\n", boot_contract_code!($name)), + version: $version, + epoch: $epoch, + } + }; +} + +const BOOT_CONTRACTS: &[BootContract] = + &[COSTS_V1, COSTS_V2, COSTS_V2_TESTNET, COST_VOTING, COSTS_V3]; + +const COSTS_V1: BootContract = + boot_contract!("costs", ClarityVersion::Clarity1, StacksEpochId::Epoch20); +const COSTS_V2: BootContract = boot_contract!( + "costs-2", + ClarityVersion::Clarity2, + StacksEpochId::Epoch2_05 +); +const COSTS_V2_TESTNET: BootContract = boot_contract!( + "costs-2-testnet", + ClarityVersion::Clarity2, + StacksEpochId::Epoch2_05 +); +const COST_VOTING: BootContract = boot_contract!( + "cost-voting", + ClarityVersion::Clarity2, + StacksEpochId::Epoch2_05 +); +const COSTS_V3: BootContract = + boot_contract!("costs-3", ClarityVersion::Clarity2, StacksEpochId::Epoch21); + #[cfg(test)] mod tests { @@ -996,4 +1132,12 @@ mod tests { assert!(!KnownBug::has_list_of_qualified_principal_issue(&res)); } } + + #[test] + fn find_boot_contracts() { + for boot_contract in std::fs::read_dir("tests/contracts/boot-contracts").unwrap() { + let b = boot_contract.unwrap(); + eprintln!("{:#?}", b.file_name()); + } + } } diff --git a/clar2wasm/src/wasm_generator.rs b/clar2wasm/src/wasm_generator.rs index 7eb155440..e8da5c1c6 100644 --- a/clar2wasm/src/wasm_generator.rs +++ b/clar2wasm/src/wasm_generator.rs @@ -344,15 +344,29 @@ impl WasmGenerator { pub fn with_cost_code(contract_analysis: ContractAnalysis) -> Result { let mut generator = Self::new(contract_analysis)?; + + let module = &mut generator.module; + + // NOTE: these are added here, and not in standard.wat, to more easily + // allow for cost tracking to be turned off without modifying + // downstream. See standard.wat for more details + + let (r, _) = module.add_import_global("clarity", "cost-runtime", ValType::I64, true); + let (rc, _) = module.add_import_global("clarity", "cost-read-count", ValType::I64, true); + let (rl, _) = module.add_import_global("clarity", "cost-read-length", ValType::I64, true); + let (wc, _) = module.add_import_global("clarity", "cost-write-count", ValType::I64, true); + let (wl, _) = module.add_import_global("clarity", "cost-write-length", ValType::I64, true); + generator.cost_context = Some(ChargeContext { - clarity_version: generator.contract_analysis.clarity_version, - runtime: get_global(&generator.module, "cost-runtime")?, - read_count: get_global(&generator.module, "cost-read-count")?, - read_length: get_global(&generator.module, "cost-read-length")?, - write_count: get_global(&generator.module, "cost-write-count")?, - write_length: get_global(&generator.module, "cost-write-length")?, - runtime_error: get_function(&generator.module, "stdlib.runtime-error")?, + epoch: generator.contract_analysis.epoch, + runtime: r, + read_count: rc, + read_length: rl, + write_count: wc, + write_length: wl, + runtime_error: get_function(module, "stdlib.runtime-error")?, }); + Ok(generator) } @@ -377,15 +391,6 @@ impl WasmGenerator { pub fn generate(mut self) -> Result { let expressions = std::mem::take(&mut self.contract_analysis.expressions); - if self.cost_context.is_some() { - let module = &mut self.module; - module.add_import_global("clarity", "cost-runtime", ValType::I64, true); - module.add_import_global("clarity", "cost-read-count", ValType::I64, true); - module.add_import_global("clarity", "cost-read-length", ValType::I64, true); - module.add_import_global("clarity", "cost-write-count", ValType::I64, true); - module.add_import_global("clarity", "cost-write-length", ValType::I64, true); - } - // Get the type of the last top-level expression with a return value // or default to `None`. let return_ty = expressions diff --git a/clar2wasm/src/words/arithmetic.rs b/clar2wasm/src/words/arithmetic.rs index 88702397a..66fecbd25 100644 --- a/clar2wasm/src/words/arithmetic.rs +++ b/clar2wasm/src/words/arithmetic.rs @@ -24,6 +24,8 @@ fn simple_typed_one_call( } }; + word.charge(generator, builder, 0)?; + let name = word.name(); let func = generator.func_by_name(&format!("stdlib.{name}-{type_suffix}")); diff --git a/clar2wasm/src/words/equal.rs b/clar2wasm/src/words/equal.rs index 5bb3b7fbd..ac7b400ae 100644 --- a/clar2wasm/src/words/equal.rs +++ b/clar2wasm/src/words/equal.rs @@ -10,7 +10,7 @@ use crate::cost::WordCharge; use crate::wasm_generator::{ clar2wasm_ty, drop_value, ArgumentsExt, GeneratorError, SequenceElementType, WasmGenerator, }; -use crate::wasm_utils::{check_argument_count, ArgumentCountCheck}; +use crate::wasm_utils::{check_argument_count, get_type_size, ArgumentCountCheck}; #[derive(Debug)] pub struct IsEq; @@ -165,9 +165,30 @@ impl ComplexWord for IndexOf { .local_set(end_offset); // STACK: [] + // compute the cost depending on the number of elements in sequence. + // we put seq_size on the stack to retrieve it later, + // and again on the stack for the cost computation. + builder.local_get(seq_size).local_get(seq_size); + match &elem_ty { + SequenceElementType::Byte => { + // nothing to change here + } + SequenceElementType::UnicodeScalar => { + // number of bytes / 4 + builder.i32_const(2).binop(BinaryOp::I32ShrU); + } + SequenceElementType::Other(ty) => { + // number of bytes / element size + builder + .i32_const(get_type_size(ty)) + .binop(BinaryOp::I32DivU); + } + } + builder.local_set(seq_size); self.charge(generator, builder, seq_size)?; - builder.local_get(seq_size).unop(UnaryOp::I32Eqz); + // seq_size was on the stack from before the cost computation + builder.local_tee(seq_size).unop(UnaryOp::I32Eqz); // STACK: [size] let ty = InstrSeqType::new( diff --git a/clar2wasm/tests/lib_tests.rs b/clar2wasm/tests/lib_tests.rs index be0023464..79559f0b4 100644 --- a/clar2wasm/tests/lib_tests.rs +++ b/clar2wasm/tests/lib_tests.rs @@ -110,7 +110,8 @@ macro_rules! test_multi_contract_init { None, &compile_result.contract_analysis, ) - .expect("Failed to initialize contract."); + .expect("Failed to initialize contract.") + .ret; let data_size = contract_context.data_size; global_context