Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0341e5a
Fix A24-4: add negative value eval tests for floor
thedavidmeister Feb 24, 2026
d84cea9
Fix A25-1: add inv(0) division by zero and negative input tests
thedavidmeister Feb 24, 2026
98bd93b
Fix A25-2, A25-3: add missing output and operand tests for sub
thedavidmeister Feb 26, 2026
5abd597
Fix A25-4, A25-5: add missing output tests for min and max
thedavidmeister Feb 26, 2026
b7dbcac
Fix A25-6: add sqrt negative input error path test
thedavidmeister Feb 26, 2026
719bb87
Fix A26-1, A26-2, A26-3: add operand disallowed tests for EVM opcodes
thedavidmeister Feb 26, 2026
2bc4b27
Fix A29-1: add operand disallowed test for uint256-max-value
thedavidmeister Feb 26, 2026
b79cc26
Fix A30-3: add yang-state word-word UnexpectedRHSChar test
thedavidmeister Feb 26, 2026
dd4303e
Fix A30-4: add stack name boundary tests for parseRHS fallback path
thedavidmeister Feb 26, 2026
3a27005
Fix A30-5: add unknown word bytecode construction boundary tests
thedavidmeister Feb 26, 2026
d6e3a26
Fix A31-1, A31-2: add unit tests for parseErrorOffset and handleError…
thedavidmeister Feb 26, 2026
6372304
Fix A32-1, A32-3, A32-4: add unit tests for skipComment, skipWhitespa…
thedavidmeister Feb 26, 2026
06b8785
Fix A33-2, A33-3: add literal dispatch and parseLiteral revert tests
thedavidmeister Feb 26, 2026
822ecdc
Fix A35-5: add mixed-case hex parsing tests
thedavidmeister Feb 26, 2026
914d272
Fix A36-6, A37-1, A37-3, A35-5: string and repeat test improvements
thedavidmeister Feb 26, 2026
2f8599e
Audit triage: fix test coverage findings and code quality items
thedavidmeister Feb 27, 2026
5fdb01c
Fix pass 4 code quality findings: relocate error, named constants, tr…
thedavidmeister Feb 27, 2026
0e63ee0
Suppress forge-lint unsafe-typecast warnings in test files
thedavidmeister Feb 27, 2026
a173f12
Fix all Rust audit findings: code quality, metadata, error handling
thedavidmeister Feb 27, 2026
7127f71
fmt
thedavidmeister Feb 28, 2026
9409c09
pointers
thedavidmeister Feb 28, 2026
92ba120
Delegate ERC165 supportsInterface to super in DISPaiRegistry
thedavidmeister Feb 28, 2026
b9f5f94
Fix CodeRabbit review findings: comments, fuzz overrides, test gaps
thedavidmeister Feb 28, 2026
60891fb
fmt
thedavidmeister Feb 28, 2026
66d08c0
Update DISPaiRegistry deploy address and codehash
thedavidmeister Feb 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ docs

# This is for our deploy scripts that report the addresses of deployed contracts
deployments
.fixes/
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ Testing patterns and conventions are in `TESTING.md`. Read that file before writ

- Test files are in `test/` mirroring `src/` structure, suffixed `.t.sol`
- Rust test fixtures (`crates/test_fixtures/`) deploy all four contracts on a local Anvil instance
- Always run test commands with `run_in_background: true` so work continues in parallel

## Process (Jidoka)

Expand Down
3 changes: 0 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ resolver = "2"
[workspace.package]
edition = "2024"
license = "CAL-1.0"
homepage = "https://github.com/rainprotocol/rain.interpreter"
homepage = "https://github.com/rainlanguage/rain.interpreter"

[workspace.dependencies]
alloy = { version = "1.0.9", features = ["sol-types", "json", "json-abi"] }
Expand Down
246 changes: 123 additions & 123 deletions audit/2026-02-17-03/triage.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[package]
name = "rain-i9r-cli"
version = "0.0.1"
edition = "2021"
license = "CAL-1.0"
edition.workspace = true
license.workspace = true
description = "Rain Interpreter CLI."
homepage = "https://github.com/rainprotocol/rain.orderbook"
homepage.workspace = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand Down
11 changes: 7 additions & 4 deletions crates/cli/src/commands/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use crate::execute::Execute;
use crate::fork::NewForkedEvmCliArgs;
use crate::output::SupportedOutputEncoding;
use alloy::primitives::{Address, U256};
use anyhow::anyhow;
use anyhow::Context;
use anyhow::Result;
use anyhow::anyhow;
use clap::Args;
use rain_interpreter_bindings::IInterpreterStoreV3::FullyQualifiedNamespace;
use rain_interpreter_eval::trace::RainEvalResult;
Expand Down Expand Up @@ -123,15 +123,18 @@ impl Execute for Eval {

match result {
Ok(res) => {
let rain_eval_result: RainEvalResult =
res.try_into().map_err(|e| anyhow!("{:?}", e))?;
let rain_eval_result: RainEvalResult = res.try_into().map_err(
|e: rain_interpreter_eval::trace::RainEvalResultFromRawCallResultError| {
anyhow!(e)
},
)?;
crate::output::output(
&self.output_path,
SupportedOutputEncoding::Binary,
format!("{:#?}", rain_eval_result).as_bytes(),
)
}
Err(e) => Err(anyhow!("Error: {:?}", e)),
Err(e) => Err(anyhow!(e)),
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/cli/src/commands/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::execute::Execute;
use crate::fork::NewForkedEvmCliArgs;
use crate::output::SupportedOutputEncoding;
use alloy::primitives::Address;
use anyhow::anyhow;
use anyhow::Result;
use anyhow::anyhow;
use clap::Args;
use rain_interpreter_eval::eval::ForkParseArgs;
use rain_interpreter_eval::fork::Forker;
Expand Down Expand Up @@ -58,7 +58,7 @@ impl Execute for Parse {
self.output_encoding.clone(),
res.raw.result.to_owned().to_vec().as_slice(),
),
Err(e) => Err(anyhow!("Error: {:?}", e)),
Err(e) => Err(anyhow!(e)),
}
}
}
6 changes: 3 additions & 3 deletions crates/dispair/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[package]
name = "rain_interpreter_dispair"
version = "0.1.0"
edition = "2024"
license = "CAL-1.0"
edition.workspace = true
license.workspace = true
description = "Rain Interpreter Rust Crate."
homepage = "https://github.com/rainlanguage/rain.interpreter"
homepage.workspace = true

[dependencies]
alloy = { workspace = true }
Expand Down
4 changes: 2 additions & 2 deletions crates/dispair/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use alloy::primitives::*;

/// DISPaiR
/// Struct representing Deployer/Interpreter/Store/Parser/Registry instances.
#[derive(Clone, Default)]
/// Struct representing Deployer/Interpreter/Store/Parser instances.
#[derive(Debug, Clone, Default)]
pub struct DISPaiR {
pub deployer: Address,
pub interpreter: Address,
Expand Down
4 changes: 2 additions & 2 deletions crates/eval/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use rain_interpreter_bindings::IInterpreterStoreV3::FullyQualifiedNamespace;
use rain_interpreter_bindings::IInterpreterV4::{EvalV4, eval4Call};
use rain_interpreter_bindings::IParserV2::parse2Call;

#[derive(Debug, Clone)]
/// Arguments for evaluating a Rainlang string in a forked EVM context
#[derive(Debug, Clone)]
pub struct ForkEvalArgs {
/// The Rainalang string to evaluate
pub rainlang_string: String,
Expand All @@ -30,8 +30,8 @@ pub struct ForkEvalArgs {
pub state_overlay: Vec<U256>,
}

#[derive(Debug, Clone)]
/// Arguments for parsing a Rainlang string in a forked EVM context
#[derive(Debug, Clone)]
pub struct ForkParseArgs {
/// The Rainlang string to parse
pub rainlang_string: String,
Expand Down
21 changes: 9 additions & 12 deletions crates/eval/src/fork.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ impl Forker {
let create_fork = CreateFork {
url: fork_url.to_string(),
enable_caching: true,
env: evm_opts.fork_evm_env(&fork_url).await.unwrap().0,
env: evm_opts.fork_evm_env(&fork_url).await?.0,
evm_opts,
};
let block_number = if let Some(block_number) = fork_block_number {
Expand Down Expand Up @@ -222,7 +222,7 @@ impl Forker {
}
}

/// Calls the forked EVM without commiting to state using alloy typed arguments.
/// Calls the forked EVM without committing to state using alloy typed arguments.
/// # Arguments
/// * `from_address` - The address to call from.
/// * `to_address` - The address to call to.
Expand Down Expand Up @@ -304,7 +304,7 @@ impl Forker {
Ok(ForkTypedReturn { raw, typed_return })
}

/// Calls the forked EVM without commiting to state.
/// Calls the forked EVM without committing to state.
/// # Arguments
/// * `from_address` - The address to call from.
/// * `to_address` - The address to call to.
Expand Down Expand Up @@ -381,18 +381,16 @@ impl Forker {
.ok_or(ForkCallError::ExecutorError("no active fork!".to_owned()))?;
let mut org_block_number = None;
let mut spec_id = SpecId::default();
#[allow(clippy::for_kv_map)]
for (_fork_id, (local_id, sid, bnumber)) in &self.forks {
for (local_id, sid, bnumber) in self.forks.values() {
if *local_id == active_fork_local_id {
spec_id = *sid;
org_block_number = Some(*bnumber);
break;
}
}
if org_block_number.is_none() {
return Err(ForkCallError::ExecutorError("no active fork!".to_owned()));
}
let block_number = block_number.unwrap_or(org_block_number.unwrap());
let org_block_number =
org_block_number.ok_or(ForkCallError::ExecutorError("no active fork!".to_owned()))?;
let block_number = block_number.unwrap_or(org_block_number);

self.executor.env_mut().evm_env.block_env.number = block_number;

Expand Down Expand Up @@ -511,7 +509,7 @@ impl Forker {
#[cfg(test)]
mod tests {
use super::*;
use crate::namespace::CreateNamespace;
use crate::namespace::qualify_namespace;
use alloy::eips::BlockNumberOrTag;
use alloy::sol;
use alloy::{
Expand Down Expand Up @@ -588,8 +586,7 @@ mod tests {
.await
.unwrap();

let fully_quallified_namespace =
CreateNamespace::qualify_namespace(namespace.into(), from_address);
let fully_quallified_namespace = qualify_namespace(namespace.into(), from_address);

let get = forker
.alloy_call(
Expand Down
28 changes: 13 additions & 15 deletions crates/eval/src/namespace.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
use alloy::primitives::{Address, B256, U256, keccak256};
use rain_interpreter_bindings::IInterpreterV4::FullyQualifiedNamespace;

pub struct CreateNamespace {}

impl CreateNamespace {
pub fn qualify_namespace(state_namespace: B256, sender: Address) -> FullyQualifiedNamespace {
// Combine state namespace and sender into a single 64-byte array
let mut combined = [0u8; 64];
combined[..32].copy_from_slice(state_namespace.as_slice());
combined[44..].copy_from_slice(sender.as_slice());

// Hash the combined array with Keccak256
let qualified_namespace = keccak256(combined);

FullyQualifiedNamespace::from(U256::from_be_bytes(qualified_namespace.0))
}
/// Qualifies a state namespace by hashing it with the sender address,
/// matching the on-chain `qualifyNamespace` logic in RainterpreterStore.
pub fn qualify_namespace(state_namespace: B256, sender: Address) -> FullyQualifiedNamespace {
// Combine state namespace and sender into a single 64-byte array
let mut combined = [0u8; 64];
combined[..32].copy_from_slice(state_namespace.as_slice());
combined[44..].copy_from_slice(sender.as_slice());

// Hash the combined array with Keccak256
let qualified_namespace = keccak256(combined);

FullyQualifiedNamespace::from(U256::from_be_bytes(qualified_namespace.0))
}

#[cfg(test)]
Expand All @@ -26,7 +24,7 @@ mod tests {
fn test_new() {
let state_namespace = B256::repeat_byte(0x1);
let sender = Address::repeat_byte(0x2);
let namespace = CreateNamespace::qualify_namespace(state_namespace, sender);
let namespace = qualify_namespace(state_namespace, sender);

// Got the below from chisel
let expected =
Expand Down
15 changes: 6 additions & 9 deletions crates/eval/src/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ use foundry_evm::executors::RawCallResult;
use rain_interpreter_bindings::IInterpreterV4::{eval4Call, eval4Return};
use revm::primitives::address;
use serde::{Deserialize, Serialize};
#[cfg(not(target_family = "wasm"))]
use std::ops::Deref;
use thiserror::Error;
#[cfg(target_family = "wasm")]
use wasm_bindgen_utils::{impl_wasm_traits, prelude::*};
Expand Down Expand Up @@ -76,12 +74,9 @@ impl TryFrom<ForkTypedReturn<eval4Call>> for RainEvalResult {
let call_trace_arena = typed_return
.raw
.traces
.ok_or(RainEvalResultFromRawCallResultError::MissingTraces)?
.to_owned();
let mut traces: Vec<RainSourceTrace> = call_trace_arena
.deref()
.clone()
.into_nodes()
.ok_or(RainEvalResultFromRawCallResultError::MissingTraces)?;
let traces: Vec<RainSourceTrace> = call_trace_arena
.nodes()
.iter()
.filter_map(|trace_node| {
if Address::from(trace_node.trace.address.into_array()) == RAIN_TRACER_ADDRESS {
Expand All @@ -90,8 +85,8 @@ impl TryFrom<ForkTypedReturn<eval4Call>> for RainEvalResult {
None
}
})
.rev()
.collect();
traces.reverse();

Ok(RainEvalResult {
reverted: typed_return.raw.reverted,
Expand All @@ -108,6 +103,8 @@ pub enum RainEvalResultFromRawCallResultError {
MissingTraces,
}

/// Note: `RawCallResult` does not contain ABI-decoded stack/writes, so these
/// fields are left empty. Only traces are populated from the call trace arena.
#[cfg(not(target_family = "wasm"))]
impl TryFrom<RawCallResult> for RainEvalResult {
type Error = RainEvalResultFromRawCallResultError;
Expand Down
8 changes: 3 additions & 5 deletions crates/parser/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
[package]
name = "rain_interpreter_parser"
version = "0.1.0"
edition = "2024"
license = "CAL-1.0"
edition.workspace = true
license.workspace = true
description = "Rain Interpreter Parser Rust Crate."
homepage = "https://github.com/rainlanguage/rain.interpreter"
homepage.workspace = true

[dependencies]
alloy-ethers-typecast = { workspace = true }
rain_interpreter_dispair = { workspace = true }
rain_interpreter_bindings = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
alloy = { workspace = true }
thiserror = { workspace = true }

Expand Down
1 change: 0 additions & 1 deletion crates/test_fixtures/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
serde_json = { workspace = true }
alloy = { workspace = true, features = ["node-bindings", "sol-types", "rpc-types", "provider-http", "network", "contract", "signer-local"] }

[target.'cfg(target_family = "wasm")'.dependencies]
Expand Down
7 changes: 1 addition & 6 deletions src/abstract/BaseRainterpreterSubParser.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {LibParseOperand} from "../lib/parse/LibParseOperand.sol";
import {IDescribedByMetaV1} from "rain.metadata/interface/IDescribedByMetaV1.sol";
import {IParserToolingV1} from "rain.sol.codegen/interface/IParserToolingV1.sol";
import {ISubParserToolingV1} from "rain.sol.codegen/interface/ISubParserToolingV1.sol";
import {SubParserIndexOutOfBounds} from "../error/ErrSubParse.sol";

/// @dev This is a placeholder for the subparser function pointers.
/// The subparser function pointers are a list of 16 bit function pointers,
Expand All @@ -38,12 +39,6 @@ bytes constant SUB_PARSER_OPERAND_HANDLERS = hex"";
/// parsers.
bytes constant SUB_PARSER_LITERAL_PARSERS = hex"";

/// @dev Thrown when a sub parser dispatch index is out of bounds for the
/// function pointer table.
/// @param index The out-of-bounds index.
/// @param length The number of function pointers available.
error SubParserIndexOutOfBounds(uint256 index, uint256 length);

/// Base implementation of `ISubParserV4`. Inherit from this contract and
/// override the virtual functions to align all the relevant pointers and
/// metadata bytes so that it can actually run.
Expand Down
8 changes: 7 additions & 1 deletion src/concrete/RainterpreterDISPaiRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@ pragma solidity =0.8.25;

import {LibInterpreterDeploy} from "../lib/deploy/LibInterpreterDeploy.sol";
import {IDISPaiRegistry} from "../interface/IDISPaiRegistry.sol";
import {ERC165} from "openzeppelin-contracts/contracts/utils/introspection/ERC165.sol";

/// @title RainterpreterDISPaiRegistry
/// @notice DISPaiR registry contract that exposes the deterministic Zoltu deploy
/// addresses of the four core interpreter components: Deployer, Interpreter,
/// Store, and Parser. Deployed via the same Zoltu pattern so that external
/// tooling can discover all component addresses from a single known registry
/// address.
contract RainterpreterDISPaiRegistry is IDISPaiRegistry {
contract RainterpreterDISPaiRegistry is IDISPaiRegistry, ERC165 {
/// @inheritdoc ERC165
function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
return interfaceId == type(IDISPaiRegistry).interfaceId || super.supportsInterface(interfaceId);
}

/// @inheritdoc IDISPaiRegistry
function expressionDeployerAddress() external pure override returns (address) {
return LibInterpreterDeploy.EXPRESSION_DEPLOYER_DEPLOYED_ADDRESS;
Expand Down
4 changes: 2 additions & 2 deletions src/concrete/RainterpreterParser.sol
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,12 @@ contract RainterpreterParser is ERC165, IParserToolingV1 {
}

/// External function to build the operand handler function pointers.
function buildOperandHandlerFunctionPointers() external pure returns (bytes memory) {
function buildOperandHandlerFunctionPointers() external pure override returns (bytes memory) {
return LibAllStandardOps.operandHandlerFunctionPointers();
}

/// External function to build the literal parser function pointers.
function buildLiteralParserFunctionPointers() external pure returns (bytes memory) {
function buildLiteralParserFunctionPointers() external pure override returns (bytes memory) {
return LibAllStandardOps.literalParserFunctionPointers();
}
}
Loading