Quint Language Documentation

repository·main·Indexed 23 days ago

https://github.com/quint-co/quint

Documentation for Quint, a language featuring a Rust-based evaluator (quint_evaluator v0.2.0) and a TypeScript simulator. Includes guides on language syntax, API operators, design principles, and tooling such as the CLI and REPL. Provides setup instructions for editor support in Vim, Neovim, and Emacs, including integration with the quint-language-server via LSP. Covers model checking with TLC and Apalache, as well as practical examples for ICS 20, ICS23, and Tendermint specifications.

Tokens
135.3K
Snippets
379
Records
710
Agent score
78%

What's inside Quint

  1. Overview of Builtin Functions

    main

    The builtin module provides a comprehensive set of primitives for Quint, including:

    • Mathematical operations: Integer arithmetic, comparison, and exponentiation.
    • Collection operations: List selection, folding, and range/set generation.
    • Temporal logic: Operators for reasoning about state transitions (always, eventually, next, fairness, etc.).
    • Action primitives: Composition, repetition, assertion, and conditional execution.
  2. What is Quint?

    main

    Quint is a specification language used to describe models (state machines) and properties (invariants or temporal properties). You can use Quint to verify these models using either a model checker or a simulator.

    • Model Checker: Explores all possible states to guarantee a property holds. If a violation is found, it provides the shortest counter-example.
    • Simulator: Runs a specified number of random samples. It is faster for finding bugs but does not provide a mathematical guarantee that a property holds across all states.
    • Counter-examples: When a property is violated, Quint provides a trace (a sequence of states) showing exactly how the violation occurred, ensuring no false positives.
  3. Quint CLI and Tooling capabilities

    main

    Quint is a CLI-first language. Users can perform the following via the command line:

    • Parsing and Transpilation: The CLI allows for parsing and transpiling Quint code.
    • JSON Interoperability: Intermediate transpiler outputs are available in JSON format for programmatic use.
    • IDE Support: While CLI-first, IDE support (such as a VSCode plugin) is available as an opt-in enhancement.
  4. Use the Quint Modelling skill

    main

    The quint-modeling skill is used to produce a Quint specification from various starting points, such as ideas, requirements, source code, or existing TLA+ specifications. It is also used to audit or review existing .qnt files against a formal checklist.

    When to use this skill:

    • To model, spec out, formally describe, model-check, or verify a system.
    • To translate existing designs (e.g., TLA+ or source code) into Quint.
    • To review or audit an existing Quint specification.

    When NOT to use this skill:

    • For questions regarding Quint language syntax, operators, the CLI, or basicSpells. For those, use the quint-lang reference instead.
  5. Explore Quint protocol specification examples

    main

    The examples/ directory contains various protocol specifications written in Quint. These examples serve as learning resources for modeling different systems.

    Note on availability: While all examples pass the parser and type checker, some may not be fully runnable or testable yet. Check the project dashboard for the current status of specific examples.

  6. Understand the Mafia game logic and components

    main

    The Mafia game is a state-based implementation where players are assigned roles and the game alternates between Day and Night phases.

    Core Types

    • Role: Mafia or Citizen.
    • LifeState: Alive or Dead.
    • Phase: Day or Night.
    • PlayerFeatures: A structure containing a player's role, life status, and voting status.

    Game Status

    The game tracks its progress using a game_status variable which can be:

    • Pending: The game is ongoing.
    • Done(Role): The game has ended, with the Role indicating the winner (either Mafia or Citizens).

    Game Flow

    1. Initial Setup: The game starts in the Night phase. Players are randomly assigned roles and set to Alive.
    2. Night Phase: The Mafia selects a Citizen to kill.
    3. Day Phase: All living players vote to hang a player. The player with the maximum votes is eliminated. If there is a tie for the most votes, no one is eliminated.

    Winning Conditions

    • Mafia Wins: All Citizen players are Dead.
    • Citizen Wins: All Mafia players are Dead.
  7. Understand the purpose of Apalache test fixtures

    main
    The testFixture/apalache directory contains specifications (specs) specifically designed to test the integration of the Quint model checker with Apalache. These specs serve as a validation suite to ensure that the translation or integration between Quint and the Apalache model checker is functioning correctly.
  8. Use the Choreo Framework for distributed protocols

    main

    Choreo is a structured framework for writing distributed protocol specifications in Quint. It provides abstractions for message passing, state management, and Byzantine fault tolerance. Use Choreo when modeling consensus protocols (e.g., Raft, Paxos, Tendermint, HotStuff), BFT protocols, multi-phase commit, or any message-passing protocol with $N$ processes.

    To use Choreo, import it using the following syntax:

    import choreo(processes = NODES) as choreo from "./choreo"
    import choreo(processes = NODES) as choreo from "./choreo"
  9. What is an inductive invariant in Quint

    main

    An inductive invariant is a logical formula (a predicate) over the variables of a system that characterizes a 'correct state'. To be considered an inductive invariant, the formula must satisfy two conditions:

    1. Base Case: All initial states must satisfy the formula.
    2. Inductive Step: If a state satisfies the formula, any state resulting from applying a protocol step must also satisfy the formula.

    In Quint, inductive invariants are used to prove that a system maintains a correct state throughout its execution. They are stronger than ordinary invariants because they must be preserved by every possible transition in the model.

  10. Understand Trace Validation in Quint

    main

    Trace validation is the inverse of Model-based Testing. It uses real-world data from production or staging environments to verify that the implementation adheres to the Quint model.

    The Trace Validation Workflow:

    1. Log Implementation Data: Add logging to your code to capture either transitions (the actions taken, e.g., deposit("Bob", 100)) and/or states (sequential snapshots of relevant data, e.g., "Bob" -> 40).
    2. Capture Traces: Collect these logs from your running environment.
    3. Validate against Model: Use Quint to check if the captured sequence of transitions or states is possible within your formal model.

    If Quint determines the sequence is impossible according to the model, it indicates a bug in the implementation (i.e., the code allowed a behavior that the specification forbids).

  11. How the Instance Flattener works

    main

    The Instance Flattener resolves the complexity of module instances by replacing each instance statement with an import statement and generating a new, dedicated module for that instance.

    Key behaviors:

    • Uniqueness: Because different instances can have different values for the same constant, the flattener creates new modules (e.g., B::A1) with unique IDs for their definitions.
    • Constant Overrides: Overrides in an instance statement (e.g., import A(N=1)) are converted into pure val definitions in the new module.
    • Dependency Tracking: If an override expression e depends on definitions in the current module, the flattener ensures those dependencies are copied into the new instance module so the expression remains resolvable.

    Example Transformation

    Original Code:

    module A {
      const N: int
      val a = N
    }
    
    module B {
      import A(N=1) as A1
      val b = A1::a
    }

    After Instance Flattening:

    module A {
      const N: int
      val a = N
    }
    
    // A new module is generated for the instance
    module B::A1 {
      pure val B::A1::N = 1
      pure val B::A1::a = B::A1::N
    }
    
    module B {
      import B::A1.*
      val b = B::A1::a
    }
    module A {
      const N: int // id 1
      val a = N // id 2
    }
    
    module B {
      import A(N=1) as A1
      val b = A1::a // id 3
    }
    
    module C {
      import A(N=0) as A1
      val C = A1::a // id 4
    }
    
    // The instance flattener will create two new modules: B::A1 and C::A1
    module B::A1 {
      pure val B::A1::N = 1 // id 5
      pure val B::A1::a = B::A1::N // id 6
    }
    
    module C::A1 {
      pure val C::A1::N = 0 // id 7
      pure val C::A1::a = C::A1::N // id 8
    }
    
    module B {
      import B::A1.*
      val b = B::A1::a // id 3
    }
    
    module C {
      import C::A1.*
      val c = C::A1::a // id 4
    }