From a0f8cc922f4682d11e199801e73326afe8c3fff4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 29 Nov 2022 11:33:18 +0100 Subject: [PATCH] Add optimizor club benchmark. --- test/benchmarks/OptimizorClub.sol | 782 ++++++++++++++++++++++++++++++ test/benchmarks/run.sh | 28 +- 2 files changed, 799 insertions(+), 11 deletions(-) create mode 100644 test/benchmarks/OptimizorClub.sol diff --git a/test/benchmarks/OptimizorClub.sol b/test/benchmarks/OptimizorClub.sol new file mode 100644 index 000000000..418e26a3a --- /dev/null +++ b/test/benchmarks/OptimizorClub.sol @@ -0,0 +1,782 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +// Compressed form of https://github.com/OptimizorClub/optimizor and its dependencies + +library Base64 { + bytes private constant TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + function encode(bytes memory data) internal pure returns (string memory result) { + uint256 len = data.length; + if (len == 0) return ""; + uint256 encodedLen; + unchecked { + encodedLen = 4 * ((len + 2) / 3); + } + unchecked { + result = new string(encodedLen + 32); + } + bytes memory table = TABLE; + assembly ("memory-safe") { + let tablePtr := add(table, 1) + let resultPtr := add(result, 32) + for { let i := 0 } lt(i, len) {} { + i := add(i, 3) + let input := and(mload(add(data, i)), 0xffffff) + let out := mload(add(tablePtr, and(shr(18, input), 0x3F))) + out := shl(8, out) + out := add(out, and(mload(add(tablePtr, and(shr(12, input), 0x3F))), 0xFF)) + out := shl(8, out) + out := add(out, and(mload(add(tablePtr, and(shr(6, input), 0x3F))), 0xFF)) + out := shl(8, out) + out := add(out, and(mload(add(tablePtr, and(input, 0x3F))), 0xFF)) + out := shl(224, out) + mstore(resultPtr, out) + resultPtr := add(resultPtr, 4) + } + switch mod(len, 3) + case 1 { mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) } + case 2 { mstore(sub(resultPtr, 1), shl(248, 0x3d)) } + mstore(result, encodedLen) + } + return result; + } +} +library Puretea { + function isMutating(bytes memory code) internal pure returns (bool) { + return check(code, 0xe43f0000000000000000001fffffffffffffffff0fff01ffffff00013fff0fff); + } + function isView(bytes memory code) internal pure returns (bool) { + return check(code, 0x640800000000000000000000ffffffffffffffff0fdf01ffffff00013fff0fff); + } + function isPureGlobal(bytes memory code) internal pure returns (bool) { + return check(code, 0x600800000000000000000000ffffffffffffffff0fdf01ff67ff00013fff0fff); + } + function isPureLocal(bytes memory code) internal pure returns (bool) { + return check(code, 0x600800000000000000000000ffffffffffffffff0fcf01ffffff00013fff0fff); + } + function check(bytes memory _code, uint256 _mask) internal pure returns (bool satisfied) { + assembly ("memory-safe") { + function matchesMask(mask, opcode) -> ret { + ret := and(mask, shl(opcode, 1)) + } + function isPush(opcode) -> ret { + ret := and(gt(opcode, 0x5f), lt(opcode, 0x80)) + } + function perform(mask, code) -> ret { + for { + let offset := add(code, 32) + let end := add(offset, mload(code )) + } lt(offset, end) { + offset := add(offset, 1) + } { + let opcode := byte(0, mload(offset)) + if iszero(matchesMask(mask, opcode)) { + leave + } + if isPush(opcode) { + let immLen := sub(opcode, 0x5f) + offset := add(offset, immLen) + if iszero(lt(offset, end)) { + leave + } + } + } + ret := 1 + } + satisfied := perform(_mask, _code) + } + } +} +abstract contract PurityChecker { + uint256 private constant acceptedOpcodesMask = 0x600800000000000000000000ffffffffffffffff0fdf01ff67ff00013fff0fff; + function check(address account) external view returns (bool) { + return Puretea.check(account.code, acceptedOpcodesMask); + } +} +interface IChallenge { + function run(address target, uint256 seed) external view returns (uint32); + function svg(uint256 tokenId) external view returns (string memory); + function name() external view returns (string memory); + function description() external view returns (string memory); +} +struct TokenDetails { + uint256 challengeId; + IChallenge challenge; + uint32 leaderGas; + uint32 leaderSolutionId; + address leaderSolver; + address leaderOwner; + address leaderSubmission; + uint32 gas; + uint32 solutionId; + uint32 rank; + uint32 improvementPercentage; + address solver; + address owner; + address submission; +} +library HexString { + error HexLengthInsufficient(); + bytes16 private constant ALPHABET = "0123456789abcdef"; + function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory) { + bytes memory buffer = new bytes(2 * length); + unchecked { + for (uint256 i = buffer.length; i > 0; --i) { + buffer[i - 1] = ALPHABET[value & 0xf]; + value >>= 4; + } + } + if (value != 0) revert HexLengthInsufficient(); + return string(buffer); + } + function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { + bytes memory buffer = new bytes(2 * length + 2); + buffer[0] = "0"; + buffer[1] = "x"; + unchecked { + for (uint256 i = 2 * length + 1; i > 1; --i) { + buffer[i] = ALPHABET[value & 0xf]; + value >>= 4; + } + } + if (value != 0) revert HexLengthInsufficient(); + return string(buffer); + } +} +function packTokenId(uint256 challengeId, uint32 solutionId) pure returns (uint256) { + return (challengeId << 32) | solutionId; +} +function unpackTokenId(uint256 tokenId) pure returns (uint256 challengeId, uint32 solutionId) { + challengeId = tokenId >> 32; + solutionId = uint32(tokenId); +} +library NFTSVG { + struct SVGParams { + string projectName; + string challengeName; + string solverAddr; + string challengeAddr; + uint256 gasUsed; + uint256 gasOpti; + uint256 tokenId; + uint32 rank; + uint32 participants; + string color; + uint256 x1; + uint256 y1; + uint256 x2; + uint256 y2; + uint256 x3; + uint256 y3; + } + function generateSVG(SVGParams memory params, string memory challengeSVG) + internal + pure + returns (string memory svg) + { + return string.concat( + generateSVGDefs(params), + generateSVGBorderText(params.projectName, params.challengeName, params.solverAddr, params.challengeAddr), + generateSVGCardMantle(params.challengeName, params.rank, params.participants), + generateRankBorder(params.rank), + generateSvgCurve(challengeSVG), + generateSVGPositionDataAndLocationCurve(LibString.toString(params.tokenId), params.gasUsed, params.gasOpti), + generateOptimizorIcon(), + "" + ); + } + function generateSVGDefs(SVGParams memory params) private pure returns (string memory svg) { + svg = string.concat( + '' + "" + '' + '" + ) + ) + ), + '"/>" + ) + ) + ), + '"/>" + ) + ) + ), + '" />', + '" + ) + ) + ), + '"/>' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' '' + '' + '' + '' + '' + '' + ); + } + function generateSVGBorderText( + string memory projectName, + string memory challengeName, + string memory solverAddr, + string memory challengeAddr + ) private pure returns (string memory svg) { + svg = string.concat( + '' + '', + challengeName, + unicode" • ", + challengeAddr, + '' + ' ', + challengeName, + unicode" • ", + challengeAddr, + '' + '', + projectName, + unicode" • ", + solverAddr, + '', + projectName, + unicode" • ", + solverAddr, + '' + ); + } + function generateSVGCardMantle(string memory challengeName, uint32 rank, uint32 participants) + private + pure + returns (string memory svg) + { + svg = string.concat( + '', + challengeName, + '' + "Rank ", + LibString.toString(rank), + " of ", + LibString.toString(participants), + "" + ); + } + function generateRankBorder(uint32 rank) private pure returns (string memory svg) { + string memory color; + if (rank == 1) { + color = "rgba(255,215,0,1.0)"; + } else if (rank == 2) { + color = "rgba(255,255,255,1.0)"; + } else if (rank == 3) { + color = "rgba(205,127,50,1.0)"; + } else { + color = "rgba(255,255,255,0.2)"; + } + svg = string.concat( + '' + ); + } + function generateSvgCurve(string memory challengeSVG) private pure returns (string memory svg) { + svg = string.concat('', challengeSVG, ""); + } + function generateSVGPositionDataAndLocationCurve(string memory tokenId, uint256 gasUsed, uint256 gasOpti) + private + pure + returns (string memory svg) + { + string memory gasUsedStr = LibString.toString(gasUsed); + string memory gasOptiStr = LibString.toString(gasOpti); + uint256 str1length = bytes(tokenId).length + 4; + uint256 str2length = bytes(gasUsedStr).length + 10; + uint256 str3length = bytes(gasOptiStr).length + 14; + svg = string.concat( + '' + '' '' 'ID: ', + tokenId, + "" '' '' + 'Gas used: ', + gasUsedStr, + "" '' '' + 'Improvement: ', + gasOptiStr, + "%" "" + ); + } + function generateOptimizorIcon() private pure returns (string memory svg) { + return + ''; + } + function getCircleCoord(address tokenAddress, uint256 offset, uint256 tokenId) internal pure returns (uint8) { + unchecked { + return uint8((((uint256(uint160(tokenAddress)) >> offset) & 0xFF) * tokenId) % 255); + } + } +} +library LibString { + function toString(uint256 value) internal pure returns (string memory str) { + assembly { + let newFreeMemoryPointer := add(mload(0x40), 160) + mstore(0x40, newFreeMemoryPointer) + str := sub(newFreeMemoryPointer, 32) + mstore(str, 0) + let end := str + for { let temp := value } 1 {} { + str := sub(str, 1) + mstore8(str, add(48, mod(temp, 10))) + temp := div(temp, 10) + if iszero(temp) { break } + } + let length := sub(end, str) + str := sub(str, 32) + mstore(str, length) + } + } +} +abstract contract Owned { + event OwnershipTransferred(address indexed user, address indexed newOwner); + address public owner; + modifier onlyOwner() virtual { + require(msg.sender == owner, "UNAUTHORIZED"); + _; + } + constructor(address _owner) { + owner = _owner; + emit OwnershipTransferred(address(0), _owner); + } + function transferOwnership(address newOwner) public virtual onlyOwner { + owner = newOwner; + emit OwnershipTransferred(msg.sender, newOwner); + } +} +abstract contract ERC721 { + event Transfer(address indexed from, address indexed to, uint256 indexed id); + event Approval(address indexed owner, address indexed spender, uint256 indexed id); + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + string public name; + string public symbol; + function tokenURI(uint256 id) public view virtual returns (string memory); + mapping(uint256 => address) internal _ownerOf; + mapping(address => uint256) internal _balanceOf; + function ownerOf(uint256 id) public view virtual returns (address owner) { + require((owner = _ownerOf[id]) != address(0), "NOT_MINTED"); + } + function balanceOf(address owner) public view virtual returns (uint256) { + require(owner != address(0), "ZERO_ADDRESS"); + return _balanceOf[owner]; + } + mapping(uint256 => address) public getApproved; + mapping(address => mapping(address => bool)) public isApprovedForAll; + constructor(string memory _name, string memory _symbol) { + name = _name; + symbol = _symbol; + } + function approve(address spender, uint256 id) public virtual { + address owner = _ownerOf[id]; + require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED"); + getApproved[id] = spender; + emit Approval(owner, spender, id); + } + function setApprovalForAll(address operator, bool approved) public virtual { + isApprovedForAll[msg.sender][operator] = approved; + emit ApprovalForAll(msg.sender, operator, approved); + } + function transferFrom( + address from, + address to, + uint256 id + ) public virtual { + require(from == _ownerOf[id], "WRONG_FROM"); + require(to != address(0), "INVALID_RECIPIENT"); + require( + msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id], + "NOT_AUTHORIZED" + ); + unchecked { + _balanceOf[from]--; + _balanceOf[to]++; + } + _ownerOf[id] = to; + delete getApproved[id]; + emit Transfer(from, to, id); + } + function safeTransferFrom( + address from, + address to, + uint256 id + ) public virtual { + transferFrom(from, to, id); + require( + to.code.length == 0 || + ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") == + ERC721TokenReceiver.onERC721Received.selector, + "UNSAFE_RECIPIENT" + ); + } + function safeTransferFrom( + address from, + address to, + uint256 id, + bytes calldata data + ) public virtual { + transferFrom(from, to, id); + require( + to.code.length == 0 || + ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) == + ERC721TokenReceiver.onERC721Received.selector, + "UNSAFE_RECIPIENT" + ); + } + function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { + return + interfaceId == 0x01ffc9a7 || + interfaceId == 0x80ac58cd || + interfaceId == 0x5b5e139f; + } + function _mint(address to, uint256 id) internal virtual { + require(to != address(0), "INVALID_RECIPIENT"); + require(_ownerOf[id] == address(0), "ALREADY_MINTED"); + unchecked { + _balanceOf[to]++; + } + _ownerOf[id] = to; + emit Transfer(address(0), to, id); + } + function _burn(uint256 id) internal virtual { + address owner = _ownerOf[id]; + require(owner != address(0), "NOT_MINTED"); + unchecked { + _balanceOf[owner]--; + } + delete _ownerOf[id]; + delete getApproved[id]; + emit Transfer(owner, address(0), id); + } + function _safeMint(address to, uint256 id) internal virtual { + _mint(to, id); + require( + to.code.length == 0 || + ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") == + ERC721TokenReceiver.onERC721Received.selector, + "UNSAFE_RECIPIENT" + ); + } + function _safeMint( + address to, + uint256 id, + bytes memory data + ) internal virtual { + _mint(to, id); + require( + to.code.length == 0 || + ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) == + ERC721TokenReceiver.onERC721Received.selector, + "UNSAFE_RECIPIENT" + ); + } +} +abstract contract ERC721TokenReceiver { + function onERC721Received( + address, + address, + uint256, + bytes calldata + ) external virtual returns (bytes4) { + return ERC721TokenReceiver.onERC721Received.selector; + } +} +abstract contract OptimizorNFT is ERC721 { + error InvalidSolutionId(uint256 challengeId, uint32 solutionId); + error ChallengeNotFound(uint256 challengeId); + struct ChallengeInfo { + IChallenge target; + uint32 solutions; + } + struct ExtraDetails { + address code; + address solver; + uint32 gas; + } + mapping(uint256 => ChallengeInfo) public challenges; + mapping(uint256 => ExtraDetails) public extraDetails; + constructor() ERC721("Optimizor Club", "OC") {} + function contractURI() external pure returns (string memory) { + return + "data:application/json;base64,eyJuYW1lIjoiT3B0aW1pem9yIENsdWIiLCJkZXNjcmlwdGlvbiI6IlRoZSBPcHRpbWl6b3IgQ2x1YiBORlQgY29sbGVjdGlvbiByZXdhcmRzIGdhcyBlZmZpY2llbnQgcGVvcGxlIGFuZCBtYWNoaW5lcyBieSBtaW50aW5nIG5ldyBpdGVtcyB3aGVuZXZlciBhIGNoZWFwZXIgc29sdXRpb24gaXMgc3VibWl0dGVkIGZvciBhIGNlcnRhaW4gY2hhbGxlbmdlLiIsImltYWdlIjoiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCM2FXUjBhRDBpTVRZMkxqVTVOeUlnYUdWcFoyaDBQU0l4TWpndU9UUXhJaUIyYVdWM1FtOTRQU0l3SURBZ05EUXVNRGM1SURNMExqRXhOaUlnZUcxc2JuTTlJbWgwZEhBNkx5OTNkM2N1ZHpNdWIzSm5Mekl3TURBdmMzWm5JajQ4Y0dGMGFDQmtQU0pOTWpBdU56a3pJREV6TGpNeU1XZ3RMall5TTFZeE1pNDNhQzAyTGpJeU5YWXVOakl5YUM0Mk1qSjJMall5TTJndExqWXlNbll1TmpJeWFDMHVOakl6ZGkwdU5qSXlTREV5TGpkMk5pNHlNalZvTGpZeU1uWXVOakl6YUM0Mk1qTjJMall5TW1nMkxqSXlOWFl0TGpZeU1tZ3VOakl6ZGkwdU5qSXphQzQyTWpKMkxUWXVNakkxYUMwdU5qSXllbTB0TXk0M016VWdOUzQyTUROMkxUUXVNelU0YURFdU9EWTNkalF1TXpVNGVtMHhNeTQyT1RndE5pNHlNalZvTFRZdU9EUTRkaTQyTWpKb0xqWXlNM1l1TmpJemFDMHVOakl6ZGk0Mk1qSm9MUzQyTWpKMkxTNDJNakpvTFM0Mk1qTjJOaTR5TWpWb0xqWXlNM1l1TmpJemFDNDJNakoyTGpZeU1tZzJMamcwT0hZdExqWXlNbWd1TmpJeWRpMHhMakkwTldndExqWXlNbll0TGpZeU0wZ3lOeTR3TW5ZdE5DNHpOVGhvTXk0M016VjJMUzQyTWpKb0xqWXlNbll0TGpZeU0yZ3RMall5TW5vaUlITjBlV3hsUFNKbWFXeHNPaU0yTmpZaUx6NDhMM04yWno0PSIsImV4dGVybmFsX2xpbmsiOiJodHRwczovL29wdGltaXpvci5jbHViLyJ9"; + } + function tokenDetails(uint256 tokenId) public view returns (TokenDetails memory) { + (uint256 challengeId, uint32 solutionId) = unpackTokenId(tokenId); + if (solutionId == 0) revert InvalidSolutionId(challengeId, solutionId); + ChallengeInfo storage chl = challenges[challengeId]; + if (address(chl.target) == address(0)) revert ChallengeNotFound(challengeId); + if (solutionId > chl.solutions) revert InvalidSolutionId(challengeId, solutionId); + ExtraDetails storage details = extraDetails[tokenId]; + uint256 leaderTokenId = packTokenId(challengeId, chl.solutions); + ExtraDetails storage leaderDetails = extraDetails[leaderTokenId]; + uint32 leaderSolutionId = chl.solutions; + uint32 rank = leaderSolutionId - solutionId + 1; + uint32 percentage = 0; + if (solutionId > 1) { + ExtraDetails storage prevDetails = extraDetails[tokenId - 1]; + percentage = (details.gas * 100) / prevDetails.gas; + } + return TokenDetails({ + challengeId: challengeId, + challenge: chl.target, + leaderGas: leaderDetails.gas, + leaderSolutionId: leaderSolutionId, + leaderSolver: leaderDetails.solver, + leaderOwner: _ownerOf[leaderTokenId], + leaderSubmission: leaderDetails.code, + gas: details.gas, + solutionId: solutionId, + rank: rank, + improvementPercentage: percentage, + solver: details.solver, + owner: _ownerOf[tokenId], + submission: details.code + }); + } + function tokenURI(uint256 tokenId) public view override returns (string memory) { + TokenDetails memory details = tokenDetails(tokenId); + string memory description = string.concat(details.challenge.description(), "\\n\\n", leaderboardString(tokenId)); + return string.concat( + "data:application/json;base64,", + Base64.encode( + bytes( + string.concat( + '{"name":"Optimizor Club: ', + details.challenge.name(), + '","description":"', + description, + '","attributes":', + attributesJSON(details), + ',"image":"data:image/svg+xml;base64,', + Base64.encode(bytes(svg(tokenId, details))), + '"}' + ) + ) + ) + ); + } + function leaderboard(uint256 tokenId) public view returns (address[] memory board) { + (uint256 challengeId,) = unpackTokenId(tokenId); + uint32 winners = challenges[challengeId].solutions; + board = new address[](winners); + unchecked { + for (uint32 i = 1; i <= winners; ++i) { + ExtraDetails storage details = extraDetails[packTokenId(challengeId, i)]; + board[i - 1] = details.solver; + } + } + } + function leaderboardString(uint256 tokenId) private view returns (string memory) { + address[] memory leaders = leaderboard(tokenId); + string memory leadersStr = ""; + uint256 lIdx = leaders.length; + unchecked { + for (uint256 i = 0; i < leaders.length; ++i) { + leadersStr = string.concat( + "\\n", + LibString.toString(lIdx), + ". ", + HexString.toHexString(uint256(uint160(leaders[i])), 20), + leadersStr + ); + --lIdx; + } + } + return string.concat("Leaderboard:\\n", leadersStr); + } + function attributesJSON(TokenDetails memory details) private view returns (string memory attributes) { + uint32 rank = details.rank; + attributes = string.concat( + '[{"trait_type":"Challenge","value":"', + details.challenge.name(), + '"},{"trait_type":"Gas used","value":', + LibString.toString(details.gas), + ',"display_type":"number"},{"trait_type":"Code size","value":', + LibString.toString(details.submission.code.length), + ',"display_type":"number"},{"trait_type":"Improvement percentage","value":"', + LibString.toString(details.improvementPercentage), + '%"},{"trait_type":"Solver","value":"', + HexString.toHexString(uint256(uint160(details.solver)), 20), + '"},{"trait_type":"Rank","value":', + LibString.toString(rank), + ',"display_type":"number"},{"trait_type":"Leader","value":"', + (rank == 1) ? "Yes" : "No", + '"},{"trait_type":"Top 3","value":"', + (rank <= 3) ? "Yes" : "No", + '"},{"trait_type":"Top 10","value":"', + (rank <= 10) ? "Yes" : "No", + '"}]' + ); + } + function scale(uint8 value, uint256 minOut, uint256 maxOut) private pure returns (uint256) { + unchecked { + return ((uint256(value) * (maxOut - minOut)) / 255) + minOut; + } + } + function svg(uint256 tokenId, TokenDetails memory details) private view returns (string memory) { + uint256 gradRgb = 0; + if (details.rank > 10) { + gradRgb = 0xbebebe; + } else if (details.rank > 3) { + uint256 fRank; + uint256 init = 40; + uint256 factor = 15; + unchecked { + fRank = init + details.rank * factor; + } + gradRgb = (uint256(fRank) << 16) | (uint256(fRank) << 8) | uint256(fRank); + } + NFTSVG.SVGParams memory svgParams = NFTSVG.SVGParams({ + projectName: "Optimizor Club", + challengeName: details.challenge.name(), + solverAddr: HexString.toHexString(uint256(uint160(address(details.owner))), 20), + challengeAddr: HexString.toHexString(uint256(uint160(address(details.challenge))), 20), + gasUsed: details.gas, + gasOpti: details.improvementPercentage, + tokenId: tokenId, + rank: details.rank, + participants: details.leaderSolutionId, + color: HexString.toHexStringNoPrefix(gradRgb, 3), + x1: scale(NFTSVG.getCircleCoord(address(details.challenge), 16, tokenId), 16, 274), + y1: scale(NFTSVG.getCircleCoord(address(details.solver), 16, tokenId), 100, 484), + x2: scale(NFTSVG.getCircleCoord(address(details.challenge), 32, tokenId), 16, 274), + y2: scale(NFTSVG.getCircleCoord(address(details.solver), 32, tokenId), 100, 484), + x3: scale(NFTSVG.getCircleCoord(address(details.challenge), 48, tokenId), 16, 274), + y3: scale(NFTSVG.getCircleCoord(address(details.solver), 48, tokenId), 100, 484) + }); + return NFTSVG.generateSVG(svgParams, details.challenge.svg(tokenId)); + } +} +abstract contract OptimizorAdmin is OptimizorNFT, Owned { + PurityChecker public purityChecker; + error ChallengeExists(uint256 challengeId); + event PurityCheckerUpdated(PurityChecker newPurityChecker); + event ChallengeAdded(uint256 challengeId, IChallenge); + constructor(PurityChecker _purityChecker) Owned(msg.sender) { + updatePurityChecker(_purityChecker); + } + function updatePurityChecker(PurityChecker _purityChecker) public onlyOwner { + purityChecker = _purityChecker; + emit PurityCheckerUpdated(_purityChecker); + } + function addChallenge(uint256 id, IChallenge challenge) external onlyOwner { + ChallengeInfo storage chl = challenges[id]; + if (address(chl.target) != address(0)) { + revert ChallengeExists(id); + } + chl.target = challenge; + emit ChallengeAdded(id, challenge); + } +} +uint256 constant EPOCH = 64; +contract Optimizor is OptimizorAdmin { + struct Submission { + address sender; + uint96 blockNumber; + } + mapping(bytes32 => Submission) public submissions; + error CodeAlreadySubmitted(); + error TooEarlyToChallenge(); + error InvalidRecipient(); + error CodeNotSubmitted(); + error NotPure(); + error NotOptimizor(); + constructor(PurityChecker _purityChecker) OptimizorAdmin(_purityChecker) {} + function commit(bytes32 key) external { + if (submissions[key].sender != address(0)) { + revert CodeAlreadySubmitted(); + } + submissions[key] = Submission({sender: msg.sender, blockNumber: uint96(block.number)}); + } + function challenge(uint256 id, address target, address recipient, uint256 salt) external { + ChallengeInfo storage chl = challenges[id]; + bytes32 key = keccak256(abi.encode(msg.sender, target.codehash, salt)); + if (submissions[key].blockNumber + EPOCH >= block.number) { + revert TooEarlyToChallenge(); + } + if (submissions[key].sender == address(0)) { + revert CodeNotSubmitted(); + } + if (address(chl.target) == address(0)) { + revert ChallengeNotFound(id); + } + if (recipient == address(0)) { + revert InvalidRecipient(); + } + if (!purityChecker.check(target)) { + revert NotPure(); + } + uint32 gas = chl.target.run(target, block.difficulty); + uint256 leaderTokenId = packTokenId(id, chl.solutions); + uint32 leaderGas = extraDetails[leaderTokenId].gas; + if ((leaderGas != 0) && (leaderGas <= gas)) { + revert NotOptimizor(); + } + unchecked { + ++chl.solutions; + } + uint256 tokenId = packTokenId(id, chl.solutions); + ERC721._mint(recipient, tokenId); + extraDetails[tokenId] = ExtraDetails(target, recipient, gas); + } +} \ No newline at end of file diff --git a/test/benchmarks/run.sh b/test/benchmarks/run.sh index 01573170b..89dc4e145 100755 --- a/test/benchmarks/run.sh +++ b/test/benchmarks/run.sh @@ -39,22 +39,28 @@ trap cleanup SIGINT SIGTERM solc="${SOLIDITY_BUILD_DIR}/solc/solc" benchmarks_dir="${REPO_ROOT}/test/benchmarks" -chains_sol="${benchmarks_dir}/chains.sol" time_bin_path=$(type -P time) -solc_command_legacy=("${solc}" --optimize --bin "${chains_sol}") -solc_command_via_ir=("${solc}" --via-ir --optimize --bin "${chains_sol}") +for input_file in "chains.sol" "OptimizorClub.sol" +do + input_path="${benchmarks_dir}/${input_file}" -"${time_bin_path}" --output "${result_legacy_file}" --format "%e" "${solc_command_legacy[@]}" >/dev/null -"${time_bin_path}" --output "${result_via_ir_file}" --format "%e" "${solc_command_via_ir[@]}" >/dev/null + solc_command_legacy=("${solc}" --optimize --bin "${input_path}") + solc_command_via_ir=("${solc}" --via-ir --optimize --bin "${input_path}") -time_legacy=$(<"${result_legacy_file}") -time_via_ir=$(<"${result_via_ir_file}") + "${time_bin_path}" --output "${result_legacy_file}" --format "%e" "${solc_command_legacy[@]}" >/dev/null + "${time_bin_path}" --output "${result_via_ir_file}" --format "%e" "${solc_command_via_ir[@]}" >/dev/null -echo "=======================================================" -echo "legacy pipeline took ${time_legacy} seconds to execute." -echo "via-ir pipeline took ${time_via_ir} seconds to execute." -echo "=======================================================" + time_legacy=$(<"${result_legacy_file}") + time_via_ir=$(<"${result_via_ir_file}") + + echo "=======================================================" + echo " ${input_file}" + echo "=======================================================" + echo "legacy pipeline took ${time_legacy} seconds to execute." + echo "via-ir pipeline took ${time_via_ir} seconds to execute." + echo "=======================================================" +done cleanup