Skip to content

Commit

Permalink
[EVM-Equivalence-YUL] Optimize EvmGasManager (#30)
Browse files Browse the repository at this point in the history
* Optimize EvmGasManager

* Fix typo

* Simplify consumeEvmFrame

* Update hashes
  • Loading branch information
0xVolosnikov authored Sep 12, 2024
1 parent 361d19a commit 220e983
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 115 deletions.
34 changes: 17 additions & 17 deletions system-contracts/SystemContractsHashes.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,49 @@
"contractName": "AccountCodeStorage",
"bytecodePath": "artifacts-zk/contracts-preprocessed/AccountCodeStorage.sol/AccountCodeStorage.json",
"sourceCodePath": "contracts-preprocessed/AccountCodeStorage.sol",
"bytecodeHash": "0x0100008312026f2cfaeeb3a9b626e5317d0952371b67e17e0f018f15ba89cdc2",
"bytecodeHash": "0x01000083020a788800971bd67cb136f0a108af6175f3c8ef6f2d9c310e7d439e",
"sourceCodeHash": "0xc92c3beabb281421f2d6dc9e46f9ba83b9d46f55a4c1d6799dbec2f57c92b98a"
},
{
"contractName": "BootloaderUtilities",
"bytecodePath": "artifacts-zk/contracts-preprocessed/BootloaderUtilities.sol/BootloaderUtilities.json",
"sourceCodePath": "contracts-preprocessed/BootloaderUtilities.sol",
"bytecodeHash": "0x010007df7ae2e4ece09fe9cdc03c112be3bb6ecc048a53b1409cfab089f670db",
"bytecodeHash": "0x010007df291657eef85afcf35b92cc543704bbe74310dc6567d38683717cf18c",
"sourceCodeHash": "0xed45097b2eaa4e47cd83f6feb3671d44adb49bac64c267844e76b3444605be19"
},
{
"contractName": "ComplexUpgrader",
"bytecodePath": "artifacts-zk/contracts-preprocessed/ComplexUpgrader.sol/ComplexUpgrader.json",
"sourceCodePath": "contracts-preprocessed/ComplexUpgrader.sol",
"bytecodeHash": "0x0100004dbc6880547bd7ca3dfd987979e377b0e7d16e51bf0f6b187dad05109f",
"bytecodeHash": "0x0100004d263ac56698c0afa68ee10a8cc421af316b9429e9adad0485e10625d9",
"sourceCodeHash": "0x796046a914fb676ba2bbd337b2924311ee2177ce54571c18a2c3945755c83614"
},
{
"contractName": "Compressor",
"bytecodePath": "artifacts-zk/contracts-preprocessed/Compressor.sol/Compressor.json",
"sourceCodePath": "contracts-preprocessed/Compressor.sol",
"bytecodeHash": "0x0100014f624a3e224929d6b036dbd64e3de4c68ca61d225f48c8222e09477f0f",
"bytecodeHash": "0x0100014fe4942740cf80092ba5cd49830fa5e06990d51a32aa829a344f8bafd1",
"sourceCodeHash": "0xc6f7cd8b21aae52ed3dd5083c09b438a7af142a4ecda6067c586770e8be745a5"
},
{
"contractName": "ContractDeployer",
"bytecodePath": "artifacts-zk/contracts-preprocessed/ContractDeployer.sol/ContractDeployer.json",
"sourceCodePath": "contracts-preprocessed/ContractDeployer.sol",
"bytecodeHash": "0x010007591deebd1c28aec2f09e01fca7831cf2527b6380075c55a46eb89bb3a4",
"bytecodeHash": "0x01000759870142495cc789f42b27ea071402b9e283385cd5105fd910d58cc171",
"sourceCodeHash": "0x83e503214f41dc6677a100378f48961d459750017fc03dc5b8227c2ddddeba8b"
},
{
"contractName": "Create2Factory",
"bytecodePath": "artifacts-zk/contracts-preprocessed/Create2Factory.sol/Create2Factory.json",
"sourceCodePath": "contracts-preprocessed/Create2Factory.sol",
"bytecodeHash": "0x01000049b107e75dfd4df73357da5e7efe0582849ecfd1583ea8cc415252cdf1",
"bytecodeHash": "0x010000492e5d8c096faae7fcf3cfdb29d5d6004c16e87dc3e96b037a2d029b64",
"sourceCodeHash": "0x114d9322a9ca654989f3e0b3b21f1311dbc4db84f443d054cd414f6414d84de3"
},
{
"contractName": "DefaultAccount",
"bytecodePath": "artifacts-zk/contracts-preprocessed/DefaultAccount.sol/DefaultAccount.json",
"sourceCodePath": "contracts-preprocessed/DefaultAccount.sol",
"bytecodeHash": "0x0100058d9eee51f4b9e9a9ecb7fd7e8301e90bef018c2bd913ed36e583fec8c2",
"bytecodeHash": "0x0100058da8bd884adbce2b6acd828821257f183a76f9727c360a599a86270b0e",
"sourceCodeHash": "0xb41382ac3d04739da79e438ee977b535bfb1c10b0dd4766f88b954b10d2710be"
},
{
Expand All @@ -59,63 +59,63 @@
"contractName": "EvmGasManager",
"bytecodePath": "artifacts-zk/contracts-preprocessed/EvmGasManager.sol/EvmGasManager.json",
"sourceCodePath": "contracts-preprocessed/EvmGasManager.sol",
"bytecodeHash": "0x010001015b890ae21f91d270229a4c3aa6fd6ed6b209a1bd7d64b5accebe07f5",
"sourceCodeHash": "0xea3433435888b4fc1195386adb5bf5ca86ee8e4e1472b0eaeef6e907fd4aae6a"
"bytecodeHash": "0x010000d9847779aa157e1cd7e0288bf2a631bc0a8310096bac63bf6645640a7a",
"sourceCodeHash": "0xe1db868b61a8e46fbbda9beaa40f148822395b17f8b3a33fc77506eb5e9459d0"
},
{
"contractName": "ImmutableSimulator",
"bytecodePath": "artifacts-zk/contracts-preprocessed/ImmutableSimulator.sol/ImmutableSimulator.json",
"sourceCodePath": "contracts-preprocessed/ImmutableSimulator.sol",
"bytecodeHash": "0x01000039194d946e10393e585e587a0fed12a0b67f2a44600c81c0c07bb3e10e",
"bytecodeHash": "0x010000391e2cdad250fd602dff3fcea7870ee223f808ead56a357e8b38055b6e",
"sourceCodeHash": "0x9659e69f7db09e8f60a8bb95314b1ed26afcc689851665cf27f5408122f60c98"
},
{
"contractName": "KnownCodesStorage",
"bytecodePath": "artifacts-zk/contracts-preprocessed/KnownCodesStorage.sol/KnownCodesStorage.json",
"sourceCodePath": "contracts-preprocessed/KnownCodesStorage.sol",
"bytecodeHash": "0x010000cfc53c719b7e5bec45883cf662e9a9099d016fba16bfe03b9e65f2b722",
"bytecodeHash": "0x010000cfc0360ecf7a6e9e8f8ecd9b54ee27385f9b6b232adfddce3d449f0f37",
"sourceCodeHash": "0xe07e6543ac09b86042eff5f9067df6db10a4fc039ecb56098a882ee1019c3031"
},
{
"contractName": "L1Messenger",
"bytecodePath": "artifacts-zk/contracts-preprocessed/L1Messenger.sol/L1Messenger.json",
"sourceCodePath": "contracts-preprocessed/L1Messenger.sol",
"bytecodeHash": "0x01000299800a4edde1beaefff3e07af2387d0b34f2dfbcdf4ed1d87c0888ae90",
"bytecodeHash": "0x01000299be7537c3834ed927d5312cf76ae2f3e4a767a96c92b453c228956668",
"sourceCodeHash": "0xa8768fdaac6d8804782f14e2a51bbe2b6be31dee9103b6d02d149ea8dc46eb6a"
},
{
"contractName": "L2BaseToken",
"bytecodePath": "artifacts-zk/contracts-preprocessed/L2BaseToken.sol/L2BaseToken.json",
"sourceCodePath": "contracts-preprocessed/L2BaseToken.sol",
"bytecodeHash": "0x01000103c7807def7581dcf186b53df739e3b4c5d93b13a96741e7295e2e3f02",
"bytecodeHash": "0x01000103ab63d39452895c83f53c1a7c4b4f95116aeae8a967d16bf2ee09eca7",
"sourceCodeHash": "0x8bdd2b4d0b53dba84c9f0af250bbaa2aad10b3de6747bba957f0bd3721090dfa"
},
{
"contractName": "MsgValueSimulator",
"bytecodePath": "artifacts-zk/contracts-preprocessed/MsgValueSimulator.sol/MsgValueSimulator.json",
"sourceCodePath": "contracts-preprocessed/MsgValueSimulator.sol",
"bytecodeHash": "0x0100005d99f6a76cec4466f3e1f95ee53731a64053035da02b287854a8a6a1a3",
"bytecodeHash": "0x0100005da55171be1d9fe53bc375c3cece09da020b2937bde85149adec4e5ea6",
"sourceCodeHash": "0x082f3dcbc2fe4d93706c86aae85faa683387097d1b676e7ebd00f71ee0f13b71"
},
{
"contractName": "NonceHolder",
"bytecodePath": "artifacts-zk/contracts-preprocessed/NonceHolder.sol/NonceHolder.json",
"sourceCodePath": "contracts-preprocessed/NonceHolder.sol",
"bytecodeHash": "0x010000d90192a1e931774c502cd530a5e950001e8f6fd8324c3a6767ad473ba9",
"bytecodeHash": "0x010000d99ef515521442da25a1688797f303f837646b83f14d3be3b873b60603",
"sourceCodeHash": "0xff1ab1ce15c2e54954077315618822ec8deaeaae79ba4e2899518b8713a7234e"
},
{
"contractName": "PubdataChunkPublisher",
"bytecodePath": "artifacts-zk/contracts-preprocessed/PubdataChunkPublisher.sol/PubdataChunkPublisher.json",
"sourceCodePath": "contracts-preprocessed/PubdataChunkPublisher.sol",
"bytecodeHash": "0x01000047524067c7075952b5ebc383b3848e7a914164db9e7133c436ec2fd00e",
"bytecodeHash": "0x0100004734464645db5a1801d3e7b23db3ce982ef4e0650f782abea9c7d355ec",
"sourceCodeHash": "0xd7161e2c8092cf57b43c6220bc605c0e7e540bddcde1af24e2d90f75633b098e"
},
{
"contractName": "SystemContext",
"bytecodePath": "artifacts-zk/contracts-preprocessed/SystemContext.sol/SystemContext.json",
"sourceCodePath": "contracts-preprocessed/SystemContext.sol",
"bytecodeHash": "0x010001a5a7920f9eac0b72744e73310ed60294df349fb9028ef3f5eb2dfab0aa",
"bytecodeHash": "0x010001a5a0ffa6d6f8eed05cf0305b346c93d08ab31950a1f51eef296523e60c",
"sourceCodeHash": "0xf308743981ef5cea2f7a3332b8e51695a5e47e811a63974437fc1cceee475e7a"
},
{
Expand Down
198 changes: 100 additions & 98 deletions system-contracts/contracts/EvmGasManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,84 +4,45 @@

pragma solidity ^0.8.0;

import "./libraries/Utils.sol";

Check failure on line 7 in system-contracts/contracts/EvmGasManager.sol

View workflow job for this annotation

GitHub Actions / lint

global import of path ./libraries/Utils.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)

Check failure on line 7 in system-contracts/contracts/EvmGasManager.sol

View workflow job for this annotation

GitHub Actions / lint

global import of path ./libraries/Utils.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)

Check failure on line 7 in system-contracts/contracts/EvmGasManager.sol

View workflow job for this annotation

GitHub Actions / lint

global import of path ./libraries/Utils.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)

import {ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT} from "./Constants.sol";
import {SystemContractHelper} from "./libraries/SystemContractHelper.sol";

// We consider all the contracts (including system ones) as warm.
uint160 constant PRECOMPILES_END = 0xffff;

// Denotes that passGas has been consumed
uint256 constant INF_PASS_GAS = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;

contract EvmGasManager {
// We need trust to use `storage` pointers
struct WarmAccountInfo {
bool isWarm;
}

struct SlotInfo {
bool warm;
uint256 originalValue;
}

// We dont care about the size, since none of it will be stored/published anyway.
struct EVMStackFrameInfo {
bool isStatic;
uint256 passGas;
}

// The following storage variables are not used anywhere explicitly and are just used to obtain the storage pointers
// to use the transient storage with.
mapping(address => WarmAccountInfo) private warmAccounts;
mapping(address => mapping(uint256 => SlotInfo)) private warmSlots;
EVMStackFrameInfo[] private evmStackFrames;

function tstoreWarmAccount(address account, bool isWarm) internal {
WarmAccountInfo storage ptr = warmAccounts[account];

assembly {
tstore(ptr.slot, isWarm)
}
}

function tloadWarmAccount(address account) internal returns (bool isWarm) {
WarmAccountInfo storage ptr = warmAccounts[account];

assembly {
isWarm := tload(ptr.slot)
}
}

function tstoreWarmSlot(address _account, uint256 _key, SlotInfo memory info) internal {
SlotInfo storage ptr = warmSlots[_account][_key];

bool warm = info.warm;
uint256 originalValue = info.originalValue;
// Transient storage prefixes
uint256 constant IS_ACCOUNT_EVM_PREFIX = 1 << 255;
uint256 constant IS_ACCOUNT_WARM_PREFIX = 1 << 254;
uint256 constant IS_SLOT_WARM_PREFIX = 1 << 253;
uint256 constant EVM_STACK_SLOT = 2;

contract EvmGasManager {
modifier onlySystemEvm() {
// cache use is safe since we do not support SELFDESTRUCT
uint256 transient_slot = IS_ACCOUNT_EVM_PREFIX | uint256(uint160(msg.sender));
bool isEVM;
assembly {
tstore(ptr.slot, warm)
tstore(add(ptr.slot, 1), originalValue)
isEVM := tload(transient_slot)
}
}

function tloadWarmSlot(address _account, uint256 _key) internal view returns (SlotInfo memory info) {
SlotInfo storage ptr = warmSlots[_account][_key];

bool isWarm;
uint256 originalValue;

assembly {
isWarm := tload(ptr.slot)
originalValue := tload(add(ptr.slot, 1))
if (!isEVM) {
bytes32 bytecodeHash = ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.getRawCodeHash(msg.sender);
isEVM = Utils.isCodeHashEVM(bytecodeHash);
if (isEVM) {
if (!Utils.isContractConstructing(bytecodeHash)) {
assembly {
tstore(transient_slot, isEVM)
}
}
}
}

info.warm = isWarm;
info.originalValue = originalValue;
}

modifier onlySystemEvm() {
require(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.isAccountEVM(msg.sender), "only system evm");
require(SystemContractHelper.isSystemCall(), "This method require system call flag");
require(isEVM, "only system evm");
require(SystemContractHelper.isSystemCall(), "This method requires system call flag");
_;
}

Expand All @@ -91,64 +52,105 @@ contract EvmGasManager {
function warmAccount(address account) external payable onlySystemEvm returns (bool wasWarm) {
if (uint160(account) < PRECOMPILES_END) return true;

wasWarm = tloadWarmAccount(account);
if (!wasWarm) tstoreWarmAccount(account, true);
}

function isSlotWarm(uint256 _slot) external view returns (bool) {
SlotInfo storage ptr = warmSlots[msg.sender][_slot];
bool isWarm;
uint256 transient_slot = IS_ACCOUNT_WARM_PREFIX | uint256(uint160(account));

assembly {
isWarm := tload(ptr.slot)
wasWarm := tload(transient_slot)
}

return isWarm;
if (!wasWarm) {
assembly {
tstore(transient_slot, 1)
}
}
}

function warmSlot(uint256 _slot, uint256 _currentValue) external payable onlySystemEvm returns (bool, uint256) {
SlotInfo memory info = tloadWarmSlot(msg.sender, _slot);
function isSlotWarm(uint256 _slot) external view returns (bool isWarm) {
uint256 prefix = IS_SLOT_WARM_PREFIX | uint256(uint160(msg.sender));
uint256 transient_slot;
assembly {
mstore(0, prefix)
mstore(0x20, _slot)
transient_slot := keccak256(0, 64)
}

if (info.warm) {
return (true, info.originalValue);
assembly {
isWarm := tload(transient_slot)
}
}

info.warm = true;
info.originalValue = _currentValue;
function warmSlot(
uint256 _slot,
uint256 _currentValue
) external payable onlySystemEvm returns (bool isWarm, uint256 originalValue) {
uint256 prefix = IS_SLOT_WARM_PREFIX | uint256(uint160(msg.sender));
uint256 transient_slot;
assembly {
mstore(0, prefix)
mstore(0x20, _slot)
transient_slot := keccak256(0, 64)
}

tstoreWarmSlot(msg.sender, _slot, info);
assembly {
isWarm := tload(transient_slot)
}

return (false, _currentValue);
if (isWarm) {
assembly {
originalValue := tload(add(transient_slot, 1))
}
} else {
originalValue = _currentValue;

assembly {
tstore(transient_slot, 1)
tstore(add(transient_slot, 1), originalValue)
}
}
}

/*
The flow is the following:
When conducting call:
1. caller calls to an EVM contract pushEVMFrame with the corresponding gas
2. callee calls consumeEvmFrame to get the gas & make sure that subsequent callee won't be able to read it.
3. callee sets the return gas
4. callee calls popEVMFrame to return the gas to the caller & remove the frame
2. callee calls consumeEvmFrame to get the gas and determine if a call is static
3. calleer calls popEVMFrame to remove the frame
*/

function pushEVMFrame(uint256 _passGas, bool _isStatic) external onlySystemEvm {
EVMStackFrameInfo memory frame = EVMStackFrameInfo({passGas: _passGas, isStatic: _isStatic});

evmStackFrames.push(frame);
function pushEVMFrame(uint256 passGas, bool isStatic) external onlySystemEvm {
assembly {
let stackDepth := add(tload(EVM_STACK_SLOT), 1)
tstore(EVM_STACK_SLOT, stackDepth)
let stackPointer := add(EVM_STACK_SLOT, mul(2, stackDepth))
tstore(stackPointer, passGas)
tstore(add(stackPointer, 1), isStatic)
}
}

function consumeEvmFrame() external onlySystemEvm returns (uint256 passGas, bool isStatic) {
if (evmStackFrames.length == 0) return (INF_PASS_GAS, false);

EVMStackFrameInfo memory frameInfo = evmStackFrames[evmStackFrames.length - 1];

passGas = frameInfo.passGas;
isStatic = frameInfo.isStatic;
function consumeEvmFrame() external view returns (uint256 passGas, bool isStatic) {
uint256 stackDepth;
assembly {
stackDepth := tload(EVM_STACK_SLOT)
}
if (stackDepth == 0) return (INF_PASS_GAS, false);

// Mark as used
evmStackFrames[evmStackFrames.length - 1].passGas = INF_PASS_GAS;
assembly {
let stackPointer := add(EVM_STACK_SLOT, mul(2, stackDepth))
passGas := tload(stackPointer)
isStatic := tload(add(stackPointer, 1))
}
}

function popEVMFrame() external onlySystemEvm {
evmStackFrames.pop();
uint256 stackDepth;
assembly {
stackDepth := tload(EVM_STACK_SLOT)
}
require(stackDepth != 0);
assembly {
tstore(EVM_STACK_SLOT, sub(stackDepth, 1))
}
}
}

0 comments on commit 220e983

Please sign in to comment.