Skip to content
This repository was archived by the owner on Dec 7, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions contracts/interpreter/deploy/LibIntegrityCheck.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ StackPointer constant INITIAL_STACK_BOTTOM = StackPointer.wrap(
type(uint256).max / 2
);

/// Thrown by an integrity check on an op if the operand exceeds the max value
/// that it can handle. MAY be for a subset of all the operand bits if the range
/// is over a destructured value from the whole operand.
error OperandOverflow(uint256 maximum, uint256 actual);

/// Thrown by an integrity check on an op if the operand subseeds the min value
/// that it can handle. MAY be for a subset of all the operand bits if the range
/// is over a destructured value from the whole operand.
error OperandUnderflow(uint256 minimum, uint256 actual);

/// It is a misconfiguration to set the initial stack bottom to zero or some
/// small value as this trivially exposes the integrity check to potential
/// underflow issues that are gas intensive to repeatedly guard against on every
Expand Down
9 changes: 9 additions & 0 deletions contracts/interpreter/ops/crypto/OpHash.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "../../../array/LibUint256Array.sol";
import "../../../type/LibCast.sol";
import "../../run/LibInterpreterState.sol";
import "../../deploy/LibIntegrityCheck.sol";
import "hardhat/console.sol";

/// @title OpHash
/// @notice Opcode for hashing a list of values.
Expand All @@ -23,6 +24,14 @@ library OpHash {
Operand operand_,
StackPointer stackTop_
) internal pure returns (StackPointer) {
if (Operand.unwrap(operand_) == 0) {
revert OperandUnderflow(1, 0);
}

if (Operand.unwrap(operand_) > 255) {
revert OperandOverflow(255, Operand.unwrap(operand_));
}

return
integrityCheckState_.applyFn(
stackTop_,
Expand Down
13 changes: 6 additions & 7 deletions contracts/interpreter/ops/tier/OpSelectLte.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ import "../../run/LibInterpreterState.sol";
import "../../deploy/LibIntegrityCheck.sol";
import "../../../math/Binary.sol";

/// Zero inputs to select lte is NOT supported.
error ZeroInputs();

/// @title OpSelectLte
/// @notice Exposes `TierwiseCombine.selectLte` as an opcode.
library OpSelectLte {
Expand All @@ -23,14 +20,16 @@ library OpSelectLte {
StackPointer stackTop_
) internal pure returns (StackPointer) {
unchecked {
uint256 inputs_ = Operand.unwrap(operand_) & MASK_8BIT;
if (inputs_ == 0) {
revert ZeroInputs();
if (Operand.unwrap(operand_) == 0) {
revert OperandUnderflow(1, 0);
}

return
integrityCheckState_.push(
integrityCheckState_.pop(stackTop_, inputs_)
integrityCheckState_.pop(
stackTop_,
Operand.unwrap(operand_)
)
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ bytes constant OPCODE_FUNCTION_POINTERS = hex"0b360b450b540bd70be50c3b0c8d0d0b0d

/// @dev Hash of the known interpreter bytecode.
bytes32 constant INTERPRETER_BYTECODE_HASH = bytes32(
0x46fd8b5ce435ff6aca550c3e278743894f4cea2b97c2b47ff4ce3aabbfced341
0x078bc41915d9b0546cf9af619740692cca28489e388a79d381c14e7752de9baf
);

/// @dev Hash of the known store bytecode.
bytes32 constant STORE_BYTECODE_HASH = bytes32(
0x33612e3d92c79aeb4108030de9f132698ba8563f5219fa6c32d88b3ea02040ae
0x529e4a275f269ea91d28c434c037bc10c8a85f2f35a83201b7883b817edc254d
);

/// @dev Hash of the known op meta.
Expand Down
151 changes: 151 additions & 0 deletions test/Interpreter/Ops/Crypto/validateMeta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { assert } from "chai";
import { BytesLike, concat } from "ethers/lib/utils";
import { ethers } from "hardhat";
import { IInterpreterV1Consumer, Rainterpreter } from "../../../../typechain";
import {
assertError,
max_uint256,
memoryOperand,
MemoryType,
op,
Opcode,
randomUint256,
standardEvaluableConfig,
} from "../../../../utils";
import { rainterpreterDeploy } from "../../../../utils/deploy/interpreter/shared/rainterpreter/deploy";
import deploy1820 from "../../../../utils/deploy/registry1820/deploy";
import { expressionConsumerDeploy } from "../../../../utils/deploy/test/iinterpreterV1Consumer/deploy";
import OpHash from "../../../../contracts/interpreter/ops/crypto/OpHash.opmeta.json";
import { constructByBits, OperandArgs } from "rainlang";

describe("HASH Opcode test", async function () {
let rainInterpreter: Rainterpreter;
let logic: IInterpreterV1Consumer;

before(async () => {
// Deploy ERC1820Registry
const signers = await ethers.getSigners();
await deploy1820(signers[0]);
rainInterpreter = await rainterpreterDeploy();

const consumerFactory = await ethers.getContractFactory(
"IInterpreterV1Consumer"
);
logic = (await consumerFactory.deploy()) as IInterpreterV1Consumer;
await logic.deployed();
});

// get array of a particualr length
const getArrayOfLength = (length: number) => {
let arr = [];
for (let i = 0; i < length; i++) {
arr.push(randomUint256());
}
return arr;
};

// get a int between min and max inclusive
const randomIntFromInterval = (min: number, max: number) => {
return Math.floor(Math.random() * (max - min + 1) + min);
};

// build source array from constants array
const buildSources = (constants: Array<number>, op_: number | number[]) => {
let soruceArray = [];
for (let i = 0; i < constants.length; i++) {
soruceArray.push(
op(Opcode.read_memory, memoryOperand(MemoryType.Constant, i))
);
}
soruceArray.push(op(Opcode.hash, op_));
let sources = concat(soruceArray);
return sources;
};

it("should build operand from array of fuzzed? operand values ", async () => {
for (let i = 0; i < 30; i++) {
// get random value for range
let range = randomIntFromInterval(1, 300);

let operandArgs: OperandArgs = [
{
name: "",
bits: [0, 7],
validRange: [[1, range]],
},
];

// array of values that will be build as a single operand
let values = [range];
const constants = getArrayOfLength(range);

// Constructing arguments for the constructByBits function
// ref resolveOp method of rain parser
let constructArgs = operandArgs.map((e, i) => {
return {
value: values[i],
bits: e.bits,
computation: e.computation,
validRange: e.validRange,
};
});

// getting the operand
let op_ = constructByBits(constructArgs);
let source = buildSources(constants, op_);

// Case if operand is zero
if (op_[0] === 0) {
await assertError(
async () =>
await expressionConsumerDeploy(
[source],
constants,
rainInterpreter,
1
),
"OperandUnderflow",
"Underflow"
);
} else if (op_[0] > 255) {
// Case if operand is greater than enforced length

await assertError(
async () =>
await expressionConsumerDeploy(
[source],
constants,
rainInterpreter,
1
),
"OperandOverflow",
"Overflow"
);
} else {
const expression0 = await expressionConsumerDeploy(
[source],
constants,
rainInterpreter,
1
);

await logic["eval(address,uint256,uint256[][])"](
rainInterpreter.address,
expression0.dispatch,
[]
);
const result = await logic.stackTop();

const expectedValue = ethers.utils.solidityKeccak256(
["uint256[]"],
[constants]
);

assert(
result.eq(expectedValue),
`Invalid output, expected ${expectedValue}, actual ${result}`
);
}
}
});
});
15 changes: 14 additions & 1 deletion test/Interpreter/Ops/Token/erc20.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { assert } from "chai";
import { concat } from "ethers/lib/utils";
import { ethers } from "hardhat";
import type {
IInterpreterV1Consumer,
Rainterpreter,
ReserveToken,
} from "../../../../typechain";
import { assertError, randomUint256 } from "../../../../utils";
import { basicDeploy } from "../../../../utils/deploy/basicDeploy";
import { rainterpreterDeploy } from "../../../../utils/deploy/interpreter/shared/rainterpreter/deploy";
import deploy1820 from "../../../../utils/deploy/registry1820/deploy";
import { expressionConsumerDeploy } from "../../../../utils/deploy/test/iinterpreterV1Consumer/deploy";
import { standardEvaluableConfig } from "../../../../utils/interpreter/interpreter";
import {
memoryOperand,
MemoryType,
op,
standardEvaluableConfig,
} from "../../../../utils/interpreter/interpreter";
import { AllStandardOps } from "../../../../utils/interpreter/ops/allStandardOps";

let signers: SignerWithAddress[];
let signer1: SignerWithAddress;

let tokenERC20: ReserveToken;
const Opcode = AllStandardOps;

describe("RainInterpreter ERC20 ops", async function () {
let rainInterpreter: Rainterpreter;
Expand Down Expand Up @@ -43,6 +52,10 @@ describe("RainInterpreter ERC20 ops", async function () {
await tokenERC20.initialize();
});

const randomUintLen = (len: number): string => {
return ethers.utils.hexZeroPad(ethers.utils.randomBytes(len), len);
};

it("should return ERC20 total supply", async () => {
const { sources, constants } = await standardEvaluableConfig(
`_: erc-20-total-supply(${tokenERC20.address});`
Expand Down