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
256 changes: 132 additions & 124 deletions .gas-snapshot

Large diffs are not rendered by default.

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

/// @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"086c089e08c20a4e0b170b290b3b0b540b780bac0bbd0bce0c700c8f0d4d0dfd0e810fc310f60d4d11ef12a1134313bb13cc13dd13dd13ee1459156415e315fc1610166f168816a116da1705171e1737178017a717ba181c186a18b819061954196219b019d31a211a521a961abb1ac91ad71af01afe1b4c1b7d1bae1bfc1c2d1c5e1cac1cd91cfc1d4a1e40";
hex"087508a708cb0a570b200b320b440b5d0b810bb50bc60bd70c790c980d560e060e8a0fcc10ff0d5611f812aa134c13c413d513e613e613f71462156d15ec160516191678169116aa16e3170e17271740178917b017c31825187318c1190f195d196b19b919dc1a2a1a5b1a9f1ac41ad21ae01b311b4a1b581ba61bd71c081c561c871cb81d061d331d561da41e9a";
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(0x23bc63ae1d26a9ebfda70ae22cca9ddb1236ebe1013163e0a179bb154ecd7b17);
bytes32 constant BYTECODE_HASH = bytes32(0x1b5519183df0925de8b552daa52766a1d41087a630c1869dcbd656e9edacefb7);

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

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

/// @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(0xd645ac93a6fb7396fcf0ed82f84c20657c693
/// 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"027360018812c0058112300804a1a02000120192e89864d17a6010788a0800310122000002000000000000000000000000000000000005000000000000000000000000253bfdf302d3ac012ac8268505b44edf36182f2a14690c630d83a7eb32c5853a08ccb48c3358350a1c21b6884298a6e5262057e32f7e09af17b43369161b9cec2b5ab5e8384715a6243c910831738e093542f25d1e8a3fa7150113a32e22720213c659b9225ac7c1285da1bd44cc18770aa63c19048eb6fa018179d300dfb6a91fde8a882cb772ea03596d3111fd7ab9457b42922d4396834158726b1d2330703994005b21f438852081752c3fff19581ac1a49e30ffeff03ae1eec73b24bdfe2760978f232d6a9a09d520ad061480a0294c65fb344763440ecb8d4d0b8b587210e7c1b518e4b09907152b041261f729198ab9053cf8942b1b45c1270f22a49a3ea6838c4360cc7c0c668e383d10da5037d6d3e340e7d2ff";
hex"027360018812c0058112300804a1a02000120192e89864d17a6030788a0800310122000002000000000000000000000000000000000005000000000000000000000000253bfdf302d3ac012ac8268505b44edf36182f2a14690c630d83a7eb32c5853a08ccb48c3358350a1c21b6884398a6e5262057e32f7e09af17b433693838e9b4161b9cec2b5ab5e8394715a6243c910831738e093542f25d1e8a3fa7150113a32e22720213c659b9225ac7c1285da1bd45cc18770aa63c19048eb6fa018179d300dfb6a91fde8a882cb772ea03596d3111fd7ab9467b42922d4396834258726b1d2330703a94005b21f438852081752c40ff19581ac1a49e30ffeff03be1eec73c24bdfe2760978f232d6a9a09d520ad061480a0294c65fb344763440ecb8d4d0b8b587210e7c1b518e4b09907152b041261f729198ab9053df8942b1b45c1270f22a49a3fa6838c4460cc7c0c668e383e10da5037d6d3e341e7d2ff";

/// @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"1acd1acd1acd1ba21cb91cb91cb91ba21ba21acd1acd1acd1cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91cb91acd1cb91cb9";
hex"1ad51ad51ad51baa1cc11cc11cc11baa1baa1ad51ad51ad51cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11cc11ad51cc11cc1";

/// @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"16151847188a1928";
bytes constant LITERAL_PARSER_FUNCTION_POINTERS = hex"161d184f18921930";
14 changes: 7 additions & 7 deletions src/lib/op/LibAllStandardOps.sol
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ import {LibOpExp} from "./math/LibOpExp.sol";
import {LibOpExp2} from "./math/LibOpExp2.sol";
import {LibOpFloor} from "./math/LibOpFloor.sol";
import {LibOpFrac} from "./math/LibOpFrac.sol";
// import {LibOpGm} from "./math/LibOpGm.sol";
import {LibOpGm} from "./math/LibOpGm.sol";
import {LibOpHeadroom} from "./math/LibOpHeadroom.sol";
import {LibOpInv} from "./math/LibOpInv.sol";
// import {LibOpLn} from "./math/LibOpLn.sol";
Expand Down Expand Up @@ -112,7 +112,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 = 70;
uint256 constant ALL_STANDARD_OPS_LENGTH = 71;

/// @title LibAllStandardOps
/// @notice Every opcode available from the core repository laid out as a single
Expand Down Expand Up @@ -277,7 +277,7 @@ library LibAllStandardOps {
AuthoringMetaV2("exp2", "Binary exponential 2^x."),
AuthoringMetaV2("floor", "Floor of a number."),
AuthoringMetaV2("frac", "Fractional part of a number."),
// AuthoringMetaV2("gm", "Geometric mean of all numbers. Errors if any number is zero."),
AuthoringMetaV2("gm", "Geometric mean of two numbers."),
AuthoringMetaV2(
"headroom",
"Headroom of a number. I.e. the distance to the next whole number (1 - frac(x)). The headroom at any whole number is 1 (not 0)."
Expand Down Expand Up @@ -480,8 +480,8 @@ library LibAllStandardOps {
LibParseOperand.handleOperandDisallowed,
// frac
LibParseOperand.handleOperandDisallowed,
// // gm
// LibParseOperand.handleOperandDisallowed,
// gm
LibParseOperand.handleOperandDisallowed,
// headroom
LibParseOperand.handleOperandDisallowed,
// inv
Expand Down Expand Up @@ -608,7 +608,7 @@ library LibAllStandardOps {
LibOpExp2.integrity,
LibOpFloor.integrity,
LibOpFrac.integrity,
// LibOpGm.integrity,
LibOpGm.integrity,
LibOpHeadroom.integrity,
LibOpInv.integrity,
// LibOpLn.integrity,
Expand Down Expand Up @@ -719,7 +719,7 @@ library LibAllStandardOps {
LibOpExp2.run,
LibOpFloor.run,
LibOpFrac.run,
// LibOpGm.run,
LibOpGm.run,
LibOpHeadroom.run,
LibOpInv.run,
// LibOpLn.run,
Expand Down
87 changes: 47 additions & 40 deletions src/lib/op/math/LibOpGm.sol
Original file line number Diff line number Diff line change
@@ -1,47 +1,54 @@
// SPDX-License-Identifier: CAL
pragma solidity ^0.8.18;

// import {UD60x18, gm} from "prb-math/UD60x18.sol";
// import {OperandV2} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol";
// import {Pointer} from "rain.solmem/lib/LibPointer.sol";
// import {InterpreterState} from "../../state/LibInterpreterState.sol";
// import {IntegrityCheckState} from "../../integrity/LibIntegrityCheck.sol";
import {OperandV2} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol";
import {Pointer} from "rain.solmem/lib/LibPointer.sol";
import {InterpreterState} from "../../state/LibInterpreterState.sol";
import {IntegrityCheckState} from "../../integrity/LibIntegrityCheck.sol";
import {LibDecimalFloat, Float} from "rain.math.float/lib/LibDecimalFloat.sol";
import {StackItem} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol";

// /// @title LibOpGm
// /// @notice Opcode for the geometric average of two decimal 18 fixed point
// /// numbers.
// library LibOpGm {
// function integrity(IntegrityCheckState memory, Operand) internal pure returns (uint256, uint256) {
// // There must be two inputs and one output.
// return (2, 1);
// }
/// @title LibOpGm
/// @notice Opcode for the geometric average of two decimal floating point
/// numbers.
library LibOpGm {
using LibDecimalFloat for Float;

// /// gm
// /// 18 decimal fixed point geometric average of two numbers.
// function run(InterpreterState memory, Operand, Pointer stackTop) internal pure returns (Pointer) {
// uint256 a;
// uint256 b;
// assembly ("memory-safe") {
// a := mload(stackTop)
// stackTop := add(stackTop, 0x20)
// b := mload(stackTop)
// }
// a = UD60x18.unwrap(gm(UD60x18.wrap(a), UD60x18.wrap(b)));
function integrity(IntegrityCheckState memory, OperandV2) internal pure returns (uint256, uint256) {
// There must be two inputs and one output.
return (2, 1);
}

// assembly ("memory-safe") {
// mstore(stackTop, a)
// }
// return stackTop;
// }
/// gm
/// decimal floating point geometric average of two numbers.
function run(InterpreterState memory, OperandV2, Pointer stackTop) internal view returns (Pointer) {
Float a;
Float b;
assembly ("memory-safe") {
a := mload(stackTop)
stackTop := add(stackTop, 0x20)
b := mload(stackTop)
}
a = a.mul(b).pow(LibDecimalFloat.FLOAT_HALF, LibDecimalFloat.LOG_TABLES_ADDRESS);

// /// Gas intensive reference implementation of gm for testing.
// function referenceFn(InterpreterState memory, Operand, uint256[] memory inputs)
// internal
// pure
// returns (uint256[] memory)
// {
// uint256[] memory outputs = new uint256[](1);
// outputs[0] = UD60x18.unwrap(gm(UD60x18.wrap(inputs[0]), UD60x18.wrap(inputs[1])));
// return outputs;
// }
// }
assembly ("memory-safe") {
mstore(stackTop, a)
}
return stackTop;
}

/// Gas intensive reference implementation of gm for testing.
function referenceFn(InterpreterState memory, OperandV2, StackItem[] memory inputs)
internal
view
returns (StackItem[] memory)
{
// The geometric mean is sqrt(a * b).
Float a = Float.wrap(StackItem.unwrap(inputs[0]));
Float b = Float.wrap(StackItem.unwrap(inputs[1]));
a = a.mul(b).pow(LibDecimalFloat.FLOAT_HALF, LibDecimalFloat.LOG_TABLES_ADDRESS);
StackItem[] memory outputs = new StackItem[](1);
outputs[0] = StackItem.wrap(Float.unwrap(a));
return outputs;
}
}
132 changes: 74 additions & 58 deletions test/src/lib/op/math/LibOpGm.t.sol
Original file line number Diff line number Diff line change
@@ -1,71 +1,87 @@
// SPDX-License-Identifier: CAL
pragma solidity =0.8.25;

// import {OpTest, IntegrityCheckState, Operand, InterpreterState, UnexpectedOperand} from "test/abstract/OpTest.sol";
// import {LibOpGm} from "src/lib/op/math/LibOpGm.sol";
// import {LibOperand} from "test/lib/operand/LibOperand.sol";
import {OpTest, IntegrityCheckState, OperandV2, InterpreterState, UnexpectedOperand} from "test/abstract/OpTest.sol";
import {LibOpGm} from "src/lib/op/math/LibOpGm.sol";
import {LibOperand} from "test/lib/operand/LibOperand.sol";
import {LibDecimalFloat, Float} from "rain.math.float/lib/LibDecimalFloat.sol";
import {StackItem} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol";

// contract LibOpGmTest is OpTest {
// /// Directly test the integrity logic of LibOpGm.
// /// Inputs are always 2, outputs are always 1.
// function testOpGmIntegrity(IntegrityCheckState memory state, Operand operand) external pure {
// (uint256 calcInputs, uint256 calcOutputs) = LibOpGm.integrity(state, operand);
// assertEq(calcInputs, 2);
// assertEq(calcOutputs, 1);
// }
contract LibOpGmTest is OpTest {
function beforeOpTestConstructor() internal virtual override {
vm.createSelectFork(vm.envString("ETH_RPC_URL"));
}

// /// Directly test the runtime logic of LibOpGm.
// function testOpGmRun(uint256 a, uint256 b, uint16 operandData) public view {
// // @TODO This is a hack to get around the fact that we are very likely
// // to overflow uint256 if we just fuzz it, and that it's clunky to
// // determine whether it will overflow or not. Basically the overflow
// // check is exactly the same as the implementation, including all the
// // intermediate squaring, so it seems like a bit of circular logic to
// // do things that way.
// a = bound(a, 0, type(uint64).max);
// b = bound(b, 0, 10);
// InterpreterState memory state = opTestDefaultInterpreterState();
/// Directly test the integrity logic of LibOpGm.
/// Inputs are always 2, outputs are always 1.
function testOpGmIntegrity(IntegrityCheckState memory state, OperandV2 operand) external pure {
(uint256 calcInputs, uint256 calcOutputs) = LibOpGm.integrity(state, operand);
assertEq(calcInputs, 2);
assertEq(calcOutputs, 1);
}

// Operand operand = LibOperand.build(2, 1, operandData);
// uint256[] memory inputs = new uint256[](2);
// inputs[0] = a;
// inputs[1] = b;
/// Directly test the runtime logic of LibOpGm.
function testOpGmRun(
int224 signedCoefficientA,
int32 exponentA,
int224 signedCoefficientB,
int32 exponentB,
uint16 operandData
) public view {
signedCoefficientA = int224(bound(signedCoefficientA, 0, 10000));
exponentA = int32(bound(exponentA, -10, 5));
signedCoefficientB = int224(bound(signedCoefficientB, 0, 10000));
exponentB = int32(bound(exponentB, -10, 5));

// opReferenceCheck(state, operand, LibOpGm.referenceFn, LibOpGm.integrity, LibOpGm.run, inputs);
// }
InterpreterState memory state = opTestDefaultInterpreterState();

// /// Test the eval of `gm`.
// function testOpGmEval() external view {
// checkHappy("_: gm(0 0);", 0, "0 0");
// checkHappy("_: gm(0 1);", 0, "0 1");
// checkHappy("_: gm(1 0);", 0, "1 0");
// checkHappy("_: gm(1 1);", 1e18, "1 1");
// checkHappy("_: gm(1 2);", 1414213562373095048, "1 2");
// checkHappy("_: gm(2 2);", 2e18, "2 2");
// checkHappy("_: gm(2 3);", 2449489742783178098, "2 3");
// checkHappy("_: gm(2 4);", 2828427124746190097, "2 4");
// checkHappy("_: gm(4 0.5);", 1414213562373095048, "4 0.5");
// }
Float a = LibDecimalFloat.packLossless(signedCoefficientA, exponentA);
Float b = LibDecimalFloat.packLossless(signedCoefficientB, exponentB);

// /// Test the eval of `gm` for bad inputs.
// function testOpGmOneInput() external {
// checkBadInputs("_: gm(1e18);", 1, 2, 1);
// }
OperandV2 operand = LibOperand.build(2, 1, operandData);
StackItem[] memory inputs = new StackItem[](2);
inputs[0] = StackItem.wrap(Float.unwrap(a));
inputs[1] = StackItem.wrap(Float.unwrap(b));

// function testOpGmThreeInputs() external {
// checkBadInputs("_: gm(1 1 1);", 3, 2, 3);
// }
opReferenceCheck(state, operand, LibOpGm.referenceFn, LibOpGm.integrity, LibOpGm.run, inputs);
}

// function testOpGmZeroOutputs() external {
// checkBadOutputs(": gm(1 1);", 2, 1, 0);
// }
/// Test the eval of `gm`.
function testOpGmEval() external view {
checkHappy("_: gm(0 0);", 0, "0 0");
checkHappy("_: gm(0 1);", 0, "0 1");
checkHappy("_: gm(1 0);", 0, "1 0");
checkHappy("_: gm(1 1);", Float.unwrap(LibDecimalFloat.packLossless(1e3, -3)), "1 1");
checkHappy("_: gm(1 2);", Float.unwrap(LibDecimalFloat.packLossless(1415, -3)), "1 2");
checkHappy("_: gm(2 2);", Float.unwrap(LibDecimalFloat.packLossless(2e3, -3)), "2 2");
checkHappy("_: gm(2 3);", Float.unwrap(LibDecimalFloat.packLossless(2450, -3)), "2 3");
checkHappy(
"_: gm(2 4);",
Float.unwrap(LibDecimalFloat.packLossless(282850000000000000000000000000000000000000, -41)),
"2 4"
);
checkHappy("_: gm(4 0.5);", Float.unwrap(LibDecimalFloat.packLossless(1415, -3)), "4 0.5");
}

// function testOpGmTwoOutputs() external {
// checkBadOutputs("_ _: gm(1 1);", 2, 1, 2);
// }
/// Test the eval of `gm` for bad inputs.
function testOpGmOneInput() external {
checkBadInputs("_: gm(1);", 1, 2, 1);
}

// /// Test that operand is disallowed.
// function testOpGmEvalOperandDisallowed() external {
// checkUnhappyParse("_: gm<0>(1 1);", abi.encodeWithSelector(UnexpectedOperand.selector));
// }
// }
function testOpGmThreeInputs() external {
checkBadInputs("_: gm(1 1 1);", 3, 2, 3);
}

function testOpGmZeroOutputs() external {
checkBadOutputs(": gm(1 1);", 2, 1, 0);
}

function testOpGmTwoOutputs() external {
checkBadOutputs("_ _: gm(1 1);", 2, 1, 2);
}

/// Test that operand is disallowed.
function testOpGmEvalOperandDisallowed() external {
checkUnhappyParse("_: gm<0>(1 1);", abi.encodeWithSelector(UnexpectedOperand.selector));
}
}