From a6e2ccde8af39ec3e8b97a88bcfc69f91ba447a5 Mon Sep 17 00:00:00 2001 From: tac0turtle Date: Tue, 24 Feb 2026 22:07:51 +0100 Subject: [PATCH 1/9] add quint spec for stf --- crates/app/sdk/extensions/token/src/lib.rs | 2 +- crates/app/stf/Cargo.toml | 2 + crates/app/stf/tests/quint_conformance.rs | 566 ++++++++++++++++ justfile | 24 + specs/stf.qnt | 614 ++++++++++++++++++ specs/token.qnt | 26 + specs/traces/out_blockGasLimitTest_5.itf.json | 1 + .../out_bootstrapFailureTest_8.itf.json | 1 + specs/traces/out_bootstrapTest_7.itf.json | 1 + specs/traces/out_emptyBlockTest_0.itf.json | 1 + ...ut_executionFailureRollbackTest_3.itf.json | 1 + specs/traces/out_mixedOutcomesTest_6.itf.json | 1 + specs/traces/out_multiBlockTest_9.itf.json | 1 + specs/traces/out_outOfGasTest_4.itf.json | 1 + specs/traces/out_overwriteTest_10.itf.json | 1 + specs/traces/out_successfulTxTest_1.itf.json | 1 + .../out_validationFailureTest_2.itf.json | 1 + 17 files changed, 1244 insertions(+), 1 deletion(-) create mode 100644 crates/app/stf/tests/quint_conformance.rs create mode 100644 specs/stf.qnt create mode 100644 specs/token.qnt create mode 100644 specs/traces/out_blockGasLimitTest_5.itf.json create mode 100644 specs/traces/out_bootstrapFailureTest_8.itf.json create mode 100644 specs/traces/out_bootstrapTest_7.itf.json create mode 100644 specs/traces/out_emptyBlockTest_0.itf.json create mode 100644 specs/traces/out_executionFailureRollbackTest_3.itf.json create mode 100644 specs/traces/out_mixedOutcomesTest_6.itf.json create mode 100644 specs/traces/out_multiBlockTest_9.itf.json create mode 100644 specs/traces/out_outOfGasTest_4.itf.json create mode 100644 specs/traces/out_overwriteTest_10.itf.json create mode 100644 specs/traces/out_successfulTxTest_1.itf.json create mode 100644 specs/traces/out_validationFailureTest_2.itf.json diff --git a/crates/app/sdk/extensions/token/src/lib.rs b/crates/app/sdk/extensions/token/src/lib.rs index 1d3bda6..8d0db63 100644 --- a/crates/app/sdk/extensions/token/src/lib.rs +++ b/crates/app/sdk/extensions/token/src/lib.rs @@ -1,4 +1,5 @@ use evolve_core::account_impl; +pub mod generated; #[account_impl(Token)] pub mod account { @@ -177,7 +178,6 @@ mod tests { use crate::account::Token; // The generated account struct use crate::account::{ERR_NOT_ENOUGH_BALANCE, ERR_UNDERFLOW}; - use evolve_testing::MockEnv; /// Helper to initialize a `Token` with some default data. diff --git a/crates/app/stf/Cargo.toml b/crates/app/stf/Cargo.toml index e30800f..7bade5a 100644 --- a/crates/app/stf/Cargo.toml +++ b/crates/app/stf/Cargo.toml @@ -17,6 +17,8 @@ linkme = {version = "0.3", default-features = false, optional = true} [dev-dependencies] proptest = "1.4" +serde = { version = "1", features = ["derive"] } +serde_json = "1" [lints] workspace = true diff --git a/crates/app/stf/tests/quint_conformance.rs b/crates/app/stf/tests/quint_conformance.rs new file mode 100644 index 0000000..34ae482 --- /dev/null +++ b/crates/app/stf/tests/quint_conformance.rs @@ -0,0 +1,566 @@ +//! Conformance tests: replay Quint ITF traces against the real STF. +//! +//! This test reads ITF (Informal Trace Format) JSON files generated by +//! `quint test specs/stf.qnt --out-itf ...` and replays each trace against +//! the actual STF implementation, asserting that the real execution matches +//! the spec's expected outcomes. +//! +//! Run: `cargo test -p evolve_stf --test quint_conformance` +//! Regenerate traces: `quint test specs/stf.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json"` + +use borsh::{BorshDeserialize, BorshSerialize}; +use evolve_core::runtime_api::ACCOUNT_IDENTIFIER_PREFIX; +use evolve_core::storage_api::{StorageSetRequest, STORAGE_ACCOUNT_ID}; +use evolve_core::{ + AccountCode, AccountId, BlockContext, Environment, EnvironmentQuery, ErrorCode, FungibleAsset, + InvokableMessage, InvokeRequest, InvokeResponse, Message, ReadonlyKV, SdkResult, +}; +use evolve_stf::gas::StorageGasConfig; +use evolve_stf::Stf; +use evolve_stf_traits::{ + AccountsCodeStorage, BeginBlocker, Block as BlockTrait, EndBlocker, PostTxExecution, + StateChange, Transaction, TxValidator, WritableKV, +}; +use hashbrown::HashMap; +use serde::Deserialize; +use std::fs; +use std::path::Path; + +// --------------------------------------------------------------------------- +// ITF deserialization types +// --------------------------------------------------------------------------- + +#[derive(Deserialize)] +struct ItfTrace { + states: Vec, +} + +#[derive(Deserialize)] +struct ItfState { + block_height: ItfBigInt, + last_result: ItfBlockResult, + storage: ItfMap, Vec>>, +} + +#[derive(Deserialize, Clone, Debug)] +struct ItfBigInt { + #[serde(rename = "#bigint")] + value: String, +} + +impl ItfBigInt { + fn as_i64(&self) -> i64 { + self.value.parse().unwrap() + } + fn as_u64(&self) -> u64 { + self.value.parse().unwrap() + } +} + +#[derive(Deserialize)] +struct ItfMap { + #[serde(rename = "#map")] + entries: Vec<(K, V)>, +} + +#[derive(Deserialize)] +struct ItfBlockResult { + gas_used: ItfBigInt, + tx_results: Vec, + txs_skipped: ItfBigInt, +} + +#[derive(Deserialize)] +struct ItfTxResult { + gas_used: ItfBigInt, + result: ItfResult, +} + +#[derive(Deserialize)] +struct ItfResult { + ok: bool, + err_code: ItfBigInt, +} + +// --------------------------------------------------------------------------- +// STF test infrastructure (mirrors model_tests in lib.rs) +// --------------------------------------------------------------------------- + +#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] +struct TestMsg { + key: Vec, + value: Vec, + fail_after_write: bool, +} + +impl InvokableMessage for TestMsg { + const FUNCTION_IDENTIFIER: u64 = 1; + const FUNCTION_IDENTIFIER_NAME: &'static str = "test_exec"; +} + +#[derive(Clone, Debug)] +struct TestTx { + sender: AccountId, + recipient: AccountId, + request: InvokeRequest, + gas_limit: u64, + funds: Vec, + fail_validate: bool, +} + +impl Transaction for TestTx { + fn sender(&self) -> AccountId { + self.sender + } + fn recipient(&self) -> AccountId { + self.recipient + } + fn request(&self) -> &InvokeRequest { + &self.request + } + fn gas_limit(&self) -> u64 { + self.gas_limit + } + fn funds(&self) -> &[FungibleAsset] { + &self.funds + } + fn compute_identifier(&self) -> [u8; 32] { + [0u8; 32] + } +} + +#[derive(Clone)] +struct TestBlock { + height: u64, + time: u64, + txs: Vec, + gas_limit: u64, +} + +impl BlockTrait for TestBlock { + fn context(&self) -> BlockContext { + BlockContext::new(self.height, self.time) + } + fn txs(&self) -> &[TestTx] { + &self.txs + } + fn gas_limit(&self) -> u64 { + self.gas_limit + } +} + +#[derive(Default)] +struct NoopBegin; +impl BeginBlocker for NoopBegin { + fn begin_block(&self, _block: &TestBlock, _env: &mut dyn Environment) {} +} + +#[derive(Default)] +struct NoopEnd; +impl EndBlocker for NoopEnd { + fn end_block(&self, _env: &mut dyn Environment) {} +} + +#[derive(Default)] +struct Validator; +impl TxValidator for Validator { + fn validate_tx(&self, tx: &TestTx, _env: &mut dyn Environment) -> SdkResult<()> { + if tx.fail_validate { + return Err(ErrorCode::new(100)); + } + Ok(()) + } +} + +#[derive(Default)] +struct NoopPostTx; +impl PostTxExecution for NoopPostTx { + fn after_tx_executed( + _tx: &TestTx, + _gas_consumed: u64, + _tx_result: &SdkResult, + _env: &mut dyn Environment, + ) -> SdkResult<()> { + Ok(()) + } +} + +#[derive(Default)] +struct TestAccount; + +impl AccountCode for TestAccount { + fn identifier(&self) -> String { + "test_account".to_string() + } + fn schema(&self) -> evolve_core::schema::AccountSchema { + evolve_core::schema::AccountSchema::new("TestAccount", "test_account") + } + fn init( + &self, + _env: &mut dyn Environment, + _request: &InvokeRequest, + ) -> SdkResult { + InvokeResponse::new(&()) + } + fn execute( + &self, + env: &mut dyn Environment, + request: &InvokeRequest, + ) -> SdkResult { + let msg: TestMsg = request.get()?; + let set = StorageSetRequest { + key: msg.key.clone(), + value: Message::from_bytes(msg.value.clone()), + }; + env.do_exec(STORAGE_ACCOUNT_ID, &InvokeRequest::new(&set)?, vec![])?; + if msg.fail_after_write { + return Err(ErrorCode::new(200)); + } + InvokeResponse::new(&()) + } + fn query( + &self, + _env: &mut dyn EnvironmentQuery, + _request: &InvokeRequest, + ) -> SdkResult { + InvokeResponse::new(&()) + } +} + +struct CodeStore { + codes: HashMap>, +} + +impl CodeStore { + fn new() -> Self { + Self { + codes: HashMap::new(), + } + } + fn add_code(&mut self, code: impl AccountCode + 'static) { + self.codes.insert(code.identifier(), Box::new(code)); + } +} + +impl AccountsCodeStorage for CodeStore { + fn with_code(&self, identifier: &str, f: F) -> Result + where + F: FnOnce(Option<&dyn AccountCode>) -> R, + { + Ok(f(self.codes.get(identifier).map(|c| c.as_ref()))) + } + fn list_identifiers(&self) -> Vec { + self.codes.keys().cloned().collect() + } +} + +#[derive(Default)] +struct InMemoryStorage { + data: HashMap, Vec>, +} + +impl ReadonlyKV for InMemoryStorage { + fn get(&self, key: &[u8]) -> Result>, ErrorCode> { + Ok(self.data.get(key).cloned()) + } +} + +impl WritableKV for InMemoryStorage { + fn apply_changes(&mut self, changes: Vec) -> Result<(), ErrorCode> { + for change in changes { + match change { + StateChange::Set { key, value } => { + self.data.insert(key, value); + } + StateChange::Remove { key } => { + self.data.remove(&key); + } + } + } + Ok(()) + } +} + +fn account_code_key(account: AccountId) -> Vec { + let mut out = vec![ACCOUNT_IDENTIFIER_PREFIX]; + out.extend_from_slice(&account.as_bytes()); + out +} + +fn account_storage_key(account: AccountId, key: &[u8]) -> Vec { + let mut out = account.as_bytes(); + out.extend_from_slice(key); + out +} + +// --------------------------------------------------------------------------- +// Test case definitions (must match the Quint spec's run declarations) +// --------------------------------------------------------------------------- + +const SPEC_ERR_OUT_OF_GAS: i64 = 0x01; +const SPEC_ERR_VALIDATION: i64 = 100; +const SPEC_ERR_EXECUTION: i64 = 200; + +fn make_tx( + sender: u128, + recipient: u128, + key: Vec, + value: Vec, + gas_limit: u64, + fail_validate: bool, + fail_execute: bool, +) -> TestTx { + let msg = TestMsg { + key, + value, + fail_after_write: fail_execute, + }; + TestTx { + sender: AccountId::new(sender), + recipient: AccountId::new(recipient), + request: InvokeRequest::new(&msg).unwrap(), + gas_limit, + funds: vec![], + fail_validate, + } +} + +fn known_test_cases() -> Vec<(&'static str, TestBlock)> { + vec![ + ( + "emptyBlockTest", + TestBlock { height: 1, time: 0, txs: vec![], gas_limit: 1_000_000 }, + ), + ( + "successfulTxTest", + TestBlock { + height: 1, time: 0, gas_limit: 1_000_000, + txs: vec![make_tx(200, 100, vec![1], vec![11], 10000, false, false)], + }, + ), + ( + "validationFailureTest", + TestBlock { + height: 1, time: 0, gas_limit: 1_000_000, + txs: vec![make_tx(200, 100, vec![1], vec![11], 10000, true, false)], + }, + ), + ( + "executionFailureRollbackTest", + TestBlock { + height: 1, time: 0, gas_limit: 1_000_000, + txs: vec![make_tx(200, 100, vec![1], vec![11], 10000, false, true)], + }, + ), + ( + "outOfGasTest", + TestBlock { + height: 1, time: 0, gas_limit: 1_000_000, + txs: vec![make_tx(200, 100, vec![1], vec![11], 1, false, false)], + }, + ), + ( + "blockGasLimitTest", + TestBlock { + height: 1, time: 0, gas_limit: 30, + txs: vec![ + make_tx(200, 100, vec![1], vec![11], 25, false, false), + make_tx(200, 100, vec![2], vec![12], 25, false, false), + ], + }, + ), + ( + "mixedOutcomesTest", + TestBlock { + height: 1, time: 0, gas_limit: 1_000_000, + txs: vec![ + make_tx(200, 100, vec![0], vec![10], 10000, false, false), + make_tx(200, 100, vec![1], vec![11], 10000, true, false), + make_tx(200, 100, vec![2], vec![12], 10000, false, true), + make_tx(200, 100, vec![3], vec![13], 1, false, false), + ], + }, + ), + ( + "overwriteTest", + TestBlock { + height: 1, time: 0, gas_limit: 1_000_000, + txs: vec![ + make_tx(200, 100, vec![1], vec![20], 10000, false, false), + make_tx(200, 100, vec![1], vec![21], 10000, false, false), + ], + }, + ), + ] +} + +// --------------------------------------------------------------------------- +// Conformance test +// --------------------------------------------------------------------------- + +#[test] +fn quint_itf_conformance() { + let traces_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../specs/traces"); + + if !traces_dir.exists() { + panic!( + "ITF traces not found at {}. \ + Run: quint test specs/stf.qnt \ + --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"", + traces_dir.display() + ); + } + + let test_cases = known_test_cases(); + let mut matched = 0; + + for (test_name, block) in &test_cases { + // Find the trace file for this test. + let trace_file = fs::read_dir(&traces_dir) + .unwrap() + .filter_map(|e| e.ok()) + .find(|e| { + let name = e.file_name().to_string_lossy().to_string(); + name.contains(test_name) && name.ends_with(".itf.json") + }); + + let trace_file = match trace_file { + Some(f) => f, + None => { + eprintln!("SKIP: no trace file for {test_name}"); + continue; + } + }; + + let trace_json = fs::read_to_string(trace_file.path()).unwrap(); + let trace: ItfTrace = serde_json::from_str(&trace_json).unwrap(); + + // Find the state after apply_block (block_height > 0). + let spec_state = trace + .states + .iter() + .find(|s| s.block_height.as_i64() > 0) + .unwrap_or_else(|| panic!("{test_name}: no apply_block state in trace")); + + let spec_result = &spec_state.last_result; + + // Set up STF with gas_config matching the spec (all charges = 1). + let gas_config = StorageGasConfig { + storage_get_charge: 1, + storage_set_charge: 1, + storage_remove_charge: 1, + }; + let stf = Stf::new(NoopBegin, NoopEnd, Validator, NoopPostTx, gas_config); + + let mut storage = InMemoryStorage::default(); + let mut codes = CodeStore::new(); + codes.add_code(TestAccount); + + // Register account 100 (matches spec's register_account(100)). + let test_account = AccountId::new(100); + let code_id = "test_account".to_string(); + storage.data.insert( + account_code_key(test_account), + Message::new(&code_id).unwrap().into_bytes().unwrap(), + ); + + // Execute. + let (real_result, exec_state) = stf.apply_block(&storage, &codes, block); + storage + .apply_changes(exec_state.into_changes().unwrap()) + .unwrap(); + + // --- Assert conformance --- + + // 1. tx_results count. + assert_eq!( + real_result.tx_results.len(), + spec_result.tx_results.len(), + "{test_name}: tx_results count mismatch" + ); + + // 2. Per-tx outcomes. + for (i, (real_tx, spec_tx)) in real_result + .tx_results + .iter() + .zip(spec_result.tx_results.iter()) + .enumerate() + { + let spec_ok = spec_tx.result.ok; + let real_ok = real_tx.response.is_ok(); + assert_eq!( + real_ok, spec_ok, + "{test_name} tx[{i}]: ok mismatch (real={real_ok}, spec={spec_ok})" + ); + + if !spec_ok { + let spec_err = spec_tx.result.err_code.as_i64(); + let real_err = real_tx.response.as_ref().unwrap_err().id; + match spec_err { + SPEC_ERR_OUT_OF_GAS => assert_eq!( + real_err, + evolve_stf::ERR_OUT_OF_GAS.id, + "{test_name} tx[{i}]: expected OOG" + ), + SPEC_ERR_VALIDATION => assert_eq!( + real_err, 100, + "{test_name} tx[{i}]: expected validation error" + ), + SPEC_ERR_EXECUTION => assert_eq!( + real_err, 200, + "{test_name} tx[{i}]: expected execution error" + ), + _ => {} + } + } + + // 3. Gas must match exactly. + assert_eq!( + real_tx.gas_used, + spec_tx.gas_used.as_u64(), + "{test_name} tx[{i}]: gas_used mismatch" + ); + } + + // 4. Block-level gas. + assert_eq!( + real_result.gas_used, + spec_result.gas_used.as_u64(), + "{test_name}: block gas_used mismatch" + ); + + // 5. Skipped count. + assert_eq!( + real_result.txs_skipped, + spec_result.txs_skipped.as_u64() as usize, + "{test_name}: txs_skipped mismatch" + ); + + // 6. Storage state. + for (account_id_itf, account_store_itf) in &spec_state.storage.entries { + let account_id = AccountId::new(account_id_itf.as_u64() as u128); + for (key_itf, value_itf) in &account_store_itf.entries { + let key: Vec = key_itf.iter().map(|b| b.as_u64() as u8).collect(); + let value: Vec = value_itf.iter().map(|b| b.as_u64() as u8).collect(); + let storage_key = account_storage_key(account_id, &key); + let real_value = storage.data.get(&storage_key); + assert_eq!( + real_value.map(|v| v.as_slice()), + Some(value.as_slice()), + "{test_name}: storage mismatch for account {account_id:?} key {key:?}", + ); + } + } + + matched += 1; + eprintln!("PASS: {test_name}"); + } + + assert!( + matched > 0, + "No trace files matched. Regenerate with: \ + quint test specs/stf.qnt \ + --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"" + ); + eprintln!("{matched}/{} conformance tests passed", test_cases.len()); +} diff --git a/justfile b/justfile index 5419bfd..2e5f838 100644 --- a/justfile +++ b/justfile @@ -49,6 +49,12 @@ check: # QUALITY # ============================================================================ +# Generate token module Rust code from Quint spec +[group('build')] +gen-token: + cargo run -p evolve_specgen -- specs/token.qnt crates/app/sdk/extensions/token/src/generated/token_from_spec.rs + cargo fmt --all + # Format all code [group('quality')] fmt: @@ -208,6 +214,24 @@ sim-debug trace: sim-report trace: cargo run -p evolve-sim -- report --trace {{trace}} +# ============================================================================ +# SPEC CONFORMANCE +# ============================================================================ + +# Run Quint STF spec tests and Rust conformance checks +[group('spec')] +spec-test: + quint test specs/stf.qnt + rm -f specs/traces/*.itf.json + quint test specs/stf.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json" + cargo test -p evolve_stf --test quint_conformance + +# Regenerate ITF traces from Quint spec (run after editing specs/stf.qnt) +[group('spec')] +spec-traces: + rm -f specs/traces/*.itf.json + quint test specs/stf.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json" + # ============================================================================ # BENCHMARKS # ============================================================================ diff --git a/specs/stf.qnt b/specs/stf.qnt new file mode 100644 index 0000000..56c47b8 --- /dev/null +++ b/specs/stf.qnt @@ -0,0 +1,614 @@ +// Evolve SDK - State Transition Function (STF) Specification +// +// This Quint spec models the core STF logic: +// - Block lifecycle: begin_block -> process txs -> end_block +// - Transaction lifecycle: bootstrap -> validate -> execute -> post_tx +// - Gas metering with per-tx limits and block gas limits +// - Storage overlay with checkpoint/restore for atomicity +// - Determinism: same block on same state => same result +// +// The model abstracts away concrete account code and message formats, +// focusing on the STF orchestration and invariants. + +module stf { + + // + // === Types === + // + + type AccountId = int + type Key = List[int] + type Value = List[int] + + /// Result of an operation: either Ok or an error code. + type Result = { ok: bool, err_code: int } + + val OK: Result = { ok: true, err_code: 0 } + pure def Err(code: int): Result = { ok: false, err_code: code } + + // Error codes (matching Rust implementation) + val ERR_OUT_OF_GAS: int = 0x01 + val ERR_CALL_DEPTH: int = 0x02 + val ERR_VALIDATION: int = 100 + val ERR_EXECUTION: int = 200 + val ERR_POST_TX: int = 999 + val ERR_BOOTSTRAP: int = 300 + val ERR_ACCOUNT_NOT_FOUND: int = 0x10 + + /// Storage gas configuration: cost per byte for get/set/remove. + type GasConfig = { + get_charge: int, + set_charge: int, + remove_charge: int, + } + + /// A transaction to be executed. + type Tx = { + sender: AccountId, + recipient: AccountId, + gas_limit: int, + // Abstract payload: key/value the tx wants to write + write_key: Key, + write_value: Value, + // Control flags for modeling different outcomes + fail_validate: bool, + fail_execute: bool, + needs_bootstrap: bool, + fail_bootstrap: bool, + } + + /// A block containing transactions and context. + type Block = { + height: int, + time: int, + txs: List[Tx], + gas_limit: int, + } + + /// Result of a single transaction execution. + type TxResult = { + result: Result, + gas_used: int, + } + + /// Result of block execution. + type BlockResult = { + tx_results: List[TxResult], + gas_used: int, + txs_skipped: int, + } + + // + // === State === + // + + /// The persistent key-value store. Maps (account_id, key) -> value. + var storage: AccountId -> (Key -> Value) + + /// Set of registered account IDs (have account code assigned). + var accounts: Set[AccountId] + + /// Current block height (monotonically increasing). + var block_height: int + + /// The last block result produced. + var last_result: BlockResult + + /// Gas configuration (fixed at construction). + val gas_config: GasConfig = { get_charge: 1, set_charge: 1, remove_charge: 1 } + + // + // === Pure helpers === + // + + /// AccountId is u128 in the Rust implementation = 16 bytes. + /// The real storage key is `account_id_bytes ++ user_key`. + val ACCOUNT_ID_BYTE_SIZE: int = 16 + + /// Compute gas cost for a storage write operation. + /// Real formula: set_charge * (full_key_len + 1 + value_len + 1) + /// where full_key_len = ACCOUNT_ID_BYTE_SIZE + user_key.length() + pure def write_gas_cost(config: GasConfig, key: Key, value: Value): int = + config.set_charge * (ACCOUNT_ID_BYTE_SIZE + key.length() + 1 + value.length() + 1) + + /// Determine whether a transaction's write would exceed its gas limit. + pure def would_exceed_gas(config: GasConfig, tx: Tx): bool = + write_gas_cost(config, tx.write_key, tx.write_value) > tx.gas_limit + + /// Process a single transaction against the current state. + /// Returns (tx_result, updated_account_storage, updated_accounts). + /// + /// Models the full tx lifecycle: + /// 1. Optional sender bootstrap (account registration) + /// 2. Validation + /// 3. Execution (write to storage) + /// 4. Post-tx handler + /// 5. Rollback on any failure + pure def process_tx( + config: GasConfig, + tx: Tx, + account_store: AccountId -> (Key -> Value), + registered: Set[AccountId], + ): { result: TxResult, store: AccountId -> (Key -> Value), accounts: Set[AccountId] } = + // Phase 0: Bootstrap sender if needed + val bootstrap_result = + if (tx.needs_bootstrap and not(registered.contains(tx.sender))) + if (tx.fail_bootstrap) + // Bootstrap failed + { ok: false, accounts: registered, gas: 1 } + else + // Bootstrap succeeds: register sender + { ok: true, accounts: registered.union(Set(tx.sender)), gas: 1 } + else + // No bootstrap needed or already registered + { ok: true, accounts: registered, gas: 0 } + + if (not(bootstrap_result.ok)) + // Bootstrap failed -> return error, no state changes + { + result: { result: Err(ERR_BOOTSTRAP), gas_used: bootstrap_result.gas }, + store: account_store, + accounts: registered, + } + else + val current_accounts = bootstrap_result.accounts + val gas_after_bootstrap = bootstrap_result.gas + + // Phase 1: Validation + if (tx.fail_validate) + { + result: { result: Err(ERR_VALIDATION), gas_used: gas_after_bootstrap }, + store: account_store, + accounts: current_accounts, + } + else + // Phase 2: Check recipient exists + if (not(current_accounts.contains(tx.recipient))) + { + result: { result: Err(ERR_ACCOUNT_NOT_FOUND), gas_used: gas_after_bootstrap }, + store: account_store, + accounts: current_accounts, + } + else + // Phase 3: Execution - compute gas for the write + val gas_needed = write_gas_cost(config, tx.write_key, tx.write_value) + val total_gas = gas_after_bootstrap + gas_needed + + if (total_gas > tx.gas_limit) + // Out of gas: the real GasCounter does NOT increment gas_used on + // a failed consume_gas call. So gas_used stays at its pre-call value. + { + result: { result: Err(ERR_OUT_OF_GAS), gas_used: gas_after_bootstrap }, + store: account_store, + accounts: current_accounts, + } + else if (tx.fail_execute) + // Execution error after write -> rollback + { + result: { result: Err(ERR_EXECUTION), gas_used: total_gas }, + store: account_store, + accounts: current_accounts, + } + else + // Phase 4: Write succeeds -> apply to storage + val recipient_store = + if (account_store.keys().contains(tx.recipient)) + account_store.get(tx.recipient) + else + Map() + val updated_recipient = recipient_store.put(tx.write_key, tx.write_value) + val updated_store = account_store.put(tx.recipient, updated_recipient) + { + result: { result: OK, gas_used: total_gas }, + store: updated_store, + accounts: current_accounts, + } + + /// Process all transactions in a block sequentially, enforcing block gas limit. + pure def process_block( + config: GasConfig, + block: Block, + account_store: AccountId -> (Key -> Value), + registered: Set[AccountId], + ): { result: BlockResult, store: AccountId -> (Key -> Value), accounts: Set[AccountId] } = + val init_state = { + tx_results: List(), + cumulative_gas: 0, + txs_skipped: 0, + store: account_store, + accounts: registered, + stopped: false, + } + val final_state = block.txs.foldl(init_state, (acc, tx) => + if (acc.stopped) + // Already hit block gas limit, skip remaining + acc.with("txs_skipped", acc.txs_skipped + 1) + else + // Check if this tx would exceed block gas limit (pre-execution check) + if (acc.cumulative_gas + tx.gas_limit > block.gas_limit) + acc.with("txs_skipped", acc.txs_skipped + 1) + else + val tx_out = process_tx(config, tx, acc.store, acc.accounts) + val new_cumulative = acc.cumulative_gas + tx_out.result.gas_used + val should_stop = new_cumulative >= block.gas_limit + { + tx_results: acc.tx_results.append(tx_out.result), + cumulative_gas: new_cumulative, + txs_skipped: acc.txs_skipped, + store: tx_out.store, + accounts: tx_out.accounts, + stopped: should_stop, + } + ) + { + result: { + tx_results: final_state.tx_results, + gas_used: final_state.cumulative_gas, + txs_skipped: final_state.txs_skipped, + }, + store: final_state.store, + accounts: final_state.accounts, + } + + // + // === Actions (state transitions) === + // + + action init = all { + storage' = Map(), + accounts' = Set(), + block_height' = 0, + last_result' = { tx_results: List(), gas_used: 0, txs_skipped: 0 }, + } + + /// Apply a block to the current state. + action apply_block(block: Block): bool = + val out = process_block(gas_config, block, storage, accounts) + all { + // Block height must be monotonically increasing. + block.height > block_height, + block.gas_limit > 0, + storage' = out.store, + accounts' = out.accounts, + block_height' = block.height, + last_result' = out.result, + } + + /// Register an account (e.g., during genesis). + action register_account(id: AccountId): bool = all { + not(accounts.contains(id)), + storage' = storage.put(id, Map()), + accounts' = accounts.union(Set(id)), + block_height' = block_height, + last_result' = last_result, + } + + // + // === Invariants === + // + + /// INV-1: Block height never decreases. + val height_monotonic: bool = block_height >= 0 + + /// INV-2: Total gas reported equals sum of individual tx gas. + val gas_accounting: bool = + val sum = last_result.tx_results.foldl(0, (acc, tr) => acc + tr.gas_used) + last_result.gas_used == sum + + /// INV-3: Number of results + skipped = number of txs submitted. + /// (Checked after each apply_block; this is a post-condition on the result.) + val result_completeness: bool = + // This holds trivially for the initial state where there are no txs. + true + + /// INV-4: Failed transactions do not modify storage. + /// (Encoded structurally: process_tx returns the original store on failure.) + + /// INV-5: Only registered accounts appear as storage keys. + val storage_accounts_registered: bool = + storage.keys().forall(id => accounts.contains(id)) + + /// INV-6: Gas used is always non-negative. + val gas_non_negative: bool = + last_result.tx_results.foldl(true, (acc, tr) => acc and tr.gas_used >= 0) + + // + // === Temporal Properties === + // + + /// PROP-1: Determinism - same block on same state produces identical results. + /// This is ensured structurally: all functions are pure, no randomness, + /// no non-deterministic collection iteration (BTreeMap in Rust, ordered Map in Quint). + + /// PROP-2: If a transaction has fail_validate=true, it always returns ERR_VALIDATION. + /// PROP-3: If a transaction runs out of gas, it always returns ERR_OUT_OF_GAS. + /// PROP-4: Successful transactions always produce storage changes. + /// These are verified by the process_tx function structure. + + // + // === Test helpers === + // + + // Byte-list keys/values for test readability + val K0: Key = [0] + val K1: Key = [1] + val K2: Key = [2] + val K3: Key = [3] + val V0: Value = [10] + val V1: Value = [11] + val V2: Value = [12] + val V3: Value = [13] + val V_OLD: Value = [20] + val V_NEW: Value = [21] + + /// Stutter step: keep all state unchanged (required to end a run). + action stutter: bool = all { + storage' = storage, + accounts' = accounts, + block_height' = block_height, + last_result' = last_result, + } + + // + // === Tests === + // + + /// Test: empty block produces no results and no state changes. + run emptyBlockTest = { + init + .then(register_account(100)) + .then(apply_block({ height: 1, time: 0, txs: List(), gas_limit: 1000000 })) + .then(all { + assert(last_result.tx_results.length() == 0), + assert(last_result.gas_used == 0), + assert(last_result.txs_skipped == 0), + stutter, + }) + } + + /// Test: successful transaction writes to storage. + run successfulTxTest = { + val tx1: Tx = { + sender: 200, recipient: 100, gas_limit: 10000, + write_key: K1, write_value: V1, + fail_validate: false, fail_execute: false, + needs_bootstrap: false, fail_bootstrap: false, + } + init + .then(register_account(100)) + .then(apply_block({ height: 1, time: 0, txs: [tx1], gas_limit: 1000000 })) + .then(all { + assert(last_result.tx_results.length() == 1), + assert(last_result.tx_results[0].result.ok == true), + assert(storage.get(100).get(K1) == V1), + stutter, + }) + } + + /// Test: validation failure does not modify storage. + run validationFailureTest = { + val tx1: Tx = { + sender: 200, recipient: 100, gas_limit: 10000, + write_key: K1, write_value: V1, + fail_validate: true, fail_execute: false, + needs_bootstrap: false, fail_bootstrap: false, + } + init + .then(register_account(100)) + .then(apply_block({ height: 1, time: 0, txs: [tx1], gas_limit: 1000000 })) + .then(all { + assert(last_result.tx_results[0].result.ok == false), + assert(last_result.tx_results[0].result.err_code == ERR_VALIDATION), + assert(storage.get(100) == Map()), + stutter, + }) + } + + /// Test: execution failure rolls back storage write. + run executionFailureRollbackTest = { + val tx1: Tx = { + sender: 200, recipient: 100, gas_limit: 10000, + write_key: K1, write_value: V1, + fail_validate: false, fail_execute: true, + needs_bootstrap: false, fail_bootstrap: false, + } + init + .then(register_account(100)) + .then(apply_block({ height: 1, time: 0, txs: [tx1], gas_limit: 1000000 })) + .then(all { + assert(last_result.tx_results[0].result.ok == false), + assert(last_result.tx_results[0].result.err_code == ERR_EXECUTION), + assert(storage.get(100) == Map()), + stutter, + }) + } + + /// Test: out-of-gas transaction fails and does not write. + run outOfGasTest = { + val tx1: Tx = { + sender: 200, recipient: 100, gas_limit: 1, + write_key: K1, write_value: V1, + fail_validate: false, fail_execute: false, + needs_bootstrap: false, fail_bootstrap: false, + } + init + .then(register_account(100)) + .then(apply_block({ height: 1, time: 0, txs: [tx1], gas_limit: 1000000 })) + .then(all { + assert(last_result.tx_results[0].result.ok == false), + assert(last_result.tx_results[0].result.err_code == ERR_OUT_OF_GAS), + assert(storage.get(100) == Map()), + stutter, + }) + } + + /// Test: block gas limit causes transactions to be skipped. + /// With Key=[1], Value=[11]: gas_cost = 1*(16+1+1+1+1) = 20 per tx. + /// tx1.gas_limit=25, tx2.gas_limit=25. Block gas_limit=30. + /// Pre-check: cumulative(0) + tx1.gas_limit(25) = 25 <= 30 -> execute tx1 (uses 20 gas). + /// Pre-check: cumulative(20) + tx2.gas_limit(25) = 45 > 30 -> skip tx2. + run blockGasLimitTest = { + val tx1: Tx = { + sender: 200, recipient: 100, gas_limit: 25, + write_key: K1, write_value: V1, + fail_validate: false, fail_execute: false, + needs_bootstrap: false, fail_bootstrap: false, + } + val tx2: Tx = { + sender: 200, recipient: 100, gas_limit: 25, + write_key: K2, write_value: V2, + fail_validate: false, fail_execute: false, + needs_bootstrap: false, fail_bootstrap: false, + } + init + .then(register_account(100)) + .then(apply_block({ height: 1, time: 0, txs: [tx1, tx2], gas_limit: 30 })) + .then(all { + assert(last_result.tx_results.length() == 1), + assert(last_result.txs_skipped == 1), + assert(last_result.tx_results[0].result.ok == true), + stutter, + }) + } + + /// Test: mixed block with success, validation failure, execution failure, OOG. + run mixedOutcomesTest = { + val tx_ok: Tx = { + sender: 200, recipient: 100, gas_limit: 10000, + write_key: K0, write_value: V0, + fail_validate: false, fail_execute: false, + needs_bootstrap: false, fail_bootstrap: false, + } + val tx_validate_fail: Tx = { + sender: 200, recipient: 100, gas_limit: 10000, + write_key: K1, write_value: V1, + fail_validate: true, fail_execute: false, + needs_bootstrap: false, fail_bootstrap: false, + } + val tx_exec_fail: Tx = { + sender: 200, recipient: 100, gas_limit: 10000, + write_key: K2, write_value: V2, + fail_validate: false, fail_execute: true, + needs_bootstrap: false, fail_bootstrap: false, + } + val tx_oog: Tx = { + sender: 200, recipient: 100, gas_limit: 1, + write_key: K3, write_value: V3, + fail_validate: false, fail_execute: false, + needs_bootstrap: false, fail_bootstrap: false, + } + init + .then(register_account(100)) + .then(apply_block({ + height: 1, time: 0, + txs: [tx_ok, tx_validate_fail, tx_exec_fail, tx_oog], + gas_limit: 1000000, + })) + .then(all { + assert(last_result.tx_results.length() == 4), + // tx_ok succeeds + assert(last_result.tx_results[0].result.ok == true), + // tx_validate_fail fails validation + assert(last_result.tx_results[1].result.err_code == ERR_VALIDATION), + // tx_exec_fail fails execution + assert(last_result.tx_results[2].result.err_code == ERR_EXECUTION), + // tx_oog fails with out of gas + assert(last_result.tx_results[3].result.err_code == ERR_OUT_OF_GAS), + // Only the first tx's write should be in storage + assert(storage.get(100).get(K0) == V0), + assert(not(storage.get(100).keys().contains(K1))), + assert(not(storage.get(100).keys().contains(K2))), + assert(not(storage.get(100).keys().contains(K3))), + stutter, + }) + } + + /// Test: sender bootstrap registers account before execution. + run bootstrapTest = { + val tx1: Tx = { + sender: 200, recipient: 100, gas_limit: 10000, + write_key: K1, write_value: V1, + fail_validate: false, fail_execute: false, + needs_bootstrap: true, fail_bootstrap: false, + } + init + .then(register_account(100)) + .then(apply_block({ height: 1, time: 0, txs: [tx1], gas_limit: 1000000 })) + .then(all { + assert(last_result.tx_results[0].result.ok == true), + // Sender should now be registered + assert(accounts.contains(200)), + stutter, + }) + } + + /// Test: failed bootstrap prevents execution. + run bootstrapFailureTest = { + val tx1: Tx = { + sender: 200, recipient: 100, gas_limit: 10000, + write_key: K1, write_value: V1, + fail_validate: false, fail_execute: false, + needs_bootstrap: true, fail_bootstrap: true, + } + init + .then(register_account(100)) + .then(apply_block({ height: 1, time: 0, txs: [tx1], gas_limit: 1000000 })) + .then(all { + assert(last_result.tx_results[0].result.ok == false), + assert(last_result.tx_results[0].result.err_code == ERR_BOOTSTRAP), + // Sender should NOT be registered + assert(not(accounts.contains(200))), + stutter, + }) + } + + /// Test: sequential blocks accumulate state correctly. + run multiBlockTest = { + val tx1: Tx = { + sender: 200, recipient: 100, gas_limit: 10000, + write_key: K1, write_value: V1, + fail_validate: false, fail_execute: false, + needs_bootstrap: false, fail_bootstrap: false, + } + val tx2: Tx = { + sender: 200, recipient: 100, gas_limit: 10000, + write_key: K2, write_value: V2, + fail_validate: false, fail_execute: false, + needs_bootstrap: false, fail_bootstrap: false, + } + init + .then(register_account(100)) + .then(apply_block({ height: 1, time: 0, txs: [tx1], gas_limit: 1000000 })) + .then(apply_block({ height: 2, time: 10, txs: [tx2], gas_limit: 1000000 })) + .then(all { + // Both writes should be present + assert(storage.get(100).get(K1) == V1), + assert(storage.get(100).get(K2) == V2), + assert(block_height == 2), + stutter, + }) + } + + /// Test: overwriting a key updates the value. + run overwriteTest = { + val tx1: Tx = { + sender: 200, recipient: 100, gas_limit: 10000, + write_key: K1, write_value: V_OLD, + fail_validate: false, fail_execute: false, + needs_bootstrap: false, fail_bootstrap: false, + } + val tx2: Tx = { + sender: 200, recipient: 100, gas_limit: 10000, + write_key: K1, write_value: V_NEW, + fail_validate: false, fail_execute: false, + needs_bootstrap: false, fail_bootstrap: false, + } + init + .then(register_account(100)) + .then(apply_block({ height: 1, time: 0, txs: [tx1, tx2], gas_limit: 1000000 })) + .then(all { + assert(storage.get(100).get(K1) == V_NEW), + stutter, + }) + } +} diff --git a/specs/token.qnt b/specs/token.qnt new file mode 100644 index 0000000..8a31041 --- /dev/null +++ b/specs/token.qnt @@ -0,0 +1,26 @@ +module TokenV1 + +// Canonical specgen directives for token_v1 code generation. +// @specgen module_kind=token_v1 +// @specgen rust_mod=generated_token +// @specgen account_type=TokenGenerated +// @specgen error.not_enough_balance.code=0x1 +// @specgen error.not_enough_balance.message=not enough balance +// @specgen error.underflow.code=0x2 +// @specgen error.underflow.message=arithmetic underflow +// @specgen slot.metadata=0 +// @specgen slot.balances=1 +// @specgen slot.total_supply=2 +// @specgen slot.supply_manager=3 + +// Quint subset declarations required by specgen. +action initialize(metadata, balances, supply_manager) = true +action mint(recipient, amount) = true +action burn(from_account, amount) = true +action transfer(to, amount) = true +action freeze(account) = true + +def metadata(accountState) = accountState.metadata +def get_balance(account, accountState) = accountState.balances.get(account) +def total_supply(accountState) = accountState.total_supply +def is_frozen(account, accountState) = false diff --git a/specs/traces/out_blockGasLimitTest_5.itf.json b/specs/traces/out_blockGasLimitTest_5.itf.json new file mode 100644 index 0000000..f1987d9 --- /dev/null +++ b/specs/traces/out_blockGasLimitTest_5.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400940},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_bootstrapFailureTest_8.itf.json b/specs/traces/out_bootstrapFailureTest_8.itf.json new file mode 100644 index 0000000..0d9346f --- /dev/null +++ b/specs/traces/out_bootstrapFailureTest_8.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400950},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"1"},"tx_results":[{"gas_used":{"#bigint":"1"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"1"},"tx_results":[{"gas_used":{"#bigint":"1"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_bootstrapTest_7.itf.json b/specs/traces/out_bootstrapTest_7.itf.json new file mode 100644 index 0000000..3eda69a --- /dev/null +++ b/specs/traces/out_bootstrapTest_7.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400949},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"21"},"tx_results":[{"gas_used":{"#bigint":"21"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"21"},"tx_results":[{"gas_used":{"#bigint":"21"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_emptyBlockTest_0.itf.json b/specs/traces/out_emptyBlockTest_0.itf.json new file mode 100644 index 0000000..a9d9906 --- /dev/null +++ b/specs/traces/out_emptyBlockTest_0.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400934},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_executionFailureRollbackTest_3.itf.json b/specs/traces/out_executionFailureRollbackTest_3.itf.json new file mode 100644 index 0000000..f7a921f --- /dev/null +++ b/specs/traces/out_executionFailureRollbackTest_3.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400937},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_mixedOutcomesTest_6.itf.json b/specs/traces/out_mixedOutcomesTest_6.itf.json new file mode 100644 index 0000000..5dd8fdd --- /dev/null +++ b/specs/traces/out_mixedOutcomesTest_6.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400941},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_multiBlockTest_9.itf.json b/specs/traces/out_multiBlockTest_9.itf.json new file mode 100644 index 0000000..35d8463 --- /dev/null +++ b/specs/traces/out_multiBlockTest_9.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400952},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}},{"#meta":{"index":4},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_outOfGasTest_4.itf.json b/specs/traces/out_outOfGasTest_4.itf.json new file mode 100644 index 0000000..654f409 --- /dev/null +++ b/specs/traces/out_outOfGasTest_4.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400938},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_overwriteTest_10.itf.json b/specs/traces/out_overwriteTest_10.itf.json new file mode 100644 index 0000000..933b5d9 --- /dev/null +++ b/specs/traces/out_overwriteTest_10.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400953},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_successfulTxTest_1.itf.json b/specs/traces/out_successfulTxTest_1.itf.json new file mode 100644 index 0000000..03391d9 --- /dev/null +++ b/specs/traces/out_successfulTxTest_1.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400936},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_validationFailureTest_2.itf.json b/specs/traces/out_validationFailureTest_2.itf.json new file mode 100644 index 0000000..3342b60 --- /dev/null +++ b/specs/traces/out_validationFailureTest_2.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400937},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file From 304bfe52a47d077a6ee1398241e9fd29890f9728 Mon Sep 17 00:00:00 2001 From: tac0turtle Date: Tue, 24 Feb 2026 22:17:28 +0100 Subject: [PATCH 2/9] regenerate specs --- crates/app/stf/tests/quint_conformance.rs | 543 ++++++++++++++---- specs/stf.qnt | 17 +- specs/traces/out_blockGasLimitTest_5.itf.json | 2 +- .../out_bootstrapFailureTest_8.itf.json | 2 +- specs/traces/out_bootstrapTest_7.itf.json | 2 +- specs/traces/out_emptyBlockTest_0.itf.json | 2 +- ...ut_executionFailureRollbackTest_3.itf.json | 2 +- specs/traces/out_mixedOutcomesTest_6.itf.json | 2 +- specs/traces/out_multiBlockTest_9.itf.json | 2 +- specs/traces/out_outOfGasTest_4.itf.json | 2 +- specs/traces/out_overwriteTest_10.itf.json | 2 +- specs/traces/out_successfulTxTest_1.itf.json | 2 +- .../out_validationFailureTest_2.itf.json | 2 +- 13 files changed, 445 insertions(+), 137 deletions(-) diff --git a/crates/app/stf/tests/quint_conformance.rs b/crates/app/stf/tests/quint_conformance.rs index 34ae482..991b682 100644 --- a/crates/app/stf/tests/quint_conformance.rs +++ b/crates/app/stf/tests/quint_conformance.rs @@ -19,7 +19,7 @@ use evolve_stf::gas::StorageGasConfig; use evolve_stf::Stf; use evolve_stf_traits::{ AccountsCodeStorage, BeginBlocker, Block as BlockTrait, EndBlocker, PostTxExecution, - StateChange, Transaction, TxValidator, WritableKV, + SenderBootstrap, StateChange, Transaction, TxValidator, WritableKV, }; use hashbrown::HashMap; use serde::Deserialize; @@ -106,6 +106,8 @@ struct TestTx { gas_limit: u64, funds: Vec, fail_validate: bool, + needs_bootstrap: bool, + fail_bootstrap: bool, } impl Transaction for TestTx { @@ -127,6 +129,20 @@ impl Transaction for TestTx { fn compute_identifier(&self) -> [u8; 32] { [0u8; 32] } + fn sender_bootstrap(&self) -> Option { + if !self.needs_bootstrap { + return None; + } + let init = BootstrapInit { + fail: self.fail_bootstrap, + }; + let init_message = + Message::new(&init).expect("bootstrap init serialization must succeed in tests"); + Some(SenderBootstrap { + account_code_id: "test_account", + init_message, + }) + } } #[derive(Clone)] @@ -188,6 +204,11 @@ impl PostTxExecution for NoopPostTx { #[derive(Default)] struct TestAccount; +#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] +struct BootstrapInit { + fail: bool, +} + impl AccountCode for TestAccount { fn identifier(&self) -> String { "test_account".to_string() @@ -198,8 +219,12 @@ impl AccountCode for TestAccount { fn init( &self, _env: &mut dyn Environment, - _request: &InvokeRequest, + request: &InvokeRequest, ) -> SdkResult { + let init: BootstrapInit = request.get()?; + if init.fail { + return Err(ErrorCode::new(300)); + } InvokeResponse::new(&()) } fn execute( @@ -287,12 +312,6 @@ fn account_code_key(account: AccountId) -> Vec { out } -fn account_storage_key(account: AccountId, key: &[u8]) -> Vec { - let mut out = account.as_bytes(); - out.extend_from_slice(key); - out -} - // --------------------------------------------------------------------------- // Test case definitions (must match the Quint spec's run declarations) // --------------------------------------------------------------------------- @@ -301,7 +320,7 @@ const SPEC_ERR_OUT_OF_GAS: i64 = 0x01; const SPEC_ERR_VALIDATION: i64 = 100; const SPEC_ERR_EXECUTION: i64 = 200; -fn make_tx( +struct TxCase { sender: u128, recipient: u128, key: Vec, @@ -309,88 +328,317 @@ fn make_tx( gas_limit: u64, fail_validate: bool, fail_execute: bool, -) -> TestTx { + needs_bootstrap: bool, + fail_bootstrap: bool, +} + +fn make_tx(tc: TxCase) -> TestTx { let msg = TestMsg { - key, - value, - fail_after_write: fail_execute, + key: tc.key, + value: tc.value, + fail_after_write: tc.fail_execute, }; TestTx { - sender: AccountId::new(sender), - recipient: AccountId::new(recipient), + sender: AccountId::new(tc.sender), + recipient: AccountId::new(tc.recipient), request: InvokeRequest::new(&msg).unwrap(), - gas_limit, + gas_limit: tc.gas_limit, funds: vec![], - fail_validate, + fail_validate: tc.fail_validate, + needs_bootstrap: tc.needs_bootstrap, + fail_bootstrap: tc.fail_bootstrap, } } -fn known_test_cases() -> Vec<(&'static str, TestBlock)> { +const SPEC_ERR_BOOTSTRAP: i64 = 300; +const TEST_ACCOUNT: u128 = 100; +const TEST_SENDER: u128 = 200; + +struct ConformanceCase { + test_name: &'static str, + blocks: Vec, +} + +fn known_test_cases() -> Vec { vec![ - ( - "emptyBlockTest", - TestBlock { height: 1, time: 0, txs: vec![], gas_limit: 1_000_000 }, - ), - ( - "successfulTxTest", - TestBlock { - height: 1, time: 0, gas_limit: 1_000_000, - txs: vec![make_tx(200, 100, vec![1], vec![11], 10000, false, false)], - }, - ), - ( - "validationFailureTest", - TestBlock { - height: 1, time: 0, gas_limit: 1_000_000, - txs: vec![make_tx(200, 100, vec![1], vec![11], 10000, true, false)], - }, - ), - ( - "executionFailureRollbackTest", - TestBlock { - height: 1, time: 0, gas_limit: 1_000_000, - txs: vec![make_tx(200, 100, vec![1], vec![11], 10000, false, true)], - }, - ), - ( - "outOfGasTest", - TestBlock { - height: 1, time: 0, gas_limit: 1_000_000, - txs: vec![make_tx(200, 100, vec![1], vec![11], 1, false, false)], - }, - ), - ( - "blockGasLimitTest", - TestBlock { - height: 1, time: 0, gas_limit: 30, + ConformanceCase { + test_name: "emptyBlockTest", + blocks: vec![TestBlock { + height: 1, + time: 0, + txs: vec![], + gas_limit: 1_000_000, + }], + }, + ConformanceCase { + test_name: "successfulTxTest", + blocks: vec![TestBlock { + height: 1, + time: 0, + gas_limit: 1_000_000, + txs: vec![make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![1], + value: vec![11], + gas_limit: 10000, + fail_validate: false, + fail_execute: false, + needs_bootstrap: false, + fail_bootstrap: false, + })], + }], + }, + ConformanceCase { + test_name: "validationFailureTest", + blocks: vec![TestBlock { + height: 1, + time: 0, + gas_limit: 1_000_000, + txs: vec![make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![1], + value: vec![11], + gas_limit: 10000, + fail_validate: true, + fail_execute: false, + needs_bootstrap: false, + fail_bootstrap: false, + })], + }], + }, + ConformanceCase { + test_name: "executionFailureRollbackTest", + blocks: vec![TestBlock { + height: 1, + time: 0, + gas_limit: 1_000_000, + txs: vec![make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![1], + value: vec![11], + gas_limit: 10000, + fail_validate: false, + fail_execute: true, + needs_bootstrap: false, + fail_bootstrap: false, + })], + }], + }, + ConformanceCase { + test_name: "outOfGasTest", + blocks: vec![TestBlock { + height: 1, + time: 0, + gas_limit: 1_000_000, + txs: vec![make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![1], + value: vec![11], + gas_limit: 1, + fail_validate: false, + fail_execute: false, + needs_bootstrap: false, + fail_bootstrap: false, + })], + }], + }, + ConformanceCase { + test_name: "blockGasLimitTest", + blocks: vec![TestBlock { + height: 1, + time: 0, + gas_limit: 30, txs: vec![ - make_tx(200, 100, vec![1], vec![11], 25, false, false), - make_tx(200, 100, vec![2], vec![12], 25, false, false), + make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![1], + value: vec![11], + gas_limit: 25, + fail_validate: false, + fail_execute: false, + needs_bootstrap: false, + fail_bootstrap: false, + }), + make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![2], + value: vec![12], + gas_limit: 25, + fail_validate: false, + fail_execute: false, + needs_bootstrap: false, + fail_bootstrap: false, + }), ], - }, - ), - ( - "mixedOutcomesTest", - TestBlock { - height: 1, time: 0, gas_limit: 1_000_000, + }], + }, + ConformanceCase { + test_name: "mixedOutcomesTest", + blocks: vec![TestBlock { + height: 1, + time: 0, + gas_limit: 1_000_000, txs: vec![ - make_tx(200, 100, vec![0], vec![10], 10000, false, false), - make_tx(200, 100, vec![1], vec![11], 10000, true, false), - make_tx(200, 100, vec![2], vec![12], 10000, false, true), - make_tx(200, 100, vec![3], vec![13], 1, false, false), + make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![0], + value: vec![10], + gas_limit: 10000, + fail_validate: false, + fail_execute: false, + needs_bootstrap: false, + fail_bootstrap: false, + }), + make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![1], + value: vec![11], + gas_limit: 10000, + fail_validate: true, + fail_execute: false, + needs_bootstrap: false, + fail_bootstrap: false, + }), + make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![2], + value: vec![12], + gas_limit: 10000, + fail_validate: false, + fail_execute: true, + needs_bootstrap: false, + fail_bootstrap: false, + }), + make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![3], + value: vec![13], + gas_limit: 1, + fail_validate: false, + fail_execute: false, + needs_bootstrap: false, + fail_bootstrap: false, + }), ], - }, - ), - ( - "overwriteTest", - TestBlock { - height: 1, time: 0, gas_limit: 1_000_000, + }], + }, + ConformanceCase { + test_name: "bootstrapTest", + blocks: vec![TestBlock { + height: 1, + time: 0, + gas_limit: 1_000_000, + txs: vec![make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![1], + value: vec![11], + gas_limit: 10000, + fail_validate: false, + fail_execute: false, + needs_bootstrap: true, + fail_bootstrap: false, + })], + }], + }, + ConformanceCase { + test_name: "bootstrapFailureTest", + blocks: vec![TestBlock { + height: 1, + time: 0, + gas_limit: 1_000_000, + txs: vec![make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![1], + value: vec![11], + gas_limit: 10000, + fail_validate: false, + fail_execute: false, + needs_bootstrap: true, + fail_bootstrap: true, + })], + }], + }, + ConformanceCase { + test_name: "multiBlockTest", + blocks: vec![ + TestBlock { + height: 1, + time: 0, + gas_limit: 1_000_000, + txs: vec![make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![1], + value: vec![11], + gas_limit: 10000, + fail_validate: false, + fail_execute: false, + needs_bootstrap: false, + fail_bootstrap: false, + })], + }, + TestBlock { + height: 2, + time: 10, + gas_limit: 1_000_000, + txs: vec![make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![2], + value: vec![12], + gas_limit: 10000, + fail_validate: false, + fail_execute: false, + needs_bootstrap: false, + fail_bootstrap: false, + })], + }, + ], + }, + ConformanceCase { + test_name: "overwriteTest", + blocks: vec![TestBlock { + height: 1, + time: 0, + gas_limit: 1_000_000, txs: vec![ - make_tx(200, 100, vec![1], vec![20], 10000, false, false), - make_tx(200, 100, vec![1], vec![21], 10000, false, false), + make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![1], + value: vec![20], + gas_limit: 10000, + fail_validate: false, + fail_execute: false, + needs_bootstrap: false, + fail_bootstrap: false, + }), + make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![1], + value: vec![21], + gas_limit: 10000, + fail_validate: false, + fail_execute: false, + needs_bootstrap: false, + fail_bootstrap: false, + }), ], - }, - ), + }], + }, ] } @@ -414,33 +662,42 @@ fn quint_itf_conformance() { let test_cases = known_test_cases(); let mut matched = 0; - for (test_name, block) in &test_cases { - // Find the trace file for this test. - let trace_file = fs::read_dir(&traces_dir) + for case in &test_cases { + // Find the unique trace file for this test. + let trace_files: Vec<_> = fs::read_dir(&traces_dir) .unwrap() .filter_map(|e| e.ok()) - .find(|e| { + .filter(|e| { let name = e.file_name().to_string_lossy().to_string(); - name.contains(test_name) && name.ends_with(".itf.json") - }); + name.starts_with(&format!("out_{}_", case.test_name)) && name.ends_with(".itf.json") + }) + .collect(); - let trace_file = match trace_file { - Some(f) => f, - None => { - eprintln!("SKIP: no trace file for {test_name}"); - continue; - } - }; + assert_eq!( + trace_files.len(), + 1, + "{}: expected exactly 1 trace file, found {}", + case.test_name, + trace_files.len() + ); + let trace_file = &trace_files[0]; let trace_json = fs::read_to_string(trace_file.path()).unwrap(); let trace: ItfTrace = serde_json::from_str(&trace_json).unwrap(); - // Find the state after apply_block (block_height > 0). + // Find the final state for this run after all apply_block steps. + let expected_block_height = + case.blocks.last().expect("case must have blocks").height as i64; let spec_state = trace .states .iter() - .find(|s| s.block_height.as_i64() > 0) - .unwrap_or_else(|| panic!("{test_name}: no apply_block state in trace")); + .find(|s| s.block_height.as_i64() == expected_block_height) + .unwrap_or_else(|| { + panic!( + "{}: no trace state with block_height={expected_block_height}", + case.test_name + ) + }); let spec_result = &spec_state.last_result; @@ -457,18 +714,23 @@ fn quint_itf_conformance() { codes.add_code(TestAccount); // Register account 100 (matches spec's register_account(100)). - let test_account = AccountId::new(100); + let test_account = AccountId::new(TEST_ACCOUNT); let code_id = "test_account".to_string(); storage.data.insert( account_code_key(test_account), Message::new(&code_id).unwrap().into_bytes().unwrap(), ); - // Execute. - let (real_result, exec_state) = stf.apply_block(&storage, &codes, block); - storage - .apply_changes(exec_state.into_changes().unwrap()) - .unwrap(); + // Execute the full block sequence for this case. + let mut real_result = None; + for block in &case.blocks { + let (result, exec_state) = stf.apply_block(&storage, &codes, block); + storage + .apply_changes(exec_state.into_changes().unwrap()) + .unwrap(); + real_result = Some(result); + } + let real_result = real_result.expect("case must execute at least one block"); // --- Assert conformance --- @@ -476,7 +738,8 @@ fn quint_itf_conformance() { assert_eq!( real_result.tx_results.len(), spec_result.tx_results.len(), - "{test_name}: tx_results count mismatch" + "{}: tx_results count mismatch", + case.test_name ); // 2. Per-tx outcomes. @@ -490,7 +753,8 @@ fn quint_itf_conformance() { let real_ok = real_tx.response.is_ok(); assert_eq!( real_ok, spec_ok, - "{test_name} tx[{i}]: ok mismatch (real={real_ok}, spec={spec_ok})" + "{} tx[{i}]: ok mismatch (real={real_ok}, spec={spec_ok})", + case.test_name ); if !spec_ok { @@ -500,17 +764,28 @@ fn quint_itf_conformance() { SPEC_ERR_OUT_OF_GAS => assert_eq!( real_err, evolve_stf::ERR_OUT_OF_GAS.id, - "{test_name} tx[{i}]: expected OOG" + "{} tx[{i}]: expected OOG", + case.test_name ), SPEC_ERR_VALIDATION => assert_eq!( real_err, 100, - "{test_name} tx[{i}]: expected validation error" + "{} tx[{i}]: expected validation error", + case.test_name ), SPEC_ERR_EXECUTION => assert_eq!( real_err, 200, - "{test_name} tx[{i}]: expected execution error" + "{} tx[{i}]: expected execution error", + case.test_name + ), + SPEC_ERR_BOOTSTRAP => assert_eq!( + real_err, 300, + "{} tx[{i}]: expected bootstrap error", + case.test_name + ), + _ => panic!( + "{} tx[{i}]: unknown spec error code {spec_err}", + case.test_name ), - _ => {} } } @@ -518,7 +793,8 @@ fn quint_itf_conformance() { assert_eq!( real_tx.gas_used, spec_tx.gas_used.as_u64(), - "{test_name} tx[{i}]: gas_used mismatch" + "{} tx[{i}]: gas_used mismatch", + case.test_name ); } @@ -526,41 +802,60 @@ fn quint_itf_conformance() { assert_eq!( real_result.gas_used, spec_result.gas_used.as_u64(), - "{test_name}: block gas_used mismatch" + "{}: block gas_used mismatch", + case.test_name ); // 5. Skipped count. assert_eq!( real_result.txs_skipped, spec_result.txs_skipped.as_u64() as usize, - "{test_name}: txs_skipped mismatch" + "{}: txs_skipped mismatch", + case.test_name ); - // 6. Storage state. - for (account_id_itf, account_store_itf) in &spec_state.storage.entries { - let account_id = AccountId::new(account_id_itf.as_u64() as u128); - for (key_itf, value_itf) in &account_store_itf.entries { - let key: Vec = key_itf.iter().map(|b| b.as_u64() as u8).collect(); - let value: Vec = value_itf.iter().map(|b| b.as_u64() as u8).collect(); - let storage_key = account_storage_key(account_id, &key); - let real_value = storage.data.get(&storage_key); - assert_eq!( - real_value.map(|v| v.as_slice()), - Some(value.as_slice()), - "{test_name}: storage mismatch for account {account_id:?} key {key:?}", - ); + // 6. Storage state must match exactly (for modeled accounts). + let modeled_accounts = [AccountId::new(TEST_ACCOUNT), AccountId::new(TEST_SENDER)]; + for account_id in modeled_accounts { + let mut expected = HashMap::, Vec>::new(); + for (account_id_itf, account_store_itf) in &spec_state.storage.entries { + if AccountId::new(account_id_itf.as_u64() as u128) != account_id { + continue; + } + for (key_itf, value_itf) in &account_store_itf.entries { + let key: Vec = key_itf.iter().map(|b| b.as_u64() as u8).collect(); + let value: Vec = value_itf.iter().map(|b| b.as_u64() as u8).collect(); + expected.insert(key, value); + } + } + + let mut actual = HashMap::, Vec>::new(); + let account_prefix = account_id.as_bytes(); + for (raw_key, raw_value) in &storage.data { + if raw_key.len() < account_prefix.len() { + continue; + } + if raw_key[..account_prefix.len()] == account_prefix { + actual.insert(raw_key[account_prefix.len()..].to_vec(), raw_value.clone()); + } } + assert_eq!( + actual, expected, + "{}: exact storage mismatch for account {:?}", + case.test_name, account_id + ); } matched += 1; - eprintln!("PASS: {test_name}"); + eprintln!("PASS: {}", case.test_name); } assert!( - matched > 0, - "No trace files matched. Regenerate with: \ + matched == test_cases.len(), + "Matched {matched}/{} traces. Regenerate with: \ quint test specs/stf.qnt \ - --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"" + --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"", + test_cases.len() ); eprintln!("{matched}/{} conformance tests passed", test_cases.len()); } diff --git a/specs/stf.qnt b/specs/stf.qnt index 56c47b8..28fa1a9 100644 --- a/specs/stf.qnt +++ b/specs/stf.qnt @@ -104,6 +104,9 @@ module stf { /// AccountId is u128 in the Rust implementation = 16 bytes. /// The real storage key is `account_id_bytes ++ user_key`. val ACCOUNT_ID_BYTE_SIZE: int = 16 + val ACCOUNT_CODE_PREFIX_SIZE: int = 1 + val BORSH_LEN_PREFIX_SIZE: int = 4 + val TEST_ACCOUNT_CODE_ID_LEN: int = 12 // "test_account" /// Compute gas cost for a storage write operation. /// Real formula: set_charge * (full_key_len + 1 + value_len + 1) @@ -111,6 +114,16 @@ module stf { pure def write_gas_cost(config: GasConfig, key: Key, value: Value): int = config.set_charge * (ACCOUNT_ID_BYTE_SIZE + key.length() + 1 + value.length() + 1) + /// Gas cost for sender bootstrap account-code registration. + /// Real runtime charges storage set gas for: + /// key = ACCOUNT_IDENTIFIER_PREFIX (1 byte) ++ account_id (16 bytes) + /// value = Borsh-encoded code id string ("test_account" => 4-byte len + 12 bytes) + pure def bootstrap_gas_cost(config: GasConfig): int = + config.set_charge * ( + ACCOUNT_CODE_PREFIX_SIZE + ACCOUNT_ID_BYTE_SIZE + 1 + + BORSH_LEN_PREFIX_SIZE + TEST_ACCOUNT_CODE_ID_LEN + 1 + ) + /// Determine whether a transaction's write would exceed its gas limit. pure def would_exceed_gas(config: GasConfig, tx: Tx): bool = write_gas_cost(config, tx.write_key, tx.write_value) > tx.gas_limit @@ -135,10 +148,10 @@ module stf { if (tx.needs_bootstrap and not(registered.contains(tx.sender))) if (tx.fail_bootstrap) // Bootstrap failed - { ok: false, accounts: registered, gas: 1 } + { ok: false, accounts: registered, gas: bootstrap_gas_cost(config) } else // Bootstrap succeeds: register sender - { ok: true, accounts: registered.union(Set(tx.sender)), gas: 1 } + { ok: true, accounts: registered.union(Set(tx.sender)), gas: bootstrap_gas_cost(config) } else // No bootstrap needed or already registered { ok: true, accounts: registered, gas: 0 } diff --git a/specs/traces/out_blockGasLimitTest_5.itf.json b/specs/traces/out_blockGasLimitTest_5.itf.json index f1987d9..1d2a8fa 100644 --- a/specs/traces/out_blockGasLimitTest_5.itf.json +++ b/specs/traces/out_blockGasLimitTest_5.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400940},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826685},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_bootstrapFailureTest_8.itf.json b/specs/traces/out_bootstrapFailureTest_8.itf.json index 0d9346f..a2739f9 100644 --- a/specs/traces/out_bootstrapFailureTest_8.itf.json +++ b/specs/traces/out_bootstrapFailureTest_8.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400950},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"1"},"tx_results":[{"gas_used":{"#bigint":"1"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"1"},"tx_results":[{"gas_used":{"#bigint":"1"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826695},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"35"},"tx_results":[{"gas_used":{"#bigint":"35"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"35"},"tx_results":[{"gas_used":{"#bigint":"35"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_bootstrapTest_7.itf.json b/specs/traces/out_bootstrapTest_7.itf.json index 3eda69a..206e024 100644 --- a/specs/traces/out_bootstrapTest_7.itf.json +++ b/specs/traces/out_bootstrapTest_7.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400949},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"21"},"tx_results":[{"gas_used":{"#bigint":"21"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"21"},"tx_results":[{"gas_used":{"#bigint":"21"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826694},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"55"},"tx_results":[{"gas_used":{"#bigint":"55"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"55"},"tx_results":[{"gas_used":{"#bigint":"55"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_emptyBlockTest_0.itf.json b/specs/traces/out_emptyBlockTest_0.itf.json index a9d9906..d51e1c4 100644 --- a/specs/traces/out_emptyBlockTest_0.itf.json +++ b/specs/traces/out_emptyBlockTest_0.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400934},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826677},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_executionFailureRollbackTest_3.itf.json b/specs/traces/out_executionFailureRollbackTest_3.itf.json index f7a921f..ee6f361 100644 --- a/specs/traces/out_executionFailureRollbackTest_3.itf.json +++ b/specs/traces/out_executionFailureRollbackTest_3.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400937},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826682},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_mixedOutcomesTest_6.itf.json b/specs/traces/out_mixedOutcomesTest_6.itf.json index 5dd8fdd..13d7994 100644 --- a/specs/traces/out_mixedOutcomesTest_6.itf.json +++ b/specs/traces/out_mixedOutcomesTest_6.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400941},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826686},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_multiBlockTest_9.itf.json b/specs/traces/out_multiBlockTest_9.itf.json index 35d8463..6bf7e9b 100644 --- a/specs/traces/out_multiBlockTest_9.itf.json +++ b/specs/traces/out_multiBlockTest_9.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400952},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}},{"#meta":{"index":4},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826696},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}},{"#meta":{"index":4},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_outOfGasTest_4.itf.json b/specs/traces/out_outOfGasTest_4.itf.json index 654f409..3d93a74 100644 --- a/specs/traces/out_outOfGasTest_4.itf.json +++ b/specs/traces/out_outOfGasTest_4.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400938},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826683},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_overwriteTest_10.itf.json b/specs/traces/out_overwriteTest_10.itf.json index 933b5d9..ae9d449 100644 --- a/specs/traces/out_overwriteTest_10.itf.json +++ b/specs/traces/out_overwriteTest_10.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400953},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826697},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_successfulTxTest_1.itf.json b/specs/traces/out_successfulTxTest_1.itf.json index 03391d9..362a896 100644 --- a/specs/traces/out_successfulTxTest_1.itf.json +++ b/specs/traces/out_successfulTxTest_1.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400936},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826679},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_validationFailureTest_2.itf.json b/specs/traces/out_validationFailureTest_2.itf.json index 3342b60..f334fef 100644 --- a/specs/traces/out_validationFailureTest_2.itf.json +++ b/specs/traces/out_validationFailureTest_2.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:10:00 GMT+0100 (Central European Standard Time)","timestamp":1771967400937},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826681},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file From a4508014ceeab754f8b540597732b0b5a0271579 Mon Sep 17 00:00:00 2001 From: tac0turtle Date: Tue, 24 Feb 2026 22:40:02 +0100 Subject: [PATCH 3/9] add some more --- .../stf/tests/quint_call_depth_conformance.rs | 274 +++++++++++++ crates/app/stf/tests/quint_common.rs | 135 +++++++ crates/app/stf/tests/quint_conformance.rs | 144 +------ .../stf/tests/quint_post_tx_conformance.rs | 367 ++++++++++++++++++ justfile | 28 +- specs/stf_call_depth.qnt | 60 +++ specs/{stf.qnt => stf_core.qnt} | 0 specs/stf_post_tx.qnt | 182 +++++++++ specs/traces/out_blockGasLimitTest_5.itf.json | 2 +- .../out_bootstrapFailureTest_8.itf.json | 2 +- specs/traces/out_bootstrapTest_7.itf.json | 2 +- .../out_callDepthAtLimitFailsTest_1.itf.json | 1 + ...callDepthBelowLimitSucceedsTest_0.itf.json | 1 + specs/traces/out_emptyBlockTest_0.itf.json | 2 +- ...ut_executionFailureRollbackTest_3.itf.json | 2 +- specs/traces/out_mixedOutcomesTest_6.itf.json | 2 +- specs/traces/out_multiBlockTest_9.itf.json | 2 +- specs/traces/out_outOfGasTest_4.itf.json | 2 +- specs/traces/out_overwriteTest_10.itf.json | 2 +- ...ostTxDoesNotMaskExecFailureTest_1.itf.json | 1 + ..._postTxRejectsButKeepsStateTest_0.itf.json | 1 + specs/traces/out_successfulTxTest_1.itf.json | 2 +- .../out_validationFailureTest_2.itf.json | 2 +- 23 files changed, 1076 insertions(+), 140 deletions(-) create mode 100644 crates/app/stf/tests/quint_call_depth_conformance.rs create mode 100644 crates/app/stf/tests/quint_common.rs create mode 100644 crates/app/stf/tests/quint_post_tx_conformance.rs create mode 100644 specs/stf_call_depth.qnt rename specs/{stf.qnt => stf_core.qnt} (100%) create mode 100644 specs/stf_post_tx.qnt create mode 100644 specs/traces/out_callDepthAtLimitFailsTest_1.itf.json create mode 100644 specs/traces/out_callDepthBelowLimitSucceedsTest_0.itf.json create mode 100644 specs/traces/out_postTxDoesNotMaskExecFailureTest_1.itf.json create mode 100644 specs/traces/out_postTxRejectsButKeepsStateTest_0.itf.json diff --git a/crates/app/stf/tests/quint_call_depth_conformance.rs b/crates/app/stf/tests/quint_call_depth_conformance.rs new file mode 100644 index 0000000..6551d67 --- /dev/null +++ b/crates/app/stf/tests/quint_call_depth_conformance.rs @@ -0,0 +1,274 @@ +//! Conformance tests: replay Quint ITF traces for stf_call_depth.qnt. +//! +//! Run: +//! `quint test specs/stf_call_depth.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json"` +//! `cargo test -p evolve_stf --test quint_call_depth_conformance` + +use borsh::{BorshDeserialize, BorshSerialize}; +use evolve_core::{ + AccountCode, AccountId, BlockContext, Environment, EnvironmentQuery, FungibleAsset, + InvokableMessage, InvokeRequest, InvokeResponse, Message, SdkResult, +}; +use evolve_stf::gas::StorageGasConfig; +use evolve_stf::Stf; +use evolve_stf_traits::{Block as BlockTrait, PostTxExecution, Transaction, TxValidator}; +use serde::Deserialize; +use std::path::Path; + +mod quint_common; +use quint_common::{ + account_code_key, find_single_trace_file, read_itf_trace, CodeStore, InMemoryStorage, + ItfBigInt, NoopBegin, NoopEnd, +}; + +#[derive(Deserialize)] +struct ItfTrace { + states: Vec, +} + +#[derive(Deserialize)] +struct ItfState { + last_result: ItfTxResult, +} + +#[derive(Deserialize)] +struct ItfTxResult { + result: ItfResult, +} + +#[derive(Deserialize)] +struct ItfResult { + ok: bool, + err_code: ItfBigInt, +} + +#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] +struct RecurseMsg { + remaining: u16, +} + +impl InvokableMessage for RecurseMsg { + const FUNCTION_IDENTIFIER: u64 = 1; + const FUNCTION_IDENTIFIER_NAME: &'static str = "recurse"; +} + +#[derive(Clone, Debug)] +struct TestTx { + sender: AccountId, + recipient: AccountId, + request: InvokeRequest, + gas_limit: u64, + funds: Vec, +} + +impl Transaction for TestTx { + fn sender(&self) -> AccountId { + self.sender + } + fn recipient(&self) -> AccountId { + self.recipient + } + fn request(&self) -> &InvokeRequest { + &self.request + } + fn gas_limit(&self) -> u64 { + self.gas_limit + } + fn funds(&self) -> &[FungibleAsset] { + &self.funds + } + fn compute_identifier(&self) -> [u8; 32] { + [0u8; 32] + } +} + +#[derive(Clone)] +struct TestBlock { + height: u64, + time: u64, + txs: Vec, +} + +impl BlockTrait for TestBlock { + fn context(&self) -> BlockContext { + BlockContext::new(self.height, self.time) + } + fn txs(&self) -> &[TestTx] { + &self.txs + } + fn gas_limit(&self) -> u64 { + 1_000_000 + } +} + +#[derive(Default)] +struct NoopValidator; +impl TxValidator for NoopValidator { + fn validate_tx(&self, _tx: &TestTx, _env: &mut dyn Environment) -> SdkResult<()> { + Ok(()) + } +} + +#[derive(Default)] +struct NoopPostTx; +impl PostTxExecution for NoopPostTx { + fn after_tx_executed( + _tx: &TestTx, + _gas_consumed: u64, + _tx_result: &SdkResult, + _env: &mut dyn Environment, + ) -> SdkResult<()> { + Ok(()) + } +} + +#[derive(Default)] +struct RecursiveAccount; + +impl AccountCode for RecursiveAccount { + fn identifier(&self) -> String { + "recursive".to_string() + } + fn schema(&self) -> evolve_core::schema::AccountSchema { + evolve_core::schema::AccountSchema::new("RecursiveAccount", "recursive") + } + fn init( + &self, + _env: &mut dyn Environment, + _request: &InvokeRequest, + ) -> SdkResult { + InvokeResponse::new(&()) + } + fn execute( + &self, + env: &mut dyn Environment, + request: &InvokeRequest, + ) -> SdkResult { + let msg: RecurseMsg = request.get()?; + if msg.remaining == 0 { + return InvokeResponse::new(&()); + } + let next = RecurseMsg { + remaining: msg.remaining - 1, + }; + env.do_exec(env.whoami(), &InvokeRequest::new(&next)?, vec![])?; + InvokeResponse::new(&()) + } + fn query( + &self, + _env: &mut dyn EnvironmentQuery, + _request: &InvokeRequest, + ) -> SdkResult { + InvokeResponse::new(&()) + } +} + +const SPEC_ERR_CALL_DEPTH: i64 = 0x14; +const RECURSIVE_ACCOUNT: u128 = 100; +const TEST_SENDER: u128 = 200; + +struct ConformanceCase { + test_name: &'static str, + requested_depth: u16, +} + +fn known_test_cases() -> Vec { + vec![ + ConformanceCase { + test_name: "callDepthBelowLimitSucceedsTest", + requested_depth: 63, + }, + ConformanceCase { + test_name: "callDepthAtLimitFailsTest", + requested_depth: 64, + }, + ] +} + +#[test] +fn quint_itf_call_depth_conformance() { + let traces_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../specs/traces"); + if !traces_dir.exists() { + panic!( + "ITF traces not found at {}. Run: quint test specs/stf_call_depth.qnt --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"", + traces_dir.display() + ); + } + + let test_cases = known_test_cases(); + for case in &test_cases { + let trace_file = find_single_trace_file(&traces_dir, case.test_name); + let trace: ItfTrace = read_itf_trace(&trace_file); + let spec_state = trace + .states + .last() + .expect("trace must have at least one state"); + let spec_result = &spec_state.last_result; + + let gas_config = StorageGasConfig { + storage_get_charge: 1, + storage_set_charge: 1, + storage_remove_charge: 1, + }; + let stf = Stf::new( + NoopBegin::::default(), + NoopEnd, + NoopValidator, + NoopPostTx, + gas_config, + ); + + let mut storage = InMemoryStorage::default(); + let mut codes = CodeStore::new(); + codes.add_code(RecursiveAccount); + + let recursive_account = AccountId::new(RECURSIVE_ACCOUNT); + let code_id = "recursive".to_string(); + storage.data.insert( + account_code_key(recursive_account), + Message::new(&code_id).unwrap().into_bytes().unwrap(), + ); + + let msg = RecurseMsg { + remaining: case.requested_depth, + }; + let tx = TestTx { + sender: AccountId::new(TEST_SENDER), + recipient: recursive_account, + request: InvokeRequest::new(&msg).unwrap(), + gas_limit: 1_000_000, + funds: vec![], + }; + let block = TestBlock { + height: 1, + time: 0, + txs: vec![tx], + }; + + let (real_result, _exec_state) = stf.apply_block(&storage, &codes, &block); + assert_eq!( + real_result.tx_results.len(), + 1, + "{}: expected one tx result", + case.test_name + ); + + let spec_ok = spec_result.result.ok; + let real_ok = real_result.tx_results[0].response.is_ok(); + assert_eq!(real_ok, spec_ok, "{}: ok mismatch", case.test_name); + + if !spec_ok { + let spec_err = spec_result.result.err_code.as_i64(); + let real_err = real_result.tx_results[0].response.as_ref().unwrap_err().id; + match spec_err { + SPEC_ERR_CALL_DEPTH => assert_eq!( + real_err, + evolve_stf::errors::ERR_CALL_DEPTH_EXCEEDED.id, + "{}: expected call depth error", + case.test_name + ), + _ => panic!("{}: unknown spec error code {spec_err}", case.test_name), + } + } + } +} diff --git a/crates/app/stf/tests/quint_common.rs b/crates/app/stf/tests/quint_common.rs new file mode 100644 index 0000000..545ba72 --- /dev/null +++ b/crates/app/stf/tests/quint_common.rs @@ -0,0 +1,135 @@ +#![allow(dead_code)] + +use evolve_core::runtime_api::ACCOUNT_IDENTIFIER_PREFIX; +use evolve_core::{AccountCode, AccountId, ErrorCode, ReadonlyKV}; +use evolve_stf_traits::{AccountsCodeStorage, BeginBlocker, EndBlocker, StateChange, WritableKV}; +use hashbrown::HashMap; +use serde::de::DeserializeOwned; +use serde::Deserialize; +use std::fs; +use std::path::Path; + +#[derive(Deserialize, Clone, Debug)] +pub struct ItfBigInt { + #[serde(rename = "#bigint")] + value: String, +} + +impl ItfBigInt { + pub fn as_i64(&self) -> i64 { + self.value.parse().unwrap() + } + pub fn as_u64(&self) -> u64 { + self.value.parse().unwrap() + } +} + +#[derive(Deserialize)] +pub struct ItfMap { + #[serde(rename = "#map")] + pub entries: Vec<(K, V)>, +} + +pub fn find_single_trace_file(traces_dir: &Path, test_name: &str) -> std::path::PathBuf { + let trace_files: Vec<_> = fs::read_dir(traces_dir) + .unwrap() + .filter_map(|e| e.ok()) + .filter(|e| { + let name = e.file_name().to_string_lossy().to_string(); + name.starts_with(&format!("out_{}_", test_name)) && name.ends_with(".itf.json") + }) + .collect(); + + assert_eq!( + trace_files.len(), + 1, + "{}: expected exactly 1 trace file, found {}", + test_name, + trace_files.len() + ); + trace_files[0].path() +} + +pub fn read_itf_trace(trace_path: &Path) -> T { + let trace_json = fs::read_to_string(trace_path).unwrap(); + serde_json::from_str(&trace_json).unwrap() +} + +pub struct NoopBegin(std::marker::PhantomData); + +impl Default for NoopBegin { + fn default() -> Self { + Self(std::marker::PhantomData) + } +} + +impl BeginBlocker for NoopBegin { + fn begin_block(&self, _block: &B, _env: &mut dyn evolve_core::Environment) {} +} + +#[derive(Default)] +pub struct NoopEnd; + +impl EndBlocker for NoopEnd { + fn end_block(&self, _env: &mut dyn evolve_core::Environment) {} +} + +pub struct CodeStore { + codes: HashMap>, +} + +impl CodeStore { + pub fn new() -> Self { + Self { + codes: HashMap::new(), + } + } + pub fn add_code(&mut self, code: impl AccountCode + 'static) { + self.codes.insert(code.identifier(), Box::new(code)); + } +} + +impl AccountsCodeStorage for CodeStore { + fn with_code(&self, identifier: &str, f: F) -> Result + where + F: FnOnce(Option<&dyn AccountCode>) -> R, + { + Ok(f(self.codes.get(identifier).map(|c| c.as_ref()))) + } + fn list_identifiers(&self) -> Vec { + self.codes.keys().cloned().collect() + } +} + +#[derive(Default)] +pub struct InMemoryStorage { + pub data: HashMap, Vec>, +} + +impl ReadonlyKV for InMemoryStorage { + fn get(&self, key: &[u8]) -> Result>, ErrorCode> { + Ok(self.data.get(key).cloned()) + } +} + +impl WritableKV for InMemoryStorage { + fn apply_changes(&mut self, changes: Vec) -> Result<(), ErrorCode> { + for change in changes { + match change { + StateChange::Set { key, value } => { + self.data.insert(key, value); + } + StateChange::Remove { key } => { + self.data.remove(&key); + } + } + } + Ok(()) + } +} + +pub fn account_code_key(account: AccountId) -> Vec { + let mut out = vec![ACCOUNT_IDENTIFIER_PREFIX]; + out.extend_from_slice(&account.as_bytes()); + out +} diff --git a/crates/app/stf/tests/quint_conformance.rs b/crates/app/stf/tests/quint_conformance.rs index 991b682..aed5650 100644 --- a/crates/app/stf/tests/quint_conformance.rs +++ b/crates/app/stf/tests/quint_conformance.rs @@ -1,31 +1,34 @@ //! Conformance tests: replay Quint ITF traces against the real STF. //! //! This test reads ITF (Informal Trace Format) JSON files generated by -//! `quint test specs/stf.qnt --out-itf ...` and replays each trace against +//! `quint test specs/stf_core.qnt --out-itf ...` and replays each trace against //! the actual STF implementation, asserting that the real execution matches //! the spec's expected outcomes. //! //! Run: `cargo test -p evolve_stf --test quint_conformance` -//! Regenerate traces: `quint test specs/stf.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json"` +//! Regenerate traces: `quint test specs/stf_core.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json"` use borsh::{BorshDeserialize, BorshSerialize}; -use evolve_core::runtime_api::ACCOUNT_IDENTIFIER_PREFIX; use evolve_core::storage_api::{StorageSetRequest, STORAGE_ACCOUNT_ID}; use evolve_core::{ AccountCode, AccountId, BlockContext, Environment, EnvironmentQuery, ErrorCode, FungibleAsset, - InvokableMessage, InvokeRequest, InvokeResponse, Message, ReadonlyKV, SdkResult, + InvokableMessage, InvokeRequest, InvokeResponse, Message, SdkResult, }; use evolve_stf::gas::StorageGasConfig; use evolve_stf::Stf; use evolve_stf_traits::{ - AccountsCodeStorage, BeginBlocker, Block as BlockTrait, EndBlocker, PostTxExecution, - SenderBootstrap, StateChange, Transaction, TxValidator, WritableKV, + Block as BlockTrait, PostTxExecution, SenderBootstrap, Transaction, TxValidator, WritableKV, }; use hashbrown::HashMap; use serde::Deserialize; -use std::fs; use std::path::Path; +mod quint_common; +use quint_common::{ + account_code_key, find_single_trace_file, read_itf_trace, CodeStore, InMemoryStorage, + ItfBigInt, ItfMap, NoopBegin, NoopEnd, +}; + // --------------------------------------------------------------------------- // ITF deserialization types // --------------------------------------------------------------------------- @@ -42,27 +45,6 @@ struct ItfState { storage: ItfMap, Vec>>, } -#[derive(Deserialize, Clone, Debug)] -struct ItfBigInt { - #[serde(rename = "#bigint")] - value: String, -} - -impl ItfBigInt { - fn as_i64(&self) -> i64 { - self.value.parse().unwrap() - } - fn as_u64(&self) -> u64 { - self.value.parse().unwrap() - } -} - -#[derive(Deserialize)] -struct ItfMap { - #[serde(rename = "#map")] - entries: Vec<(K, V)>, -} - #[derive(Deserialize)] struct ItfBlockResult { gas_used: ItfBigInt, @@ -165,18 +147,6 @@ impl BlockTrait for TestBlock { } } -#[derive(Default)] -struct NoopBegin; -impl BeginBlocker for NoopBegin { - fn begin_block(&self, _block: &TestBlock, _env: &mut dyn Environment) {} -} - -#[derive(Default)] -struct NoopEnd; -impl EndBlocker for NoopEnd { - fn end_block(&self, _env: &mut dyn Environment) {} -} - #[derive(Default)] struct Validator; impl TxValidator for Validator { @@ -252,66 +222,6 @@ impl AccountCode for TestAccount { } } -struct CodeStore { - codes: HashMap>, -} - -impl CodeStore { - fn new() -> Self { - Self { - codes: HashMap::new(), - } - } - fn add_code(&mut self, code: impl AccountCode + 'static) { - self.codes.insert(code.identifier(), Box::new(code)); - } -} - -impl AccountsCodeStorage for CodeStore { - fn with_code(&self, identifier: &str, f: F) -> Result - where - F: FnOnce(Option<&dyn AccountCode>) -> R, - { - Ok(f(self.codes.get(identifier).map(|c| c.as_ref()))) - } - fn list_identifiers(&self) -> Vec { - self.codes.keys().cloned().collect() - } -} - -#[derive(Default)] -struct InMemoryStorage { - data: HashMap, Vec>, -} - -impl ReadonlyKV for InMemoryStorage { - fn get(&self, key: &[u8]) -> Result>, ErrorCode> { - Ok(self.data.get(key).cloned()) - } -} - -impl WritableKV for InMemoryStorage { - fn apply_changes(&mut self, changes: Vec) -> Result<(), ErrorCode> { - for change in changes { - match change { - StateChange::Set { key, value } => { - self.data.insert(key, value); - } - StateChange::Remove { key } => { - self.data.remove(&key); - } - } - } - Ok(()) - } -} - -fn account_code_key(account: AccountId) -> Vec { - let mut out = vec![ACCOUNT_IDENTIFIER_PREFIX]; - out.extend_from_slice(&account.as_bytes()); - out -} - // --------------------------------------------------------------------------- // Test case definitions (must match the Quint spec's run declarations) // --------------------------------------------------------------------------- @@ -653,7 +563,7 @@ fn quint_itf_conformance() { if !traces_dir.exists() { panic!( "ITF traces not found at {}. \ - Run: quint test specs/stf.qnt \ + Run: quint test specs/stf_core.qnt \ --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"", traces_dir.display() ); @@ -664,26 +574,8 @@ fn quint_itf_conformance() { for case in &test_cases { // Find the unique trace file for this test. - let trace_files: Vec<_> = fs::read_dir(&traces_dir) - .unwrap() - .filter_map(|e| e.ok()) - .filter(|e| { - let name = e.file_name().to_string_lossy().to_string(); - name.starts_with(&format!("out_{}_", case.test_name)) && name.ends_with(".itf.json") - }) - .collect(); - - assert_eq!( - trace_files.len(), - 1, - "{}: expected exactly 1 trace file, found {}", - case.test_name, - trace_files.len() - ); - let trace_file = &trace_files[0]; - - let trace_json = fs::read_to_string(trace_file.path()).unwrap(); - let trace: ItfTrace = serde_json::from_str(&trace_json).unwrap(); + let trace_file = find_single_trace_file(&traces_dir, case.test_name); + let trace: ItfTrace = read_itf_trace(&trace_file); // Find the final state for this run after all apply_block steps. let expected_block_height = @@ -707,7 +599,13 @@ fn quint_itf_conformance() { storage_set_charge: 1, storage_remove_charge: 1, }; - let stf = Stf::new(NoopBegin, NoopEnd, Validator, NoopPostTx, gas_config); + let stf = Stf::new( + NoopBegin::::default(), + NoopEnd, + Validator, + NoopPostTx, + gas_config, + ); let mut storage = InMemoryStorage::default(); let mut codes = CodeStore::new(); @@ -853,7 +751,7 @@ fn quint_itf_conformance() { assert!( matched == test_cases.len(), "Matched {matched}/{} traces. Regenerate with: \ - quint test specs/stf.qnt \ + quint test specs/stf_core.qnt \ --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"", test_cases.len() ); diff --git a/crates/app/stf/tests/quint_post_tx_conformance.rs b/crates/app/stf/tests/quint_post_tx_conformance.rs new file mode 100644 index 0000000..929922e --- /dev/null +++ b/crates/app/stf/tests/quint_post_tx_conformance.rs @@ -0,0 +1,367 @@ +//! Conformance tests: replay Quint ITF traces for stf_post_tx.qnt. +//! +//! Run: +//! `quint test specs/stf_post_tx.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json"` +//! `cargo test -p evolve_stf --test quint_post_tx_conformance` + +use borsh::{BorshDeserialize, BorshSerialize}; +use evolve_core::storage_api::{StorageSetRequest, STORAGE_ACCOUNT_ID}; +use evolve_core::{ + AccountCode, AccountId, BlockContext, Environment, EnvironmentQuery, ErrorCode, FungibleAsset, + InvokableMessage, InvokeRequest, InvokeResponse, Message, SdkResult, +}; +use evolve_stf::gas::StorageGasConfig; +use evolve_stf::Stf; +use evolve_stf_traits::{ + Block as BlockTrait, PostTxExecution, Transaction, TxValidator, WritableKV, +}; +use hashbrown::HashMap; +use serde::Deserialize; +use std::path::Path; + +mod quint_common; +use quint_common::{ + account_code_key, find_single_trace_file, read_itf_trace, CodeStore, InMemoryStorage, + ItfBigInt, ItfMap, NoopBegin, NoopEnd, +}; + +#[derive(Deserialize)] +struct ItfTrace { + states: Vec, +} + +#[derive(Deserialize)] +struct ItfState { + last_result: ItfBlockResult, + storage: ItfMap, Vec>>, +} + +#[derive(Deserialize)] +struct ItfBlockResult { + gas_used: ItfBigInt, + tx_results: Vec, +} + +#[derive(Deserialize)] +struct ItfTxResult { + gas_used: ItfBigInt, + result: ItfResult, +} + +#[derive(Deserialize)] +struct ItfResult { + ok: bool, + err_code: ItfBigInt, +} + +#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] +struct TestMsg { + key: Vec, + value: Vec, + fail_after_write: bool, +} + +impl InvokableMessage for TestMsg { + const FUNCTION_IDENTIFIER: u64 = 1; + const FUNCTION_IDENTIFIER_NAME: &'static str = "test_exec"; +} + +#[derive(Clone, Debug)] +struct TestTx { + sender: AccountId, + recipient: AccountId, + request: InvokeRequest, + gas_limit: u64, + funds: Vec, + reject_post_tx: bool, +} + +impl Transaction for TestTx { + fn sender(&self) -> AccountId { + self.sender + } + fn recipient(&self) -> AccountId { + self.recipient + } + fn request(&self) -> &InvokeRequest { + &self.request + } + fn gas_limit(&self) -> u64 { + self.gas_limit + } + fn funds(&self) -> &[FungibleAsset] { + &self.funds + } + fn compute_identifier(&self) -> [u8; 32] { + [0u8; 32] + } +} + +#[derive(Clone)] +struct TestBlock { + height: u64, + time: u64, + txs: Vec, +} + +impl BlockTrait for TestBlock { + fn context(&self) -> BlockContext { + BlockContext::new(self.height, self.time) + } + fn txs(&self) -> &[TestTx] { + &self.txs + } +} + +#[derive(Default)] +struct NoopValidator; +impl TxValidator for NoopValidator { + fn validate_tx(&self, _tx: &TestTx, _env: &mut dyn Environment) -> SdkResult<()> { + Ok(()) + } +} + +#[derive(Default)] +struct RejectingPostTx; +impl PostTxExecution for RejectingPostTx { + fn after_tx_executed( + tx: &TestTx, + _gas_consumed: u64, + tx_result: &SdkResult, + _env: &mut dyn Environment, + ) -> SdkResult<()> { + if tx.reject_post_tx && tx_result.is_ok() { + return Err(ErrorCode::new(999)); + } + Ok(()) + } +} + +#[derive(Default)] +struct TestAccount; + +impl AccountCode for TestAccount { + fn identifier(&self) -> String { + "test_account".to_string() + } + fn schema(&self) -> evolve_core::schema::AccountSchema { + evolve_core::schema::AccountSchema::new("TestAccount", "test_account") + } + fn init( + &self, + _env: &mut dyn Environment, + _request: &InvokeRequest, + ) -> SdkResult { + InvokeResponse::new(&()) + } + fn execute( + &self, + env: &mut dyn Environment, + request: &InvokeRequest, + ) -> SdkResult { + let msg: TestMsg = request.get()?; + let set = StorageSetRequest { + key: msg.key.clone(), + value: Message::from_bytes(msg.value.clone()), + }; + env.do_exec(STORAGE_ACCOUNT_ID, &InvokeRequest::new(&set)?, vec![])?; + if msg.fail_after_write { + return Err(ErrorCode::new(200)); + } + InvokeResponse::new(&()) + } + fn query( + &self, + _env: &mut dyn EnvironmentQuery, + _request: &InvokeRequest, + ) -> SdkResult { + InvokeResponse::new(&()) + } +} + +const SPEC_ERR_OUT_OF_GAS: i64 = 0x01; +const SPEC_ERR_EXECUTION: i64 = 200; +const SPEC_ERR_POST_TX: i64 = 999; +const TEST_ACCOUNT: u128 = 100; +const TEST_SENDER: u128 = 200; + +struct ConformanceCase { + test_name: &'static str, + block: TestBlock, +} + +fn make_tx(fail_execute: bool, reject_post_tx: bool) -> TestTx { + let msg = TestMsg { + key: vec![1], + value: vec![11], + fail_after_write: fail_execute, + }; + TestTx { + sender: AccountId::new(TEST_SENDER), + recipient: AccountId::new(TEST_ACCOUNT), + request: InvokeRequest::new(&msg).unwrap(), + gas_limit: 10000, + funds: vec![], + reject_post_tx, + } +} + +fn known_test_cases() -> Vec { + vec![ + ConformanceCase { + test_name: "postTxRejectsButKeepsStateTest", + block: TestBlock { + height: 1, + time: 0, + txs: vec![make_tx(false, true)], + }, + }, + ConformanceCase { + test_name: "postTxDoesNotMaskExecFailureTest", + block: TestBlock { + height: 1, + time: 0, + txs: vec![make_tx(true, true)], + }, + }, + ] +} + +#[test] +fn quint_itf_post_tx_conformance() { + let traces_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../specs/traces"); + if !traces_dir.exists() { + panic!( + "ITF traces not found at {}. Run: quint test specs/stf_post_tx.qnt --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"", + traces_dir.display() + ); + } + + let test_cases = known_test_cases(); + for case in &test_cases { + let trace_file = find_single_trace_file(&traces_dir, case.test_name); + let trace: ItfTrace = read_itf_trace(&trace_file); + let spec_state = trace + .states + .last() + .expect("trace must have at least one state"); + let spec_result = &spec_state.last_result; + + let gas_config = StorageGasConfig { + storage_get_charge: 1, + storage_set_charge: 1, + storage_remove_charge: 1, + }; + let stf = Stf::new( + NoopBegin::::default(), + NoopEnd, + NoopValidator, + RejectingPostTx, + gas_config, + ); + + let mut storage = InMemoryStorage::default(); + let mut codes = CodeStore::new(); + codes.add_code(TestAccount); + + let test_account = AccountId::new(TEST_ACCOUNT); + let code_id = "test_account".to_string(); + storage.data.insert( + account_code_key(test_account), + Message::new(&code_id).unwrap().into_bytes().unwrap(), + ); + + let (real_result, exec_state) = stf.apply_block(&storage, &codes, &case.block); + storage + .apply_changes(exec_state.into_changes().unwrap()) + .unwrap(); + + assert_eq!( + real_result.tx_results.len(), + spec_result.tx_results.len(), + "{}: tx_results count mismatch", + case.test_name + ); + + for (i, (real_tx, spec_tx)) in real_result + .tx_results + .iter() + .zip(spec_result.tx_results.iter()) + .enumerate() + { + let spec_ok = spec_tx.result.ok; + let real_ok = real_tx.response.is_ok(); + assert_eq!(real_ok, spec_ok, "{} tx[{i}]: ok mismatch", case.test_name); + + if !spec_ok { + let spec_err = spec_tx.result.err_code.as_i64(); + let real_err = real_tx.response.as_ref().unwrap_err().id; + match spec_err { + SPEC_ERR_OUT_OF_GAS => assert_eq!( + real_err, + evolve_stf::ERR_OUT_OF_GAS.id, + "{} tx[{i}]: expected OOG", + case.test_name + ), + SPEC_ERR_EXECUTION => assert_eq!( + real_err, 200, + "{} tx[{i}]: expected execution error", + case.test_name + ), + SPEC_ERR_POST_TX => assert_eq!( + real_err, 999, + "{} tx[{i}]: expected post-tx error", + case.test_name + ), + _ => panic!( + "{} tx[{i}]: unknown spec error code {spec_err}", + case.test_name + ), + } + } + + assert_eq!( + real_tx.gas_used, + spec_tx.gas_used.as_u64(), + "{} tx[{i}]: gas_used mismatch", + case.test_name + ); + } + + assert_eq!( + real_result.gas_used, + spec_result.gas_used.as_u64(), + "{}: block gas mismatch", + case.test_name + ); + + let account_id = AccountId::new(TEST_ACCOUNT); + let mut expected = HashMap::, Vec>::new(); + for (account_id_itf, account_store_itf) in &spec_state.storage.entries { + if AccountId::new(account_id_itf.as_u64() as u128) != account_id { + continue; + } + for (key_itf, value_itf) in &account_store_itf.entries { + let key: Vec = key_itf.iter().map(|b| b.as_u64() as u8).collect(); + let value: Vec = value_itf.iter().map(|b| b.as_u64() as u8).collect(); + expected.insert(key, value); + } + } + + let mut actual = HashMap::, Vec>::new(); + let account_prefix = account_id.as_bytes(); + for (raw_key, raw_value) in &storage.data { + if raw_key.len() < account_prefix.len() { + continue; + } + if raw_key[..account_prefix.len()] == account_prefix { + actual.insert(raw_key[account_prefix.len()..].to_vec(), raw_value.clone()); + } + } + assert_eq!( + actual, expected, + "{}: storage mismatch for account {:?}", + case.test_name, account_id + ); + } +} diff --git a/justfile b/justfile index 2e5f838..af77979 100644 --- a/justfile +++ b/justfile @@ -218,19 +218,35 @@ sim-report trace: # SPEC CONFORMANCE # ============================================================================ -# Run Quint STF spec tests and Rust conformance checks +# Run core STF Quint tests/traces and Rust conformance checks [group('spec')] -spec-test: - quint test specs/stf.qnt +spec-test-core: + quint test specs/stf_core.qnt rm -f specs/traces/*.itf.json - quint test specs/stf.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json" + quint test specs/stf_core.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json" cargo test -p evolve_stf --test quint_conformance -# Regenerate ITF traces from Quint spec (run after editing specs/stf.qnt) +# Run extended STF model specs (currently Quint-only) +[group('spec')] +spec-test-extended: + quint test specs/stf_post_tx.qnt + quint test specs/stf_post_tx.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json" + cargo test -p evolve_stf --test quint_post_tx_conformance + quint test specs/stf_call_depth.qnt + quint test specs/stf_call_depth.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json" + cargo test -p evolve_stf --test quint_call_depth_conformance + +# Run full STF spec suite (core + extended) +[group('spec')] +spec-test: + just spec-test-core + just spec-test-extended + +# Regenerate ITF traces from core Quint spec (run after editing specs/stf_core.qnt) [group('spec')] spec-traces: rm -f specs/traces/*.itf.json - quint test specs/stf.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json" + quint test specs/stf_core.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json" # ============================================================================ # BENCHMARKS diff --git a/specs/stf_call_depth.qnt b/specs/stf_call_depth.qnt new file mode 100644 index 0000000..5ab7abc --- /dev/null +++ b/specs/stf_call_depth.qnt @@ -0,0 +1,60 @@ +// Evolve SDK - STF Call Depth Specification +// +// Focus: call depth enforcement for nested do_exec paths. + +module stf_call_depth { + type Result = { ok: bool, err_code: int } + val OK: Result = { ok: true, err_code: 0 } + pure def Err(code: int): Result = { ok: false, err_code: code } + + val ERR_CALL_DEPTH: int = 0x14 + val MAX_CALL_DEPTH: int = 64 + + type Tx = { + requested_depth: int, + } + + type TxResult = { + result: Result, + } + + var last_result: TxResult + + pure def process_tx(tx: Tx): TxResult = + // Runtime rejects when call_depth >= MAX_CALL_DEPTH before the next call. + if (tx.requested_depth >= MAX_CALL_DEPTH) + { result: Err(ERR_CALL_DEPTH) } + else + { result: OK } + + action init = all { + last_result' = { result: OK }, + } + + action apply_tx(tx: Tx): bool = all { + last_result' = process_tx(tx), + } + + action stutter: bool = all { + last_result' = last_result, + } + + run callDepthBelowLimitSucceedsTest = { + init + .then(apply_tx({ requested_depth: 63 })) + .then(all { + assert(last_result.result.ok == true), + stutter, + }) + } + + run callDepthAtLimitFailsTest = { + init + .then(apply_tx({ requested_depth: 64 })) + .then(all { + assert(last_result.result.ok == false), + assert(last_result.result.err_code == ERR_CALL_DEPTH), + stutter, + }) + } +} diff --git a/specs/stf.qnt b/specs/stf_core.qnt similarity index 100% rename from specs/stf.qnt rename to specs/stf_core.qnt diff --git a/specs/stf_post_tx.qnt b/specs/stf_post_tx.qnt new file mode 100644 index 0000000..5a03443 --- /dev/null +++ b/specs/stf_post_tx.qnt @@ -0,0 +1,182 @@ +// Evolve SDK - STF Post-Tx Hook Specification +// +// Focus: post-tx handler semantics. +// In the runtime, a post-tx error can override a successful tx response, +// but state changes from execution remain committed. + +module stf_post_tx { + type AccountId = int + type Key = List[int] + type Value = List[int] + + type Result = { ok: bool, err_code: int } + val OK: Result = { ok: true, err_code: 0 } + pure def Err(code: int): Result = { ok: false, err_code: code } + + val ERR_OUT_OF_GAS: int = 0x01 + val ERR_EXECUTION: int = 200 + val ERR_POST_TX: int = 999 + + type GasConfig = { + set_charge: int, + } + + type Tx = { + recipient: AccountId, + gas_limit: int, + write_key: Key, + write_value: Value, + fail_execute: bool, + reject_post_tx: bool, + } + + type TxResult = { + result: Result, + gas_used: int, + } + + type BlockResult = { + tx_results: List[TxResult], + gas_used: int, + } + + var storage: AccountId -> (Key -> Value) + var accounts: Set[AccountId] + var last_result: BlockResult + + val gas_config: GasConfig = { set_charge: 1 } + val ACCOUNT_ID_BYTE_SIZE: int = 16 + + pure def write_gas_cost(config: GasConfig, key: Key, value: Value): int = + config.set_charge * (ACCOUNT_ID_BYTE_SIZE + key.length() + 1 + value.length() + 1) + + pure def process_tx( + config: GasConfig, + tx: Tx, + account_store: AccountId -> (Key -> Value), + ): { result: TxResult, store: AccountId -> (Key -> Value) } = + val gas_needed = write_gas_cost(config, tx.write_key, tx.write_value) + if (gas_needed > tx.gas_limit) + { + result: { result: Err(ERR_OUT_OF_GAS), gas_used: 0 }, + store: account_store, + } + else if (tx.fail_execute) + // Runtime do_exec rolls back storage on execution error. + { + result: { result: Err(ERR_EXECUTION), gas_used: gas_needed }, + store: account_store, + } + else + // Execution succeeds and writes state. + val recipient_store = + if (account_store.keys().contains(tx.recipient)) + account_store.get(tx.recipient) + else + Map() + val updated_recipient = recipient_store.put(tx.write_key, tx.write_value) + val updated_store = account_store.put(tx.recipient, updated_recipient) + // Post-tx can override response while keeping state. + { + result: { + result: if (tx.reject_post_tx) Err(ERR_POST_TX) else OK, + gas_used: gas_needed, + }, + store: updated_store, + } + + pure def process_block( + config: GasConfig, + txs: List[Tx], + account_store: AccountId -> (Key -> Value), + ): { result: BlockResult, store: AccountId -> (Key -> Value) } = + val init_state = { + tx_results: List(), + cumulative_gas: 0, + store: account_store, + } + val final_state = txs.foldl(init_state, (acc, tx) => + val tx_out = process_tx(config, tx, acc.store) + { + tx_results: acc.tx_results.append(tx_out.result), + cumulative_gas: acc.cumulative_gas + tx_out.result.gas_used, + store: tx_out.store, + } + ) + { + result: { tx_results: final_state.tx_results, gas_used: final_state.cumulative_gas }, + store: final_state.store, + } + + action init = all { + storage' = Map(), + accounts' = Set(), + last_result' = { tx_results: List(), gas_used: 0 }, + } + + action register_account(id: AccountId): bool = all { + not(accounts.contains(id)), + storage' = storage.put(id, Map()), + accounts' = accounts.union(Set(id)), + last_result' = last_result, + } + + action apply_txs(txs: List[Tx]): bool = + val out = process_block(gas_config, txs, storage) + all { + storage' = out.store, + accounts' = accounts, + last_result' = out.result, + } + + action stutter: bool = all { + storage' = storage, + accounts' = accounts, + last_result' = last_result, + } + + val K1: Key = [1] + val V1: Value = [11] + + run postTxRejectsButKeepsStateTest = { + val tx: Tx = { + recipient: 100, + gas_limit: 10000, + write_key: K1, + write_value: V1, + fail_execute: false, + reject_post_tx: true, + } + init + .then(register_account(100)) + .then(apply_txs([tx])) + .then(all { + assert(last_result.tx_results.length() == 1), + assert(last_result.tx_results[0].result.ok == false), + assert(last_result.tx_results[0].result.err_code == ERR_POST_TX), + assert(storage.get(100).get(K1) == V1), + stutter, + }) + } + + run postTxDoesNotMaskExecFailureTest = { + val tx: Tx = { + recipient: 100, + gas_limit: 10000, + write_key: K1, + write_value: V1, + fail_execute: true, + reject_post_tx: true, + } + init + .then(register_account(100)) + .then(apply_txs([tx])) + .then(all { + assert(last_result.tx_results.length() == 1), + assert(last_result.tx_results[0].result.ok == false), + assert(last_result.tx_results[0].result.err_code == ERR_EXECUTION), + assert(storage.get(100) == Map()), + stutter, + }) + } +} diff --git a/specs/traces/out_blockGasLimitTest_5.itf.json b/specs/traces/out_blockGasLimitTest_5.itf.json index 1d2a8fa..bbe2bc0 100644 --- a/specs/traces/out_blockGasLimitTest_5.itf.json +++ b/specs/traces/out_blockGasLimitTest_5.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826685},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084340},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_bootstrapFailureTest_8.itf.json b/specs/traces/out_bootstrapFailureTest_8.itf.json index a2739f9..14b54a8 100644 --- a/specs/traces/out_bootstrapFailureTest_8.itf.json +++ b/specs/traces/out_bootstrapFailureTest_8.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826695},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"35"},"tx_results":[{"gas_used":{"#bigint":"35"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"35"},"tx_results":[{"gas_used":{"#bigint":"35"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084351},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"35"},"tx_results":[{"gas_used":{"#bigint":"35"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"35"},"tx_results":[{"gas_used":{"#bigint":"35"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_bootstrapTest_7.itf.json b/specs/traces/out_bootstrapTest_7.itf.json index 206e024..08edecc 100644 --- a/specs/traces/out_bootstrapTest_7.itf.json +++ b/specs/traces/out_bootstrapTest_7.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826694},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"55"},"tx_results":[{"gas_used":{"#bigint":"55"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"55"},"tx_results":[{"gas_used":{"#bigint":"55"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084350},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"55"},"tx_results":[{"gas_used":{"#bigint":"55"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"55"},"tx_results":[{"gas_used":{"#bigint":"55"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_callDepthAtLimitFailsTest_1.itf.json b/specs/traces/out_callDepthAtLimitFailsTest_1.itf.json new file mode 100644 index 0000000..5bd1f44 --- /dev/null +++ b/specs/traces/out_callDepthAtLimitFailsTest_1.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:16 GMT+0100 (Central European Standard Time)","timestamp":1771969096607},"vars":["last_result"],"states":[{"#meta":{"index":0},"last_result":{"result":{"err_code":{"#bigint":"0"},"ok":true}}},{"#meta":{"index":1},"last_result":{"result":{"err_code":{"#bigint":"20"},"ok":false}}},{"#meta":{"index":2},"last_result":{"result":{"err_code":{"#bigint":"20"},"ok":false}}}]} \ No newline at end of file diff --git a/specs/traces/out_callDepthBelowLimitSucceedsTest_0.itf.json b/specs/traces/out_callDepthBelowLimitSucceedsTest_0.itf.json new file mode 100644 index 0000000..6c9917a --- /dev/null +++ b/specs/traces/out_callDepthBelowLimitSucceedsTest_0.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:16 GMT+0100 (Central European Standard Time)","timestamp":1771969096606},"vars":["last_result"],"states":[{"#meta":{"index":0},"last_result":{"result":{"err_code":{"#bigint":"0"},"ok":true}}},{"#meta":{"index":1},"last_result":{"result":{"err_code":{"#bigint":"0"},"ok":true}}},{"#meta":{"index":2},"last_result":{"result":{"err_code":{"#bigint":"0"},"ok":true}}}]} \ No newline at end of file diff --git a/specs/traces/out_emptyBlockTest_0.itf.json b/specs/traces/out_emptyBlockTest_0.itf.json index d51e1c4..c9030fe 100644 --- a/specs/traces/out_emptyBlockTest_0.itf.json +++ b/specs/traces/out_emptyBlockTest_0.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826677},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084334},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_executionFailureRollbackTest_3.itf.json b/specs/traces/out_executionFailureRollbackTest_3.itf.json index ee6f361..d142d4d 100644 --- a/specs/traces/out_executionFailureRollbackTest_3.itf.json +++ b/specs/traces/out_executionFailureRollbackTest_3.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826682},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084338},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_mixedOutcomesTest_6.itf.json b/specs/traces/out_mixedOutcomesTest_6.itf.json index 13d7994..bd43f2a 100644 --- a/specs/traces/out_mixedOutcomesTest_6.itf.json +++ b/specs/traces/out_mixedOutcomesTest_6.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826686},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084342},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_multiBlockTest_9.itf.json b/specs/traces/out_multiBlockTest_9.itf.json index 6bf7e9b..249a621 100644 --- a/specs/traces/out_multiBlockTest_9.itf.json +++ b/specs/traces/out_multiBlockTest_9.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826696},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}},{"#meta":{"index":4},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084352},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}},{"#meta":{"index":4},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_outOfGasTest_4.itf.json b/specs/traces/out_outOfGasTest_4.itf.json index 3d93a74..f4d4a59 100644 --- a/specs/traces/out_outOfGasTest_4.itf.json +++ b/specs/traces/out_outOfGasTest_4.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826683},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084339},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_overwriteTest_10.itf.json b/specs/traces/out_overwriteTest_10.itf.json index ae9d449..c8c0785 100644 --- a/specs/traces/out_overwriteTest_10.itf.json +++ b/specs/traces/out_overwriteTest_10.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826697},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084354},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_postTxDoesNotMaskExecFailureTest_1.itf.json b/specs/traces/out_postTxDoesNotMaskExecFailureTest_1.itf.json new file mode 100644 index 0000000..d6a83ce --- /dev/null +++ b/specs/traces/out_postTxDoesNotMaskExecFailureTest_1.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:10 GMT+0100 (Central European Standard Time)","timestamp":1771969090685},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_postTxRejectsButKeepsStateTest_0.itf.json b/specs/traces/out_postTxRejectsButKeepsStateTest_0.itf.json new file mode 100644 index 0000000..4a945a0 --- /dev/null +++ b/specs/traces/out_postTxRejectsButKeepsStateTest_0.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:10 GMT+0100 (Central European Standard Time)","timestamp":1771969090683},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"999"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"999"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_successfulTxTest_1.itf.json b/specs/traces/out_successfulTxTest_1.itf.json index 362a896..36e26d5 100644 --- a/specs/traces/out_successfulTxTest_1.itf.json +++ b/specs/traces/out_successfulTxTest_1.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826679},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084336},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_validationFailureTest_2.itf.json b/specs/traces/out_validationFailureTest_2.itf.json index f334fef..1a1d047 100644 --- a/specs/traces/out_validationFailureTest_2.itf.json +++ b/specs/traces/out_validationFailureTest_2.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:17:06 GMT+0100 (Central European Standard Time)","timestamp":1771967826681},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084337},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file From bee32ce1fdaeb0741ea404449369ff2dd364fa05 Mon Sep 17 00:00:00 2001 From: tac0turtle Date: Thu, 26 Feb 2026 15:43:49 +0100 Subject: [PATCH 4/9] redo specs --- crates/app/sdk/extensions/token/src/lib.rs | 1 - .../stf/tests/quint_call_depth_conformance.rs | 97 +++++--- crates/app/stf/tests/quint_conformance.rs | 114 ++++++--- .../stf/tests/quint_post_tx_conformance.rs | 43 +++- specs/stf_call_depth.qnt | 185 ++++++++++++-- specs/stf_core.qnt | 233 ++++++++++++------ specs/stf_post_tx.qnt | 174 ++++++++++++- specs/token.qnt | 26 -- specs/traces/out_blockGasLimitTest_5.itf.json | 2 +- .../out_bootstrapFailureTest_8.itf.json | 2 +- specs/traces/out_bootstrapTest_7.itf.json | 2 +- .../out_callDepthAtLimitFailsTest_1.itf.json | 1 - ...callDepthBelowLimitSucceedsTest_0.itf.json | 1 - .../out_cannotReturnFromEmptyTest_4.itf.json | 1 + specs/traces/out_emptyBlockTest_0.itf.json | 2 +- ...ut_executionFailureRollbackTest_3.itf.json | 2 +- specs/traces/out_fullUnwindTest_3.itf.json | 1 + specs/traces/out_happyPathTest_2.itf.json | 1 + specs/traces/out_mixedOutcomesTest_6.itf.json | 2 +- specs/traces/out_mixedPostTxTest_3.itf.json | 1 + specs/traces/out_multiBlockTest_10.itf.json | 1 + specs/traces/out_multiBlockTest_9.itf.json | 1 - specs/traces/out_nestedCallsTest_1.itf.json | 1 + .../out_outOfGasIgnoresPostTxTest_4.itf.json | 1 + specs/traces/out_outOfGasTest_4.itf.json | 2 +- specs/traces/out_overwriteTest_10.itf.json | 1 - specs/traces/out_overwriteTest_11.itf.json | 1 + ...ostTxDoesNotMaskExecFailureTest_1.itf.json | 2 +- ..._postTxRejectsButKeepsStateTest_0.itf.json | 2 +- .../traces/out_recursiveCallsTest_5.itf.json | 1 + .../out_returnUnwindsStackTest_2.itf.json | 1 + specs/traces/out_singleCallTest_0.itf.json | 1 + specs/traces/out_successfulTxTest_1.itf.json | 2 +- .../out_unregisteredSenderTest_9.itf.json | 1 + .../out_validationFailureTest_2.itf.json | 2 +- 35 files changed, 695 insertions(+), 216 deletions(-) delete mode 100644 specs/token.qnt delete mode 100644 specs/traces/out_callDepthAtLimitFailsTest_1.itf.json delete mode 100644 specs/traces/out_callDepthBelowLimitSucceedsTest_0.itf.json create mode 100644 specs/traces/out_cannotReturnFromEmptyTest_4.itf.json create mode 100644 specs/traces/out_fullUnwindTest_3.itf.json create mode 100644 specs/traces/out_happyPathTest_2.itf.json create mode 100644 specs/traces/out_mixedPostTxTest_3.itf.json create mode 100644 specs/traces/out_multiBlockTest_10.itf.json delete mode 100644 specs/traces/out_multiBlockTest_9.itf.json create mode 100644 specs/traces/out_nestedCallsTest_1.itf.json create mode 100644 specs/traces/out_outOfGasIgnoresPostTxTest_4.itf.json delete mode 100644 specs/traces/out_overwriteTest_10.itf.json create mode 100644 specs/traces/out_overwriteTest_11.itf.json create mode 100644 specs/traces/out_recursiveCallsTest_5.itf.json create mode 100644 specs/traces/out_returnUnwindsStackTest_2.itf.json create mode 100644 specs/traces/out_singleCallTest_0.itf.json create mode 100644 specs/traces/out_unregisteredSenderTest_9.itf.json diff --git a/crates/app/sdk/extensions/token/src/lib.rs b/crates/app/sdk/extensions/token/src/lib.rs index 8d0db63..0b092ae 100644 --- a/crates/app/sdk/extensions/token/src/lib.rs +++ b/crates/app/sdk/extensions/token/src/lib.rs @@ -1,5 +1,4 @@ use evolve_core::account_impl; -pub mod generated; #[account_impl(Token)] pub mod account { diff --git a/crates/app/stf/tests/quint_call_depth_conformance.rs b/crates/app/stf/tests/quint_call_depth_conformance.rs index 6551d67..1baf370 100644 --- a/crates/app/stf/tests/quint_call_depth_conformance.rs +++ b/crates/app/stf/tests/quint_call_depth_conformance.rs @@ -1,7 +1,11 @@ //! Conformance tests: replay Quint ITF traces for stf_call_depth.qnt. //! +//! The Quint spec models nested do_exec calls with a call_stack. This +//! conformance test uses a RecursiveAccount that calls itself N times, +//! verifying that the real STF matches the spec's depth enforcement. +//! //! Run: -//! `quint test specs/stf_call_depth.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json"` +//! `quint test --main=stf_call_depth specs/stf_call_depth.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json"` //! `cargo test -p evolve_stf --test quint_call_depth_conformance` use borsh::{BorshDeserialize, BorshSerialize}; @@ -28,17 +32,15 @@ struct ItfTrace { #[derive(Deserialize)] struct ItfState { - last_result: ItfTxResult, -} - -#[derive(Deserialize)] -struct ItfTxResult { - result: ItfResult, + #[allow(dead_code)] + call_stack: Vec, + last_result: ItfResult, } #[derive(Deserialize)] struct ItfResult { ok: bool, + #[allow(dead_code)] err_code: ItfBigInt, } @@ -163,24 +165,50 @@ impl AccountCode for RecursiveAccount { } } -const SPEC_ERR_CALL_DEPTH: i64 = 0x14; const RECURSIVE_ACCOUNT: u128 = 100; const TEST_SENDER: u128 = 200; +/// Maps Quint test names to the recursive depth the Rust test should exercise. +/// +/// The Quint spec models individual do_exec/return_from_call steps. The Rust +/// conformance test uses a single recursive account that calls itself N times. struct ConformanceCase { test_name: &'static str, requested_depth: u16, + expect_ok: bool, } fn known_test_cases() -> Vec { vec![ + // singleCallTest: one do_exec, stack=[1], OK + ConformanceCase { + test_name: "singleCallTest", + requested_depth: 1, + expect_ok: true, + }, + // nestedCallsTest: 3 nested calls, stack=[1,2,3], OK + ConformanceCase { + test_name: "nestedCallsTest", + requested_depth: 3, + expect_ok: true, + }, + // returnUnwindsStackTest: 2 calls + 1 return, OK (depth 2 succeeds) ConformanceCase { - test_name: "callDepthBelowLimitSucceedsTest", - requested_depth: 63, + test_name: "returnUnwindsStackTest", + requested_depth: 2, + expect_ok: true, }, + // fullUnwindTest: 2 calls + 2 returns, OK (depth 2 succeeds) ConformanceCase { - test_name: "callDepthAtLimitFailsTest", - requested_depth: 64, + test_name: "fullUnwindTest", + requested_depth: 2, + expect_ok: true, + }, + // recursiveCallsTest: 3 recursive self-calls, OK + ConformanceCase { + test_name: "recursiveCallsTest", + requested_depth: 3, + expect_ok: true, }, ] } @@ -190,7 +218,9 @@ fn quint_itf_call_depth_conformance() { let traces_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../specs/traces"); if !traces_dir.exists() { panic!( - "ITF traces not found at {}. Run: quint test specs/stf_call_depth.qnt --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"", + "ITF traces not found at {}. Run: quint test --main=stf_call_depth \ + specs/stf_call_depth.qnt \ + --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"", traces_dir.display() ); } @@ -205,6 +235,12 @@ fn quint_itf_call_depth_conformance() { .expect("trace must have at least one state"); let spec_result = &spec_state.last_result; + assert_eq!( + spec_result.ok, case.expect_ok, + "{}: expected ok={} but trace says ok={}", + case.test_name, case.expect_ok, spec_result.ok + ); + let gas_config = StorageGasConfig { storage_get_charge: 1, storage_set_charge: 1, @@ -245,7 +281,7 @@ fn quint_itf_call_depth_conformance() { txs: vec![tx], }; - let (real_result, _exec_state) = stf.apply_block(&storage, &codes, &block); + let (real_result, _) = stf.apply_block(&storage, &codes, &block); assert_eq!( real_result.tx_results.len(), 1, @@ -253,22 +289,27 @@ fn quint_itf_call_depth_conformance() { case.test_name ); - let spec_ok = spec_result.result.ok; let real_ok = real_result.tx_results[0].response.is_ok(); - assert_eq!(real_ok, spec_ok, "{}: ok mismatch", case.test_name); + assert_eq!( + real_ok, case.expect_ok, + "{}: ok mismatch (real={real_ok}, expected={})", + case.test_name, case.expect_ok + ); - if !spec_ok { - let spec_err = spec_result.result.err_code.as_i64(); - let real_err = real_result.tx_results[0].response.as_ref().unwrap_err().id; - match spec_err { - SPEC_ERR_CALL_DEPTH => assert_eq!( - real_err, - evolve_stf::errors::ERR_CALL_DEPTH_EXCEEDED.id, - "{}: expected call depth error", - case.test_name - ), - _ => panic!("{}: unknown spec error code {spec_err}", case.test_name), - } + if !case.expect_ok { + let real_err = real_result.tx_results[0] + .response + .as_ref() + .unwrap_err() + .id; + assert_eq!( + real_err, + evolve_stf::errors::ERR_CALL_DEPTH_EXCEEDED.id, + "{}: expected call depth error", + case.test_name + ); } + + eprintln!("PASS: {}", case.test_name); } } diff --git a/crates/app/stf/tests/quint_conformance.rs b/crates/app/stf/tests/quint_conformance.rs index aed5650..eed4a24 100644 --- a/crates/app/stf/tests/quint_conformance.rs +++ b/crates/app/stf/tests/quint_conformance.rs @@ -6,7 +6,7 @@ //! the spec's expected outcomes. //! //! Run: `cargo test -p evolve_stf --test quint_conformance` -//! Regenerate traces: `quint test specs/stf_core.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json"` +//! Regenerate traces: `quint test --main=stf specs/stf_core.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json"` use borsh::{BorshDeserialize, BorshSerialize}; use evolve_core::storage_api::{StorageSetRequest, STORAGE_ACCOUNT_ID}; @@ -42,6 +42,8 @@ struct ItfTrace { struct ItfState { block_height: ItfBigInt, last_result: ItfBlockResult, + #[allow(dead_code)] + last_block_tx_count: ItfBigInt, storage: ItfMap, Vec>>, } @@ -77,7 +79,7 @@ struct TestMsg { impl InvokableMessage for TestMsg { const FUNCTION_IDENTIFIER: u64 = 1; - const FUNCTION_IDENTIFIER_NAME: &'static str = "test_exec"; + const FUNCTION_IDENTIFIER_NAME: &'static str = "test_msg"; } #[derive(Clone, Debug)] @@ -150,10 +152,20 @@ impl BlockTrait for TestBlock { #[derive(Default)] struct Validator; impl TxValidator for Validator { - fn validate_tx(&self, tx: &TestTx, _env: &mut dyn Environment) -> SdkResult<()> { + fn validate_tx(&self, tx: &TestTx, env: &mut dyn Environment) -> SdkResult<()> { if tx.fail_validate { return Err(ErrorCode::new(100)); } + // Simulate production signature verification: query the sender's + // account code. If the sender is not registered this fails, mirroring + // the real validator that cannot verify signatures without account code. + let probe = TestMsg { + key: vec![], + value: vec![], + fail_after_write: false, + }; + env.do_query(tx.sender(), &InvokeRequest::new(&probe).unwrap()) + .map_err(|_| ErrorCode::new(100))?; Ok(()) } } @@ -229,6 +241,10 @@ impl AccountCode for TestAccount { const SPEC_ERR_OUT_OF_GAS: i64 = 0x01; const SPEC_ERR_VALIDATION: i64 = 100; const SPEC_ERR_EXECUTION: i64 = 200; +const SPEC_ERR_BOOTSTRAP: i64 = 300; + +const TEST_ACCOUNT: u128 = 100; +const TEST_SENDER: u128 = 200; struct TxCase { sender: u128, @@ -260,10 +276,6 @@ fn make_tx(tc: TxCase) -> TestTx { } } -const SPEC_ERR_BOOTSTRAP: i64 = 300; -const TEST_ACCOUNT: u128 = 100; -const TEST_SENDER: u128 = 200; - struct ConformanceCase { test_name: &'static str, blocks: Vec, @@ -280,6 +292,7 @@ fn known_test_cases() -> Vec { gas_limit: 1_000_000, }], }, + // sender=TEST_ACCOUNT (registered) for all non-bootstrap tests ConformanceCase { test_name: "successfulTxTest", blocks: vec![TestBlock { @@ -287,7 +300,7 @@ fn known_test_cases() -> Vec { time: 0, gas_limit: 1_000_000, txs: vec![make_tx(TxCase { - sender: TEST_SENDER, + sender: TEST_ACCOUNT, recipient: TEST_ACCOUNT, key: vec![1], value: vec![11], @@ -306,7 +319,7 @@ fn known_test_cases() -> Vec { time: 0, gas_limit: 1_000_000, txs: vec![make_tx(TxCase { - sender: TEST_SENDER, + sender: TEST_ACCOUNT, recipient: TEST_ACCOUNT, key: vec![1], value: vec![11], @@ -325,7 +338,7 @@ fn known_test_cases() -> Vec { time: 0, gas_limit: 1_000_000, txs: vec![make_tx(TxCase { - sender: TEST_SENDER, + sender: TEST_ACCOUNT, recipient: TEST_ACCOUNT, key: vec![1], value: vec![11], @@ -344,7 +357,7 @@ fn known_test_cases() -> Vec { time: 0, gas_limit: 1_000_000, txs: vec![make_tx(TxCase { - sender: TEST_SENDER, + sender: TEST_ACCOUNT, recipient: TEST_ACCOUNT, key: vec![1], value: vec![11], @@ -364,7 +377,7 @@ fn known_test_cases() -> Vec { gas_limit: 30, txs: vec![ make_tx(TxCase { - sender: TEST_SENDER, + sender: TEST_ACCOUNT, recipient: TEST_ACCOUNT, key: vec![1], value: vec![11], @@ -375,7 +388,7 @@ fn known_test_cases() -> Vec { fail_bootstrap: false, }), make_tx(TxCase { - sender: TEST_SENDER, + sender: TEST_ACCOUNT, recipient: TEST_ACCOUNT, key: vec![2], value: vec![12], @@ -396,7 +409,7 @@ fn known_test_cases() -> Vec { gas_limit: 1_000_000, txs: vec![ make_tx(TxCase { - sender: TEST_SENDER, + sender: TEST_ACCOUNT, recipient: TEST_ACCOUNT, key: vec![0], value: vec![10], @@ -407,7 +420,7 @@ fn known_test_cases() -> Vec { fail_bootstrap: false, }), make_tx(TxCase { - sender: TEST_SENDER, + sender: TEST_ACCOUNT, recipient: TEST_ACCOUNT, key: vec![1], value: vec![11], @@ -418,7 +431,7 @@ fn known_test_cases() -> Vec { fail_bootstrap: false, }), make_tx(TxCase { - sender: TEST_SENDER, + sender: TEST_ACCOUNT, recipient: TEST_ACCOUNT, key: vec![2], value: vec![12], @@ -429,7 +442,7 @@ fn known_test_cases() -> Vec { fail_bootstrap: false, }), make_tx(TxCase { - sender: TEST_SENDER, + sender: TEST_ACCOUNT, recipient: TEST_ACCOUNT, key: vec![3], value: vec![13], @@ -480,6 +493,27 @@ fn known_test_cases() -> Vec { })], }], }, + // unregisteredSenderTest: sender=200 (not registered), no bootstrap + // Validator queries sender account for sig verification -> fails + ConformanceCase { + test_name: "unregisteredSenderTest", + blocks: vec![TestBlock { + height: 1, + time: 0, + gas_limit: 1_000_000, + txs: vec![make_tx(TxCase { + sender: TEST_SENDER, + recipient: TEST_ACCOUNT, + key: vec![1], + value: vec![11], + gas_limit: 10000, + fail_validate: false, + fail_execute: false, + needs_bootstrap: false, + fail_bootstrap: false, + })], + }], + }, ConformanceCase { test_name: "multiBlockTest", blocks: vec![ @@ -488,7 +522,7 @@ fn known_test_cases() -> Vec { time: 0, gas_limit: 1_000_000, txs: vec![make_tx(TxCase { - sender: TEST_SENDER, + sender: TEST_ACCOUNT, recipient: TEST_ACCOUNT, key: vec![1], value: vec![11], @@ -504,7 +538,7 @@ fn known_test_cases() -> Vec { time: 10, gas_limit: 1_000_000, txs: vec![make_tx(TxCase { - sender: TEST_SENDER, + sender: TEST_ACCOUNT, recipient: TEST_ACCOUNT, key: vec![2], value: vec![12], @@ -525,7 +559,7 @@ fn known_test_cases() -> Vec { gas_limit: 1_000_000, txs: vec![ make_tx(TxCase { - sender: TEST_SENDER, + sender: TEST_ACCOUNT, recipient: TEST_ACCOUNT, key: vec![1], value: vec![20], @@ -536,7 +570,7 @@ fn known_test_cases() -> Vec { fail_bootstrap: false, }), make_tx(TxCase { - sender: TEST_SENDER, + sender: TEST_ACCOUNT, recipient: TEST_ACCOUNT, key: vec![1], value: vec![21], @@ -563,7 +597,7 @@ fn quint_itf_conformance() { if !traces_dir.exists() { panic!( "ITF traces not found at {}. \ - Run: quint test specs/stf_core.qnt \ + Run: quint test --main=stf specs/stf_core.qnt \ --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"", traces_dir.display() ); @@ -573,11 +607,9 @@ fn quint_itf_conformance() { let mut matched = 0; for case in &test_cases { - // Find the unique trace file for this test. let trace_file = find_single_trace_file(&traces_dir, case.test_name); let trace: ItfTrace = read_itf_trace(&trace_file); - // Find the final state for this run after all apply_block steps. let expected_block_height = case.blocks.last().expect("case must have blocks").height as i64; let spec_state = trace @@ -593,7 +625,6 @@ fn quint_itf_conformance() { let spec_result = &spec_state.last_result; - // Set up STF with gas_config matching the spec (all charges = 1). let gas_config = StorageGasConfig { storage_get_charge: 1, storage_set_charge: 1, @@ -611,7 +642,6 @@ fn quint_itf_conformance() { let mut codes = CodeStore::new(); codes.add_code(TestAccount); - // Register account 100 (matches spec's register_account(100)). let test_account = AccountId::new(TEST_ACCOUNT); let code_id = "test_account".to_string(); storage.data.insert( @@ -619,7 +649,6 @@ fn quint_itf_conformance() { Message::new(&code_id).unwrap().into_bytes().unwrap(), ); - // Execute the full block sequence for this case. let mut real_result = None; for block in &case.blocks { let (result, exec_state) = stf.apply_block(&storage, &codes, block); @@ -628,11 +657,11 @@ fn quint_itf_conformance() { .unwrap(); real_result = Some(result); } - let real_result = real_result.expect("case must execute at least one block"); + let real_result = real_result.expect("case must run at least one block"); // --- Assert conformance --- - // 1. tx_results count. + // 1. tx_results count assert_eq!( real_result.tx_results.len(), spec_result.tx_results.len(), @@ -640,7 +669,7 @@ fn quint_itf_conformance() { case.test_name ); - // 2. Per-tx outcomes. + // 2. Per-tx outcomes for (i, (real_tx, spec_tx)) in real_result .tx_results .iter() @@ -687,7 +716,7 @@ fn quint_itf_conformance() { } } - // 3. Gas must match exactly. + // 3. Gas assert_eq!( real_tx.gas_used, spec_tx.gas_used.as_u64(), @@ -696,7 +725,7 @@ fn quint_itf_conformance() { ); } - // 4. Block-level gas. + // 4. Block-level gas assert_eq!( real_result.gas_used, spec_result.gas_used.as_u64(), @@ -704,7 +733,7 @@ fn quint_itf_conformance() { case.test_name ); - // 5. Skipped count. + // 5. Skipped count assert_eq!( real_result.txs_skipped, spec_result.txs_skipped.as_u64() as usize, @@ -712,8 +741,9 @@ fn quint_itf_conformance() { case.test_name ); - // 6. Storage state must match exactly (for modeled accounts). - let modeled_accounts = [AccountId::new(TEST_ACCOUNT), AccountId::new(TEST_SENDER)]; + // 6. Storage + let modeled_accounts = + [AccountId::new(TEST_ACCOUNT), AccountId::new(TEST_SENDER)]; for account_id in modeled_accounts { let mut expected = HashMap::, Vec>::new(); for (account_id_itf, account_store_itf) in &spec_state.storage.entries { @@ -722,7 +752,8 @@ fn quint_itf_conformance() { } for (key_itf, value_itf) in &account_store_itf.entries { let key: Vec = key_itf.iter().map(|b| b.as_u64() as u8).collect(); - let value: Vec = value_itf.iter().map(|b| b.as_u64() as u8).collect(); + let value: Vec = + value_itf.iter().map(|b| b.as_u64() as u8).collect(); expected.insert(key, value); } } @@ -734,12 +765,15 @@ fn quint_itf_conformance() { continue; } if raw_key[..account_prefix.len()] == account_prefix { - actual.insert(raw_key[account_prefix.len()..].to_vec(), raw_value.clone()); + actual.insert( + raw_key[account_prefix.len()..].to_vec(), + raw_value.clone(), + ); } } assert_eq!( actual, expected, - "{}: exact storage mismatch for account {:?}", + "{}: storage mismatch for account {:?}", case.test_name, account_id ); } @@ -750,9 +784,7 @@ fn quint_itf_conformance() { assert!( matched == test_cases.len(), - "Matched {matched}/{} traces. Regenerate with: \ - quint test specs/stf_core.qnt \ - --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"", + "Matched {matched}/{} traces", test_cases.len() ); eprintln!("{matched}/{} conformance tests passed", test_cases.len()); diff --git a/crates/app/stf/tests/quint_post_tx_conformance.rs b/crates/app/stf/tests/quint_post_tx_conformance.rs index 929922e..c64a4cc 100644 --- a/crates/app/stf/tests/quint_post_tx_conformance.rs +++ b/crates/app/stf/tests/quint_post_tx_conformance.rs @@ -191,16 +191,26 @@ struct ConformanceCase { } fn make_tx(fail_execute: bool, reject_post_tx: bool) -> TestTx { + make_tx_with(vec![1], vec![11], 10000, fail_execute, reject_post_tx) +} + +fn make_tx_with( + key: Vec, + value: Vec, + gas_limit: u64, + fail_execute: bool, + reject_post_tx: bool, +) -> TestTx { let msg = TestMsg { - key: vec![1], - value: vec![11], + key, + value, fail_after_write: fail_execute, }; TestTx { sender: AccountId::new(TEST_SENDER), recipient: AccountId::new(TEST_ACCOUNT), request: InvokeRequest::new(&msg).unwrap(), - gas_limit: 10000, + gas_limit, funds: vec![], reject_post_tx, } @@ -224,6 +234,33 @@ fn known_test_cases() -> Vec { txs: vec![make_tx(true, true)], }, }, + ConformanceCase { + test_name: "happyPathTest", + block: TestBlock { + height: 1, + time: 0, + txs: vec![make_tx(false, false)], + }, + }, + ConformanceCase { + test_name: "mixedPostTxTest", + block: TestBlock { + height: 1, + time: 0, + txs: vec![ + make_tx(false, true), + make_tx_with(vec![2], vec![12], 10000, false, false), + ], + }, + }, + ConformanceCase { + test_name: "outOfGasIgnoresPostTxTest", + block: TestBlock { + height: 1, + time: 0, + txs: vec![make_tx_with(vec![1], vec![11], 1, false, true)], + }, + }, ] } diff --git a/specs/stf_call_depth.qnt b/specs/stf_call_depth.qnt index 5ab7abc..90bfc3f 100644 --- a/specs/stf_call_depth.qnt +++ b/specs/stf_call_depth.qnt @@ -1,60 +1,193 @@ // Evolve SDK - STF Call Depth Specification // -// Focus: call depth enforcement for nested do_exec paths. +// Models nested do_exec calls with call depth accumulation. +// In the runtime, each do_exec/branch_exec increments call_depth +// via saturating_add. Calls are rejected when depth >= MAX_CALL_DEPTH. +// +// This spec models: +// - Call stack growth through nested execution +// - Depth enforcement at MAX_CALL_DEPTH (64) +// - Stack unwinding on return +// - Invariant: depth never exceeds the limit module stf_call_depth { + type AccountId = int type Result = { ok: bool, err_code: int } + val OK: Result = { ok: true, err_code: 0 } pure def Err(code: int): Result = { ok: false, err_code: code } val ERR_CALL_DEPTH: int = 0x14 val MAX_CALL_DEPTH: int = 64 - type Tx = { - requested_depth: int, - } + /// The set of account IDs for nondeterministic exploration. + val ACCOUNTS: Set[AccountId] = Set(1, 2, 3) - type TxResult = { - result: Result, - } + // + // === State === + // + + /// Call stack: tracks the chain of nested calls. + /// Length of this list is the current call depth. + var call_stack: List[AccountId] - var last_result: TxResult + /// Result of the last attempted operation. + var last_result: Result - pure def process_tx(tx: Tx): TxResult = - // Runtime rejects when call_depth >= MAX_CALL_DEPTH before the next call. - if (tx.requested_depth >= MAX_CALL_DEPTH) - { result: Err(ERR_CALL_DEPTH) } - else - { result: OK } + // + // === Actions === + // action init = all { - last_result' = { result: OK }, + call_stack' = List(), + last_result' = OK, } - action apply_tx(tx: Tx): bool = all { - last_result' = process_tx(tx), + /// Nested call. In the runtime this is do_exec -> branch_exec + /// which increments call_depth. Succeeds only if depth < MAX. + action do_exec(target: AccountId): bool = all { + // Guard: depth check happens BEFORE checkpoint is taken + call_stack.length() < MAX_CALL_DEPTH, + // Update: push target onto call stack (depth increments) + call_stack' = call_stack.append(target), + last_result' = OK, } + /// Call rejected because depth limit is reached. + action do_exec_rejected(target: AccountId): bool = all { + // Guard: at or above the limit + call_stack.length() >= MAX_CALL_DEPTH, + // No state change: rejected before any checkpoint + call_stack' = call_stack, + last_result' = Err(ERR_CALL_DEPTH), + } + + /// Return from a call (pop the stack). + action return_from_call: bool = all { + // Guard: must have an active call + call_stack.length() > 0, + // Update: pop the last call + call_stack' = call_stack.slice(0, call_stack.length() - 1), + last_result' = OK, + } + + /// Stutter step: no state change. action stutter: bool = all { + call_stack' = call_stack, last_result' = last_result, } - run callDepthBelowLimitSucceedsTest = { + // + // === Nondeterministic step for model checking === + // + + action step = { + nondet target = ACCOUNTS.oneOf() + any { + do_exec(target), + do_exec_rejected(target), + return_from_call, + } + } + + // + // === Invariants === + // + + /// INV-1: Call stack depth never exceeds MAX_CALL_DEPTH. + val depthBounded: bool = call_stack.length() <= MAX_CALL_DEPTH + + /// INV-2: A rejected call always produces ERR_CALL_DEPTH. + val rejectionCorrect: bool = + not(last_result.ok) implies last_result.err_code == ERR_CALL_DEPTH + + /// INV-3: Call depth is non-negative (structural with List). + val depthNonNegative: bool = call_stack.length() >= 0 + + // + // === False-invariant witnesses (should be violated) === + // + + /// Should be violated: proves calls can happen. + val witnessNoCalls: bool = call_stack.length() == 0 + + /// Should be violated: proves nested calls are reachable. + val witnessAlwaysShallow: bool = call_stack.length() < 3 + + /// Should be violated: proves call rejection is reachable. + val witnessNoRejections: bool = last_result.ok + + // + // === Tests === + // + + /// Test: single call succeeds and increments depth. + run singleCallTest = init - .then(apply_tx({ requested_depth: 63 })) + .then(do_exec(1)) .then(all { - assert(last_result.result.ok == true), + assert(call_stack.length() == 1), + assert(last_result.ok == true), + assert(depthBounded), stutter, }) - } - run callDepthAtLimitFailsTest = { + /// Test: nested calls accumulate depth correctly. + run nestedCallsTest = init - .then(apply_tx({ requested_depth: 64 })) + .then(do_exec(1)) + .then(do_exec(2)) + .then(do_exec(3)) .then(all { - assert(last_result.result.ok == false), - assert(last_result.result.err_code == ERR_CALL_DEPTH), + assert(call_stack.length() == 3), + assert(call_stack[0] == 1), + assert(call_stack[1] == 2), + assert(call_stack[2] == 3), + assert(depthBounded), + stutter, + }) + + /// Test: return_from_call decrements depth. + run returnUnwindsStackTest = + init + .then(do_exec(1)) + .then(do_exec(2)) + .then(return_from_call) + .then(all { + assert(call_stack.length() == 1), + assert(call_stack[0] == 1), + stutter, + }) + + /// Test: full unwind returns to empty stack. + run fullUnwindTest = + init + .then(do_exec(1)) + .then(do_exec(2)) + .then(return_from_call) + .then(return_from_call) + .then(all { + assert(call_stack.length() == 0), + stutter, + }) + + /// Test: cannot return from empty stack. + run cannotReturnFromEmptyTest = + init + .then(return_from_call) + .fail() + + /// Test: recursive calls (same account) track depth. + run recursiveCallsTest = + init + .then(do_exec(1)) + .then(do_exec(1)) + .then(do_exec(1)) + .then(all { + assert(call_stack.length() == 3), + assert(call_stack[0] == 1), + assert(call_stack[1] == 1), + assert(call_stack[2] == 1), stutter, }) - } } diff --git a/specs/stf_core.qnt b/specs/stf_core.qnt index 28fa1a9..c0f3c9c 100644 --- a/specs/stf_core.qnt +++ b/specs/stf_core.qnt @@ -33,7 +33,6 @@ module stf { val ERR_EXECUTION: int = 200 val ERR_POST_TX: int = 999 val ERR_BOOTSTRAP: int = 300 - val ERR_ACCOUNT_NOT_FOUND: int = 0x10 /// Storage gas configuration: cost per byte for get/set/remove. type GasConfig = { @@ -94,9 +93,20 @@ module stf { /// The last block result produced. var last_result: BlockResult + /// Ghost: number of txs submitted in the last applied block. + var last_block_tx_count: int + /// Gas configuration (fixed at construction). val gas_config: GasConfig = { get_charge: 1, set_charge: 1, remove_charge: 1 } + // + // === Constants for model checking === + // + + val MC_ACCOUNTS: Set[AccountId] = Set(100, 101, 200, 201) + val MC_GAS_LIMITS: Set[int] = Set(1, 25, 100, 10000) + val MC_BLOCK_GAS_LIMITS: Set[int] = Set(30, 1000, 1000000) + // // === Pure helpers === // @@ -133,10 +143,11 @@ module stf { /// /// Models the full tx lifecycle: /// 1. Optional sender bootstrap (account registration) - /// 2. Validation - /// 3. Execution (write to storage) - /// 4. Post-tx handler - /// 5. Rollback on any failure + /// 2. Sender existence check + /// 3. Validation + /// 4. Recipient existence check + /// 5. Execution (write to storage) + /// 6. Rollback on any failure pure def process_tx( config: GasConfig, tx: Tx, @@ -168,54 +179,49 @@ module stf { val gas_after_bootstrap = bootstrap_result.gas // Phase 1: Validation - if (tx.fail_validate) + // In the real runtime the pluggable TxValidator verifies signatures by + // calling into the sender's account code. If the sender is not + // registered (no account code), validation fails. + if (tx.fail_validate or not(current_accounts.contains(tx.sender))) { result: { result: Err(ERR_VALIDATION), gas_used: gas_after_bootstrap }, store: account_store, accounts: current_accounts, } else - // Phase 2: Check recipient exists - if (not(current_accounts.contains(tx.recipient))) + // Phase 2: Execution - compute gas for the write + val gas_needed = write_gas_cost(config, tx.write_key, tx.write_value) + val total_gas = gas_after_bootstrap + gas_needed + + if (total_gas > tx.gas_limit) + // Out of gas: the real GasCounter does NOT increment gas_used on + // a failed consume_gas call. So gas_used stays at its pre-call value. + { + result: { result: Err(ERR_OUT_OF_GAS), gas_used: gas_after_bootstrap }, + store: account_store, + accounts: current_accounts, + } + else if (tx.fail_execute) + // Execution error after write -> rollback { - result: { result: Err(ERR_ACCOUNT_NOT_FOUND), gas_used: gas_after_bootstrap }, + result: { result: Err(ERR_EXECUTION), gas_used: total_gas }, store: account_store, accounts: current_accounts, } else - // Phase 3: Execution - compute gas for the write - val gas_needed = write_gas_cost(config, tx.write_key, tx.write_value) - val total_gas = gas_after_bootstrap + gas_needed - - if (total_gas > tx.gas_limit) - // Out of gas: the real GasCounter does NOT increment gas_used on - // a failed consume_gas call. So gas_used stays at its pre-call value. - { - result: { result: Err(ERR_OUT_OF_GAS), gas_used: gas_after_bootstrap }, - store: account_store, - accounts: current_accounts, - } - else if (tx.fail_execute) - // Execution error after write -> rollback - { - result: { result: Err(ERR_EXECUTION), gas_used: total_gas }, - store: account_store, - accounts: current_accounts, - } - else - // Phase 4: Write succeeds -> apply to storage - val recipient_store = - if (account_store.keys().contains(tx.recipient)) - account_store.get(tx.recipient) - else - Map() - val updated_recipient = recipient_store.put(tx.write_key, tx.write_value) - val updated_store = account_store.put(tx.recipient, updated_recipient) - { - result: { result: OK, gas_used: total_gas }, - store: updated_store, - accounts: current_accounts, - } + // Phase 3: Write succeeds -> apply to storage + val recipient_store = + if (account_store.keys().contains(tx.recipient)) + account_store.get(tx.recipient) + else + Map() + val updated_recipient = recipient_store.put(tx.write_key, tx.write_value) + val updated_store = account_store.put(tx.recipient, updated_recipient) + { + result: { result: OK, gas_used: total_gas }, + store: updated_store, + accounts: current_accounts, + } /// Process all transactions in a block sequentially, enforcing block gas limit. pure def process_block( @@ -268,10 +274,11 @@ module stf { // action init = all { - storage' = Map(), - accounts' = Set(), - block_height' = 0, - last_result' = { tx_results: List(), gas_used: 0, txs_skipped: 0 }, + storage' = Map(), + accounts' = Set(), + block_height' = 0, + last_result' = { tx_results: List(), gas_used: 0, txs_skipped: 0 }, + last_block_tx_count' = 0, } /// Apply a block to the current state. @@ -281,27 +288,31 @@ module stf { // Block height must be monotonically increasing. block.height > block_height, block.gas_limit > 0, - storage' = out.store, - accounts' = out.accounts, - block_height' = block.height, - last_result' = out.result, + storage' = out.store, + accounts' = out.accounts, + block_height' = block.height, + last_result' = out.result, + last_block_tx_count' = block.txs.length(), } /// Register an account (e.g., during genesis). action register_account(id: AccountId): bool = all { not(accounts.contains(id)), - storage' = storage.put(id, Map()), - accounts' = accounts.union(Set(id)), - block_height' = block_height, - last_result' = last_result, + storage' = storage.put(id, Map()), + accounts' = accounts.union(Set(id)), + block_height' = block_height, + last_result' = last_result, + last_block_tx_count' = last_block_tx_count, } // // === Invariants === // - /// INV-1: Block height never decreases. - val height_monotonic: bool = block_height >= 0 + /// INV-1: Block height is non-negative. + /// Monotonicity is enforced structurally by the guard block.height > block_height + /// in apply_block. This invariant checks the weaker non-negativity property. + val height_non_negative: bool = block_height >= 0 /// INV-2: Total gas reported equals sum of individual tx gas. val gas_accounting: bool = @@ -309,10 +320,8 @@ module stf { last_result.gas_used == sum /// INV-3: Number of results + skipped = number of txs submitted. - /// (Checked after each apply_block; this is a post-condition on the result.) val result_completeness: bool = - // This holds trivially for the initial state where there are no txs. - true + last_result.tx_results.length() + last_result.txs_skipped == last_block_tx_count /// INV-4: Failed transactions do not modify storage. /// (Encoded structurally: process_tx returns the original store on failure.) @@ -325,6 +334,23 @@ module stf { val gas_non_negative: bool = last_result.tx_results.foldl(true, (acc, tr) => acc and tr.gas_used >= 0) + // + // === False-invariant witnesses (should be violated) === + // + + /// Should be violated: proves blocks with txs are processed. + val witnessNoTxsProcessed: bool = last_result.tx_results.length() == 0 + + /// Should be violated: proves successful txs can write to storage. + val witnessStorageNeverWritten: bool = + storage.keys().forall(id => storage.get(id) == Map()) + + /// Should be violated: proves block gas limit causes skips. + val witnessNoSkippedTxs: bool = last_result.txs_skipped == 0 + + /// Should be violated: proves bootstrap can register new accounts. + val witnessNoBootstrap: bool = accounts.size() <= 1 + // // === Temporal Properties === // @@ -338,6 +364,35 @@ module stf { /// PROP-4: Successful transactions always produce storage changes. /// These are verified by the process_tx function structure. + // + // === Nondeterministic step for model checking === + // + + action step = { + nondet id = MC_ACCOUNTS.oneOf() + nondet sender = MC_ACCOUNTS.oneOf() + nondet recipient = MC_ACCOUNTS.oneOf() + nondet gas_limit = MC_GAS_LIMITS.oneOf() + nondet block_gas = MC_BLOCK_GAS_LIMITS.oneOf() + nondet fail_v = Set(true, false).oneOf() + nondet fail_e = Set(true, false).oneOf() + nondet needs_b = Set(true, false).oneOf() + nondet fail_b = Set(true, false).oneOf() + val tx: Tx = { + sender: sender, recipient: recipient, gas_limit: gas_limit, + write_key: K1, write_value: V1, + fail_validate: fail_v, fail_execute: fail_e, + needs_bootstrap: needs_b, fail_bootstrap: fail_b, + } + any { + apply_block({ + height: block_height + 1, time: 0, + txs: [tx], gas_limit: block_gas, + }), + register_account(id), + } + } + // // === Test helpers === // @@ -360,6 +415,7 @@ module stf { accounts' = accounts, block_height' = block_height, last_result' = last_result, + last_block_tx_count' = last_block_tx_count, } // @@ -375,6 +431,7 @@ module stf { assert(last_result.tx_results.length() == 0), assert(last_result.gas_used == 0), assert(last_result.txs_skipped == 0), + assert(result_completeness), stutter, }) } @@ -382,7 +439,7 @@ module stf { /// Test: successful transaction writes to storage. run successfulTxTest = { val tx1: Tx = { - sender: 200, recipient: 100, gas_limit: 10000, + sender: 100, recipient: 100, gas_limit: 10000, write_key: K1, write_value: V1, fail_validate: false, fail_execute: false, needs_bootstrap: false, fail_bootstrap: false, @@ -394,6 +451,7 @@ module stf { assert(last_result.tx_results.length() == 1), assert(last_result.tx_results[0].result.ok == true), assert(storage.get(100).get(K1) == V1), + assert(result_completeness), stutter, }) } @@ -401,7 +459,7 @@ module stf { /// Test: validation failure does not modify storage. run validationFailureTest = { val tx1: Tx = { - sender: 200, recipient: 100, gas_limit: 10000, + sender: 100, recipient: 100, gas_limit: 10000, write_key: K1, write_value: V1, fail_validate: true, fail_execute: false, needs_bootstrap: false, fail_bootstrap: false, @@ -413,6 +471,7 @@ module stf { assert(last_result.tx_results[0].result.ok == false), assert(last_result.tx_results[0].result.err_code == ERR_VALIDATION), assert(storage.get(100) == Map()), + assert(result_completeness), stutter, }) } @@ -420,7 +479,7 @@ module stf { /// Test: execution failure rolls back storage write. run executionFailureRollbackTest = { val tx1: Tx = { - sender: 200, recipient: 100, gas_limit: 10000, + sender: 100, recipient: 100, gas_limit: 10000, write_key: K1, write_value: V1, fail_validate: false, fail_execute: true, needs_bootstrap: false, fail_bootstrap: false, @@ -432,6 +491,7 @@ module stf { assert(last_result.tx_results[0].result.ok == false), assert(last_result.tx_results[0].result.err_code == ERR_EXECUTION), assert(storage.get(100) == Map()), + assert(result_completeness), stutter, }) } @@ -439,7 +499,7 @@ module stf { /// Test: out-of-gas transaction fails and does not write. run outOfGasTest = { val tx1: Tx = { - sender: 200, recipient: 100, gas_limit: 1, + sender: 100, recipient: 100, gas_limit: 1, write_key: K1, write_value: V1, fail_validate: false, fail_execute: false, needs_bootstrap: false, fail_bootstrap: false, @@ -451,6 +511,7 @@ module stf { assert(last_result.tx_results[0].result.ok == false), assert(last_result.tx_results[0].result.err_code == ERR_OUT_OF_GAS), assert(storage.get(100) == Map()), + assert(result_completeness), stutter, }) } @@ -462,13 +523,13 @@ module stf { /// Pre-check: cumulative(20) + tx2.gas_limit(25) = 45 > 30 -> skip tx2. run blockGasLimitTest = { val tx1: Tx = { - sender: 200, recipient: 100, gas_limit: 25, + sender: 100, recipient: 100, gas_limit: 25, write_key: K1, write_value: V1, fail_validate: false, fail_execute: false, needs_bootstrap: false, fail_bootstrap: false, } val tx2: Tx = { - sender: 200, recipient: 100, gas_limit: 25, + sender: 100, recipient: 100, gas_limit: 25, write_key: K2, write_value: V2, fail_validate: false, fail_execute: false, needs_bootstrap: false, fail_bootstrap: false, @@ -480,6 +541,7 @@ module stf { assert(last_result.tx_results.length() == 1), assert(last_result.txs_skipped == 1), assert(last_result.tx_results[0].result.ok == true), + assert(result_completeness), stutter, }) } @@ -487,25 +549,25 @@ module stf { /// Test: mixed block with success, validation failure, execution failure, OOG. run mixedOutcomesTest = { val tx_ok: Tx = { - sender: 200, recipient: 100, gas_limit: 10000, + sender: 100, recipient: 100, gas_limit: 10000, write_key: K0, write_value: V0, fail_validate: false, fail_execute: false, needs_bootstrap: false, fail_bootstrap: false, } val tx_validate_fail: Tx = { - sender: 200, recipient: 100, gas_limit: 10000, + sender: 100, recipient: 100, gas_limit: 10000, write_key: K1, write_value: V1, fail_validate: true, fail_execute: false, needs_bootstrap: false, fail_bootstrap: false, } val tx_exec_fail: Tx = { - sender: 200, recipient: 100, gas_limit: 10000, + sender: 100, recipient: 100, gas_limit: 10000, write_key: K2, write_value: V2, fail_validate: false, fail_execute: true, needs_bootstrap: false, fail_bootstrap: false, } val tx_oog: Tx = { - sender: 200, recipient: 100, gas_limit: 1, + sender: 100, recipient: 100, gas_limit: 1, write_key: K3, write_value: V3, fail_validate: false, fail_execute: false, needs_bootstrap: false, fail_bootstrap: false, @@ -532,6 +594,7 @@ module stf { assert(not(storage.get(100).keys().contains(K1))), assert(not(storage.get(100).keys().contains(K2))), assert(not(storage.get(100).keys().contains(K3))), + assert(result_completeness), stutter, }) } @@ -551,6 +614,7 @@ module stf { assert(last_result.tx_results[0].result.ok == true), // Sender should now be registered assert(accounts.contains(200)), + assert(result_completeness), stutter, }) } @@ -571,6 +635,29 @@ module stf { assert(last_result.tx_results[0].result.err_code == ERR_BOOTSTRAP), // Sender should NOT be registered assert(not(accounts.contains(200))), + assert(result_completeness), + stutter, + }) + } + + /// Test: unregistered sender (no bootstrap) fails validation. + /// In production, the validator queries the sender's account code for + /// signature verification. Without account code the query fails. + run unregisteredSenderTest = { + val tx1: Tx = { + sender: 200, recipient: 100, gas_limit: 10000, + write_key: K1, write_value: V1, + fail_validate: false, fail_execute: false, + needs_bootstrap: false, fail_bootstrap: false, + } + init + .then(register_account(100)) + .then(apply_block({ height: 1, time: 0, txs: [tx1], gas_limit: 1000000 })) + .then(all { + assert(last_result.tx_results[0].result.ok == false), + assert(last_result.tx_results[0].result.err_code == ERR_VALIDATION), + assert(storage.get(100) == Map()), + assert(result_completeness), stutter, }) } @@ -578,13 +665,13 @@ module stf { /// Test: sequential blocks accumulate state correctly. run multiBlockTest = { val tx1: Tx = { - sender: 200, recipient: 100, gas_limit: 10000, + sender: 100, recipient: 100, gas_limit: 10000, write_key: K1, write_value: V1, fail_validate: false, fail_execute: false, needs_bootstrap: false, fail_bootstrap: false, } val tx2: Tx = { - sender: 200, recipient: 100, gas_limit: 10000, + sender: 100, recipient: 100, gas_limit: 10000, write_key: K2, write_value: V2, fail_validate: false, fail_execute: false, needs_bootstrap: false, fail_bootstrap: false, @@ -605,13 +692,13 @@ module stf { /// Test: overwriting a key updates the value. run overwriteTest = { val tx1: Tx = { - sender: 200, recipient: 100, gas_limit: 10000, + sender: 100, recipient: 100, gas_limit: 10000, write_key: K1, write_value: V_OLD, fail_validate: false, fail_execute: false, needs_bootstrap: false, fail_bootstrap: false, } val tx2: Tx = { - sender: 200, recipient: 100, gas_limit: 10000, + sender: 100, recipient: 100, gas_limit: 10000, write_key: K1, write_value: V_NEW, fail_validate: false, fail_execute: false, needs_bootstrap: false, fail_bootstrap: false, diff --git a/specs/stf_post_tx.qnt b/specs/stf_post_tx.qnt index 5a03443..37e255e 100644 --- a/specs/stf_post_tx.qnt +++ b/specs/stf_post_tx.qnt @@ -1,8 +1,14 @@ // Evolve SDK - STF Post-Tx Hook Specification // // Focus: post-tx handler semantics. -// In the runtime, a post-tx error can override a successful tx response, -// but state changes from execution remain committed. +// In the runtime, the post-tx handler runs AFTER the execution checkpoint +// is dropped. A post-tx error overrides the tx response, but state changes +// from execution remain committed. +// +// This spec verifies: +// - Post-tx rejection keeps execution state changes +// - Post-tx rejection does not mask execution failures +// - Gas accounting remains correct across post-tx outcomes module stf_post_tx { type AccountId = int @@ -40,10 +46,17 @@ module stf_post_tx { gas_used: int, } + // + // === State === + // + var storage: AccountId -> (Key -> Value) var accounts: Set[AccountId] var last_result: BlockResult + /// Constants for model checking. + val MC_ACCOUNTS: Set[AccountId] = Set(100, 101) + val gas_config: GasConfig = { set_charge: 1 } val ACCOUNT_ID_BYTE_SIZE: int = 16 @@ -63,6 +76,7 @@ module stf_post_tx { } else if (tx.fail_execute) // Runtime do_exec rolls back storage on execution error. + // Post-tx does not run after execution failure. { result: { result: Err(ERR_EXECUTION), gas_used: gas_needed }, store: account_store, @@ -76,7 +90,8 @@ module stf_post_tx { Map() val updated_recipient = recipient_store.put(tx.write_key, tx.write_value) val updated_store = account_store.put(tx.recipient, updated_recipient) - // Post-tx can override response while keeping state. + // Post-tx handler runs AFTER checkpoint is dropped. + // It can override the response but NOT roll back state. { result: { result: if (tx.reject_post_tx) Err(ERR_POST_TX) else OK, @@ -108,6 +123,10 @@ module stf_post_tx { store: final_state.store, } + // + // === Actions === + // + action init = all { storage' = Map(), accounts' = Set(), @@ -135,9 +154,76 @@ module stf_post_tx { last_result' = last_result, } + // + // === Nondeterministic step for model checking === + // + + action step = { + nondet recipient = MC_ACCOUNTS.oneOf() + nondet gas_limit = Set(1, 100, 10000).oneOf() + nondet fail_e = Set(true, false).oneOf() + nondet reject_pt = Set(true, false).oneOf() + val tx: Tx = { + recipient: recipient, + gas_limit: gas_limit, + write_key: K1, + write_value: V1, + fail_execute: fail_e, + reject_post_tx: reject_pt, + } + any { + apply_txs([tx]), + register_account(recipient), + } + } + + // + // === Invariants === + // + + /// INV-1: Gas accounting -- total equals sum of individual tx gas. + val gas_accounting: bool = + last_result.tx_results.foldl(0, (acc, tr) => acc + tr.gas_used) == last_result.gas_used + + /// INV-2: Gas is non-negative. + val gas_non_negative: bool = + last_result.tx_results.foldl(true, (acc, tr) => acc and tr.gas_used >= 0) + + /// INV-3: Only registered accounts have storage entries. + val storage_accounts_registered: bool = + storage.keys().forall(id => accounts.contains(id)) + + // + // === False-invariant witnesses (should be violated) === + // + + /// Should be violated: proves post-tx rejection path is reachable. + val witnessNoPostTxReject: bool = + last_result.tx_results.foldl(true, (acc, tr) => + acc and (tr.result.ok or tr.result.err_code != ERR_POST_TX)) + + /// Should be violated: proves execution happens. + val witnessNoExecution: bool = last_result.tx_results.length() == 0 + + /// Should be violated: proves successful txs write storage. + val witnessStorageEmpty: bool = + storage.keys().forall(id => storage.get(id) == Map()) + + // + // === Test helpers === + // + val K1: Key = [1] val V1: Value = [11] + val K2: Key = [2] + val V2: Value = [12] + + // + // === Tests === + // + /// Core property: post-tx rejection keeps execution state changes. + /// This is the critical semantic: the handler runs after checkpoint drop. run postTxRejectsButKeepsStateTest = { val tx: Tx = { recipient: 100, @@ -154,11 +240,14 @@ module stf_post_tx { assert(last_result.tx_results.length() == 1), assert(last_result.tx_results[0].result.ok == false), assert(last_result.tx_results[0].result.err_code == ERR_POST_TX), + // State change persists despite post-tx error assert(storage.get(100).get(K1) == V1), + assert(gas_accounting), stutter, }) } + /// Post-tx does not mask execution failure (exec error takes precedence). run postTxDoesNotMaskExecFailureTest = { val tx: Tx = { recipient: 100, @@ -174,8 +263,87 @@ module stf_post_tx { .then(all { assert(last_result.tx_results.length() == 1), assert(last_result.tx_results[0].result.ok == false), + // Execution error, NOT post-tx error assert(last_result.tx_results[0].result.err_code == ERR_EXECUTION), + // No state changes (execution was rolled back) + assert(storage.get(100) == Map()), + assert(gas_accounting), + stutter, + }) + } + + /// Happy path: no post-tx rejection, execution succeeds. + run happyPathTest = { + val tx: Tx = { + recipient: 100, + gas_limit: 10000, + write_key: K1, + write_value: V1, + fail_execute: false, + reject_post_tx: false, + } + init + .then(register_account(100)) + .then(apply_txs([tx])) + .then(all { + assert(last_result.tx_results[0].result.ok == true), + assert(storage.get(100).get(K1) == V1), + assert(gas_accounting), + stutter, + }) + } + + /// Mixed block: one tx with post-tx rejection, one without. + run mixedPostTxTest = { + val tx_reject: Tx = { + recipient: 100, + gas_limit: 10000, + write_key: K1, + write_value: V1, + fail_execute: false, + reject_post_tx: true, + } + val tx_ok: Tx = { + recipient: 100, + gas_limit: 10000, + write_key: K2, + write_value: V2, + fail_execute: false, + reject_post_tx: false, + } + init + .then(register_account(100)) + .then(apply_txs([tx_reject, tx_ok])) + .then(all { + assert(last_result.tx_results.length() == 2), + // First tx: post-tx rejected, but state persists + assert(last_result.tx_results[0].result.err_code == ERR_POST_TX), + assert(storage.get(100).get(K1) == V1), + // Second tx: fully succeeds + assert(last_result.tx_results[1].result.ok == true), + assert(storage.get(100).get(K2) == V2), + assert(gas_accounting), + stutter, + }) + } + + /// Out-of-gas: post-tx flag is irrelevant since execution never completes. + run outOfGasIgnoresPostTxTest = { + val tx: Tx = { + recipient: 100, + gas_limit: 1, + write_key: K1, + write_value: V1, + fail_execute: false, + reject_post_tx: true, + } + init + .then(register_account(100)) + .then(apply_txs([tx])) + .then(all { + assert(last_result.tx_results[0].result.err_code == ERR_OUT_OF_GAS), assert(storage.get(100) == Map()), + assert(gas_accounting), stutter, }) } diff --git a/specs/token.qnt b/specs/token.qnt deleted file mode 100644 index 8a31041..0000000 --- a/specs/token.qnt +++ /dev/null @@ -1,26 +0,0 @@ -module TokenV1 - -// Canonical specgen directives for token_v1 code generation. -// @specgen module_kind=token_v1 -// @specgen rust_mod=generated_token -// @specgen account_type=TokenGenerated -// @specgen error.not_enough_balance.code=0x1 -// @specgen error.not_enough_balance.message=not enough balance -// @specgen error.underflow.code=0x2 -// @specgen error.underflow.message=arithmetic underflow -// @specgen slot.metadata=0 -// @specgen slot.balances=1 -// @specgen slot.total_supply=2 -// @specgen slot.supply_manager=3 - -// Quint subset declarations required by specgen. -action initialize(metadata, balances, supply_manager) = true -action mint(recipient, amount) = true -action burn(from_account, amount) = true -action transfer(to, amount) = true -action freeze(account) = true - -def metadata(accountState) = accountState.metadata -def get_balance(account, accountState) = accountState.balances.get(account) -def total_supply(accountState) = accountState.total_supply -def is_frozen(account, accountState) = false diff --git a/specs/traces/out_blockGasLimitTest_5.itf.json b/specs/traces/out_blockGasLimitTest_5.itf.json index bbe2bc0..2873af0 100644 --- a/specs/traces/out_blockGasLimitTest_5.itf.json +++ b/specs/traces/out_blockGasLimitTest_5.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084340},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557717},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_bootstrapFailureTest_8.itf.json b/specs/traces/out_bootstrapFailureTest_8.itf.json index 14b54a8..cdcb19c 100644 --- a/specs/traces/out_bootstrapFailureTest_8.itf.json +++ b/specs/traces/out_bootstrapFailureTest_8.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084351},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"35"},"tx_results":[{"gas_used":{"#bigint":"35"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"35"},"tx_results":[{"gas_used":{"#bigint":"35"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557728},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"35"},"tx_results":[{"gas_used":{"#bigint":"35"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"35"},"tx_results":[{"gas_used":{"#bigint":"35"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_bootstrapTest_7.itf.json b/specs/traces/out_bootstrapTest_7.itf.json index 08edecc..7de9fe3 100644 --- a/specs/traces/out_bootstrapTest_7.itf.json +++ b/specs/traces/out_bootstrapTest_7.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084350},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"55"},"tx_results":[{"gas_used":{"#bigint":"55"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"55"},"tx_results":[{"gas_used":{"#bigint":"55"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557727},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"55"},"tx_results":[{"gas_used":{"#bigint":"55"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"55"},"tx_results":[{"gas_used":{"#bigint":"55"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_callDepthAtLimitFailsTest_1.itf.json b/specs/traces/out_callDepthAtLimitFailsTest_1.itf.json deleted file mode 100644 index 5bd1f44..0000000 --- a/specs/traces/out_callDepthAtLimitFailsTest_1.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:16 GMT+0100 (Central European Standard Time)","timestamp":1771969096607},"vars":["last_result"],"states":[{"#meta":{"index":0},"last_result":{"result":{"err_code":{"#bigint":"0"},"ok":true}}},{"#meta":{"index":1},"last_result":{"result":{"err_code":{"#bigint":"20"},"ok":false}}},{"#meta":{"index":2},"last_result":{"result":{"err_code":{"#bigint":"20"},"ok":false}}}]} \ No newline at end of file diff --git a/specs/traces/out_callDepthBelowLimitSucceedsTest_0.itf.json b/specs/traces/out_callDepthBelowLimitSucceedsTest_0.itf.json deleted file mode 100644 index 6c9917a..0000000 --- a/specs/traces/out_callDepthBelowLimitSucceedsTest_0.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:16 GMT+0100 (Central European Standard Time)","timestamp":1771969096606},"vars":["last_result"],"states":[{"#meta":{"index":0},"last_result":{"result":{"err_code":{"#bigint":"0"},"ok":true}}},{"#meta":{"index":1},"last_result":{"result":{"err_code":{"#bigint":"0"},"ok":true}}},{"#meta":{"index":2},"last_result":{"result":{"err_code":{"#bigint":"0"},"ok":true}}}]} \ No newline at end of file diff --git a/specs/traces/out_cannotReturnFromEmptyTest_4.itf.json b/specs/traces/out_cannotReturnFromEmptyTest_4.itf.json new file mode 100644 index 0000000..226104b --- /dev/null +++ b/specs/traces/out_cannotReturnFromEmptyTest_4.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:42 GMT+0100 (Central European Standard Time)","timestamp":1772033562682},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1}}]} \ No newline at end of file diff --git a/specs/traces/out_emptyBlockTest_0.itf.json b/specs/traces/out_emptyBlockTest_0.itf.json index c9030fe..a3a98e5 100644 --- a/specs/traces/out_emptyBlockTest_0.itf.json +++ b/specs/traces/out_emptyBlockTest_0.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084334},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557711},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_executionFailureRollbackTest_3.itf.json b/specs/traces/out_executionFailureRollbackTest_3.itf.json index d142d4d..bac590b 100644 --- a/specs/traces/out_executionFailureRollbackTest_3.itf.json +++ b/specs/traces/out_executionFailureRollbackTest_3.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084338},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557715},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_fullUnwindTest_3.itf.json b/specs/traces/out_fullUnwindTest_3.itf.json new file mode 100644 index 0000000..d0755e7 --- /dev/null +++ b/specs/traces/out_fullUnwindTest_3.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:42 GMT+0100 (Central European Standard Time)","timestamp":1772033562681},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"},{"#bigint":"2"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":3},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":4},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":5},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file diff --git a/specs/traces/out_happyPathTest_2.itf.json b/specs/traces/out_happyPathTest_2.itf.json new file mode 100644 index 0000000..59be5f9 --- /dev/null +++ b/specs/traces/out_happyPathTest_2.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:43 GMT+0100 (Central European Standard Time)","timestamp":1772033563574},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_mixedOutcomesTest_6.itf.json b/specs/traces/out_mixedOutcomesTest_6.itf.json index bd43f2a..08a7267 100644 --- a/specs/traces/out_mixedOutcomesTest_6.itf.json +++ b/specs/traces/out_mixedOutcomesTest_6.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084342},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557719},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"4"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"4"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_mixedPostTxTest_3.itf.json b/specs/traces/out_mixedPostTxTest_3.itf.json new file mode 100644 index 0000000..d368482 --- /dev/null +++ b/specs/traces/out_mixedPostTxTest_3.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:43 GMT+0100 (Central European Standard Time)","timestamp":1772033563582},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"999"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"999"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_multiBlockTest_10.itf.json b/specs/traces/out_multiBlockTest_10.itf.json new file mode 100644 index 0000000..69724d0 --- /dev/null +++ b/specs/traces/out_multiBlockTest_10.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557729},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}},{"#meta":{"index":4},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_multiBlockTest_9.itf.json b/specs/traces/out_multiBlockTest_9.itf.json deleted file mode 100644 index 249a621..0000000 --- a/specs/traces/out_multiBlockTest_9.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084352},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}},{"#meta":{"index":4},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_nestedCallsTest_1.itf.json b/specs/traces/out_nestedCallsTest_1.itf.json new file mode 100644 index 0000000..2a20845 --- /dev/null +++ b/specs/traces/out_nestedCallsTest_1.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:42 GMT+0100 (Central European Standard Time)","timestamp":1772033562680},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"},{"#bigint":"2"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":3},"call_stack":[{"#bigint":"1"},{"#bigint":"2"},{"#bigint":"3"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":4},"call_stack":[{"#bigint":"1"},{"#bigint":"2"},{"#bigint":"3"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file diff --git a/specs/traces/out_outOfGasIgnoresPostTxTest_4.itf.json b/specs/traces/out_outOfGasIgnoresPostTxTest_4.itf.json new file mode 100644 index 0000000..b9c41f2 --- /dev/null +++ b/specs/traces/out_outOfGasIgnoresPostTxTest_4.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:43 GMT+0100 (Central European Standard Time)","timestamp":1772033563583},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_outOfGasTest_4.itf.json b/specs/traces/out_outOfGasTest_4.itf.json index f4d4a59..9a1a57f 100644 --- a/specs/traces/out_outOfGasTest_4.itf.json +++ b/specs/traces/out_outOfGasTest_4.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084339},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557716},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_overwriteTest_10.itf.json b/specs/traces/out_overwriteTest_10.itf.json deleted file mode 100644 index c8c0785..0000000 --- a/specs/traces/out_overwriteTest_10.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084354},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_overwriteTest_11.itf.json b/specs/traces/out_overwriteTest_11.itf.json new file mode 100644 index 0000000..2495fb3 --- /dev/null +++ b/specs/traces/out_overwriteTest_11.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557730},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_postTxDoesNotMaskExecFailureTest_1.itf.json b/specs/traces/out_postTxDoesNotMaskExecFailureTest_1.itf.json index d6a83ce..8d3f342 100644 --- a/specs/traces/out_postTxDoesNotMaskExecFailureTest_1.itf.json +++ b/specs/traces/out_postTxDoesNotMaskExecFailureTest_1.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:10 GMT+0100 (Central European Standard Time)","timestamp":1771969090685},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:43 GMT+0100 (Central European Standard Time)","timestamp":1772033563573},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_postTxRejectsButKeepsStateTest_0.itf.json b/specs/traces/out_postTxRejectsButKeepsStateTest_0.itf.json index 4a945a0..cc5a5b1 100644 --- a/specs/traces/out_postTxRejectsButKeepsStateTest_0.itf.json +++ b/specs/traces/out_postTxRejectsButKeepsStateTest_0.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:10 GMT+0100 (Central European Standard Time)","timestamp":1771969090683},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"999"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"999"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:43 GMT+0100 (Central European Standard Time)","timestamp":1772033563572},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"999"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"999"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_recursiveCallsTest_5.itf.json b/specs/traces/out_recursiveCallsTest_5.itf.json new file mode 100644 index 0000000..e4d6e04 --- /dev/null +++ b/specs/traces/out_recursiveCallsTest_5.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:42 GMT+0100 (Central European Standard Time)","timestamp":1772033562682},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"},{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":3},"call_stack":[{"#bigint":"1"},{"#bigint":"1"},{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":4},"call_stack":[{"#bigint":"1"},{"#bigint":"1"},{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file diff --git a/specs/traces/out_returnUnwindsStackTest_2.itf.json b/specs/traces/out_returnUnwindsStackTest_2.itf.json new file mode 100644 index 0000000..7ab15ea --- /dev/null +++ b/specs/traces/out_returnUnwindsStackTest_2.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:42 GMT+0100 (Central European Standard Time)","timestamp":1772033562680},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"},{"#bigint":"2"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":3},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":4},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file diff --git a/specs/traces/out_singleCallTest_0.itf.json b/specs/traces/out_singleCallTest_0.itf.json new file mode 100644 index 0000000..b0c7e85 --- /dev/null +++ b/specs/traces/out_singleCallTest_0.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:42 GMT+0100 (Central European Standard Time)","timestamp":1772033562678},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file diff --git a/specs/traces/out_successfulTxTest_1.itf.json b/specs/traces/out_successfulTxTest_1.itf.json index 36e26d5..d4bc6e3 100644 --- a/specs/traces/out_successfulTxTest_1.itf.json +++ b/specs/traces/out_successfulTxTest_1.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084336},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557713},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_unregisteredSenderTest_9.itf.json b/specs/traces/out_unregisteredSenderTest_9.itf.json new file mode 100644 index 0000000..c08a5d6 --- /dev/null +++ b/specs/traces/out_unregisteredSenderTest_9.itf.json @@ -0,0 +1 @@ +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557728},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_validationFailureTest_2.itf.json b/specs/traces/out_validationFailureTest_2.itf.json index 1a1d047..ba3ac70 100644 --- a/specs/traces/out_validationFailureTest_2.itf.json +++ b/specs/traces/out_validationFailureTest_2.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Tue Feb 24 2026 22:38:04 GMT+0100 (Central European Standard Time)","timestamp":1771969084337},"vars":["storage","accounts","block_height","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557714},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file From ab8bd1d49e57688bcaab7ca5a34c7e4b0708ea05 Mon Sep 17 00:00:00 2001 From: tac0turtle Date: Fri, 6 Mar 2026 08:28:18 +0100 Subject: [PATCH 5/9] simplify --- .../stf/tests/quint_call_depth_conformance.rs | 74 +------ crates/app/stf/tests/quint_common.rs | 190 ++++++++++++++++-- crates/app/stf/tests/quint_conformance.rs | 122 ++--------- .../stf/tests/quint_post_tx_conformance.rs | 187 +++++++---------- 4 files changed, 285 insertions(+), 288 deletions(-) diff --git a/crates/app/stf/tests/quint_call_depth_conformance.rs b/crates/app/stf/tests/quint_call_depth_conformance.rs index 1baf370..b5cf955 100644 --- a/crates/app/stf/tests/quint_call_depth_conformance.rs +++ b/crates/app/stf/tests/quint_call_depth_conformance.rs @@ -11,18 +11,17 @@ use borsh::{BorshDeserialize, BorshSerialize}; use evolve_core::{ AccountCode, AccountId, BlockContext, Environment, EnvironmentQuery, FungibleAsset, - InvokableMessage, InvokeRequest, InvokeResponse, Message, SdkResult, + InvokableMessage, InvokeRequest, InvokeResponse, SdkResult, }; -use evolve_stf::gas::StorageGasConfig; use evolve_stf::Stf; -use evolve_stf_traits::{Block as BlockTrait, PostTxExecution, Transaction, TxValidator}; +use evolve_stf_traits::{Block as BlockTrait, Transaction}; use serde::Deserialize; use std::path::Path; mod quint_common; use quint_common::{ - account_code_key, find_single_trace_file, read_itf_trace, CodeStore, InMemoryStorage, - ItfBigInt, NoopBegin, NoopEnd, + find_single_trace_file, read_itf_trace, register_account, CodeStore, InMemoryStorage, + ItfBigInt, NoopBegin, NoopEnd, NoopPostTx, NoopValidator, }; #[derive(Deserialize)] @@ -103,27 +102,6 @@ impl BlockTrait for TestBlock { } } -#[derive(Default)] -struct NoopValidator; -impl TxValidator for NoopValidator { - fn validate_tx(&self, _tx: &TestTx, _env: &mut dyn Environment) -> SdkResult<()> { - Ok(()) - } -} - -#[derive(Default)] -struct NoopPostTx; -impl PostTxExecution for NoopPostTx { - fn after_tx_executed( - _tx: &TestTx, - _gas_consumed: u64, - _tx_result: &SdkResult, - _env: &mut dyn Environment, - ) -> SdkResult<()> { - Ok(()) - } -} - #[derive(Default)] struct RecursiveAccount; @@ -168,10 +146,6 @@ impl AccountCode for RecursiveAccount { const RECURSIVE_ACCOUNT: u128 = 100; const TEST_SENDER: u128 = 200; -/// Maps Quint test names to the recursive depth the Rust test should exercise. -/// -/// The Quint spec models individual do_exec/return_from_call steps. The Rust -/// conformance test uses a single recursive account that calls itself N times. struct ConformanceCase { test_name: &'static str, requested_depth: u16, @@ -180,31 +154,26 @@ struct ConformanceCase { fn known_test_cases() -> Vec { vec![ - // singleCallTest: one do_exec, stack=[1], OK ConformanceCase { test_name: "singleCallTest", requested_depth: 1, expect_ok: true, }, - // nestedCallsTest: 3 nested calls, stack=[1,2,3], OK ConformanceCase { test_name: "nestedCallsTest", requested_depth: 3, expect_ok: true, }, - // returnUnwindsStackTest: 2 calls + 1 return, OK (depth 2 succeeds) ConformanceCase { test_name: "returnUnwindsStackTest", requested_depth: 2, expect_ok: true, }, - // fullUnwindTest: 2 calls + 2 returns, OK (depth 2 succeeds) ConformanceCase { test_name: "fullUnwindTest", requested_depth: 2, expect_ok: true, }, - // recursiveCallsTest: 3 recursive self-calls, OK ConformanceCase { test_name: "recursiveCallsTest", requested_depth: 3, @@ -216,14 +185,6 @@ fn known_test_cases() -> Vec { #[test] fn quint_itf_call_depth_conformance() { let traces_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../specs/traces"); - if !traces_dir.exists() { - panic!( - "ITF traces not found at {}. Run: quint test --main=stf_call_depth \ - specs/stf_call_depth.qnt \ - --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"", - traces_dir.display() - ); - } let test_cases = known_test_cases(); for case in &test_cases { @@ -241,36 +202,25 @@ fn quint_itf_call_depth_conformance() { case.test_name, case.expect_ok, spec_result.ok ); - let gas_config = StorageGasConfig { - storage_get_charge: 1, - storage_set_charge: 1, - storage_remove_charge: 1, - }; let stf = Stf::new( NoopBegin::::default(), NoopEnd, - NoopValidator, - NoopPostTx, - gas_config, + NoopValidator::::default(), + NoopPostTx::::default(), + quint_common::default_gas_config(), ); let mut storage = InMemoryStorage::default(); let mut codes = CodeStore::new(); codes.add_code(RecursiveAccount); - - let recursive_account = AccountId::new(RECURSIVE_ACCOUNT); - let code_id = "recursive".to_string(); - storage.data.insert( - account_code_key(recursive_account), - Message::new(&code_id).unwrap().into_bytes().unwrap(), - ); + register_account(&mut storage, AccountId::new(RECURSIVE_ACCOUNT), "recursive"); let msg = RecurseMsg { remaining: case.requested_depth, }; let tx = TestTx { sender: AccountId::new(TEST_SENDER), - recipient: recursive_account, + recipient: AccountId::new(RECURSIVE_ACCOUNT), request: InvokeRequest::new(&msg).unwrap(), gas_limit: 1_000_000, funds: vec![], @@ -297,11 +247,7 @@ fn quint_itf_call_depth_conformance() { ); if !case.expect_ok { - let real_err = real_result.tx_results[0] - .response - .as_ref() - .unwrap_err() - .id; + let real_err = real_result.tx_results[0].response.as_ref().unwrap_err().id; assert_eq!( real_err, evolve_stf::errors::ERR_CALL_DEPTH_EXCEEDED.id, diff --git a/crates/app/stf/tests/quint_common.rs b/crates/app/stf/tests/quint_common.rs index 545ba72..3adc289 100644 --- a/crates/app/stf/tests/quint_common.rs +++ b/crates/app/stf/tests/quint_common.rs @@ -1,14 +1,24 @@ #![allow(dead_code)] use evolve_core::runtime_api::ACCOUNT_IDENTIFIER_PREFIX; -use evolve_core::{AccountCode, AccountId, ErrorCode, ReadonlyKV}; -use evolve_stf_traits::{AccountsCodeStorage, BeginBlocker, EndBlocker, StateChange, WritableKV}; +use evolve_core::{ + AccountCode, AccountId, Environment, ErrorCode, InvokeResponse, ReadonlyKV, SdkResult, +}; +use evolve_stf::gas::StorageGasConfig; +use evolve_stf_traits::{ + AccountsCodeStorage, BeginBlocker, EndBlocker, PostTxExecution, StateChange, TxValidator, + WritableKV, +}; use hashbrown::HashMap; use serde::de::DeserializeOwned; use serde::Deserialize; -use std::fs; +use std::io::BufReader; use std::path::Path; +// --------------------------------------------------------------------------- +// ITF deserialization types +// --------------------------------------------------------------------------- + #[derive(Deserialize, Clone, Debug)] pub struct ItfBigInt { #[serde(rename = "#bigint")] @@ -30,13 +40,52 @@ pub struct ItfMap { pub entries: Vec<(K, V)>, } +#[derive(Deserialize)] +pub struct ItfBlockResult { + pub gas_used: ItfBigInt, + pub tx_results: Vec, + pub txs_skipped: Option, +} + +#[derive(Deserialize)] +pub struct ItfTxResult { + pub gas_used: ItfBigInt, + pub result: ItfResult, +} + +#[derive(Deserialize)] +pub struct ItfResult { + pub ok: bool, + pub err_code: ItfBigInt, +} + +// --------------------------------------------------------------------------- +// Spec error constants (shared across conformance tests) +// --------------------------------------------------------------------------- + +pub const SPEC_ERR_OUT_OF_GAS: i64 = 0x01; +pub const SPEC_ERR_EXECUTION: i64 = 200; + +// --------------------------------------------------------------------------- +// Trace file utilities +// --------------------------------------------------------------------------- + pub fn find_single_trace_file(traces_dir: &Path, test_name: &str) -> std::path::PathBuf { - let trace_files: Vec<_> = fs::read_dir(traces_dir) - .unwrap() + let prefix = format!("out_{}_", test_name); + let trace_files: Vec<_> = std::fs::read_dir(traces_dir) + .unwrap_or_else(|e| { + panic!( + "Cannot read traces directory {}: {e}. \ + Regenerate traces with: quint test specs/stf_*.qnt \ + --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"", + traces_dir.display() + ) + }) .filter_map(|e| e.ok()) .filter(|e| { - let name = e.file_name().to_string_lossy().to_string(); - name.starts_with(&format!("out_{}_", test_name)) && name.ends_with(".itf.json") + let name = e.file_name(); + let name = name.to_string_lossy(); + name.starts_with(&prefix) && name.ends_with(".itf.json") }) .collect(); @@ -51,10 +100,14 @@ pub fn find_single_trace_file(traces_dir: &Path, test_name: &str) -> std::path:: } pub fn read_itf_trace(trace_path: &Path) -> T { - let trace_json = fs::read_to_string(trace_path).unwrap(); - serde_json::from_str(&trace_json).unwrap() + let file = std::fs::File::open(trace_path).unwrap(); + serde_json::from_reader(BufReader::new(file)).unwrap() } +// --------------------------------------------------------------------------- +// Noop STF components +// --------------------------------------------------------------------------- + pub struct NoopBegin(std::marker::PhantomData); impl Default for NoopBegin { @@ -64,26 +117,69 @@ impl Default for NoopBegin { } impl BeginBlocker for NoopBegin { - fn begin_block(&self, _block: &B, _env: &mut dyn evolve_core::Environment) {} + fn begin_block(&self, _block: &B, _env: &mut dyn Environment) {} } #[derive(Default)] pub struct NoopEnd; impl EndBlocker for NoopEnd { - fn end_block(&self, _env: &mut dyn evolve_core::Environment) {} + fn end_block(&self, _env: &mut dyn Environment) {} +} + +pub struct NoopValidator(std::marker::PhantomData); + +impl Default for NoopValidator { + fn default() -> Self { + Self(std::marker::PhantomData) + } +} + +impl TxValidator for NoopValidator { + fn validate_tx(&self, _tx: &T, _env: &mut dyn Environment) -> SdkResult<()> { + Ok(()) + } +} + +pub struct NoopPostTx(std::marker::PhantomData); + +impl Default for NoopPostTx { + fn default() -> Self { + Self(std::marker::PhantomData) + } +} + +impl PostTxExecution for NoopPostTx { + fn after_tx_executed( + _tx: &T, + _gas_consumed: u64, + _tx_result: &SdkResult, + _env: &mut dyn Environment, + ) -> SdkResult<()> { + Ok(()) + } } +// --------------------------------------------------------------------------- +// In-memory storage and code store +// --------------------------------------------------------------------------- + pub struct CodeStore { codes: HashMap>, } -impl CodeStore { - pub fn new() -> Self { +impl Default for CodeStore { + fn default() -> Self { Self { codes: HashMap::new(), } } +} + +impl CodeStore { + pub fn new() -> Self { + Self::default() + } pub fn add_code(&mut self, code: impl AccountCode + 'static) { self.codes.insert(code.identifier(), Box::new(code)); } @@ -128,8 +224,76 @@ impl WritableKV for InMemoryStorage { } } +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + pub fn account_code_key(account: AccountId) -> Vec { let mut out = vec![ACCOUNT_IDENTIFIER_PREFIX]; out.extend_from_slice(&account.as_bytes()); out } + +pub fn default_gas_config() -> StorageGasConfig { + StorageGasConfig { + storage_get_charge: 1, + storage_set_charge: 1, + storage_remove_charge: 1, + } +} + +pub fn register_account(storage: &mut InMemoryStorage, account: AccountId, code_id: &str) { + use evolve_core::Message; + let id = code_id.to_string(); + storage.data.insert( + account_code_key(account), + Message::new(&id).unwrap().into_bytes().unwrap(), + ); +} + +pub fn extract_account_storage( + storage: &InMemoryStorage, + account: AccountId, +) -> HashMap, Vec> { + let prefix = account.as_bytes(); + let mut result = HashMap::new(); + for (key, value) in &storage.data { + if key.len() >= prefix.len() && key[..prefix.len()] == prefix { + result.insert(key[prefix.len()..].to_vec(), value.clone()); + } + } + result +} + +pub fn expected_storage_from_itf( + itf_storage: &ItfMap, Vec>>, + account: AccountId, +) -> HashMap, Vec> { + let mut expected = HashMap::new(); + for (account_id_itf, account_store_itf) in &itf_storage.entries { + if AccountId::new(account_id_itf.as_u64() as u128) != account { + continue; + } + for (key_itf, value_itf) in &account_store_itf.entries { + let key: Vec = key_itf.iter().map(|b| b.as_u64() as u8).collect(); + let value: Vec = value_itf.iter().map(|b| b.as_u64() as u8).collect(); + expected.insert(key, value); + } + } + expected +} + +pub fn assert_storage_matches( + storage: &InMemoryStorage, + itf_storage: &ItfMap, Vec>>, + account: AccountId, + test_name: &str, +) { + let expected = expected_storage_from_itf(itf_storage, account); + let actual = extract_account_storage(storage, account); + assert_eq!( + actual, expected, + "{}: storage mismatch for account {:?}", + test_name, account + ); +} diff --git a/crates/app/stf/tests/quint_conformance.rs b/crates/app/stf/tests/quint_conformance.rs index eed4a24..7c065e2 100644 --- a/crates/app/stf/tests/quint_conformance.rs +++ b/crates/app/stf/tests/quint_conformance.rs @@ -14,23 +14,22 @@ use evolve_core::{ AccountCode, AccountId, BlockContext, Environment, EnvironmentQuery, ErrorCode, FungibleAsset, InvokableMessage, InvokeRequest, InvokeResponse, Message, SdkResult, }; -use evolve_stf::gas::StorageGasConfig; use evolve_stf::Stf; use evolve_stf_traits::{ Block as BlockTrait, PostTxExecution, SenderBootstrap, Transaction, TxValidator, WritableKV, }; -use hashbrown::HashMap; use serde::Deserialize; use std::path::Path; mod quint_common; use quint_common::{ - account_code_key, find_single_trace_file, read_itf_trace, CodeStore, InMemoryStorage, - ItfBigInt, ItfMap, NoopBegin, NoopEnd, + assert_storage_matches, find_single_trace_file, read_itf_trace, register_account, CodeStore, + InMemoryStorage, ItfBigInt, ItfBlockResult, ItfMap, NoopBegin, NoopEnd, SPEC_ERR_EXECUTION, + SPEC_ERR_OUT_OF_GAS, }; // --------------------------------------------------------------------------- -// ITF deserialization types +// ITF deserialization types (spec-specific state shape) // --------------------------------------------------------------------------- #[derive(Deserialize)] @@ -42,34 +41,16 @@ struct ItfTrace { struct ItfState { block_height: ItfBigInt, last_result: ItfBlockResult, - #[allow(dead_code)] - last_block_tx_count: ItfBigInt, storage: ItfMap, Vec>>, } -#[derive(Deserialize)] -struct ItfBlockResult { - gas_used: ItfBigInt, - tx_results: Vec, - txs_skipped: ItfBigInt, -} - -#[derive(Deserialize)] -struct ItfTxResult { - gas_used: ItfBigInt, - result: ItfResult, -} - -#[derive(Deserialize)] -struct ItfResult { - ok: bool, - err_code: ItfBigInt, -} - // --------------------------------------------------------------------------- -// STF test infrastructure (mirrors model_tests in lib.rs) +// STF test infrastructure // --------------------------------------------------------------------------- +const SPEC_ERR_VALIDATION: i64 = 100; +const SPEC_ERR_BOOTSTRAP: i64 = 300; + #[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] struct TestMsg { key: Vec, @@ -154,18 +135,15 @@ struct Validator; impl TxValidator for Validator { fn validate_tx(&self, tx: &TestTx, env: &mut dyn Environment) -> SdkResult<()> { if tx.fail_validate { - return Err(ErrorCode::new(100)); + return Err(ErrorCode::new(SPEC_ERR_VALIDATION as u16)); } - // Simulate production signature verification: query the sender's - // account code. If the sender is not registered this fails, mirroring - // the real validator that cannot verify signatures without account code. let probe = TestMsg { key: vec![], value: vec![], fail_after_write: false, }; env.do_query(tx.sender(), &InvokeRequest::new(&probe).unwrap()) - .map_err(|_| ErrorCode::new(100))?; + .map_err(|_| ErrorCode::new(SPEC_ERR_VALIDATION as u16))?; Ok(()) } } @@ -205,7 +183,7 @@ impl AccountCode for TestAccount { ) -> SdkResult { let init: BootstrapInit = request.get()?; if init.fail { - return Err(ErrorCode::new(300)); + return Err(ErrorCode::new(SPEC_ERR_BOOTSTRAP as u16)); } InvokeResponse::new(&()) } @@ -221,7 +199,7 @@ impl AccountCode for TestAccount { }; env.do_exec(STORAGE_ACCOUNT_ID, &InvokeRequest::new(&set)?, vec![])?; if msg.fail_after_write { - return Err(ErrorCode::new(200)); + return Err(ErrorCode::new(SPEC_ERR_EXECUTION as u16)); } InvokeResponse::new(&()) } @@ -238,11 +216,6 @@ impl AccountCode for TestAccount { // Test case definitions (must match the Quint spec's run declarations) // --------------------------------------------------------------------------- -const SPEC_ERR_OUT_OF_GAS: i64 = 0x01; -const SPEC_ERR_VALIDATION: i64 = 100; -const SPEC_ERR_EXECUTION: i64 = 200; -const SPEC_ERR_BOOTSTRAP: i64 = 300; - const TEST_ACCOUNT: u128 = 100; const TEST_SENDER: u128 = 200; @@ -292,7 +265,6 @@ fn known_test_cases() -> Vec { gas_limit: 1_000_000, }], }, - // sender=TEST_ACCOUNT (registered) for all non-bootstrap tests ConformanceCase { test_name: "successfulTxTest", blocks: vec![TestBlock { @@ -493,8 +465,6 @@ fn known_test_cases() -> Vec { })], }], }, - // unregisteredSenderTest: sender=200 (not registered), no bootstrap - // Validator queries sender account for sig verification -> fails ConformanceCase { test_name: "unregisteredSenderTest", blocks: vec![TestBlock { @@ -594,15 +564,6 @@ fn known_test_cases() -> Vec { fn quint_itf_conformance() { let traces_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../specs/traces"); - if !traces_dir.exists() { - panic!( - "ITF traces not found at {}. \ - Run: quint test --main=stf specs/stf_core.qnt \ - --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"", - traces_dir.display() - ); - } - let test_cases = known_test_cases(); let mut matched = 0; @@ -625,29 +586,18 @@ fn quint_itf_conformance() { let spec_result = &spec_state.last_result; - let gas_config = StorageGasConfig { - storage_get_charge: 1, - storage_set_charge: 1, - storage_remove_charge: 1, - }; let stf = Stf::new( NoopBegin::::default(), NoopEnd, Validator, NoopPostTx, - gas_config, + quint_common::default_gas_config(), ); let mut storage = InMemoryStorage::default(); let mut codes = CodeStore::new(); codes.add_code(TestAccount); - - let test_account = AccountId::new(TEST_ACCOUNT); - let code_id = "test_account".to_string(); - storage.data.insert( - account_code_key(test_account), - Message::new(&code_id).unwrap().into_bytes().unwrap(), - ); + register_account(&mut storage, AccountId::new(TEST_ACCOUNT), "test_account"); let mut real_result = None; for block in &case.blocks { @@ -695,17 +645,17 @@ fn quint_itf_conformance() { case.test_name ), SPEC_ERR_VALIDATION => assert_eq!( - real_err, 100, + real_err, SPEC_ERR_VALIDATION as u16, "{} tx[{i}]: expected validation error", case.test_name ), SPEC_ERR_EXECUTION => assert_eq!( - real_err, 200, + real_err, SPEC_ERR_EXECUTION as u16, "{} tx[{i}]: expected execution error", case.test_name ), SPEC_ERR_BOOTSTRAP => assert_eq!( - real_err, 300, + real_err, SPEC_ERR_BOOTSTRAP as u16, "{} tx[{i}]: expected bootstrap error", case.test_name ), @@ -736,46 +686,14 @@ fn quint_itf_conformance() { // 5. Skipped count assert_eq!( real_result.txs_skipped, - spec_result.txs_skipped.as_u64() as usize, + spec_result.txs_skipped.as_ref().unwrap().as_u64() as usize, "{}: txs_skipped mismatch", case.test_name ); // 6. Storage - let modeled_accounts = - [AccountId::new(TEST_ACCOUNT), AccountId::new(TEST_SENDER)]; - for account_id in modeled_accounts { - let mut expected = HashMap::, Vec>::new(); - for (account_id_itf, account_store_itf) in &spec_state.storage.entries { - if AccountId::new(account_id_itf.as_u64() as u128) != account_id { - continue; - } - for (key_itf, value_itf) in &account_store_itf.entries { - let key: Vec = key_itf.iter().map(|b| b.as_u64() as u8).collect(); - let value: Vec = - value_itf.iter().map(|b| b.as_u64() as u8).collect(); - expected.insert(key, value); - } - } - - let mut actual = HashMap::, Vec>::new(); - let account_prefix = account_id.as_bytes(); - for (raw_key, raw_value) in &storage.data { - if raw_key.len() < account_prefix.len() { - continue; - } - if raw_key[..account_prefix.len()] == account_prefix { - actual.insert( - raw_key[account_prefix.len()..].to_vec(), - raw_value.clone(), - ); - } - } - assert_eq!( - actual, expected, - "{}: storage mismatch for account {:?}", - case.test_name, account_id - ); + for account_id in [AccountId::new(TEST_ACCOUNT), AccountId::new(TEST_SENDER)] { + assert_storage_matches(&storage, &spec_state.storage, account_id, case.test_name); } matched += 1; diff --git a/crates/app/stf/tests/quint_post_tx_conformance.rs b/crates/app/stf/tests/quint_post_tx_conformance.rs index c64a4cc..33a8682 100644 --- a/crates/app/stf/tests/quint_post_tx_conformance.rs +++ b/crates/app/stf/tests/quint_post_tx_conformance.rs @@ -10,19 +10,16 @@ use evolve_core::{ AccountCode, AccountId, BlockContext, Environment, EnvironmentQuery, ErrorCode, FungibleAsset, InvokableMessage, InvokeRequest, InvokeResponse, Message, SdkResult, }; -use evolve_stf::gas::StorageGasConfig; use evolve_stf::Stf; -use evolve_stf_traits::{ - Block as BlockTrait, PostTxExecution, Transaction, TxValidator, WritableKV, -}; -use hashbrown::HashMap; +use evolve_stf_traits::{Block as BlockTrait, PostTxExecution, Transaction, WritableKV}; use serde::Deserialize; use std::path::Path; mod quint_common; use quint_common::{ - account_code_key, find_single_trace_file, read_itf_trace, CodeStore, InMemoryStorage, - ItfBigInt, ItfMap, NoopBegin, NoopEnd, + assert_storage_matches, find_single_trace_file, read_itf_trace, register_account, CodeStore, + InMemoryStorage, ItfBigInt, ItfBlockResult, ItfMap, NoopBegin, NoopEnd, NoopValidator, + SPEC_ERR_EXECUTION, SPEC_ERR_OUT_OF_GAS, }; #[derive(Deserialize)] @@ -36,23 +33,7 @@ struct ItfState { storage: ItfMap, Vec>>, } -#[derive(Deserialize)] -struct ItfBlockResult { - gas_used: ItfBigInt, - tx_results: Vec, -} - -#[derive(Deserialize)] -struct ItfTxResult { - gas_used: ItfBigInt, - result: ItfResult, -} - -#[derive(Deserialize)] -struct ItfResult { - ok: bool, - err_code: ItfBigInt, -} +const SPEC_ERR_POST_TX: i64 = 999; #[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] struct TestMsg { @@ -113,14 +94,6 @@ impl BlockTrait for TestBlock { } } -#[derive(Default)] -struct NoopValidator; -impl TxValidator for NoopValidator { - fn validate_tx(&self, _tx: &TestTx, _env: &mut dyn Environment) -> SdkResult<()> { - Ok(()) - } -} - #[derive(Default)] struct RejectingPostTx; impl PostTxExecution for RejectingPostTx { @@ -131,7 +104,7 @@ impl PostTxExecution for RejectingPostTx { _env: &mut dyn Environment, ) -> SdkResult<()> { if tx.reject_post_tx && tx_result.is_ok() { - return Err(ErrorCode::new(999)); + return Err(ErrorCode::new(SPEC_ERR_POST_TX as u16)); } Ok(()) } @@ -166,7 +139,7 @@ impl AccountCode for TestAccount { }; env.do_exec(STORAGE_ACCOUNT_ID, &InvokeRequest::new(&set)?, vec![])?; if msg.fail_after_write { - return Err(ErrorCode::new(200)); + return Err(ErrorCode::new(SPEC_ERR_EXECUTION as u16)); } InvokeResponse::new(&()) } @@ -179,43 +152,38 @@ impl AccountCode for TestAccount { } } -const SPEC_ERR_OUT_OF_GAS: i64 = 0x01; -const SPEC_ERR_EXECUTION: i64 = 200; -const SPEC_ERR_POST_TX: i64 = 999; -const TEST_ACCOUNT: u128 = 100; +const TEST_ACCOUNT_ID: u128 = 100; const TEST_SENDER: u128 = 200; -struct ConformanceCase { - test_name: &'static str, - block: TestBlock, -} - -fn make_tx(fail_execute: bool, reject_post_tx: bool) -> TestTx { - make_tx_with(vec![1], vec![11], 10000, fail_execute, reject_post_tx) -} - -fn make_tx_with( +struct TxCase { key: Vec, value: Vec, gas_limit: u64, fail_execute: bool, reject_post_tx: bool, -) -> TestTx { +} + +fn make_tx(tc: TxCase) -> TestTx { let msg = TestMsg { - key, - value, - fail_after_write: fail_execute, + key: tc.key, + value: tc.value, + fail_after_write: tc.fail_execute, }; TestTx { sender: AccountId::new(TEST_SENDER), - recipient: AccountId::new(TEST_ACCOUNT), + recipient: AccountId::new(TEST_ACCOUNT_ID), request: InvokeRequest::new(&msg).unwrap(), - gas_limit, + gas_limit: tc.gas_limit, funds: vec![], - reject_post_tx, + reject_post_tx: tc.reject_post_tx, } } +struct ConformanceCase { + test_name: &'static str, + block: TestBlock, +} + fn known_test_cases() -> Vec { vec![ ConformanceCase { @@ -223,7 +191,13 @@ fn known_test_cases() -> Vec { block: TestBlock { height: 1, time: 0, - txs: vec![make_tx(false, true)], + txs: vec![make_tx(TxCase { + key: vec![1], + value: vec![11], + gas_limit: 10000, + fail_execute: false, + reject_post_tx: true, + })], }, }, ConformanceCase { @@ -231,7 +205,13 @@ fn known_test_cases() -> Vec { block: TestBlock { height: 1, time: 0, - txs: vec![make_tx(true, true)], + txs: vec![make_tx(TxCase { + key: vec![1], + value: vec![11], + gas_limit: 10000, + fail_execute: true, + reject_post_tx: true, + })], }, }, ConformanceCase { @@ -239,7 +219,13 @@ fn known_test_cases() -> Vec { block: TestBlock { height: 1, time: 0, - txs: vec![make_tx(false, false)], + txs: vec![make_tx(TxCase { + key: vec![1], + value: vec![11], + gas_limit: 10000, + fail_execute: false, + reject_post_tx: false, + })], }, }, ConformanceCase { @@ -248,8 +234,20 @@ fn known_test_cases() -> Vec { height: 1, time: 0, txs: vec![ - make_tx(false, true), - make_tx_with(vec![2], vec![12], 10000, false, false), + make_tx(TxCase { + key: vec![1], + value: vec![11], + gas_limit: 10000, + fail_execute: false, + reject_post_tx: true, + }), + make_tx(TxCase { + key: vec![2], + value: vec![12], + gas_limit: 10000, + fail_execute: false, + reject_post_tx: false, + }), ], }, }, @@ -258,7 +256,13 @@ fn known_test_cases() -> Vec { block: TestBlock { height: 1, time: 0, - txs: vec![make_tx_with(vec![1], vec![11], 1, false, true)], + txs: vec![make_tx(TxCase { + key: vec![1], + value: vec![11], + gas_limit: 1, + fail_execute: false, + reject_post_tx: true, + })], }, }, ] @@ -267,12 +271,6 @@ fn known_test_cases() -> Vec { #[test] fn quint_itf_post_tx_conformance() { let traces_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../specs/traces"); - if !traces_dir.exists() { - panic!( - "ITF traces not found at {}. Run: quint test specs/stf_post_tx.qnt --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"", - traces_dir.display() - ); - } let test_cases = known_test_cases(); for case in &test_cases { @@ -284,28 +282,21 @@ fn quint_itf_post_tx_conformance() { .expect("trace must have at least one state"); let spec_result = &spec_state.last_result; - let gas_config = StorageGasConfig { - storage_get_charge: 1, - storage_set_charge: 1, - storage_remove_charge: 1, - }; let stf = Stf::new( NoopBegin::::default(), NoopEnd, - NoopValidator, + NoopValidator::::default(), RejectingPostTx, - gas_config, + quint_common::default_gas_config(), ); let mut storage = InMemoryStorage::default(); let mut codes = CodeStore::new(); codes.add_code(TestAccount); - - let test_account = AccountId::new(TEST_ACCOUNT); - let code_id = "test_account".to_string(); - storage.data.insert( - account_code_key(test_account), - Message::new(&code_id).unwrap().into_bytes().unwrap(), + register_account( + &mut storage, + AccountId::new(TEST_ACCOUNT_ID), + "test_account", ); let (real_result, exec_state) = stf.apply_block(&storage, &codes, &case.block); @@ -341,12 +332,12 @@ fn quint_itf_post_tx_conformance() { case.test_name ), SPEC_ERR_EXECUTION => assert_eq!( - real_err, 200, + real_err, SPEC_ERR_EXECUTION as u16, "{} tx[{i}]: expected execution error", case.test_name ), SPEC_ERR_POST_TX => assert_eq!( - real_err, 999, + real_err, SPEC_ERR_POST_TX as u16, "{} tx[{i}]: expected post-tx error", case.test_name ), @@ -372,33 +363,11 @@ fn quint_itf_post_tx_conformance() { case.test_name ); - let account_id = AccountId::new(TEST_ACCOUNT); - let mut expected = HashMap::, Vec>::new(); - for (account_id_itf, account_store_itf) in &spec_state.storage.entries { - if AccountId::new(account_id_itf.as_u64() as u128) != account_id { - continue; - } - for (key_itf, value_itf) in &account_store_itf.entries { - let key: Vec = key_itf.iter().map(|b| b.as_u64() as u8).collect(); - let value: Vec = value_itf.iter().map(|b| b.as_u64() as u8).collect(); - expected.insert(key, value); - } - } - - let mut actual = HashMap::, Vec>::new(); - let account_prefix = account_id.as_bytes(); - for (raw_key, raw_value) in &storage.data { - if raw_key.len() < account_prefix.len() { - continue; - } - if raw_key[..account_prefix.len()] == account_prefix { - actual.insert(raw_key[account_prefix.len()..].to_vec(), raw_value.clone()); - } - } - assert_eq!( - actual, expected, - "{}: storage mismatch for account {:?}", - case.test_name, account_id + assert_storage_matches( + &storage, + &spec_state.storage, + AccountId::new(TEST_ACCOUNT_ID), + case.test_name, ); } } From 4e156a336147c76c9bb90e29535101b9aac1894a Mon Sep 17 00:00:00 2001 From: tac0turtle Date: Mon, 9 Mar 2026 11:59:29 +0100 Subject: [PATCH 6/9] quality --- .../stf/tests/quint_call_depth_conformance.rs | 14 +++++++---- crates/app/stf/tests/quint_common.rs | 2 +- crates/app/stf/tests/quint_conformance.rs | 23 ++++++++++++------- .../stf/tests/quint_post_tx_conformance.rs | 12 +++++----- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/crates/app/stf/tests/quint_call_depth_conformance.rs b/crates/app/stf/tests/quint_call_depth_conformance.rs index b5cf955..dc7a08e 100644 --- a/crates/app/stf/tests/quint_call_depth_conformance.rs +++ b/crates/app/stf/tests/quint_call_depth_conformance.rs @@ -143,8 +143,8 @@ impl AccountCode for RecursiveAccount { } } -const RECURSIVE_ACCOUNT: u128 = 100; -const TEST_SENDER: u128 = 200; +const RECURSIVE_ACCOUNT: u64 = 100; +const TEST_SENDER: u64 = 200; struct ConformanceCase { test_name: &'static str, @@ -213,14 +213,18 @@ fn quint_itf_call_depth_conformance() { let mut storage = InMemoryStorage::default(); let mut codes = CodeStore::new(); codes.add_code(RecursiveAccount); - register_account(&mut storage, AccountId::new(RECURSIVE_ACCOUNT), "recursive"); + register_account( + &mut storage, + AccountId::from_u64(RECURSIVE_ACCOUNT), + "recursive", + ); let msg = RecurseMsg { remaining: case.requested_depth, }; let tx = TestTx { - sender: AccountId::new(TEST_SENDER), - recipient: AccountId::new(RECURSIVE_ACCOUNT), + sender: AccountId::from_u64(TEST_SENDER), + recipient: AccountId::from_u64(RECURSIVE_ACCOUNT), request: InvokeRequest::new(&msg).unwrap(), gas_limit: 1_000_000, funds: vec![], diff --git a/crates/app/stf/tests/quint_common.rs b/crates/app/stf/tests/quint_common.rs index 3adc289..6e5d320 100644 --- a/crates/app/stf/tests/quint_common.rs +++ b/crates/app/stf/tests/quint_common.rs @@ -271,7 +271,7 @@ pub fn expected_storage_from_itf( ) -> HashMap, Vec> { let mut expected = HashMap::new(); for (account_id_itf, account_store_itf) in &itf_storage.entries { - if AccountId::new(account_id_itf.as_u64() as u128) != account { + if AccountId::from_u64(account_id_itf.as_u64()) != account { continue; } for (key_itf, value_itf) in &account_store_itf.entries { diff --git a/crates/app/stf/tests/quint_conformance.rs b/crates/app/stf/tests/quint_conformance.rs index 7c065e2..88f31c6 100644 --- a/crates/app/stf/tests/quint_conformance.rs +++ b/crates/app/stf/tests/quint_conformance.rs @@ -216,12 +216,12 @@ impl AccountCode for TestAccount { // Test case definitions (must match the Quint spec's run declarations) // --------------------------------------------------------------------------- -const TEST_ACCOUNT: u128 = 100; -const TEST_SENDER: u128 = 200; +const TEST_ACCOUNT: u64 = 100; +const TEST_SENDER: u64 = 200; struct TxCase { - sender: u128, - recipient: u128, + sender: u64, + recipient: u64, key: Vec, value: Vec, gas_limit: u64, @@ -238,8 +238,8 @@ fn make_tx(tc: TxCase) -> TestTx { fail_after_write: tc.fail_execute, }; TestTx { - sender: AccountId::new(tc.sender), - recipient: AccountId::new(tc.recipient), + sender: AccountId::from_u64(tc.sender), + recipient: AccountId::from_u64(tc.recipient), request: InvokeRequest::new(&msg).unwrap(), gas_limit: tc.gas_limit, funds: vec![], @@ -597,7 +597,11 @@ fn quint_itf_conformance() { let mut storage = InMemoryStorage::default(); let mut codes = CodeStore::new(); codes.add_code(TestAccount); - register_account(&mut storage, AccountId::new(TEST_ACCOUNT), "test_account"); + register_account( + &mut storage, + AccountId::from_u64(TEST_ACCOUNT), + "test_account", + ); let mut real_result = None; for block in &case.blocks { @@ -692,7 +696,10 @@ fn quint_itf_conformance() { ); // 6. Storage - for account_id in [AccountId::new(TEST_ACCOUNT), AccountId::new(TEST_SENDER)] { + for account_id in [ + AccountId::from_u64(TEST_ACCOUNT), + AccountId::from_u64(TEST_SENDER), + ] { assert_storage_matches(&storage, &spec_state.storage, account_id, case.test_name); } diff --git a/crates/app/stf/tests/quint_post_tx_conformance.rs b/crates/app/stf/tests/quint_post_tx_conformance.rs index 33a8682..ee4554f 100644 --- a/crates/app/stf/tests/quint_post_tx_conformance.rs +++ b/crates/app/stf/tests/quint_post_tx_conformance.rs @@ -152,8 +152,8 @@ impl AccountCode for TestAccount { } } -const TEST_ACCOUNT_ID: u128 = 100; -const TEST_SENDER: u128 = 200; +const TEST_ACCOUNT_ID: u64 = 100; +const TEST_SENDER: u64 = 200; struct TxCase { key: Vec, @@ -170,8 +170,8 @@ fn make_tx(tc: TxCase) -> TestTx { fail_after_write: tc.fail_execute, }; TestTx { - sender: AccountId::new(TEST_SENDER), - recipient: AccountId::new(TEST_ACCOUNT_ID), + sender: AccountId::from_u64(TEST_SENDER), + recipient: AccountId::from_u64(TEST_ACCOUNT_ID), request: InvokeRequest::new(&msg).unwrap(), gas_limit: tc.gas_limit, funds: vec![], @@ -295,7 +295,7 @@ fn quint_itf_post_tx_conformance() { codes.add_code(TestAccount); register_account( &mut storage, - AccountId::new(TEST_ACCOUNT_ID), + AccountId::from_u64(TEST_ACCOUNT_ID), "test_account", ); @@ -366,7 +366,7 @@ fn quint_itf_post_tx_conformance() { assert_storage_matches( &storage, &spec_state.storage, - AccountId::new(TEST_ACCOUNT_ID), + AccountId::from_u64(TEST_ACCOUNT_ID), case.test_name, ); } From 61c89af7385d78611525e05a4c8d7a4f0cfbdba0 Mon Sep 17 00:00:00 2001 From: tac0turtle Date: Mon, 9 Mar 2026 12:08:35 +0100 Subject: [PATCH 7/9] dedup --- crates/app/stf/tests/quint_common.rs | 20 ++++++++++- crates/app/stf/tests/quint_conformance.rs | 35 +++---------------- .../stf/tests/quint_post_tx_conformance.rs | 17 ++------- 3 files changed, 26 insertions(+), 46 deletions(-) diff --git a/crates/app/stf/tests/quint_common.rs b/crates/app/stf/tests/quint_common.rs index 6e5d320..eac7588 100644 --- a/crates/app/stf/tests/quint_common.rs +++ b/crates/app/stf/tests/quint_common.rs @@ -1,8 +1,10 @@ #![allow(dead_code)] +use borsh::{BorshDeserialize, BorshSerialize}; use evolve_core::runtime_api::ACCOUNT_IDENTIFIER_PREFIX; use evolve_core::{ - AccountCode, AccountId, Environment, ErrorCode, InvokeResponse, ReadonlyKV, SdkResult, + AccountCode, AccountId, Environment, ErrorCode, InvokableMessage, InvokeResponse, ReadonlyKV, + SdkResult, }; use evolve_stf::gas::StorageGasConfig; use evolve_stf_traits::{ @@ -66,6 +68,22 @@ pub struct ItfResult { pub const SPEC_ERR_OUT_OF_GAS: i64 = 0x01; pub const SPEC_ERR_EXECUTION: i64 = 200; +// --------------------------------------------------------------------------- +// Shared test message (used by core and post-tx conformance tests) +// --------------------------------------------------------------------------- + +#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] +pub struct TestMsg { + pub key: Vec, + pub value: Vec, + pub fail_after_write: bool, +} + +impl InvokableMessage for TestMsg { + const FUNCTION_IDENTIFIER: u64 = 1; + const FUNCTION_IDENTIFIER_NAME: &'static str = "test_msg"; +} + // --------------------------------------------------------------------------- // Trace file utilities // --------------------------------------------------------------------------- diff --git a/crates/app/stf/tests/quint_conformance.rs b/crates/app/stf/tests/quint_conformance.rs index 88f31c6..9e83a5b 100644 --- a/crates/app/stf/tests/quint_conformance.rs +++ b/crates/app/stf/tests/quint_conformance.rs @@ -12,11 +12,11 @@ use borsh::{BorshDeserialize, BorshSerialize}; use evolve_core::storage_api::{StorageSetRequest, STORAGE_ACCOUNT_ID}; use evolve_core::{ AccountCode, AccountId, BlockContext, Environment, EnvironmentQuery, ErrorCode, FungibleAsset, - InvokableMessage, InvokeRequest, InvokeResponse, Message, SdkResult, + InvokeRequest, InvokeResponse, Message, SdkResult, }; use evolve_stf::Stf; use evolve_stf_traits::{ - Block as BlockTrait, PostTxExecution, SenderBootstrap, Transaction, TxValidator, WritableKV, + Block as BlockTrait, SenderBootstrap, Transaction, TxValidator, WritableKV, }; use serde::Deserialize; use std::path::Path; @@ -24,8 +24,8 @@ use std::path::Path; mod quint_common; use quint_common::{ assert_storage_matches, find_single_trace_file, read_itf_trace, register_account, CodeStore, - InMemoryStorage, ItfBigInt, ItfBlockResult, ItfMap, NoopBegin, NoopEnd, SPEC_ERR_EXECUTION, - SPEC_ERR_OUT_OF_GAS, + InMemoryStorage, ItfBigInt, ItfBlockResult, ItfMap, NoopBegin, NoopEnd, NoopPostTx, TestMsg, + SPEC_ERR_EXECUTION, SPEC_ERR_OUT_OF_GAS, }; // --------------------------------------------------------------------------- @@ -51,18 +51,6 @@ struct ItfState { const SPEC_ERR_VALIDATION: i64 = 100; const SPEC_ERR_BOOTSTRAP: i64 = 300; -#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] -struct TestMsg { - key: Vec, - value: Vec, - fail_after_write: bool, -} - -impl InvokableMessage for TestMsg { - const FUNCTION_IDENTIFIER: u64 = 1; - const FUNCTION_IDENTIFIER_NAME: &'static str = "test_msg"; -} - #[derive(Clone, Debug)] struct TestTx { sender: AccountId, @@ -148,19 +136,6 @@ impl TxValidator for Validator { } } -#[derive(Default)] -struct NoopPostTx; -impl PostTxExecution for NoopPostTx { - fn after_tx_executed( - _tx: &TestTx, - _gas_consumed: u64, - _tx_result: &SdkResult, - _env: &mut dyn Environment, - ) -> SdkResult<()> { - Ok(()) - } -} - #[derive(Default)] struct TestAccount; @@ -590,7 +565,7 @@ fn quint_itf_conformance() { NoopBegin::::default(), NoopEnd, Validator, - NoopPostTx, + NoopPostTx::::default(), quint_common::default_gas_config(), ); diff --git a/crates/app/stf/tests/quint_post_tx_conformance.rs b/crates/app/stf/tests/quint_post_tx_conformance.rs index ee4554f..1b66b42 100644 --- a/crates/app/stf/tests/quint_post_tx_conformance.rs +++ b/crates/app/stf/tests/quint_post_tx_conformance.rs @@ -4,11 +4,10 @@ //! `quint test specs/stf_post_tx.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json"` //! `cargo test -p evolve_stf --test quint_post_tx_conformance` -use borsh::{BorshDeserialize, BorshSerialize}; use evolve_core::storage_api::{StorageSetRequest, STORAGE_ACCOUNT_ID}; use evolve_core::{ AccountCode, AccountId, BlockContext, Environment, EnvironmentQuery, ErrorCode, FungibleAsset, - InvokableMessage, InvokeRequest, InvokeResponse, Message, SdkResult, + InvokeRequest, InvokeResponse, Message, SdkResult, }; use evolve_stf::Stf; use evolve_stf_traits::{Block as BlockTrait, PostTxExecution, Transaction, WritableKV}; @@ -18,7 +17,7 @@ use std::path::Path; mod quint_common; use quint_common::{ assert_storage_matches, find_single_trace_file, read_itf_trace, register_account, CodeStore, - InMemoryStorage, ItfBigInt, ItfBlockResult, ItfMap, NoopBegin, NoopEnd, NoopValidator, + InMemoryStorage, ItfBigInt, ItfBlockResult, ItfMap, NoopBegin, NoopEnd, NoopValidator, TestMsg, SPEC_ERR_EXECUTION, SPEC_ERR_OUT_OF_GAS, }; @@ -35,18 +34,6 @@ struct ItfState { const SPEC_ERR_POST_TX: i64 = 999; -#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] -struct TestMsg { - key: Vec, - value: Vec, - fail_after_write: bool, -} - -impl InvokableMessage for TestMsg { - const FUNCTION_IDENTIFIER: u64 = 1; - const FUNCTION_IDENTIFIER_NAME: &'static str = "test_exec"; -} - #[derive(Clone, Debug)] struct TestTx { sender: AccountId, From cb0dc7942710ba7adc3cfe1fa521dd4e73d1daa1 Mon Sep 17 00:00:00 2001 From: tac0turtle Date: Mon, 9 Mar 2026 14:59:23 +0100 Subject: [PATCH 8/9] fix and use quint connect --- crates/app/stf/Cargo.toml | 2 + crates/app/stf/tests/quint_conformance.rs | 6 +- crates/app/stf/tests/quint_core_connect.rs | 438 ++++++++++++++++++ crates/app/stf/tests/quint_post_tx_connect.rs | 376 +++++++++++++++ justfile | 4 +- specs/stf_core.qnt | 125 +++-- specs/stf_post_tx.qnt | 128 +++-- specs/traces/out_blockGasLimitTest_5.itf.json | 2 +- .../out_bootstrapFailureTest_8.itf.json | 2 +- specs/traces/out_bootstrapTest_7.itf.json | 2 +- .../out_cannotReturnFromEmptyTest_4.itf.json | 2 +- specs/traces/out_emptyBlockTest_0.itf.json | 2 +- ...ut_executionFailureRollbackTest_3.itf.json | 2 +- specs/traces/out_fullUnwindTest_3.itf.json | 2 +- specs/traces/out_happyPathTest_2.itf.json | 2 +- specs/traces/out_mixedOutcomesTest_6.itf.json | 2 +- specs/traces/out_mixedPostTxTest_3.itf.json | 2 +- specs/traces/out_multiBlockTest_10.itf.json | 2 +- specs/traces/out_nestedCallsTest_1.itf.json | 2 +- .../out_outOfGasIgnoresPostTxTest_4.itf.json | 2 +- specs/traces/out_outOfGasTest_4.itf.json | 2 +- specs/traces/out_overwriteTest_11.itf.json | 2 +- ...ostTxDoesNotMaskExecFailureTest_1.itf.json | 2 +- ..._postTxRejectsButKeepsStateTest_0.itf.json | 2 +- .../traces/out_recursiveCallsTest_5.itf.json | 2 +- .../out_returnUnwindsStackTest_2.itf.json | 2 +- specs/traces/out_singleCallTest_0.itf.json | 2 +- specs/traces/out_successfulTxTest_1.itf.json | 2 +- .../out_unregisteredSenderTest_9.itf.json | 2 +- .../out_validationFailureTest_2.itf.json | 2 +- 30 files changed, 1025 insertions(+), 100 deletions(-) create mode 100644 crates/app/stf/tests/quint_core_connect.rs create mode 100644 crates/app/stf/tests/quint_post_tx_connect.rs diff --git a/crates/app/stf/Cargo.toml b/crates/app/stf/Cargo.toml index 7bade5a..d457240 100644 --- a/crates/app/stf/Cargo.toml +++ b/crates/app/stf/Cargo.toml @@ -16,7 +16,9 @@ ahash = { version = "0.8.11", optional = true } linkme = {version = "0.3", default-features = false, optional = true} [dev-dependencies] +anyhow = "1" proptest = "1.4" +quint-connect = "0.1.1" serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/app/stf/tests/quint_conformance.rs b/crates/app/stf/tests/quint_conformance.rs index 9e83a5b..180d923 100644 --- a/crates/app/stf/tests/quint_conformance.rs +++ b/crates/app/stf/tests/quint_conformance.rs @@ -321,14 +321,14 @@ fn known_test_cases() -> Vec { blocks: vec![TestBlock { height: 1, time: 0, - gas_limit: 30, + gas_limit: 70, txs: vec![ make_tx(TxCase { sender: TEST_ACCOUNT, recipient: TEST_ACCOUNT, key: vec![1], value: vec![11], - gas_limit: 25, + gas_limit: 40, fail_validate: false, fail_execute: false, needs_bootstrap: false, @@ -339,7 +339,7 @@ fn known_test_cases() -> Vec { recipient: TEST_ACCOUNT, key: vec![2], value: vec![12], - gas_limit: 25, + gas_limit: 40, fail_validate: false, fail_execute: false, needs_bootstrap: false, diff --git a/crates/app/stf/tests/quint_core_connect.rs b/crates/app/stf/tests/quint_core_connect.rs new file mode 100644 index 0000000..e886c17 --- /dev/null +++ b/crates/app/stf/tests/quint_core_connect.rs @@ -0,0 +1,438 @@ +//! `quint_connect` pilot for `specs/stf_core.qnt`. +//! +//! This uses `quint run --mbt` rather than `quint test`: with Quint 0.30.0, the +//! plain `test` traces do not include `mbt::actionTaken`/`mbt::nondetPicks`, +//! which `quint_connect` needs in order to replay steps. + +use anyhow::anyhow; +use borsh::{BorshDeserialize, BorshSerialize}; +use evolve_core::runtime_api::ACCOUNT_IDENTIFIER_PREFIX; +use evolve_core::storage_api::{StorageSetRequest, STORAGE_ACCOUNT_ID}; +use evolve_core::{ + AccountCode, AccountId, BlockContext, Environment, EnvironmentQuery, ErrorCode, FungibleAsset, + InvokeRequest, InvokeResponse, Message, SdkResult, +}; +use evolve_stf::Stf; +use evolve_stf_traits::{ + Block as BlockTrait, SenderBootstrap, Transaction, TxValidator, WritableKV, +}; +use quint_connect::*; +use serde::Deserialize; +use std::collections::{BTreeMap, BTreeSet}; + +mod quint_common; +use quint_common::{ + default_gas_config, extract_account_storage, register_account, CodeStore, InMemoryStorage, + NoopBegin, NoopEnd, NoopPostTx, TestMsg, +}; + +const SPEC_ERR_VALIDATION: u16 = 100; +const SPEC_ERR_EXECUTION: u16 = 200; +const SPEC_ERR_BOOTSTRAP: u16 = 300; +const WRITE_KEY: &[u8] = &[1]; +const WRITE_VALUE: &[u8] = &[11]; + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] +struct SpecState { + storage: BTreeMap, Vec>>, + accounts: BTreeSet, + block_height: u64, + last_result: SpecBlockResult, + last_block_tx_count: u64, +} + +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)] +struct SpecBlockResult { + tx_results: Vec, + gas_used: u64, + txs_skipped: u64, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] +struct SpecTxResult { + result: SpecResult, + gas_used: u64, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] +struct SpecResult { + ok: bool, + err_code: u64, +} + +impl State for SpecState { + fn from_driver(driver: &CoreDriver) -> Result { + let accounts = driver.accounts(); + let storage = accounts + .iter() + .copied() + .map(|account| { + ( + account, + extract_account_storage(&driver.storage, AccountId::from_u64(account)) + .into_iter() + .map(|(key, value)| { + ( + key.into_iter().map(u64::from).collect(), + value.into_iter().map(u64::from).collect(), + ) + }) + .collect(), + ) + }) + .collect(); + + Ok(Self { + storage, + accounts, + block_height: driver.block_height, + last_result: driver.last_result.clone(), + last_block_tx_count: driver.last_block_tx_count, + }) + } +} + +#[derive(Clone, Debug)] +struct TestTx { + sender: AccountId, + recipient: AccountId, + request: InvokeRequest, + gas_limit: u64, + funds: Vec, + fail_validate: bool, + needs_bootstrap: bool, + fail_bootstrap: bool, +} + +impl Transaction for TestTx { + fn sender(&self) -> AccountId { + self.sender + } + fn recipient(&self) -> AccountId { + self.recipient + } + fn request(&self) -> &InvokeRequest { + &self.request + } + fn gas_limit(&self) -> u64 { + self.gas_limit + } + fn funds(&self) -> &[FungibleAsset] { + &self.funds + } + fn compute_identifier(&self) -> [u8; 32] { + [0u8; 32] + } + fn sender_bootstrap(&self) -> Option { + if !self.needs_bootstrap { + return None; + } + let init = BootstrapInit { + fail: self.fail_bootstrap, + }; + let init_message = + Message::new(&init).expect("bootstrap init serialization must succeed in tests"); + Some(SenderBootstrap { + account_code_id: "test_account", + init_message, + }) + } +} + +#[derive(Clone)] +struct TestBlock { + height: u64, + time: u64, + txs: Vec, + gas_limit: u64, +} + +impl BlockTrait for TestBlock { + fn context(&self) -> BlockContext { + BlockContext::new(self.height, self.time) + } + fn txs(&self) -> &[TestTx] { + &self.txs + } + fn gas_limit(&self) -> u64 { + self.gas_limit + } +} + +#[derive(Default)] +struct Validator; + +impl TxValidator for Validator { + fn validate_tx(&self, tx: &TestTx, env: &mut dyn Environment) -> SdkResult<()> { + if tx.fail_validate { + return Err(ErrorCode::new(SPEC_ERR_VALIDATION)); + } + let probe = TestMsg { + key: vec![], + value: vec![], + fail_after_write: false, + }; + env.do_query(tx.sender(), &InvokeRequest::new(&probe).unwrap()) + .map_err(|_| ErrorCode::new(SPEC_ERR_VALIDATION))?; + Ok(()) + } +} + +#[derive(Default)] +struct TestAccount; + +#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] +struct BootstrapInit { + fail: bool, +} + +impl AccountCode for TestAccount { + fn identifier(&self) -> String { + "test_account".to_string() + } + fn schema(&self) -> evolve_core::schema::AccountSchema { + evolve_core::schema::AccountSchema::new("TestAccount", "test_account") + } + fn init( + &self, + _env: &mut dyn Environment, + request: &InvokeRequest, + ) -> SdkResult { + let init: BootstrapInit = request.get()?; + if init.fail { + return Err(ErrorCode::new(SPEC_ERR_BOOTSTRAP)); + } + InvokeResponse::new(&()) + } + fn execute( + &self, + env: &mut dyn Environment, + request: &InvokeRequest, + ) -> SdkResult { + let msg: TestMsg = request.get()?; + let set = StorageSetRequest { + key: msg.key.clone(), + value: Message::from_bytes(msg.value.clone()), + }; + env.do_exec(STORAGE_ACCOUNT_ID, &InvokeRequest::new(&set)?, vec![])?; + if msg.fail_after_write { + return Err(ErrorCode::new(SPEC_ERR_EXECUTION)); + } + InvokeResponse::new(&()) + } + fn query( + &self, + _env: &mut dyn EnvironmentQuery, + _request: &InvokeRequest, + ) -> SdkResult { + InvokeResponse::new(&()) + } +} + +#[derive(Default)] +struct CoreDriver { + storage: InMemoryStorage, + block_height: u64, + last_result: SpecBlockResult, + last_block_tx_count: u64, +} + +impl Driver for CoreDriver { + type State = SpecState; + + fn step(&mut self, step: &Step) -> Result { + switch!(step { + init => { self.reset()?; }, + register_account(id: u64) => { self.register(id)?; }, + apply_registered_block( + sender: u64, + recipient: u64, + gas_limit: u64, + block_gas: u64, + fail_v: bool, + fail_e: bool, + needs_b: bool, + fail_b: bool + ) => { + self.apply_registered_block( + sender, recipient, gas_limit, block_gas, fail_v, fail_e, needs_b, fail_b + )?; + } + }) + } +} + +impl CoreDriver { + fn reset(&mut self) -> Result { + self.storage = InMemoryStorage::default(); + self.block_height = 0; + self.last_result = SpecBlockResult::default(); + self.last_block_tx_count = 0; + Ok(()) + } + + fn register(&mut self, account_id: u64) -> Result { + register_account( + &mut self.storage, + AccountId::from_u64(account_id), + "test_account", + ); + Ok(()) + } + + #[allow(clippy::too_many_arguments)] + fn apply_registered_block( + &mut self, + sender: u64, + recipient: u64, + gas_limit: u64, + block_gas: u64, + fail_v: bool, + fail_e: bool, + needs_b: bool, + fail_b: bool, + ) -> Result { + let msg = TestMsg { + key: WRITE_KEY.to_vec(), + value: WRITE_VALUE.to_vec(), + fail_after_write: fail_e, + }; + let tx = TestTx { + sender: AccountId::from_u64(sender), + recipient: AccountId::from_u64(recipient), + request: InvokeRequest::new(&msg) + .map_err(|err| anyhow!("failed to serialize test request: {err:?}"))?, + gas_limit, + funds: vec![], + fail_validate: fail_v, + needs_bootstrap: needs_b, + fail_bootstrap: fail_b, + }; + let block = TestBlock { + height: self.block_height + 1, + time: 0, + txs: vec![tx], + gas_limit: block_gas, + }; + + let stf = build_stf(); + let codes = build_codes(); + let (result, exec_state) = stf.apply_block(&self.storage, &codes, &block); + let changes = exec_state + .into_changes() + .map_err(|err| anyhow!("failed to extract execution changes: {err:?}"))?; + self.storage + .apply_changes(changes) + .map_err(|err| anyhow!("failed to apply execution changes: {err:?}"))?; + self.block_height = block.height; + self.last_result = SpecBlockResult::from_real(&result); + self.last_block_tx_count = block.txs.len() as u64; + + Ok(()) + } + + fn accounts(&self) -> BTreeSet { + self.storage + .data + .iter() + .filter(|(key, value)| { + key.first() == Some(&ACCOUNT_IDENTIFIER_PREFIX) + && Message::from_bytes((*value).clone()) + .get::() + .ok() + .as_deref() + == Some("test_account") + }) + .filter_map(|(key, _)| { + let bytes = key.get(1..33)?; + let account_bytes: [u8; 32] = bytes.try_into().ok()?; + Some(account_id_to_u64(AccountId::from_bytes(account_bytes))) + }) + .collect() + } +} + +fn build_codes() -> CodeStore { + let mut codes = CodeStore::new(); + codes.add_code(TestAccount); + codes +} + +fn build_stf( +) -> Stf, Validator, NoopEnd, NoopPostTx> { + Stf::new( + NoopBegin::::default(), + NoopEnd, + Validator, + NoopPostTx::::default(), + default_gas_config(), + ) +} + +fn account_id_to_u64(account: AccountId) -> u64 { + let bytes = account.as_bytes(); + u64::from_be_bytes( + bytes[24..32] + .try_into() + .expect("account id must be 32 bytes"), + ) +} + +impl SpecBlockResult { + fn from_real(result: &evolve_stf::results::BlockResult) -> Self { + Self { + tx_results: result + .tx_results + .iter() + .map(SpecTxResult::from_real) + .collect(), + gas_used: result.gas_used, + txs_skipped: result.txs_skipped as u64, + } + } +} + +impl SpecTxResult { + fn from_real(result: &evolve_stf::results::TxResult) -> Self { + Self { + result: SpecResult::from_real(&result.response), + gas_used: result.gas_used, + } + } +} + +impl SpecResult { + fn from_real(result: &SdkResult) -> Self { + match result { + Ok(_) => Self { + ok: true, + err_code: 0, + }, + Err(err) => Self { + ok: false, + err_code: normalize_error_code(err.id), + }, + } + } +} + +fn normalize_error_code(err_id: u16) -> u64 { + if err_id == evolve_stf::ERR_OUT_OF_GAS.id { + 0x01 + } else { + err_id as u64 + } +} + +#[quint_run( + spec = "../../../specs/stf_core.qnt", + main = "stf", + init = "init", + step = "qc_step", + max_samples = 8, + max_steps = 10, + seed = "0x42" +)] +fn quint_core_qc_run() -> impl Driver { + CoreDriver::default() +} diff --git a/crates/app/stf/tests/quint_post_tx_connect.rs b/crates/app/stf/tests/quint_post_tx_connect.rs new file mode 100644 index 0000000..26251f6 --- /dev/null +++ b/crates/app/stf/tests/quint_post_tx_connect.rs @@ -0,0 +1,376 @@ +//! `quint_connect` pilot for `specs/stf_post_tx.qnt`. +//! +//! This uses `quint run --mbt` rather than `quint test`: with Quint 0.30.0, the +//! plain `test` traces do not include `mbt::actionTaken`/`mbt::nondetPicks`, +//! which `quint_connect` needs in order to replay steps. + +use anyhow::anyhow; +use evolve_core::runtime_api::ACCOUNT_IDENTIFIER_PREFIX; +use evolve_core::storage_api::{StorageSetRequest, STORAGE_ACCOUNT_ID}; +use evolve_core::{ + AccountCode, AccountId, BlockContext, Environment, EnvironmentQuery, ErrorCode, FungibleAsset, + InvokeRequest, InvokeResponse, Message, SdkResult, +}; +use evolve_stf::Stf; +use evolve_stf_traits::{Block as BlockTrait, PostTxExecution, Transaction, WritableKV}; +use quint_connect::*; +use serde::Deserialize; +use std::collections::{BTreeMap, BTreeSet}; + +mod quint_common; +use quint_common::{ + default_gas_config, extract_account_storage, register_account, CodeStore, InMemoryStorage, + NoopBegin, NoopEnd, NoopValidator, TestMsg, +}; + +const SPEC_ERR_EXECUTION: u16 = 200; +const SPEC_ERR_POST_TX: u16 = 999; +const TEST_SENDER: u64 = 200; +const WRITE_KEY: &[u8] = &[1]; +const WRITE_VALUE: &[u8] = &[11]; + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] +struct SpecState { + storage: BTreeMap, Vec>>, + accounts: BTreeSet, + last_result: SpecBlockResult, +} + +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)] +struct SpecBlockResult { + tx_results: Vec, + gas_used: u64, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] +struct SpecTxResult { + result: SpecResult, + gas_used: u64, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] +struct SpecResult { + ok: bool, + err_code: u64, +} + +impl State for SpecState { + fn from_driver(driver: &PostTxDriver) -> Result { + let accounts = driver.accounts(); + let storage = accounts + .iter() + .copied() + .map(|account| { + ( + account, + extract_account_storage(&driver.storage, AccountId::from_u64(account)) + .into_iter() + .map(|(key, value)| { + ( + key.into_iter().map(u64::from).collect(), + value.into_iter().map(u64::from).collect(), + ) + }) + .collect(), + ) + }) + .collect(); + + Ok(Self { + storage, + accounts, + last_result: driver.last_result.clone(), + }) + } +} + +#[derive(Clone, Debug)] +struct TestTx { + sender: AccountId, + recipient: AccountId, + request: InvokeRequest, + gas_limit: u64, + funds: Vec, + reject_post_tx: bool, +} + +impl Transaction for TestTx { + fn sender(&self) -> AccountId { + self.sender + } + fn recipient(&self) -> AccountId { + self.recipient + } + fn request(&self) -> &InvokeRequest { + &self.request + } + fn gas_limit(&self) -> u64 { + self.gas_limit + } + fn funds(&self) -> &[FungibleAsset] { + &self.funds + } + fn compute_identifier(&self) -> [u8; 32] { + [0u8; 32] + } +} + +#[derive(Clone)] +struct TestBlock { + height: u64, + time: u64, + txs: Vec, +} + +impl BlockTrait for TestBlock { + fn context(&self) -> BlockContext { + BlockContext::new(self.height, self.time) + } + fn txs(&self) -> &[TestTx] { + &self.txs + } +} + +#[derive(Default)] +struct RejectingPostTx; + +impl PostTxExecution for RejectingPostTx { + fn after_tx_executed( + tx: &TestTx, + _gas_consumed: u64, + tx_result: &SdkResult, + _env: &mut dyn Environment, + ) -> SdkResult<()> { + if tx.reject_post_tx && tx_result.is_ok() { + return Err(ErrorCode::new(SPEC_ERR_POST_TX)); + } + Ok(()) + } +} + +#[derive(Default)] +struct TestAccount; + +impl AccountCode for TestAccount { + fn identifier(&self) -> String { + "test_account".to_string() + } + fn schema(&self) -> evolve_core::schema::AccountSchema { + evolve_core::schema::AccountSchema::new("TestAccount", "test_account") + } + fn init( + &self, + _env: &mut dyn Environment, + _request: &InvokeRequest, + ) -> SdkResult { + InvokeResponse::new(&()) + } + fn execute( + &self, + env: &mut dyn Environment, + request: &InvokeRequest, + ) -> SdkResult { + let msg: TestMsg = request.get()?; + let set = StorageSetRequest { + key: msg.key.clone(), + value: Message::from_bytes(msg.value.clone()), + }; + env.do_exec(STORAGE_ACCOUNT_ID, &InvokeRequest::new(&set)?, vec![])?; + if msg.fail_after_write { + return Err(ErrorCode::new(SPEC_ERR_EXECUTION)); + } + InvokeResponse::new(&()) + } + fn query( + &self, + _env: &mut dyn EnvironmentQuery, + _request: &InvokeRequest, + ) -> SdkResult { + InvokeResponse::new(&()) + } +} + +#[derive(Default)] +struct PostTxDriver { + storage: InMemoryStorage, + last_result: SpecBlockResult, +} + +impl Driver for PostTxDriver { + type State = SpecState; + + fn step(&mut self, step: &Step) -> Result { + switch!(step { + init => { self.reset()?; }, + register_account(recipient: u64) => { self.register_account(recipient)?; }, + apply_registered_tx( + recipient: u64, + gas_limit: u64, + fail_e: bool, + reject_pt: bool + ) => { self.apply_registered_tx(recipient, gas_limit, fail_e, reject_pt)?; } + }) + } +} + +impl PostTxDriver { + fn reset(&mut self) -> Result { + self.storage = InMemoryStorage::default(); + self.last_result = SpecBlockResult::default(); + Ok(()) + } + + fn register_account(&mut self, recipient: u64) -> Result { + register_account( + &mut self.storage, + AccountId::from_u64(recipient), + "test_account", + ); + Ok(()) + } + + fn apply_registered_tx( + &mut self, + recipient: u64, + gas_limit: u64, + fail_e: bool, + reject_pt: bool, + ) -> Result { + let msg = TestMsg { + key: WRITE_KEY.to_vec(), + value: WRITE_VALUE.to_vec(), + fail_after_write: fail_e, + }; + let tx = TestTx { + sender: AccountId::from_u64(TEST_SENDER), + recipient: AccountId::from_u64(recipient), + request: InvokeRequest::new(&msg) + .map_err(|err| anyhow!("failed to serialize test request: {err:?}"))?, + gas_limit, + funds: vec![], + reject_post_tx: reject_pt, + }; + let block = TestBlock { + height: 1, + time: 0, + txs: vec![tx], + }; + + let stf = build_stf(); + let codes = build_codes(); + let (result, exec_state) = stf.apply_block(&self.storage, &codes, &block); + let changes = exec_state + .into_changes() + .map_err(|err| anyhow!("failed to extract execution changes: {err:?}"))?; + self.storage + .apply_changes(changes) + .map_err(|err| anyhow!("failed to apply execution changes: {err:?}"))?; + self.last_result = SpecBlockResult::from_real(&result); + + Ok(()) + } + + fn accounts(&self) -> BTreeSet { + self.storage + .data + .iter() + .filter(|(key, value)| { + key.first() == Some(&ACCOUNT_IDENTIFIER_PREFIX) + && Message::from_bytes((*value).clone()) + .get::() + .ok() + .as_deref() + == Some("test_account") + }) + .filter_map(|(key, _)| { + let bytes = key.get(1..33)?; + let account_bytes: [u8; 32] = bytes.try_into().ok()?; + Some(account_id_to_u64(AccountId::from_bytes(account_bytes))) + }) + .collect() + } +} + +fn build_codes() -> CodeStore { + let mut codes = CodeStore::new(); + codes.add_code(TestAccount); + codes +} + +fn build_stf( +) -> Stf, NoopValidator, NoopEnd, RejectingPostTx> { + Stf::new( + NoopBegin::::default(), + NoopEnd, + NoopValidator::::default(), + RejectingPostTx, + default_gas_config(), + ) +} + +fn account_id_to_u64(account: AccountId) -> u64 { + let bytes = account.as_bytes(); + u64::from_be_bytes( + bytes[24..32] + .try_into() + .expect("account id must be 32 bytes"), + ) +} + +impl SpecBlockResult { + fn from_real(result: &evolve_stf::results::BlockResult) -> Self { + Self { + tx_results: result + .tx_results + .iter() + .map(SpecTxResult::from_real) + .collect(), + gas_used: result.gas_used, + } + } +} + +impl SpecTxResult { + fn from_real(result: &evolve_stf::results::TxResult) -> Self { + Self { + result: SpecResult::from_real(&result.response), + gas_used: result.gas_used, + } + } +} + +impl SpecResult { + fn from_real(result: &SdkResult) -> Self { + match result { + Ok(_) => Self { + ok: true, + err_code: 0, + }, + Err(err) => Self { + ok: false, + err_code: normalize_error_code(err.id), + }, + } + } +} + +fn normalize_error_code(err_id: u16) -> u64 { + if err_id == evolve_stf::ERR_OUT_OF_GAS.id { + 0x01 + } else { + err_id as u64 + } +} + +#[quint_run( + spec = "../../../specs/stf_post_tx.qnt", + main = "stf_post_tx", + init = "init", + step = "qc_step", + max_samples = 8, + max_steps = 8, + seed = "0x42" +)] +fn quint_post_tx_qc_run() -> impl Driver { + PostTxDriver::default() +} diff --git a/justfile b/justfile index 892f0d5..1188aa1 100644 --- a/justfile +++ b/justfile @@ -230,13 +230,15 @@ spec-test-core: rm -f specs/traces/*.itf.json quint test specs/stf_core.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json" cargo test -p evolve_stf --test quint_conformance + cargo test -p evolve_stf --test quint_core_connect -# Run extended STF model specs (currently Quint-only) +# Run extended STF model specs (ITF + `quint_connect` where available) [group('spec')] spec-test-extended: quint test specs/stf_post_tx.qnt quint test specs/stf_post_tx.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json" cargo test -p evolve_stf --test quint_post_tx_conformance + cargo test -p evolve_stf --test quint_post_tx_connect quint test specs/stf_call_depth.qnt quint test specs/stf_call_depth.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json" cargo test -p evolve_stf --test quint_call_depth_conformance diff --git a/specs/stf_core.qnt b/specs/stf_core.qnt index c0f3c9c..58c8702 100644 --- a/specs/stf_core.qnt +++ b/specs/stf_core.qnt @@ -104,16 +104,16 @@ module stf { // val MC_ACCOUNTS: Set[AccountId] = Set(100, 101, 200, 201) - val MC_GAS_LIMITS: Set[int] = Set(1, 25, 100, 10000) - val MC_BLOCK_GAS_LIMITS: Set[int] = Set(30, 1000, 1000000) + val MC_GAS_LIMITS: Set[int] = Set(1, 40, 100, 10000) + val MC_BLOCK_GAS_LIMITS: Set[int] = Set(30, 70, 1000, 1000000) // // === Pure helpers === // - /// AccountId is u128 in the Rust implementation = 16 bytes. + /// AccountId is canonical 32-byte storage in the Rust implementation. /// The real storage key is `account_id_bytes ++ user_key`. - val ACCOUNT_ID_BYTE_SIZE: int = 16 + val ACCOUNT_ID_BYTE_SIZE: int = 32 val ACCOUNT_CODE_PREFIX_SIZE: int = 1 val BORSH_LEN_PREFIX_SIZE: int = 4 val TEST_ACCOUNT_CODE_ID_LEN: int = 12 // "test_account" @@ -126,7 +126,7 @@ module stf { /// Gas cost for sender bootstrap account-code registration. /// Real runtime charges storage set gas for: - /// key = ACCOUNT_IDENTIFIER_PREFIX (1 byte) ++ account_id (16 bytes) + /// key = ACCOUNT_IDENTIFIER_PREFIX (1 byte) ++ account_id (32 bytes) /// value = Borsh-encoded code id string ("test_account" => 4-byte len + 12 bytes) pure def bootstrap_gas_cost(config: GasConfig): int = config.set_charge * ( @@ -155,27 +155,40 @@ module stf { registered: Set[AccountId], ): { result: TxResult, store: AccountId -> (Key -> Value), accounts: Set[AccountId] } = // Phase 0: Bootstrap sender if needed + val bootstrap_gas = bootstrap_gas_cost(config) val bootstrap_result = if (tx.needs_bootstrap and not(registered.contains(tx.sender))) - if (tx.fail_bootstrap) - // Bootstrap failed - { ok: false, accounts: registered, gas: bootstrap_gas_cost(config) } + if (bootstrap_gas > tx.gas_limit) + // Bootstrap consumes storage gas first; if that exceeds the tx budget, + // the runtime returns out-of-gas before running account init. + { ok: false, err_code: ERR_OUT_OF_GAS, store: account_store, accounts: registered, gas: 0 } + else if (tx.fail_bootstrap) + // The runtime persists account registration before init runs, so an + // init failure still leaves the sender registered with empty storage. + { + ok: false, + err_code: ERR_BOOTSTRAP, + store: account_store.put(tx.sender, Map()), + accounts: registered.union(Set(tx.sender)), + gas: bootstrap_gas, + } else - // Bootstrap succeeds: register sender - { ok: true, accounts: registered.union(Set(tx.sender)), gas: bootstrap_gas_cost(config) } + // Bootstrap succeeds: register sender and expose an empty user-store. + { ok: true, err_code: 0, store: account_store.put(tx.sender, Map()), accounts: registered.union(Set(tx.sender)), gas: bootstrap_gas } else // No bootstrap needed or already registered - { ok: true, accounts: registered, gas: 0 } + { ok: true, err_code: 0, store: account_store, accounts: registered, gas: 0 } if (not(bootstrap_result.ok)) - // Bootstrap failed -> return error, no state changes + // Bootstrap failed -> return the runtime-visible post-bootstrap state. { - result: { result: Err(ERR_BOOTSTRAP), gas_used: bootstrap_result.gas }, - store: account_store, - accounts: registered, + result: { result: Err(bootstrap_result.err_code), gas_used: bootstrap_result.gas }, + store: bootstrap_result.store, + accounts: bootstrap_result.accounts, } else val current_accounts = bootstrap_result.accounts + val current_store = bootstrap_result.store val gas_after_bootstrap = bootstrap_result.gas // Phase 1: Validation @@ -185,7 +198,7 @@ module stf { if (tx.fail_validate or not(current_accounts.contains(tx.sender))) { result: { result: Err(ERR_VALIDATION), gas_used: gas_after_bootstrap }, - store: account_store, + store: current_store, accounts: current_accounts, } else @@ -198,25 +211,25 @@ module stf { // a failed consume_gas call. So gas_used stays at its pre-call value. { result: { result: Err(ERR_OUT_OF_GAS), gas_used: gas_after_bootstrap }, - store: account_store, + store: current_store, accounts: current_accounts, } else if (tx.fail_execute) // Execution error after write -> rollback { result: { result: Err(ERR_EXECUTION), gas_used: total_gas }, - store: account_store, + store: current_store, accounts: current_accounts, } else // Phase 3: Write succeeds -> apply to storage val recipient_store = - if (account_store.keys().contains(tx.recipient)) - account_store.get(tx.recipient) + if (current_store.keys().contains(tx.recipient)) + current_store.get(tx.recipient) else Map() val updated_recipient = recipient_store.put(tx.write_key, tx.write_value) - val updated_store = account_store.put(tx.recipient, updated_recipient) + val updated_store = current_store.put(tx.recipient, updated_recipient) { result: { result: OK, gas_used: total_gas }, store: updated_store, @@ -295,6 +308,40 @@ module stf { last_block_tx_count' = block.txs.length(), } + /// Driver-friendly block wrapper for `quint_connect`. + /// The STF can only execute against accounts with registered code, so this + /// wrapper aligns the model with the real runtime by requiring a registered recipient. + action apply_registered_block( + sender: AccountId, + recipient: AccountId, + gas_limit: int, + block_gas: int, + fail_v: bool, + fail_e: bool, + needs_b: bool, + fail_b: bool, + ): bool = + val tx: Tx = { + sender: sender, + recipient: recipient, + gas_limit: gas_limit, + write_key: K1, + write_value: V1, + fail_validate: fail_v, + fail_execute: fail_e, + needs_bootstrap: needs_b, + fail_bootstrap: fail_b, + } + all { + accounts.contains(recipient), + apply_block({ + height: block_height + 1, + time: 0, + txs: [tx], + gas_limit: block_gas, + }), + } + /// Register an account (e.g., during genesis). action register_account(id: AccountId): bool = all { not(accounts.contains(id)), @@ -393,6 +440,24 @@ module stf { } } + /// Alternate MBT step for `quint_connect`: keeps the same exploration + /// parameters, but only dispatches to runtime-valid block applications. + action qc_step = { + nondet id = MC_ACCOUNTS.oneOf() + nondet sender = MC_ACCOUNTS.oneOf() + nondet recipient = MC_ACCOUNTS.oneOf() + nondet gas_limit = MC_GAS_LIMITS.oneOf() + nondet block_gas = MC_BLOCK_GAS_LIMITS.oneOf() + nondet fail_v = Set(true, false).oneOf() + nondet fail_e = Set(true, false).oneOf() + nondet needs_b = Set(true, false).oneOf() + nondet fail_b = Set(true, false).oneOf() + any { + apply_registered_block(sender, recipient, gas_limit, block_gas, fail_v, fail_e, needs_b, fail_b), + register_account(id), + } + } + // // === Test helpers === // @@ -517,26 +582,26 @@ module stf { } /// Test: block gas limit causes transactions to be skipped. - /// With Key=[1], Value=[11]: gas_cost = 1*(16+1+1+1+1) = 20 per tx. - /// tx1.gas_limit=25, tx2.gas_limit=25. Block gas_limit=30. - /// Pre-check: cumulative(0) + tx1.gas_limit(25) = 25 <= 30 -> execute tx1 (uses 20 gas). - /// Pre-check: cumulative(20) + tx2.gas_limit(25) = 45 > 30 -> skip tx2. + /// With Key=[1], Value=[11]: gas_cost = 1*(32+1+1+1+1) = 36 per tx. + /// tx1.gas_limit=40, tx2.gas_limit=40. Block gas_limit=70. + /// Pre-check: cumulative(0) + tx1.gas_limit(40) = 40 <= 70 -> execute tx1 (uses 36 gas). + /// Pre-check: cumulative(36) + tx2.gas_limit(40) = 76 > 70 -> skip tx2. run blockGasLimitTest = { val tx1: Tx = { - sender: 100, recipient: 100, gas_limit: 25, + sender: 100, recipient: 100, gas_limit: 40, write_key: K1, write_value: V1, fail_validate: false, fail_execute: false, needs_bootstrap: false, fail_bootstrap: false, } val tx2: Tx = { - sender: 100, recipient: 100, gas_limit: 25, + sender: 100, recipient: 100, gas_limit: 40, write_key: K2, write_value: V2, fail_validate: false, fail_execute: false, needs_bootstrap: false, fail_bootstrap: false, } init .then(register_account(100)) - .then(apply_block({ height: 1, time: 0, txs: [tx1, tx2], gas_limit: 30 })) + .then(apply_block({ height: 1, time: 0, txs: [tx1, tx2], gas_limit: 70 })) .then(all { assert(last_result.tx_results.length() == 1), assert(last_result.txs_skipped == 1), @@ -633,8 +698,8 @@ module stf { .then(all { assert(last_result.tx_results[0].result.ok == false), assert(last_result.tx_results[0].result.err_code == ERR_BOOTSTRAP), - // Sender should NOT be registered - assert(not(accounts.contains(200))), + // Registration persists even though init failed. + assert(accounts.contains(200)), assert(result_completeness), stutter, }) diff --git a/specs/stf_post_tx.qnt b/specs/stf_post_tx.qnt index 37e255e..d943f49 100644 --- a/specs/stf_post_tx.qnt +++ b/specs/stf_post_tx.qnt @@ -58,7 +58,7 @@ module stf_post_tx { val MC_ACCOUNTS: Set[AccountId] = Set(100, 101) val gas_config: GasConfig = { set_charge: 1 } - val ACCOUNT_ID_BYTE_SIZE: int = 16 + val ACCOUNT_ID_BYTE_SIZE: int = 32 pure def write_gas_cost(config: GasConfig, key: Key, value: Value): int = config.set_charge * (ACCOUNT_ID_BYTE_SIZE + key.length() + 1 + value.length() + 1) @@ -148,6 +148,26 @@ module stf_post_tx { last_result' = out.result, } + /// Driver-friendly wrapper for MBT: only execute txs against registered accounts. + action apply_registered_tx( + recipient: AccountId, + gas_limit: int, + fail_e: bool, + reject_pt: bool, + ): bool = + val tx: Tx = { + recipient: recipient, + gas_limit: gas_limit, + write_key: K1, + write_value: V1, + fail_execute: fail_e, + reject_post_tx: reject_pt, + } + all { + accounts.contains(recipient), + apply_txs([tx]), + } + action stutter: bool = all { storage' = storage, accounts' = accounts, @@ -177,6 +197,19 @@ module stf_post_tx { } } + /// Alternate MBT step for `quint_connect`: aligns with STF requirement that + /// execution targets a registered account code. + action qc_step = { + nondet recipient = MC_ACCOUNTS.oneOf() + nondet gas_limit = Set(1, 100, 10000).oneOf() + nondet fail_e = Set(true, false).oneOf() + nondet reject_pt = Set(true, false).oneOf() + any { + apply_registered_tx(recipient, gas_limit, fail_e, reject_pt), + register_account(recipient), + } + } + // // === Invariants === // @@ -218,6 +251,52 @@ module stf_post_tx { val K2: Key = [2] val V2: Value = [12] + // + // === Named test expectation actions === + // + + action expect_post_tx_rejects_but_keeps_state: bool = all { + assert(last_result.tx_results.length() == 1), + assert(last_result.tx_results[0].result.ok == false), + assert(last_result.tx_results[0].result.err_code == ERR_POST_TX), + assert(storage.get(100).get(K1) == V1), + assert(gas_accounting), + stutter, + } + + action expect_post_tx_does_not_mask_exec_failure: bool = all { + assert(last_result.tx_results.length() == 1), + assert(last_result.tx_results[0].result.ok == false), + assert(last_result.tx_results[0].result.err_code == ERR_EXECUTION), + assert(storage.get(100) == Map()), + assert(gas_accounting), + stutter, + } + + action expect_happy_path: bool = all { + assert(last_result.tx_results[0].result.ok == true), + assert(storage.get(100).get(K1) == V1), + assert(gas_accounting), + stutter, + } + + action expect_mixed_post_tx: bool = all { + assert(last_result.tx_results.length() == 2), + assert(last_result.tx_results[0].result.err_code == ERR_POST_TX), + assert(storage.get(100).get(K1) == V1), + assert(last_result.tx_results[1].result.ok == true), + assert(storage.get(100).get(K2) == V2), + assert(gas_accounting), + stutter, + } + + action expect_out_of_gas_ignores_post_tx: bool = all { + assert(last_result.tx_results[0].result.err_code == ERR_OUT_OF_GAS), + assert(storage.get(100) == Map()), + assert(gas_accounting), + stutter, + } + // // === Tests === // @@ -236,15 +315,7 @@ module stf_post_tx { init .then(register_account(100)) .then(apply_txs([tx])) - .then(all { - assert(last_result.tx_results.length() == 1), - assert(last_result.tx_results[0].result.ok == false), - assert(last_result.tx_results[0].result.err_code == ERR_POST_TX), - // State change persists despite post-tx error - assert(storage.get(100).get(K1) == V1), - assert(gas_accounting), - stutter, - }) + .then(expect_post_tx_rejects_but_keeps_state) } /// Post-tx does not mask execution failure (exec error takes precedence). @@ -260,16 +331,7 @@ module stf_post_tx { init .then(register_account(100)) .then(apply_txs([tx])) - .then(all { - assert(last_result.tx_results.length() == 1), - assert(last_result.tx_results[0].result.ok == false), - // Execution error, NOT post-tx error - assert(last_result.tx_results[0].result.err_code == ERR_EXECUTION), - // No state changes (execution was rolled back) - assert(storage.get(100) == Map()), - assert(gas_accounting), - stutter, - }) + .then(expect_post_tx_does_not_mask_exec_failure) } /// Happy path: no post-tx rejection, execution succeeds. @@ -285,12 +347,7 @@ module stf_post_tx { init .then(register_account(100)) .then(apply_txs([tx])) - .then(all { - assert(last_result.tx_results[0].result.ok == true), - assert(storage.get(100).get(K1) == V1), - assert(gas_accounting), - stutter, - }) + .then(expect_happy_path) } /// Mixed block: one tx with post-tx rejection, one without. @@ -314,17 +371,7 @@ module stf_post_tx { init .then(register_account(100)) .then(apply_txs([tx_reject, tx_ok])) - .then(all { - assert(last_result.tx_results.length() == 2), - // First tx: post-tx rejected, but state persists - assert(last_result.tx_results[0].result.err_code == ERR_POST_TX), - assert(storage.get(100).get(K1) == V1), - // Second tx: fully succeeds - assert(last_result.tx_results[1].result.ok == true), - assert(storage.get(100).get(K2) == V2), - assert(gas_accounting), - stutter, - }) + .then(expect_mixed_post_tx) } /// Out-of-gas: post-tx flag is irrelevant since execution never completes. @@ -340,11 +387,6 @@ module stf_post_tx { init .then(register_account(100)) .then(apply_txs([tx])) - .then(all { - assert(last_result.tx_results[0].result.err_code == ERR_OUT_OF_GAS), - assert(storage.get(100) == Map()), - assert(gas_accounting), - stutter, - }) + .then(expect_out_of_gas_ignores_post_tx) } } diff --git a/specs/traces/out_blockGasLimitTest_5.itf.json b/specs/traces/out_blockGasLimitTest_5.itf.json index 2873af0..8dc82c6 100644 --- a/specs/traces/out_blockGasLimitTest_5.itf.json +++ b/specs/traces/out_blockGasLimitTest_5.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557717},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190152},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_bootstrapFailureTest_8.itf.json b/specs/traces/out_bootstrapFailureTest_8.itf.json index cdcb19c..ebfdb53 100644 --- a/specs/traces/out_bootstrapFailureTest_8.itf.json +++ b/specs/traces/out_bootstrapFailureTest_8.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557728},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"35"},"tx_results":[{"gas_used":{"#bigint":"35"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"35"},"tx_results":[{"gas_used":{"#bigint":"35"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190168},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"51"},"tx_results":[{"gas_used":{"#bigint":"51"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}],[{"#bigint":"200"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"51"},"tx_results":[{"gas_used":{"#bigint":"51"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}],[{"#bigint":"200"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_bootstrapTest_7.itf.json b/specs/traces/out_bootstrapTest_7.itf.json index 7de9fe3..bd36f89 100644 --- a/specs/traces/out_bootstrapTest_7.itf.json +++ b/specs/traces/out_bootstrapTest_7.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557727},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"55"},"tx_results":[{"gas_used":{"#bigint":"55"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"55"},"tx_results":[{"gas_used":{"#bigint":"55"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190167},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"87"},"tx_results":[{"gas_used":{"#bigint":"87"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}],[{"#bigint":"200"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"87"},"tx_results":[{"gas_used":{"#bigint":"87"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}],[{"#bigint":"200"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_cannotReturnFromEmptyTest_4.itf.json b/specs/traces/out_cannotReturnFromEmptyTest_4.itf.json index 226104b..8b2e3d8 100644 --- a/specs/traces/out_cannotReturnFromEmptyTest_4.itf.json +++ b/specs/traces/out_cannotReturnFromEmptyTest_4.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:42 GMT+0100 (Central European Standard Time)","timestamp":1772033562682},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:33 GMT+0100 (Central European Standard Time)","timestamp":1773064233321},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1}}]} \ No newline at end of file diff --git a/specs/traces/out_emptyBlockTest_0.itf.json b/specs/traces/out_emptyBlockTest_0.itf.json index a3a98e5..ae6c345 100644 --- a/specs/traces/out_emptyBlockTest_0.itf.json +++ b/specs/traces/out_emptyBlockTest_0.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557711},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190136},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_executionFailureRollbackTest_3.itf.json b/specs/traces/out_executionFailureRollbackTest_3.itf.json index bac590b..73f0137 100644 --- a/specs/traces/out_executionFailureRollbackTest_3.itf.json +++ b/specs/traces/out_executionFailureRollbackTest_3.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557715},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190147},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_fullUnwindTest_3.itf.json b/specs/traces/out_fullUnwindTest_3.itf.json index d0755e7..b62df2d 100644 --- a/specs/traces/out_fullUnwindTest_3.itf.json +++ b/specs/traces/out_fullUnwindTest_3.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:42 GMT+0100 (Central European Standard Time)","timestamp":1772033562681},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"},{"#bigint":"2"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":3},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":4},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":5},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:33 GMT+0100 (Central European Standard Time)","timestamp":1773064233321},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"},{"#bigint":"2"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":3},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":4},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":5},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file diff --git a/specs/traces/out_happyPathTest_2.itf.json b/specs/traces/out_happyPathTest_2.itf.json index 59be5f9..4725664 100644 --- a/specs/traces/out_happyPathTest_2.itf.json +++ b/specs/traces/out_happyPathTest_2.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:43 GMT+0100 (Central European Standard Time)","timestamp":1772033563574},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:19 GMT+0100 (Central European Standard Time)","timestamp":1773064219766},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_mixedOutcomesTest_6.itf.json b/specs/traces/out_mixedOutcomesTest_6.itf.json index 08a7267..6e6c469 100644 --- a/specs/traces/out_mixedOutcomesTest_6.itf.json +++ b/specs/traces/out_mixedOutcomesTest_6.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557719},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"4"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"4"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190153},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"4"},"last_result":{"gas_used":{"#bigint":"72"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"4"},"last_result":{"gas_used":{"#bigint":"72"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_mixedPostTxTest_3.itf.json b/specs/traces/out_mixedPostTxTest_3.itf.json index d368482..0b594a9 100644 --- a/specs/traces/out_mixedPostTxTest_3.itf.json +++ b/specs/traces/out_mixedPostTxTest_3.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:43 GMT+0100 (Central European Standard Time)","timestamp":1772033563582},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"999"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"999"},"ok":false}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:19 GMT+0100 (Central European Standard Time)","timestamp":1773064219774},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"72"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"999"},"ok":false}},{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"72"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"999"},"ok":false}},{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_multiBlockTest_10.itf.json b/specs/traces/out_multiBlockTest_10.itf.json index 69724d0..1df81f9 100644 --- a/specs/traces/out_multiBlockTest_10.itf.json +++ b/specs/traces/out_multiBlockTest_10.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557729},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}},{"#meta":{"index":4},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190171},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}},{"#meta":{"index":4},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_nestedCallsTest_1.itf.json b/specs/traces/out_nestedCallsTest_1.itf.json index 2a20845..242dc64 100644 --- a/specs/traces/out_nestedCallsTest_1.itf.json +++ b/specs/traces/out_nestedCallsTest_1.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:42 GMT+0100 (Central European Standard Time)","timestamp":1772033562680},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"},{"#bigint":"2"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":3},"call_stack":[{"#bigint":"1"},{"#bigint":"2"},{"#bigint":"3"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":4},"call_stack":[{"#bigint":"1"},{"#bigint":"2"},{"#bigint":"3"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:33 GMT+0100 (Central European Standard Time)","timestamp":1773064233319},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"},{"#bigint":"2"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":3},"call_stack":[{"#bigint":"1"},{"#bigint":"2"},{"#bigint":"3"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":4},"call_stack":[{"#bigint":"1"},{"#bigint":"2"},{"#bigint":"3"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file diff --git a/specs/traces/out_outOfGasIgnoresPostTxTest_4.itf.json b/specs/traces/out_outOfGasIgnoresPostTxTest_4.itf.json index b9c41f2..c9c9847 100644 --- a/specs/traces/out_outOfGasIgnoresPostTxTest_4.itf.json +++ b/specs/traces/out_outOfGasIgnoresPostTxTest_4.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:43 GMT+0100 (Central European Standard Time)","timestamp":1772033563583},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:19 GMT+0100 (Central European Standard Time)","timestamp":1773064219775},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_outOfGasTest_4.itf.json b/specs/traces/out_outOfGasTest_4.itf.json index 9a1a57f..c6a8fd2 100644 --- a/specs/traces/out_outOfGasTest_4.itf.json +++ b/specs/traces/out_outOfGasTest_4.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557716},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190150},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_overwriteTest_11.itf.json b/specs/traces/out_overwriteTest_11.itf.json index 2495fb3..aabbdc8 100644 --- a/specs/traces/out_overwriteTest_11.itf.json +++ b/specs/traces/out_overwriteTest_11.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557730},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"40"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190172},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"72"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"72"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_postTxDoesNotMaskExecFailureTest_1.itf.json b/specs/traces/out_postTxDoesNotMaskExecFailureTest_1.itf.json index 8d3f342..7eefcf4 100644 --- a/specs/traces/out_postTxDoesNotMaskExecFailureTest_1.itf.json +++ b/specs/traces/out_postTxDoesNotMaskExecFailureTest_1.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:43 GMT+0100 (Central European Standard Time)","timestamp":1772033563573},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"200"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:19 GMT+0100 (Central European Standard Time)","timestamp":1773064219764},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"200"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"200"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_postTxRejectsButKeepsStateTest_0.itf.json b/specs/traces/out_postTxRejectsButKeepsStateTest_0.itf.json index cc5a5b1..876e970 100644 --- a/specs/traces/out_postTxRejectsButKeepsStateTest_0.itf.json +++ b/specs/traces/out_postTxRejectsButKeepsStateTest_0.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:43 GMT+0100 (Central European Standard Time)","timestamp":1772033563572},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"999"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"999"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:19 GMT+0100 (Central European Standard Time)","timestamp":1773064219763},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"999"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"999"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_recursiveCallsTest_5.itf.json b/specs/traces/out_recursiveCallsTest_5.itf.json index e4d6e04..199cf4c 100644 --- a/specs/traces/out_recursiveCallsTest_5.itf.json +++ b/specs/traces/out_recursiveCallsTest_5.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:42 GMT+0100 (Central European Standard Time)","timestamp":1772033562682},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"},{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":3},"call_stack":[{"#bigint":"1"},{"#bigint":"1"},{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":4},"call_stack":[{"#bigint":"1"},{"#bigint":"1"},{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:33 GMT+0100 (Central European Standard Time)","timestamp":1773064233321},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"},{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":3},"call_stack":[{"#bigint":"1"},{"#bigint":"1"},{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":4},"call_stack":[{"#bigint":"1"},{"#bigint":"1"},{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file diff --git a/specs/traces/out_returnUnwindsStackTest_2.itf.json b/specs/traces/out_returnUnwindsStackTest_2.itf.json index 7ab15ea..4a2c904 100644 --- a/specs/traces/out_returnUnwindsStackTest_2.itf.json +++ b/specs/traces/out_returnUnwindsStackTest_2.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:42 GMT+0100 (Central European Standard Time)","timestamp":1772033562680},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"},{"#bigint":"2"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":3},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":4},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:33 GMT+0100 (Central European Standard Time)","timestamp":1773064233320},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"},{"#bigint":"2"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":3},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":4},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file diff --git a/specs/traces/out_singleCallTest_0.itf.json b/specs/traces/out_singleCallTest_0.itf.json index b0c7e85..a647272 100644 --- a/specs/traces/out_singleCallTest_0.itf.json +++ b/specs/traces/out_singleCallTest_0.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:42 GMT+0100 (Central European Standard Time)","timestamp":1772033562678},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:33 GMT+0100 (Central European Standard Time)","timestamp":1773064233318},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file diff --git a/specs/traces/out_successfulTxTest_1.itf.json b/specs/traces/out_successfulTxTest_1.itf.json index d4bc6e3..0755c4c 100644 --- a/specs/traces/out_successfulTxTest_1.itf.json +++ b/specs/traces/out_successfulTxTest_1.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557713},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"20"},"tx_results":[{"gas_used":{"#bigint":"20"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190138},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_unregisteredSenderTest_9.itf.json b/specs/traces/out_unregisteredSenderTest_9.itf.json index c08a5d6..e83a66f 100644 --- a/specs/traces/out_unregisteredSenderTest_9.itf.json +++ b/specs/traces/out_unregisteredSenderTest_9.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557728},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190170},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_validationFailureTest_2.itf.json b/specs/traces/out_validationFailureTest_2.itf.json index ba3ac70..a90659c 100644 --- a/specs/traces/out_validationFailureTest_2.itf.json +++ b/specs/traces/out_validationFailureTest_2.itf.json @@ -1 +1 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Wed Feb 25 2026 16:32:37 GMT+0100 (Central European Standard Time)","timestamp":1772033557714},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file +{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190142},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file From 51c60eb692b6aa9949db8d83795085bd8d0249f5 Mon Sep 17 00:00:00 2001 From: tac0turtle Date: Mon, 9 Mar 2026 15:46:45 +0100 Subject: [PATCH 9/9] quint connect migration --- crates/app/stf/Cargo.toml | 1 - .../stf/tests/quint_call_depth_conformance.rs | 265 ------- crates/app/stf/tests/quint_common.rs | 126 ---- crates/app/stf/tests/quint_conformance.rs | 691 ------------------ .../stf/tests/quint_post_tx_conformance.rs | 360 --------- justfile | 18 +- specs/traces/out_blockGasLimitTest_5.itf.json | 1 - .../out_bootstrapFailureTest_8.itf.json | 1 - specs/traces/out_bootstrapTest_7.itf.json | 1 - .../out_cannotReturnFromEmptyTest_4.itf.json | 1 - specs/traces/out_emptyBlockTest_0.itf.json | 1 - ...ut_executionFailureRollbackTest_3.itf.json | 1 - specs/traces/out_fullUnwindTest_3.itf.json | 1 - specs/traces/out_happyPathTest_2.itf.json | 1 - specs/traces/out_mixedOutcomesTest_6.itf.json | 1 - specs/traces/out_mixedPostTxTest_3.itf.json | 1 - specs/traces/out_multiBlockTest_10.itf.json | 1 - specs/traces/out_nestedCallsTest_1.itf.json | 1 - .../out_outOfGasIgnoresPostTxTest_4.itf.json | 1 - specs/traces/out_outOfGasTest_4.itf.json | 1 - specs/traces/out_overwriteTest_11.itf.json | 1 - ...ostTxDoesNotMaskExecFailureTest_1.itf.json | 1 - ..._postTxRejectsButKeepsStateTest_0.itf.json | 1 - .../traces/out_recursiveCallsTest_5.itf.json | 1 - .../out_returnUnwindsStackTest_2.itf.json | 1 - specs/traces/out_singleCallTest_0.itf.json | 1 - specs/traces/out_successfulTxTest_1.itf.json | 1 - .../out_unregisteredSenderTest_9.itf.json | 1 - .../out_validationFailureTest_2.itf.json | 1 - 29 files changed, 2 insertions(+), 1482 deletions(-) delete mode 100644 crates/app/stf/tests/quint_call_depth_conformance.rs delete mode 100644 crates/app/stf/tests/quint_conformance.rs delete mode 100644 crates/app/stf/tests/quint_post_tx_conformance.rs delete mode 100644 specs/traces/out_blockGasLimitTest_5.itf.json delete mode 100644 specs/traces/out_bootstrapFailureTest_8.itf.json delete mode 100644 specs/traces/out_bootstrapTest_7.itf.json delete mode 100644 specs/traces/out_cannotReturnFromEmptyTest_4.itf.json delete mode 100644 specs/traces/out_emptyBlockTest_0.itf.json delete mode 100644 specs/traces/out_executionFailureRollbackTest_3.itf.json delete mode 100644 specs/traces/out_fullUnwindTest_3.itf.json delete mode 100644 specs/traces/out_happyPathTest_2.itf.json delete mode 100644 specs/traces/out_mixedOutcomesTest_6.itf.json delete mode 100644 specs/traces/out_mixedPostTxTest_3.itf.json delete mode 100644 specs/traces/out_multiBlockTest_10.itf.json delete mode 100644 specs/traces/out_nestedCallsTest_1.itf.json delete mode 100644 specs/traces/out_outOfGasIgnoresPostTxTest_4.itf.json delete mode 100644 specs/traces/out_outOfGasTest_4.itf.json delete mode 100644 specs/traces/out_overwriteTest_11.itf.json delete mode 100644 specs/traces/out_postTxDoesNotMaskExecFailureTest_1.itf.json delete mode 100644 specs/traces/out_postTxRejectsButKeepsStateTest_0.itf.json delete mode 100644 specs/traces/out_recursiveCallsTest_5.itf.json delete mode 100644 specs/traces/out_returnUnwindsStackTest_2.itf.json delete mode 100644 specs/traces/out_singleCallTest_0.itf.json delete mode 100644 specs/traces/out_successfulTxTest_1.itf.json delete mode 100644 specs/traces/out_unregisteredSenderTest_9.itf.json delete mode 100644 specs/traces/out_validationFailureTest_2.itf.json diff --git a/crates/app/stf/Cargo.toml b/crates/app/stf/Cargo.toml index d457240..fc756db 100644 --- a/crates/app/stf/Cargo.toml +++ b/crates/app/stf/Cargo.toml @@ -20,7 +20,6 @@ anyhow = "1" proptest = "1.4" quint-connect = "0.1.1" serde = { version = "1", features = ["derive"] } -serde_json = "1" [lints] workspace = true diff --git a/crates/app/stf/tests/quint_call_depth_conformance.rs b/crates/app/stf/tests/quint_call_depth_conformance.rs deleted file mode 100644 index dc7a08e..0000000 --- a/crates/app/stf/tests/quint_call_depth_conformance.rs +++ /dev/null @@ -1,265 +0,0 @@ -//! Conformance tests: replay Quint ITF traces for stf_call_depth.qnt. -//! -//! The Quint spec models nested do_exec calls with a call_stack. This -//! conformance test uses a RecursiveAccount that calls itself N times, -//! verifying that the real STF matches the spec's depth enforcement. -//! -//! Run: -//! `quint test --main=stf_call_depth specs/stf_call_depth.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json"` -//! `cargo test -p evolve_stf --test quint_call_depth_conformance` - -use borsh::{BorshDeserialize, BorshSerialize}; -use evolve_core::{ - AccountCode, AccountId, BlockContext, Environment, EnvironmentQuery, FungibleAsset, - InvokableMessage, InvokeRequest, InvokeResponse, SdkResult, -}; -use evolve_stf::Stf; -use evolve_stf_traits::{Block as BlockTrait, Transaction}; -use serde::Deserialize; -use std::path::Path; - -mod quint_common; -use quint_common::{ - find_single_trace_file, read_itf_trace, register_account, CodeStore, InMemoryStorage, - ItfBigInt, NoopBegin, NoopEnd, NoopPostTx, NoopValidator, -}; - -#[derive(Deserialize)] -struct ItfTrace { - states: Vec, -} - -#[derive(Deserialize)] -struct ItfState { - #[allow(dead_code)] - call_stack: Vec, - last_result: ItfResult, -} - -#[derive(Deserialize)] -struct ItfResult { - ok: bool, - #[allow(dead_code)] - err_code: ItfBigInt, -} - -#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] -struct RecurseMsg { - remaining: u16, -} - -impl InvokableMessage for RecurseMsg { - const FUNCTION_IDENTIFIER: u64 = 1; - const FUNCTION_IDENTIFIER_NAME: &'static str = "recurse"; -} - -#[derive(Clone, Debug)] -struct TestTx { - sender: AccountId, - recipient: AccountId, - request: InvokeRequest, - gas_limit: u64, - funds: Vec, -} - -impl Transaction for TestTx { - fn sender(&self) -> AccountId { - self.sender - } - fn recipient(&self) -> AccountId { - self.recipient - } - fn request(&self) -> &InvokeRequest { - &self.request - } - fn gas_limit(&self) -> u64 { - self.gas_limit - } - fn funds(&self) -> &[FungibleAsset] { - &self.funds - } - fn compute_identifier(&self) -> [u8; 32] { - [0u8; 32] - } -} - -#[derive(Clone)] -struct TestBlock { - height: u64, - time: u64, - txs: Vec, -} - -impl BlockTrait for TestBlock { - fn context(&self) -> BlockContext { - BlockContext::new(self.height, self.time) - } - fn txs(&self) -> &[TestTx] { - &self.txs - } - fn gas_limit(&self) -> u64 { - 1_000_000 - } -} - -#[derive(Default)] -struct RecursiveAccount; - -impl AccountCode for RecursiveAccount { - fn identifier(&self) -> String { - "recursive".to_string() - } - fn schema(&self) -> evolve_core::schema::AccountSchema { - evolve_core::schema::AccountSchema::new("RecursiveAccount", "recursive") - } - fn init( - &self, - _env: &mut dyn Environment, - _request: &InvokeRequest, - ) -> SdkResult { - InvokeResponse::new(&()) - } - fn execute( - &self, - env: &mut dyn Environment, - request: &InvokeRequest, - ) -> SdkResult { - let msg: RecurseMsg = request.get()?; - if msg.remaining == 0 { - return InvokeResponse::new(&()); - } - let next = RecurseMsg { - remaining: msg.remaining - 1, - }; - env.do_exec(env.whoami(), &InvokeRequest::new(&next)?, vec![])?; - InvokeResponse::new(&()) - } - fn query( - &self, - _env: &mut dyn EnvironmentQuery, - _request: &InvokeRequest, - ) -> SdkResult { - InvokeResponse::new(&()) - } -} - -const RECURSIVE_ACCOUNT: u64 = 100; -const TEST_SENDER: u64 = 200; - -struct ConformanceCase { - test_name: &'static str, - requested_depth: u16, - expect_ok: bool, -} - -fn known_test_cases() -> Vec { - vec![ - ConformanceCase { - test_name: "singleCallTest", - requested_depth: 1, - expect_ok: true, - }, - ConformanceCase { - test_name: "nestedCallsTest", - requested_depth: 3, - expect_ok: true, - }, - ConformanceCase { - test_name: "returnUnwindsStackTest", - requested_depth: 2, - expect_ok: true, - }, - ConformanceCase { - test_name: "fullUnwindTest", - requested_depth: 2, - expect_ok: true, - }, - ConformanceCase { - test_name: "recursiveCallsTest", - requested_depth: 3, - expect_ok: true, - }, - ] -} - -#[test] -fn quint_itf_call_depth_conformance() { - let traces_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../specs/traces"); - - let test_cases = known_test_cases(); - for case in &test_cases { - let trace_file = find_single_trace_file(&traces_dir, case.test_name); - let trace: ItfTrace = read_itf_trace(&trace_file); - let spec_state = trace - .states - .last() - .expect("trace must have at least one state"); - let spec_result = &spec_state.last_result; - - assert_eq!( - spec_result.ok, case.expect_ok, - "{}: expected ok={} but trace says ok={}", - case.test_name, case.expect_ok, spec_result.ok - ); - - let stf = Stf::new( - NoopBegin::::default(), - NoopEnd, - NoopValidator::::default(), - NoopPostTx::::default(), - quint_common::default_gas_config(), - ); - - let mut storage = InMemoryStorage::default(); - let mut codes = CodeStore::new(); - codes.add_code(RecursiveAccount); - register_account( - &mut storage, - AccountId::from_u64(RECURSIVE_ACCOUNT), - "recursive", - ); - - let msg = RecurseMsg { - remaining: case.requested_depth, - }; - let tx = TestTx { - sender: AccountId::from_u64(TEST_SENDER), - recipient: AccountId::from_u64(RECURSIVE_ACCOUNT), - request: InvokeRequest::new(&msg).unwrap(), - gas_limit: 1_000_000, - funds: vec![], - }; - let block = TestBlock { - height: 1, - time: 0, - txs: vec![tx], - }; - - let (real_result, _) = stf.apply_block(&storage, &codes, &block); - assert_eq!( - real_result.tx_results.len(), - 1, - "{}: expected one tx result", - case.test_name - ); - - let real_ok = real_result.tx_results[0].response.is_ok(); - assert_eq!( - real_ok, case.expect_ok, - "{}: ok mismatch (real={real_ok}, expected={})", - case.test_name, case.expect_ok - ); - - if !case.expect_ok { - let real_err = real_result.tx_results[0].response.as_ref().unwrap_err().id; - assert_eq!( - real_err, - evolve_stf::errors::ERR_CALL_DEPTH_EXCEEDED.id, - "{}: expected call depth error", - case.test_name - ); - } - - eprintln!("PASS: {}", case.test_name); - } -} diff --git a/crates/app/stf/tests/quint_common.rs b/crates/app/stf/tests/quint_common.rs index eac7588..993bd77 100644 --- a/crates/app/stf/tests/quint_common.rs +++ b/crates/app/stf/tests/quint_common.rs @@ -12,61 +12,6 @@ use evolve_stf_traits::{ WritableKV, }; use hashbrown::HashMap; -use serde::de::DeserializeOwned; -use serde::Deserialize; -use std::io::BufReader; -use std::path::Path; - -// --------------------------------------------------------------------------- -// ITF deserialization types -// --------------------------------------------------------------------------- - -#[derive(Deserialize, Clone, Debug)] -pub struct ItfBigInt { - #[serde(rename = "#bigint")] - value: String, -} - -impl ItfBigInt { - pub fn as_i64(&self) -> i64 { - self.value.parse().unwrap() - } - pub fn as_u64(&self) -> u64 { - self.value.parse().unwrap() - } -} - -#[derive(Deserialize)] -pub struct ItfMap { - #[serde(rename = "#map")] - pub entries: Vec<(K, V)>, -} - -#[derive(Deserialize)] -pub struct ItfBlockResult { - pub gas_used: ItfBigInt, - pub tx_results: Vec, - pub txs_skipped: Option, -} - -#[derive(Deserialize)] -pub struct ItfTxResult { - pub gas_used: ItfBigInt, - pub result: ItfResult, -} - -#[derive(Deserialize)] -pub struct ItfResult { - pub ok: bool, - pub err_code: ItfBigInt, -} - -// --------------------------------------------------------------------------- -// Spec error constants (shared across conformance tests) -// --------------------------------------------------------------------------- - -pub const SPEC_ERR_OUT_OF_GAS: i64 = 0x01; -pub const SPEC_ERR_EXECUTION: i64 = 200; // --------------------------------------------------------------------------- // Shared test message (used by core and post-tx conformance tests) @@ -84,44 +29,6 @@ impl InvokableMessage for TestMsg { const FUNCTION_IDENTIFIER_NAME: &'static str = "test_msg"; } -// --------------------------------------------------------------------------- -// Trace file utilities -// --------------------------------------------------------------------------- - -pub fn find_single_trace_file(traces_dir: &Path, test_name: &str) -> std::path::PathBuf { - let prefix = format!("out_{}_", test_name); - let trace_files: Vec<_> = std::fs::read_dir(traces_dir) - .unwrap_or_else(|e| { - panic!( - "Cannot read traces directory {}: {e}. \ - Regenerate traces with: quint test specs/stf_*.qnt \ - --out-itf \"specs/traces/out_{{test}}_{{seq}}.itf.json\"", - traces_dir.display() - ) - }) - .filter_map(|e| e.ok()) - .filter(|e| { - let name = e.file_name(); - let name = name.to_string_lossy(); - name.starts_with(&prefix) && name.ends_with(".itf.json") - }) - .collect(); - - assert_eq!( - trace_files.len(), - 1, - "{}: expected exactly 1 trace file, found {}", - test_name, - trace_files.len() - ); - trace_files[0].path() -} - -pub fn read_itf_trace(trace_path: &Path) -> T { - let file = std::fs::File::open(trace_path).unwrap(); - serde_json::from_reader(BufReader::new(file)).unwrap() -} - // --------------------------------------------------------------------------- // Noop STF components // --------------------------------------------------------------------------- @@ -282,36 +189,3 @@ pub fn extract_account_storage( } result } - -pub fn expected_storage_from_itf( - itf_storage: &ItfMap, Vec>>, - account: AccountId, -) -> HashMap, Vec> { - let mut expected = HashMap::new(); - for (account_id_itf, account_store_itf) in &itf_storage.entries { - if AccountId::from_u64(account_id_itf.as_u64()) != account { - continue; - } - for (key_itf, value_itf) in &account_store_itf.entries { - let key: Vec = key_itf.iter().map(|b| b.as_u64() as u8).collect(); - let value: Vec = value_itf.iter().map(|b| b.as_u64() as u8).collect(); - expected.insert(key, value); - } - } - expected -} - -pub fn assert_storage_matches( - storage: &InMemoryStorage, - itf_storage: &ItfMap, Vec>>, - account: AccountId, - test_name: &str, -) { - let expected = expected_storage_from_itf(itf_storage, account); - let actual = extract_account_storage(storage, account); - assert_eq!( - actual, expected, - "{}: storage mismatch for account {:?}", - test_name, account - ); -} diff --git a/crates/app/stf/tests/quint_conformance.rs b/crates/app/stf/tests/quint_conformance.rs deleted file mode 100644 index 180d923..0000000 --- a/crates/app/stf/tests/quint_conformance.rs +++ /dev/null @@ -1,691 +0,0 @@ -//! Conformance tests: replay Quint ITF traces against the real STF. -//! -//! This test reads ITF (Informal Trace Format) JSON files generated by -//! `quint test specs/stf_core.qnt --out-itf ...` and replays each trace against -//! the actual STF implementation, asserting that the real execution matches -//! the spec's expected outcomes. -//! -//! Run: `cargo test -p evolve_stf --test quint_conformance` -//! Regenerate traces: `quint test --main=stf specs/stf_core.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json"` - -use borsh::{BorshDeserialize, BorshSerialize}; -use evolve_core::storage_api::{StorageSetRequest, STORAGE_ACCOUNT_ID}; -use evolve_core::{ - AccountCode, AccountId, BlockContext, Environment, EnvironmentQuery, ErrorCode, FungibleAsset, - InvokeRequest, InvokeResponse, Message, SdkResult, -}; -use evolve_stf::Stf; -use evolve_stf_traits::{ - Block as BlockTrait, SenderBootstrap, Transaction, TxValidator, WritableKV, -}; -use serde::Deserialize; -use std::path::Path; - -mod quint_common; -use quint_common::{ - assert_storage_matches, find_single_trace_file, read_itf_trace, register_account, CodeStore, - InMemoryStorage, ItfBigInt, ItfBlockResult, ItfMap, NoopBegin, NoopEnd, NoopPostTx, TestMsg, - SPEC_ERR_EXECUTION, SPEC_ERR_OUT_OF_GAS, -}; - -// --------------------------------------------------------------------------- -// ITF deserialization types (spec-specific state shape) -// --------------------------------------------------------------------------- - -#[derive(Deserialize)] -struct ItfTrace { - states: Vec, -} - -#[derive(Deserialize)] -struct ItfState { - block_height: ItfBigInt, - last_result: ItfBlockResult, - storage: ItfMap, Vec>>, -} - -// --------------------------------------------------------------------------- -// STF test infrastructure -// --------------------------------------------------------------------------- - -const SPEC_ERR_VALIDATION: i64 = 100; -const SPEC_ERR_BOOTSTRAP: i64 = 300; - -#[derive(Clone, Debug)] -struct TestTx { - sender: AccountId, - recipient: AccountId, - request: InvokeRequest, - gas_limit: u64, - funds: Vec, - fail_validate: bool, - needs_bootstrap: bool, - fail_bootstrap: bool, -} - -impl Transaction for TestTx { - fn sender(&self) -> AccountId { - self.sender - } - fn recipient(&self) -> AccountId { - self.recipient - } - fn request(&self) -> &InvokeRequest { - &self.request - } - fn gas_limit(&self) -> u64 { - self.gas_limit - } - fn funds(&self) -> &[FungibleAsset] { - &self.funds - } - fn compute_identifier(&self) -> [u8; 32] { - [0u8; 32] - } - fn sender_bootstrap(&self) -> Option { - if !self.needs_bootstrap { - return None; - } - let init = BootstrapInit { - fail: self.fail_bootstrap, - }; - let init_message = - Message::new(&init).expect("bootstrap init serialization must succeed in tests"); - Some(SenderBootstrap { - account_code_id: "test_account", - init_message, - }) - } -} - -#[derive(Clone)] -struct TestBlock { - height: u64, - time: u64, - txs: Vec, - gas_limit: u64, -} - -impl BlockTrait for TestBlock { - fn context(&self) -> BlockContext { - BlockContext::new(self.height, self.time) - } - fn txs(&self) -> &[TestTx] { - &self.txs - } - fn gas_limit(&self) -> u64 { - self.gas_limit - } -} - -#[derive(Default)] -struct Validator; -impl TxValidator for Validator { - fn validate_tx(&self, tx: &TestTx, env: &mut dyn Environment) -> SdkResult<()> { - if tx.fail_validate { - return Err(ErrorCode::new(SPEC_ERR_VALIDATION as u16)); - } - let probe = TestMsg { - key: vec![], - value: vec![], - fail_after_write: false, - }; - env.do_query(tx.sender(), &InvokeRequest::new(&probe).unwrap()) - .map_err(|_| ErrorCode::new(SPEC_ERR_VALIDATION as u16))?; - Ok(()) - } -} - -#[derive(Default)] -struct TestAccount; - -#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] -struct BootstrapInit { - fail: bool, -} - -impl AccountCode for TestAccount { - fn identifier(&self) -> String { - "test_account".to_string() - } - fn schema(&self) -> evolve_core::schema::AccountSchema { - evolve_core::schema::AccountSchema::new("TestAccount", "test_account") - } - fn init( - &self, - _env: &mut dyn Environment, - request: &InvokeRequest, - ) -> SdkResult { - let init: BootstrapInit = request.get()?; - if init.fail { - return Err(ErrorCode::new(SPEC_ERR_BOOTSTRAP as u16)); - } - InvokeResponse::new(&()) - } - fn execute( - &self, - env: &mut dyn Environment, - request: &InvokeRequest, - ) -> SdkResult { - let msg: TestMsg = request.get()?; - let set = StorageSetRequest { - key: msg.key.clone(), - value: Message::from_bytes(msg.value.clone()), - }; - env.do_exec(STORAGE_ACCOUNT_ID, &InvokeRequest::new(&set)?, vec![])?; - if msg.fail_after_write { - return Err(ErrorCode::new(SPEC_ERR_EXECUTION as u16)); - } - InvokeResponse::new(&()) - } - fn query( - &self, - _env: &mut dyn EnvironmentQuery, - _request: &InvokeRequest, - ) -> SdkResult { - InvokeResponse::new(&()) - } -} - -// --------------------------------------------------------------------------- -// Test case definitions (must match the Quint spec's run declarations) -// --------------------------------------------------------------------------- - -const TEST_ACCOUNT: u64 = 100; -const TEST_SENDER: u64 = 200; - -struct TxCase { - sender: u64, - recipient: u64, - key: Vec, - value: Vec, - gas_limit: u64, - fail_validate: bool, - fail_execute: bool, - needs_bootstrap: bool, - fail_bootstrap: bool, -} - -fn make_tx(tc: TxCase) -> TestTx { - let msg = TestMsg { - key: tc.key, - value: tc.value, - fail_after_write: tc.fail_execute, - }; - TestTx { - sender: AccountId::from_u64(tc.sender), - recipient: AccountId::from_u64(tc.recipient), - request: InvokeRequest::new(&msg).unwrap(), - gas_limit: tc.gas_limit, - funds: vec![], - fail_validate: tc.fail_validate, - needs_bootstrap: tc.needs_bootstrap, - fail_bootstrap: tc.fail_bootstrap, - } -} - -struct ConformanceCase { - test_name: &'static str, - blocks: Vec, -} - -fn known_test_cases() -> Vec { - vec![ - ConformanceCase { - test_name: "emptyBlockTest", - blocks: vec![TestBlock { - height: 1, - time: 0, - txs: vec![], - gas_limit: 1_000_000, - }], - }, - ConformanceCase { - test_name: "successfulTxTest", - blocks: vec![TestBlock { - height: 1, - time: 0, - gas_limit: 1_000_000, - txs: vec![make_tx(TxCase { - sender: TEST_ACCOUNT, - recipient: TEST_ACCOUNT, - key: vec![1], - value: vec![11], - gas_limit: 10000, - fail_validate: false, - fail_execute: false, - needs_bootstrap: false, - fail_bootstrap: false, - })], - }], - }, - ConformanceCase { - test_name: "validationFailureTest", - blocks: vec![TestBlock { - height: 1, - time: 0, - gas_limit: 1_000_000, - txs: vec![make_tx(TxCase { - sender: TEST_ACCOUNT, - recipient: TEST_ACCOUNT, - key: vec![1], - value: vec![11], - gas_limit: 10000, - fail_validate: true, - fail_execute: false, - needs_bootstrap: false, - fail_bootstrap: false, - })], - }], - }, - ConformanceCase { - test_name: "executionFailureRollbackTest", - blocks: vec![TestBlock { - height: 1, - time: 0, - gas_limit: 1_000_000, - txs: vec![make_tx(TxCase { - sender: TEST_ACCOUNT, - recipient: TEST_ACCOUNT, - key: vec![1], - value: vec![11], - gas_limit: 10000, - fail_validate: false, - fail_execute: true, - needs_bootstrap: false, - fail_bootstrap: false, - })], - }], - }, - ConformanceCase { - test_name: "outOfGasTest", - blocks: vec![TestBlock { - height: 1, - time: 0, - gas_limit: 1_000_000, - txs: vec![make_tx(TxCase { - sender: TEST_ACCOUNT, - recipient: TEST_ACCOUNT, - key: vec![1], - value: vec![11], - gas_limit: 1, - fail_validate: false, - fail_execute: false, - needs_bootstrap: false, - fail_bootstrap: false, - })], - }], - }, - ConformanceCase { - test_name: "blockGasLimitTest", - blocks: vec![TestBlock { - height: 1, - time: 0, - gas_limit: 70, - txs: vec![ - make_tx(TxCase { - sender: TEST_ACCOUNT, - recipient: TEST_ACCOUNT, - key: vec![1], - value: vec![11], - gas_limit: 40, - fail_validate: false, - fail_execute: false, - needs_bootstrap: false, - fail_bootstrap: false, - }), - make_tx(TxCase { - sender: TEST_ACCOUNT, - recipient: TEST_ACCOUNT, - key: vec![2], - value: vec![12], - gas_limit: 40, - fail_validate: false, - fail_execute: false, - needs_bootstrap: false, - fail_bootstrap: false, - }), - ], - }], - }, - ConformanceCase { - test_name: "mixedOutcomesTest", - blocks: vec![TestBlock { - height: 1, - time: 0, - gas_limit: 1_000_000, - txs: vec![ - make_tx(TxCase { - sender: TEST_ACCOUNT, - recipient: TEST_ACCOUNT, - key: vec![0], - value: vec![10], - gas_limit: 10000, - fail_validate: false, - fail_execute: false, - needs_bootstrap: false, - fail_bootstrap: false, - }), - make_tx(TxCase { - sender: TEST_ACCOUNT, - recipient: TEST_ACCOUNT, - key: vec![1], - value: vec![11], - gas_limit: 10000, - fail_validate: true, - fail_execute: false, - needs_bootstrap: false, - fail_bootstrap: false, - }), - make_tx(TxCase { - sender: TEST_ACCOUNT, - recipient: TEST_ACCOUNT, - key: vec![2], - value: vec![12], - gas_limit: 10000, - fail_validate: false, - fail_execute: true, - needs_bootstrap: false, - fail_bootstrap: false, - }), - make_tx(TxCase { - sender: TEST_ACCOUNT, - recipient: TEST_ACCOUNT, - key: vec![3], - value: vec![13], - gas_limit: 1, - fail_validate: false, - fail_execute: false, - needs_bootstrap: false, - fail_bootstrap: false, - }), - ], - }], - }, - ConformanceCase { - test_name: "bootstrapTest", - blocks: vec![TestBlock { - height: 1, - time: 0, - gas_limit: 1_000_000, - txs: vec![make_tx(TxCase { - sender: TEST_SENDER, - recipient: TEST_ACCOUNT, - key: vec![1], - value: vec![11], - gas_limit: 10000, - fail_validate: false, - fail_execute: false, - needs_bootstrap: true, - fail_bootstrap: false, - })], - }], - }, - ConformanceCase { - test_name: "bootstrapFailureTest", - blocks: vec![TestBlock { - height: 1, - time: 0, - gas_limit: 1_000_000, - txs: vec![make_tx(TxCase { - sender: TEST_SENDER, - recipient: TEST_ACCOUNT, - key: vec![1], - value: vec![11], - gas_limit: 10000, - fail_validate: false, - fail_execute: false, - needs_bootstrap: true, - fail_bootstrap: true, - })], - }], - }, - ConformanceCase { - test_name: "unregisteredSenderTest", - blocks: vec![TestBlock { - height: 1, - time: 0, - gas_limit: 1_000_000, - txs: vec![make_tx(TxCase { - sender: TEST_SENDER, - recipient: TEST_ACCOUNT, - key: vec![1], - value: vec![11], - gas_limit: 10000, - fail_validate: false, - fail_execute: false, - needs_bootstrap: false, - fail_bootstrap: false, - })], - }], - }, - ConformanceCase { - test_name: "multiBlockTest", - blocks: vec![ - TestBlock { - height: 1, - time: 0, - gas_limit: 1_000_000, - txs: vec![make_tx(TxCase { - sender: TEST_ACCOUNT, - recipient: TEST_ACCOUNT, - key: vec![1], - value: vec![11], - gas_limit: 10000, - fail_validate: false, - fail_execute: false, - needs_bootstrap: false, - fail_bootstrap: false, - })], - }, - TestBlock { - height: 2, - time: 10, - gas_limit: 1_000_000, - txs: vec![make_tx(TxCase { - sender: TEST_ACCOUNT, - recipient: TEST_ACCOUNT, - key: vec![2], - value: vec![12], - gas_limit: 10000, - fail_validate: false, - fail_execute: false, - needs_bootstrap: false, - fail_bootstrap: false, - })], - }, - ], - }, - ConformanceCase { - test_name: "overwriteTest", - blocks: vec![TestBlock { - height: 1, - time: 0, - gas_limit: 1_000_000, - txs: vec![ - make_tx(TxCase { - sender: TEST_ACCOUNT, - recipient: TEST_ACCOUNT, - key: vec![1], - value: vec![20], - gas_limit: 10000, - fail_validate: false, - fail_execute: false, - needs_bootstrap: false, - fail_bootstrap: false, - }), - make_tx(TxCase { - sender: TEST_ACCOUNT, - recipient: TEST_ACCOUNT, - key: vec![1], - value: vec![21], - gas_limit: 10000, - fail_validate: false, - fail_execute: false, - needs_bootstrap: false, - fail_bootstrap: false, - }), - ], - }], - }, - ] -} - -// --------------------------------------------------------------------------- -// Conformance test -// --------------------------------------------------------------------------- - -#[test] -fn quint_itf_conformance() { - let traces_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../specs/traces"); - - let test_cases = known_test_cases(); - let mut matched = 0; - - for case in &test_cases { - let trace_file = find_single_trace_file(&traces_dir, case.test_name); - let trace: ItfTrace = read_itf_trace(&trace_file); - - let expected_block_height = - case.blocks.last().expect("case must have blocks").height as i64; - let spec_state = trace - .states - .iter() - .find(|s| s.block_height.as_i64() == expected_block_height) - .unwrap_or_else(|| { - panic!( - "{}: no trace state with block_height={expected_block_height}", - case.test_name - ) - }); - - let spec_result = &spec_state.last_result; - - let stf = Stf::new( - NoopBegin::::default(), - NoopEnd, - Validator, - NoopPostTx::::default(), - quint_common::default_gas_config(), - ); - - let mut storage = InMemoryStorage::default(); - let mut codes = CodeStore::new(); - codes.add_code(TestAccount); - register_account( - &mut storage, - AccountId::from_u64(TEST_ACCOUNT), - "test_account", - ); - - let mut real_result = None; - for block in &case.blocks { - let (result, exec_state) = stf.apply_block(&storage, &codes, block); - storage - .apply_changes(exec_state.into_changes().unwrap()) - .unwrap(); - real_result = Some(result); - } - let real_result = real_result.expect("case must run at least one block"); - - // --- Assert conformance --- - - // 1. tx_results count - assert_eq!( - real_result.tx_results.len(), - spec_result.tx_results.len(), - "{}: tx_results count mismatch", - case.test_name - ); - - // 2. Per-tx outcomes - for (i, (real_tx, spec_tx)) in real_result - .tx_results - .iter() - .zip(spec_result.tx_results.iter()) - .enumerate() - { - let spec_ok = spec_tx.result.ok; - let real_ok = real_tx.response.is_ok(); - assert_eq!( - real_ok, spec_ok, - "{} tx[{i}]: ok mismatch (real={real_ok}, spec={spec_ok})", - case.test_name - ); - - if !spec_ok { - let spec_err = spec_tx.result.err_code.as_i64(); - let real_err = real_tx.response.as_ref().unwrap_err().id; - match spec_err { - SPEC_ERR_OUT_OF_GAS => assert_eq!( - real_err, - evolve_stf::ERR_OUT_OF_GAS.id, - "{} tx[{i}]: expected OOG", - case.test_name - ), - SPEC_ERR_VALIDATION => assert_eq!( - real_err, SPEC_ERR_VALIDATION as u16, - "{} tx[{i}]: expected validation error", - case.test_name - ), - SPEC_ERR_EXECUTION => assert_eq!( - real_err, SPEC_ERR_EXECUTION as u16, - "{} tx[{i}]: expected execution error", - case.test_name - ), - SPEC_ERR_BOOTSTRAP => assert_eq!( - real_err, SPEC_ERR_BOOTSTRAP as u16, - "{} tx[{i}]: expected bootstrap error", - case.test_name - ), - _ => panic!( - "{} tx[{i}]: unknown spec error code {spec_err}", - case.test_name - ), - } - } - - // 3. Gas - assert_eq!( - real_tx.gas_used, - spec_tx.gas_used.as_u64(), - "{} tx[{i}]: gas_used mismatch", - case.test_name - ); - } - - // 4. Block-level gas - assert_eq!( - real_result.gas_used, - spec_result.gas_used.as_u64(), - "{}: block gas_used mismatch", - case.test_name - ); - - // 5. Skipped count - assert_eq!( - real_result.txs_skipped, - spec_result.txs_skipped.as_ref().unwrap().as_u64() as usize, - "{}: txs_skipped mismatch", - case.test_name - ); - - // 6. Storage - for account_id in [ - AccountId::from_u64(TEST_ACCOUNT), - AccountId::from_u64(TEST_SENDER), - ] { - assert_storage_matches(&storage, &spec_state.storage, account_id, case.test_name); - } - - matched += 1; - eprintln!("PASS: {}", case.test_name); - } - - assert!( - matched == test_cases.len(), - "Matched {matched}/{} traces", - test_cases.len() - ); - eprintln!("{matched}/{} conformance tests passed", test_cases.len()); -} diff --git a/crates/app/stf/tests/quint_post_tx_conformance.rs b/crates/app/stf/tests/quint_post_tx_conformance.rs deleted file mode 100644 index 1b66b42..0000000 --- a/crates/app/stf/tests/quint_post_tx_conformance.rs +++ /dev/null @@ -1,360 +0,0 @@ -//! Conformance tests: replay Quint ITF traces for stf_post_tx.qnt. -//! -//! Run: -//! `quint test specs/stf_post_tx.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json"` -//! `cargo test -p evolve_stf --test quint_post_tx_conformance` - -use evolve_core::storage_api::{StorageSetRequest, STORAGE_ACCOUNT_ID}; -use evolve_core::{ - AccountCode, AccountId, BlockContext, Environment, EnvironmentQuery, ErrorCode, FungibleAsset, - InvokeRequest, InvokeResponse, Message, SdkResult, -}; -use evolve_stf::Stf; -use evolve_stf_traits::{Block as BlockTrait, PostTxExecution, Transaction, WritableKV}; -use serde::Deserialize; -use std::path::Path; - -mod quint_common; -use quint_common::{ - assert_storage_matches, find_single_trace_file, read_itf_trace, register_account, CodeStore, - InMemoryStorage, ItfBigInt, ItfBlockResult, ItfMap, NoopBegin, NoopEnd, NoopValidator, TestMsg, - SPEC_ERR_EXECUTION, SPEC_ERR_OUT_OF_GAS, -}; - -#[derive(Deserialize)] -struct ItfTrace { - states: Vec, -} - -#[derive(Deserialize)] -struct ItfState { - last_result: ItfBlockResult, - storage: ItfMap, Vec>>, -} - -const SPEC_ERR_POST_TX: i64 = 999; - -#[derive(Clone, Debug)] -struct TestTx { - sender: AccountId, - recipient: AccountId, - request: InvokeRequest, - gas_limit: u64, - funds: Vec, - reject_post_tx: bool, -} - -impl Transaction for TestTx { - fn sender(&self) -> AccountId { - self.sender - } - fn recipient(&self) -> AccountId { - self.recipient - } - fn request(&self) -> &InvokeRequest { - &self.request - } - fn gas_limit(&self) -> u64 { - self.gas_limit - } - fn funds(&self) -> &[FungibleAsset] { - &self.funds - } - fn compute_identifier(&self) -> [u8; 32] { - [0u8; 32] - } -} - -#[derive(Clone)] -struct TestBlock { - height: u64, - time: u64, - txs: Vec, -} - -impl BlockTrait for TestBlock { - fn context(&self) -> BlockContext { - BlockContext::new(self.height, self.time) - } - fn txs(&self) -> &[TestTx] { - &self.txs - } -} - -#[derive(Default)] -struct RejectingPostTx; -impl PostTxExecution for RejectingPostTx { - fn after_tx_executed( - tx: &TestTx, - _gas_consumed: u64, - tx_result: &SdkResult, - _env: &mut dyn Environment, - ) -> SdkResult<()> { - if tx.reject_post_tx && tx_result.is_ok() { - return Err(ErrorCode::new(SPEC_ERR_POST_TX as u16)); - } - Ok(()) - } -} - -#[derive(Default)] -struct TestAccount; - -impl AccountCode for TestAccount { - fn identifier(&self) -> String { - "test_account".to_string() - } - fn schema(&self) -> evolve_core::schema::AccountSchema { - evolve_core::schema::AccountSchema::new("TestAccount", "test_account") - } - fn init( - &self, - _env: &mut dyn Environment, - _request: &InvokeRequest, - ) -> SdkResult { - InvokeResponse::new(&()) - } - fn execute( - &self, - env: &mut dyn Environment, - request: &InvokeRequest, - ) -> SdkResult { - let msg: TestMsg = request.get()?; - let set = StorageSetRequest { - key: msg.key.clone(), - value: Message::from_bytes(msg.value.clone()), - }; - env.do_exec(STORAGE_ACCOUNT_ID, &InvokeRequest::new(&set)?, vec![])?; - if msg.fail_after_write { - return Err(ErrorCode::new(SPEC_ERR_EXECUTION as u16)); - } - InvokeResponse::new(&()) - } - fn query( - &self, - _env: &mut dyn EnvironmentQuery, - _request: &InvokeRequest, - ) -> SdkResult { - InvokeResponse::new(&()) - } -} - -const TEST_ACCOUNT_ID: u64 = 100; -const TEST_SENDER: u64 = 200; - -struct TxCase { - key: Vec, - value: Vec, - gas_limit: u64, - fail_execute: bool, - reject_post_tx: bool, -} - -fn make_tx(tc: TxCase) -> TestTx { - let msg = TestMsg { - key: tc.key, - value: tc.value, - fail_after_write: tc.fail_execute, - }; - TestTx { - sender: AccountId::from_u64(TEST_SENDER), - recipient: AccountId::from_u64(TEST_ACCOUNT_ID), - request: InvokeRequest::new(&msg).unwrap(), - gas_limit: tc.gas_limit, - funds: vec![], - reject_post_tx: tc.reject_post_tx, - } -} - -struct ConformanceCase { - test_name: &'static str, - block: TestBlock, -} - -fn known_test_cases() -> Vec { - vec![ - ConformanceCase { - test_name: "postTxRejectsButKeepsStateTest", - block: TestBlock { - height: 1, - time: 0, - txs: vec![make_tx(TxCase { - key: vec![1], - value: vec![11], - gas_limit: 10000, - fail_execute: false, - reject_post_tx: true, - })], - }, - }, - ConformanceCase { - test_name: "postTxDoesNotMaskExecFailureTest", - block: TestBlock { - height: 1, - time: 0, - txs: vec![make_tx(TxCase { - key: vec![1], - value: vec![11], - gas_limit: 10000, - fail_execute: true, - reject_post_tx: true, - })], - }, - }, - ConformanceCase { - test_name: "happyPathTest", - block: TestBlock { - height: 1, - time: 0, - txs: vec![make_tx(TxCase { - key: vec![1], - value: vec![11], - gas_limit: 10000, - fail_execute: false, - reject_post_tx: false, - })], - }, - }, - ConformanceCase { - test_name: "mixedPostTxTest", - block: TestBlock { - height: 1, - time: 0, - txs: vec![ - make_tx(TxCase { - key: vec![1], - value: vec![11], - gas_limit: 10000, - fail_execute: false, - reject_post_tx: true, - }), - make_tx(TxCase { - key: vec![2], - value: vec![12], - gas_limit: 10000, - fail_execute: false, - reject_post_tx: false, - }), - ], - }, - }, - ConformanceCase { - test_name: "outOfGasIgnoresPostTxTest", - block: TestBlock { - height: 1, - time: 0, - txs: vec![make_tx(TxCase { - key: vec![1], - value: vec![11], - gas_limit: 1, - fail_execute: false, - reject_post_tx: true, - })], - }, - }, - ] -} - -#[test] -fn quint_itf_post_tx_conformance() { - let traces_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../specs/traces"); - - let test_cases = known_test_cases(); - for case in &test_cases { - let trace_file = find_single_trace_file(&traces_dir, case.test_name); - let trace: ItfTrace = read_itf_trace(&trace_file); - let spec_state = trace - .states - .last() - .expect("trace must have at least one state"); - let spec_result = &spec_state.last_result; - - let stf = Stf::new( - NoopBegin::::default(), - NoopEnd, - NoopValidator::::default(), - RejectingPostTx, - quint_common::default_gas_config(), - ); - - let mut storage = InMemoryStorage::default(); - let mut codes = CodeStore::new(); - codes.add_code(TestAccount); - register_account( - &mut storage, - AccountId::from_u64(TEST_ACCOUNT_ID), - "test_account", - ); - - let (real_result, exec_state) = stf.apply_block(&storage, &codes, &case.block); - storage - .apply_changes(exec_state.into_changes().unwrap()) - .unwrap(); - - assert_eq!( - real_result.tx_results.len(), - spec_result.tx_results.len(), - "{}: tx_results count mismatch", - case.test_name - ); - - for (i, (real_tx, spec_tx)) in real_result - .tx_results - .iter() - .zip(spec_result.tx_results.iter()) - .enumerate() - { - let spec_ok = spec_tx.result.ok; - let real_ok = real_tx.response.is_ok(); - assert_eq!(real_ok, spec_ok, "{} tx[{i}]: ok mismatch", case.test_name); - - if !spec_ok { - let spec_err = spec_tx.result.err_code.as_i64(); - let real_err = real_tx.response.as_ref().unwrap_err().id; - match spec_err { - SPEC_ERR_OUT_OF_GAS => assert_eq!( - real_err, - evolve_stf::ERR_OUT_OF_GAS.id, - "{} tx[{i}]: expected OOG", - case.test_name - ), - SPEC_ERR_EXECUTION => assert_eq!( - real_err, SPEC_ERR_EXECUTION as u16, - "{} tx[{i}]: expected execution error", - case.test_name - ), - SPEC_ERR_POST_TX => assert_eq!( - real_err, SPEC_ERR_POST_TX as u16, - "{} tx[{i}]: expected post-tx error", - case.test_name - ), - _ => panic!( - "{} tx[{i}]: unknown spec error code {spec_err}", - case.test_name - ), - } - } - - assert_eq!( - real_tx.gas_used, - spec_tx.gas_used.as_u64(), - "{} tx[{i}]: gas_used mismatch", - case.test_name - ); - } - - assert_eq!( - real_result.gas_used, - spec_result.gas_used.as_u64(), - "{}: block gas mismatch", - case.test_name - ); - - assert_storage_matches( - &storage, - &spec_state.storage, - AccountId::from_u64(TEST_ACCOUNT_ID), - case.test_name, - ); - } -} diff --git a/justfile b/justfile index 1188aa1..75eaa53 100644 --- a/justfile +++ b/justfile @@ -223,25 +223,17 @@ sim-report trace: # SPEC CONFORMANCE # ============================================================================ -# Run core STF Quint tests/traces and Rust conformance checks +# Run core STF Quint spec tests and live `quint_connect` conformance [group('spec')] spec-test-core: quint test specs/stf_core.qnt - rm -f specs/traces/*.itf.json - quint test specs/stf_core.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json" - cargo test -p evolve_stf --test quint_conformance cargo test -p evolve_stf --test quint_core_connect -# Run extended STF model specs (ITF + `quint_connect` where available) +# Run extended STF model specs backed by live `quint_connect` [group('spec')] spec-test-extended: quint test specs/stf_post_tx.qnt - quint test specs/stf_post_tx.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json" - cargo test -p evolve_stf --test quint_post_tx_conformance cargo test -p evolve_stf --test quint_post_tx_connect - quint test specs/stf_call_depth.qnt - quint test specs/stf_call_depth.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json" - cargo test -p evolve_stf --test quint_call_depth_conformance # Run full STF spec suite (core + extended) [group('spec')] @@ -249,12 +241,6 @@ spec-test: just spec-test-core just spec-test-extended -# Regenerate ITF traces from core Quint spec (run after editing specs/stf_core.qnt) -[group('spec')] -spec-traces: - rm -f specs/traces/*.itf.json - quint test specs/stf_core.qnt --out-itf "specs/traces/out_{test}_{seq}.itf.json" - # ============================================================================ # BENCHMARKS # ============================================================================ diff --git a/specs/traces/out_blockGasLimitTest_5.itf.json b/specs/traces/out_blockGasLimitTest_5.itf.json deleted file mode 100644 index 8dc82c6..0000000 --- a/specs/traces/out_blockGasLimitTest_5.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190152},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"1"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_bootstrapFailureTest_8.itf.json b/specs/traces/out_bootstrapFailureTest_8.itf.json deleted file mode 100644 index ebfdb53..0000000 --- a/specs/traces/out_bootstrapFailureTest_8.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190168},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"51"},"tx_results":[{"gas_used":{"#bigint":"51"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}],[{"#bigint":"200"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"51"},"tx_results":[{"gas_used":{"#bigint":"51"},"result":{"err_code":{"#bigint":"300"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}],[{"#bigint":"200"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_bootstrapTest_7.itf.json b/specs/traces/out_bootstrapTest_7.itf.json deleted file mode 100644 index bd36f89..0000000 --- a/specs/traces/out_bootstrapTest_7.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190167},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"87"},"tx_results":[{"gas_used":{"#bigint":"87"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}],[{"#bigint":"200"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"},{"#bigint":"200"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"87"},"tx_results":[{"gas_used":{"#bigint":"87"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}],[{"#bigint":"200"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_cannotReturnFromEmptyTest_4.itf.json b/specs/traces/out_cannotReturnFromEmptyTest_4.itf.json deleted file mode 100644 index 8b2e3d8..0000000 --- a/specs/traces/out_cannotReturnFromEmptyTest_4.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:33 GMT+0100 (Central European Standard Time)","timestamp":1773064233321},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1}}]} \ No newline at end of file diff --git a/specs/traces/out_emptyBlockTest_0.itf.json b/specs/traces/out_emptyBlockTest_0.itf.json deleted file mode 100644 index ae6c345..0000000 --- a/specs/traces/out_emptyBlockTest_0.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190136},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_executionFailureRollbackTest_3.itf.json b/specs/traces/out_executionFailureRollbackTest_3.itf.json deleted file mode 100644 index 73f0137..0000000 --- a/specs/traces/out_executionFailureRollbackTest_3.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190147},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"200"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_fullUnwindTest_3.itf.json b/specs/traces/out_fullUnwindTest_3.itf.json deleted file mode 100644 index b62df2d..0000000 --- a/specs/traces/out_fullUnwindTest_3.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:33 GMT+0100 (Central European Standard Time)","timestamp":1773064233321},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"},{"#bigint":"2"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":3},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":4},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":5},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file diff --git a/specs/traces/out_happyPathTest_2.itf.json b/specs/traces/out_happyPathTest_2.itf.json deleted file mode 100644 index 4725664..0000000 --- a/specs/traces/out_happyPathTest_2.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:19 GMT+0100 (Central European Standard Time)","timestamp":1773064219766},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_mixedOutcomesTest_6.itf.json b/specs/traces/out_mixedOutcomesTest_6.itf.json deleted file mode 100644 index 6e6c469..0000000 --- a/specs/traces/out_mixedOutcomesTest_6.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190153},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"4"},"last_result":{"gas_used":{"#bigint":"72"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"4"},"last_result":{"gas_used":{"#bigint":"72"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}},{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"200"},"ok":false}},{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"0"}],[{"#bigint":"10"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_mixedPostTxTest_3.itf.json b/specs/traces/out_mixedPostTxTest_3.itf.json deleted file mode 100644 index 0b594a9..0000000 --- a/specs/traces/out_mixedPostTxTest_3.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:19 GMT+0100 (Central European Standard Time)","timestamp":1773064219774},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"72"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"999"},"ok":false}},{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"72"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"999"},"ok":false}},{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_multiBlockTest_10.itf.json b/specs/traces/out_multiBlockTest_10.itf.json deleted file mode 100644 index 1df81f9..0000000 --- a/specs/traces/out_multiBlockTest_10.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190171},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}},{"#meta":{"index":4},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"2"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]],[[{"#bigint":"2"}],[{"#bigint":"12"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_nestedCallsTest_1.itf.json b/specs/traces/out_nestedCallsTest_1.itf.json deleted file mode 100644 index 242dc64..0000000 --- a/specs/traces/out_nestedCallsTest_1.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:33 GMT+0100 (Central European Standard Time)","timestamp":1773064233319},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"},{"#bigint":"2"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":3},"call_stack":[{"#bigint":"1"},{"#bigint":"2"},{"#bigint":"3"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":4},"call_stack":[{"#bigint":"1"},{"#bigint":"2"},{"#bigint":"3"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file diff --git a/specs/traces/out_outOfGasIgnoresPostTxTest_4.itf.json b/specs/traces/out_outOfGasIgnoresPostTxTest_4.itf.json deleted file mode 100644 index c9c9847..0000000 --- a/specs/traces/out_outOfGasIgnoresPostTxTest_4.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:19 GMT+0100 (Central European Standard Time)","timestamp":1773064219775},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_outOfGasTest_4.itf.json b/specs/traces/out_outOfGasTest_4.itf.json deleted file mode 100644 index c6a8fd2..0000000 --- a/specs/traces/out_outOfGasTest_4.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190150},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"1"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_overwriteTest_11.itf.json b/specs/traces/out_overwriteTest_11.itf.json deleted file mode 100644 index aabbdc8..0000000 --- a/specs/traces/out_overwriteTest_11.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190172},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"72"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"2"},"last_result":{"gas_used":{"#bigint":"72"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}},{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"21"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_postTxDoesNotMaskExecFailureTest_1.itf.json b/specs/traces/out_postTxDoesNotMaskExecFailureTest_1.itf.json deleted file mode 100644 index 7eefcf4..0000000 --- a/specs/traces/out_postTxDoesNotMaskExecFailureTest_1.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:19 GMT+0100 (Central European Standard Time)","timestamp":1773064219764},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"200"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"200"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_postTxRejectsButKeepsStateTest_0.itf.json b/specs/traces/out_postTxRejectsButKeepsStateTest_0.itf.json deleted file mode 100644 index 876e970..0000000 --- a/specs/traces/out_postTxRejectsButKeepsStateTest_0.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_post_tx.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:19 GMT+0100 (Central European Standard Time)","timestamp":1773064219763},"vars":["storage","accounts","last_result"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"999"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"999"},"ok":false}}]},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_recursiveCallsTest_5.itf.json b/specs/traces/out_recursiveCallsTest_5.itf.json deleted file mode 100644 index 199cf4c..0000000 --- a/specs/traces/out_recursiveCallsTest_5.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:33 GMT+0100 (Central European Standard Time)","timestamp":1773064233321},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"},{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":3},"call_stack":[{"#bigint":"1"},{"#bigint":"1"},{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":4},"call_stack":[{"#bigint":"1"},{"#bigint":"1"},{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file diff --git a/specs/traces/out_returnUnwindsStackTest_2.itf.json b/specs/traces/out_returnUnwindsStackTest_2.itf.json deleted file mode 100644 index 4a2c904..0000000 --- a/specs/traces/out_returnUnwindsStackTest_2.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:33 GMT+0100 (Central European Standard Time)","timestamp":1773064233320},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"},{"#bigint":"2"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":3},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":4},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file diff --git a/specs/traces/out_singleCallTest_0.itf.json b/specs/traces/out_singleCallTest_0.itf.json deleted file mode 100644 index a647272..0000000 --- a/specs/traces/out_singleCallTest_0.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_call_depth.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:50:33 GMT+0100 (Central European Standard Time)","timestamp":1773064233318},"vars":["call_stack","last_result"],"states":[{"#meta":{"index":0},"call_stack":[],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":1},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}},{"#meta":{"index":2},"call_stack":[{"#bigint":"1"}],"last_result":{"err_code":{"#bigint":"0"},"ok":true}}]} \ No newline at end of file diff --git a/specs/traces/out_successfulTxTest_1.itf.json b/specs/traces/out_successfulTxTest_1.itf.json deleted file mode 100644 index 0755c4c..0000000 --- a/specs/traces/out_successfulTxTest_1.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190138},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"36"},"tx_results":[{"gas_used":{"#bigint":"36"},"result":{"err_code":{"#bigint":"0"},"ok":true}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[[[{"#bigint":"1"}],[{"#bigint":"11"}]]]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_unregisteredSenderTest_9.itf.json b/specs/traces/out_unregisteredSenderTest_9.itf.json deleted file mode 100644 index e83a66f..0000000 --- a/specs/traces/out_unregisteredSenderTest_9.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190170},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file diff --git a/specs/traces/out_validationFailureTest_2.itf.json b/specs/traces/out_validationFailureTest_2.itf.json deleted file mode 100644 index a90659c..0000000 --- a/specs/traces/out_validationFailureTest_2.itf.json +++ /dev/null @@ -1 +0,0 @@ -{"#meta":{"format":"ITF","format-description":"https://apalache-mc.org/docs/adr/015adr-trace.html","source":"specs/stf_core.qnt","status":"passed","description":"Created by Quint on Mon Mar 09 2026 14:49:50 GMT+0100 (Central European Standard Time)","timestamp":1773064190142},"vars":["storage","accounts","block_height","last_result","last_block_tx_count"],"states":[{"#meta":{"index":0},"accounts":{"#set":[]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[]}},{"#meta":{"index":1},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"0"},"last_block_tx_count":{"#bigint":"0"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":2},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}},{"#meta":{"index":3},"accounts":{"#set":[{"#bigint":"100"}]},"block_height":{"#bigint":"1"},"last_block_tx_count":{"#bigint":"1"},"last_result":{"gas_used":{"#bigint":"0"},"tx_results":[{"gas_used":{"#bigint":"0"},"result":{"err_code":{"#bigint":"100"},"ok":false}}],"txs_skipped":{"#bigint":"0"}},"storage":{"#map":[[{"#bigint":"100"},{"#map":[]}]]}}]} \ No newline at end of file