Skip to content

Commit

Permalink
🎨 Extract JSON stuff from base test into a separate contract
Browse files Browse the repository at this point in the history
  • Loading branch information
KONFeature committed Feb 1, 2024
1 parent 38a6a50 commit 4f8290a
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 44 deletions.
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ cbor_metadata = false
optimize = true
via-ir = false
runs = 1000000
fs_permissions = [{ access = "write", path = "./benchmarks"}]
fs_permissions = [{ access = "read-write", path = "./gas"}]

[profile.deploy]
via-ir = true
Expand Down
18 changes: 18 additions & 0 deletions gas/validator/base.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"global": {
"disable": 0,
"enable": 0,
"fullDeployment": 0
},
"signature": {
"ko_viaKernel": 0,
"ko_viaValidator": 0,
"viaKernel": 0,
"viaValidator": 0
},
"userOp": {
"viaEntryPoint": 0,
"viaKernel": 0,
"viaValidator": 0
}
}
File renamed without changes.
2 changes: 1 addition & 1 deletion src/validator/WeightedECDSAValidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ contract WeightedECDSAValidator is EIP712, IKernelValidator {
return packValidationData(ValidAfter.wrap(0), ValidUntil.wrap(0));
}
} else if (proposal.status == ProposalStatus.Approved || passed) {
if(userOp.paymasterAndData.length == 0) {
if (userOp.paymasterAndData.length == 0) {
address signer = ECDSA.recover(ECDSA.toEthSignedMessageHash(userOpHash), userOp.signature);
if (guardian[signer][msg.sender].weight != 0) {
proposal.status = ProposalStatus.Executed;
Expand Down
54 changes: 12 additions & 42 deletions test/benchmark/BaseValidatorBenchmark.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ import {ENTRYPOINT_0_6_ADDRESS, ENTRYPOINT_0_6_BYTECODE} from "I4337/artifacts/E

import {MainnetMetering} from "gas-metering/MainnetMetering.sol";

import {JsonBenchmarkerTest} from "./JsonBenchmarker.sol";

using ERC4337Utils for IEntryPoint;
using ERC4337Utils for Kernel;

/// @dev Test contract used to perform benchmark of the differents validators
/// @author KONFeature
abstract contract BaseValidatorBenchmark is MainnetMetering, Test {
abstract contract BaseValidatorBenchmark is MainnetMetering, JsonBenchmarkerTest {
// @dev The different 'master' wallets
address private _factoryOwner;
address private _fakeKernel;
Expand All @@ -42,13 +44,6 @@ abstract contract BaseValidatorBenchmark is MainnetMetering, Test {
/// @dev The current validator we will benchmark
IKernelValidator internal _validator;

/// @dev The JSON output of the benchmark
string private _jsonOutput;
string private _currentJson;

// Global benchmark config
bool private _isWriteEnabled;

/// @dev dummy contract we will use to test user op
DummyContract private _dummyContract;

Expand All @@ -60,7 +55,8 @@ abstract contract BaseValidatorBenchmark is MainnetMetering, Test {
// Prepare for gas mettering
setUpMetering({verbose: false});

_isWriteEnabled = vm.envOr("WRITE_BENCHMARK_RESULT", false);
// Init the json write
_initJsonWriter();

_dummyContract = new DummyContract();

Expand Down Expand Up @@ -105,6 +101,11 @@ abstract contract BaseValidatorBenchmark is MainnetMetering, Test {
return abi.encodePacked(bytes4(0x00000000), signature);
}

/// @dev Get the current output file name
function _getOutputFileName() internal view virtual override returns (string memory) {
return string.concat("mainnet_", _getValidatorName());
}

/* -------------------------------------------------------------------------- */
/* Abstract methods */
/* -------------------------------------------------------------------------- */
Expand Down Expand Up @@ -138,32 +139,22 @@ abstract contract BaseValidatorBenchmark is MainnetMetering, Test {

// Run the global methods
console.log("Global:");
_currentJson = "global";
_benchmark_fullDeployment();
_benchmark_enable();
_benchmark_disable();
_addToGlobalJson("global");

// Run the user op related tests
console.log("User op:");
_benchmark_userOp_viaEntryPoint();
_benchmark_userOp_viaKernel();
_benchmark_userOp_viaValidator();
_addToGlobalJson("userOp");

// Run the signature related test
console.log("Signature:");
_benchmark_signature_viaValidator();
_benchmark_signature_viaKernel();
_benchmark_signature_ko_viaValidator();
_benchmark_signature_ko_viaKernel();
_addToGlobalJson("signature");

// Write the json output
if (_isWriteEnabled) {
string memory fileName = string.concat("./benchmarks/validator/", validatorName, ".json");
vm.writeJson(_jsonOutput, fileName);
}
}

/* -------------------------------------------------------------------------- */
Expand Down Expand Up @@ -324,7 +315,8 @@ abstract contract BaseValidatorBenchmark is MainnetMetering, Test {
/// @dev Get a dummy user op
function _getDummyUserOp() private view returns (UserOperation memory) {
bytes memory dummyCallData = abi.encodeWithSelector(DummyContract.doDummyShit.selector);
bytes memory executeCallData = abi.encodeWithSelector(IKernel.execute.selector, address(_dummyContract), 0, dummyCallData, Operation.Call);
bytes memory executeCallData =
abi.encodeWithSelector(IKernel.execute.selector, address(_dummyContract), 0, dummyCallData, Operation.Call);
return _entryPoint.fillUserOp(address(_kernel), executeCallData);
}

Expand Down Expand Up @@ -455,28 +447,6 @@ abstract contract BaseValidatorBenchmark is MainnetMetering, Test {
_addResult("signature", _testCase, _gasUsed);
}

/// @dev Add benchmark result to the json and log it
function _addResult(string memory _key, string memory _testCase, uint256 _gasUsed) private {
// Log the output
console.log("- case: %s", _testCase);
console.log(" gas : ", _gasUsed);

// Add it to the json
if (_isWriteEnabled) {
_currentJson = vm.serializeUint(_key, _testCase, _gasUsed);
}
}

/// @dev Add the current json to the output one
function _addToGlobalJson(string memory _globalTest) private {
// Add the current json to the global one
if (_isWriteEnabled) {
_jsonOutput = vm.serializeString("final", _globalTest, _currentJson);
}
// Reset the current json
_currentJson = "";
}

/// @dev Revert the state after the run of the method
modifier runInCleanState() {
vm.revertTo(_snapshot);
Expand Down
87 changes: 87 additions & 0 deletions test/benchmark/JsonBenchmarker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Test} from "forge-std/Test.sol";
import {console} from "forge-std/Console.sol";

import {Kernel} from "src/Kernel.sol";
import {KernelFactory} from "src/factory/KernelFactory.sol";
import {IKernel} from "src/interfaces/IKernel.sol";
import {IKernelValidator} from "src/interfaces/IKernelValidator.sol";
import {ValidationData} from "src/common/Types.sol";
import {Operation} from "src/common/Enums.sol";
import {LibString} from "solady/utils/LibString.sol";
import {ERC4337Utils} from "src/utils/ERC4337Utils.sol";

import {IEntryPoint} from "I4337/interfaces/IEntryPoint.sol";
import {UserOperation} from "I4337/interfaces/UserOperation.sol";
import {ENTRYPOINT_0_6_ADDRESS, ENTRYPOINT_0_6_BYTECODE} from "I4337/artifacts/EntryPoint_0_6.sol";

import {MainnetMetering} from "gas-metering/MainnetMetering.sol";

using ERC4337Utils for IEntryPoint;
using ERC4337Utils for Kernel;

/// @dev File path for the base json file (that will be copy pasted for new tests)
string constant baseJsonFilePath = "./gas/validator/base.json";

/// @dev Global test contract that will output benchmark result inside a json file
/// @notice Build to output a json like that: {"network": {"testCase": "gasUsed"}}
/// @author KONFeature
abstract contract JsonBenchmarkerTest is Test {
/// @dev Check if the json writer is enabled or not
bool private _isWriteEnabled;

/// @dev The key we will use to write stuff in our json, to parallelise a few things
string private _writerKey;

/// @dev The JSON output of the benchmark
string private _outputFilePath;

/// @dev Init the base stuff required to run the benchmark
function _initJsonWriter() internal {
_isWriteEnabled = vm.envOr("WRITE_BENCHMARK_RESULT", false);

// Early exit if write not enable
if (!_isWriteEnabled) {
return;
}

// Check if the file exist
_outputFilePath = string.concat("./gas/validator/", _getOutputFileName(), ".json");
if (!vm.exists(_outputFilePath)) {
// If not, create the initial version of it
vm.copyFile(baseJsonFilePath, _outputFilePath);
}
}

/* -------------------------------------------------------------------------- */
/* Abstract methods */
/* -------------------------------------------------------------------------- */

/// @dev Get the current output file name
function _getOutputFileName() internal view virtual returns (string memory);

/* -------------------------------------------------------------------------- */
/* Utility methods */
/* -------------------------------------------------------------------------- */

/// @dev Only execute the method if json write is enabled
modifier _onlyIfJsonWriteEnabled() {
if (_isWriteEnabled) {
_;
}
}

/// @dev Add benchmark result to the json and log it
function _addResult(string memory _key, string memory _testCase, uint256 _gasUsed)
internal
_onlyIfJsonWriteEnabled
{
// Build the json key path of the result
string memory keyPath = string.concat(".", _key, ".", _testCase);

// Add it to the json
vm.writeJson(LibString.toString(_gasUsed), _outputFilePath, keyPath);
}
}

0 comments on commit 4f8290a

Please sign in to comment.