diff --git a/src/generated/Rainterpreter.pointers.sol b/src/generated/Rainterpreter.pointers.sol index 37d816c00..ff492ec3d 100644 --- a/src/generated/Rainterpreter.pointers.sol +++ b/src/generated/Rainterpreter.pointers.sol @@ -10,11 +10,11 @@ pragma solidity =0.8.25; /// @dev Hash of the known bytecode. -bytes32 constant BYTECODE_HASH = bytes32(0xd00f0e827724d7c7ced9e96d7eb3b9133a04f7fc0ddd8b988978c3b8f350445e); +bytes32 constant BYTECODE_HASH = bytes32(0x1ed65c8c3d3ffc05de391f627229478134e639737bdd866422cd8df0abe08657); /// @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"07f30825084909d50a9e0ab00ac20adb0aff0b330b440b550bf70c160cd40d840e080f4a107d0cd41176122812ca1342135313641364137513e0148b14a414b8151715301549158215ad15c615df16061619167b16c91717176517b31801184f1880188e18dc190d195b198c19da1a281b1e"; + hex"07fc082e085209de0aa70ab90acb0ae40b080b3c0b4d0b5e0c000c1f0cdd0d8d0e110f5310860cdd117f123112d3134b135c136d136d137e13e914f41573158c15a015ff16181631166a169516ae16c716ee1701176317b117ff184d189b18e919371968197619c419f51a431a741ac21b101c06"; diff --git a/src/generated/RainterpreterExpressionDeployer.pointers.sol b/src/generated/RainterpreterExpressionDeployer.pointers.sol index 88eef67dd..f1234719f 100644 --- a/src/generated/RainterpreterExpressionDeployer.pointers.sol +++ b/src/generated/RainterpreterExpressionDeployer.pointers.sol @@ -10,11 +10,11 @@ pragma solidity =0.8.25; /// @dev Hash of the known bytecode. -bytes32 constant BYTECODE_HASH = bytes32(0x0cff864c099ad3c101d7a6b97f053d616eb60d1411a73c7b302fe54cd388ea42); +bytes32 constant BYTECODE_HASH = bytes32(0xf2207cd3afbd9ca7533c86525c77d0d2b740db0becbe048a274555b9a6a2c12a); /// @dev The hash of the meta that describes the contract. -bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0x66598d8ffa2941c707072bea547b0b1fe0d8d86e67c326cb5d7463e4b8079f85); +bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0x7627bf6e126d126d07bea44350d6ff7a6893030e6217749ccc2d47d851eebd8c); /// @dev The function pointers for the integrity check fns. bytes constant INTEGRITY_FUNCTION_POINTERS = - hex"0e800efe0f6210dc10e610e610f010f9111411ba11ba1216128e129b10e610f0129b10e610f010e610e610e610f010dc10dc10dc10dc12a512ca10e610e612a510e610e6129b10f010e610e6129b10dc12d412d412d412d412d412d412ee10dc10f012d410dc12d410dc12ee12ee10f012ca"; + hex"0e880f060f6a10e410ee10ee10f81101111c11c211c2121e129612a310ee10f812a310ee10f810ee10ee10ee10f810e410e410e410e412ad12d212ec10ee10ee12ad10ee10ee12a310f810ee10ee12a310e412f612f612f612f612f612f6131010e410f812f610e412f610e41310131010f812ec"; diff --git a/src/generated/RainterpreterParser.pointers.sol b/src/generated/RainterpreterParser.pointers.sol index 0f1f2d0f1..fe6dd1d0c 100644 --- a/src/generated/RainterpreterParser.pointers.sol +++ b/src/generated/RainterpreterParser.pointers.sol @@ -10,7 +10,7 @@ pragma solidity =0.8.25; /// @dev Hash of the known bytecode. -bytes32 constant BYTECODE_HASH = bytes32(0xc7ad4977b4fdb19ab6b2e08e1d96d3b9ab0eee10b20c226f437c104cb2d343e1); +bytes32 constant BYTECODE_HASH = bytes32(0x6ffd9e7d346f2c3f3aa8e40939dcd8b27f15e0b57a639fb5b8674df19c36bb64); /// @dev The parse meta that is used to lookup word definitions. /// The structure of the parse meta is: @@ -29,7 +29,7 @@ bytes32 constant BYTECODE_HASH = bytes32(0xc7ad4977b4fdb19ab6b2e08e1d96d3b9ab0ee /// 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"0178ac80600029b010910908a00a7c48c001280030060500180000200cc2200408e426fe9a970c95d83822c0361a3748145c3834b3f332d587e204afc8f01e95a26007b9a51f0d5e6f2803ce312215fae0a12a1233de118cfd53051c784d28a55ade3412233f1681ac9624ac6dfc33a4e5e9205fc32a0e92b2401dbbd837100ef76b00cd8814215902e62d9714ec133135922c0d473b1f3ddb3123f880501427634f2fe048d30f23b4970ac0e53d0b895f991ac908be0983724f12c080bc359d7b7d2b757aea303a7b86296f62e02e5c0ab5086d2a7b2590a6b319bcb24402af7d4506414b8117b04fb31cb2182f18ef532927c301e31b9314ad36fa5d8001336a5931ab2461"; + hex"0278ac80600029b010910908a00a7c48c001280030060500180000200cc2200408e400000000000000000000000000000000000000000000000000200000000000000027fe9a970c95d83823c0361a3848145c3934b3f333d587e204afc8f01f95a26007b9a51f0d5e6f2803ce312215fae0a12b1233de118cfd53051c784d29a55ade3512233f1681ac9625ac6dfc34a4e5e9215fc32a0e92b2401ebbd837100ef76b00cd8814225902e62e9714ec133135922d0d473b203ddb3124f880501427634f1c69b5340f23b4970ac0e53d0b895f991ac908be0983724f12c080bc369d7b7d2c757aea313a7b862a6f62e02f5c0ab5086d2a7b2690a6b319bcb24402af7d4506414b8117b04fb31db2182f18ef532928c301e31b9314ad37fa5d8001336a5932ab2461308fa388"; /// @dev The build depth of the parser meta. @@ -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"1a651a651a651b3a1c511c511c511b3a1b3a1a651a651a651c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511c511a651c511c51"; + hex"1a6d1a6d1a6d1b421c591c591c591b421b421a6d1a6d1a6d1c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591c591a6d1c591c59"; /// @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"15ad17df182218c0"; +bytes constant LITERAL_PARSER_FUNCTION_POINTERS = hex"15b517e7182a18c8"; diff --git a/src/lib/op/LibAllStandardOps.sol b/src/lib/op/LibAllStandardOps.sol index 75528e100..6ddb59c7b 100644 --- a/src/lib/op/LibAllStandardOps.sol +++ b/src/lib/op/LibAllStandardOps.sol @@ -47,7 +47,7 @@ import {LibOpChainId} from "./evm/LibOpChainId.sol"; import {LibOpTimestamp} from "./evm/LibOpTimestamp.sol"; import {LibOpAny} from "./logic/LibOpAny.sol"; -import {LibOpConditionsNP} from "./logic/LibOpConditionsNP.sol"; +import {LibOpConditions} from "./logic/LibOpConditions.sol"; import {LibOpEnsure} from "./logic/LibOpEnsure.sol"; import {LibOpEqualTo} from "./logic/LibOpEqualTo.sol"; import {LibOpBinaryEqualTo} from "./logic/LibOpBinaryEqualTo.sol"; @@ -109,7 +109,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 = 57; +uint256 constant ALL_STANDARD_OPS_LENGTH = 58; /// @title LibAllStandardOps /// @notice Every opcode available from the core repository laid out as a single @@ -208,10 +208,10 @@ library LibAllStandardOps { AuthoringMetaV2("block-timestamp", "The current block timestamp."), AuthoringMetaV2("now", "The current block timestamp."), AuthoringMetaV2("any", "The first non-zero value out of all inputs, or 0 if every input is 0."), - // AuthoringMetaV2( - // "conditions", - // "Treats inputs as pairwise condition/value pairs. The first nonzero condition's value is used. If no conditions are nonzero, the expression reverts. Provide a constant nonzero value to define a fallback case. If the number of inputs is odd, the final value is used as an error string in the case that no conditions match." - // ), + AuthoringMetaV2( + "conditions", + "Treats inputs as pairwise condition/value pairs. The first nonzero condition's value is used. If no conditions are nonzero, the expression reverts. Provide a constant nonzero value to define a fallback case. If the number of inputs is odd, the final value is used as an error string in the case that no conditions match." + ), AuthoringMetaV2( "ensure", "Reverts if the first input is 0. This has to be exactly binary 0 (i.e. NOT the number 0). The second input is a string that is used as the revert reason if the first input is 0. Has 0 outputs." @@ -413,8 +413,8 @@ library LibAllStandardOps { LibParseOperand.handleOperandDisallowed, // any LibParseOperand.handleOperandDisallowed, - // // conditions - // LibParseOperand.handleOperandDisallowed, + // conditions + LibParseOperand.handleOperandDisallowed, // ensure LibParseOperand.handleOperandDisallowed, // equal-to @@ -564,7 +564,7 @@ library LibAllStandardOps { // now LibOpTimestamp.integrity, LibOpAny.integrity, - // LibOpConditionsNP.integrity, + LibOpConditions.integrity, LibOpEnsure.integrity, LibOpEqualTo.integrity, LibOpBinaryEqualTo.integrity, @@ -672,7 +672,7 @@ library LibAllStandardOps { // now LibOpTimestamp.run, LibOpAny.run, - // LibOpConditionsNP.run, + LibOpConditions.run, LibOpEnsure.run, LibOpEqualTo.run, LibOpBinaryEqualTo.run, diff --git a/src/lib/op/logic/LibOpConditionsNP.sol b/src/lib/op/logic/LibOpConditions.sol similarity index 55% rename from src/lib/op/logic/LibOpConditionsNP.sol rename to src/lib/op/logic/LibOpConditions.sol index 542b3061d..b9d037926 100644 --- a/src/lib/op/logic/LibOpConditionsNP.sol +++ b/src/lib/op/logic/LibOpConditions.sol @@ -6,12 +6,15 @@ import {Pointer} from "rain.solmem/lib/LibPointer.sol"; import {IntegrityCheckState} from "../../integrity/LibIntegrityCheck.sol"; import {InterpreterState} from "../../state/LibInterpreterState.sol"; import {LibIntOrAString, IntOrAString} from "rain.intorastring/lib/LibIntOrAString.sol"; +import {StackItem} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol"; +import {LibDecimalFloat, Float} from "rain.math.float/lib/LibDecimalFloat.sol"; -/// @title LibOpConditionsNP +/// @title LibOpConditions /// @notice Opcode to return the first nonzero item on the stack up to the inputs /// limit. -library LibOpConditionsNP { +library LibOpConditions { using LibIntOrAString for IntOrAString; + using LibDecimalFloat for Float; function integrity(IntegrityCheckState memory, OperandV2 operand) internal pure returns (uint256, uint256) { // There must be at least two inputs. @@ -28,48 +31,64 @@ library LibOpConditionsNP { /// If an author wants to provide some default value, they can set the last /// condition to some nonzero constant value such as 1. function run(InterpreterState memory, OperandV2 operand, Pointer stackTop) internal pure returns (Pointer) { - uint256 condition; - IntOrAString reason = IntOrAString.wrap(0); - assembly ("memory-safe") { - let inputs := and(shr(0x10, operand), 0x0F) - let oddInputs := mod(inputs, 2) - - let cursor := stackTop - for { - let end := add(cursor, mul(sub(inputs, oddInputs), 0x20)) + unchecked { + Float condition; + IntOrAString reason = IntOrAString.wrap(0); + uint256 inputs; + bool oddInputs; + Pointer cursor; + Pointer end; + assembly ("memory-safe") { + inputs := and(shr(0x10, operand), 0x0F) + oddInputs := mod(inputs, 2) + cursor := stackTop + end := add(cursor, mul(sub(inputs, oddInputs), 0x20)) stackTop := sub(end, mul(iszero(oddInputs), 0x20)) if oddInputs { reason := mload(end) } - } lt(cursor, end) { cursor := add(cursor, 0x40) } { - condition := mload(cursor) - if condition { - mstore(stackTop, mload(add(cursor, 0x20))) - break + } + bool conditionIsZero; + while (Pointer.unwrap(cursor) < Pointer.unwrap(end)) { + assembly ("memory-safe") { + condition := mload(cursor) + } + conditionIsZero = condition.isZero(); + if (!conditionIsZero) { + assembly ("memory-safe") { + mstore(stackTop, mload(add(cursor, 0x20))) + } + break; } + + cursor = Pointer.wrap(Pointer.unwrap(cursor) + 0x40); + } + + if (conditionIsZero) { + revert(reason.toString()); } + // require(condition > 0, reason.toString()); + return stackTop; } - require(condition > 0, reason.toString()); - return stackTop; } /// Gas intensive reference implementation of `condition` 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) { // Unchecked so that any overflow errors come from the real // implementation. unchecked { uint256 length = inputs.length; - outputs = new uint256[](1); + outputs = new StackItem[](1); for (uint256 i = 0; i < length; i += 2) { - if (inputs[i] != 0) { + if (!Float.wrap(StackItem.unwrap(inputs[i])).isZero()) { outputs[0] = inputs[i + 1]; return outputs; } } if (inputs.length % 2 != 0) { - IntOrAString reason = IntOrAString.wrap(inputs[length - 1]); + IntOrAString reason = IntOrAString.wrap(uint256(StackItem.unwrap(inputs[length - 1]))); require(false, reason.toString()); } else { require(false, ""); diff --git a/test/src/lib/op/logic/LibOpConditions.t.sol b/test/src/lib/op/logic/LibOpConditions.t.sol new file mode 100644 index 000000000..27a1e5d36 --- /dev/null +++ b/test/src/lib/op/logic/LibOpConditions.t.sol @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: CAL +pragma solidity =0.8.25; + +import {LibUint256Array} from "rain.solmem/lib/LibUint256Array.sol"; + +import {OpTest, UnexpectedOperand} from "test/abstract/OpTest.sol"; +import {LibOpConditions} from "src/lib/op/logic/LibOpConditions.sol"; +import {IntegrityCheckState, BadOpInputsLength} from "src/lib/integrity/LibIntegrityCheck.sol"; +import {InterpreterState} from "src/lib/state/LibInterpreterState.sol"; +import { + IInterpreterV4, + OperandV2, + SourceIndexV2, + FullyQualifiedNamespace +} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol"; +import {LibIntOrAString, IntOrAString} from "rain.intorastring/lib/LibIntOrAString.sol"; +import {LibOperand} from "test/lib/operand/LibOperand.sol"; +import {Float, LibDecimalFloat} from "rain.math.float/lib/LibDecimalFloat.sol"; +import {StackItem} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol"; + +contract LibOpConditionsTest is OpTest { + using LibUint256Array for uint256[]; + using LibDecimalFloat for Float; + + /// Directly test the integrity logic of LibOpConditions. This tests the happy + /// path where the operand is valid. + function testOpConditionsIntegrityHappy( + 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) = + LibOpConditions.integrity(state, LibOperand.build(inputs, outputs, operandData)); + + uint256 expectedCalcInputs = inputs; + // Calc inputs will be minimum 2. + if (inputs < 2) { + expectedCalcInputs = 2; + } + assertEq(calcInputs, expectedCalcInputs, "calc inputs"); + assertEq(calcOutputs, 1); + } + + /// Directly test the runtime logic of LibOpConditions. + function testOpConditionsRun(StackItem[] memory inputs, bytes32 finalNonZero) external view { + InterpreterState memory state = opTestDefaultInterpreterState(); + + // Ensure that we have inputs that are a valid pairwise conditions. + vm.assume(inputs.length > 1); + vm.assume(inputs.length <= 0x0F); + if (inputs.length % 2 != 0) { + uint256[] memory inputsIntArray; + assembly ("memory-safe") { + inputsIntArray := inputs + } + inputsIntArray.truncate(inputs.length - 1); + } + // Ensure the final condition is nonzero so that we don't error. + if (Float.wrap(StackItem.unwrap(inputs[inputs.length - 2])).isZero()) { + vm.assume(finalNonZero != 0); + inputs[inputs.length - 2] = StackItem.wrap(finalNonZero); + } + OperandV2 operand = LibOperand.build(uint8(inputs.length), 1, 0); + opReferenceCheck( + state, operand, LibOpConditions.referenceFn, LibOpConditions.integrity, LibOpConditions.run, inputs + ); + } + + function _testOpConditionsRunNoConditionsMet(StackItem[] memory inputs, OperandV2 operand) external view { + InterpreterState memory state = opTestDefaultInterpreterState(); + + opReferenceCheck( + state, operand, LibOpConditions.referenceFn, LibOpConditions.integrity, LibOpConditions.run, inputs + ); + } + + /// Test the error case where no conditions are met. + function testOpConditionsRunNoConditionsMet(StackItem[] memory inputs, string memory reason) external { + vm.assume(bytes(reason).length <= 31); + // Ensure that we have inputs that are a valid pairwise conditions. + vm.assume(inputs.length > 1); + if (inputs.length > 0x0F) { + uint256[] memory inputsIntArray; + assembly ("memory-safe") { + inputsIntArray := inputs + } + inputsIntArray.truncate(0x0F); + } + + OperandV2 operand = LibOperand.build(uint8(inputs.length), 1, 0); + + // Ensure all the conditions are zero so that we error. + for (uint256 i = 0; i < inputs.length; i += 2) { + inputs[i] = StackItem.wrap(0); + } + + if (inputs.length % 2 != 0) { + inputs[inputs.length - 1] = + StackItem.wrap(bytes32(IntOrAString.unwrap(LibIntOrAString.fromString2(reason)))); + } else { + reason = ""; + } + + vm.expectRevert(bytes(reason)); + this._testOpConditionsRunNoConditionsMet(inputs, operand); + } + + /// Test the eval of conditions opcode parsed from a string. Tests 1 true input 1 zero output. + function testOpConditionsEval1TrueInputZeroOutput() external view { + checkHappy("_: conditions(5 0);", Float.unwrap(LibDecimalFloat.packLossless(0, 0)), ""); + } + + /// Test the eval of conditions opcode parsed from a string. Tests 1 nonzero + /// input 1 nonzero output. + function testOpConditionsEval2MixedInputs() external view { + checkHappy("_: conditions(5 6);", Float.unwrap(LibDecimalFloat.packLossless(6, 0)), ""); + } + + /// Test that if conditions are NOT met, the expression reverts. + function testOpConditionsEval1FalseInputRevert() external { + checkUnhappy("_: conditions(0 5);", ""); + } + + /// Test that conditions can take an error code as an operand. + function testOpConditionsEvalErrorCode() external { + checkUnhappy("_: conditions(0x00 0x00 0x00 0x00 \"fail\");", "fail"); + } + + /// Test the eval of conditions opcode parsed from a string. Tests 1 zero + /// then 1 nonzero condition. + function testOpConditionsEval1FalseInput1TrueInput() external view { + checkHappy("_: conditions(0 9 3 4);", Float.unwrap(LibDecimalFloat.packLossless(4, 0)), ""); + } + + /// Test the eval of conditions opcode parsed from a string. Tests 2 true + /// conditions. The first should be used. + function testOpConditionsEval2TrueInputs() external view { + checkHappy("_: conditions(5 6 7 8);", Float.unwrap(LibDecimalFloat.packLossless(6, 0)), ""); + } + + /// Test the eval of conditions opcode parsed from a string. Tests 1 nonzero + /// condition then 1 zero condition. + function testOpConditionsEval1TrueInput1FalseInput() external view { + checkHappy("_: conditions(5 6 0 9);", Float.unwrap(LibDecimalFloat.packLossless(6, 0)), ""); + } + + /// Test that conditions without inputs fails integrity check. + function testOpConditionsEvalFail0Inputs() public { + vm.expectRevert(abi.encodeWithSelector(BadOpInputsLength.selector, 0, 2, 0)); + bytes memory bytecode = iDeployer.parse2("_: conditions();"); + (bytecode); + } + + /// Test that conditions with 1 inputs fails integrity check. + function testOpConditionsEvalFail1Inputs() public { + vm.expectRevert(abi.encodeWithSelector(BadOpInputsLength.selector, 1, 2, 1)); + bytes memory bytecode = iDeployer.parse2("_: conditions(0x00);"); + (bytecode); + } + + /// Test the eval of `conditions` parsed from a string. Tests the unhappy path + /// where an operand is provided. + function testOpConditionsEvalUnhappyOperand() external { + checkUnhappyParse("_ :conditions<0>(1 1 \"foo\");", abi.encodeWithSelector(UnexpectedOperand.selector)); + } + + function testOpConditionsZeroOutputs() external { + checkBadOutputs(": conditions(0x00 0x00);", 2, 1, 0); + } + + function testOpConditionsTwoOutputs() external { + checkBadOutputs("_ _: conditions(0x00 0x00);", 2, 1, 2); + } +} diff --git a/test/src/lib/op/logic/LibOpConditionsNP.t.sol b/test/src/lib/op/logic/LibOpConditionsNP.t.sol deleted file mode 100644 index 8c639a6ad..000000000 --- a/test/src/lib/op/logic/LibOpConditionsNP.t.sol +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: CAL -pragma solidity =0.8.25; - -// // import {LibUint256Array} from "rain.solmem/lib/LibUint256Array.sol"; - -// // import {OpTest, UnexpectedOperand} from "test/abstract/OpTest.sol"; -// // import {LibContext} from "rain.interpreter.interface/lib/caller/LibContext.sol"; -// // import {LibOpConditionsNP} from "src/lib/op/logic/LibOpConditionsNP.sol"; -// // import {IntegrityCheckState, BadOpInputsLength} from "src/lib/integrity/LibIntegrityCheck.sol"; -// // import {InterpreterState} from "src/lib/state/LibInterpreterState.sol"; -// // import { -// // IInterpreterV4, -// // Operand, -// // SourceIndexV2, -// // FullyQualifiedNamespace -// // } from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol"; -// // import {IInterpreterStoreV2} from "rain.interpreter.interface/interface/IInterpreterStoreV2.sol"; -// // import {SignedContextV1} from "rain.interpreter.interface/interface/IInterpreterCallerV3.sol"; -// // import {LibIntOrAString, IntOrAString} from "rain.intorastring/lib/LibIntOrAString.sol"; -// // import {LibOperand} from "test/lib/operand/LibOperand.sol"; - -// // contract LibOpConditionsNPTest is OpTest { -// // using LibUint256Array for uint256[]; - -// /// Directly test the integrity logic of LibOpConditionsNP. This tests the happy -// /// path where the operand is valid. -// function testOpConditionsNPIntegrityHappy( -// 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) = -// LibOpConditionsNP.integrity(state, LibOperand.build(inputs, outputs, operandData)); - -// // uint256 expectedCalcInputs = inputs; -// // // Calc inputs will be minimum 2. -// // if (inputs < 2) { -// // expectedCalcInputs = 2; -// // } -// // assertEq(calcInputs, expectedCalcInputs, "calc inputs"); -// // assertEq(calcOutputs, 1); -// // } - -// /// Directly test the runtime logic of LibOpConditionsNP. -// function testOpConditionsNPRun(uint256[] memory inputs, uint256 finalNonZero) external view { -// InterpreterState memory state = opTestDefaultInterpreterState(); - -// // // Ensure that we have inputs that are a valid pairwise conditions. -// // vm.assume(inputs.length > 1); -// // vm.assume(inputs.length <= 0x0F); -// // if (inputs.length % 2 != 0) { -// // inputs.truncate(inputs.length - 1); -// // } -// // // Ensure the final condition is nonzero so that we don't error. -// // if (inputs[inputs.length - 2] == 0) { -// // vm.assume(finalNonZero != 0); -// // inputs[inputs.length - 2] = finalNonZero; -// // } -// // Operand operand = LibOperand.build(uint8(inputs.length), 1, 0); -// // opReferenceCheck( -// // state, operand, LibOpConditionsNP.referenceFn, LibOpConditionsNP.integrity, LibOpConditionsNP.run, inputs -// // ); -// // } - -// // /// Test the error case where no conditions are met. -// // function testOpConditionsNPRunNoConditionsMet(uint256[] memory inputs, string memory reason) external { -// // vm.assume(bytes(reason).length <= 31); -// // InterpreterState memory state = opTestDefaultInterpreterState(); -// // // Ensure that we have inputs that are a valid pairwise conditions. -// // vm.assume(inputs.length > 1); -// // if (inputs.length > 0x0F) { -// // inputs.truncate(0x0F); -// // } - -// // Operand operand = LibOperand.build(uint8(inputs.length), 1, 0); - -// // // Ensure all the conditions are zero so that we error. -// // for (uint256 i = 0; i < inputs.length; i += 2) { -// // inputs[i] = 0; -// // } - -// // if (inputs.length % 2 != 0) { -// // inputs[inputs.length - 1] = IntOrAString.unwrap(LibIntOrAString.fromString2(reason)); -// // } else { -// // reason = ""; -// // } - -// // vm.expectRevert(bytes(reason)); -// // opReferenceCheck( -// // state, operand, LibOpConditionsNP.referenceFn, LibOpConditionsNP.integrity, LibOpConditionsNP.run, inputs -// // ); -// // } - -// // /// Test the eval of conditions opcode parsed from a string. Tests 1 true input 1 zero output. -// // function testOpConditionsNPEval1TrueInputZeroOutput() external { -// // checkHappy("_: conditions(5 0);", 0, ""); -// // } - -// // /// Test the eval of conditions opcode parsed from a string. Tests 1 nonzero -// // /// input 1 nonzero output. -// // function testOpConditionsNPEval2MixedInputs() external { -// // checkHappy("_: conditions(5 6);", 6e18, ""); -// // } - -// // /// Test that if conditions are NOT met, the expression reverts. -// // function testOpConditionsNPEval1FalseInputRevert() external { -// // checkUnhappy("_: conditions(0 5);", ""); -// // } - -// // /// Test that conditions can take an error code as an operand. -// // function testOpConditionsNPEvalErrorCode() external { -// // checkUnhappy("_: conditions(0x00 0x00 0x00 0x00 \"fail\");", "fail"); -// // } - -// // /// Test the eval of conditions opcode parsed from a string. Tests 1 zero -// // /// then 1 nonzero condition. -// // function testOpConditionsNPEval1FalseInput1TrueInput() external { -// // checkHappy("_: conditions(0 9 3 4);", 4e18, ""); -// // } - -// // /// Test the eval of conditions opcode parsed from a string. Tests 2 true -// // /// conditions. The first should be used. -// // function testOpConditionsNPEval2TrueInputs() external { -// // checkHappy("_: conditions(5 6 7 8);", 6e18, ""); -// // } - -// // /// Test the eval of conditions opcode parsed from a string. Tests 1 nonzero -// // /// condition then 1 zero condition. -// // function testOpConditionsNPEval1TrueInput1FalseInput() external { -// // checkHappy("_: conditions(5 6 0 9);", 6e18, ""); -// // } - -// // /// Test that conditions without inputs fails integrity check. -// // function testOpConditionsNPEvalFail0Inputs() public { -// // vm.expectRevert(abi.encodeWithSelector(BadOpInputsLength.selector, 0, 2, 0)); -// // bytes memory bytecode = iDeployer.parse2("_: conditions();"); -// // (bytecode); -// // } - -// // /// Test that conditions with 1 inputs fails integrity check. -// // function testOpConditionsNPEvalFail1Inputs() public { -// // vm.expectRevert(abi.encodeWithSelector(BadOpInputsLength.selector, 1, 2, 1)); -// // bytes memory bytecode = iDeployer.parse2("_: conditions(0x00);"); -// // (bytecode); -// // } - -// // /// Test the eval of `conditions` parsed from a string. Tests the unhappy path -// // /// where an operand is provided. -// // function testOpConditionsNPEvalUnhappyOperand() external { -// // checkUnhappyParse("_ :conditions<0>(1 1 \"foo\");", abi.encodeWithSelector(UnexpectedOperand.selector)); -// // } - -// // function testOpConditionsNPZeroOutputs() external { -// // checkBadOutputs(": conditions(0x00 0x00);", 2, 1, 0); -// // } - -// // function testOpConditionsNPTwoOutputs() external { -// // checkBadOutputs("_ _: conditions(0x00 0x00);", 2, 1, 2); -// // } -// // }