Skip to content

Commit

Permalink
Merge pull request #264 from rainlanguage/2024-07-30-simplify-deployer
Browse files Browse the repository at this point in the history
2024 07 30 simplify deployer
  • Loading branch information
thedavidmeister authored Jul 31, 2024
2 parents 30563e8 + a282766 commit 2a09ae6
Show file tree
Hide file tree
Showing 38 changed files with 1,491 additions and 1,939 deletions.
1,865 changes: 944 additions & 921 deletions .gas-snapshot

Large diffs are not rendered by default.

49 changes: 1 addition & 48 deletions src/concrete/RainterpreterExpressionDeployerNPE2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ import {
UnexpectedParserBytecodeHash,
UnexpectedPointers
} from "../error/ErrDeploy.sol";
import {
IExpressionDeployerV4,
IERC1820_NAME_IEXPRESSION_DEPLOYER_V4
} from "rain.interpreter.interface/interface/unstable/IExpressionDeployerV4.sol";
import {IParserV1View} from "rain.interpreter.interface/interface/unstable/IParserV1View.sol";
import {IInterpreterV2} from "rain.interpreter.interface/interface/IInterpreterV2.sol";
import {IInterpreterStoreV2} from "rain.interpreter.interface/interface/IInterpreterStoreV2.sol";
Expand Down Expand Up @@ -53,7 +49,6 @@ struct RainterpreterExpressionDeployerNPE2ConstructionConfigV2 {
/// @title RainterpreterExpressionDeployerNPE2
contract RainterpreterExpressionDeployerNPE2 is
IDescribedByMetaV1,
IExpressionDeployerV4,
IParserV2,
IParserPragmaV1,
IIntegrityToolingV1,
Expand Down Expand Up @@ -106,56 +101,14 @@ contract RainterpreterExpressionDeployerNPE2 is
if (parserHash != expectedParserBytecodeHash()) {
revert UnexpectedParserBytecodeHash(expectedParserBytecodeHash(), parserHash);
}

// Emit the DISPairV2.
// The parser is this contract as it implements both
// `IExpressionDeployerV4` and `IParserV1View`.
emit DISPairV2(msg.sender, address(interpreter), address(store), address(parser));

// Register the interface for the deployer.
// We have to check that the 1820 registry has bytecode at the address
// before we can register the interface. We can't assume that the chain
// we are deploying to has 1820 deployed.
if (address(IERC1820_REGISTRY).code.length > 0) {
IERC1820_REGISTRY.setInterfaceImplementer(
address(this), IERC1820_REGISTRY.interfaceHash(IERC1820_NAME_IEXPRESSION_DEPLOYER_V4), address(this)
);
}
}

/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IExpressionDeployerV4).interfaceId
|| interfaceId == type(IDescribedByMetaV1).interfaceId || interfaceId == type(IParserV2).interfaceId
return interfaceId == type(IDescribedByMetaV1).interfaceId || interfaceId == type(IParserV2).interfaceId
|| interfaceId == type(IParserPragmaV1).interfaceId || super.supportsInterface(interfaceId);
}

/// @inheritdoc IExpressionDeployerV4
function deployExpression2(bytes memory bytecode, uint256[] memory constants)
external
virtual
returns (IInterpreterV2, IInterpreterStoreV2, address, bytes memory)
{
bytes memory io = LibIntegrityCheckNP.integrityCheck2(INTEGRITY_FUNCTION_POINTERS, bytecode, constants);

emit NewExpression(msg.sender, bytecode, constants);

(DataContractMemoryContainer container, Pointer pointer) =
LibDataContract.newContainer(LibInterpreterStateDataContractNP.serializeSizeNP(bytecode, constants));

// Serialize the state config into bytes that can be deserialized later
// by the interpreter.
LibInterpreterStateDataContractNP.unsafeSerializeNP(pointer, bytecode, constants);

// Deploy the serialized expression onchain.
address expression = LibDataContract.write(container);

// Emit and return the address of the deployed expression.
emit DeployedExpression(msg.sender, iInterpreter, iStore, expression, io);

return (iInterpreter, iStore, expression, io);
}

/// @inheritdoc IParserV2
function parse2(bytes memory data) external view virtual override returns (bytes memory) {
(bytes memory bytecode, uint256[] memory constants) = iParser.parse(data);
Expand Down
47 changes: 4 additions & 43 deletions src/concrete/RainterpreterNPE2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,48 +32,10 @@ import {IOpcodeToolingV1} from "rain.sol.codegen/interface/IOpcodeToolingV1.sol"
/// @title RainterpreterNPE2
/// @notice Implementation of a Rainlang interpreter that is compatible with
/// native onchain Rainlang parsing.
contract RainterpreterNPE2 is IInterpreterV2, IInterpreterV3, IOpcodeToolingV1, ERC165 {
contract RainterpreterNPE2 is IInterpreterV3, IOpcodeToolingV1, ERC165 {
using LibEvalNP for InterpreterStateNP;
using LibInterpreterStateDataContractNP for bytes;

/// There are MANY ways that eval can be forced into undefined/corrupt
/// behaviour by passing in invalid data. This is a deliberate design
/// decision to allow for the interpreter to be as gas efficient as
/// possible. The interpreter is provably read only, it contains no state
/// changing evm opcodes reachable on any logic path. This means that
/// the caller can only harm themselves by passing in invalid data and
/// either reverting, exhausting gas or getting back some garbage data.
/// The caller can trivially protect themselves from these OOB issues by
/// ensuring the integrity check has successfully run over the bytecode
/// before calling eval. Any smart contract caller can do this by using a
/// trusted and appropriate deployer contract to deploy the bytecode, which
/// will automatically run the integrity check during deployment, then
/// keeping a registry of trusted expression addresses for itself in storage.
///
/// This appears first in the contract in the hope that the compiler will
/// put it in the most efficient internal dispatch location to save a few
/// gas per eval call.
///
/// @inheritdoc IInterpreterV2
function eval2(
IInterpreterStoreV2 store,
FullyQualifiedNamespace namespace,
EncodedDispatch dispatch,
uint256[][] memory context,
uint256[] memory inputs
) external view virtual returns (uint256[] memory, uint256[] memory) {
// Decode the dispatch.
(address expression, SourceIndexV2 sourceIndex, uint256 maxOutputs) = LibEncodedDispatch.decode2(dispatch);
bytes memory expressionData = LibDataContract.read(expression);

InterpreterStateNP memory state = expressionData.unsafeDeserializeNP(
SourceIndexV2.unwrap(sourceIndex), namespace, store, context, OPCODE_FUNCTION_POINTERS
);
// We use the return by returning it. Slither false positive.
//slither-disable-next-line unused-return
return state.eval2(inputs, maxOutputs);
}

/// @inheritdoc IInterpreterV3
function eval3(
IInterpreterStoreV2 store,
Expand All @@ -93,12 +55,11 @@ contract RainterpreterNPE2 is IInterpreterV2, IInterpreterV3, IOpcodeToolingV1,

/// @inheritdoc ERC165
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IInterpreterV2).interfaceId || interfaceId == type(IInterpreterV3).interfaceId
|| super.supportsInterface(interfaceId);
return interfaceId == type(IInterpreterV3).interfaceId || super.supportsInterface(interfaceId);
}

/// @inheritdoc IInterpreterV2
function functionPointers() external view virtual override(IInterpreterV2, IInterpreterV3) returns (bytes memory) {
/// @inheritdoc IInterpreterV3
function functionPointers() external view virtual override returns (bytes memory) {
return buildOpcodeFunctionPointers();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
pragma solidity =0.8.25;

/// @dev Hash of the known bytecode.
bytes32 constant BYTECODE_HASH = bytes32(0x57270048a3f944bdb0193caf95f295331d91952cc7fa33ccd2a410d9b33e92cb);
bytes32 constant BYTECODE_HASH = bytes32(0x120bb857e25b094e30d19b11f0bf73b80df656781d0e28d7e7150775a3eebdad);

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

/// @dev The function pointers for the integrity check fns.
bytes constant INTEGRITY_FUNCTION_POINTERS =
hex"123712b5131a1494149e149e14a814b114cc1572157215ce16481655149e14a81655149e14a8149e149e14a814941494149414941494165f1684169e149e165f149e149e165514a8149e149e1655165516a816a816a816a8149e14a816a8149414a814a814a814a8149e14a814a814a814a814a816a816a816a816a8149e14a814a8149e14a814a8149e149e14a816a816a814a8169e";
hex"0f4c0fca102f11a911b311b311bd11c611e11287128712e3135d136a11b311bd136a11b311bd11b311b311bd11a911a911a911a911a91374139913b311b3137411b311b3136a11bd11b311b3136a136a13bd13bd13bd13bd11b311bd13bd11a911bd11bd11bd11bd11b311bd11bd11bd11bd11bd13bd13bd13bd13bd11b311bd11bd11b311bd11bd11b311b311bd13bd13bd11bd13b3";
4 changes: 2 additions & 2 deletions src/generated/RainterpreterNPE2.pointers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
pragma solidity =0.8.25;

/// @dev Hash of the known bytecode.
bytes32 constant BYTECODE_HASH = bytes32(0x96b05ca42ed55bfa6c6ef3bc39ed6bfe2ad71aea061cad6c74a6620cf35cd6e9);
bytes32 constant BYTECODE_HASH = bytes32(0xfee767105c6c61d954e857509e83c39f1352e9ad804e9e9eb297493462c784ce);

/// @dev The function pointers known to the interpreter for dynamic dispatch.
/// By setting these as a constant they can be inlined into the interpreter
/// and loaded at eval time for very low gas (~100) due to the compiler
/// optimising it to a single `codecopy` to build the in memory bytes array.
bytes constant OPCODE_FUNCTION_POINTERS =
hex"090c095d099f0b6b0c520c640c760c990cdb0d2d0d3e0d4f0df10e2e0eec0f9c10201163128a0eec1386142814a014d9151215611561159a15ff16d31726173a179317a717bc17d617e117f5180a1842186918e91937198519d319eb1a041a521a891a971aa51ac01ad51aed1b061b141b221b301b3e1b8c1bda1c281c761c8e1c8e1ca51cd31cd31cea1d191d6e1d7c1d7c1e201f07";
hex"077c07cd080f09db0ac20ad40ae60b090b4b0b9d0bae0bbf0c610c9e0d5c0e0c0e900fd310fa0d5c11f6129813101349138213d113d1140a146f1543159615aa16031617162c164616511665167a16b216d9175917a717f51843185b187418c218f91907191519301945195d19761984199219a019ae19fc1a4a1a981ae61afe1afe1b151b431b431b5a1b891bde1bec1bec1c901d77";
2 changes: 1 addition & 1 deletion src/generated/RainterpreterParserNPE2.pointers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
pragma solidity =0.8.25;

/// @dev Hash of the known bytecode.
bytes32 constant BYTECODE_HASH = bytes32(0x5fa6b8ac251b8099a7797d0e0455f3e86e607d41c6ef859ad8677000f952889d);
bytes32 constant BYTECODE_HASH = bytes32(0x0913de8e0b36ea4fde9df417b198d01eba3e5cb78da1c5c6bbc50bea09444023);

/// @dev Encodes the parser meta that is used to lookup word definitions.
/// The structure of the parser meta is:
Expand Down
29 changes: 22 additions & 7 deletions src/lib/op/LibAllStandardOpsNP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -125,19 +125,34 @@ library LibAllStandardOpsNP {
"Copies a value from the context. The first operand is the context column and second is the context row."
),
// These are all ordered according to how they appear in the file system.
AuthoringMetaV2("bitwise-and", "Bitwise AND the top two items on the stack."),
AuthoringMetaV2("bitwise-or", "Bitwise OR the top two items on the stack."),
AuthoringMetaV2("bitwise-count-ones", "Counts the number of binary bits set to 1 in the input."),
AuthoringMetaV2(
"bitwise-and",
"Bitwise AND the top two items on the stack. Probably does NOT do what you expect for decimal numbers."
),
AuthoringMetaV2(
"bitwise-or",
"Bitwise OR the top two items on the stack. Probably does NOT do what you expect for decimal numbers."
),
AuthoringMetaV2(
"bitwise-count-ones",
"Counts the number of binary bits set to 1 in the input. Probably does NOT do what you expect for decimal numbers."
),
AuthoringMetaV2(
"bitwise-decode",
"Decodes a value from a 256 bit value that was encoded with bitwise-encode. The first operand is the start bit and the second is the length."
"Decodes a value from a 256 bit value that was encoded with bitwise-encode. The first operand is the start bit and the second is the length. Probably does NOT do what you expect for decimal numbers."
),
AuthoringMetaV2(
"bitwise-encode",
"Encodes a value into a 256 bit value. The first operand is the start bit and the second is the length."
"Encodes a value into a 256 bit value. The first operand is the start bit and the second is the length. Probably does NOT do what you expect for decimal numbers."
),
AuthoringMetaV2(
"bitwise-shift-left",
"Shifts the input left by the number of bits specified in the operand. Probably does NOT do what you expect for decimal numbers."
),
AuthoringMetaV2(
"bitwise-shift-right",
"Shifts the input right by the number of bits specified in the operand. Probably does NOT do what you expect for decimal numbers."
),
AuthoringMetaV2("bitwise-shift-left", "Shifts the input left by the number of bits specified in the operand."),
AuthoringMetaV2("bitwise-shift-right", "Shifts the input right by the number of bits specified in the operand."),
AuthoringMetaV2(
"call",
"Calls a source by index in the same Rain bytecode. The inputs to call are copied to the top of the called stack and the outputs are copied back to the calling stack according to the LHS items. The first operand is the source index."
Expand Down
39 changes: 17 additions & 22 deletions test/abstract/OpTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -189,17 +189,16 @@ abstract contract OpTest is RainterpreterExpressionDeployerNPE2DeploymentTest {

function parseAndEval(bytes memory rainString, uint256[][] memory context)
internal
view
returns (uint256[] memory, uint256[] memory)
{
(bytes memory bytecode, uint256[] memory constants) = iParser.parse(rainString);
(IInterpreterV2 interpreterDeployer, IInterpreterStoreV2 storeDeployer, address expression, bytes memory io) =
iDeployer.deployExpression2(bytecode, constants);
(io);
bytes memory bytecode = iDeployer.parse2(rainString);

(uint256[] memory stack, uint256[] memory kvs) = interpreterDeployer.eval2(
storeDeployer,
(uint256[] memory stack, uint256[] memory kvs) = iInterpreter.eval3(
iStore,
LibNamespace.qualifyNamespace(StateNamespace.wrap(0), address(this)),
LibEncodedDispatch.encode2(expression, SourceIndexV2.wrap(0), type(uint16).max),
bytecode,
SourceIndexV2.wrap(0),
context,
new uint256[](0)
);
Expand All @@ -208,7 +207,7 @@ abstract contract OpTest is RainterpreterExpressionDeployerNPE2DeploymentTest {

/// 90%+ of the time we don't need to pass a context. This overloads a
/// simplified interface to parse and eval.
function parseAndEval(bytes memory rainString) internal returns (uint256[] memory, uint256[] memory) {
function parseAndEval(bytes memory rainString) internal view returns (uint256[] memory, uint256[] memory) {
return parseAndEval(rainString, LibContext.build(new uint256[][](0), new SignedContextV1[](0)));
}

Expand Down Expand Up @@ -252,27 +251,23 @@ abstract contract OpTest is RainterpreterExpressionDeployerNPE2DeploymentTest {
}

function checkUnhappy(bytes memory rainString, bytes memory err) internal {
(bytes memory bytecode, uint256[] memory constants) = iParser.parse(rainString);
(IInterpreterV2 interpreterDeployer, IInterpreterStoreV2 storeDeployer, address expression, bytes memory io) =
iDeployer.deployExpression2(bytecode, constants);
(io);
bytes memory bytecode = iDeployer.parse2(rainString);
vm.expectRevert(err);
(uint256[] memory stack, uint256[] memory kvs) = interpreterDeployer.eval2(
storeDeployer,
(uint256[] memory stack, uint256[] memory kvs) = iInterpreter.eval3(
iStore,
FullyQualifiedNamespace.wrap(0),
LibEncodedDispatch.encode2(expression, SourceIndexV2.wrap(0), 1),
bytecode,
SourceIndexV2.wrap(0),
LibContext.build(new uint256[][](0), new SignedContextV1[](0)),
new uint256[](0)
);
(stack, kvs);
}

function checkUnhappyDeploy(bytes memory rainString, bytes memory err) internal {
(bytes memory bytecode, uint256[] memory constants) = iParser.parse(rainString);
function checkUnhappyParse2(bytes memory rainString, bytes memory err) internal {
vm.expectRevert(err);
(IInterpreterV2 interpreterDeployer, IInterpreterStoreV2 storeDeployer, address expression, bytes memory io) =
iDeployer.deployExpression2(bytecode, constants);
(interpreterDeployer, storeDeployer, expression, io);
bytes memory bytecode = iDeployer.parse2(rainString);
(bytecode);
}

function checkUnhappyParse(bytes memory rainString, bytes memory err) internal {
Expand All @@ -285,15 +280,15 @@ abstract contract OpTest is RainterpreterExpressionDeployerNPE2DeploymentTest {
function checkBadInputs(bytes memory rainString, uint256 opIndex, uint256 calcInputs, uint256 bytecodeInputs)
internal
{
checkUnhappyDeploy(
checkUnhappyParse2(
rainString, abi.encodeWithSelector(BadOpInputsLength.selector, opIndex, calcInputs, bytecodeInputs)
);
}

function checkBadOutputs(bytes memory rainString, uint256 opIndex, uint256 calcOutputs, uint256 bytecodeOutputs)
internal
{
checkUnhappyDeploy(
checkUnhappyParse2(
rainString, abi.encodeWithSelector(BadOpOutputsLength.selector, opIndex, calcOutputs, bytecodeOutputs)
);
}
Expand Down
Loading

0 comments on commit 2a09ae6

Please sign in to comment.