## File: crates/proof-of-sql-planner/examples/posql_db/README.md # `posql_db` Example demonstrating an implementation of a simple csv-backed database with Proof of SQL capabilities. ## Install Run `cargo install --example posql_db --path crates/proof-of-sql` to install the example. > [!NOTE] > To run this example without the `blitzar` (i.e., CPU only) feature > ```bash > cargo install --example posql_db --path crates/proof-of-sql --no-default-features --features="cpu-perf" > ``` ## Quick Start Example Run the following ```bash posql_db create -t sxt.table -c a,b -d BIGINT,VARCHAR posql_db append -t sxt.table -f hello_world.csv posql_db prove -q "SELECT b FROM sxt.table WHERE a = 2" -f hello.proof posql_db verify -q "SELECT b FROM sxt.table WHERE a = 2" -f hello.proof ``` --- ## File: crates/proof-of-sql-planner/examples/hello_world/README.md # Proof of SQL "Hello World" This example demonstrates generating and verifying a proof of the query `SELECT b FROM table WHERE a = 2` for the table: | a | b | |------------|-------------| | 1 | hi | | 2 | hello | | 3 | there | | 2 | world | #### Run ```bash cargo run --example hello_world ``` > [!NOTE] > To run this example without the `blitzar` (i.e., CPU only) feature: > ```bash > cargo run --example hello_world --no-default-features --features="test cpu-perf" > ``` #### Output ``` Warming up GPU... 520.959485ms Loading data... 3.229767ms Parsing Query... 1.870256ms Generating Proof... 467.45371ms Verifying Proof... 7.106864ms Valid proof! Query result: OwnedTable { table: {Ident { value: "b", quote_style: None }: VarChar(["hello", "world"])} } ``` --- ## File: crates/proof-of-sql-benches/src/README.md # Running benchmarks ## Jaeger benchmarking The Jaeger benchmarks/tracing is wrapped by a binary. The motivation of the wrapper is to allow greater control over benchmark parameters. To run benchmarks with Jaeger, you need to do the following 1. Spin up Jaeger service on port 6831 to receive the benchmarks trace data, and provides Jaeger UI on port 16686. ```bash docker run --rm -d --name jaeger -p 6831:6831/udp -p 16686:16686 jaegertracing/all-in-one:1.62.0 ``` 2. See all the options to run a benchmark. ```bash cargo run --release --bin proof-of-sql-benches -- --help ``` 3. Navigate to http://localhost:16686/ to see the results. 4. To end the Jaeger service, run ```bash docker kill jaeger ``` All the options are outlined in the help and `main.rs` module. ### Example To run a benchmark on the `HyperKZG` commitment scheme using the `Single Column Filter` query with a table size of `1_000_000` for `3` iterations, your command would be the following. ```bash cargo run --release --bin proof-of-sql-benches -- -s hyper-kzg -i 3 -t 1000000 -q single-column-filter ``` ### Memory logging (optional) Jaeger benchmarks default to logging any traces at `DEBUG` level and above. Memory consumption is logged at `TRACE` level. In order to capture memory consumption in the Jaeger benchmarks, add `RUST_LOG=trace` to the command. Example ``` RUST_LOG=trace cargo run --release --bin proof-of-sql-benches ``` ## Criterion benchmarking To run benchmarks with Criterion, you need to do the following 1. Run the benchmarks. (Warning: this takes a very long time.) ```bash cargo bench -p proof-of-sql-benches --bench bench_append_rows --features="test" ``` 2. Navigate to `target/criterion/report/index.html` to see the results. --- ## File: crates/proof-of-sql/utils/generate-parameters/README.md # Space and Time ParamGen A simple tool to generate the Space and Time public network parameters. ## πŸ“‘ Table of Contents - [πŸš€ Quick Start](#quick-start) - [πŸ“š Background](#background) - [πŸ“š Additional Resources](#additional-resources) ## πŸš€ Quick Start Ensure that you have rust installed. Then, clone this repo and simply run the following: ```bash cargo run --release --bin generate-parameters ``` This generates the setups for both the prover and verifier as two separate tar.gz files with a default nu value of 14. It saves these parameters at the head of this repo as tar.gz archives. | Description | Command | | --------------- | --------------- | | Run the Prover setup only | ```cargo run --release --bin generate-parameters -- --mode prover``` | |Run the Verifier setup only | ```cargo run --release --bin generate-parameters -- --mode verifier``` | | Run both Prover and Verifier setups with a custom nu value | ```cargo run --release --bin generate-parameters -- --mode all --nu 4``` | | Specify an output directory (with --target argument) | ```cargo run --release --bin generate-parameters -- --mode all --target ./output ``` | ## πŸ“š Background ### What are public parameters? There are a wide variety of zero-knowledge proof and argument systems, all offering different performance characteristics. The classic example is the [Groth16](https://eprint.iacr.org/2016/260.pdf) argument, a commonly used proof system which establishes a trusted setup (known formally as a common reference string (CRS) or structured reference string (SRS)) to be shared among participants in the network. This setup is structured in such a way that allows arguments of valid computation to be produced with very small sizes. In the case of Groth16, this can be as low as a few group elements or a couple hundred bytes, which is the perfect size to store on a blockchain. The Space and Time network makes use of a few different argument systems. The Dory polynomial commitment scheme (PCS) is a SNARK which requires a setup to be established between the proving and verifying parties. The Dory PCS is chosen because it is amenable to forming proofs and arguments over matrices, which is perfect for the Proof-Of-SQL case, since databases and tables are essentially matrices. The Dory setup process is unique in that it is *transparent*, meaning there is no toxic waste or secret values to forget once the setup is complete. The setup is initialized with an arbitrary random string which establishes common parameters. We choose the random string "SpaceAndTime" for our setup. This string is a "[Nothing-up-my-sleeve number](https://en.wikipedia.org/wiki/Nothing-up-my-sleeve_number)", meaning it is easily auditable and has no hidden structure that can be exploited to generate false proofs or compromise the integrity of the system. The Space and Time implementation of the [Dory PCS](https://eprint.iacr.org/2020/1274) is non-zero knowledge and does not explicitly blind the inputs used in the argument of correct sql execution. This yields a leaner implementation and slightly better performance. We may add zero-knowledge blinding in the future, but for now it is not necessary for Proof-Of-SQL to function correctly. This tool generates the public setups for either the prover or verifier. Both setups are parameterized over a value *nu*, which helps establish the maximum dimension of the table that can be argued against. The prover and the verifier both possess a slightly different setup. The verifier setup is relatively cheap to compute and scales linearly for large nu/table sizes. The prover setup is larger and has a higher cost to compute. We provide pre-computed setups that can easily be downloaded and used with the SxT network in order to skip the expensive generation process, but this repo contains a tool to generate the parameters at your option. ### Table Sizes The maximum table supported table size for parameters generated by this tool is determined by the value of max nu. For Dynamic Dory, that size is $2^{2 \nu - 1}$. This translates into the number of rows that the parameter can support below: | Ξ½ (nu) | Number of rows | |--------|-------------------| | 8 | 33 thousand | | 9 | 131 thousand | | 10 | 524 thousand | | 11 | 2.10 million | | 12 | 8.39 million | | 13 | 33.55 million | | 14 | 134.22 million | | 15 | 536.87 million | | 16 | 2.15 billion | | 17 | 8.59 billion | | 18 | 34.36 billion | | 19 | 137.44 billion | | 20 | 549.76 billion | NOTE: Setups using the same random string but different nu values remain compatible with each other up to the minimum value of nu shared between them. SxT as of this writing uses a nu value of 16. Setups with smaller values of nu should be compatible with this setup. ## πŸ“š Additional Resources - [Dory: Efficient, Transparent arguments for Generalised Inner Products and Polynomial Commitments](https://eprint.iacr.org/2020/1274) - [Groth16](https://eprint.iacr.org/2016/260.pdf) - [Nothing-up-my-sleeve number](https://en.wikipedia.org/wiki/Nothing-up-my-sleeve_number) --- ## File: crates/proof-of-sql/README.md # Proof of SQL Proof of SQL is a high performance zero knowledge (ZK) prover developed by the [Space and Time](https://www.spaceandtime.io/) team, which cryptographically guarantees SQL queries were computed accurately against untampered data. It targets online latencies while proving computations over entire chain histories, an order of magnitude faster than state-of-the art zkVMs and coprocessors. As the first sub-second ZK prover, the protocol can execute analytic queries over 1M+ rows in less than a second (see benchmarks below). It can aggregate over millions of rows of indexed data within Ethereum block time on a single NVIDIA T4. The protocol is designed to support both onchain and offchain verification, leveraging a novel commitment scheme which significantly lowers gas fees with onchain verification. Using Proof of SQL, developers can compute over both onchain and offchain datasets in a trustless manner, proving the result back to their smart contract (or offchain verifier) just-in-time during a transaction to power more sophisticated DeFi protocols with data-driven contracts. Proof of SQL can be integrated into any SQL database (such as [Google BigQuery](https://cloud.google.com/blog/topics/partners/how-space-and-times-proof-of-sql-integrates-with-bigquery)), centralized or decentralized, and is already securing some of the most prominent Web3 apps, financial institutions, and enterprises. ## Contributing Proof of SQL is in active development, and not all SQL functions are supported yet. Proof of SQL is most powerful as a community-driven project. We hope to foster a large group of contributors that can help maintain, improve, and use this project to create a trustless and data-driven future. Please create an Issue, file a PR, or reach out via Discord if you want to add a SQL feature, integrate into another ZK solution, use this in your project, or anything else! Check out our guidelines: [CONTRIBUTING.md](https://github.com/spaceandtimelabs/sxt-proof-of-sql/blob/main/CONTRIBUTING.md). ## For Developers Get started with Proof of SQL by using the published crate on [crates.io](https://crates.io/) or clone the repo and check out the examples. Check out the following sections of the README: * [Examples](#examples) * [Benchmarks](#benchmarks) * [Supported SQL Syntax](#supported-sql-syntax) * [Roadmap](#roadmap) * [Protocol Overview](#protocol-overview) ## Setup ### Prerequisites * Linux `x86_64` (NOTE: Most of the codebase _should_ work for most rust targets. However, proofs are accelerated using NVIDIA GPUs, so other targets would run very slowly and may require modification.) * NVIDIA GPU & Drivers (Strongly Recommended) * lld (`sudo apt install lld`) * clang (`sudo apt install clang`) * [Rust 1.81.0](https://www.rust-lang.org/tools/install) Workaround for non-Linux and/or non-GPU machines. * Workaround #1: enable the CPU version of Blitzar by setting the `BLITZAR_BACKEND` environment variable. Example: ```bash export BLITZAR_BACKEND=cpu cargo test --all-features --all-targets ``` * Workaround #2: disable the `blitzar` feature in the repo. Example ```bash cargo test --no-default-features --features="arrow cpu-perf" ``` ## Examples Proof of SQL comes with example code demonstrating its usage. You can find the examples in the `crates/proof-of-sql/examples` folder. Below are explanations of how to run some of these examples: ### "Hello World" Example The "Hello World" example demonstrates generating and verifying a proof of the query `SELECT b FROM table WHERE a = 2` for the table: | a | b | |------------|-------------| | 1 | hi | | 2 | hello | | 3 | there | | 2 | world | #### Run ```bash cargo run --example hello_world ``` > [!NOTE] > To run this example without the `blitzar` (i.e CPU only) feature: > ```bash > cargo run --example hello_world --no-default-features --features="rayon test" > ``` #### Output ```bash Warming up GPU... 520.959485ms Loading data... 3.229767ms Parsing Query... 1.870256ms Generating Proof... 467.45371ms Verifying Proof... 7.106864ms Valid proof! Query result: OwnedTable { table: {Ident { value: "b", quote_style: None }: VarChar(["hello", "world"])} } ``` For a detailed explanation of the example and its implementation, refer to the [README](https://github.com/spaceandtimelabs/sxt-proof-of-sql/blob/main/crates/proof-of-sql/examples/hello_world/README.md) and source code in [hello_world/main.rs](https://github.com/spaceandtimelabs/sxt-proof-of-sql/blob/main/crates/proof-of-sql/examples/hello_world/main.rs). ### CSV Database Example The CSV Database example demonstrates an implementation of a simple CSV-backed database with Proof of SQL capabilities. To install the example: ```bash cargo install --example posql_db --path crates/proof-of-sql #TODO: update once this is published to crates.io ``` For detailed usage instructions and examples of how to create, append to, prove, and verify queries in the CSV-backed database, refer to the [README](https://github.com/spaceandtimelabs/sxt-proof-of-sql/blob/main/crates/proof-of-sql/examples/posql_db/README.md) and source code in [posql_db/main.rs](https://github.com/spaceandtimelabs/sxt-proof-of-sql/blob/main/crates/proof-of-sql/examples/posql_db/main.rs). ## Benchmarks Proof of SQL is optimized for speed and efficiency. Here's how it's so fast: 1. We use **native, precomputed commitments** to the data. In other words, when adding data to the database, we compute a "digest" of the data, which effectively "locks in" the data. Instead of using a merkle tree based commitment, like those used in most blockchains, we use the commitment scheme that is inherent to Proof of SQL itself. 2. SQL is conducive to a **natural arithmetization**, meaning that there is very little overhead compared with other proof systems that are designed around instructions/sequential compute. Instead, Proof of SQL is designed from the ground up with data processing and parallelism in mind. 3. We use **GPU acceleration** on the most expensive cryptography in the prover. We use [Blitzar](https://github.com/spaceandtimelabs/blitzar) as our acceleration framework. ### Setup We run benchmarks using NVIDIA A100 GPUs (NC A100 v4-series Azure VM). To run these benchmarks we first generate a large, randomly-filled table of data such as the following: Then, we run the following 4 queries against these data, prove, and verify the results: * Filter - `SELECT b FROM bench_table WHERE a = 0` * Complex Filter - `SELECT * FROM bench_table WHERE (((a = 0) AND (b = 1)) OR ((c = 'a') AND (d = 'b')))` * Group By - `SELECT SUM(a), COUNT(*) FROM bench_table WHERE a = 0 GROUP BY b` * Join - `SELECT table_a.column, table_b.column FROM table_a JOIN table_b on table_a.column=table_b.column` ### Results The results for the `HyperKZG` commitment scheme are shown in the graphs below for a single and multiple A100 machine. ## Supported SQL Syntax See the [SQL specification](https://github.com/spaceandtimelabs/sxt-proof-of-sql/blob/main/docs/SQLSyntaxSpecification.md) for more details. Broadly, we support the following with more SQL features being added quickly: * `SELECT ... WHERE` * `GROUP BY` * Comparison operations: `=`, `>=`, `<=`, etc. * Logical operations: `AND`, `OR`, `NOT`. * Numerical operations `+`, `-`, `*`. * Aggregations: `SUM`, `COUNT` * Data Types: `BOOLEAN`, Integer types, `VARCHAR`, `DECIMAL75`, `TIMESTAMP`. ## Roadmap Proof of SQL is in active development. Here are some items that we are currently working on. We are happy to receive feedback on additional features that the community requests. * Expanded SQL support - in particular, multi-table queries (like JOIN) and subqueries * Cluster scale proofs - this means faster proofs over larger tables! * Solidity (EVM) verifier - for more efficient onchain verification. * A novel commitment scheme - while we support a variety of commitment schemes, we are developing a commitment scheme specifically for database operations, ensuring lower-gas onchain verification. We are also currently undergoing robust security audits. Keep this in mind as you use this code. ## Protocol Overview See the [Space and Time Whitepaper](https://assets-global.website-files.com/642d91209f1e772d3740afa0/658edf3cf26933c4878ec965_whitepaper.pdf) for a more in-depth explanation. We will also be adding more technical documentation to this repo soon. We created this protocol with a few key goals. First, it needs to be super fast for data processing, both for verification and round-trip execution. This requires a design that is built from the ground up, as opposed to using arbitrary zkVMs. Second, we made it very developer-friendly. Using SQL, the most popular data query language, ensures a familiar experience for anyone building data-focused applications, or sophisticated data-driven contracts. Finally, our protocol is designed to handle complex data processing, not just simple serial compute or data retrieval. In this protocol, there are two main roles: the client sending the query (Verifier) and the database service returning the result (Prover). Of course, the Verifier doesn't always have to send the query; it can be any client, such as a smart contract, a dapp frontend, or a laptop. This setup is crucial for applications with limited compute or storage but still requires a security guarantee that data analytics are correctly executed and the data remains unaltered. The Prover handles heavy computations, while the Verifier is lightweight, suitable for client devices or smart contracts with limited resources. A key architectural feature is the concept of a commitment, or digest. To ensure data integrity, the Verifier maintains this commitment to detect any tampering. Think of it as a digital fingerprintβ€”a lightweight digest representing the data in the table. ### Data Ingestion The initial interaction between the Verifier and the Prover involves data ingestion. In this process, when a service or client submits data for database inclusion, it first passes through the Verifier. Here, the Verifier generates (or updates) a commitment containing sufficient information to safeguard against tampering throughout the protocol. Once this commitment is established, the Verifier forwards the data to the database for storage, while retaining the commitment for future reference. ### Query Request The second interaction involves query requests, where the Verifier seeks data analytics on Prover-held data. When a service, client, or Verifier initiates a query request, it sends the request to the Prover. Here, the Prover parses the query, computes the result, and generates a proof, sent alongside the result to the Verifier, which is maintaining the commitment. The Verifier, armed with the proof and commitment, can verify the Prover's result against the query request. ## License Proof of SQL is licensed under the Decentralized Open Software License 1.0. Please see the [LICENSE](https://github.com/spaceandtimelabs/sxt-proof-of-sql/blob/main/LICENSE) file for details. --- ## File: docs/SQLSyntaxSpecification.md # PoSQL SQL Syntax Proof of SQL uses [sqlparser](https://github.com/apache/datafusion-sqlparser-rs) to parse SQL. It currently supports the following syntax. The syntax support is rapidly expanding, and we are happy to take suggestions about what should be added. Anyone submitting a PR must ensure that this is kept up to date. | **Category** | **Feature** | **Prover** | **EVM Verification** | |--------------------------|---------------------------|:----------:|:--------------------:| | **DataTypes** | Bool / Boolean | βœ… | βœ… | | | Uint8 (8 bits) | βœ… | βœ… | | | TinyInt (8 bits) | βœ… | βœ… | | | SmallInt (16 bits) | βœ… | βœ… | | | Int / Integer (32 bits) | βœ… | βœ… | | | BigInt (64 bits) | βœ… | βœ… | | | Int128 | βœ… | βœ… | | | Decimal75[^4] | βœ… | βœ… | | | Varchar[^1] | βœ… | βœ… | | | Varbinary[^1] | βœ… | βœ… | | | Timestamp | βœ… | βœ… | | **Operators** | AND, OR | βœ… | βœ… | | | NOT | βœ… | βœ… | | | +, –, * | βœ… | βœ… | | | / | ❌ | ❌ | | | =, != | βœ… | βœ… | | | >, β‰₯, <, ≀ | βœ… | βœ… | | **Aggregate Functions**[^3] | SUM | βœ… | βœ… | | | COUNT | βœ… | βœ… | | **SELECT Syntax** | WHERE clause | βœ… | βœ… | | | GROUP BY clause | βœ… | βœ… | | | LIMIT clause | βœ… | βœ… | | | OFFSET clause | βœ… | βœ… | | | UNION ALL operator | βœ… | βœ… | | | JOIN clause[^2] | βœ… | βœ… | [^1]: Currently, we do not support any string or binary operations beyond = and !=. [^2]: Currently, we only support some inner joins on one column. [^3]: Currently there are restrictions on aggregations we support. [^4]: Currently, we only support decimals up to 75 digits of precision and inequality operators only operate on decimals up to 38 digits of precision. For more details please refer to [DataFusion SELECT syntax](https://datafusion.apache.org/user-guide/sql/select.html). ## Reserved keywords The following keywords may not be used as aliases: - `count` --- ## File: solidity/preprocessor/README.md # Yul Preprocessor A preprocessor for Solidity files that resolves Yul function imports within assembly blocks, enabling better code organization and reusability for inline assembly code. ## Features - **Import Yul functions** from other `.presl` files into your assembly blocks - **Multiple imports per line**: Import several functions in a single statement - **Self imports**: Reference functions from different assembly blocks in the same file - **Relative path support**: Import from files using relative paths - **Circular dependencies allowed**: Files can import from each other - circular dependency groups are automatically resolved - **Transitive dependency resolution**: Importing from a file automatically includes all its dependencies - **Function deduplication**: Automatically deduplicates identical function imports - **Caching**: Efficiently processes files with intelligent caching - **Automatic formatting**: Runs `forge fmt` on output files for clean, consistent formatting ## Installation No installation required! Just use Python 3.6+: ```bash python3 yul_preprocessor.py ``` **Optional**: Install [Foundry](https://book.getfoundry.sh/getting-started/installation) for automatic formatting of output files with `forge fmt`. ## Usage ### Basic Usage Process all `.presl` and `.t.presl` files in a directory: ```bash python3 yul_preprocessor.py ./contracts ``` The preprocessor processes both `.presl` and `.t.presl` files (for test files), generating corresponding `.post.sol` and `.t.post.sol` output files respectively. The preprocessor automatically runs `forge fmt` on all generated `.post.sol` files to ensure clean, consistent formatting. If `forge` is not available in your PATH, the preprocessor will skip formatting with a warning. ### Import Syntax The preprocessor supports three import patterns: #### 1. Single Function Import ```solidity // import from ``` Example: ```solidity assembly { // import add5 from utils.presl let result := add5(10) } ``` #### 2. Multiple Functions Per Line ```solidity // import , , from ``` Example: ```solidity assembly { // import add, multiply, divide from math.presl let sum := add(5, 10) let product := multiply(3, 7) } ``` #### 3. Self Import ```solidity // import from self ``` Import functions from a different assembly block in the same file: ```solidity contract Example { function defineHelpers() external pure { assembly { function helper(x) -> result { result := mul(x, 2) } } } function useHelpers() external pure { assembly { // import helper from self let doubled := helper(5) } } } ``` ### Relative Paths Import from subdirectories or parent directories: ```solidity // import compute_fold from ../base/MathUtil.presl // import err from ./errors/Errors.sol // import safe_add from lib/SafeMath.presl ``` ## Complete Example ### Source File: `utils.presl` ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Utils { function process() external pure returns (uint256) { assembly { function add5(x) -> result { result := add(x, 5) } function multiply2(x) -> result { result := mul(x, 2) } } } } ``` ### Target File: `main.presl` ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Main { function compute() external pure returns (uint256) { assembly { // import add5, multiply2 from utils.presl let a := add5(10) // a = 15 let b := multiply2(a) // b = 30 } } } ``` ### Output File: `main.post.sol` ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Main { function compute() external pure returns (uint256) { assembly { function add5(x) -> result { result := add(x, 5) } function multiply2(x) -> result { result := mul(x, 2) } let a := add5(10) // a = 15 let b := multiply2(a) // b = 30 } } } ``` ## File Naming Convention - **Input files**: - `*.presl` - Standard files with import statements - `*.t.presl` - Test files with import statements - **Output files**: - `*.post.sol` - Processed standard files with imports resolved - `*.t.post.sol` - Processed test files with imports resolved The preprocessor automatically generates `.post.sol` files from `.presl` files and `.t.post.sol` files from `.t.presl` files. ## Circular Dependencies Circular dependencies are **fully supported**! When files A and B import from each other, they are processed as a unified dependency group. ### How It Works When a circular dependency is detected (e.g., A imports from B, B imports from A), the preprocessor: 1. Identifies all files in the circular group 2. Collects all Yul functions from all files in the group 3. Makes the complete set of functions available to every file in the group 4. Ensures all assembly blocks in the cycle have identical function sets ### Example **file_a.presl:** ```solidity assembly { // import funcB from file_b.presl function funcA() -> result { result := 1 } } ``` **file_b.presl:** ```solidity assembly { // import funcA from file_a.presl function funcB() -> result { result := 2 } } ``` After processing, both files will have both `funcA` and `funcB` in their assembly blocks. ### Nested Circular Dependencies The preprocessor handles complex scenarios where: - Group C imports from Group B (B0, B1) which are mutually dependent - Group B imports from Group A (A0, A1) which are mutually dependent - Group A imports from standalone files All transitive dependencies are correctly resolved and propagated. ## Error Handling The preprocessor detects and reports several types of errors: ### Missing Functions ``` ValueError: Function 'nonExistent' not found in utils.presl Available functions: add5, multiply2 ``` ### Function Signature Mismatch ``` ValueError: Function signature mismatch for 'add': Existing: function add(a, b) -> result New: function add(x) -> result ``` ## Architecture ### Key Components 1. **YulFunction**: Represents a parsed Yul function with name, signature, body, and full text 2. **YulPreprocessor**: Main processor class that handles: - File parsing and processing - Import resolution - Function extraction - Caching ### Processing Flow ``` Input .presl file ↓ Find assembly blocks ↓ For each assembly block: - Parse import statements - Resolve imported functions - Process dependencies recursively - Deduplicate functions - Insert functions at block start ↓ Generate .post.sol file ``` ## Testing Run the test suite: ```bash python3 -m pytest test_yul_preprocessor.py -v ``` Test coverage includes: - Basic imports - Multiple imports per line - Self imports - Relative path imports - Function deduplication - Circular dependency detection - Missing function errors - Complex function signatures - Multiple assembly blocks - Caching ## Advanced Features ### Function Deduplication If the same function is imported multiple times, only one copy is included: ```solidity assembly { // import add from math.presl // import add from math.presl // Deduplicated let x := add(1, 2) } ``` ### Multiple Assembly Blocks The preprocessor handles multiple assembly blocks within a single contract: ```solidity contract Multi { function first() external pure { assembly { // import func1 from lib.presl } } function second() external pure { assembly { // import func2 from lib.presl } } } ``` ### Caching Processed files are cached to improve performance when the same file is imported multiple times in a dependency tree. ## Behavior Notes - **Transitive Dependencies**: When you import a function from a file, you automatically get all functions from that file's assembly block. This ensures the complete dependency closure is available. - **Import statements** must be on a single line - **Import syntax** must follow: `// import from ` - Only **Yul functions** within `assembly {}` blocks are extracted and imported ## Contributing To add new features or fix bugs: 1. Add test cases in `test_files/` directory 2. Update `test_yul_preprocessor.py` with corresponding tests 3. Implement changes in `yul_preprocessor.py` 4. Run tests to ensure everything passes --- ## File: solidity/README.md # Development Dependencies Installation 1. `forge` ```bash curl -L https://foundry.paradigm.xyz | bash foundryup ``` 2. `lcov`/`genhtml` ```bash sudo apt install lcov ``` 3. `solhint` ```bash npm install solhint ``` 4. `slither` ```bash pipx install slither-analyzer ``` 5. `aderyn` (Recommended) ```bash npm install @cyfrin/aderyn ``` # Build and Test To generate the solidity library artifact, ```bash ./scripts/preprocess_yul_imports.sh src ``` The final artifact is `./src/verifier/Verifier.t.post.sol`. To run all tests and lints: ```bash ./scripts/lint-and-test.sh ```