Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions src/generated/Rainterpreter.pointers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
pragma solidity =0.8.25;

/// @dev Hash of the known bytecode.
bytes32 constant BYTECODE_HASH = bytes32(0xddf6e7bcbe3d3394d100881801381404f5f91c19b9d21d348482ff09fbabd3f4);
bytes32 constant BYTECODE_HASH = bytes32(0xe67bfbe2d8ebe116b64bcb02d5e29330d9ade17c1c3367def6883adee4d399e7);

/// @dev The function pointers known to the interpreter for dynamic dispatch.
/// By setting these as a constant they can be inlined into the interpreter
/// and loaded at eval time for very low gas (~100) due to the compiler
/// optimising it to a single `codecopy` to build the in memory bytes array.
bytes constant OPCODE_FUNCTION_POINTERS =
hex"070a073c076008ec09b509c709d909f20a160a4a0a5b0a6c0b0e0b2d0bdc0c600c710c820c820c930d200d390d4d0d660d790ddb0e290e5a0ea80ef60fdd";
hex"07120744076808f409bd09cf09e109fa0a1e0a520a630a740b160b350be40c680c790c8a0c8a0c9b0d280d410d550d6e0d870d9a0dfc0e4a0e7b0ec90f170ffe";
6 changes: 3 additions & 3 deletions src/generated/RainterpreterExpressionDeployer.pointers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
pragma solidity =0.8.25;

/// @dev Hash of the known bytecode.
bytes32 constant BYTECODE_HASH = bytes32(0x92914df17907d045c2e205ffc7629e9362435836efff6ff7bf1d5cb6ead71c74);
bytes32 constant BYTECODE_HASH = bytes32(0x84daba4ae586abd5a30c407378175e919ea82b291b9bff08604debfa5e59d183);

/// @dev The hash of the meta that describes the contract.
bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0xf2422b9957601bb0f760ed0062347ad9e9fda6fad2f4b119aa9ddb874618397a);
bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0x53bf9fadcc5583af0b446b3d62f227146b09d4476ca6f3998e21c1410fe04965);

/// @dev The function pointers for the integrity check fns.
bytes constant INTEGRITY_FUNCTION_POINTERS =
hex"0db00e2e0e92100c1016101610201029104410ea10ea114611be10161020100c100c100c100c11cb101610161016100c11d511fd100c11fd11fd102011cb";
hex"0db80e360e9a1014101e101e10281031104c10f210f2114e11c6101e1028101410141014101411d3101e101e101e101e101411dd1205101412051205102811d3";
8 changes: 4 additions & 4 deletions src/generated/RainterpreterParser.pointers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
pragma solidity =0.8.25;

/// @dev Hash of the known bytecode.
bytes32 constant BYTECODE_HASH = bytes32(0x58f6018d11af765b99cb6f438c3cfcbb09b18a8406b4fafa5cb1645b3ac674af);
bytes32 constant BYTECODE_HASH = bytes32(0xd292c1e12f252b9afbe785ce00e0c8980c99b3469982fc5ffdab15e8017acd83);

/// @dev The parse meta that is used to lookup word definitions.
/// The structure of the parse meta is:
Expand All @@ -29,7 +29,7 @@ bytes32 constant BYTECODE_HASH = bytes32(0x58f6018d11af765b99cb6f438c3cfcbb09b18
/// bit count of the previous bloom filter. If we reach the end of the bloom
/// filters then we have a miss.
bytes constant PARSE_META =
hex"01092404900810009008201088000400e0010080000800001028010100080900090002cd70471d14fbe61685cf8700f861d119d0e20c0e8be82d1e7c7f8004e5ab37184d06d00d67df8415fedd510602c61f08bcae760bcd088610f1c17805ac4b9f0a09bc09170ce432091ef7b011dfb72601a91367121faf890c2b9f481be804c11abd68ce14811fdb034a067c0fffb07907743c4a1c15024513edf10f";
hex"01092404940810009008201088000400e0010080000800001028010100080900090002cd70471e14fbe61685cf8700f861d11ad0e20c0e8be82d1f7c7f8004e5ab37194d06d00d67df8415fedd510602c61f08bcae760bcd088610f1c17805ac4b9f0a09bc09180ce432091ef7b011dfb72601a91367121faf890c2b9f481ce804c11bbd68ce14811fdb1726c459034a067c0fffb07907743c4a1d15024513edf10f";

/// @dev The build depth of the parser meta.

Expand All @@ -39,11 +39,11 @@ uint8 constant PARSE_META_BUILD_DEPTH = 2;
/// These positional indexes all map to the same indexes looked up in the parse
/// meta.
bytes constant OPERAND_HANDLER_FUNCTION_POINTERS =
hex"1992199219921a671b7e1b7e1b7e1a671a671992199219921b7e1b7e1b7e1b7e1b7e1b7e1b7e1b7e1b7e1b7e1b7e1b7e1b7e1b7e1b7e1b7e19921b7e1b7e";
hex"199a199a199a1a6f1b861b861b861a6f1a6f199a199a199a1b861b861b861b861b861b861b861b861b861b861b861b861b861b861b861b861b86199a1b861b86";

/// @dev Every two bytes is a function pointer for a literal parser.
/// Literal dispatches are determined by the first byte(s) of the literal
/// rather than a full word lookup, and are done with simple conditional
/// jumps as the possibilities are limited compared to the number of words we
/// have.
bytes constant LITERAL_PARSER_FUNCTION_POINTERS = hex"14da170c174f17ed";
bytes constant LITERAL_PARSER_FUNCTION_POINTERS = hex"14e21714175717f5";
14 changes: 7 additions & 7 deletions src/lib/op/LibAllStandardOps.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ import {LibOpGreaterThan} from "./logic/LibOpGreaterThan.sol";
import {LibOpGreaterThanOrEqualToNP} from "./logic/LibOpGreaterThanOrEqualToNP.sol";
import {LibOpIfNP} from "./logic/LibOpIfNP.sol";
import {LibOpIsZeroNP} from "./logic/LibOpIsZeroNP.sol";
import {LibOpLessThanNP} from "./logic/LibOpLessThanNP.sol";
import {LibOpLessThan} from "./logic/LibOpLessThan.sol";
import {LibOpLessThanOrEqualToNP} from "./logic/LibOpLessThanOrEqualToNP.sol";

// import {LibOpExponentialGrowth} from "./math/growth/LibOpExponentialGrowth.sol";
Expand Down Expand Up @@ -105,7 +105,7 @@ import {LibParseLiteralHex} from "../parse/literal/LibParseLiteralHex.sol";
import {LibParseLiteralSubParseable} from "../parse/literal/LibParseLiteralSubParseable.sol";

/// @dev Number of ops currently provided by `AllStandardOps`.
uint256 constant ALL_STANDARD_OPS_LENGTH = 31;
uint256 constant ALL_STANDARD_OPS_LENGTH = 32;

/// @title LibAllStandardOps
/// @notice Every opcode available from the core repository laid out as a single
Expand Down Expand Up @@ -221,7 +221,7 @@ library LibAllStandardOps {
// "If the first input is nonzero, the second input is used. Otherwise, the third input is used. If is eagerly evaluated."
// ),
// AuthoringMetaV2("is-zero", "1 if the input is 0, 0 otherwise."),
// AuthoringMetaV2("less-than", "1 if the first input is less than the second input, 0 otherwise."),
AuthoringMetaV2("less-than", "true if the first input is less than the second input, false otherwise."),
// AuthoringMetaV2(
// "less-than-or-equal-to", "1 if the first input is less than or equal to the second input, 0 otherwise."
// ),
Expand Down Expand Up @@ -431,8 +431,8 @@ library LibAllStandardOps {
// LibParseOperand.handleOperandDisallowed,
// // is-zero
// LibParseOperand.handleOperandDisallowed,
// // less-than
// LibParseOperand.handleOperandDisallowed,
// less-than
LibParseOperand.handleOperandDisallowed,
// // less-than-or-equal-to
// LibParseOperand.handleOperandDisallowed,
// // exponential-growth
Expand Down Expand Up @@ -578,7 +578,7 @@ library LibAllStandardOps {
// LibOpGreaterThanOrEqualToNP.integrity,
// LibOpIfNP.integrity,
// LibOpIsZeroNP.integrity,
// LibOpLessThanNP.integrity,
LibOpLessThan.integrity,
// LibOpLessThanOrEqualToNP.integrity,
// LibOpExponentialGrowth.integrity,
// LibOpLinearGrowth.integrity,
Expand Down Expand Up @@ -690,7 +690,7 @@ library LibAllStandardOps {
// LibOpGreaterThanOrEqualToNP.run,
// LibOpIfNP.run,
// LibOpIsZeroNP.run,
// LibOpLessThanNP.run,
LibOpLessThan.run,
// LibOpLessThanOrEqualToNP.run,
// LibOpExponentialGrowth.run,
// LibOpLinearGrowth.run,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,44 @@ import {OperandV2} from "rain.interpreter.interface/interface/unstable/IInterpre
import {Pointer} from "rain.solmem/lib/LibPointer.sol";
import {IntegrityCheckState} from "../../integrity/LibIntegrityCheck.sol";
import {InterpreterState} from "../../state/LibInterpreterState.sol";
import {StackItem} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol";
import {Float, LibDecimalFloat} from "rain.math.float/lib/LibDecimalFloat.sol";

/// @title LibOpLessThanNP
/// @title LibOpLessThan
/// @notice Opcode to return 1 if the first item on the stack is less than
/// the second item on the stack, else 0.
library LibOpLessThanNP {
library LibOpLessThan {
function integrity(IntegrityCheckState memory, OperandV2) internal pure returns (uint256, uint256) {
return (2, 1);
}

/// LT
/// LT is 1 if the first item is less than the second item, else 0.
function run(InterpreterState memory, OperandV2, Pointer stackTop) internal pure returns (Pointer) {
Float a;
Float b;
assembly ("memory-safe") {
let a := mload(stackTop)
a := mload(stackTop)
stackTop := add(stackTop, 0x20)
mstore(stackTop, lt(a, mload(stackTop)))
b := mload(stackTop)
}
bool lessThan = LibDecimalFloat.lt(a, b);
assembly ("memory-safe") {
mstore(stackTop, lessThan)
}
return stackTop;
}

/// Gas intensive reference implementation of LT for testing.
function referenceFn(InterpreterState memory, OperandV2, uint256[] memory inputs)
function referenceFn(InterpreterState memory, OperandV2, StackItem[] memory inputs)
internal
pure
returns (uint256[] memory outputs)
returns (StackItem[] memory outputs)
{
outputs = new uint256[](1);
outputs[0] = inputs[0] < inputs[1] ? 1 : 0;
Float a = Float.wrap(StackItem.unwrap(inputs[0]));
Float b = Float.wrap(StackItem.unwrap(inputs[1]));
bool lessThan = LibDecimalFloat.lt(a, b);
outputs = new StackItem[](1);
outputs[0] = StackItem.wrap(bytes32(uint256(lessThan ? 1 : 0)));
}
}
122 changes: 122 additions & 0 deletions test/src/lib/op/logic/LibOpLessThan.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// SPDX-License-Identifier: CAL
pragma solidity =0.8.25;

import {OpTest} from "test/abstract/OpTest.sol";
import {LibContext} from "rain.interpreter.interface/lib/caller/LibContext.sol";
import {LibOpLessThan} from "src/lib/op/logic/LibOpLessThan.sol";
import {
IInterpreterV4,
OperandV2,
SourceIndexV2,
FullyQualifiedNamespace,
EvalV4
} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol";
import {SignedContextV1} from "rain.interpreter.interface/interface/IInterpreterCallerV3.sol";
import {InterpreterState} from "src/lib/state/LibInterpreterState.sol";
import {IntegrityCheckState, BadOpInputsLength} from "src/lib/integrity/LibIntegrityCheck.sol";
import {LibOperand} from "test/lib/operand/LibOperand.sol";
import {StackItem} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol";

contract LibOpLessThanTest is OpTest {
/// Directly test the integrity logic of LibOpLessThan. No matter the
/// operand inputs, the calc inputs must be 2, and the calc outputs must be
/// 1.
function testOpLessThanIntegrityHappy(
IntegrityCheckState memory state,
uint8 inputs,
uint8 outputs,
uint16 operandData
) external pure {
inputs = uint8(bound(inputs, 0, 0x0F));
outputs = uint8(bound(outputs, 0, 0x0F));
(uint256 calcInputs, uint256 calcOutputs) =
LibOpLessThan.integrity(state, LibOperand.build(inputs, outputs, operandData));

// The inputs from the operand are ignored. The op is always 2 inputs.
assertEq(calcInputs, 2);
assertEq(calcOutputs, 1);
}

/// Directly test the runtime logic of LibOpLessThan.
function testOpLessThanRun(StackItem input1, StackItem input2) external view {
InterpreterState memory state = opTestDefaultInterpreterState();
StackItem[] memory inputs = new StackItem[](2);
inputs[0] = input1;
inputs[1] = input2;
OperandV2 operand = LibOperand.build(uint8(inputs.length), 1, 0);
opReferenceCheck(state, operand, LibOpLessThan.referenceFn, LibOpLessThan.integrity, LibOpLessThan.run, inputs);
}

/// Test the eval of less than opcode parsed from a string. Tests 2 inputs.
/// Both inputs are 0.
function testOpLessThanEval2ZeroInputs() external view {
checkHappy("_: less-than(0 0);", 0, "");
}

/// Test the eval of less than opcode parsed from a string. Tests 2 inputs.
/// The first input is 0, the second input is 1.
function testOpLessThanEval2InputsFirstZeroSecondOne() external view {
checkHappy("_: less-than(0 1);", bytes32(uint256(1)), "");
}

/// Test the eval of less than opcode parsed from a string. Tests 2 inputs.
/// The first input is 1, the second input is 0.
function testOpLessThanNPEval2InputsFirstOneSecondZero() external view {
checkHappy("_: less-than(1 0);", bytes32(uint256(0)), "");
}

/// Test the eval of less than opcode parsed from a string. Tests 2 inputs.
/// Both inputs are 1.
function testOpLessThanNPEval2InputsBothOne() external view {
checkHappy("_: less-than(1 1);", bytes32(uint256(0)), "");
}

// Test 1.1 lt 1.2, which should return 1.
function testOpLessThanNP1_1Lt1_2() external view {
checkHappy("_: less-than(1.1 1.2);", bytes32(uint256(1)), "");
}

/// Test 1.0 lt 1 which should return 0.
function testOpLessThanNP1_0Lt1() external view {
checkHappy("_: less-than(1.0 1);", bytes32(uint256(0)), "");
}

// Test -1.1 lt -1.2, which should return 0.
function testOpLessThanNPMinus1_1LtMinus1_2() external view {
checkHappy("_: less-than(-1.1 -1.2);", bytes32(uint256(0)), "");
}

/// Test -1 lt 0, which should return 1.
function testOpLessThanNPMinus1Lt0() external view {
checkHappy("_: less-than(-1 0);", bytes32(uint256(1)), "");
Comment on lines +64 to +91

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Fix inconsistent function naming in test comments.

Several test function names still contain the "NP" suffix from the old implementation (lines 64, 70, 75, 80, 85, 90), which should be removed for consistency.

-    function testOpLessThanNPEval2InputsFirstOneSecondZero() external view {
+    function testOpLessThanEval2InputsFirstOneSecondZero() external view {

-    function testOpLessThanNPEval2InputsBothOne() external view {
+    function testOpLessThanEval2InputsBothOne() external view {

-    function testOpLessThanNP1_1Lt1_2() external view {
+    function testOpLessThan1_1Lt1_2() external view {

-    function testOpLessThanNP1_0Lt1() external view {
+    function testOpLessThan1_0Lt1() external view {

-    function testOpLessThanNPMinus1_1LtMinus1_2() external view {
+    function testOpLessThanMinus1_1LtMinus1_2() external view {

-    function testOpLessThanNPMinus1Lt0() external view {
+    function testOpLessThanMinus1Lt0() external view {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function testOpLessThanNPEval2InputsFirstOneSecondZero() external view {
checkHappy("_: less-than(1 0);", bytes32(uint256(0)), "");
}
/// Test the eval of less than opcode parsed from a string. Tests 2 inputs.
/// Both inputs are 1.
function testOpLessThanNPEval2InputsBothOne() external view {
checkHappy("_: less-than(1 1);", bytes32(uint256(0)), "");
}
// Test 1.1 lt 1.2, which should return 1.
function testOpLessThanNP1_1Lt1_2() external view {
checkHappy("_: less-than(1.1 1.2);", bytes32(uint256(1)), "");
}
/// Test 1.0 lt 1 which should return 0.
function testOpLessThanNP1_0Lt1() external view {
checkHappy("_: less-than(1.0 1);", bytes32(uint256(0)), "");
}
// Test -1.1 lt -1.2, which should return 0.
function testOpLessThanNPMinus1_1LtMinus1_2() external view {
checkHappy("_: less-than(-1.1 -1.2);", bytes32(uint256(0)), "");
}
/// Test -1 lt 0, which should return 1.
function testOpLessThanNPMinus1Lt0() external view {
checkHappy("_: less-than(-1 0);", bytes32(uint256(1)), "");
function testOpLessThanEval2InputsFirstOneSecondZero() external view {
checkHappy("_: less-than(1 0);", bytes32(uint256(0)), "");
}
/// Test the eval of less than opcode parsed from a string. Tests 2 inputs.
/// Both inputs are 1.
function testOpLessThanEval2InputsBothOne() external view {
checkHappy("_: less-than(1 1);", bytes32(uint256(0)), "");
}
// Test 1.1 lt 1.2, which should return 1.
function testOpLessThan1_1Lt1_2() external view {
checkHappy("_: less-than(1.1 1.2);", bytes32(uint256(1)), "");
}
/// Test 1.0 lt 1 which should return 0.
function testOpLessThan1_0Lt1() external view {
checkHappy("_: less-than(1.0 1);", bytes32(uint256(0)), "");
}
// Test -1.1 lt -1.2, which should return 0.
function testOpLessThanMinus1_1LtMinus1_2() external view {
checkHappy("_: less-than(-1.1 -1.2);", bytes32(uint256(0)), "");
}
/// Test -1 lt 0, which should return 1.
function testOpLessThanMinus1Lt0() external view {
checkHappy("_: less-than(-1 0);", bytes32(uint256(1)), "");
}
🤖 Prompt for AI Agents
In test/src/lib/op/logic/LibOpLessThan.t.sol between lines 64 and 91, the test
function names include the outdated "NP" suffix. Rename these functions to
remove the "NP" suffix for consistency with the current naming convention,
ensuring the function names accurately reflect their purpose without the old
suffix.

}

/// Test that a less than to without inputs fails integrity check.
function testOpLessThanToNPEvalFail0Inputs() public {
vm.expectRevert(abi.encodeWithSelector(BadOpInputsLength.selector, 0, 2, 0));
bytes memory bytecode = iDeployer.parse2("_: less-than();");
(bytecode);
}

/// Test that a less than to with 1 input fails integrity check.
function testOpLessThanToNPEvalFail1Input() public {
vm.expectRevert(abi.encodeWithSelector(BadOpInputsLength.selector, 1, 2, 1));
bytes memory bytecode = iDeployer.parse2("_: less-than(0x00);");
(bytecode);
}

/// Test that a less than to with 3 inputs fails integrity check.
function testOpLessThanToNPEvalFail3Inputs() public {
vm.expectRevert(abi.encodeWithSelector(BadOpInputsLength.selector, 3, 2, 3));
bytes memory bytecode = iDeployer.parse2("_: less-than(0x00 0x00 0x00);");
(bytecode);
}

function testOpLessThanNPZeroOutputs() external {
checkBadOutputs(": less-than(0 0);", 2, 1, 0);
}

function testOpLessThanNPTwoOutputs() external {
checkBadOutputs("_ _: less-than(30 0);", 2, 1, 2);
}
}
Loading
Loading