Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2024 05 26 pointers gen #240

Merged
merged 18 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/git-clean.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Git is clean
on: [push]

jobs:
git-clean:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0

- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main

- run: nix develop -c i9r-prelude

- run: nix develop -c forge script ./script/BuildPointers.sol

- run: git diff --exit-code
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ cbor_metadata = false

fs_permissions = [
{ access = "read-write", path = "./meta" },
{ access = "read-write", path = "src/generated" },
{ access = "write", path = "./deployments/latest/RainterpreterParserNPE2" },
{ access = "write", path = "./deployments/latest/RainterpreterStoreNPE2" },
{ access = "write", path = "./deployments/latest/RainterpreterNPE2" },
Expand Down
232 changes: 232 additions & 0 deletions script/BuildPointers.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
// SPDX-License-Identifier: CAL
pragma solidity =0.8.25;

import {Script} from "forge-std/Script.sol";
import {RainterpreterNPE2} from "src/concrete/RainterpreterNPE2.sol";
import {RainterpreterStoreNPE2} from "src/concrete/RainterpreterStoreNPE2.sol";
import {IInterpreterV2} from "rain.interpreter.interface/interface/IInterpreterV2.sol";
import {RainterpreterParserNPE2, PARSE_META_BUILD_DEPTH} from "src/concrete/RainterpreterParserNPE2.sol";
import {
RainterpreterExpressionDeployerNPE2,
RainterpreterExpressionDeployerNPE2ConstructionConfigV2
} from "src/concrete/RainterpreterExpressionDeployerNPE2.sol";
import {LibAllStandardOpsNP, AuthoringMetaV2} from "src/lib/op/LibAllStandardOpsNP.sol";
import {LibParseMeta} from "src/lib/parse/LibParseMeta.sol";
import {EXPRESSION_DEPLOYER_NP_META_PATH} from "src/lib/constants/ExpressionDeployerNPConstants.sol";

contract BuildPointers is Script {
function filePrefix() internal pure returns (string memory) {
return string.concat(
"// THIS FILE IS AUTOGENERATED BY ./script/BuildPointers.sol\n\n",
"// This file is committed to the repository because there is a circular\n"
"// dependency between the contract and its pointers file. The contract\n"
"// needs the pointers file to exist so that it can compile, and the pointers\n"
"// file needs the contract to exist so that it can be compiled.\n\n",
"// SPDX-License-Identifier: CAL\n",
"pragma solidity =0.8.25;\n"
);
}

function pathForContract(string memory contractName) internal pure returns (string memory) {
return string.concat("src/generated/", contractName, ".pointers.sol");
}

function bytesToHex(bytes memory data) internal pure returns (string memory) {
string memory hexString = vm.toString(data);
assembly ("memory-safe") {
// Remove the leading 0x
let newHexString := add(hexString, 2)
mstore(newHexString, sub(mload(hexString), 2))
hexString := newHexString
}
return hexString;
}

function bytecodeHashConstantString(address instance) internal view returns (string memory) {
bytes32 bytecodeHash;
assembly {
bytecodeHash := extcodehash(instance)
}
return string.concat(
"\n",
"/// @dev Hash of the known bytecode.\n",
"bytes32 constant BYTECODE_HASH = bytes32(",
vm.toString(bytecodeHash),
");\n"
);
}

function interpreterFunctionPointersConstantString(IInterpreterV2 interpreter)
internal
view
returns (string memory)
{
return string.concat(
"\n",
"/// @dev The function pointers known to the interpreter for dynamic dispatch.\n",
"/// By setting these as a constant they can be inlined into the interpreter\n",
"/// and loaded at eval time for very low gas (~100) due to the compiler\n",
"/// optimising it to a single `codecopy` to build the in memory bytes array.\n",
"bytes constant OPCODE_FUNCTION_POINTERS =\n",
" hex\"",
bytesToHex(interpreter.functionPointers()),
"\";\n"
);
}

function literalParserFunctionPointersConstantString(RainterpreterParserNPE2 instance)
internal
pure
returns (string memory)
{
return string.concat(
"\n",
"/// @dev Every two bytes is a function pointer for a literal parser.\n",
"/// Literal dispatches are determined by the first byte(s) of the literal\n",
"/// rather than a full word lookup, and are done with simple conditional\n",
"/// jumps as the possibilities are limited compared to the number of words we\n" "/// have.\n",
"bytes constant LITERAL_PARSER_FUNCTION_POINTERS = hex\"",
bytesToHex(instance.buildLiteralParserFunctionPointers()),
"\";\n"
);
}

function operandHandlerFunctionPointersConstantString(RainterpreterParserNPE2 instance)
internal
pure
returns (string memory)
{
return string.concat(
"\n",
"/// @dev Every two bytes is a function pointer for an operand handler.\n",
"/// These positional indexes all map to the same indexes looked up in the parse\n",
"/// meta.\n",
"bytes constant OPERAND_HANDLER_FUNCTION_POINTERS =\n",
" hex\"",
bytesToHex(instance.buildOperandHandlerFunctionPointers()),
"\";\n"
);
}

function parseMetaConstantString(bytes memory authoringMetaBytes) internal pure returns (string memory) {
AuthoringMetaV2[] memory authoringMeta = abi.decode(authoringMetaBytes, (AuthoringMetaV2[]));
bytes memory parseMeta = LibParseMeta.buildParseMetaV2(authoringMeta, PARSE_META_BUILD_DEPTH);
return string.concat(
"\n",
"/// @dev Encodes the parser meta that is used to lookup word definitions.\n",
"/// The structure of the parser meta is:\n",
"/// - 1 byte: The depth of the bloom filters\n",
"/// - 1 byte: The hashing seed\n",
"/// - The bloom filters, each is 32 bytes long, one for each build depth.\n",
"/// - All the items for each word, each is 4 bytes long. Each item's first byte\n",
"/// is its opcode index, the remaining 3 bytes are the word fingerprint.\n",
"/// To do a lookup, the word is hashed with the seed, then the first byte of the\n",
"/// hash is compared against the bloom filter. If there is a hit then we count\n",
"/// the number of 1 bits in the bloom filter up to this item's 1 bit. We then\n",
"/// treat this a the index of the item in the items array. We then compare the\n",
"/// word fingerprint against the fingerprint of the item at this index. If the\n",
"/// fingerprints equal then we have a match, else we increment the seed and try\n",
"/// again with the next bloom filter, offsetting all the indexes by the total\n",
"/// bit count of the previous bloom filter. If we reach the end of the bloom\n",
"/// filters then we have a miss.\n",
"bytes constant PARSE_META =\n",
" hex\"",
bytesToHex(parseMeta),
"\";\n\n",
"/// @dev The build depth of the parser meta.\n",
"uint8 constant PARSE_META_BUILD_DEPTH = ",
vm.toString(PARSE_META_BUILD_DEPTH),
";\n"
);
}

function integrityFunctionPointersConstantString(RainterpreterExpressionDeployerNPE2 deployer)
internal
view
returns (string memory)
{
return string.concat(
"\n",
"/// @dev The function pointers for the integrity check fns.\n",
"bytes constant INTEGRITY_FUNCTION_POINTERS =\n",
" hex\"",
bytesToHex(deployer.integrityFunctionPointers()),
"\";\n"
);
}

function describedByMetaHashConstantString(bytes memory describedByMeta) internal pure returns (string memory) {
return string.concat(
"\n",
"/// @dev The hash of the meta that describes the contract.\n",
"bytes32 constant DESCRIBED_BY_META_HASH = bytes32(",
vm.toString(keccak256(describedByMeta)),
");\n"
);
}

function buildFileForContract(address instance, string memory contractName, string memory body) internal {
string memory path = pathForContract(contractName);

if (vm.exists(path)) {
vm.removeFile(path);
}
vm.writeFile(path, string.concat(filePrefix(), bytecodeHashConstantString(instance), body));
}

function buildRainterpreterNPE2Pointers() internal {
RainterpreterNPE2 interpreter = new RainterpreterNPE2();

buildFileForContract(
address(interpreter), "RainterpreterNPE2", interpreterFunctionPointersConstantString(interpreter)
);
}

function buildRainterpreterStoreNPE2Pointers() internal {
RainterpreterStoreNPE2 store = new RainterpreterStoreNPE2();

buildFileForContract(address(store), "RainterpreterStoreNPE2", "");
}

function buildRainterpreterParserNPE2Pointers() internal {
RainterpreterParserNPE2 parser = new RainterpreterParserNPE2();

buildFileForContract(
address(parser),
"RainterpreterParserNPE2",
string.concat(
parseMetaConstantString(LibAllStandardOpsNP.authoringMetaV2()),
operandHandlerFunctionPointersConstantString(parser),
literalParserFunctionPointersConstantString(parser)
)
);
}

function buildRainterpreterExpressionDeployerNPE2Pointers() internal {
RainterpreterNPE2 interpreter = new RainterpreterNPE2();
RainterpreterStoreNPE2 store = new RainterpreterStoreNPE2();
RainterpreterParserNPE2 parser = new RainterpreterParserNPE2();

RainterpreterExpressionDeployerNPE2 deployer = new RainterpreterExpressionDeployerNPE2(
RainterpreterExpressionDeployerNPE2ConstructionConfigV2(
address(interpreter), address(store), address(parser)
)
);

buildFileForContract(
address(deployer),
"RainterpreterExpressionDeployerNPE2",
string.concat(
describedByMetaHashConstantString(vm.readFileBinary(EXPRESSION_DEPLOYER_NP_META_PATH)),
integrityFunctionPointersConstantString(deployer)
)
);
}

function run() external {
buildRainterpreterNPE2Pointers();
buildRainterpreterStoreNPE2Pointers();
buildRainterpreterParserNPE2Pointers();
buildRainterpreterExpressionDeployerNPE2Pointers();
}
}
11 changes: 4 additions & 7 deletions src/concrete/RainterpreterExpressionDeployerNPE2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,10 @@ import {LibParse, LibParseMeta} from "../lib/parse/LibParse.sol";
import {RainterpreterNPE2, INTERPRETER_BYTECODE_HASH} from "./RainterpreterNPE2.sol";
import {PARSER_BYTECODE_HASH} from "./RainterpreterParserNPE2.sol";
import {STORE_BYTECODE_HASH} from "./RainterpreterStoreNPE2.sol";

/// @dev The function pointers for the integrity check fns.
bytes constant INTEGRITY_FUNCTION_POINTERS =
hex"0e780ef60f5b10d510df10df10e910f2110d11b311b3120f1289129610df10e910df10df10e910d510d510d510d512a012c512df10df12a010df10df129610e910df10df1296129612e912e912e912e910df10e912e910e910e910e910e910df10e910e910e910e910e912e912e912e912e910df10e910e910df10e910e910df10df10e912e912e910e912df";

/// @dev Hash of the metadata that describes the deployer (parsing).
bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0x89148873ea148e1c312c3c6cffbc5fb012194570f04ed1177db6002901e7bf38);
import {
INTEGRITY_FUNCTION_POINTERS,
DESCRIBED_BY_META_HASH
} from "../generated/RainterpreterExpressionDeployerNPE2.pointers.sol";

/// All config required to construct a `RainterpreterNPE2`.
/// @param interpreter The `IInterpreterV2` to use for evaluation. MUST match
Expand Down
14 changes: 4 additions & 10 deletions src/concrete/RainterpreterNPE2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,10 @@ import {
IInterpreterStoreV2
} from "rain.interpreter.interface/interface/IInterpreterV2.sol";
import {IInterpreterV3} from "rain.interpreter.interface/interface/unstable/IInterpreterV3.sol";

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

/// @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"0e060e570e991065114c115e1170119311d512271238124912eb132813e6149613e6151a15bc1634166d16a616f5172e1793186718ba18ce1927193b1950196a19751989199e19d619fd1a7d1acb1b191b671b7f1b981be61bf41c021c1d1c321c4a1c631c711c7f1c8d1c9b1ce91d371d851dd31deb1deb1e021e301e301e471e761ecb1ed91ed91f7d2064";
import {
BYTECODE_HASH as INTERPRETER_BYTECODE_HASH,
OPCODE_FUNCTION_POINTERS
} from "../generated/RainterpreterNPE2.pointers.sol";

/// @title RainterpreterNPE2
/// @notice Implementation of a Rainlang interpreter that is compatible with
Expand Down
44 changes: 7 additions & 37 deletions src/concrete/RainterpreterParserNPE2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,13 @@ import {LibParseLiteral} from "../lib/parse/literal/LibParseLiteral.sol";
import {LibAllStandardOpsNP} from "../lib/op/LibAllStandardOpsNP.sol";
import {LibBytes, Pointer} from "rain.solmem/lib/LibBytes.sol";
import {LibParseInterstitial} from "../lib/parse/LibParseInterstitial.sol";

/// @dev The known hash of the parser bytecode. This is used by the deployer to
/// check that it is deploying a parser that is compatible with the interpreter.
bytes32 constant PARSER_BYTECODE_HASH = bytes32(0x458b319779ec46db91b9a23ad6cef12479a4e05fa05f5c0094fa3211b1c937cd);

/// @dev Encodes the parser meta that is used to lookup word definitions.
/// The structure of the parser meta is:
/// - 1 byte: The depth of the bloom filters
/// - 1 byte: The hashing seed
/// - The bloom filters, each is 32 bytes long, one for each build depth.
/// - All the items for each word, each is 4 bytes long. Each item's first byte
/// is its opcode index, the remaining 3 bytes are the word fingerprint.
/// To do a lookup, the word is hashed with the seed, then the first byte of the
/// hash is compared against the bloom filter. If there is a hit then we count
/// the number of 1 bits in the bloom filter up to this item's 1 bit. We then
/// treat this a the index of the item in the items array. We then compare the
/// word fingerprint against the fingerprint of the item at this index. If the
/// fingerprints equal then we have a match, else we increment the seed and try
/// again with the next bloom filter, offsetting all the indexes by the total
/// bit count of the previous bloom filter. If we reach the end of the bloom
/// filters then we have a miss.
bytes constant PARSE_META =
hex"027d02482901b41410193601380a408092011324604290a201223062960011040a8900000000000000000800000008000000100000000000000000000000000000000037af1e5831ee31ff1a4c426226d999cd14d454a62287204a17ca041510bfc04d05382451258fb9fe30e745fd28dbc3c529415aff38530a92368e9cbd2f4c3a173b402c5804ea67600a2aa235164d56371805a7653edd323d0cd5a68e1e3f22703f2237031f80073412d4a5b3119bd3ec068483ae402e554c35f6dc5d23f0c0a632fef45a2da6feff1c9677b92edb58d43c742e374178613501d4fa88030cae020885d59f2b766a3e158413022a67b453194070aa2040ab0f245a53d84392737c335bcbd00ff7e3283d5a200713686f5c39bd3f5f024641b909f14a300b1ec71b27a38323349552b91d792a60425d96b7457b8cf22153f8d14433bccc0d403ded0791b7eb002ddffc0e1abd702ce98c713ac04abd1b4507bb";

/// @dev The build depth of the parser meta.
uint8 constant PARSE_META_BUILD_DEPTH = 2;

/// @dev Every two bytes is a function pointer for an operand handler. These
/// positional indexes all map to the same indexes looked up in the parse meta.
bytes constant OPERAND_HANDLER_FUNCTION_POINTERS =
hex"18d818d818d8193d19b619b619b6193d193d18d818d818d819b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619b619fb19b61acd19fb19b61acd19b619b618d81b3619b619b6";

/// @dev Every two bytes is a function pointer for a literal parser. Literal
/// dispatches are determined by the first byte(s) of the literal rather than a
/// full word lookup, and are done with simple conditional jumps as the
/// possibilities are limited compared to the number of words we have.
bytes constant LITERAL_PARSER_FUNCTION_POINTERS = hex"0f4e1216161d16f7";
import {
BYTECODE_HASH as PARSER_BYTECODE_HASH,
LITERAL_PARSER_FUNCTION_POINTERS,
OPERAND_HANDLER_FUNCTION_POINTERS,
PARSE_META,
PARSE_META_BUILD_DEPTH
} from "../generated/RainterpreterParserNPE2.pointers.sol";

/// @title RainterpreterParserNPE2
/// @dev The parser implementation.
Expand Down
13 changes: 7 additions & 6 deletions src/concrete/RainterpreterStoreNPE2.sol
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
// SPDX-License-Identifier: CAL
pragma solidity =0.8.25;

import "openzeppelin-contracts/contracts/utils/introspection/ERC165.sol";
import {ERC165} from "openzeppelin-contracts/contracts/utils/introspection/ERC165.sol";

import "rain.interpreter.interface/interface/IInterpreterStoreV2.sol";
import "rain.interpreter.interface/lib/ns/LibNamespace.sol";
import {IInterpreterStoreV2} from "rain.interpreter.interface/interface/IInterpreterStoreV2.sol";
import {
LibNamespace, FullyQualifiedNamespace, StateNamespace
} from "rain.interpreter.interface/lib/ns/LibNamespace.sol";

import {BYTECODE_HASH as STORE_BYTECODE_HASH} from "../generated/RainterpreterStoreNPE2.pointers.sol";

/// Thrown when a `set` call is made with an odd number of arguments.
error OddSetLength(uint256 length);

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

/// @title RainterpreterStore
/// @notice Simplest possible `IInterpreterStoreV2` that could work.
/// Takes key/value pairings from the input array and stores each in an internal
Expand Down
19 changes: 19 additions & 0 deletions src/generated/RainterpreterExpressionDeployerNPE2.pointers.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// THIS FILE IS AUTOGENERATED BY ./script/BuildPointers.sol

// This file is committed to the repository because there is a circular
// dependency between the contract and its pointers file. The contract
// needs the pointers file to exist so that it can compile, and the pointers
// file needs the contract to exist so that it can be compiled.

// SPDX-License-Identifier: CAL
pragma solidity =0.8.25;

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

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

/// @dev The function pointers for the integrity check fns.
bytes constant INTEGRITY_FUNCTION_POINTERS =
hex"0e780ef60f5b10d510df10df10e910f2110d11b311b3120f1289129610df10e910df10df10e910d510d510d510d512a012c512df10df12a010df10df129610e910df10df1296129612e912e912e912e910df10e912e910e910e910e910e910df10e910e910e910e910e912e912e912e912e910df10e910e910df10e910e910df10df10e912e912e910e912df";
Loading
Loading