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

chore: merge develop #48

Merged
merged 61 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
f370113
test: fix superchain erc20 invariants (#11688)
agusduha Aug 30, 2024
814c9de
feat: add createX preinstall (#29) (#11618)
agusduha Aug 30, 2024
3e68cf0
Update contracts publish job to use env vars rather than contexts (#1…
mslipper Aug 30, 2024
18f6453
op-program: Prune L1 blocks after advancing origin (#11530)
ajsutton Aug 31, 2024
96ecb2e
npmrc: delete (#11699)
tynes Aug 31, 2024
c198a89
Increase executor size on contract publisher (#11701)
mslipper Aug 31, 2024
8ab4d3d
feat(op-program): Use `PebbleDB` for `DiskKV` (#11705)
clabby Sep 2, 2024
4c211fa
Revert "feat(op-program): Use `PebbleDB` for `DiskKV` (#11705)" (#11707)
ajsutton Sep 2, 2024
9452aa6
op-service/txmgr: multiple fixes / improvements (#11614)
Sep 2, 2024
42085ce
chore(op-service): Remove `reth` receipts source (#11706)
clabby Sep 2, 2024
375b976
fix: op-challenger move.go cli command wasn't cancellable (#11716)
samlaf Sep 3, 2024
957e13d
feat: add ERC-5202 Blueprint library with initial tests (#11700)
mds1 Sep 3, 2024
5b907dc
chore: Fix lint error (#11714)
ajsutton Sep 3, 2024
669a0fb
maint: no kontrol summaries in PRs (#11725)
smartcontracts Sep 3, 2024
337749f
feat: check interface differences in CI (#11677)
smartcontracts Sep 4, 2024
72eff5f
add comment to SingularBatch.Timestamp (#11720)
zhiqiangxu Sep 4, 2024
e80d23b
op-program: Use PebbleDB for DiskKV (Reapply with fixes) (#11709)
ajsutton Sep 4, 2024
5a1a18d
fix: SuperchainWETH fuzz flake with bad sender (#11728)
smartcontracts Sep 4, 2024
f8b421b
Fix copy-paste typo (#11731)
geoknee Sep 4, 2024
e0f6e1e
maint: add interfaces for legacy contracts (#11625)
smartcontracts Sep 4, 2024
2f10fb7
Slim down contracts-bedrock package (#11723)
mslipper Sep 4, 2024
1f0d8a8
fix: properly check all interfaces (#11734)
smartcontracts Sep 4, 2024
31f408b
feat: update interface validation script to check semver (#11740)
smartcontracts Sep 4, 2024
3410109
challenger: Introduce StateConverter to abstract loading VM states (#…
ajsutton Sep 4, 2024
f63cba6
fix: flake tests (#11742)
0xDiscotech Sep 4, 2024
2272a5d
repo: bump foundry (#11746)
tynes Sep 4, 2024
3f7a8ad
Revert "challenger: Introduce StateConverter to abstract loading VM s…
clabby Sep 4, 2024
c2d0911
cannon: Run expensive tests in parallel (#11739)
Inphi Sep 5, 2024
224c5fd
op-node: sequencer should clear inconsistent asyncGossip buffer on se…
protolambda Sep 5, 2024
df4d723
Reapply "challenger: Introduce StateConverter to abstract loading VM …
ajsutton Sep 5, 2024
ec06858
contracts-bedrock: Add OPSM file I/O for superchain deployments (#11750)
mslipper Sep 5, 2024
51150d8
maint: update solidity vscode extension (#11751)
smartcontracts Sep 5, 2024
91c7ed0
op-conductor: adds raft log snapshot configs (#11745)
zhwrd Sep 5, 2024
ffd9ed4
fix: initialization tests (#11674)
smartcontracts Sep 5, 2024
e7e5229
Update VERSIONING.md (#11761)
mds1 Sep 5, 2024
f85f189
Fix contracts-bedrock-docker-publish build (#11755)
mslipper Sep 5, 2024
ac19f2f
op-node: op-node interop block verification (devnet-1 scope) (#11611)
protolambda Sep 5, 2024
67ba188
cannon: Support binary serialisation for snapshots (#11718)
ajsutton Sep 6, 2024
31444eb
op-node: sanity-check the forkchoice updates (#11780)
protolambda Sep 6, 2024
247b399
Use local logger in data source (#11756)
joshklop Sep 6, 2024
85c79a5
fix: small typo fixes to DeploySuperchain.s.sol. (#11758)
blmalone Sep 6, 2024
873a4ea
cannon: Port audit fixes (#11697)
mbaxter Sep 6, 2024
ccf9d3e
ci: pull cached artifacts for contracts tests (#11771)
smartcontracts Sep 6, 2024
afcc51a
test: more blueprint tests (#11782)
mds1 Sep 6, 2024
49a4e9f
op-chain-ops: prep / utils for interop genesis work (#11790)
protolambda Sep 6, 2024
8733626
Add Maintenance Trigger for Cascading Updates (#11619)
axelKingsley Sep 6, 2024
4562b60
Update and cleanup CODEOWNERS with expert teams (#11786)
sebastianst Sep 6, 2024
75c7326
Catch 'header not found' string in L2BlockRefByLabel (#11759)
emhane Sep 6, 2024
f954cbb
ci: speed up CI by up to 50% (#11794)
smartcontracts Sep 6, 2024
1824499
dependabot(gomod): bump github.com/prometheus/client_golang (#11764)
dependabot[bot] Sep 6, 2024
52336b4
fix(challenger): `asterisc-kona` trace type (#11789)
clabby Sep 7, 2024
fb735d6
dependabot(gomod): bump golang.org/x/crypto from 0.26.0 to 0.27.0 (#1…
dependabot[bot] Sep 7, 2024
747c9e7
Add Admin RPC for Adding New Chain Monitors (#11792)
axelKingsley Sep 7, 2024
4fbe14f
semgrep: try to fix CI timeout (#11798)
protolambda Sep 7, 2024
9170e93
maint: move existing interfaces to folders (#11749)
smartcontracts Sep 8, 2024
40750a5
op-challenger: Support binary and JSON snapshots (#11754)
ajsutton Sep 8, 2024
c27aead
Remove hardcoded entrypoint (#11801)
mslipper Sep 9, 2024
e0f8589
Allow cheatcodes on DeploySuperchain.s.sol (#11802)
mslipper Sep 9, 2024
c05f5ad
op-program: Add recent op-program versions to reproducibility check (…
ajsutton Sep 9, 2024
29dc095
Merge branch 'develop' into chore/merge-develop
0xDiscotech Sep 9, 2024
9055e3e
fix: conflicts and broken imports
0xDiscotech Sep 9, 2024
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
Prev Previous commit
Next Next commit
test: more blueprint tests (ethereum-optimism#11782)
* test: more blueprint tests

* address PR feedback
  • Loading branch information
mds1 authored Sep 6, 2024
commit afcc51a4e89d5189930ce5b6701f7ceb0b1db10d
18 changes: 11 additions & 7 deletions packages/contracts-bedrock/src/libraries/Blueprint.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ library Blueprint {

/// @notice Thrown when parsing a blueprint preamble and the preamble data is not empty.
/// We do not use the preamble data, so it's expected to be empty.
error UnexpectedPreambleData();
error UnexpectedPreambleData(bytes data);

/// @notice Thrown during deployment if the ERC version is not supported.
error UnsupportedERCVersion(uint8 version);
Expand All @@ -37,6 +37,9 @@ library Blueprint {
/// which will deploy a corresponding blueprint contract (with no data section). Based on the
/// reference implementation in https://eips.ethereum.org/EIPS/eip-5202.
function blueprintDeployerBytecode(bytes memory _initcode) internal pure returns (bytes memory) {
// Check that the initcode is not empty.
if (_initcode.length == 0) revert EmptyInitcode();

bytes memory blueprintPreamble = hex"FE7100"; // ERC-5202 preamble.
bytes memory blueprintBytecode = bytes.concat(blueprintPreamble, _initcode);

Expand Down Expand Up @@ -89,12 +92,18 @@ library Blueprint {
return Preamble(ercVersion, preambleData, initcode);
}

/// @notice Parses the code at the given `_target` as a blueprint and deploys the resulting initcode.
/// This version of `deployFrom` is used when the initcode requires no constructor arguments.
function deployFrom(address _target, bytes32 _salt) internal returns (address) {
return deployFrom(_target, _salt, new bytes(0));
}

/// @notice Parses the code at the given `_target` as a blueprint and deploys the resulting initcode
/// with the given `_data` appended, i.e. `_data` is the ABI-encoded constructor arguments.
function deployFrom(address _target, bytes32 _salt, bytes memory _data) internal returns (address newContract_) {
Preamble memory preamble = parseBlueprintPreamble(address(_target).code);
if (preamble.ercVersion != 0) revert UnsupportedERCVersion(preamble.ercVersion);
if (preamble.preambleData.length != 0) revert UnexpectedPreambleData();
if (preamble.preambleData.length != 0) revert UnexpectedPreambleData(preamble.preambleData);

bytes memory initcode = bytes.concat(preamble.initcode, _data);
assembly ("memory-safe") {
Expand All @@ -103,11 +112,6 @@ library Blueprint {
if (newContract_ == address(0)) revert DeploymentFailed();
}

/// @notice Parses the code at the given `_target` as a blueprint and deploys the resulting initcode.
function deployFrom(address _target, bytes32 _salt) internal returns (address) {
return deployFrom(_target, _salt, new bytes(0));
}

/// @notice Convert a bytes array to a uint256.
function bytesToUint(bytes memory _b) internal pure returns (uint256) {
if (_b.length > 32) revert BytesArrayTooLong();
Expand Down
236 changes: 217 additions & 19 deletions packages/contracts-bedrock/test/libraries/Blueprint.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,241 @@ pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol";
import { Blueprint } from "src/libraries/Blueprint.sol";

// Used to test that constructor args are appended properly when deploying from a blueprint.
contract ConstructorArgMock {
uint256 public x;
bytes public y;

constructor(uint256 _x, bytes memory _y) {
x = _x;
y = _y;
}
}

// Foundry cheatcodes operate on the next call, and since all library methods are internal we would
// just JUMP to them if called directly in the test. Therefore we wrap the library in a contract.
contract BlueprintHarness {
function blueprintDeployerBytecode(bytes memory _initcode) public pure returns (bytes memory) {
return Blueprint.blueprintDeployerBytecode(_initcode);
}

function parseBlueprintPreamble(bytes memory _bytecode) public pure returns (Blueprint.Preamble memory) {
return Blueprint.parseBlueprintPreamble(_bytecode);
}

function deployFrom(address _blueprint, bytes32 _salt) public returns (address) {
return Blueprint.deployFrom(_blueprint, _salt);
}

function deployFrom(address _blueprint, bytes32 _salt, bytes memory _args) public returns (address) {
return Blueprint.deployFrom(_blueprint, _salt, _args);
}

function bytesToUint(bytes memory _bytes) public pure returns (uint256) {
return Blueprint.bytesToUint(_bytes);
}
}

contract Blueprint_Test is Test {
// TODO add tests that things revert if an address has no code.
BlueprintHarness blueprint;

function setUp() public {
blueprint = new BlueprintHarness();
}

function deployWithCreate2(bytes memory _initcode, bytes32 _salt) public returns (address addr_) {
assembly ("memory-safe") {
addr_ := create2(0, add(_initcode, 0x20), mload(_initcode), _salt)
}
require(addr_ != address(0), "deployWithCreate2: deployment failed");
}

// --- We start with the test cases from ERC-5202 ---

// An example (and trivial!) blueprint contract with no data section, whose initcode is just the STOP instruction.
function test_ERC5202_trivialBlueprint_succeeds() public view {
bytes memory bytecode = hex"FE710000";
Blueprint.Preamble memory preamble = blueprint.parseBlueprintPreamble(bytecode);

assertEq(preamble.ercVersion, 0, "100");
assertEq(preamble.preambleData, hex"", "200");
assertEq(preamble.initcode, hex"00", "300");
}

// An example blueprint contract whose initcode is the trivial STOP instruction and whose data
// section contains the byte 0xFF repeated seven times.
function test_ERC5202_blueprintWithDataSection_succeeds() public view {
// Here, 0xFE71 is the magic header, 0x01 means version 0 + 1 length bit, 0x07 encodes the
// length in bytes of the data section. These are followed by the data section, and then the
// initcode. For illustration, this code with delimiters would be:
// 0xFE71|01|07|FFFFFFFFFFFFFF|00
bytes memory bytecode = hex"FE710107FFFFFFFFFFFFFF00";
Blueprint.Preamble memory preamble = blueprint.parseBlueprintPreamble(bytecode);

assertEq(preamble.ercVersion, 0, "100");
assertEq(preamble.preambleData, hex"FFFFFFFFFFFFFF", "200");
assertEq(preamble.initcode, hex"00", "300");
}

// An example blueprint whose initcode is the trivial STOP instruction and whose data section
// contains the byte 0xFF repeated 256 times.
function test_ERC5202_blueprintWithLargeDataSection_succeeds() public view {
// Delimited, this would be 0xFE71|02|0100|FF...FF|00
bytes memory bytecode =
hex"FE71020100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00";
Blueprint.Preamble memory preamble = blueprint.parseBlueprintPreamble(bytecode);

assertEq(preamble.ercVersion, 0, "100");
assertEq(preamble.preambleData.length, 256, "200");
for (uint256 i = 0; i < 256; i++) {
assertEq(preamble.preambleData[i], bytes1(0xFF), string.concat("300-", vm.toString(i)));
}
assertEq(preamble.initcode, hex"00", "400");
}

function test_roundtrip_succeeds(bytes memory _initcode) public {
// --- Now we add a generic roundtrip test ---

// Test that a roundtrip from initcode to blueprint to initcode succeeds, i.e. the invariant
// here is that `parseBlueprintPreamble(blueprintDeployerBytecode(x)) = x`.
function testFuzz_roundtrip_succeeds(bytes memory _initcode) public {
vm.assume(_initcode.length > 0);

// Convert the initcode to match the ERC-5202 blueprint format.
bytes memory blueprintInitcode = Blueprint.blueprintDeployerBytecode(_initcode);
bytes memory blueprintInitcode = blueprint.blueprintDeployerBytecode(_initcode);

// Deploy the blueprint.
address blueprintAddress;
assembly ("memory-safe") {
blueprintAddress := create2(0, add(blueprintInitcode, 0x20), mload(blueprintInitcode), 0)
}
require(blueprintAddress != address(0), "DeployImplementations: create2 failed");
address blueprintAddress = deployWithCreate2(blueprintInitcode, bytes32(0));

// Read the blueprint code from the deployed code.
bytes memory blueprintCode = address(blueprintAddress).code;

// Parse the blueprint preamble.
Blueprint.Preamble memory preamble = Blueprint.parseBlueprintPreamble(blueprintCode);
// Parse the blueprint preamble and ensure it matches the expected values.
Blueprint.Preamble memory preamble = blueprint.parseBlueprintPreamble(blueprintCode);
assertEq(preamble.ercVersion, 0, "100");
assertEq(preamble.preambleData, hex"", "200");
assertEq(preamble.initcode, _initcode, "300");
}

function test_bytesToUint_succeeds() public pure {
// --- Lastly, function-specific unit tests ---

function test_blueprintDeployerBytecode_emptyInitcode_reverts() public {
bytes memory initcode = "";
vm.expectRevert(Blueprint.EmptyInitcode.selector);
blueprint.blueprintDeployerBytecode(initcode);
}

function test_parseBlueprintPreamble_notABlueprint_reverts() public {
// Length too short.
bytes memory invalidBytecode = hex"01";
vm.expectRevert(Blueprint.NotABlueprint.selector);
blueprint.parseBlueprintPreamble(invalidBytecode);

// First byte is not 0xFE.
invalidBytecode = hex"0071";
vm.expectRevert(Blueprint.NotABlueprint.selector);
blueprint.parseBlueprintPreamble(invalidBytecode);

// Second byte is not 0x71.
invalidBytecode = hex"FE00";
vm.expectRevert(Blueprint.NotABlueprint.selector);
blueprint.parseBlueprintPreamble(invalidBytecode);
}

function test_parseBlueprintPreamble_reservedBitsSet_reverts() public {
bytes memory invalidBytecode = hex"FE7103";
vm.expectRevert(Blueprint.ReservedBitsSet.selector);
blueprint.parseBlueprintPreamble(invalidBytecode);
}

function test_parseBlueprintPreamble_emptyInitcode_reverts() public {
bytes memory invalidBytecode = hex"FE7100";
vm.expectRevert(Blueprint.EmptyInitcode.selector);
blueprint.parseBlueprintPreamble(invalidBytecode);
}

function testFuzz_deployFrom_succeeds(bytes memory _initcode, bytes32 _salt) public {
vm.assume(_initcode.length > 0);
vm.assume(_initcode[0] != 0xef); // https://eips.ethereum.org/EIPS/eip-3541

// This deployBytecode prefix is the same bytecode used in `blueprintDeployerBytecode`, and
// it ensures that whatever initcode the fuzzer generates is actually deployable.
bytes memory deployBytecode = bytes.concat(hex"61", bytes2(uint16(_initcode.length)), hex"3d81600a3d39f3");
bytes memory initcode = bytes.concat(deployBytecode, _initcode);
bytes memory blueprintInitcode = blueprint.blueprintDeployerBytecode(initcode);

// Deploy the blueprint.
address blueprintAddress = deployWithCreate2(blueprintInitcode, _salt);

// Deploy from the blueprint.
address deployedContract = Blueprint.deployFrom(blueprintAddress, _salt);

// Verify the deployment worked.
assertTrue(deployedContract != address(0), "100");
assertTrue(deployedContract.code.length > 0, "200");
assertEq(keccak256(deployedContract.code), keccak256(_initcode), "300");
}

// Here we deploy a simple mock contract to test that constructor args are appended properly.
function testFuzz_deployFrom_withConstructorArgs_succeeds(uint256 _x, bytes memory _y, bytes32 _salt) public {
bytes memory blueprintInitcode = blueprint.blueprintDeployerBytecode(type(ConstructorArgMock).creationCode);

// Deploy the blueprint.
address blueprintAddress = deployWithCreate2(blueprintInitcode, _salt);

// Deploy from the blueprint.
bytes memory args = abi.encode(_x, _y);
address deployedContract = blueprint.deployFrom(blueprintAddress, _salt, args);

// Verify the deployment worked.
assertTrue(deployedContract != address(0), "100");
assertTrue(deployedContract.code.length > 0, "200");
assertEq(keccak256(deployedContract.code), keccak256(type(ConstructorArgMock).runtimeCode), "300");
assertEq(ConstructorArgMock(deployedContract).x(), _x, "400");
assertEq(ConstructorArgMock(deployedContract).y(), _y, "500");
}

function test_deployFrom_unsupportedERCVersion_reverts() public {
bytes32 salt = bytes32(0);
address blueprintAddress = makeAddr("blueprint");

bytes memory invalidBlueprintCode = hex"FE710400"; // ercVersion = uint8(0x04 & 0xfc) >> 2 = 1
vm.etch(blueprintAddress, invalidBlueprintCode);
vm.expectRevert(abi.encodeWithSelector(Blueprint.UnsupportedERCVersion.selector, 1));
blueprint.deployFrom(blueprintAddress, salt);

invalidBlueprintCode = hex"FE71B000"; // ercVersion = uint8(0xB0 & 0xfc) >> 2 = 44
vm.etch(blueprintAddress, invalidBlueprintCode);
vm.expectRevert(abi.encodeWithSelector(Blueprint.UnsupportedERCVersion.selector, 44));
blueprint.deployFrom(blueprintAddress, salt);
}

function test_deployFrom_unexpectedPreambleData_reverts() public {
bytes32 salt = bytes32(0);
address blueprintAddress = makeAddr("blueprint");

// Create invalid blueprint code with non-empty preamble data
bytes memory invalidBlueprintCode = hex"FE7101030102030001020304";
vm.etch(blueprintAddress, invalidBlueprintCode);

// Expect revert with UnexpectedPreambleData error
vm.expectRevert(abi.encodeWithSelector(Blueprint.UnexpectedPreambleData.selector, hex"010203"));
blueprint.deployFrom(blueprintAddress, salt);
}

function test_bytesToUint_succeeds() public view {
// These test cases (and the logic for bytesToUint) are taken from forge-std.
assertEq(3, Blueprint.bytesToUint(hex"03"));
assertEq(2, Blueprint.bytesToUint(hex"02"));
assertEq(255, Blueprint.bytesToUint(hex"ff"));
assertEq(29625, Blueprint.bytesToUint(hex"73b9"));
assertEq(3, blueprint.bytesToUint(hex"03"));
assertEq(2, blueprint.bytesToUint(hex"02"));
assertEq(255, blueprint.bytesToUint(hex"ff"));
assertEq(29625, blueprint.bytesToUint(hex"73b9"));

// Additional test cases.
assertEq(0, Blueprint.bytesToUint(hex""));
assertEq(0, Blueprint.bytesToUint(hex"00"));
assertEq(14545064521499334880, Blueprint.bytesToUint(hex"c9da731e871ad8e0"));
assertEq(type(uint256).max, Blueprint.bytesToUint(bytes.concat(bytes32(type(uint256).max))));
assertEq(0, blueprint.bytesToUint(hex""));
assertEq(0, blueprint.bytesToUint(hex"00"));
assertEq(3, blueprint.bytesToUint(hex"0003"));
assertEq(3145731, blueprint.bytesToUint(hex"300003"));
assertEq(14545064521499334880, blueprint.bytesToUint(hex"c9da731e871ad8e0"));
assertEq(14545064521499334880, blueprint.bytesToUint(hex"00c9da731e871ad8e0"));
assertEq(type(uint256).max, blueprint.bytesToUint(bytes.concat(bytes32(type(uint256).max))));
}
}