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(0x65839bbc78993c95140ed4476132110d45c4ed446553985be09d23f9af769599);
bytes32 constant BYTECODE_HASH = bytes32(0x1a3fbd5bbbf67323604d9d6e171b5922ce1c661c9f890518b6d8c7c948349acb);

/// @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"07a207d407f809840a4d0a5f0a710a8a0aae0ae20af30b040ba60bc50c830d330db70ef9102c0c83112511d7127912f1130213131313132413b113ca13de13f71422143b144e14b014fe154c159a15e81636168416d217031751179f17ed18d4";
hex"07ab07dd0801098d0a560a680a7a0a930ab70aeb0afc0b0d0baf0bce0c8c0d3c0dc00f0210350c8c112e11e0128212fa130b131c131c132d137413f6140f1423143c14671480149314f51543159115df162d167b16c917171748179617e418321919";
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(0x176dcc1d53f1a9b3bc350f2863646819c36b13ceab17bb08d832559a8d6d3c6f);
bytes32 constant BYTECODE_HASH = bytes32(0xd09b4991a12ba8c2a22269ff879e110f22450dba8c3850ebdfd90d37a76936a9);

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

/// @dev The function pointers for the integrity check fns.
bytes constant INTEGRITY_FUNCTION_POINTERS =
hex"0e380eb60f1a1094109e109e10a810b110cc1172117211ce12461253109e10a81253109e10a8109e109e109e10a81094109410941094125d109e109e109e10a8109e1094126712671267126712671267128f126710941267128f128f10a8125d";
hex"0e400ebe0f22109c10a610a610b010b910d4117a117a11d6124e125b10a610b0125b10a610b010a610a610a610b0109c109c109c109c1265128a10a610a610a610b010a6109c12941294129412941294129412ae1294109c129412ae12ae10b0128a";
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(0x4dae17e8ff2c40976367ed98a4f9aca0cc65ea9518e3e922961b3060c694da1d);
bytes32 constant BYTECODE_HASH = bytes32(0x2e38cc130243a36a0482c2e7e70b5928cedb02e1ad0972b7bd7b232b936b7eae);

/// @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(0x4dae17e8ff2c40976367ed98a4f9aca0cc65e
/// 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"0178a8806000299010810908a00a34484001280030060100180000200cc2200408a00c95d8382e48145c2f34b3f32ad587e204afc8f01d95a26007b9a51f0d5e6f2803ce312215fae0a1241233de118cfd53051c784d22a55ade1681ac9620ac6dfc2ba4e5e91e5fc32a0e92b2401cbbd837100ef76b00cd8814279714ec13313592260d473b1ff880501427634f0f23b4970ac0e53d0b895f991ac908be0983724f12c080bc2c9d7b7d25757aea236f62e0285c0ab5086d2a7b19bcb24402af7d4506414b8117b04fb31bb2182f18ef532921c301e32dfa5d8001336a5929ab2461";
hex"0178ac806000299010810908a00a34484001280030060100180000200cc2200408a00c95d8382f48145c3034b3f32bd587e204afc8f01e95a26007b9a51f0d5e6f2803ce312215fae0a1251233de118cfd53051c784d23a55ade1681ac9621ac6dfc2ca4e5e91f5fc32a0e92b2401dbbd837100ef76b00cd8814289714ec13313592270d473b20f880501427634f0f23b4970ac0e53d0b895f991ac908be0983724f12c080bc2d9d7b7d26757aea246f62e0295c0ab5086d2a7b19bcb24402af7d4506414b8117b04fb31cb2182f18ef532922c301e31b9314ad2efa5d8001336a592aab2461";

/// @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"1a1b1a1b1a1b1af01c071c071c071af01af01a1b1a1b1a1b1c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071c071a1b1c071c07";
hex"1a231a231a231af81c0f1c0f1c0f1af81af81a231a231a231c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1c0f1a231c0f1c0f";

/// @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"1563179517d81876";
bytes constant LITERAL_PARSER_FUNCTION_POINTERS = hex"156b179d17e0187e";
14 changes: 7 additions & 7 deletions src/lib/op/LibAllStandardOps.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import {LibOpBlockNumber} from "./evm/LibOpBlockNumber.sol";
import {LibOpChainId} from "./evm/LibOpChainId.sol";
import {LibOpTimestamp} from "./evm/LibOpTimestamp.sol";

import {LibOpAnyNP} from "./logic/LibOpAnyNP.sol";
import {LibOpAny} from "./logic/LibOpAny.sol";
import {LibOpConditionsNP} from "./logic/LibOpConditionsNP.sol";
import {LibOpEnsure} from "./logic/LibOpEnsure.sol";
import {LibOpEqualTo} from "./logic/LibOpEqualTo.sol";
Expand Down Expand Up @@ -108,7 +108,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 = 48;
uint256 constant ALL_STANDARD_OPS_LENGTH = 49;

/// @title LibAllStandardOps
/// @notice Every opcode available from the core repository laid out as a single
Expand Down Expand Up @@ -206,7 +206,7 @@ library LibAllStandardOps {
AuthoringMetaV2("chain-id", "The current chain id."),
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("any", "The first binary 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."
Expand Down Expand Up @@ -430,8 +430,8 @@ library LibAllStandardOps {
LibParseOperand.handleOperandDisallowed,
// now
LibParseOperand.handleOperandDisallowed,
// // any
// LibParseOperand.handleOperandDisallowed,
// any
LibParseOperand.handleOperandDisallowed,
// // conditions
// LibParseOperand.handleOperandDisallowed,
// ensure
Expand Down Expand Up @@ -592,7 +592,7 @@ library LibAllStandardOps {
LibOpTimestamp.integrity,
// now
LibOpTimestamp.integrity,
// LibOpAnyNP.integrity,
LibOpAny.integrity,
// LibOpConditionsNP.integrity,
LibOpEnsure.integrity,
LibOpEqualTo.integrity,
Expand Down Expand Up @@ -707,7 +707,7 @@ library LibAllStandardOps {
LibOpTimestamp.run,
// now
LibOpTimestamp.run,
// LibOpAnyNP.run,
LibOpAny.run,
// LibOpConditionsNP.run,
LibOpEnsure.run,
LibOpEqualTo.run,
Expand Down
19 changes: 10 additions & 9 deletions src/lib/op/logic/LibOpAnyNP.sol → src/lib/op/logic/LibOpAny.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ 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";

/// @title LibOpAnyNP
/// @title LibOpAny
/// @notice Opcode to return the first nonzero item on the stack up to the inputs
/// limit.
library LibOpAnyNP {
library LibOpAny {
function integrity(IntegrityCheckState memory, OperandV2 operand) internal pure returns (uint256, uint256) {
// There must be at least one input.
uint256 inputs = uint256((OperandV2.unwrap(operand) >> 0x10) & bytes32(uint256(0x0F)));
Expand All @@ -36,21 +37,21 @@ library LibOpAnyNP {
}

/// Gas intensive reference implementation of ANY 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)
{
// Zero length inputs is not supported so this 0 will always be written
// over.
uint256 value = 0;
bytes32 value = 0;
for (uint256 i = 0; i < inputs.length; i++) {
value = inputs[i];
if (value != 0) {
value = StackItem.unwrap(inputs[i]);
if (value != bytes32(0)) {
break;
}
}
outputs = new uint256[](1);
outputs[0] = value;
outputs = new StackItem[](1);
outputs[0] = StackItem.wrap(value);
}
}
147 changes: 147 additions & 0 deletions test/src/lib/op/logic/LibOpAny.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// SPDX-License-Identifier: CAL
pragma solidity =0.8.25;

import {Pointer} from "rain.solmem/lib/LibPointer.sol";
import {LibUint256Array} from "rain.solmem/lib/LibUint256Array.sol";
import {MemoryKV} from "rain.lib.memkv/lib/LibMemoryKV.sol";

import {OpTest} from "test/abstract/OpTest.sol";
import {LibOpAny} from "src/lib/op/logic/LibOpAny.sol";
import {
IInterpreterV4, OperandV2, SourceIndexV2
} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol";
import {
IInterpreterStoreV3,
FullyQualifiedNamespace
} from "rain.interpreter.interface/interface/unstable/IInterpreterStoreV3.sol";
import {LibIntegrityCheck, IntegrityCheckState} from "src/lib/integrity/LibIntegrityCheck.sol";
import {LibInterpreterState, InterpreterState} from "src/lib/state/LibInterpreterState.sol";
import {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 LibOpAnyTest is OpTest {
using LibUint256Array for uint256[];

/// Directly test the integrity logic of LibOpAny. This tests the happy
/// path where the operand is valid.
function testOpAnyIntegrityHappy(uint8 inputs, uint16 operandData) external pure {
IntegrityCheckState memory state = opTestDefaultIngegrityCheckState();
inputs = uint8(bound(uint256(inputs), 1, 0x0F));
(uint256 calcInputs, uint256 calcOutputs) = LibOpAny.integrity(state, LibOperand.build(inputs, 1, operandData));

assertEq(calcInputs, inputs);
assertEq(calcOutputs, 1);
}

/// Sample the gas cost of the integrity check.
function testOpAnyIntegrityGas0() external {
vm.pauseGasMetering();
IntegrityCheckState memory state = IntegrityCheckState(6, 6, 6, new bytes32[](3), 9, "");
OperandV2 operand = OperandV2.wrap(bytes32(uint256(0x50000)));
vm.resumeGasMetering();
// 5 inputs. Any stack index above this is fine for the state.
(uint256 calcInputs, uint256 calcOutputs) = LibOpAny.integrity(state, operand);
(calcInputs);
(calcOutputs);
}

/// Directly test the integrity logic of LibOpAny. This tests the unhappy
/// path where the operand is invalid due to 0 inputs.
function testOpAnyIntegrityUnhappyZeroInputs() external pure {
IntegrityCheckState memory state = opTestDefaultIngegrityCheckState();
(uint256 calcInputs, uint256 calcOutputs) = LibOpAny.integrity(state, OperandV2.wrap(0));
// Calc inputs will be minimum 1.
assertEq(calcInputs, 1);
assertEq(calcOutputs, 1);
}

function _testOpAnyRun(OperandV2 operand, StackItem[] memory inputs) external view {
InterpreterState memory state = opTestDefaultInterpreterState();
opReferenceCheck(state, operand, LibOpAny.referenceFn, LibOpAny.integrity, LibOpAny.run, inputs);
}

/// Directly test the runtime logic of LibOpAny.
function testOpAnyRun(StackItem[] memory inputs, uint16 operandData) external view {
vm.assume(inputs.length != 0);
vm.assume(inputs.length <= 0x0F);
OperandV2 operand = LibOperand.build(uint8(inputs.length), 1, operandData);
this._testOpAnyRun(operand, inputs);
}

/// Sample the gas cost of the run function.
function testOpAnyRunGas0() external {
vm.pauseGasMetering();
StackItem[][] memory stacks = new StackItem[][](1);
stacks[0] = new StackItem[](1);
Pointer stackTop;
assembly ("memory-safe") {
stackTop := add(stacks, 0x20)
}
InterpreterState memory state = InterpreterState(
LibInterpreterState.stackBottoms(stacks),
new bytes32[](0),
0,
MemoryKV.wrap(0),
FullyQualifiedNamespace.wrap(0),
IInterpreterStoreV3(address(0)),
new bytes32[][](0),
"",
""
);
OperandV2 operand = OperandV2.wrap(bytes32(uint256(0x10000)));
vm.resumeGasMetering();
// 1 inputs. Any stack index above this is fine for the state.
LibOpAny.run(state, operand, stackTop);
}

/// Test the eval of any opcode parsed from a string. Tests 1 true input.
function testOpAnyEval1TrueInput() external view {
checkHappy("_: any(5);", bytes32(uint256(5)), "");
}

/// Test the eval of any opcode parsed from a string. Tests 1 false input.
function testOpAnyEval1FalseInput() external view {
checkHappy("_: any(0);", 0, "");
}

/// Test the eval of any opcode parsed from a string. Tests 2 true inputs.
/// The first true input should be the overall result.
function testOpAnyEval2TrueInputs() external view {
checkHappy("_: any(5 6);", bytes32(uint256(5)), "");
}

/// Test the eval of any opcode parsed from a string. Tests 2 false inputs.
function testOpAnyEval2FalseInputs() external view {
checkHappy("_: any(0 0);", 0, "");
}

/// Test the eval of any opcode parsed from a string. Tests 2 inputs, one
/// true and one false. The first true input should be the overall result.
/// The first value is the true value.
function testOpAnyEval2MixedInputs() external view {
checkHappy("_: any(5 0);", bytes32(uint256(5)), "");
}

/// Test the eval of any opcode parsed from a string. Tests 2 inputs, one
/// true and one false. The first true input should be the overall result.
/// The first value is the false value.
function testOpAnyEval2MixedInputs2() external view {
checkHappy("_: any(0 5);", bytes32(uint256(5)), "");
}

/// Test that any without inputs fails integrity check.
function testOpAnyEvalFail() public {
vm.expectRevert(abi.encodeWithSelector(BadOpInputsLength.selector, 0, 1, 0));
bytes memory bytecode = iDeployer.parse2("_: any();");
(bytecode);
}

function testOpAnyZeroOutputs() external {
checkBadOutputs(": any(0);", 1, 1, 0);
}

function testOpAnyTwoOutputs() external {
checkBadOutputs("_ _: any(0);", 1, 1, 2);
}
}
Loading
Loading