From caee3d61858ab668f6eb2a3ee37ad0ed16e97794 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Sun, 22 Jan 2023 14:29:24 +0400 Subject: [PATCH 01/23] extern op interface extern --- .../deploy/IExpressionDeployerV1.sol | 2 +- .../extern/IInterpreterExternV1.sol | 12 + contracts/interpreter/extern/LibExtern.sol | 17 + contracts/interpreter/ops/core/OpExtern.sol | 79 ++++ .../math/fixedPoint/OpFixedPointScaleBy.sol | 6 +- .../FixedPointMath/FixedPointMathTest.sol | 8 +- echidna/Math/FixedPointMathEchidna.sol | 199 ++++++---- test/Math/FixedPointMath/scaleArithmetic.ts | 16 +- test/Math/FixedPointMath/scaleNum.ts | 345 ++++++++++++------ .../OrderBookFlashLender/flashLoan.ts | 3 +- test/OrderBook/takeOrders.ts | 9 +- test/Sale/buy.ts | 7 +- 12 files changed, 493 insertions(+), 210 deletions(-) create mode 100644 contracts/interpreter/extern/IInterpreterExternV1.sol create mode 100644 contracts/interpreter/extern/LibExtern.sol create mode 100644 contracts/interpreter/ops/core/OpExtern.sol diff --git a/contracts/interpreter/deploy/IExpressionDeployerV1.sol b/contracts/interpreter/deploy/IExpressionDeployerV1.sol index e8ababa24..dad6a138b 100644 --- a/contracts/interpreter/deploy/IExpressionDeployerV1.sol +++ b/contracts/interpreter/deploy/IExpressionDeployerV1.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: CAL -pragma solidity =0.8.17; +pragma solidity ^0.8.17; /// Config required to build a new `State`. /// @param sources Sources verbatim. These sources MUST be provided in their diff --git a/contracts/interpreter/extern/IInterpreterExternV1.sol b/contracts/interpreter/extern/IInterpreterExternV1.sol new file mode 100644 index 000000000..f4181e283 --- /dev/null +++ b/contracts/interpreter/extern/IInterpreterExternV1.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.15; + +type EncodedExternDispatch is uint256; +type ExternDispatch is uint256; + +interface IInterpreterExternV1 { + function extern( + ExternDispatch dispatch_, + uint256[] calldata inputs_ + ) external view returns (uint256[] calldata outputs); +} diff --git a/contracts/interpreter/extern/LibExtern.sol b/contracts/interpreter/extern/LibExtern.sol new file mode 100644 index 000000000..a5a07ae55 --- /dev/null +++ b/contracts/interpreter/extern/LibExtern.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.15; + +import "./IInterpreterExternV1.sol"; + +library LibExtern { + function decode( + EncodedExternDispatch dispatch_ + ) internal pure returns (IInterpreterExternV1, ExternDispatch) { + return ( + IInterpreterExternV1( + address(uint160(EncodedExternDispatch.unwrap(dispatch_))) + ), + ExternDispatch.wrap(EncodedExternDispatch.unwrap(dispatch_) >> 160) + ); + } +} diff --git a/contracts/interpreter/ops/core/OpExtern.sol b/contracts/interpreter/ops/core/OpExtern.sol new file mode 100644 index 000000000..97ee1f806 --- /dev/null +++ b/contracts/interpreter/ops/core/OpExtern.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.15; + +import "../../../math/Binary.sol"; +import "../../deploy/LibIntegrityCheck.sol"; +import "./OpReadMemory.sol"; +import "../../extern/LibExtern.sol"; +import "../../run/LibStackPointer.sol"; + +/// Thrown when the length of results from an extern don't match what the operand +/// defines. This is bad because it implies our integrity check miscalculated the +/// stack which is undefined behaviour. +/// @param expected The length we expected based on the operand. +/// @param actual The length that was returned from the extern. +error BadExternResultsLength(uint256 expected, uint256 actual); + +library OpExtern { + using LibIntegrityCheck for IntegrityCheckState; + using LibStackPointer for StackPointer; + + function integrity( + IntegrityCheckState memory integrityCheckState_, + Operand operand_, + StackPointer stackTop_ + ) internal pure returns (StackPointer) { + uint256 inputs_ = Operand.unwrap(operand_) & MASK_5BIT; + uint256 outputs_ = (Operand.unwrap(operand_) >> 5) & MASK_5BIT; + uint256 offset_ = Operand.unwrap(operand_) >> 10; + + if (offset_ >= integrityCheckState_.constantsLength) { + revert OutOfBoundsConstantsRead( + integrityCheckState_.constantsLength, + offset_ + ); + } + + return + integrityCheckState_.push( + integrityCheckState_.pop(stackTop_, inputs_), + outputs_ + ); + } + + function intern( + InterpreterState memory interpreterState_, + Operand operand_, + StackPointer stackTop_ + ) internal view returns (StackPointer) { + uint256 inputs_ = Operand.unwrap(operand_) & MASK_5BIT; + uint256 outputs_ = (Operand.unwrap(operand_) >> 5) & MASK_5BIT; + uint256 offset_ = (Operand.unwrap(operand_) >> 10); + + // Mirrors constant opcode. + EncodedExternDispatch encodedDispatch_; + assembly ("memory-safe") { + encodedDispatch_ := add( + mload(add(interpreterState_, 0x20)), + mul(0x20, offset_) + ) + } + + ( + IInterpreterExternV1 interpreterExtern_, + ExternDispatch externDispatch_ + ) = LibExtern.decode(encodedDispatch_); + (uint256 head_, uint256[] memory tail_) = stackTop_.list(inputs_); + + uint256[] memory results_ = interpreterExtern_.extern( + externDispatch_, + tail_ + ); + + if (results_.length != outputs_) { + revert BadExternResultsLength(outputs_, results_.length); + } + + return stackTop_.down(inputs_).down().push(head_).push(results_); + } +} diff --git a/contracts/interpreter/ops/math/fixedPoint/OpFixedPointScaleBy.sol b/contracts/interpreter/ops/math/fixedPoint/OpFixedPointScaleBy.sol index d106372c4..a54ffab2c 100644 --- a/contracts/interpreter/ops/math/fixedPoint/OpFixedPointScaleBy.sol +++ b/contracts/interpreter/ops/math/fixedPoint/OpFixedPointScaleBy.sol @@ -14,7 +14,11 @@ library OpFixedPointScaleBy { using LibIntegrityCheck for IntegrityCheckState; function f(Operand operand_, uint256 a_) internal pure returns (uint256) { - return a_.scaleBy(int8(uint8(Operand.unwrap(operand_))), Math.Rounding.Down); + return + a_.scaleBy( + int8(uint8(Operand.unwrap(operand_))), + Math.Rounding.Down + ); } function integrity( diff --git a/contracts/test/math/FixedPointMath/FixedPointMathTest.sol b/contracts/test/math/FixedPointMath/FixedPointMathTest.sol index 2d668b8ea..21dc66a68 100644 --- a/contracts/test/math/FixedPointMath/FixedPointMathTest.sol +++ b/contracts/test/math/FixedPointMath/FixedPointMathTest.sol @@ -50,7 +50,7 @@ contract FixedPointMathTest { Math.Rounding rounding_ ) external pure returns (uint256) { return a_.scaleBy(scaleBy_, rounding_); - } + } /// Wraps `FixedPointMath.scaleRatio`. /// Scale a fixed point decimals of `DECIMALS` that represents a ratio of @@ -65,8 +65,8 @@ contract FixedPointMathTest { uint256 bDecimals_, Math.Rounding rounding_ ) external pure returns (uint256) { - return ratio_.scaleRatio(aDecimals_,bDecimals_,rounding_); - } + return ratio_.scaleRatio(aDecimals_, bDecimals_, rounding_); + } /// Wraps `FixedPointMath.scaleDown`. /// Scales `a_` down by a specified number of decimals, rounding in the @@ -81,7 +81,7 @@ contract FixedPointMathTest { uint256 scaleDownBy_, Math.Rounding rounding_ ) external pure returns (uint256) { - return a_.scaleDown(scaleDownBy_,rounding_) ; + return a_.scaleDown(scaleDownBy_, rounding_); } /// Wraps `FixedPointMath.fixedPointMul`. diff --git a/echidna/Math/FixedPointMathEchidna.sol b/echidna/Math/FixedPointMathEchidna.sol index 9c46e1f9e..306014937 100644 --- a/echidna/Math/FixedPointMathEchidna.sol +++ b/echidna/Math/FixedPointMathEchidna.sol @@ -5,17 +5,15 @@ import {MathUpgradeable as Math} from "@openzeppelin/contracts-upgradeable/utils import {SafeCastUpgradeable as SafeCast} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import {FixedPointMath, FP_DECIMALS, FP_ONE} from "../../contracts/math/FixedPointMath.sol"; -import {FixedPointMathTest} from "../../contracts/test/math/FixedPointMath/FixedPointMathTest.sol"; -import {SaturatingMath} from "../../contracts/math/SaturatingMath.sol"; - - +import {FixedPointMathTest} from "../../contracts/test/math/FixedPointMath/FixedPointMathTest.sol"; +import {SaturatingMath} from "../../contracts/math/SaturatingMath.sol"; /// @title FixedPointMathEchidna /// Wrapper around the `FixedPointMath` library for echidna fuzz testing. -contract FixedPointMathEchidna { +contract FixedPointMathEchidna { using Math for uint256; using SafeCast for int256; - using SafeCast for int8; + using SafeCast for int8; using SaturatingMath for uint256; bytes4 private panicBytes = 0x4e487b71; // Code bytes on the data reason that represent Panic @@ -28,16 +26,26 @@ contract FixedPointMathEchidna { constructor() { _fixedPointMathTest = new FixedPointMathTest(); - } + } // Fuzz test to scale a fixed point decimals of `DECIMALS` that represents a ratio - function ScaleRatio(uint256 ratio_, int8 aDecimals_, int8 bDecimals_, uint256 rounding_) external view { - // Make sure that - try _fixedPointMathTest.scaleRatio(ratio_, aDecimals_.toUint256(),bDecimals_.toUint256(),Math.Rounding(rounding_ % 3)) returns ( - uint256 valueScaled - ) { - int8 scaleBy_ = bDecimals_ - aDecimals_ ; - + function ScaleRatio( + uint256 ratio_, + int8 aDecimals_, + int8 bDecimals_, + uint256 rounding_ + ) external view { + // Make sure that + try + _fixedPointMathTest.scaleRatio( + ratio_, + aDecimals_.toUint256(), + bDecimals_.toUint256(), + Math.Rounding(rounding_ % 3) + ) + returns (uint256 valueScaled) { + int8 scaleBy_ = bDecimals_ - aDecimals_; + if (scaleBy_ == 0) { assert(ratio_ == valueScaled); } else { @@ -48,10 +56,13 @@ contract FixedPointMathEchidna { uint256 scaleDownBy_; unchecked { scaleDownBy_ = uint8(-1 * scaleBy_); - } + } uint256 b_ = 10 ** scaleDownBy_; valueExpected = ratio_ / b_; - if ( Math.Rounding(rounding_ % 3) == Math.Rounding.Up && ratio_ != valueExpected * b_) { + if ( + Math.Rounding(rounding_ % 3) == Math.Rounding.Up && + ratio_ != valueExpected * b_ + ) { valueExpected += 1; } } @@ -60,30 +71,48 @@ contract FixedPointMathEchidna { } catch (bytes memory reason) { _checkReason(reason); } - } + } // Fuzz test to Scale `a_` down by a specified number of decimals - function ScaleDown(uint256 a_, uint256 scaleDownBy_, uint256 rounding_) external view { - try _fixedPointMathTest.scaleDown(a_, scaleDownBy_,Math.Rounding(rounding_ % 3)) returns ( - uint256 valueScaled - ) { + function ScaleDown( + uint256 a_, + uint256 scaleDownBy_, + uint256 rounding_ + ) external view { + try + _fixedPointMathTest.scaleDown( + a_, + scaleDownBy_, + Math.Rounding(rounding_ % 3) + ) + returns (uint256 valueScaled) { uint256 b_ = 10 ** scaleDownBy_; uint256 valueExpected = a_ / b_; - if ( Math.Rounding(rounding_ % 3) == Math.Rounding.Up && a_ != valueExpected * b_) { + if ( + Math.Rounding(rounding_ % 3) == Math.Rounding.Up && + a_ != valueExpected * b_ + ) { valueExpected += 1; - } + } assert(valueScaled == valueExpected); } catch (bytes memory reason) { _checkReason(reason); } - } - + } // Fuzz test to Scale a fixed point decimal of some scale factor to match the decimals. - function Scale18(uint256 a_, uint256 aDecimals_, uint256 rounding_) external view { - try _fixedPointMathTest.scale18(a_, aDecimals_,Math.Rounding(rounding_ % 3)) returns ( - uint256 valueScaled - ) { + function Scale18( + uint256 a_, + uint256 aDecimals_, + uint256 rounding_ + ) external view { + try + _fixedPointMathTest.scale18( + a_, + aDecimals_, + Math.Rounding(rounding_ % 3) + ) + returns (uint256 valueScaled) { if (FP_DECIMALS == aDecimals_) { assert(a_ == valueScaled); } else { @@ -95,11 +124,14 @@ contract FixedPointMathEchidna { valueExpected = a_ * 10 ** decimals; } else { // - decimals = aDecimals_ - FP_DECIMALS; - uint256 b_ = 10 ** decimals ; - valueExpected = a_ / b_ ; + decimals = aDecimals_ - FP_DECIMALS; + uint256 b_ = 10 ** decimals; + valueExpected = a_ / b_; - if (Math.Rounding(rounding_ % 3) == Math.Rounding.Up && a_ != valueExpected * b_) { + if ( + Math.Rounding(rounding_ % 3) == Math.Rounding.Up && + a_ != valueExpected * b_ + ) { valueExpected += 1; } } @@ -109,14 +141,21 @@ contract FixedPointMathEchidna { } catch (bytes memory reason) { _checkReason(reason); } - } + } - // Fuzz test to Scale a fixed point decimals of decimals to some other scale. - function ScaleN(uint256 a_, uint256 targetDecimals_, uint256 rounding_) external view { - try _fixedPointMathTest.scaleN(a_, targetDecimals_,Math.Rounding(rounding_ % 3)) returns ( - uint256 valueScaled - ) { + function ScaleN( + uint256 a_, + uint256 targetDecimals_, + uint256 rounding_ + ) external view { + try + _fixedPointMathTest.scaleN( + a_, + targetDecimals_, + Math.Rounding(rounding_ % 3) + ) + returns (uint256 valueScaled) { if (FP_DECIMALS == targetDecimals_) { assert(a_ == valueScaled); } else { @@ -125,17 +164,19 @@ contract FixedPointMathEchidna { if (FP_DECIMALS > targetDecimals_) { // decimals = FP_DECIMALS - targetDecimals_; - uint256 b_ = 10 ** decimals ; - valueExpected = a_ / b_; + uint256 b_ = 10 ** decimals; + valueExpected = a_ / b_; - if (Math.Rounding(rounding_ % 3) == Math.Rounding.Up && a_ != valueExpected * b_) { + if ( + Math.Rounding(rounding_ % 3) == Math.Rounding.Up && + a_ != valueExpected * b_ + ) { valueExpected += 1; } - } else { // decimals = targetDecimals_ - FP_DECIMALS; - valueExpected = a_ * 10 ** decimals; + valueExpected = a_ * 10 ** decimals; } assert(valueScaled == valueExpected); @@ -143,13 +184,21 @@ contract FixedPointMathEchidna { } catch (bytes memory reason) { _checkReason(reason); } - } + } // Fuzz test to Scale a fixed point up or down by scaleBy orders of magnitude. - function ScaleBy(uint256 a_, int8 scaleBy_, uint256 rounding_) external view { - try _fixedPointMathTest.scaleBy(a_, scaleBy_,Math.Rounding(rounding_ % 3)) returns ( - uint256 valueScaled - ) { + function ScaleBy( + uint256 a_, + int8 scaleBy_, + uint256 rounding_ + ) external view { + try + _fixedPointMathTest.scaleBy( + a_, + scaleBy_, + Math.Rounding(rounding_ % 3) + ) + returns (uint256 valueScaled) { if (scaleBy_ == 0) { assert(a_ == valueScaled); } else { @@ -159,9 +208,12 @@ contract FixedPointMathEchidna { assert(valueExpected == valueScaled); } else { uint256 posScaleDownBy_ = uint8(-1 * scaleBy_); - uint256 b_ = 10 ** posScaleDownBy_ ; + uint256 b_ = 10 ** posScaleDownBy_; valueExpected = a_ / b_; - if (Math.Rounding(rounding_ % 3) == Math.Rounding.Up && a_ != valueExpected * b_) { + if ( + Math.Rounding(rounding_ % 3) == Math.Rounding.Up && + a_ != valueExpected * b_ + ) { valueExpected += 1; } assert(valueExpected == valueScaled); @@ -170,28 +222,53 @@ contract FixedPointMathEchidna { } catch (bytes memory reason) { _checkReason(reason); } - } + } // Fuzz test to Fixed point multiplication in native scale decimals. - function FixedPointMul(uint256 a_, uint256 b_, uint256 rounding_) external view { - uint256 valueCalculated = _fixedPointMathTest.fixedPointMul(a_, b_,Math.Rounding(rounding_ % 3)); + function FixedPointMul( + uint256 a_, + uint256 b_, + uint256 rounding_ + ) external view { + uint256 valueCalculated = _fixedPointMathTest.fixedPointMul( + a_, + b_, + Math.Rounding(rounding_ % 3) + ); // FixedPointMul call is using library from OZ contracts - uint256 valueExpected = Math.mulDiv(a_, b_, FP_ONE,Math.Rounding(rounding_ % 3)); + uint256 valueExpected = Math.mulDiv( + a_, + b_, + FP_ONE, + Math.Rounding(rounding_ % 3) + ); assert(valueCalculated == valueExpected); - } + } // Fuzz test to Fixed point division in native scale decimals. - function FixedPointDiv(uint256 a_, uint256 b_, uint256 rounding_) external view { - uint256 valueCalculated = _fixedPointMathTest.fixedPointDiv(a_, b_,Math.Rounding(rounding_ % 3)); + function FixedPointDiv( + uint256 a_, + uint256 b_, + uint256 rounding_ + ) external view { + uint256 valueCalculated = _fixedPointMathTest.fixedPointDiv( + a_, + b_, + Math.Rounding(rounding_ % 3) + ); // FixedPointDiv call is using library from OZ contracts - uint256 valueExpected = Math.mulDiv(a_, FP_ONE, b_,Math.Rounding(rounding_ % 3)); + uint256 valueExpected = Math.mulDiv( + a_, + FP_ONE, + b_, + Math.Rounding(rounding_ % 3) + ); assert(valueCalculated == valueExpected); - } + } - // Helper function to check a reason bytes agains a Panic revert with Underflow or Overflow error code function _checkReason(bytes memory reason_) private view { bytes4 failureReason = bytes4(reason_); diff --git a/test/Math/FixedPointMath/scaleArithmetic.ts b/test/Math/FixedPointMath/scaleArithmetic.ts index fb7a20c34..5d4dfa1c3 100644 --- a/test/Math/FixedPointMath/scaleArithmetic.ts +++ b/test/Math/FixedPointMath/scaleArithmetic.ts @@ -19,11 +19,9 @@ describe("FixedPointMathTest scaling during arithmetic op", async function () { it("should scale a number by 18 order of magnitude while multiplying", async () => { const a_ = 5; const b_ = ethers.BigNumber.from("1000000000000000000").mul(2); - const result = await fixedPointMathTest["fixedPointMul(uint256,uint256,uint8)"]( - a_, - b_, - ROUND_UP - ); + const result = await fixedPointMathTest[ + "fixedPointMul(uint256,uint256,uint8)" + ](a_, b_, ROUND_UP); const expectedResult = ethers.BigNumber.from(a_).mul(b_).div(ONE); assert(result.eq(expectedResult)); @@ -89,11 +87,9 @@ describe("FixedPointMathTest scaling during arithmetic op", async function () { const a_ = 60; const b_ = ethers.BigNumber.from("2" + eighteenZeros); - const result = await fixedPointMathTest["fixedPointDiv(uint256,uint256,uint8)"]( - a_, - b_, - ROUND_UP - ); + const result = await fixedPointMathTest[ + "fixedPointDiv(uint256,uint256,uint8)" + ](a_, b_, ROUND_UP); const expectedResult = ethers.BigNumber.from(a_).mul(ONE).div(b_); assert(result.eq(expectedResult)); diff --git a/test/Math/FixedPointMath/scaleNum.ts b/test/Math/FixedPointMath/scaleNum.ts index 7e3568747..a6fae3a09 100644 --- a/test/Math/FixedPointMath/scaleNum.ts +++ b/test/Math/FixedPointMath/scaleNum.ts @@ -1,37 +1,43 @@ import { assert } from "chai"; import { ethers } from "hardhat"; import { FixedPointMathTest } from "../../../typechain/contracts/test/math/FixedPointMath/FixedPointMathTest"; -import { basicDeploy, eighteenZeros, sixteenZeros, sixZeros, tenZeros } from "../../../utils"; +import { + basicDeploy, + eighteenZeros, + sixteenZeros, + sixZeros, + tenZeros, +} from "../../../utils"; import { fixedPointMathDeploy } from "../../../utils/deploy/math/fixedPointMath/deploy"; describe("FixedPointMathTest scaling a number", async function () { - let fixedPointMathTest: FixedPointMathTest; + let fixedPointMathTest: FixedPointMathTest; const ROUND_DOWN = 0; const ROUND_UP = 1; const ROUND_TO_ZERO = 2; before(async () => { - fixedPointMathTest = await fixedPointMathDeploy(); - }); - - + fixedPointMathTest = await fixedPointMathDeploy(); + }); // Scale 18 it("should scale a fixed point decimal UP to scale 18", async () => { const a_ = ethers.BigNumber.from(1 + sixZeros); const aDecimals_ = 8; // 0.01 - const result = await fixedPointMathTest.scale18(a_, aDecimals_,ROUND_UP); + const result = await fixedPointMathTest.scale18(a_, aDecimals_, ROUND_UP); const expectedResult = ethers.BigNumber.from(1 + sixZeros + tenZeros); assert(result.eq(expectedResult)); - }); + }); it("should scale a fixed point decimal UP to scale 18 for a_ != scaled_ * b_", async () => { const a_ = ethers.BigNumber.from(1 + sixteenZeros + "3367"); - const aDecimals_ = 20; - const result = await fixedPointMathTest.scale18(a_, aDecimals_,ROUND_UP); - const expectedResult = ethers.BigNumber.from(1 + sixteenZeros + "33").add(ethers.BigNumber.from(1)); + const aDecimals_ = 20; + const result = await fixedPointMathTest.scale18(a_, aDecimals_, ROUND_UP); + const expectedResult = ethers.BigNumber.from(1 + sixteenZeros + "33").add( + ethers.BigNumber.from(1) + ); assert(result.eq(expectedResult)); }); @@ -39,16 +45,16 @@ describe("FixedPointMathTest scaling a number", async function () { const a_ = ethers.BigNumber.from(1 + eighteenZeros + sixZeros); const aDecimals_ = 24; // 1.0 - const result = await fixedPointMathTest.scale18(a_, aDecimals_,ROUND_DOWN); + const result = await fixedPointMathTest.scale18(a_, aDecimals_, ROUND_DOWN); const expectedResult = ethers.BigNumber.from(1 + eighteenZeros); assert(result.eq(expectedResult)); - }); + }); it("should scale a fixed point decimal DOWN to scale 18 for (a_ != scaled_ * b_)", async () => { - const a_ = ethers.BigNumber.from(1 + sixteenZeros + "3667") - const aDecimals_ = 20; + const a_ = ethers.BigNumber.from(1 + sixteenZeros + "3667"); + const aDecimals_ = 20; - const result = await fixedPointMathTest.scale18(a_, aDecimals_,ROUND_DOWN); + const result = await fixedPointMathTest.scale18(a_, aDecimals_, ROUND_DOWN); const expectedResult = ethers.BigNumber.from(1 + sixteenZeros + "36"); assert(result.eq(expectedResult)); }); @@ -56,15 +62,23 @@ describe("FixedPointMathTest scaling a number", async function () { it("should scale a 0 order of magnitude decimal to scale 18", async () => { const a_ = ethers.BigNumber.from(1); const aDecimals_ = 0; // 1.0 - const result = await fixedPointMathTest.scale18(a_, aDecimals_,ROUND_TO_ZERO); + const result = await fixedPointMathTest.scale18( + a_, + aDecimals_, + ROUND_TO_ZERO + ); const expectedResult = ethers.BigNumber.from(1 + eighteenZeros); assert(result.eq(expectedResult)); - }); + }); it("should scale a 0 order of magnitude decimal to scale 18", async () => { - const a_ = ethers.BigNumber.from(1 + sixteenZeros + "3667") - const aDecimals_ = 20; - const result = await fixedPointMathTest.scale18(a_, aDecimals_,ROUND_TO_ZERO); + const a_ = ethers.BigNumber.from(1 + sixteenZeros + "3667"); + const aDecimals_ = 20; + const result = await fixedPointMathTest.scale18( + a_, + aDecimals_, + ROUND_TO_ZERO + ); const expectedResult = ethers.BigNumber.from(1 + sixteenZeros + "36"); assert(result.eq(expectedResult)); }); @@ -74,32 +88,50 @@ describe("FixedPointMathTest scaling a number", async function () { const a_ = ethers.BigNumber.from(1 + eighteenZeros); const targetDecimals_ = 20; - const result = await fixedPointMathTest.scaleN(a_, targetDecimals_,ROUND_UP); + const result = await fixedPointMathTest.scaleN( + a_, + targetDecimals_, + ROUND_UP + ); const expectedResult = ethers.BigNumber.from(1 + eighteenZeros + "00"); assert(result.eq(expectedResult)); - }); + }); it("should scale a fixed point decimal UP to scale N", async () => { const a_ = ethers.BigNumber.from(1 + sixteenZeros + "36"); const targetDecimals_ = 11; - const result = await fixedPointMathTest.scaleN(a_, targetDecimals_,ROUND_UP); - const expectedResult = ethers.BigNumber.from(10 + tenZeros).add(ethers.BigNumber.from(1)); + const result = await fixedPointMathTest.scaleN( + a_, + targetDecimals_, + ROUND_UP + ); + const expectedResult = ethers.BigNumber.from(10 + tenZeros).add( + ethers.BigNumber.from(1) + ); assert(result.eq(expectedResult)); }); it("should scale a fixed point decimal DOWN to scale N", async () => { const a_ = ethers.BigNumber.from(1 + eighteenZeros); const targetDecimals_ = 8; - const result = await fixedPointMathTest.scaleN(a_, targetDecimals_,ROUND_DOWN); + const result = await fixedPointMathTest.scaleN( + a_, + targetDecimals_, + ROUND_DOWN + ); const expectedResult = ethers.BigNumber.from(1 + sixZeros + "00"); assert(result.eq(expectedResult)); - }); + }); it("should scale a fixed point decimal UP to scale N", async () => { const a_ = ethers.BigNumber.from(178945 + tenZeros + "36"); const targetDecimals_ = 12; - const result = await fixedPointMathTest.scaleN(a_, targetDecimals_,ROUND_DOWN); + const result = await fixedPointMathTest.scaleN( + a_, + targetDecimals_, + ROUND_DOWN + ); const expectedResult = ethers.BigNumber.from(178945 + "000000"); assert(result.eq(expectedResult)); }); @@ -108,7 +140,11 @@ describe("FixedPointMathTest scaling a number", async function () { const a_ = ethers.BigNumber.from(1); const targetDecimals_ = 18; - const result = await fixedPointMathTest.scaleN(a_, targetDecimals_,ROUND_TO_ZERO); + const result = await fixedPointMathTest.scaleN( + a_, + targetDecimals_, + ROUND_TO_ZERO + ); const expectedResult = ethers.BigNumber.from(1); assert(result.eq(expectedResult)); }); @@ -118,26 +154,28 @@ describe("FixedPointMathTest scaling a number", async function () { const a_ = ethers.BigNumber.from(1 + eighteenZeros); const scaleBy_ = 2; - const result = await fixedPointMathTest.scaleBy(a_, scaleBy_,ROUND_UP); + const result = await fixedPointMathTest.scaleBy(a_, scaleBy_, ROUND_UP); const expectedResult = ethers.BigNumber.from(1 + eighteenZeros + "00"); assert(result.eq(expectedResult)); - }); + }); it("should scale a fixed point decimal UP by scale N (scaleBy_ < 0) ", async () => { const a_ = ethers.BigNumber.from(1 + eighteenZeros); const scaleBy_ = -2; - const result = await fixedPointMathTest.scaleBy(a_, scaleBy_,ROUND_UP); + const result = await fixedPointMathTest.scaleBy(a_, scaleBy_, ROUND_UP); const expectedResult = ethers.BigNumber.from(1 + sixteenZeros); assert(result.eq(expectedResult)); - }); + }); it("should scale a fixed point decimal UP by scale N (scaleBy_ < 0) ", async () => { const a_ = ethers.BigNumber.from(1 + sixteenZeros + "36"); const scaleBy_ = -2; - const result = await fixedPointMathTest.scaleBy(a_, scaleBy_,ROUND_UP); - const expectedResult = ethers.BigNumber.from(1 + sixteenZeros).add(ethers.BigNumber.from(1)); + const result = await fixedPointMathTest.scaleBy(a_, scaleBy_, ROUND_UP); + const expectedResult = ethers.BigNumber.from(1 + sixteenZeros).add( + ethers.BigNumber.from(1) + ); assert(result.eq(expectedResult)); }); @@ -145,16 +183,16 @@ describe("FixedPointMathTest scaling a number", async function () { const a_ = ethers.BigNumber.from(1 + sixZeros + "00"); const scaleBy_ = -2; - const result = await fixedPointMathTest.scaleBy(a_, scaleBy_,ROUND_DOWN); + const result = await fixedPointMathTest.scaleBy(a_, scaleBy_, ROUND_DOWN); const expectedResult = ethers.BigNumber.from(1 + sixZeros); assert(result.eq(expectedResult)); - }); + }); it("should scale a fixed point decimal DOWN by scale N (scaleBy_ < 0) ", async () => { const a_ = ethers.BigNumber.from(1 + sixteenZeros + "36"); const scaleBy_ = -2; - const result = await fixedPointMathTest.scaleBy(a_, scaleBy_,ROUND_DOWN); + const result = await fixedPointMathTest.scaleBy(a_, scaleBy_, ROUND_DOWN); const expectedResult = ethers.BigNumber.from(1 + sixteenZeros); assert(result.eq(expectedResult)); }); @@ -163,146 +201,211 @@ describe("FixedPointMathTest scaling a number", async function () { const a_ = ethers.BigNumber.from(1 + eighteenZeros); const scaleBy_ = 0; - const result = await fixedPointMathTest.scaleBy(a_, scaleBy_,ROUND_TO_ZERO); + const result = await fixedPointMathTest.scaleBy( + a_, + scaleBy_, + ROUND_TO_ZERO + ); const expectedResult = ethers.BigNumber.from(1 + eighteenZeros); assert(result.eq(expectedResult)); - }); + }); + + // Scale Ratio - // Scale Ratio - it("should scale ratio UP for aDecimals > bDecimals", async () => { const ratio = ethers.BigNumber.from(1 + tenZeros + "00367589"); - const aDecimals = 12 - const bDecimals = 10 - - const result = await fixedPointMathTest.scaleRatio(ratio , aDecimals , bDecimals ,ROUND_UP); - const expectedResult = ethers.BigNumber.from(1 + tenZeros + "003675").add(ethers.BigNumber.from(1)); + const aDecimals = 12; + const bDecimals = 10; + + const result = await fixedPointMathTest.scaleRatio( + ratio, + aDecimals, + bDecimals, + ROUND_UP + ); + const expectedResult = ethers.BigNumber.from(1 + tenZeros + "003675").add( + ethers.BigNumber.from(1) + ); assert(result.eq(expectedResult)); - }); + }); it("should scale ratio UP for aDecimals < bDecimals", async () => { const ratio = ethers.BigNumber.from(1 + tenZeros + "00367589"); - const aDecimals = 10 - const bDecimals = 12 - - const result = await fixedPointMathTest.scaleRatio(ratio , aDecimals , bDecimals ,ROUND_UP); - const expectedResult = ethers.BigNumber.from(1 + tenZeros + "00367589" + "00"); + const aDecimals = 10; + const bDecimals = 12; + + const result = await fixedPointMathTest.scaleRatio( + ratio, + aDecimals, + bDecimals, + ROUND_UP + ); + const expectedResult = ethers.BigNumber.from( + 1 + tenZeros + "00367589" + "00" + ); assert(result.eq(expectedResult)); }); it("should scale ratio UP for aDecimals == bDecimals", async () => { const ratio = ethers.BigNumber.from(1 + tenZeros + "00367589"); - const aDecimals = 12 - const bDecimals = 12 - - const result = await fixedPointMathTest.scaleRatio(ratio , aDecimals , bDecimals ,ROUND_UP); + const aDecimals = 12; + const bDecimals = 12; + + const result = await fixedPointMathTest.scaleRatio( + ratio, + aDecimals, + bDecimals, + ROUND_UP + ); const expectedResult = ethers.BigNumber.from(1 + tenZeros + "00367589"); assert(result.eq(expectedResult)); - }); + }); it("should scale ratio DOWN for aDecimals > bDecimals", async () => { const ratio = ethers.BigNumber.from(1 + tenZeros + "00367589"); - const aDecimals = 12 - const bDecimals = 10 - - const result = await fixedPointMathTest.scaleRatio(ratio , aDecimals , bDecimals ,ROUND_DOWN); + const aDecimals = 12; + const bDecimals = 10; + + const result = await fixedPointMathTest.scaleRatio( + ratio, + aDecimals, + bDecimals, + ROUND_DOWN + ); const expectedResult = ethers.BigNumber.from(1 + tenZeros + "003675"); assert(result.eq(expectedResult)); - }); + }); it("should scale ratio DOWN for aDecimals < bDecimals", async () => { const ratio = ethers.BigNumber.from(1 + tenZeros + "00367589"); - const aDecimals = 10 - const bDecimals = 12 - - const result = await fixedPointMathTest.scaleRatio(ratio , aDecimals , bDecimals ,ROUND_DOWN); - const expectedResult = ethers.BigNumber.from(1 + tenZeros + "00367589" + "00"); + const aDecimals = 10; + const bDecimals = 12; + + const result = await fixedPointMathTest.scaleRatio( + ratio, + aDecimals, + bDecimals, + ROUND_DOWN + ); + const expectedResult = ethers.BigNumber.from( + 1 + tenZeros + "00367589" + "00" + ); assert(result.eq(expectedResult)); }); it("should scale ratio DOWN for aDecimals == bDecimals", async () => { const ratio = ethers.BigNumber.from(1 + tenZeros + "00367589"); - const aDecimals = 12 - const bDecimals = 12 - - const result = await fixedPointMathTest.scaleRatio(ratio , aDecimals , bDecimals ,ROUND_DOWN); + const aDecimals = 12; + const bDecimals = 12; + + const result = await fixedPointMathTest.scaleRatio( + ratio, + aDecimals, + bDecimals, + ROUND_DOWN + ); const expectedResult = ethers.BigNumber.from(1 + tenZeros + "00367589"); assert(result.eq(expectedResult)); - }); + }); it("should scale ratio by 18 OOM for bDecimals = 0", async () => { const ratio = ethers.BigNumber.from(1 + tenZeros + "00367589"); - const aDecimals = 18 - const bDecimals = 0 - - const result = await fixedPointMathTest.scaleRatio(ratio , aDecimals , bDecimals ,ROUND_TO_ZERO); + const aDecimals = 18; + const bDecimals = 0; + + const result = await fixedPointMathTest.scaleRatio( + ratio, + aDecimals, + bDecimals, + ROUND_TO_ZERO + ); const expectedResult = ethers.BigNumber.from(1); assert(result.eq(expectedResult)); }); it("should scale ratio by 18 OOM for aDecimals = 0", async () => { const ratio = ethers.BigNumber.from(1 + tenZeros + "00367589"); - const aDecimals = 0 - const bDecimals = 18 - - const result = await fixedPointMathTest.scaleRatio(ratio , aDecimals , bDecimals ,ROUND_TO_ZERO); - const expectedResult = ethers.BigNumber.from(1 + tenZeros + "00367589" + eighteenZeros); + const aDecimals = 0; + const bDecimals = 18; + + const result = await fixedPointMathTest.scaleRatio( + ratio, + aDecimals, + bDecimals, + ROUND_TO_ZERO + ); + const expectedResult = ethers.BigNumber.from( + 1 + tenZeros + "00367589" + eighteenZeros + ); assert(result.eq(expectedResult)); - }); + }); + + // Scale Down - // Scale Down - it("should scale down by specified decimals and round UP", async () => { - const a_ = ethers.BigNumber.from(1 + eighteenZeros) - const scaleDownBy = 2 + const a_ = ethers.BigNumber.from(1 + eighteenZeros); + const scaleDownBy = 2; - const result = await fixedPointMathTest.scaleDown(a_ , scaleDownBy ,ROUND_UP); + const result = await fixedPointMathTest.scaleDown( + a_, + scaleDownBy, + ROUND_UP + ); const expectedResult = ethers.BigNumber.from(1 + sixteenZeros); - assert(result.eq(expectedResult)); - - }); + assert(result.eq(expectedResult)); + }); it("should scale down by specified decimals and round UP", async () => { - const a_ = ethers.BigNumber.from(1 + tenZeros + "00367589") - const scaleDownBy = 2 - - const result = await fixedPointMathTest.scaleDown(a_ , scaleDownBy ,ROUND_UP); - const expectedResult = ethers.BigNumber.from(1 + tenZeros + "003675").add(ethers.BigNumber.from(1)); - assert(result.eq(expectedResult)); - + const a_ = ethers.BigNumber.from(1 + tenZeros + "00367589"); + const scaleDownBy = 2; + + const result = await fixedPointMathTest.scaleDown( + a_, + scaleDownBy, + ROUND_UP + ); + const expectedResult = ethers.BigNumber.from(1 + tenZeros + "003675").add( + ethers.BigNumber.from(1) + ); + assert(result.eq(expectedResult)); }); it("should scale down by specified decimals and round DOWN", async () => { - const a_ = ethers.BigNumber.from(1 + eighteenZeros) - const scaleDownBy = 2 + const a_ = ethers.BigNumber.from(1 + eighteenZeros); + const scaleDownBy = 2; - const result = await fixedPointMathTest.scaleDown(a_ , scaleDownBy ,ROUND_DOWN); + const result = await fixedPointMathTest.scaleDown( + a_, + scaleDownBy, + ROUND_DOWN + ); const expectedResult = ethers.BigNumber.from(1 + sixteenZeros); - assert(result.eq(expectedResult)); - - }); + assert(result.eq(expectedResult)); + }); it("should scale down by specified decimals and round DOWN", async () => { - const a_ = ethers.BigNumber.from(1 + tenZeros + "00367589") - const scaleDownBy = 2 - - const result = await fixedPointMathTest.scaleDown(a_ , scaleDownBy ,ROUND_DOWN); + const a_ = ethers.BigNumber.from(1 + tenZeros + "00367589"); + const scaleDownBy = 2; + + const result = await fixedPointMathTest.scaleDown( + a_, + scaleDownBy, + ROUND_DOWN + ); const expectedResult = ethers.BigNumber.from(1 + tenZeros + "003675"); - assert(result.eq(expectedResult)); - - }); + assert(result.eq(expectedResult)); + }); it("should scale down by 18 OOMs", async () => { - const a_ = ethers.BigNumber.from(1 + tenZeros + "00367589") - const scaleDownBy = 18 - - const result = await fixedPointMathTest.scaleDown(a_ , scaleDownBy ,ROUND_TO_ZERO); + const a_ = ethers.BigNumber.from(1 + tenZeros + "00367589"); + const scaleDownBy = 18; + + const result = await fixedPointMathTest.scaleDown( + a_, + scaleDownBy, + ROUND_TO_ZERO + ); const expectedResult = ethers.BigNumber.from(1); - assert(result.eq(expectedResult)); - + assert(result.eq(expectedResult)); }); - - - - }); diff --git a/test/OrderBook/OrderBookFlashLender/flashLoan.ts b/test/OrderBook/OrderBookFlashLender/flashLoan.ts index e98a64e4a..addadedac 100644 --- a/test/OrderBook/OrderBookFlashLender/flashLoan.ts +++ b/test/OrderBook/OrderBookFlashLender/flashLoan.ts @@ -104,8 +104,7 @@ describe("OrderBookFlashLender flashLoan test", async function () { ), `FlashLenderCallbackFailed`, "did not roll back transaction" - ); - + ); }); it("should roll back flash loan transaction if callback does not return correct hash", async function () { diff --git a/test/OrderBook/takeOrders.ts b/test/OrderBook/takeOrders.ts index 9010dafd8..9cb59f86c 100644 --- a/test/OrderBook/takeOrders.ts +++ b/test/OrderBook/takeOrders.ts @@ -5275,12 +5275,5 @@ describe("OrderBook take orders", async function () { } } } - }); - - - - - - - + }); }); diff --git a/test/Sale/buy.ts b/test/Sale/buy.ts index 0f0227e02..b6fc5d545 100644 --- a/test/Sale/buy.ts +++ b/test/Sale/buy.ts @@ -279,8 +279,11 @@ describe("Sale buy", async function () { // Rounding up the cost by adding 1 // Internally Cost is calculated using fixedPointMul with Rounding Up - // Check FixedPointMath for refrence - const expectedCost0 = expectedPrice0.mul(maxUnits).div(ONE).add(ethers.BigNumber.from(1)) + // Check FixedPointMath for refrence + const expectedCost0 = expectedPrice0 + .mul(maxUnits) + .div(ONE) + .add(ethers.BigNumber.from(1)); // give signer1 reserve to cover cost + fee await reserve.transfer(signer1.address, expectedCost0.add(fee)); From f96ae8cca932bdf062c9ce701a3300aa4f2ace6b Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 26 Jan 2023 20:51:53 +0400 Subject: [PATCH 02/23] opextern compiling --- contracts/interpreter/ops/AllStandardOps.sol | 5 +- contracts/interpreter/ops/core/OpExtern.sol | 56 ++++++++++++-------- utils/interpreter/ops/allStandardOps.ts | 2 +- 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/contracts/interpreter/ops/AllStandardOps.sol b/contracts/interpreter/ops/AllStandardOps.sol index 1ad36a7ca..c39c29229 100644 --- a/contracts/interpreter/ops/AllStandardOps.sol +++ b/contracts/interpreter/ops/AllStandardOps.sol @@ -11,6 +11,7 @@ import "./core/OpContext.sol"; import "./core/OpContextRow.sol"; import "./core/OpDebug.sol"; import "./core/OpDoWhile.sol"; +import "./core/OpExtern.sol"; import "./core/OpFoldContext.sol"; import "./core/OpGet.sol"; import "./core/OpLoopN.sol"; @@ -195,12 +196,12 @@ library AllStandardOps { returns (StackPointer)[ALL_STANDARD_OPS_LENGTH + 1] memory pointersFixed_ = [ ALL_STANDARD_OPS_LENGTH.asIntegrityFunctionPointer(), - OpChainlinkOraclePrice.integrity, OpCall.integrity, OpContext.integrity, OpContextRow.integrity, OpDebug.integrity, OpDoWhile.integrity, + OpExtern.integrity, OpFoldContext.integrity, OpGet.integrity, OpLoopN.integrity, @@ -282,12 +283,12 @@ library AllStandardOps { returns (StackPointer)[ALL_STANDARD_OPS_LENGTH + 1] memory pointersFixed_ = [ ALL_STANDARD_OPS_LENGTH.asOpFunctionPointer(), - OpChainlinkOraclePrice.run, OpCall.run, OpContext.run, OpContextRow.run, OpDebug.run, OpDoWhile.run, + OpExtern.intern, OpFoldContext.run, OpGet.run, OpLoopN.run, diff --git a/contracts/interpreter/ops/core/OpExtern.sol b/contracts/interpreter/ops/core/OpExtern.sol index 97ee1f806..117741ce7 100644 --- a/contracts/interpreter/ops/core/OpExtern.sol +++ b/contracts/interpreter/ops/core/OpExtern.sol @@ -46,34 +46,46 @@ library OpExtern { Operand operand_, StackPointer stackTop_ ) internal view returns (StackPointer) { - uint256 inputs_ = Operand.unwrap(operand_) & MASK_5BIT; - uint256 outputs_ = (Operand.unwrap(operand_) >> 5) & MASK_5BIT; - uint256 offset_ = (Operand.unwrap(operand_) >> 10); + IInterpreterExternV1 interpreterExtern_; + ExternDispatch externDispatch_; + uint256 head_; + uint256[] memory tail_; + { + uint256 inputs_ = Operand.unwrap(operand_) & MASK_5BIT; + uint256 offset_ = (Operand.unwrap(operand_) >> 10); + + // Mirrors constant opcode. + EncodedExternDispatch encodedDispatch_; + assembly ("memory-safe") { + encodedDispatch_ := add( + mload(add(interpreterState_, 0x20)), + mul(0x20, offset_) + ) + } - // Mirrors constant opcode. - EncodedExternDispatch encodedDispatch_; - assembly ("memory-safe") { - encodedDispatch_ := add( - mload(add(interpreterState_, 0x20)), - mul(0x20, offset_) - ) + ( + interpreterExtern_, + externDispatch_ + ) = LibExtern.decode(encodedDispatch_); + (head_, tail_) = stackTop_.list(inputs_); + stackTop_ = stackTop_.down(inputs_).down().push(head_); } - ( - IInterpreterExternV1 interpreterExtern_, - ExternDispatch externDispatch_ - ) = LibExtern.decode(encodedDispatch_); - (uint256 head_, uint256[] memory tail_) = stackTop_.list(inputs_); + { + uint256 outputs_ = (Operand.unwrap(operand_) >> 5) & MASK_5BIT; + + uint256[] memory results_ = interpreterExtern_.extern( + externDispatch_, + tail_ + ); - uint256[] memory results_ = interpreterExtern_.extern( - externDispatch_, - tail_ - ); + if (results_.length != outputs_) { + revert BadExternResultsLength(outputs_, results_.length); + } - if (results_.length != outputs_) { - revert BadExternResultsLength(outputs_, results_.length); + stackTop_ = stackTop_.push(results_); } - return stackTop_.down(inputs_).down().push(head_).push(results_); + return stackTop_; } } diff --git a/utils/interpreter/ops/allStandardOps.ts b/utils/interpreter/ops/allStandardOps.ts index c74d240ec..e7aabb649 100644 --- a/utils/interpreter/ops/allStandardOps.ts +++ b/utils/interpreter/ops/allStandardOps.ts @@ -1,10 +1,10 @@ export enum AllStandardOps { - CHAINLINK_PRICE, CALL, CONTEXT, CONTEXT_ROW, DEBUG, DO_WHILE, + EXTERN, FOLD_CONTEXT, GET, LOOP_N, From 176c951b8156c9b892617dfc3d554e01803f5de1 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 26 Jan 2023 21:19:49 +0400 Subject: [PATCH 03/23] experimental extern contract --- contracts/interpreter/ops/AllStandardOps.sol | 4 ++- contracts/interpreter/ops/core/OpExtern.sol | 7 ++--- .../shared/RainterpreterExtern.sol | 30 +++++++++++++++++++ utils/interpreter/ops/allStandardOps.ts | 1 + 4 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 contracts/interpreter/shared/RainterpreterExtern.sol diff --git a/contracts/interpreter/ops/AllStandardOps.sol b/contracts/interpreter/ops/AllStandardOps.sol index c39c29229..11f61b9a7 100644 --- a/contracts/interpreter/ops/AllStandardOps.sol +++ b/contracts/interpreter/ops/AllStandardOps.sol @@ -70,7 +70,7 @@ import "./tier/OpUpdateTimesForTierRange.sol"; error BadDynamicLength(uint256 dynamicLength, uint256 standardOpsLength); /// @dev Number of ops currently provided by `AllStandardOps`. -uint256 constant ALL_STANDARD_OPS_LENGTH = 59; +uint256 constant ALL_STANDARD_OPS_LENGTH = 60; /// @title AllStandardOps /// @notice Every opcode available from the core repository laid out as a single @@ -196,6 +196,7 @@ library AllStandardOps { returns (StackPointer)[ALL_STANDARD_OPS_LENGTH + 1] memory pointersFixed_ = [ ALL_STANDARD_OPS_LENGTH.asIntegrityFunctionPointer(), + OpChainlinkOraclePrice.integrity, OpCall.integrity, OpContext.integrity, OpContextRow.integrity, @@ -283,6 +284,7 @@ library AllStandardOps { returns (StackPointer)[ALL_STANDARD_OPS_LENGTH + 1] memory pointersFixed_ = [ ALL_STANDARD_OPS_LENGTH.asOpFunctionPointer(), + OpChainlinkOraclePrice.run, OpCall.run, OpContext.run, OpContextRow.run, diff --git a/contracts/interpreter/ops/core/OpExtern.sol b/contracts/interpreter/ops/core/OpExtern.sol index 117741ce7..c38538bf4 100644 --- a/contracts/interpreter/ops/core/OpExtern.sol +++ b/contracts/interpreter/ops/core/OpExtern.sol @@ -63,10 +63,9 @@ library OpExtern { ) } - ( - interpreterExtern_, - externDispatch_ - ) = LibExtern.decode(encodedDispatch_); + (interpreterExtern_, externDispatch_) = LibExtern.decode( + encodedDispatch_ + ); (head_, tail_) = stackTop_.list(inputs_); stackTop_ = stackTop_.down(inputs_).down().push(head_); } diff --git a/contracts/interpreter/shared/RainterpreterExtern.sol b/contracts/interpreter/shared/RainterpreterExtern.sol new file mode 100644 index 000000000..ea9b224ea --- /dev/null +++ b/contracts/interpreter/shared/RainterpreterExtern.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.15; + +import "../extern/IInterpreterExternV1.sol"; +import "../ops/chainlink/OpChainlinkOraclePrice.sol"; +import "../run/LibStackPointer.sol"; +import "../../array/LibUint256Array.sol"; + +error BadInputs(uint256 expected, uint256 actual); + +contract RainterpreterExtern is IInterpreterExternV1 { + using LibStackPointer for uint256[]; + using LibStackPointer for StackPointer; + using LibUint256Array for uint256; + + function extern( + ExternDispatch, + uint256[] memory inputs_ + ) external view returns (uint256[] memory outputs) { + if (inputs_.length != 2) { + revert BadInputs(2, inputs_.length); + } + return + inputs_ + .asStackPointerAfter() + .applyFn(OpChainlinkOraclePrice.f) + .peek() + .arrayFrom(); + } +} diff --git a/utils/interpreter/ops/allStandardOps.ts b/utils/interpreter/ops/allStandardOps.ts index e7aabb649..2bc291441 100644 --- a/utils/interpreter/ops/allStandardOps.ts +++ b/utils/interpreter/ops/allStandardOps.ts @@ -1,4 +1,5 @@ export enum AllStandardOps { + CHAINLINK_PRICE, CALL, CONTEXT, CONTEXT_ROW, From 3b17f313c02bf247667513edddc5f63693fa2ec9 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 26 Jan 2023 21:32:51 +0400 Subject: [PATCH 04/23] add docs --- .../interpreter/extern/IInterpreterExternV1.sol | 13 +++++++++++++ .../interpreter/shared/RainterpreterExtern.sol | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/contracts/interpreter/extern/IInterpreterExternV1.sol b/contracts/interpreter/extern/IInterpreterExternV1.sol index f4181e283..225fb2dc6 100644 --- a/contracts/interpreter/extern/IInterpreterExternV1.sol +++ b/contracts/interpreter/extern/IInterpreterExternV1.sol @@ -4,7 +4,20 @@ pragma solidity ^0.8.15; type EncodedExternDispatch is uint256; type ExternDispatch is uint256; +/// @title IInterpreterExternV1 +/// Handle a single dispatch from some calling contract with an array of +/// inputs and array of outputs. Ostensibly useful to build "word packs" for +/// `IInterpreterV1` so that less frequently used words can be provided in +/// a less efficient format, but without bloating the base interpreter in +/// terms of code size. Effectively allows unlimited words to exist as externs +/// alongside interpreters. interface IInterpreterExternV1 { + + /// Handles a single dispatch. + /// @param dispatch_ Encoded information about the extern to dispatch. + /// Analogous to the opcode/operand in the interpreter. + /// @param inputs_ The array of inputs for the dispatched logic. + /// @return outputs_ The result of the dispatched logic. function extern( ExternDispatch dispatch_, uint256[] calldata inputs_ diff --git a/contracts/interpreter/shared/RainterpreterExtern.sol b/contracts/interpreter/shared/RainterpreterExtern.sol index ea9b224ea..e475a8f6e 100644 --- a/contracts/interpreter/shared/RainterpreterExtern.sol +++ b/contracts/interpreter/shared/RainterpreterExtern.sol @@ -6,13 +6,18 @@ import "../ops/chainlink/OpChainlinkOraclePrice.sol"; import "../run/LibStackPointer.sol"; import "../../array/LibUint256Array.sol"; +/// Thrown when the inputs don't match the expected inputs. error BadInputs(uint256 expected, uint256 actual); +/// EXPERIMENTAL implementation of `IInterpreterExternV1`. +/// Currently only implements the Chainlink oracle price opcode as a starting +/// point to test and flesh out externs generally. contract RainterpreterExtern is IInterpreterExternV1 { using LibStackPointer for uint256[]; using LibStackPointer for StackPointer; using LibUint256Array for uint256; + /// @inheritdoc IInterpreterExternV1 function extern( ExternDispatch, uint256[] memory inputs_ From c1ac63cecb3230411d5d0331356642665bebf691 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Thu, 26 Jan 2023 21:34:35 +0400 Subject: [PATCH 05/23] docs --- contracts/interpreter/shared/RainterpreterExtern.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contracts/interpreter/shared/RainterpreterExtern.sol b/contracts/interpreter/shared/RainterpreterExtern.sol index e475a8f6e..c51797cf7 100644 --- a/contracts/interpreter/shared/RainterpreterExtern.sol +++ b/contracts/interpreter/shared/RainterpreterExtern.sol @@ -12,6 +12,8 @@ error BadInputs(uint256 expected, uint256 actual); /// EXPERIMENTAL implementation of `IInterpreterExternV1`. /// Currently only implements the Chainlink oracle price opcode as a starting /// point to test and flesh out externs generally. +/// Hopefully one day the idea of there being only a single extern contract seems +/// quaint. contract RainterpreterExtern is IInterpreterExternV1 { using LibStackPointer for uint256[]; using LibStackPointer for StackPointer; From 4b118013cb80e02d5cf79b24f6959599bc355579 Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Fri, 27 Jan 2023 12:48:12 +0530 Subject: [PATCH 06/23] extern test --- .../extern/IInterpreterExternV1.sol | 2 +- .../Ops/Chainlink/chainlinkPrice.ts | 33 +++++++++++++++++-- .../shared/rainterpreter/deploy.ts | 8 +++-- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/contracts/interpreter/extern/IInterpreterExternV1.sol b/contracts/interpreter/extern/IInterpreterExternV1.sol index 225fb2dc6..1add10d95 100644 --- a/contracts/interpreter/extern/IInterpreterExternV1.sol +++ b/contracts/interpreter/extern/IInterpreterExternV1.sol @@ -21,5 +21,5 @@ interface IInterpreterExternV1 { function extern( ExternDispatch dispatch_, uint256[] calldata inputs_ - ) external view returns (uint256[] calldata outputs); + ) external view returns (uint256[] calldata outputs_); } diff --git a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts index 6cd750a87..627cc2ce0 100644 --- a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts +++ b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts @@ -1,4 +1,4 @@ -import type { AggregatorV3Interface } from "../../../../typechain"; +import type { AggregatorV3Interface, RainterpreterExtern } from "../../../../typechain"; import { AllStandardOps, assertError, @@ -14,14 +14,17 @@ import { FakeContract, smock } from "@defi-wonderland/smock"; import { concat } from "ethers/lib/utils"; import { assert } from "chai"; import { iinterpreterV1ConsumerDeploy } from "../../../../utils/deploy/test/iinterpreterV1Consumer/deploy"; +import { rainterpreterExtern } from "../../../../utils/deploy/interpreter/shared/rainterpreter/deploy"; const Opcode = AllStandardOps; describe("CHAINLINK_PRICE Opcode tests", async function () { - let fakeChainlinkOracle: FakeContract; + let rainInterpreterExtern: RainterpreterExtern; + let fakeChainlinkOracle: FakeContract; beforeEach(async () => { - fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); + fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); + rainInterpreterExtern = await rainterpreterExtern() }); it("should revert if price is stale", async () => { @@ -187,5 +190,29 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { const price_ = await consumerLogic.stackTop(); assert(price_.eq(123 + eighteenZeros)); + }); + + it.only("extern test", async () => { + const chainlinkPriceData = { + roundId: 1, + answer: 123 + eighteenZeros, + startedAt: 2, + updatedAt: 3, + answeredInRound: 4, + }; + + fakeChainlinkOracle.latestRoundData.returns(chainlinkPriceData); + fakeChainlinkOracle.decimals.returns(18); + + const feed = fakeChainlinkOracle.address; + const staleAfter = (await getBlockTimestamp()) + 10000; + + const inputs = [feed,staleAfter] + + let priceData = await rainInterpreterExtern.extern(0,inputs) + + assert(priceData) + + }); }); diff --git a/utils/deploy/interpreter/shared/rainterpreter/deploy.ts b/utils/deploy/interpreter/shared/rainterpreter/deploy.ts index ecd18610b..177be0193 100644 --- a/utils/deploy/interpreter/shared/rainterpreter/deploy.ts +++ b/utils/deploy/interpreter/shared/rainterpreter/deploy.ts @@ -1,4 +1,4 @@ -import { Rainterpreter, RainterpreterStore } from "../../../../../typechain"; +import { Rainterpreter, RainterpreterStore , RainterpreterExtern } from "../../../../../typechain"; import { basicDeploy } from "../../../basicDeploy"; export const rainterpreterDeploy = async () => { @@ -16,4 +16,8 @@ export const rainterpreterDeploy = async () => { export const rainterpreterStoreDeploy = async () => { return (await basicDeploy("RainterpreterStore", {})) as RainterpreterStore; -}; +}; + +export const rainterpreterExtern = async () => { + return (await basicDeploy("RainterpreterExtern", {})) as RainterpreterExtern +} \ No newline at end of file From d7ac2063a741bc1823100672ae8329c900202884 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Fri, 27 Jan 2023 11:49:31 +0400 Subject: [PATCH 07/23] fix extern tests --- .../shared/RainterpreterExtern.sol | 11 +++++---- .../Ops/Chainlink/chainlinkPrice.ts | 24 +++++++++---------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/contracts/interpreter/shared/RainterpreterExtern.sol b/contracts/interpreter/shared/RainterpreterExtern.sol index c51797cf7..0b992bc11 100644 --- a/contracts/interpreter/shared/RainterpreterExtern.sol +++ b/contracts/interpreter/shared/RainterpreterExtern.sol @@ -6,6 +6,8 @@ import "../ops/chainlink/OpChainlinkOraclePrice.sol"; import "../run/LibStackPointer.sol"; import "../../array/LibUint256Array.sol"; +import "hardhat/console.sol"; + /// Thrown when the inputs don't match the expected inputs. error BadInputs(uint256 expected, uint256 actual); @@ -23,15 +25,14 @@ contract RainterpreterExtern is IInterpreterExternV1 { function extern( ExternDispatch, uint256[] memory inputs_ - ) external view returns (uint256[] memory outputs) { + ) external view returns (uint256[] memory) { if (inputs_.length != 2) { revert BadInputs(2, inputs_.length); } - return - inputs_ + uint256[] memory result_ = inputs_ .asStackPointerAfter() .applyFn(OpChainlinkOraclePrice.f) - .peek() - .arrayFrom(); + .peek().arrayFrom(); + return result_; } } diff --git a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts index 627cc2ce0..1104e4a8b 100644 --- a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts +++ b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts @@ -20,10 +20,10 @@ const Opcode = AllStandardOps; describe("CHAINLINK_PRICE Opcode tests", async function () { let rainInterpreterExtern: RainterpreterExtern; - let fakeChainlinkOracle: FakeContract; + let fakeChainlinkOracle: FakeContract; beforeEach(async () => { - fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); + fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); rainInterpreterExtern = await rainterpreterExtern() }); @@ -190,14 +190,14 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { const price_ = await consumerLogic.stackTop(); assert(price_.eq(123 + eighteenZeros)); - }); + }); it.only("extern test", async () => { const chainlinkPriceData = { - roundId: 1, + roundId: 4, answer: 123 + eighteenZeros, - startedAt: 2, - updatedAt: 3, + startedAt: await getBlockTimestamp(), + updatedAt: await getBlockTimestamp(), answeredInRound: 4, }; @@ -205,14 +205,14 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { fakeChainlinkOracle.decimals.returns(18); const feed = fakeChainlinkOracle.address; - const staleAfter = (await getBlockTimestamp()) + 10000; + const staleAfter = 10000; + + const inputs = [feed,staleAfter] - const inputs = [feed,staleAfter] + let priceData = await rainInterpreterExtern.extern(0,inputs) + console.log(priceData) + // assert(priceData) - let priceData = await rainInterpreterExtern.extern(0,inputs) - assert(priceData) - - }); }); From 3c1ad40bc0cc686262bdbfa0f834d1777d674654 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Sun, 29 Jan 2023 15:03:18 +0400 Subject: [PATCH 08/23] workaround for memory issues --- contracts/interpreter/run/LibStackPointer.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/interpreter/run/LibStackPointer.sol b/contracts/interpreter/run/LibStackPointer.sol index b1596b009..90fdb2c05 100644 --- a/contracts/interpreter/run/LibStackPointer.sol +++ b/contracts/interpreter/run/LibStackPointer.sol @@ -476,9 +476,9 @@ library LibStackPointer { a_ := mload(location_) b_ := mload(stackTop_) } - a_ = fn_(a_, b_); + uint256 c_ = fn_(a_, b_); assembly ("memory-safe") { - mstore(location_, a_) + mstore(location_, c_) } return stackTop_; } From aec0c471f35878ed1740b68ba76d63208c4efa88 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Sun, 29 Jan 2023 15:18:52 +0400 Subject: [PATCH 09/23] debug --- contracts/interpreter/extern/IInterpreterExternV1.sol | 4 ++-- contracts/interpreter/run/LibStackPointer.sol | 4 ++-- contracts/interpreter/shared/RainterpreterExtern.sol | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/contracts/interpreter/extern/IInterpreterExternV1.sol b/contracts/interpreter/extern/IInterpreterExternV1.sol index 1add10d95..da2322770 100644 --- a/contracts/interpreter/extern/IInterpreterExternV1.sol +++ b/contracts/interpreter/extern/IInterpreterExternV1.sol @@ -20,6 +20,6 @@ interface IInterpreterExternV1 { /// @return outputs_ The result of the dispatched logic. function extern( ExternDispatch dispatch_, - uint256[] calldata inputs_ - ) external view returns (uint256[] calldata outputs_); + uint256[] memory inputs_ + ) external view returns (uint256[] memory outputs_); } diff --git a/contracts/interpreter/run/LibStackPointer.sol b/contracts/interpreter/run/LibStackPointer.sol index 90fdb2c05..b1596b009 100644 --- a/contracts/interpreter/run/LibStackPointer.sol +++ b/contracts/interpreter/run/LibStackPointer.sol @@ -476,9 +476,9 @@ library LibStackPointer { a_ := mload(location_) b_ := mload(stackTop_) } - uint256 c_ = fn_(a_, b_); + a_ = fn_(a_, b_); assembly ("memory-safe") { - mstore(location_, c_) + mstore(location_, a_) } return stackTop_; } diff --git a/contracts/interpreter/shared/RainterpreterExtern.sol b/contracts/interpreter/shared/RainterpreterExtern.sol index a22e29ea0..7433c4b10 100644 --- a/contracts/interpreter/shared/RainterpreterExtern.sol +++ b/contracts/interpreter/shared/RainterpreterExtern.sol @@ -27,10 +27,9 @@ contract RainterpreterExtern is IInterpreterExternV1 { if (inputs_.length != 2) { revert BadInputs(2, inputs_.length); } - uint256[] memory result_ = inputs_ + return inputs_ .asStackPointerAfter() .applyFn(OpChainlinkOraclePrice.f) .peek().arrayFrom(); - return result_; } } From 5b66bf82754a95fda10977114d1f836d4c525a8e Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Sun, 29 Jan 2023 15:24:01 +0400 Subject: [PATCH 10/23] debug --- contracts/array/LibUint256Array.sol | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/contracts/array/LibUint256Array.sol b/contracts/array/LibUint256Array.sol index 3d6aa5bbc..f88340258 100644 --- a/contracts/array/LibUint256Array.sol +++ b/contracts/array/LibUint256Array.sol @@ -18,9 +18,10 @@ library LibUint256Array { /// @return the newly allocated array including a_ as a single item. function arrayFrom(uint256 a_) internal pure returns (uint256[] memory) { uint256[] memory array_ = new uint256[](1); - assembly ("memory-safe") { - mstore(add(array_, 0x20), a_) - } + array_[0] = a_; + // assembly ("memory-safe") { + // mstore(add(array_, 0x20), a_) + // } return array_; } From 39f7b79810d913315a5b768bcc69d856e29260b6 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Sun, 29 Jan 2023 15:36:17 +0400 Subject: [PATCH 11/23] debug --- .../extern/IInterpreterExternV1.sol | 1 - .../shared/RainterpreterExtern.sol | 11 ++- scripts/genOpmeta.ts | 82 ++++++++++++------- scripts/genRainterpreterOpmeta.ts | 19 ++--- .../Ops/Chainlink/chainlinkPrice.ts | 21 +++-- .../shared/rainterpreter/deploy.ts | 4 +- 6 files changed, 81 insertions(+), 57 deletions(-) diff --git a/contracts/interpreter/extern/IInterpreterExternV1.sol b/contracts/interpreter/extern/IInterpreterExternV1.sol index da2322770..69957842a 100644 --- a/contracts/interpreter/extern/IInterpreterExternV1.sol +++ b/contracts/interpreter/extern/IInterpreterExternV1.sol @@ -12,7 +12,6 @@ type ExternDispatch is uint256; /// terms of code size. Effectively allows unlimited words to exist as externs /// alongside interpreters. interface IInterpreterExternV1 { - /// Handles a single dispatch. /// @param dispatch_ Encoded information about the extern to dispatch. /// Analogous to the opcode/operand in the interpreter. diff --git a/contracts/interpreter/shared/RainterpreterExtern.sol b/contracts/interpreter/shared/RainterpreterExtern.sol index 7433c4b10..b930c82c8 100644 --- a/contracts/interpreter/shared/RainterpreterExtern.sol +++ b/contracts/interpreter/shared/RainterpreterExtern.sol @@ -6,6 +6,8 @@ import "../ops/chainlink/OpChainlinkOraclePrice.sol"; import "../run/LibStackPointer.sol"; import "../../array/LibUint256Array.sol"; +import "hardhat/console.sol"; + /// Thrown when the inputs don't match the expected inputs. error BadInputs(uint256 expected, uint256 actual); @@ -24,12 +26,15 @@ contract RainterpreterExtern is IInterpreterExternV1 { ExternDispatch, uint256[] memory inputs_ ) external view returns (uint256[] memory) { + console.log(block.timestamp); if (inputs_.length != 2) { revert BadInputs(2, inputs_.length); } - return inputs_ - .asStackPointerAfter() + StackPointer stackPointer_ = inputs_.asStackPointerAfter(); + return + stackPointer_ .applyFn(OpChainlinkOraclePrice.f) - .peek().arrayFrom(); + .peek() + .arrayFrom(); } } diff --git a/scripts/genOpmeta.ts b/scripts/genOpmeta.ts index e77caa781..fbeb3626e 100755 --- a/scripts/genOpmeta.ts +++ b/scripts/genOpmeta.ts @@ -28,7 +28,12 @@ const main = async () => { const root = path.resolve(); const args = argv.slice(2); - if (!args.length || args.includes("--help") || args.includes("-h") || args.includes("-H")){ + if ( + !args.length || + args.includes("--help") || + args.includes("-h") || + args.includes("-H") + ) { console.log( ` usage: @@ -54,30 +59,35 @@ const main = async () => { - relative path must start with letters or 1 or 2 dots ".", example: relative/path ./relative/path ../../relative/path - absolute path must start with slash "/", example: /absolute/path ` - ) - } - else { + ); + } else { let dir = root; let schemaPath = ""; if (args.includes("--dest") || args.includes("-d") || args.includes("-D")) { - const _i = args.indexOf("--dest") > -1 - ? args.indexOf("--dest") - : args.indexOf("-d") > -1 - ? args.indexOf("-d") - : args.indexOf("-D") + const _i = + args.indexOf("--dest") > -1 + ? args.indexOf("--dest") + : args.indexOf("-d") > -1 + ? args.indexOf("-d") + : args.indexOf("-D"); const _tmp = args.splice(_i, _i + 2); - if (_tmp.length != 2) throw new Error("expected destination path") + if (_tmp.length != 2) throw new Error("expected destination path"); dir = path.resolve(root, _tmp[1]); } - if (args.includes("--schema") || args.includes("-s") || args.includes("-S")) { - const _i = args.indexOf("--schema") > -1 - ? args.indexOf("--schema") - : args.indexOf("-s") > -1 - ? args.indexOf("-s") - : args.indexOf("-S") + if ( + args.includes("--schema") || + args.includes("-s") || + args.includes("-S") + ) { + const _i = + args.indexOf("--schema") > -1 + ? args.indexOf("--schema") + : args.indexOf("-s") > -1 + ? args.indexOf("-s") + : args.indexOf("-S"); const _tmp = args.splice(_i, _i + 2); - if (_tmp.length != 2) throw new Error("expected path to the schema") + if (_tmp.length != 2) throw new Error("expected path to the schema"); if (_tmp[1].endsWith(".json")) { schemaPath = path.resolve(root, _tmp[1]); } else throw new Error("invalid schema, must be a valid json"); @@ -89,25 +99,33 @@ const main = async () => { const validate = new Ajv().compile(schema); const opmetas = []; - if (args.includes("--opmeta") || args.includes("-o") || args.includes("-O")) { - const _i = args.indexOf("--opmeta") > -1 - ? args.indexOf("--opmeta") - : args.indexOf("-o") > -1 - ? args.indexOf("-o") - : args.indexOf("-O") + if ( + args.includes("--opmeta") || + args.includes("-o") || + args.includes("-O") + ) { + const _i = + args.indexOf("--opmeta") > -1 + ? args.indexOf("--opmeta") + : args.indexOf("-o") > -1 + ? args.indexOf("-o") + : args.indexOf("-O"); const _tmp = args.splice(_i + 1); - if (!_tmp.length) throw new Error("expected path to opmeta files") + if (!_tmp.length) throw new Error("expected path to opmeta files"); for (let i = 0; i < _tmp.length; i++) { if (_tmp[i].endsWith(".json")) { const tmp = JSON.parse(readFile(path.resolve(root, _tmp[i]))); if (validate(tmp)) opmetas.push(tmp); else throw new Error(`invalid opmeta content at index ${i}`); - } else throw new Error(`invalid opmeta at index ${i}, must be a valid json`); + } else + throw new Error(`invalid opmeta at index ${i}, must be a valid json`); } let opmetaHexString = "0x"; const opmetaBytes = Uint8Array.from( - deflateSync(format(JSON.stringify(opmetas, null, 4), { parser: "json" })) + deflateSync( + format(JSON.stringify(opmetas, null, 4), { parser: "json" }) + ) ); for (let i = 0; i < opmetaBytes.length; i++) { opmetaHexString = @@ -128,17 +146,19 @@ const main = async () => { deployableOpmetaBytes: opmetaHexString, deployableSchemaBytes: schemaHexString, }; - const fileData = format(JSON.stringify(data, null, 4), { parser: "json" }); + const fileData = format(JSON.stringify(data, null, 4), { + parser: "json", + }); if (!dir.endsWith(".json")) dir = dir + "/Opmeta.json"; writeFile(dir, fileData); - } - else console.log( - ` + } else + console.log( + ` Expected Opmeta Files! ` - ) + ); } }; diff --git a/scripts/genRainterpreterOpmeta.ts b/scripts/genRainterpreterOpmeta.ts index 7896348aa..ee694f989 100755 --- a/scripts/genRainterpreterOpmeta.ts +++ b/scripts/genRainterpreterOpmeta.ts @@ -20,7 +20,7 @@ const main = async () => { const root = path.resolve(); let dir = root; const args = argv.slice(2); - if (args.includes("--help") || args.includes("-h") || args.includes("-H")){ + if (args.includes("--help") || args.includes("-h") || args.includes("-H")) { console.log( ` usage: @@ -28,14 +28,12 @@ const main = async () => { ** Writes to root of the current workiing directory if no destination path provided. ` - ) - } - else { + ); + } else { let opmetaHexString = "0x"; const opmetaBytes = Uint8Array.from( - deflateSync(format( - JSON.stringify(rainterpreterOpmeta, null, 4), - {parser: "json"}) + deflateSync( + format(JSON.stringify(rainterpreterOpmeta, null, 4), { parser: "json" }) ) ); for (let i = 0; i < opmetaBytes.length; i++) { @@ -45,10 +43,9 @@ const main = async () => { let schemaHexString = "0x"; const schemaBytes = Uint8Array.from( - deflateSync(format( - JSON.stringify(OpmetaSchema, null, 4), - {parser: "json"} - )) + deflateSync( + format(JSON.stringify(OpmetaSchema, null, 4), { parser: "json" }) + ) ); for (let i = 0; i < schemaBytes.length; i++) { schemaHexString = diff --git a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts index 1104e4a8b..93bd495dc 100644 --- a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts +++ b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts @@ -1,4 +1,7 @@ -import type { AggregatorV3Interface, RainterpreterExtern } from "../../../../typechain"; +import type { + AggregatorV3Interface, + RainterpreterExtern, +} from "../../../../typechain"; import { AllStandardOps, assertError, @@ -24,7 +27,7 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { beforeEach(async () => { fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); - rainInterpreterExtern = await rainterpreterExtern() + rainInterpreterExtern = await rainterpreterExtern(); }); it("should revert if price is stale", async () => { @@ -193,11 +196,13 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { }); it.only("extern test", async () => { + const timestamp =await getBlockTimestamp(); + console.log(timestamp) const chainlinkPriceData = { roundId: 4, answer: 123 + eighteenZeros, - startedAt: await getBlockTimestamp(), - updatedAt: await getBlockTimestamp(), + startedAt: timestamp, + updatedAt: timestamp, answeredInRound: 4, }; @@ -207,12 +212,10 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { const feed = fakeChainlinkOracle.address; const staleAfter = 10000; - const inputs = [feed,staleAfter] + const inputs = [feed, staleAfter]; - let priceData = await rainInterpreterExtern.extern(0,inputs) - console.log(priceData) + const priceData = await rainInterpreterExtern.extern(0, inputs); + console.log(priceData); // assert(priceData) - - }); }); diff --git a/utils/deploy/interpreter/shared/rainterpreter/deploy.ts b/utils/deploy/interpreter/shared/rainterpreter/deploy.ts index 854bc9e95..1898f2294 100644 --- a/utils/deploy/interpreter/shared/rainterpreter/deploy.ts +++ b/utils/deploy/interpreter/shared/rainterpreter/deploy.ts @@ -20,5 +20,5 @@ export const rainterpreterStoreDeploy = async () => { }; export const rainterpreterExtern = async () => { - return (await basicDeploy("RainterpreterExtern", {})) as RainterpreterExtern -} \ No newline at end of file + return (await basicDeploy("RainterpreterExtern", {})) as RainterpreterExtern; +}; From b2b3c6b9b514fafe008d5ffc592e509f7ab3446d Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Sun, 29 Jan 2023 16:05:54 +0400 Subject: [PATCH 12/23] debug --- contracts/chainlink/LibChainlink.sol | 12 ++++++- .../shared/RainterpreterExtern.sol | 10 +++--- .../Ops/Chainlink/chainlinkPrice.ts | 31 ++++++++++++------- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/contracts/chainlink/LibChainlink.sol b/contracts/chainlink/LibChainlink.sol index 3cca4884b..d3030acb2 100644 --- a/contracts/chainlink/LibChainlink.sol +++ b/contracts/chainlink/LibChainlink.sol @@ -4,6 +4,8 @@ pragma solidity ^0.8.15; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import "../math/LibFixedPointMath.sol"; +import "hardhat/console.sol"; + /// Thrown if a price is zero or negative as this is probably not anticipated or /// useful for most users of a price feed. Of course there are use cases where /// zero or negative _oracle values_ in general are useful, such as negative @@ -32,11 +34,15 @@ library LibChainlink { address feed_, uint256 staleAfter_ ) internal view returns (uint256) { + console.log("aa", feed_); (, int256 answer_, , uint256 updatedAt_, ) = AggregatorV3Interface( feed_ ).latestRoundData(); + console.log("zz"); + if (answer_ <= 0) { + console.log("not pos"); revert NotPosIntPrice(answer_); } @@ -44,14 +50,18 @@ library LibChainlink { // would overflow, and no stale prices. // solhint-disable-next-line not-rely-on-time if (block.timestamp - updatedAt_ > staleAfter_) { + console.log("stale"); revert StalePrice(updatedAt_, staleAfter_); } // Safely cast the answer to uint256 and scale it to 18 decimal FP. + // We round up because reporting a non-zero price as zero can cause + // issues downstream. This rounding up only happens if the values are + // being scaled down. return answer_.toUint256().scale18( AggregatorV3Interface(feed_).decimals(), - Math.Rounding.Down + Math.Rounding.Up ); } } diff --git a/contracts/interpreter/shared/RainterpreterExtern.sol b/contracts/interpreter/shared/RainterpreterExtern.sol index b930c82c8..74de58ab3 100644 --- a/contracts/interpreter/shared/RainterpreterExtern.sol +++ b/contracts/interpreter/shared/RainterpreterExtern.sol @@ -31,10 +31,12 @@ contract RainterpreterExtern is IInterpreterExternV1 { revert BadInputs(2, inputs_.length); } StackPointer stackPointer_ = inputs_.asStackPointerAfter(); - return - stackPointer_ + uint256 result_ = stackPointer_ .applyFn(OpChainlinkOraclePrice.f) - .peek() - .arrayFrom(); + .peek(); + console.log("r", result_); + return + + result_.arrayFrom(); } } diff --git a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts index 93bd495dc..520909e28 100644 --- a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts +++ b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts @@ -26,7 +26,7 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { let fakeChainlinkOracle: FakeContract; beforeEach(async () => { - fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); + // fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); rainInterpreterExtern = await rainterpreterExtern(); }); @@ -196,20 +196,27 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { }); it.only("extern test", async () => { - const timestamp =await getBlockTimestamp(); + const fakeChainlinkOracle2 = await smock.fake("AggregatorV3Interface"); + + const timestamp = (await getBlockTimestamp()) - 1; console.log(timestamp) - const chainlinkPriceData = { - roundId: 4, - answer: 123 + eighteenZeros, - startedAt: timestamp, - updatedAt: timestamp, - answeredInRound: 4, - }; + const chainlinkPriceData2 = [ + // roundID + 4, + // answer + "123" + eighteenZeros, + // startedAt + timestamp, + // updatedAt + timestamp, + // answeredInRound + 4, + ]; - fakeChainlinkOracle.latestRoundData.returns(chainlinkPriceData); - fakeChainlinkOracle.decimals.returns(18); + fakeChainlinkOracle2.latestRoundData.returns(chainlinkPriceData2); + fakeChainlinkOracle2.decimals.returns(18); - const feed = fakeChainlinkOracle.address; + const feed = fakeChainlinkOracle2.address; const staleAfter = 10000; const inputs = [feed, staleAfter]; From 9c1163d131b0c3ae42bce6ac0b957f5036e05920 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Sun, 29 Jan 2023 16:08:25 +0400 Subject: [PATCH 13/23] lint --- contracts/array/LibUint256Array.sol | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/contracts/array/LibUint256Array.sol b/contracts/array/LibUint256Array.sol index f88340258..3d6aa5bbc 100644 --- a/contracts/array/LibUint256Array.sol +++ b/contracts/array/LibUint256Array.sol @@ -18,10 +18,9 @@ library LibUint256Array { /// @return the newly allocated array including a_ as a single item. function arrayFrom(uint256 a_) internal pure returns (uint256[] memory) { uint256[] memory array_ = new uint256[](1); - array_[0] = a_; - // assembly ("memory-safe") { - // mstore(add(array_, 0x20), a_) - // } + assembly ("memory-safe") { + mstore(add(array_, 0x20), a_) + } return array_; } From 5483dd1b43db621c1a6ebdf1eee3e03e8faacc1f Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Sun, 29 Jan 2023 16:09:02 +0400 Subject: [PATCH 14/23] lint --- contracts/chainlink/LibChainlink.sol | 7 ------- 1 file changed, 7 deletions(-) diff --git a/contracts/chainlink/LibChainlink.sol b/contracts/chainlink/LibChainlink.sol index d3030acb2..03f1c9bce 100644 --- a/contracts/chainlink/LibChainlink.sol +++ b/contracts/chainlink/LibChainlink.sol @@ -4,8 +4,6 @@ pragma solidity ^0.8.15; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import "../math/LibFixedPointMath.sol"; -import "hardhat/console.sol"; - /// Thrown if a price is zero or negative as this is probably not anticipated or /// useful for most users of a price feed. Of course there are use cases where /// zero or negative _oracle values_ in general are useful, such as negative @@ -34,15 +32,11 @@ library LibChainlink { address feed_, uint256 staleAfter_ ) internal view returns (uint256) { - console.log("aa", feed_); (, int256 answer_, , uint256 updatedAt_, ) = AggregatorV3Interface( feed_ ).latestRoundData(); - console.log("zz"); - if (answer_ <= 0) { - console.log("not pos"); revert NotPosIntPrice(answer_); } @@ -50,7 +44,6 @@ library LibChainlink { // would overflow, and no stale prices. // solhint-disable-next-line not-rely-on-time if (block.timestamp - updatedAt_ > staleAfter_) { - console.log("stale"); revert StalePrice(updatedAt_, staleAfter_); } From ec56b4b740ae4548d5f5876be351cc258515b2a7 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Sun, 29 Jan 2023 16:12:33 +0400 Subject: [PATCH 15/23] lint --- contracts/interpreter/shared/RainterpreterExtern.sol | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/contracts/interpreter/shared/RainterpreterExtern.sol b/contracts/interpreter/shared/RainterpreterExtern.sol index 74de58ab3..8015fd3bd 100644 --- a/contracts/interpreter/shared/RainterpreterExtern.sol +++ b/contracts/interpreter/shared/RainterpreterExtern.sol @@ -6,8 +6,6 @@ import "../ops/chainlink/OpChainlinkOraclePrice.sol"; import "../run/LibStackPointer.sol"; import "../../array/LibUint256Array.sol"; -import "hardhat/console.sol"; - /// Thrown when the inputs don't match the expected inputs. error BadInputs(uint256 expected, uint256 actual); @@ -26,17 +24,11 @@ contract RainterpreterExtern is IInterpreterExternV1 { ExternDispatch, uint256[] memory inputs_ ) external view returns (uint256[] memory) { - console.log(block.timestamp); if (inputs_.length != 2) { revert BadInputs(2, inputs_.length); } - StackPointer stackPointer_ = inputs_.asStackPointerAfter(); - uint256 result_ = stackPointer_ + return inputs_.asStackPointerAfter() .applyFn(OpChainlinkOraclePrice.f) - .peek(); - console.log("r", result_); - return - - result_.arrayFrom(); + .peek().arrayFrom(); } } From cc8ddb1ffd2c9580c63fa8f9452e2c5661f5af21 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Sun, 29 Jan 2023 16:13:52 +0400 Subject: [PATCH 16/23] lint --- .../shared/RainterpreterExtern.sol | 7 ++++-- .../Ops/Chainlink/chainlinkPrice.ts | 22 +++++++------------ 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/contracts/interpreter/shared/RainterpreterExtern.sol b/contracts/interpreter/shared/RainterpreterExtern.sol index 8015fd3bd..96b8dcfa8 100644 --- a/contracts/interpreter/shared/RainterpreterExtern.sol +++ b/contracts/interpreter/shared/RainterpreterExtern.sol @@ -27,8 +27,11 @@ contract RainterpreterExtern is IInterpreterExternV1 { if (inputs_.length != 2) { revert BadInputs(2, inputs_.length); } - return inputs_.asStackPointerAfter() + return + inputs_ + .asStackPointerAfter() .applyFn(OpChainlinkOraclePrice.f) - .peek().arrayFrom(); + .peek() + .arrayFrom(); } } diff --git a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts index 520909e28..c1207d503 100644 --- a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts +++ b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts @@ -199,21 +199,15 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { const fakeChainlinkOracle2 = await smock.fake("AggregatorV3Interface"); const timestamp = (await getBlockTimestamp()) - 1; - console.log(timestamp) - const chainlinkPriceData2 = [ - // roundID - 4, - // answer - "123" + eighteenZeros, - // startedAt - timestamp, - // updatedAt - timestamp, - // answeredInRound - 4, - ]; + const chainlinkPriceData = { + roundId: 4, + answer: "123" + eighteenZeros, + startedAt: timestamp, + updatedAt: timestamp, + answeredInRound: 4, + }; - fakeChainlinkOracle2.latestRoundData.returns(chainlinkPriceData2); + fakeChainlinkOracle2.latestRoundData.returns(chainlinkPriceData); fakeChainlinkOracle2.decimals.returns(18); const feed = fakeChainlinkOracle2.address; From 8bc503d33d9eda87107445dcf49f27bfc4c3b0e2 Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Mon, 30 Jan 2023 12:02:28 +0530 Subject: [PATCH 17/23] test extern operand --- .../interpreter/shared/Rainterpreter.sol | 2 +- .../RainterpreterExpressionDeployer.sol | 2 +- .../Ops/Chainlink/chainlinkPrice.ts | 74 +++++++++++++++++-- utils/interpreter/interpreter.ts | 11 ++- 4 files changed, 80 insertions(+), 9 deletions(-) diff --git a/contracts/interpreter/shared/Rainterpreter.sol b/contracts/interpreter/shared/Rainterpreter.sol index 4a383ea70..a647c5899 100644 --- a/contracts/interpreter/shared/Rainterpreter.sol +++ b/contracts/interpreter/shared/Rainterpreter.sol @@ -17,7 +17,7 @@ error UnexpectedOpMetaHash(bytes32 actualOpMeta); /// @dev Hash of the known store bytecode. bytes32 constant STORE_BYTECODE_HASH = bytes32( - 0x99d0f2d5f5d71a2d4a8db29d920f86fdb4e7688d01799f6cfe9242fb314f3354 + 0x64046a25ba76e42f96b36ef7546e0deaeae1fb84740da14f111591802fcdb3ad ); /// @dev Hash of the known op meta. diff --git a/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol b/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol index f3c867cb1..1d5105a04 100644 --- a/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol +++ b/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol @@ -21,7 +21,7 @@ error MissingEntrypoint(uint256 expectedEntrypoints, uint256 actualEntrypoints); /// immutable for any given interpreter so once the expression deployer is /// constructed and has verified that this matches what the interpreter reports, /// it can use this constant value to compile and serialize expressions. -bytes constant OPCODE_FUNCTION_POINTERS = hex"0a010a0f0a650ab70b350b610bfa0cc40df90e2e0e4c0ed40ee30ef10eff0f0d0ee30f1b0f290f370f460f550f630f710fe90ff81007101610251034107d108f109d10cf10dd10eb10f9110811171126113511441153116211711180118f119e11ac11ba11c811d611e411f21200120f121e122c12a3"; +bytes constant OPCODE_FUNCTION_POINTERS = hex"0a0a0a180a6e0ac00b3e0b6a0c030d8c0e560f8b0fc00fde1066107510831091109f107510ad10bb10c910d810e710f51103117b118a119911a811b711c6120f1221122f1261126f127d128b129a12a912b812c712d612e512f41303131213211330133e134c135a136813761384139213a113b013be1430"; /// @title RainterpreterExpressionDeployer /// @notice Minimal binding of the `IExpressionDeployerV1` interface to the diff --git a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts index c1207d503..d89a8a449 100644 --- a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts +++ b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts @@ -1,11 +1,14 @@ import type { AggregatorV3Interface, RainterpreterExtern, + Rainterpreter, + IInterpreterV1Consumer } from "../../../../typechain"; import { AllStandardOps, assertError, eighteenZeros, + externOprand, getBlockTimestamp, memoryOperand, MemoryType, @@ -16,18 +19,30 @@ import { import { FakeContract, smock } from "@defi-wonderland/smock"; import { concat } from "ethers/lib/utils"; import { assert } from "chai"; -import { iinterpreterV1ConsumerDeploy } from "../../../../utils/deploy/test/iinterpreterV1Consumer/deploy"; -import { rainterpreterExtern } from "../../../../utils/deploy/interpreter/shared/rainterpreter/deploy"; +import { expressionConsumerDeploy, iinterpreterV1ConsumerDeploy } from "../../../../utils/deploy/test/iinterpreterV1Consumer/deploy"; +import { rainterpreterDeploy, rainterpreterExtern } from "../../../../utils/deploy/interpreter/shared/rainterpreter/deploy"; +import { ethers } from "hardhat"; const Opcode = AllStandardOps; describe("CHAINLINK_PRICE Opcode tests", async function () { + let rainInterpreter: Rainterpreter; + let logic: IInterpreterV1Consumer; let rainInterpreterExtern: RainterpreterExtern; let fakeChainlinkOracle: FakeContract; - beforeEach(async () => { + beforeEach(async () => { + rainInterpreter = await rainterpreterDeploy(); + // fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); - rainInterpreterExtern = await rainterpreterExtern(); + rainInterpreterExtern = await rainterpreterExtern(); + const consumerFactory = await ethers.getContractFactory( + "IInterpreterV1Consumer" + ); + logic = (await consumerFactory.deploy()) as IInterpreterV1Consumer; + await logic.deployed(); + + }); it("should revert if price is stale", async () => { @@ -195,12 +210,12 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { assert(price_.eq(123 + eighteenZeros)); }); - it.only("extern test", async () => { + it("extern test", async () => { const fakeChainlinkOracle2 = await smock.fake("AggregatorV3Interface"); const timestamp = (await getBlockTimestamp()) - 1; const chainlinkPriceData = { - roundId: 4, + roundId: 4, answer: "123" + eighteenZeros, startedAt: timestamp, updatedAt: timestamp, @@ -218,5 +233,52 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { const priceData = await rainInterpreterExtern.extern(0, inputs); console.log(priceData); // assert(priceData) + }); + + it.only("extern test 2", async () => { + const fakeChainlinkOracle2 = await smock.fake("AggregatorV3Interface"); + + const chainlinkPriceData = { + roundId: 4, + answer: 123 + eighteenZeros, + startedAt: await getBlockTimestamp(), + updatedAt: await getBlockTimestamp(), + answeredInRound: 4, + }; + + fakeChainlinkOracle2.latestRoundData.returns(chainlinkPriceData); + fakeChainlinkOracle2.decimals.returns(18); + + const feed = fakeChainlinkOracle2.address; + const staleAfter = 10000; + + const constants = [rainInterpreterExtern.address,feed,staleAfter] + + const v0 = op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 1)); + const v1 = op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 2)); + + // prettier-ignore + const source0 = concat([ + v0, + v1, + op(Opcode.EXTERN, externOprand(0, 2 ,1)), + ]); + + const expression0 = await expressionConsumerDeploy( + { + sources: [source0], + constants, + }, + rainInterpreter, + 1 + ); + await logic.eval(rainInterpreter.address, expression0.dispatch, []); + const result0 = await logic.stackTop(); + + console.log("result0 : " , result0) + }); + + + }); diff --git a/utils/interpreter/interpreter.ts b/utils/interpreter/interpreter.ts index f20ab6ef8..146aecff2 100644 --- a/utils/interpreter/interpreter.ts +++ b/utils/interpreter/interpreter.ts @@ -74,6 +74,15 @@ export function callOperand( ): number { const operand = (sourceIndex << 8) + (outputSize << 4) + inputSize; return operand; +} + +export function externOprand( + offset: number, + inputs: number, + outputs: number +): number { + const operand = (offset << 10) + (outputs << 5) + inputs; + return operand; } /** @@ -106,7 +115,7 @@ export function doWhileOperand( reserved: number, sourceIndex: number ): number { - const operand = (sourceIndex << 8) + (reserved << 4) + inputSize; + const operand = (sourceIndex << 8) + (reserved << 4) + inputSize; return operand; } From 765d508879e8fef1fe6336c2b722b58b2f11ac00 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Mon, 30 Jan 2023 12:53:56 +0400 Subject: [PATCH 18/23] fix extern --- contracts/interpreter/ops/core/OpExtern.sol | 4 +-- .../RainterpreterExpressionDeployer.sol | 2 +- .../Ops/Chainlink/chainlinkPrice.ts | 26 ++++++++++--------- utils/interpreter/interpreter.ts | 6 ++--- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/contracts/interpreter/ops/core/OpExtern.sol b/contracts/interpreter/ops/core/OpExtern.sol index c38538bf4..0c1ecfd46 100644 --- a/contracts/interpreter/ops/core/OpExtern.sol +++ b/contracts/interpreter/ops/core/OpExtern.sol @@ -57,10 +57,10 @@ library OpExtern { // Mirrors constant opcode. EncodedExternDispatch encodedDispatch_; assembly ("memory-safe") { - encodedDispatch_ := add( + encodedDispatch_ := mload(add( mload(add(interpreterState_, 0x20)), mul(0x20, offset_) - ) + )) } (interpreterExtern_, externDispatch_) = LibExtern.decode( diff --git a/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol b/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol index 1d5105a04..784a01779 100644 --- a/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol +++ b/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol @@ -21,7 +21,7 @@ error MissingEntrypoint(uint256 expectedEntrypoints, uint256 actualEntrypoints); /// immutable for any given interpreter so once the expression deployer is /// constructed and has verified that this matches what the interpreter reports, /// it can use this constant value to compile and serialize expressions. -bytes constant OPCODE_FUNCTION_POINTERS = hex"0a0a0a180a6e0ac00b3e0b6a0c030d8c0e560f8b0fc00fde1066107510831091109f107510ad10bb10c910d810e710f51103117b118a119911a811b711c6120f1221122f1261126f127d128b129a12a912b812c712d612e512f41303131213211330133e134c135a136813761384139213a113b013be1430"; +bytes constant OPCODE_FUNCTION_POINTERS = hex"0a0a0a180a6e0ac00b3e0b6a0c030d8e0e580f8d0fc20fe0106810771085109310a1107710af10bd10cb10da10e910f71105117d118c119b11aa11b911c812111223123112631271127f128d129c12ab12ba12c912d812e712f613051314132313321340134e135c136a13781386139413a313b213c01432"; /// @title RainterpreterExpressionDeployer /// @notice Minimal binding of the `IExpressionDeployerV1` interface to the diff --git a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts index d89a8a449..6d0a40646 100644 --- a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts +++ b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts @@ -8,7 +8,7 @@ import { AllStandardOps, assertError, eighteenZeros, - externOprand, + externOperand, getBlockTimestamp, memoryOperand, MemoryType, @@ -31,11 +31,11 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { let rainInterpreterExtern: RainterpreterExtern; let fakeChainlinkOracle: FakeContract; - beforeEach(async () => { + beforeEach(async () => { rainInterpreter = await rainterpreterDeploy(); // fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); - rainInterpreterExtern = await rainterpreterExtern(); + rainInterpreterExtern = await rainterpreterExtern(); const consumerFactory = await ethers.getContractFactory( "IInterpreterV1Consumer" ); @@ -215,7 +215,7 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { const timestamp = (await getBlockTimestamp()) - 1; const chainlinkPriceData = { - roundId: 4, + roundId: 4, answer: "123" + eighteenZeros, startedAt: timestamp, updatedAt: timestamp, @@ -233,16 +233,18 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { const priceData = await rainInterpreterExtern.extern(0, inputs); console.log(priceData); // assert(priceData) - }); + }); - it.only("extern test 2", async () => { + it.only("extern test 2", async () => { const fakeChainlinkOracle2 = await smock.fake("AggregatorV3Interface"); + const timestamp = await getBlockTimestamp(); + const chainlinkPriceData = { roundId: 4, answer: 123 + eighteenZeros, - startedAt: await getBlockTimestamp(), - updatedAt: await getBlockTimestamp(), + startedAt: timestamp, + updatedAt: timestamp, answeredInRound: 4, }; @@ -252,16 +254,16 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { const feed = fakeChainlinkOracle2.address; const staleAfter = 10000; - const constants = [rainInterpreterExtern.address,feed,staleAfter] + const constants = [rainInterpreterExtern.address,feed,staleAfter] const v0 = op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 1)); const v1 = op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 2)); - + // prettier-ignore const source0 = concat([ v0, v1, - op(Opcode.EXTERN, externOprand(0, 2 ,1)), + op(Opcode.EXTERN, externOperand(0, 2 ,1)), ]); const expression0 = await expressionConsumerDeploy( @@ -275,7 +277,7 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { await logic.eval(rainInterpreter.address, expression0.dispatch, []); const result0 = await logic.stackTop(); - console.log("result0 : " , result0) + console.log("result0 : " , result0) }); diff --git a/utils/interpreter/interpreter.ts b/utils/interpreter/interpreter.ts index 146aecff2..2f962bd30 100644 --- a/utils/interpreter/interpreter.ts +++ b/utils/interpreter/interpreter.ts @@ -74,9 +74,9 @@ export function callOperand( ): number { const operand = (sourceIndex << 8) + (outputSize << 4) + inputSize; return operand; -} +} -export function externOprand( +export function externOperand( offset: number, inputs: number, outputs: number @@ -115,7 +115,7 @@ export function doWhileOperand( reserved: number, sourceIndex: number ): number { - const operand = (sourceIndex << 8) + (reserved << 4) + inputSize; + const operand = (sourceIndex << 8) + (reserved << 4) + inputSize; return operand; } From 1029355707f33216781ffe4cd74ac330547a4570 Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Mon, 30 Jan 2023 22:43:30 +0530 Subject: [PATCH 19/23] test --- .../Shared/RainterpreterExtern/extern.ts | 287 ++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 test/Interpreter/Shared/RainterpreterExtern/extern.ts diff --git a/test/Interpreter/Shared/RainterpreterExtern/extern.ts b/test/Interpreter/Shared/RainterpreterExtern/extern.ts new file mode 100644 index 000000000..41c28e463 --- /dev/null +++ b/test/Interpreter/Shared/RainterpreterExtern/extern.ts @@ -0,0 +1,287 @@ +import type { + AggregatorV3Interface, + RainterpreterExtern, + Rainterpreter, + IInterpreterV1Consumer + } from "../../../../typechain"; + import { + AllStandardOps, + assertError, + eighteenZeros, + externOperand, + getBlockTimestamp, + memoryOperand, + MemoryType, + op, + sixZeros, + timewarp, + } from "../../../../utils"; + import { FakeContract, smock } from "@defi-wonderland/smock"; + import { concat } from "ethers/lib/utils"; + import { assert } from "chai"; + import { expressionConsumerDeploy, iinterpreterV1ConsumerDeploy } from "../../../../utils/deploy/test/iinterpreterV1Consumer/deploy"; + import { rainterpreterDeploy, rainterpreterExtern } from "../../../../utils/deploy/interpreter/shared/rainterpreter/deploy"; + import { ethers } from "hardhat"; + + const Opcode = AllStandardOps; + + describe("CHAINLINK_PRICE Opcode tests", async function () { + let rainInterpreter: Rainterpreter; + let logic: IInterpreterV1Consumer; + let rainInterpreterExtern: RainterpreterExtern; + let fakeChainlinkOracle: FakeContract; + + beforeEach(async () => { + rainInterpreter = await rainterpreterDeploy(); + + // fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); + rainInterpreterExtern = await rainterpreterExtern(); + const consumerFactory = await ethers.getContractFactory( + "IInterpreterV1Consumer" + ); + logic = (await consumerFactory.deploy()) as IInterpreterV1Consumer; + await logic.deployed(); + + + }); + + it("should revert if price is stale", async () => { + const chainlinkPriceData = { + roundId: 1, + answer: 123 + eighteenZeros, + startedAt: 2, + updatedAt: 1800, // 1800 sec into the future + answeredInRound: 4, + }; + + fakeChainlinkOracle.latestRoundData.returns(chainlinkPriceData); + fakeChainlinkOracle.decimals.returns(18); + + const feed = fakeChainlinkOracle.address; + const staleAfter = (await getBlockTimestamp()) + 3600; + + const sources = [ + concat([ + op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 0)), + op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 1)), + op(Opcode.CHAINLINK_PRICE), + ]), + ]; + const constants = [feed, staleAfter]; + + const { consumerLogic, interpreter, dispatch } = + await iinterpreterV1ConsumerDeploy( + { + sources, + constants, + }, + 1 + ); + + // Eval + + await timewarp(1900); // updated 100 sec ago + + await consumerLogic.eval(interpreter.address, dispatch, []); + + await timewarp(3600); // updated 3700 sec ago (stale) + + await assertError( + async () => await consumerLogic.eval(interpreter.address, dispatch, []), + "StalePrice(1800, ", + "did not revert when chainlink price was stale" + ); + }); + + it("should revert if price is 0", async () => { + const chainlinkPriceData = { + roundId: 1, + answer: 0 + eighteenZeros, + startedAt: 2, + updatedAt: 3, + answeredInRound: 4, + }; + + fakeChainlinkOracle.latestRoundData.returns(chainlinkPriceData); + fakeChainlinkOracle.decimals.returns(18); + + const feed = fakeChainlinkOracle.address; + const staleAfter = (await getBlockTimestamp()) + 10000; + + const sources = [ + concat([ + op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 0)), + op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 1)), + op(Opcode.CHAINLINK_PRICE), + ]), + ]; + const constants = [feed, staleAfter]; + + const { consumerLogic, interpreter, dispatch } = + await iinterpreterV1ConsumerDeploy( + { + sources, + constants, + }, + 1 + ); + + await assertError( + async () => await consumerLogic.eval(interpreter.address, dispatch, []), + "NotPosIntPrice(0)", + "did not revert when chainlink price was 0" + ); + }); + + it("should correctly scale answer from 6 decimal to 18 decimal FP", async () => { + const chainlinkPriceData = { + roundId: 1, + answer: 123 + sixZeros, + startedAt: 2, + updatedAt: 3, + answeredInRound: 4, + }; + + fakeChainlinkOracle.latestRoundData.returns(chainlinkPriceData); + fakeChainlinkOracle.decimals.returns(6); + + const feed = fakeChainlinkOracle.address; + const staleAfter = (await getBlockTimestamp()) + 10000; + + const sources = [ + concat([ + op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 0)), + op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 1)), + op(Opcode.CHAINLINK_PRICE), + ]), + ]; + const constants = [feed, staleAfter]; + + const { consumerLogic, interpreter, dispatch } = + await iinterpreterV1ConsumerDeploy( + { + sources, + constants, + }, + 1 + ); + + await consumerLogic.eval(interpreter.address, dispatch, []); + const price_ = await consumerLogic.stackTop(); + assert(price_.eq(123 + eighteenZeros)); + }); + + it("should get price from chainlink oracle", async () => { + const chainlinkPriceData = { + roundId: 1, + answer: 123 + eighteenZeros, + startedAt: 2, + updatedAt: 3, + answeredInRound: 4, + }; + + fakeChainlinkOracle.latestRoundData.returns(chainlinkPriceData); + fakeChainlinkOracle.decimals.returns(18); + + const feed = fakeChainlinkOracle.address; + const staleAfter = (await getBlockTimestamp()) + 10000; + + const sources = [ + concat([ + op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 0)), + op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 1)), + op(Opcode.CHAINLINK_PRICE), + ]), + ]; + const constants = [feed, staleAfter]; + + const { consumerLogic, interpreter, dispatch } = + await iinterpreterV1ConsumerDeploy( + { + sources, + constants, + }, + 1 + ); + + await consumerLogic.eval(interpreter.address, dispatch, []); + const price_ = await consumerLogic.stackTop(); + + assert(price_.eq(123 + eighteenZeros)); + }); + + it("extern test", async () => { + const fakeChainlinkOracle2 = await smock.fake("AggregatorV3Interface"); + + const timestamp = (await getBlockTimestamp()) - 1; + const chainlinkPriceData = { + roundId: 4, + answer: "123" + eighteenZeros, + startedAt: timestamp, + updatedAt: timestamp, + answeredInRound: 4, + }; + + fakeChainlinkOracle2.latestRoundData.returns(chainlinkPriceData); + fakeChainlinkOracle2.decimals.returns(18); + + const feed = fakeChainlinkOracle2.address; + const staleAfter = 10000; + + const inputs = [feed, staleAfter]; + + const priceData = await rainInterpreterExtern.extern(0, inputs); + console.log(priceData); + // assert(priceData) + }); + + it.only("extern test 2", async () => { + const fakeChainlinkOracle2 = await smock.fake("AggregatorV3Interface"); + + const timestamp = await getBlockTimestamp(); + + const chainlinkPriceData = { + roundId: 4, + answer: 123 + eighteenZeros, + startedAt: timestamp, + updatedAt: timestamp, + answeredInRound: 4, + }; + + fakeChainlinkOracle2.latestRoundData.returns(chainlinkPriceData); + fakeChainlinkOracle2.decimals.returns(18); + + const feed = fakeChainlinkOracle2.address; + const staleAfter = 10000; + + const constants = [rainInterpreterExtern.address,feed,staleAfter] + + const v0 = op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 1)); + const v1 = op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 2)); + + // prettier-ignore + const source0 = concat([ + v0, + v1, + op(Opcode.EXTERN, externOperand(0, 2 ,1)), + ]); + + const expression0 = await expressionConsumerDeploy( + { + sources: [source0], + constants, + }, + rainInterpreter, + 1 + ); + await logic.eval(rainInterpreter.address, expression0.dispatch, []); + const result0 = await logic.stackTop(); + + console.log("result0 : " , result0) + + }); + + + + }); + \ No newline at end of file From c130443bc3608f9b2dde983b98d56358a7f3b767 Mon Sep 17 00:00:00 2001 From: SocioDroid Date: Tue, 31 Jan 2023 01:27:28 +0530 Subject: [PATCH 20/23] update hash and fix test --- .../interpreter/shared/Rainterpreter.sol | 2 +- .../RainterpreterExpressionDeployer.sol | 2 +- .../Ops/Chainlink/chainlinkPrice.ts | 28 +++++++++++-------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/contracts/interpreter/shared/Rainterpreter.sol b/contracts/interpreter/shared/Rainterpreter.sol index dd8c1c0a5..9da2359a2 100644 --- a/contracts/interpreter/shared/Rainterpreter.sol +++ b/contracts/interpreter/shared/Rainterpreter.sol @@ -17,7 +17,7 @@ error UnexpectedOpMetaHash(bytes32 actualOpMeta); /// @dev Hash of the known store bytecode. bytes32 constant STORE_BYTECODE_HASH = bytes32( - 0xaa747007d5351b774ad5b5fcea64d2d3b0f43b4dcf5d366bf1a61455a3ecef7c + 0xbc52db76e944bf5245c516990668f268c9d2f24dc3aa1b06b4f9d128914df383 ); /// @dev Hash of the known op meta. diff --git a/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol b/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol index 5ed91062d..b394f503a 100644 --- a/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol +++ b/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol @@ -21,7 +21,7 @@ error MissingEntrypoint(uint256 expectedEntrypoints, uint256 actualEntrypoints); /// immutable for any given interpreter so once the expression deployer is /// constructed and has verified that this matches what the interpreter reports, /// it can use this constant value to compile and serialize expressions. -bytes constant OPCODE_FUNCTION_POINTERS = hex"09bc09ca0a200a720af00b1c0bb50c7f0db40de90e070e8f0e9e0eac0eba0ec80e9e0ed60ee40ef20f010f100f1e0f2c0fa40fb30fc20fd10fe00fef1038104a1058108a109810a610b410c310d210e110f010ff110e111d112c113b114a11591167117511831191119f11ad11bb11ca11d911e7125e"; +bytes constant OPCODE_FUNCTION_POINTERS = hex"09c509d30a290a7b0af90b250bbe0d490e130f480f7d0f9b102310321040104e105c1032106a10781086109510a410b210c011381147115611651174118311cc11de11ec121e122c123a12481257126612751284129312a212b112c012cf12de12ed12fb13091317132513331341134f135e136d137b13ed"; /// @title RainterpreterExpressionDeployer /// @notice Minimal binding of the `IExpressionDeployerV1` interface to the diff --git a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts index 1c7e60fc3..90706e8db 100644 --- a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts +++ b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts @@ -2,7 +2,7 @@ import type { AggregatorV3Interface, RainterpreterExtern, Rainterpreter, - IInterpreterV1Consumer + IInterpreterV1Consumer, } from "../../../../typechain"; import { AllStandardOps, @@ -19,8 +19,14 @@ import { import { FakeContract, smock } from "@defi-wonderland/smock"; import { concat } from "ethers/lib/utils"; import { assert } from "chai"; -import { expressionConsumerDeploy, iinterpreterV1ConsumerDeploy } from "../../../../utils/deploy/test/iinterpreterV1Consumer/deploy"; -import { rainterpreterDeploy, rainterpreterExtern } from "../../../../utils/deploy/interpreter/shared/rainterpreter/deploy"; +import { + expressionConsumerDeploy, + iinterpreterV1ConsumerDeploy, +} from "../../../../utils/deploy/test/iinterpreterV1Consumer/deploy"; +import { + rainterpreterDeploy, + rainterpreterExtern, +} from "../../../../utils/deploy/interpreter/shared/rainterpreter/deploy"; import { ethers } from "hardhat"; const Opcode = AllStandardOps; @@ -41,8 +47,6 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { ); logic = (await consumerFactory.deploy()) as IInterpreterV1Consumer; await logic.deployed(); - - }); it("should revert if price is stale", async () => { @@ -276,7 +280,7 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { const feed = fakeChainlinkOracle2.address; const staleAfter = 10000; - const constants = [rainInterpreterExtern.address,feed,staleAfter] + const constants = [rainInterpreterExtern.address, feed, staleAfter]; const v0 = op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 1)); const v1 = op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 2)); @@ -296,13 +300,13 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { rainInterpreter, 1 ); - await logic.eval(rainInterpreter.address, expression0.dispatch, []); + await logic["eval(address,uint256,uint256[][])"]( + rainInterpreter.address, + expression0.dispatch, + [] + ); const result0 = await logic.stackTop(); - console.log("result0 : " , result0) - + console.log("result0 : ", result0); }); - - - }); From 7c11a275625f7dece7d38392ba4148244f8acfa5 Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Tue, 31 Jan 2023 11:04:58 +0530 Subject: [PATCH 21/23] extern op tests --- .../Ops/Chainlink/chainlinkPrice.ts | 89 +----- .../Shared/RainterpreterExtern/extern.ts | 291 ++++++++---------- .../shared/rainterpreter/deploy.ts | 2 +- 3 files changed, 144 insertions(+), 238 deletions(-) diff --git a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts index 90706e8db..87e6e62a7 100644 --- a/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts +++ b/test/Interpreter/Ops/Chainlink/chainlinkPrice.ts @@ -35,12 +35,11 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { let rainInterpreter: Rainterpreter; let logic: IInterpreterV1Consumer; let rainInterpreterExtern: RainterpreterExtern; - let fakeChainlinkOracle: FakeContract; + beforeEach(async () => { rainInterpreter = await rainterpreterDeploy(); - // fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); rainInterpreterExtern = await rainterpreterExtern(); const consumerFactory = await ethers.getContractFactory( "IInterpreterV1Consumer" @@ -49,7 +48,9 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { await logic.deployed(); }); - it("should revert if price is stale", async () => { + it("should revert if price is stale", async () => { + + const fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); const chainlinkPriceData = { roundId: 1, answer: 123 + eighteenZeros, @@ -106,7 +107,9 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { ); }); - it("should revert if price is 0", async () => { + it("should revert if price is 0", async () => { + const fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); + const chainlinkPriceData = { roundId: 1, answer: 0 + eighteenZeros, @@ -152,6 +155,8 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { }); it("should correctly scale answer from 6 decimal to 18 decimal FP", async () => { + const fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); + const chainlinkPriceData = { roundId: 1, answer: 123 + sixZeros, @@ -194,6 +199,8 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { }); it("should get price from chainlink oracle", async () => { + const fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); + const chainlinkPriceData = { roundId: 1, answer: 123 + eighteenZeros, @@ -236,77 +243,5 @@ describe("CHAINLINK_PRICE Opcode tests", async function () { assert(price_.eq(123 + eighteenZeros)); }); - it("extern test", async () => { - const fakeChainlinkOracle2 = await smock.fake("AggregatorV3Interface"); - - const timestamp = (await getBlockTimestamp()) - 1; - const chainlinkPriceData = { - roundId: 4, - answer: "123" + eighteenZeros, - startedAt: timestamp, - updatedAt: timestamp, - answeredInRound: 4, - }; - - fakeChainlinkOracle2.latestRoundData.returns(chainlinkPriceData); - fakeChainlinkOracle2.decimals.returns(18); - - const feed = fakeChainlinkOracle2.address; - const staleAfter = 10000; - - const inputs = [feed, staleAfter]; - - const priceData = await rainInterpreterExtern.extern(0, inputs); - console.log(priceData); - // assert(priceData) - }); - - it.only("extern test 2", async () => { - const fakeChainlinkOracle2 = await smock.fake("AggregatorV3Interface"); - - const timestamp = await getBlockTimestamp(); - - const chainlinkPriceData = { - roundId: 4, - answer: 123 + eighteenZeros, - startedAt: timestamp, - updatedAt: timestamp, - answeredInRound: 4, - }; - - fakeChainlinkOracle2.latestRoundData.returns(chainlinkPriceData); - fakeChainlinkOracle2.decimals.returns(18); - - const feed = fakeChainlinkOracle2.address; - const staleAfter = 10000; - - const constants = [rainInterpreterExtern.address, feed, staleAfter]; - - const v0 = op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 1)); - const v1 = op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 2)); - - // prettier-ignore - const source0 = concat([ - v0, - v1, - op(Opcode.EXTERN, externOperand(0, 2 ,1)), - ]); - - const expression0 = await expressionConsumerDeploy( - { - sources: [source0], - constants, - }, - rainInterpreter, - 1 - ); - await logic["eval(address,uint256,uint256[][])"]( - rainInterpreter.address, - expression0.dispatch, - [] - ); - const result0 = await logic.stackTop(); - - console.log("result0 : ", result0); - }); + }); diff --git a/test/Interpreter/Shared/RainterpreterExtern/extern.ts b/test/Interpreter/Shared/RainterpreterExtern/extern.ts index 41c28e463..62a52bc7e 100644 --- a/test/Interpreter/Shared/RainterpreterExtern/extern.ts +++ b/test/Interpreter/Shared/RainterpreterExtern/extern.ts @@ -29,12 +29,9 @@ import type { let rainInterpreter: Rainterpreter; let logic: IInterpreterV1Consumer; let rainInterpreterExtern: RainterpreterExtern; - let fakeChainlinkOracle: FakeContract; beforeEach(async () => { rainInterpreter = await rainterpreterDeploy(); - - // fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); rainInterpreterExtern = await rainterpreterExtern(); const consumerFactory = await ethers.getContractFactory( "IInterpreterV1Consumer" @@ -43,57 +40,11 @@ import type { await logic.deployed(); - }); - - it("should revert if price is stale", async () => { - const chainlinkPriceData = { - roundId: 1, - answer: 123 + eighteenZeros, - startedAt: 2, - updatedAt: 1800, // 1800 sec into the future - answeredInRound: 4, - }; - - fakeChainlinkOracle.latestRoundData.returns(chainlinkPriceData); - fakeChainlinkOracle.decimals.returns(18); - - const feed = fakeChainlinkOracle.address; - const staleAfter = (await getBlockTimestamp()) + 3600; - - const sources = [ - concat([ - op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 0)), - op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 1)), - op(Opcode.CHAINLINK_PRICE), - ]), - ]; - const constants = [feed, staleAfter]; - - const { consumerLogic, interpreter, dispatch } = - await iinterpreterV1ConsumerDeploy( - { - sources, - constants, - }, - 1 - ); - - // Eval - - await timewarp(1900); // updated 100 sec ago + }); + + it("extern op should revert if price is 0", async () => { + const fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); - await consumerLogic.eval(interpreter.address, dispatch, []); - - await timewarp(3600); // updated 3700 sec ago (stale) - - await assertError( - async () => await consumerLogic.eval(interpreter.address, dispatch, []), - "StalePrice(1800, ", - "did not revert when chainlink price was stale" - ); - }); - - it("should revert if price is 0", async () => { const chainlinkPriceData = { roundId: 1, answer: 0 + eighteenZeros, @@ -108,32 +59,45 @@ import type { const feed = fakeChainlinkOracle.address; const staleAfter = (await getBlockTimestamp()) + 10000; - const sources = [ - concat([ - op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 0)), - op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 1)), - op(Opcode.CHAINLINK_PRICE), - ]), - ]; - const constants = [feed, staleAfter]; - - const { consumerLogic, interpreter, dispatch } = - await iinterpreterV1ConsumerDeploy( - { - sources, - constants, - }, - 1 - ); + const constants = [rainInterpreterExtern.address,feed,staleAfter] + + const v0 = op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 1)); + const v1 = op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 2)); + + // prettier-ignore + const source0 = concat([ + v0, + v1, + op(Opcode.EXTERN, externOperand(0, 2 ,1)), + ]); + const expression0 = await expressionConsumerDeploy( + { + sources: [source0], + constants, + }, + rainInterpreter, + 1 + ); + + await assertError( - async () => await consumerLogic.eval(interpreter.address, dispatch, []), + async () => + await logic["eval(address,uint256,uint256[][])"]( + rainInterpreter.address, + expression0.dispatch, + [] + ), "NotPosIntPrice(0)", "did not revert when chainlink price was 0" ); - }); - it("should correctly scale answer from 6 decimal to 18 decimal FP", async () => { + + }); + + it("extern op should correctly scale answer from 6 decimal to 18 decimal FP", async () => { + const fakeChainlinkOracle = await smock.fake("AggregatorV3Interface"); + const chainlinkPriceData = { roundId: 1, answer: 123 + sixZeros, @@ -148,94 +112,38 @@ import type { const feed = fakeChainlinkOracle.address; const staleAfter = (await getBlockTimestamp()) + 10000; - const sources = [ - concat([ - op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 0)), - op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 1)), - op(Opcode.CHAINLINK_PRICE), - ]), - ]; - const constants = [feed, staleAfter]; - - const { consumerLogic, interpreter, dispatch } = - await iinterpreterV1ConsumerDeploy( - { - sources, - constants, - }, - 1 - ); - - await consumerLogic.eval(interpreter.address, dispatch, []); - const price_ = await consumerLogic.stackTop(); - assert(price_.eq(123 + eighteenZeros)); - }); - - it("should get price from chainlink oracle", async () => { - const chainlinkPriceData = { - roundId: 1, - answer: 123 + eighteenZeros, - startedAt: 2, - updatedAt: 3, - answeredInRound: 4, - }; - - fakeChainlinkOracle.latestRoundData.returns(chainlinkPriceData); - fakeChainlinkOracle.decimals.returns(18); - - const feed = fakeChainlinkOracle.address; - const staleAfter = (await getBlockTimestamp()) + 10000; - - const sources = [ - concat([ - op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 0)), - op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 1)), - op(Opcode.CHAINLINK_PRICE), - ]), - ]; - const constants = [feed, staleAfter]; - - const { consumerLogic, interpreter, dispatch } = - await iinterpreterV1ConsumerDeploy( - { - sources, - constants, - }, - 1 - ); - - await consumerLogic.eval(interpreter.address, dispatch, []); - const price_ = await consumerLogic.stackTop(); - - assert(price_.eq(123 + eighteenZeros)); - }); - - it("extern test", async () => { - const fakeChainlinkOracle2 = await smock.fake("AggregatorV3Interface"); - - const timestamp = (await getBlockTimestamp()) - 1; - const chainlinkPriceData = { - roundId: 4, - answer: "123" + eighteenZeros, - startedAt: timestamp, - updatedAt: timestamp, - answeredInRound: 4, - }; - - fakeChainlinkOracle2.latestRoundData.returns(chainlinkPriceData); - fakeChainlinkOracle2.decimals.returns(18); - - const feed = fakeChainlinkOracle2.address; - const staleAfter = 10000; + const constants = [rainInterpreterExtern.address,feed,staleAfter] - const inputs = [feed, staleAfter]; + const v0 = op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 1)); + const v1 = op(Opcode.READ_MEMORY, memoryOperand(MemoryType.Constant, 2)); - const priceData = await rainInterpreterExtern.extern(0, inputs); - console.log(priceData); - // assert(priceData) - }); + // prettier-ignore + const source0 = concat([ + v0, + v1, + op(Opcode.EXTERN, externOperand(0, 2 ,1)), + ]); - it.only("extern test 2", async () => { + const expression0 = await expressionConsumerDeploy( + { + sources: [source0], + constants, + }, + rainInterpreter, + 1 + ); + await logic["eval(address,uint256,uint256[][])"]( + rainInterpreter.address, + expression0.dispatch, + [] + ); + const result0 = await logic.stackTop(); + + assert(result0.eq(123 + eighteenZeros)); + + }); + + it("extern op should return expected value", async () => { const fakeChainlinkOracle2 = await smock.fake("AggregatorV3Interface"); const timestamp = await getBlockTimestamp(); @@ -274,13 +182,76 @@ import type { rainInterpreter, 1 ); - await logic.eval(rainInterpreter.address, expression0.dispatch, []); + await logic["eval(address,uint256,uint256[][])"]( + rainInterpreter.address, + expression0.dispatch, + [] + ); const result0 = await logic.stackTop(); - console.log("result0 : " , result0) + assert(result0.eq(123 + eighteenZeros)); }); + it("rainInterpreterExtern should revert with BadInputs", async () => { + const fakeChainlinkOracle2 = await smock.fake("AggregatorV3Interface"); + + const timestamp = (await getBlockTimestamp()) - 1; + const chainlinkPriceData = { + roundId: 4, + answer: "123" + eighteenZeros, + startedAt: timestamp, + updatedAt: timestamp, + answeredInRound: 4, + }; + + fakeChainlinkOracle2.latestRoundData.returns(chainlinkPriceData); + fakeChainlinkOracle2.decimals.returns(18); + + const feed = fakeChainlinkOracle2.address; + + const inputs = [feed]; + + await assertError( + async () => + await rainInterpreterExtern.extern(0, inputs), + "BadInputs", + "did not revert when incorrect inputs" + ); + + + }); + + it("rainInterpreterExtern should get price from oracle", async () => { + const fakeChainlinkOracle2 = await smock.fake("AggregatorV3Interface"); + + const timestamp = (await getBlockTimestamp()) - 1; + const chainlinkPriceData = { + roundId: 4, + answer: "123" + eighteenZeros, + startedAt: timestamp, + updatedAt: timestamp, + answeredInRound: 4, + }; + + fakeChainlinkOracle2.latestRoundData.returns(chainlinkPriceData); + fakeChainlinkOracle2.decimals.returns(18); + + const feed = fakeChainlinkOracle2.address; + const staleAfter = 10000; + + const inputs = [feed , staleAfter ]; + + const priceData = await rainInterpreterExtern.extern(0, inputs); + assert(priceData[0].eq(123 + eighteenZeros)); + + + }); + + + + + }); diff --git a/utils/deploy/interpreter/shared/rainterpreter/deploy.ts b/utils/deploy/interpreter/shared/rainterpreter/deploy.ts index 1898f2294..f24f3b9a2 100644 --- a/utils/deploy/interpreter/shared/rainterpreter/deploy.ts +++ b/utils/deploy/interpreter/shared/rainterpreter/deploy.ts @@ -1,4 +1,4 @@ -import { Rainterpreter, RainterpreterStore } from "../../../../../typechain"; +import { Rainterpreter, RainterpreterExtern, RainterpreterStore } from "../../../../../typechain"; import { getRainterpreterOpmetaBytes } from "../../../../interpreter/ops/allStandardOpmeta"; import { basicDeploy } from "../../../basicDeploy"; From 8bfda43ddb3f2d2d10fdc84c758ca2956212df4b Mon Sep 17 00:00:00 2001 From: SocioDroid Date: Tue, 31 Jan 2023 12:12:23 +0530 Subject: [PATCH 22/23] Update func pointers --- .../interpreter/shared/RainterpreterExpressionDeployer.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol b/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol index b394f503a..5f7f74a4d 100644 --- a/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol +++ b/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol @@ -21,7 +21,7 @@ error MissingEntrypoint(uint256 expectedEntrypoints, uint256 actualEntrypoints); /// immutable for any given interpreter so once the expression deployer is /// constructed and has verified that this matches what the interpreter reports, /// it can use this constant value to compile and serialize expressions. -bytes constant OPCODE_FUNCTION_POINTERS = hex"09c509d30a290a7b0af90b250bbe0d490e130f480f7d0f9b102310321040104e105c1032106a10781086109510a410b210c011381147115611651174118311cc11de11ec121e122c123a12481257126612751284129312a212b112c012cf12de12ed12fb13091317132513331341134f135e136d137b13ed"; +bytes constant OPCODE_FUNCTION_POINTERS = hex"09ce09dc0a320a840b020b2e0bc70d520e1c0f510f860fa4102c103b104910571065103b10731081108f109e10ac10bb10c910d7114f115e116d117c118b119a11e311f51203123512431251125f126e127d128c129b12aa12b912c812d712e612f5130413121320132e133c134a135813661375138413921404"; /// @title RainterpreterExpressionDeployer /// @notice Minimal binding of the `IExpressionDeployerV1` interface to the From 0cfb12fd2a832792b8a2fefe52d2049b51bc75a8 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Tue, 31 Jan 2023 11:44:51 +0400 Subject: [PATCH 23/23] fix pointers --- contracts/interpreter/ops/AllStandardOps.sol | 2 +- .../interpreter/shared/RainterpreterExpressionDeployer.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/interpreter/ops/AllStandardOps.sol b/contracts/interpreter/ops/AllStandardOps.sol index fc168704b..e2b50b50a 100644 --- a/contracts/interpreter/ops/AllStandardOps.sol +++ b/contracts/interpreter/ops/AllStandardOps.sol @@ -72,7 +72,7 @@ import "./tier/OpUpdateTimesForTierRange.sol"; error BadDynamicLength(uint256 dynamicLength, uint256 standardOpsLength); /// @dev Number of ops currently provided by `AllStandardOps`. -uint256 constant ALL_STANDARD_OPS_LENGTH = 61; +uint256 constant ALL_STANDARD_OPS_LENGTH = 62; /// @title AllStandardOps /// @notice Every opcode available from the core repository laid out as a single diff --git a/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol b/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol index 0cc53612b..f668f55b9 100644 --- a/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol +++ b/contracts/interpreter/shared/RainterpreterExpressionDeployer.sol @@ -21,7 +21,7 @@ error MissingEntrypoint(uint256 expectedEntrypoints, uint256 actualEntrypoints); /// immutable for any given interpreter so once the expression deployer is /// constructed and has verified that this matches what the interpreter reports, /// it can use this constant value to compile and serialize expressions. -bytes constant OPCODE_FUNCTION_POINTERS = hex"09ce09dc0a320a840b020b2e0bc70c910dc60dfb0e190ea10eb00ebe0ecc0eda0eb00ee80ef60f040f130f210f300f3e0f4c0fc40fd30fe20ff11000100f101e10671079108710b910c710d510e310f211011110111f112e113d114c115b116a11791188119611a411b211c011ce11dc11ea11f912081216128d"; +bytes constant OPCODE_FUNCTION_POINTERS = hex"09d709e50a3b0a8d0b0b0b370bd00d5b0e250f5a0f8f0fad1035104410521060106e1044107c108a109810a710b510c410d210e01158116711761185119411a311b211fb120d121b124d125b126912771286129512a412b312c212d112e012ef12fe130d131c132a13381346135413621370137e138d139c13aa141c"; /// @title RainterpreterExpressionDeployer /// @notice Minimal binding of the `IExpressionDeployerV1` interface to the