Commit ac03c25f authored by Kelvin Fichter's avatar Kelvin Fichter

refactor: remove unused contracts

parent 3e2aa16a
...@@ -323,120 +323,5 @@ describe('Native ETH value integration tests', () => { ...@@ -323,120 +323,5 @@ describe('Native ETH value integration tests', () => {
expect(delegatedSuccess).to.be.true expect(delegatedSuccess).to.be.true
expect(delegatedReturndata).to.deep.eq(BigNumber.from(value)) expect(delegatedReturndata).to.deep.eq(BigNumber.from(value))
}) })
describe('Intrinsic gas for ovmCALL types', async () => {
let CALL_WITH_VALUE_INTRINSIC_GAS
let ValueGasMeasurer: Contract
before(async () => {
// Grab public variable from the EM
const OVM_ExecutionManager = new Contract(
await env.addressManager.getAddress('OVM_ExecutionManager'),
getContractInterface('OVM_ExecutionManager'),
env.l1Wallet.provider
)
const CALL_WITH_VALUE_INTRINSIC_GAS_BIGNUM =
await OVM_ExecutionManager.CALL_WITH_VALUE_INTRINSIC_GAS()
CALL_WITH_VALUE_INTRINSIC_GAS =
CALL_WITH_VALUE_INTRINSIC_GAS_BIGNUM.toNumber()
const Factory__ValueGasMeasurer = await ethers.getContractFactory(
'ValueGasMeasurer',
wallet
)
ValueGasMeasurer = await Factory__ValueGasMeasurer.deploy()
await ValueGasMeasurer.deployTransaction.wait()
})
it('a call with value to an empty account consumes <= the intrinsic gas including a buffer', async () => {
const value = 1
const gasLimit = 1_000_000
const minimalSendGas =
await ValueGasMeasurer.callStatic.measureGasOfTransferingEthViaCall(
ethers.constants.AddressZero,
value,
gasLimit,
{
gasLimit: 2_000_000,
}
)
const buffer = 1.2
expect(minimalSendGas * buffer).to.be.lte(CALL_WITH_VALUE_INTRINSIC_GAS)
})
it('a call with value to an reverting account consumes <= the intrinsic gas including a buffer', async () => {
// [magic deploy prefix] . [MSTORE] (will throw exception from no stack args)
const AutoRevertInitcode = '0x600D380380600D6000396000f3' + '52'
const Factory__AutoRevert = new ContractFactory(
new Interface([]),
AutoRevertInitcode,
wallet
)
const AutoRevert: Contract = await Factory__AutoRevert.deploy()
await AutoRevert.deployTransaction.wait()
const value = 1
const gasLimit = 1_000_000
// A revert, causing the ETH to be sent back, should consume the minimal possible gas for a nonzero ETH send
const minimalSendGas =
await ValueGasMeasurer.callStatic.measureGasOfTransferingEthViaCall(
AutoRevert.address,
value,
gasLimit,
{
gasLimit: 2_000_000,
}
)
const buffer = 1.2
expect(minimalSendGas * buffer).to.be.lte(CALL_WITH_VALUE_INTRINSIC_GAS)
})
it('a value call passing less than the intrinsic gas should appear to revert', async () => {
const Factory__PayableConstant: ContractFactory =
await ethers.getContractFactory('PayableConstant', wallet)
const PayableConstant: Contract =
await Factory__PayableConstant.deploy()
await PayableConstant.deployTransaction.wait()
const sendAmount = 15
const [success, returndata] =
await ValueCalls0.callStatic.sendWithDataAndGas(
PayableConstant.address,
sendAmount,
PayableConstant.interface.encodeFunctionData('returnValue'),
CALL_WITH_VALUE_INTRINSIC_GAS - 1,
{
gasLimit: 2_000_000,
}
)
expect(success).to.eq(false)
expect(returndata).to.eq('0x')
})
it('a value call which runs out of gas does not out-of-gas the parent', async () => {
const Factory__TestOOG: ContractFactory =
await ethers.getContractFactory('TestOOG', wallet)
const TestOOG: Contract = await Factory__TestOOG.deploy()
await TestOOG.deployTransaction.wait()
const sendAmount = 15
// Implicitly test that this call is not rejected
const [success, returndata] =
await ValueCalls0.callStatic.sendWithDataAndGas(
TestOOG.address,
sendAmount,
TestOOG.interface.encodeFunctionData('runOutOfGas'),
CALL_WITH_VALUE_INTRINSIC_GAS * 2,
{
gasLimit: 2_000_000,
}
)
expect(success).to.eq(false)
expect(returndata).to.eq('0x')
})
})
}) })
}) })
...@@ -314,20 +314,6 @@ func ApplyOvmStateToState(statedb *state.StateDB, stateDump *dump.OvmDump, l1XDo ...@@ -314,20 +314,6 @@ func ApplyOvmStateToState(statedb *state.StateDB, stateDump *dump.OvmDump, l1XDo
l1BridgeValue := common.BytesToHash(l1StandardBridgeAddress.Bytes()) l1BridgeValue := common.BytesToHash(l1StandardBridgeAddress.Bytes())
statedb.SetState(OVM_L2StandardBridge.Address, l1BridgeSlot, l1BridgeValue) statedb.SetState(OVM_L2StandardBridge.Address, l1BridgeSlot, l1BridgeValue)
} }
ExecutionManager, ok := stateDump.Accounts["OVM_ExecutionManager"]
if ok {
if chainID == nil {
chainID = new(big.Int)
}
log.Info("Setting ovmCHAINID in ExecutionManager", "chain-id", chainID.Uint64())
chainIdSlot := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000007")
chainIdValue := common.BytesToHash(chainID.Bytes())
statedb.SetState(ExecutionManager.Address, chainIdSlot, chainIdValue)
log.Info("Setting maxTransactionGasLimit in ExecutionManager", "gas-limit", gasLimit)
maxTxGasLimitSlot := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000004")
maxTxGasLimitValue := common.BytesToHash(new(big.Int).SetUint64(gasLimit).Bytes())
statedb.SetState(ExecutionManager.Address, maxTxGasLimitSlot, maxTxGasLimitValue)
}
OVM_SequencerFeeVault, ok := stateDump.Accounts["OVM_SequencerFeeVault"] OVM_SequencerFeeVault, ok := stateDump.Accounts["OVM_SequencerFeeVault"]
if ok { if ok {
log.Info("Setting l1FeeWallet in OVM_SequencerFeeVault", "wallet", l1FeeWalletAddress.Hex()) log.Info("Setting l1FeeWallet in OVM_SequencerFeeVault", "wallet", l1FeeWalletAddress.Hex())
...@@ -514,18 +500,6 @@ func DeveloperGenesisBlock(period uint64, faucet, l1XDomainMessengerAddress comm ...@@ -514,18 +500,6 @@ func DeveloperGenesisBlock(period uint64, faucet, l1XDomainMessengerAddress comm
if !ok { if !ok {
panic("Lib_AddressManager not in state dump") panic("Lib_AddressManager not in state dump")
} }
_, ok = stateDump.Accounts["OVM_StateManager"]
if !ok {
panic("OVM_StateManager not in state dump")
}
_, ok = stateDump.Accounts["OVM_ExecutionManager"]
if !ok {
panic("OVM_ExecutionManager not in state dump")
}
_, ok = stateDump.Accounts["OVM_SequencerEntrypoint"]
if !ok {
panic("OVM_SequencerEntrypoint not in state dump")
}
} }
config.StateDump = &stateDump config.StateDump = &stateDump
......
...@@ -96,10 +96,6 @@ describe('BatchSubmitter', () => { ...@@ -96,10 +96,6 @@ describe('BatchSubmitter', () => {
'OVM_Sequencer', 'OVM_Sequencer',
await sequencer.getAddress() await sequencer.getAddress()
) )
await AddressManager.setAddress(
'OVM_DecompressionPrecompileAddress',
predeploys.OVM_SequencerEntrypoint
)
Mock__OVM_ExecutionManager = await smockit( Mock__OVM_ExecutionManager = await smockit(
await getContractFactory('OVM_ExecutionManager') await getContractFactory('OVM_ExecutionManager')
......
...@@ -36,11 +36,6 @@ const parseEnv = () => { ...@@ -36,11 +36,6 @@ const parseEnv = () => {
'number' 'number'
), ),
ctcMaxTransactionGasLimit: ensure('MAX_TRANSACTION_GAS_LIMIT', 'number'), ctcMaxTransactionGasLimit: ensure('MAX_TRANSACTION_GAS_LIMIT', 'number'),
emMinTransactionGasLimit: ensure('MIN_TRANSACTION_GAS_LIMIT', 'number'),
emMaxtransactionGasLimit: ensure('MAX_TRANSACTION_GAS_LIMIT', 'number'),
emMaxGasPerQueuePerEpoch: ensure('MAX_GAS_PER_QUEUE_PER_EPOCH', 'number'),
emSecondsPerEpoch: ensure('ECONDS_PER_EPOCH', 'number'),
emOvmChainId: ensure('CHAIN_ID', 'number'),
sccFraudProofWindow: ensure('FRAUD_PROOF_WINDOW_SECONDS', 'number'), sccFraudProofWindow: ensure('FRAUD_PROOF_WINDOW_SECONDS', 'number'),
sccSequencerPublishWindow: ensure( sccSequencerPublishWindow: ensure(
'SEQUENCER_PUBLISH_WINDOW_SECONDS', 'SEQUENCER_PUBLISH_WINDOW_SECONDS',
...@@ -56,16 +51,10 @@ const main = async () => { ...@@ -56,16 +51,10 @@ const main = async () => {
l1BlockTimeSeconds: config.l1BlockTimeSeconds, l1BlockTimeSeconds: config.l1BlockTimeSeconds,
ctcForceInclusionPeriodSeconds: config.ctcForceInclusionPeriodSeconds, ctcForceInclusionPeriodSeconds: config.ctcForceInclusionPeriodSeconds,
ctcMaxTransactionGasLimit: config.ctcMaxTransactionGasLimit, ctcMaxTransactionGasLimit: config.ctcMaxTransactionGasLimit,
emMinTransactionGasLimit: config.emMinTransactionGasLimit,
emMaxtransactionGasLimit: config.emMaxtransactionGasLimit,
emMaxGasPerQueuePerEpoch: config.emMaxGasPerQueuePerEpoch,
emSecondsPerEpoch: config.emSecondsPerEpoch,
emOvmChainId: config.emOvmChainId,
sccFraudProofWindow: config.sccFraudProofWindow, sccFraudProofWindow: config.sccFraudProofWindow,
sccSequencerPublishWindow: config.sccFraudProofWindow, sccSequencerPublishWindow: config.sccFraudProofWindow,
ovmSequencerAddress: sequencer.address, ovmSequencerAddress: sequencer.address,
ovmProposerAddress: sequencer.address, ovmProposerAddress: sequencer.address,
ovmRelayerAddress: sequencer.address,
ovmAddressManagerOwner: deployer.address, ovmAddressManagerOwner: deployer.address,
noCompile: process.env.NO_COMPILE ? true : false, noCompile: process.env.NO_COMPILE ? true : false,
}) })
...@@ -75,7 +64,6 @@ const main = async () => { ...@@ -75,7 +64,6 @@ const main = async () => {
// get the hardhat-deploy stuff merged. Woot. // get the hardhat-deploy stuff merged. Woot.
const nicknames = { const nicknames = {
Lib_AddressManager: 'AddressManager', Lib_AddressManager: 'AddressManager',
mockOVM_BondManager: 'OVM_BondManager',
} }
const contracts: any = dirtree( const contracts: any = dirtree(
......
#!/usr/bin/env python3
# pip3 install pyevmasm
from pyevmasm import instruction_tables
#print(instruction_tables.keys())
def asm(x):
return [instruction_tables['istanbul'][i].opcode for i in x]
push_opcodes = asm(["PUSH%d" % i for i in range(1,33)])
stop_opcodes = asm(["STOP", "JUMP", "RETURN", "INVALID"])
caller_opcodes = asm(["CALLER"])
blacklist_ops = set([
"ADDRESS", "BALANCE", "BLOCKHASH",
"CALL", "CALLCODE", "CHAINID", "COINBASE",
"CREATE", "CREATE2", "DELEGATECALL", "DIFFICULTY",
"EXTCODESIZE", "EXTCODECOPY", "EXTCODEHASH",
"GASLIMIT", "GASPRICE", "NUMBER",
"ORIGIN", "REVERT", "SELFBALANCE", "SELFDESTRUCT",
"SLOAD", "SSTORE", "STATICCALL", "TIMESTAMP"])
whitelist_opcodes = []
for x in instruction_tables['istanbul']:
if x.name not in blacklist_ops:
whitelist_opcodes.append(x.opcode)
pushmask = 0
for x in push_opcodes:
pushmask |= 1 << x
stopmask = 0
for x in stop_opcodes:
stopmask |= 1 << x
stoplist = [0]*256
procmask = 0
for i in range(256):
if i in whitelist_opcodes and \
i not in push_opcodes and \
i not in stop_opcodes and \
i not in caller_opcodes:
# can skip this opcode
stoplist[i] = 1
else:
procmask |= 1 << i
# PUSH1 through PUSH4, can't skip in slow
for i in range(0x60, 0x64):
stoplist[i] = i-0x5e
rr = "uint256[8] memory opcodeSkippableBytes = [\n"
for i in range(0, 0x100, 0x20):
ret = "uint256(0x"
for j in range(i, i+0x20, 1):
ret += ("%02X" % stoplist[j])
rr += ret+"),\n"
rr = rr[:-2] + "];"
print(rr)
print("// Mask to gate opcode specific cases")
print("uint256 opcodeGateMask = ~uint256(0x%x);" % procmask)
print("// Halting opcodes")
print("uint256 opcodeHaltingMask = ~uint256(0x%x);" % stopmask)
print("// PUSH opcodes")
print("uint256 opcodePushMask = ~uint256(0x%x);" % pushmask)
...@@ -4,7 +4,6 @@ import * as path from 'path' ...@@ -4,7 +4,6 @@ import * as path from 'path'
import * as mkdirp from 'mkdirp' import * as mkdirp from 'mkdirp'
const env = process.env const env = process.env
const CHAIN_ID = env.CHAIN_ID || '420'
const GAS_PRICE_ORACLE_OWNER = const GAS_PRICE_ORACLE_OWNER =
env.GAS_PRICE_ORACLE_OWNER || '0x' + 'FF'.repeat(20) env.GAS_PRICE_ORACLE_OWNER || '0x' + 'FF'.repeat(20)
...@@ -17,9 +16,6 @@ import { RollupDeployConfig } from '../src/contract-deployment' ...@@ -17,9 +16,6 @@ import { RollupDeployConfig } from '../src/contract-deployment'
mkdirp.sync(outdir) mkdirp.sync(outdir)
const config = { const config = {
ovmGlobalContext: {
ovmCHAINID: parseInt(CHAIN_ID, 10),
},
gasPriceOracleConfig: { gasPriceOracleConfig: {
owner: GAS_PRICE_ORACLE_OWNER, owner: GAS_PRICE_ORACLE_OWNER,
initialGasPrice: 0, initialGasPrice: 0,
......
...@@ -31,7 +31,6 @@ import { ReentrancyGuardUpgradeable } from ...@@ -31,7 +31,6 @@ import { ReentrancyGuardUpgradeable } from
* from L2 onto L1. In the event that a message sent from L1 to L2 is rejected for exceeding the L2 * from L2 onto L1. In the event that a message sent from L1 to L2 is rejected for exceeding the L2
* epoch gas limit, it can be resubmitted via this contract's replay function. * epoch gas limit, it can be resubmitted via this contract's replay function.
* *
* Compiler used: solc
* Runtime target: EVM * Runtime target: EVM
*/ */
contract OVM_L1CrossDomainMessenger is contract OVM_L1CrossDomainMessenger is
...@@ -209,13 +208,12 @@ contract OVM_L1CrossDomainMessenger is ...@@ -209,13 +208,12 @@ contract OVM_L1CrossDomainMessenger is
nonce nonce
); );
address l2CrossDomainMessenger = resolve("OVM_L2CrossDomainMessenger");
_sendXDomainMessage( _sendXDomainMessage(
ovmCanonicalTransactionChain, ovmCanonicalTransactionChain,
l2CrossDomainMessenger,
xDomainCalldata, xDomainCalldata,
_gasLimit _gasLimit
); );
emit SentMessage(xDomainCalldata); emit SentMessage(xDomainCalldata);
} }
...@@ -312,12 +310,11 @@ contract OVM_L1CrossDomainMessenger is ...@@ -312,12 +310,11 @@ contract OVM_L1CrossDomainMessenger is
Lib_OVMCodec.QueueElement memory element = Lib_OVMCodec.QueueElement memory element =
iOVM_CanonicalTransactionChain(canonicalTransactionChain).getQueueElement(_queueIndex); iOVM_CanonicalTransactionChain(canonicalTransactionChain).getQueueElement(_queueIndex);
address l2CrossDomainMessenger = resolve("OVM_L2CrossDomainMessenger");
// Compute the transactionHash // Compute the transactionHash
bytes32 transactionHash = keccak256( bytes32 transactionHash = keccak256(
abi.encode( abi.encode(
address(this), address(this),
l2CrossDomainMessenger, Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER,
_gasLimit, _gasLimit,
_message _message
) )
...@@ -337,7 +334,6 @@ contract OVM_L1CrossDomainMessenger is ...@@ -337,7 +334,6 @@ contract OVM_L1CrossDomainMessenger is
_sendXDomainMessage( _sendXDomainMessage(
canonicalTransactionChain, canonicalTransactionChain,
l2CrossDomainMessenger,
xDomainCalldata, xDomainCalldata,
_gasLimit _gasLimit
); );
...@@ -419,7 +415,7 @@ contract OVM_L1CrossDomainMessenger is ...@@ -419,7 +415,7 @@ contract OVM_L1CrossDomainMessenger is
keccak256( keccak256(
abi.encodePacked( abi.encodePacked(
_xDomainCalldata, _xDomainCalldata,
resolve("OVM_L2CrossDomainMessenger") Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER
) )
), ),
uint256(0) uint256(0)
...@@ -455,20 +451,18 @@ contract OVM_L1CrossDomainMessenger is ...@@ -455,20 +451,18 @@ contract OVM_L1CrossDomainMessenger is
/** /**
* Sends a cross domain message. * Sends a cross domain message.
* @param _canonicalTransactionChain Address of the OVM_CanonicalTransactionChain instance. * @param _canonicalTransactionChain Address of the OVM_CanonicalTransactionChain instance.
* @param _l2CrossDomainMessenger Address of the OVM_L2CrossDomainMessenger instance.
* @param _message Message to send. * @param _message Message to send.
* @param _gasLimit OVM gas limit for the message. * @param _gasLimit OVM gas limit for the message.
*/ */
function _sendXDomainMessage( function _sendXDomainMessage(
address _canonicalTransactionChain, address _canonicalTransactionChain,
address _l2CrossDomainMessenger,
bytes memory _message, bytes memory _message,
uint256 _gasLimit uint256 _gasLimit
) )
internal internal
{ {
iOVM_CanonicalTransactionChain(_canonicalTransactionChain).enqueue( iOVM_CanonicalTransactionChain(_canonicalTransactionChain).enqueue(
_l2CrossDomainMessenger, Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER,
_gasLimit, _gasLimit,
_message _message
); );
......
// SPDX-License-Identifier: MIT
// @unsupported: ovm
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_L1CrossDomainMessenger } from
"../../../iOVM/bridge/messaging/iOVM_L1CrossDomainMessenger.sol";
import { iOVM_L1MultiMessageRelayer } from
"../../../iOVM/bridge/messaging/iOVM_L1MultiMessageRelayer.sol";
/* Library Imports */
import { Lib_AddressResolver } from "../../../libraries/resolver/Lib_AddressResolver.sol";
/**
* @title OVM_L1MultiMessageRelayer
* @dev The L1 Multi-Message Relayer contract is a gas efficiency optimization which enables the
* relayer to submit multiple messages in a single transaction to be relayed by the L1 Cross Domain
* Message Sender.
*
* Compiler used: solc
* Runtime target: EVM
*/
contract OVM_L1MultiMessageRelayer is iOVM_L1MultiMessageRelayer, Lib_AddressResolver {
/***************
* Constructor *
***************/
/**
* @param _libAddressManager Address of the Address Manager.
*/
constructor(
address _libAddressManager
)
Lib_AddressResolver(_libAddressManager)
{}
/**********************
* Function Modifiers *
**********************/
modifier onlyBatchRelayer() {
require(
msg.sender == resolve("OVM_L2BatchMessageRelayer"),
// solhint-disable-next-line max-line-length
"OVM_L1MultiMessageRelayer: Function can only be called by the OVM_L2BatchMessageRelayer"
);
_;
}
/********************
* Public Functions *
********************/
/**
* @notice Forwards multiple cross domain messages to the L1 Cross Domain Messenger for relaying
* @param _messages An array of L2 to L1 messages
*/
function batchRelayMessages(
L2ToL1Message[] calldata _messages
)
override
external
onlyBatchRelayer
{
iOVM_L1CrossDomainMessenger messenger = iOVM_L1CrossDomainMessenger(
resolve("Proxy__OVM_L1CrossDomainMessenger")
);
for (uint256 i = 0; i < _messages.length; i++) {
L2ToL1Message memory message = _messages[i];
messenger.relayMessage(
message.target,
message.sender,
message.message,
message.messageNonce,
message.proof
);
}
}
}
...@@ -23,7 +23,6 @@ import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.s ...@@ -23,7 +23,6 @@ import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.s
* @dev The L2 Cross Domain Messenger contract sends messages from L2 to L1, and is the entry point * @dev The L2 Cross Domain Messenger contract sends messages from L2 to L1, and is the entry point
* for L2 messages sent via the L1 Cross Domain Messenger. * for L2 messages sent via the L1 Cross Domain Messenger.
* *
* Compiler used: optimistic-solc
* Runtime target: OVM * Runtime target: OVM
*/ */
contract OVM_L2CrossDomainMessenger is contract OVM_L2CrossDomainMessenger is
......
...@@ -22,7 +22,6 @@ import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; ...@@ -22,7 +22,6 @@ import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
* tokens that are in use on L2. It synchronizes a corresponding L2 Bridge, informing it of deposits * tokens that are in use on L2. It synchronizes a corresponding L2 Bridge, informing it of deposits
* and listening to it for newly finalized withdrawals. * and listening to it for newly finalized withdrawals.
* *
* Compiler used: solc
* Runtime target: EVM * Runtime target: EVM
*/ */
contract OVM_L1StandardBridge is iOVM_L1StandardBridge, OVM_CrossDomainEnabled { contract OVM_L1StandardBridge is iOVM_L1StandardBridge, OVM_CrossDomainEnabled {
......
...@@ -24,7 +24,6 @@ import { IL2StandardERC20 } from "../../../libraries/standards/IL2StandardERC20. ...@@ -24,7 +24,6 @@ import { IL2StandardERC20 } from "../../../libraries/standards/IL2StandardERC20.
* This contract also acts as a burner of the tokens intended for withdrawal, informing the L1 * This contract also acts as a burner of the tokens intended for withdrawal, informing the L1
* bridge to release L1 funds. * bridge to release L1 funds.
* *
* Compiler used: optimistic-solc
* Runtime target: OVM * Runtime target: OVM
*/ */
contract OVM_L2StandardBridge is iOVM_L2ERC20Bridge, OVM_CrossDomainEnabled { contract OVM_L2StandardBridge is iOVM_L2ERC20Bridge, OVM_CrossDomainEnabled {
......
...@@ -10,7 +10,7 @@ import { Lib_PredeployAddresses } from "../../../libraries/constants/Lib_Predepl ...@@ -10,7 +10,7 @@ import { Lib_PredeployAddresses } from "../../../libraries/constants/Lib_Predepl
* @title OVM_L2StandardTokenFactory * @title OVM_L2StandardTokenFactory
* @dev Factory contract for creating standard L2 token representations of L1 ERC20s * @dev Factory contract for creating standard L2 token representations of L1 ERC20s
* compatible with and working on the standard bridge. * compatible with and working on the standard bridge.
* Compiler used: optimistic-solc *
* Runtime target: OVM * Runtime target: OVM
*/ */
contract OVM_L2StandardTokenFactory { contract OVM_L2StandardTokenFactory {
......
...@@ -13,9 +13,6 @@ import { iOVM_CanonicalTransactionChain } from ...@@ -13,9 +13,6 @@ import { iOVM_CanonicalTransactionChain } from
"../../iOVM/chain/iOVM_CanonicalTransactionChain.sol"; "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
import { iOVM_ChainStorageContainer } from "../../iOVM/chain/iOVM_ChainStorageContainer.sol"; import { iOVM_ChainStorageContainer } from "../../iOVM/chain/iOVM_ChainStorageContainer.sol";
/* Contract Imports */
import { OVM_ExecutionManager } from "../execution/OVM_ExecutionManager.sol";
/* External Imports */ /* External Imports */
import { Math } from "@openzeppelin/contracts/math/Math.sol"; import { Math } from "@openzeppelin/contracts/math/Math.sol";
...@@ -29,7 +26,6 @@ import { Math } from "@openzeppelin/contracts/math/Math.sol"; ...@@ -29,7 +26,6 @@ import { Math } from "@openzeppelin/contracts/math/Math.sol";
* If the Sequencer does not include an enqueued transaction within the 'force inclusion period', * If the Sequencer does not include an enqueued transaction within the 'force inclusion period',
* then any account may force it to be included by calling appendQueueBatch(). * then any account may force it to be included by calling appendQueueBatch().
* *
* Compiler used: solc
* Runtime target: EVM * Runtime target: EVM
*/ */
contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_AddressResolver { contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_AddressResolver {
...@@ -1140,9 +1136,6 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -1140,9 +1136,6 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
bool bool
) )
{ {
OVM_ExecutionManager ovmExecutionManager =
OVM_ExecutionManager(resolve("OVM_ExecutionManager"));
uint256 gasLimit = ovmExecutionManager.getMaxTransactionGasLimit();
bytes32 leafHash = _getSequencerLeafHash(_txChainElement); bytes32 leafHash = _getSequencerLeafHash(_txChainElement);
require( require(
...@@ -1157,8 +1150,8 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -1157,8 +1150,8 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
require( require(
_transaction.blockNumber == _txChainElement.blockNumber _transaction.blockNumber == _txChainElement.blockNumber
&& _transaction.timestamp == _txChainElement.timestamp && _transaction.timestamp == _txChainElement.timestamp
&& _transaction.entrypoint == resolve("OVM_DecompressionPrecompileAddress") && _transaction.entrypoint == address(0)
&& _transaction.gasLimit == gasLimit && _transaction.gasLimit == maxTransactionGasLimit
&& _transaction.l1TxOrigin == address(0) && _transaction.l1TxOrigin == address(0)
&& _transaction.l1QueueOrigin == Lib_OVMCodec.QueueOrigin.SEQUENCER_QUEUE && _transaction.l1QueueOrigin == Lib_OVMCodec.QueueOrigin.SEQUENCER_QUEUE
&& keccak256(_transaction.data) == keccak256(_txChainElement.txData), && keccak256(_transaction.data) == keccak256(_txChainElement.txData),
......
...@@ -19,7 +19,6 @@ import { iOVM_ChainStorageContainer } from "../../iOVM/chain/iOVM_ChainStorageCo ...@@ -19,7 +19,6 @@ import { iOVM_ChainStorageContainer } from "../../iOVM/chain/iOVM_ChainStorageCo
* 2. Stores queued transactions for the Canonical Transaction Chain * 2. Stores queued transactions for the Canonical Transaction Chain
* 3. Stores chain state batches for the State Commitment Chain * 3. Stores chain state batches for the State Commitment Chain
* *
* Compiler used: solc
* Runtime target: EVM * Runtime target: EVM
*/ */
contract OVM_ChainStorageContainer is iOVM_ChainStorageContainer, Lib_AddressResolver { contract OVM_ChainStorageContainer is iOVM_ChainStorageContainer, Lib_AddressResolver {
......
...@@ -8,7 +8,6 @@ import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolve ...@@ -8,7 +8,6 @@ import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolve
import { Lib_MerkleTree } from "../../libraries/utils/Lib_MerkleTree.sol"; import { Lib_MerkleTree } from "../../libraries/utils/Lib_MerkleTree.sol";
/* Interface Imports */ /* Interface Imports */
import { iOVM_FraudVerifier } from "../../iOVM/verification/iOVM_FraudVerifier.sol";
import { iOVM_StateCommitmentChain } from "../../iOVM/chain/iOVM_StateCommitmentChain.sol"; import { iOVM_StateCommitmentChain } from "../../iOVM/chain/iOVM_StateCommitmentChain.sol";
import { iOVM_CanonicalTransactionChain } from import { iOVM_CanonicalTransactionChain } from
"../../iOVM/chain/iOVM_CanonicalTransactionChain.sol"; "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
...@@ -25,7 +24,6 @@ import "@openzeppelin/contracts/math/SafeMath.sol"; ...@@ -25,7 +24,6 @@ import "@openzeppelin/contracts/math/SafeMath.sol";
* Elements here have a 1:1 correspondence with transactions in the CTC, and should be the unique * Elements here have a 1:1 correspondence with transactions in the CTC, and should be the unique
* state root calculated off-chain by applying the canonical transactions one by one. * state root calculated off-chain by applying the canonical transactions one by one.
* *
* Compiler used: solc
* Runtime target: EVM * Runtime target: EVM
*/ */
contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, Lib_AddressResolver { contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, Lib_AddressResolver {
......
// SPDX-License-Identifier: MIT
// @unsupported: ovm
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_Bytes32Utils } from "../../libraries/utils/Lib_Bytes32Utils.sol";
import { Lib_EthUtils } from "../../libraries/utils/Lib_EthUtils.sol";
import { Lib_ErrorUtils } from "../../libraries/utils/Lib_ErrorUtils.sol";
import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployAddresses.sol";
/* Interface Imports */
import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol";
import { iOVM_StateManager } from "../../iOVM/execution/iOVM_StateManager.sol";
import { iOVM_SafetyChecker } from "../../iOVM/execution/iOVM_SafetyChecker.sol";
/* Contract Imports */
import { OVM_DeployerWhitelist } from "../predeploys/OVM_DeployerWhitelist.sol";
/* External Imports */
import { Math } from "@openzeppelin/contracts/math/Math.sol";
/**
* @title OVM_ExecutionManager
* @dev The Execution Manager (EM) is the core of our OVM implementation, and provides a sandboxed
* environment allowing us to execute OVM transactions deterministically on either Layer 1 or
* Layer 2.
* The EM's run() function is the first function called during the execution of any
* transaction on L2.
* For each context-dependent EVM operation the EM has a function which implements a corresponding
* OVM operation, which will read state from the State Manager contract.
* The EM relies on the Safety Checker to verify that code deployed to Layer 2 does not contain any
* context-dependent operations.
*
* Compiler used: solc
* Runtime target: EVM
*/
contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
/********************************
* External Contract References *
********************************/
iOVM_SafetyChecker internal ovmSafetyChecker;
iOVM_StateManager internal ovmStateManager;
/*******************************
* Execution Context Variables *
*******************************/
GasMeterConfig internal gasMeterConfig;
GlobalContext internal globalContext;
TransactionContext internal transactionContext;
MessageContext internal messageContext;
TransactionRecord internal transactionRecord;
MessageRecord internal messageRecord;
/**************************
* Gas Metering Constants *
**************************/
address constant GAS_METADATA_ADDRESS = 0x06a506A506a506A506a506a506A506A506A506A5;
uint256 constant NUISANCE_GAS_SLOAD = 20000;
uint256 constant NUISANCE_GAS_SSTORE = 20000;
uint256 constant MIN_NUISANCE_GAS_PER_CONTRACT = 30000;
uint256 constant NUISANCE_GAS_PER_CONTRACT_BYTE = 100;
uint256 constant MIN_GAS_FOR_INVALID_STATE_ACCESS = 30000;
/**************************
* Native Value Constants *
**************************/
// Public so we can access and make assertions in integration tests.
uint256 public constant CALL_WITH_VALUE_INTRINSIC_GAS = 90000;
/**************************
* Default Context Values *
**************************/
uint256 constant DEFAULT_UINT256 =
0xdefa017defa017defa017defa017defa017defa017defa017defa017defa017d;
address constant DEFAULT_ADDRESS = 0xdEfa017defA017DeFA017DEfa017DeFA017DeFa0;
/*************************************
* Container Contract Address Prefix *
*************************************/
/**
* @dev The Execution Manager and State Manager each have this 30 byte prefix,
* and are uncallable.
*/
address constant CONTAINER_CONTRACT_PREFIX = 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000;
/***************
* Constructor *
***************/
/**
* @param _libAddressManager Address of the Address Manager.
*/
constructor(
address _libAddressManager,
GasMeterConfig memory _gasMeterConfig,
GlobalContext memory _globalContext
)
Lib_AddressResolver(_libAddressManager)
{
ovmSafetyChecker = iOVM_SafetyChecker(resolve("OVM_SafetyChecker"));
gasMeterConfig = _gasMeterConfig;
globalContext = _globalContext;
_resetContext();
}
/**********************
* Function Modifiers *
**********************/
/**
* Applies dynamically-sized refund to a transaction to account for the difference in execution
* between L1 and L2, so that the overall cost of the ovmOPCODE is fixed.
* @param _cost Desired gas cost for the function after the refund.
*/
modifier netGasCost(
uint256 _cost
) {
uint256 gasProvided = gasleft();
_;
uint256 gasUsed = gasProvided - gasleft();
// We want to refund everything *except* the specified cost.
if (_cost < gasUsed) {
transactionRecord.ovmGasRefund += gasUsed - _cost;
}
}
/**
* Applies a fixed-size gas refund to a transaction to account for the difference in execution
* between L1 and L2, so that the overall cost of an ovmOPCODE can be lowered.
* @param _discount Amount of gas cost to refund for the ovmOPCODE.
*/
modifier fixedGasDiscount(
uint256 _discount
) {
uint256 gasProvided = gasleft();
_;
uint256 gasUsed = gasProvided - gasleft();
// We want to refund the specified _discount, unless this risks underflow.
if (_discount < gasUsed) {
transactionRecord.ovmGasRefund += _discount;
} else {
// refund all we can without risking underflow.
transactionRecord.ovmGasRefund += gasUsed;
}
}
/**
* Makes sure we're not inside a static context.
*/
modifier notStatic() {
if (messageContext.isStatic == true) {
_revertWithFlag(RevertFlag.STATIC_VIOLATION);
}
_;
}
/************************************
* Transaction Execution Entrypoint *
************************************/
/**
* Starts the execution of a transaction via the OVM_ExecutionManager.
* @param _transaction Transaction data to be executed.
* @param _ovmStateManager iOVM_StateManager implementation providing account state.
*/
function run(
Lib_OVMCodec.Transaction memory _transaction,
address _ovmStateManager
)
override
external
returns (
bytes memory
)
{
// Make sure that run() is not re-enterable. This condition should always be satisfied
// Once run has been called once, due to the behavior of _isValidInput().
if (transactionContext.ovmNUMBER != DEFAULT_UINT256) {
return bytes("");
}
// Store our OVM_StateManager instance (significantly easier than attempting to pass the
// address around in calldata).
ovmStateManager = iOVM_StateManager(_ovmStateManager);
// Make sure this function can't be called by anyone except the owner of the
// OVM_StateManager (expected to be an OVM_StateTransitioner). We can revert here because
// this would make the `run` itself invalid.
require(
// This method may return false during fraud proofs, but always returns true in
// L2 nodes' State Manager precompile.
ovmStateManager.isAuthenticated(msg.sender),
"Only authenticated addresses in ovmStateManager can call this function"
);
// Initialize the execution context, must be initialized before we perform any gas metering
// or we'll throw a nuisance gas error.
_initContext(_transaction);
// TEMPORARY: Gas metering is disabled for minnet.
// // Check whether we need to start a new epoch, do so if necessary.
// _checkNeedsNewEpoch(_transaction.timestamp);
// Make sure the transaction's gas limit is valid. We don't revert here because we reserve
// reverts for INVALID_STATE_ACCESS.
if (_isValidInput(_transaction) == false) {
_resetContext();
return bytes("");
}
// TEMPORARY: Gas metering is disabled for minnet.
// // Check gas right before the call to get total gas consumed by OVM transaction.
// uint256 gasProvided = gasleft();
// Run the transaction, make sure to meter the gas usage.
(, bytes memory returndata) = ovmCALL(
_transaction.gasLimit - gasMeterConfig.minTransactionGasLimit,
_transaction.entrypoint,
0,
_transaction.data
);
// TEMPORARY: Gas metering is disabled for minnet.
// // Update the cumulative gas based on the amount of gas used.
// uint256 gasUsed = gasProvided - gasleft();
// _updateCumulativeGas(gasUsed, _transaction.l1QueueOrigin);
// Wipe the execution context.
_resetContext();
return returndata;
}
/******************************
* Opcodes: Execution Context *
******************************/
/**
* @notice Overrides CALLER.
* @return _CALLER Address of the CALLER within the current message context.
*/
function ovmCALLER()
override
external
view
returns (
address _CALLER
)
{
return messageContext.ovmCALLER;
}
/**
* @notice Overrides ADDRESS.
* @return _ADDRESS Active ADDRESS within the current message context.
*/
function ovmADDRESS()
override
public
view
returns (
address _ADDRESS
)
{
return messageContext.ovmADDRESS;
}
/**
* @notice Overrides CALLVALUE.
* @return _CALLVALUE Value sent along with the call according to the current message context.
*/
function ovmCALLVALUE()
override
public
view
returns (
uint256 _CALLVALUE
)
{
return messageContext.ovmCALLVALUE;
}
/**
* @notice Overrides TIMESTAMP.
* @return _TIMESTAMP Value of the TIMESTAMP within the transaction context.
*/
function ovmTIMESTAMP()
override
external
view
returns (
uint256 _TIMESTAMP
)
{
return transactionContext.ovmTIMESTAMP;
}
/**
* @notice Overrides NUMBER.
* @return _NUMBER Value of the NUMBER within the transaction context.
*/
function ovmNUMBER()
override
external
view
returns (
uint256 _NUMBER
)
{
return transactionContext.ovmNUMBER;
}
/**
* @notice Overrides GASLIMIT.
* @return _GASLIMIT Value of the block's GASLIMIT within the transaction context.
*/
function ovmGASLIMIT()
override
external
view
returns (
uint256 _GASLIMIT
)
{
return transactionContext.ovmGASLIMIT;
}
/**
* @notice Overrides CHAINID.
* @return _CHAINID Value of the chain's CHAINID within the global context.
*/
function ovmCHAINID()
override
external
view
returns (
uint256 _CHAINID
)
{
return globalContext.ovmCHAINID;
}
/*********************************
* Opcodes: L2 Execution Context *
*********************************/
/**
* @notice Specifies from which source (Sequencer or Queue) this transaction originated from.
* @return _queueOrigin Enum indicating the ovmL1QUEUEORIGIN within the current message context.
*/
function ovmL1QUEUEORIGIN()
override
external
view
returns (
Lib_OVMCodec.QueueOrigin _queueOrigin
)
{
return transactionContext.ovmL1QUEUEORIGIN;
}
/**
* @notice Specifies which L1 account, if any, sent this transaction by calling enqueue().
* @return _l1TxOrigin Address of the account which sent the tx into L2 from L1.
*/
function ovmL1TXORIGIN()
override
external
view
returns (
address _l1TxOrigin
)
{
return transactionContext.ovmL1TXORIGIN;
}
/********************
* Opcodes: Halting *
********************/
/**
* @notice Overrides REVERT.
* @param _data Bytes data to pass along with the REVERT.
*/
function ovmREVERT(
bytes memory _data
)
override
public
{
_revertWithFlag(RevertFlag.INTENTIONAL_REVERT, _data);
}
/******************************
* Opcodes: Contract Creation *
******************************/
/**
* @notice Overrides CREATE.
* @param _bytecode Code to be used to CREATE a new contract.
* @return Address of the created contract.
* @return Revert data, if and only if the creation threw an exception.
*/
function ovmCREATE(
bytes memory _bytecode
)
override
public
notStatic
fixedGasDiscount(40000)
returns (
address,
bytes memory
)
{
// Creator is always the current ADDRESS.
address creator = ovmADDRESS();
// Check that the deployer is whitelisted, or
// that arbitrary contract deployment has been enabled.
_checkDeployerAllowed(creator);
// Generate the correct CREATE address.
address contractAddress = Lib_EthUtils.getAddressForCREATE(
creator,
_getAccountNonce(creator)
);
return _createContract(
contractAddress,
_bytecode,
MessageType.ovmCREATE
);
}
/**
* @notice Overrides CREATE2.
* @param _bytecode Code to be used to CREATE2 a new contract.
* @param _salt Value used to determine the contract's address.
* @return Address of the created contract.
* @return Revert data, if and only if the creation threw an exception.
*/
function ovmCREATE2(
bytes memory _bytecode,
bytes32 _salt
)
override
external
notStatic
fixedGasDiscount(40000)
returns (
address,
bytes memory
)
{
// Creator is always the current ADDRESS.
address creator = ovmADDRESS();
// Check that the deployer is whitelisted, or
// that arbitrary contract deployment has been enabled.
_checkDeployerAllowed(creator);
// Generate the correct CREATE2 address.
address contractAddress = Lib_EthUtils.getAddressForCREATE2(
creator,
_bytecode,
_salt
);
return _createContract(
contractAddress,
_bytecode,
MessageType.ovmCREATE2
);
}
/*******************************
* Account Abstraction Opcodes *
******************************/
/**
* Retrieves the nonce of the current ovmADDRESS.
* @return _nonce Nonce of the current contract.
*/
function ovmGETNONCE()
override
external
returns (
uint256 _nonce
)
{
return _getAccountNonce(ovmADDRESS());
}
/**
* Bumps the nonce of the current ovmADDRESS by one.
*/
function ovmINCREMENTNONCE()
override
external
notStatic
{
address account = ovmADDRESS();
uint256 nonce = _getAccountNonce(account);
// Prevent overflow.
if (nonce + 1 > nonce) {
_setAccountNonce(account, nonce + 1);
}
}
/**
* Creates a new EOA contract account, for account abstraction.
* @dev Essentially functions like ovmCREATE or ovmCREATE2, but we can bypass a lot of checks
* because the contract we're creating is trusted (no need to do safety checking or to
* handle unexpected reverts). Doesn't need to return an address because the address is
* assumed to be the user's actual address.
* @param _messageHash Hash of a message signed by some user, for verification.
* @param _v Signature `v` parameter.
* @param _r Signature `r` parameter.
* @param _s Signature `s` parameter.
*/
function ovmCREATEEOA(
bytes32 _messageHash,
uint8 _v,
bytes32 _r,
bytes32 _s
)
override
public
notStatic
{
// Recover the EOA address from the message hash and signature parameters. Since we do the
// hashing in advance, we don't have handle different message hashing schemes. Even if this
// function were to return the wrong address (rather than explicitly returning the zero
// address), the rest of the transaction would simply fail (since there's no EOA account to
// actually execute the transaction).
address eoa = ecrecover(
_messageHash,
_v + 27,
_r,
_s
);
// Invalid signature is a case we proactively handle with a revert. We could alternatively
// have this function return a `success` boolean, but this is just easier.
if (eoa == address(0)) {
ovmREVERT(bytes("Signature provided for EOA contract creation is invalid."));
}
// If the user already has an EOA account, then there's no need to perform this operation.
if (_hasEmptyAccount(eoa) == false) {
return;
}
// We always need to initialize the contract with the default account values.
_initPendingAccount(eoa);
// Temporarily set the current address so it's easier to access on L2.
address prevADDRESS = messageContext.ovmADDRESS;
messageContext.ovmADDRESS = eoa;
// Creates a duplicate of the OVM_ProxyEOA located at 0x42....09. Uses the following
// "magic" prefix to deploy an exact copy of the code:
// PUSH1 0x0D # size of this prefix in bytes
// CODESIZE
// SUB # subtract prefix size from codesize
// DUP1
// PUSH1 0x0D
// PUSH1 0x00
// CODECOPY # copy everything after prefix into memory at pos 0
// PUSH1 0x00
// RETURN # return the copied code
address proxyEOA = Lib_EthUtils.createContract(abi.encodePacked(
hex"600D380380600D6000396000f3",
ovmEXTCODECOPY(
Lib_PredeployAddresses.PROXY_EOA,
0,
ovmEXTCODESIZE(Lib_PredeployAddresses.PROXY_EOA)
)
));
// Reset the address now that we're done deploying.
messageContext.ovmADDRESS = prevADDRESS;
// Commit the account with its final values.
_commitPendingAccount(
eoa,
address(proxyEOA),
keccak256(Lib_EthUtils.getCode(address(proxyEOA)))
);
_setAccountNonce(eoa, 0);
}
/*********************************
* Opcodes: Contract Interaction *
*********************************/
/**
* @notice Overrides CALL.
* @param _gasLimit Amount of gas to be passed into this call.
* @param _address Address of the contract to call.
* @param _value ETH value to pass with the call.
* @param _calldata Data to send along with the call.
* @return _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call.
*/
function ovmCALL(
uint256 _gasLimit,
address _address,
uint256 _value,
bytes memory _calldata
)
override
public
fixedGasDiscount(100000)
returns (
bool _success,
bytes memory _returndata
)
{
// CALL updates the CALLER and ADDRESS.
MessageContext memory nextMessageContext = messageContext;
nextMessageContext.ovmCALLER = nextMessageContext.ovmADDRESS;
nextMessageContext.ovmADDRESS = _address;
nextMessageContext.ovmCALLVALUE = _value;
return _callContract(
nextMessageContext,
_gasLimit,
_address,
_calldata,
MessageType.ovmCALL
);
}
/**
* @notice Overrides STATICCALL.
* @param _gasLimit Amount of gas to be passed into this call.
* @param _address Address of the contract to call.
* @param _calldata Data to send along with the call.
* @return _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call.
*/
function ovmSTATICCALL(
uint256 _gasLimit,
address _address,
bytes memory _calldata
)
override
public
fixedGasDiscount(80000)
returns (
bool _success,
bytes memory _returndata
)
{
// STATICCALL updates the CALLER, updates the ADDRESS, and runs in a static,
// valueless context.
MessageContext memory nextMessageContext = messageContext;
nextMessageContext.ovmCALLER = nextMessageContext.ovmADDRESS;
nextMessageContext.ovmADDRESS = _address;
nextMessageContext.isStatic = true;
nextMessageContext.ovmCALLVALUE = 0;
return _callContract(
nextMessageContext,
_gasLimit,
_address,
_calldata,
MessageType.ovmSTATICCALL
);
}
/**
* @notice Overrides DELEGATECALL.
* @param _gasLimit Amount of gas to be passed into this call.
* @param _address Address of the contract to call.
* @param _calldata Data to send along with the call.
* @return _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call.
*/
function ovmDELEGATECALL(
uint256 _gasLimit,
address _address,
bytes memory _calldata
)
override
public
fixedGasDiscount(40000)
returns (
bool _success,
bytes memory _returndata
)
{
// DELEGATECALL does not change anything about the message context.
MessageContext memory nextMessageContext = messageContext;
return _callContract(
nextMessageContext,
_gasLimit,
_address,
_calldata,
MessageType.ovmDELEGATECALL
);
}
/**
* @notice Legacy ovmCALL function which did not support ETH value; this maintains backwards
* compatibility.
* @param _gasLimit Amount of gas to be passed into this call.
* @param _address Address of the contract to call.
* @param _calldata Data to send along with the call.
* @return _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call.
*/
function ovmCALL(
uint256 _gasLimit,
address _address,
bytes memory _calldata
)
override
public
returns(
bool _success,
bytes memory _returndata
)
{
// Legacy ovmCALL assumed always-0 value.
return ovmCALL(
_gasLimit,
_address,
0,
_calldata
);
}
/************************************
* Opcodes: Contract Storage Access *
************************************/
/**
* @notice Overrides SLOAD.
* @param _key 32 byte key of the storage slot to load.
* @return _value 32 byte value of the requested storage slot.
*/
function ovmSLOAD(
bytes32 _key
)
override
external
netGasCost(40000)
returns (
bytes32 _value
)
{
// We always SLOAD from the storage of ADDRESS.
address contractAddress = ovmADDRESS();
return _getContractStorage(
contractAddress,
_key
);
}
/**
* @notice Overrides SSTORE.
* @param _key 32 byte key of the storage slot to set.
* @param _value 32 byte value for the storage slot.
*/
function ovmSSTORE(
bytes32 _key,
bytes32 _value
)
override
external
notStatic
netGasCost(60000)
{
// We always SSTORE to the storage of ADDRESS.
address contractAddress = ovmADDRESS();
_putContractStorage(
contractAddress,
_key,
_value
);
}
/*********************************
* Opcodes: Contract Code Access *
*********************************/
/**
* @notice Overrides EXTCODECOPY.
* @param _contract Address of the contract to copy code from.
* @param _offset Offset in bytes from the start of contract code to copy beyond.
* @param _length Total number of bytes to copy from the contract's code.
* @return _code Bytes of code copied from the requested contract.
*/
function ovmEXTCODECOPY(
address _contract,
uint256 _offset,
uint256 _length
)
override
public
returns (
bytes memory _code
)
{
return Lib_EthUtils.getCode(
_getAccountEthAddress(_contract),
_offset,
_length
);
}
/**
* @notice Overrides EXTCODESIZE.
* @param _contract Address of the contract to query the size of.
* @return _EXTCODESIZE Size of the requested contract in bytes.
*/
function ovmEXTCODESIZE(
address _contract
)
override
public
returns (
uint256 _EXTCODESIZE
)
{
return Lib_EthUtils.getCodeSize(
_getAccountEthAddress(_contract)
);
}
/**
* @notice Overrides EXTCODEHASH.
* @param _contract Address of the contract to query the hash of.
* @return _EXTCODEHASH Hash of the requested contract.
*/
function ovmEXTCODEHASH(
address _contract
)
override
external
returns (
bytes32 _EXTCODEHASH
)
{
return Lib_EthUtils.getCodeHash(
_getAccountEthAddress(_contract)
);
}
/***************************************
* Public Functions: ETH Value Opcodes *
***************************************/
/**
* @notice Overrides BALANCE.
* NOTE: In the future, this could be optimized to directly invoke EM._getContractStorage(...).
* @param _contract Address of the contract to query the OVM_ETH balance of.
* @return _BALANCE OVM_ETH balance of the requested contract.
*/
function ovmBALANCE(
address _contract
)
override
public
returns (
uint256 _BALANCE
)
{
// Easiest way to get the balance is query OVM_ETH as normal.
bytes memory balanceOfCalldata = abi.encodeWithSignature(
"balanceOf(address)",
_contract
);
// Static call because this should be a read-only query.
(bool success, bytes memory returndata) = ovmSTATICCALL(
gasleft(),
Lib_PredeployAddresses.OVM_ETH,
balanceOfCalldata
);
// All balanceOf queries should successfully return a uint, otherwise this must be an OOG.
if (!success || returndata.length != 32) {
_revertWithFlag(RevertFlag.OUT_OF_GAS);
}
// Return the decoded balance.
return abi.decode(returndata, (uint256));
}
/**
* @notice Overrides SELFBALANCE.
* @return _BALANCE OVM_ETH balance of the requesting contract.
*/
function ovmSELFBALANCE()
override
external
returns (
uint256 _BALANCE
)
{
return ovmBALANCE(ovmADDRESS());
}
/***************************************
* Public Functions: Execution Context *
***************************************/
function getMaxTransactionGasLimit()
external
view
override
returns (
uint256 _maxTransactionGasLimit
)
{
return gasMeterConfig.maxTransactionGasLimit;
}
/********************************************
* Public Functions: Deployment Whitelisting *
********************************************/
/**
* Checks whether the given address is on the whitelist to ovmCREATE/ovmCREATE2,
* and reverts if not.
* @param _deployerAddress Address attempting to deploy a contract.
*/
function _checkDeployerAllowed(
address _deployerAddress
)
internal
{
// From an OVM semantics perspective, this will appear identical to
// the deployer ovmCALLing the whitelist. This is fine--in a sense, we are forcing them to.
(bool success, bytes memory data) = ovmSTATICCALL(
gasleft(),
Lib_PredeployAddresses.DEPLOYER_WHITELIST,
abi.encodeWithSelector(
OVM_DeployerWhitelist.isDeployerAllowed.selector,
_deployerAddress
)
);
bool isAllowed = abi.decode(data, (bool));
if (!isAllowed || !success) {
_revertWithFlag(RevertFlag.CREATOR_NOT_ALLOWED);
}
}
/********************************************
* Internal Functions: Contract Interaction *
********************************************/
/**
* Creates a new contract and associates it with some contract address.
* @param _contractAddress Address to associate the created contract with.
* @param _bytecode Bytecode to be used to create the contract.
* @return Final OVM contract address.
* @return Revertdata, if and only if the creation threw an exception.
*/
function _createContract(
address _contractAddress,
bytes memory _bytecode,
MessageType _messageType
)
internal
returns (
address,
bytes memory
)
{
// We always update the nonce of the creating account, even if the creation fails.
_setAccountNonce(ovmADDRESS(), _getAccountNonce(ovmADDRESS()) + 1);
// We're stepping into a CREATE or CREATE2, so we need to update ADDRESS to point
// to the contract's associated address and CALLER to point to the previous ADDRESS.
MessageContext memory nextMessageContext = messageContext;
nextMessageContext.ovmCALLER = messageContext.ovmADDRESS;
nextMessageContext.ovmADDRESS = _contractAddress;
// Run the common logic which occurs between call-type and create-type messages,
// passing in the creation bytecode and `true` to trigger create-specific logic.
(bool success, bytes memory data) = _handleExternalMessage(
nextMessageContext,
gasleft(),
_contractAddress,
_bytecode,
_messageType
);
// Yellow paper requires that address returned is zero if the contract deployment fails.
return (
success ? _contractAddress : address(0),
data
);
}
/**
* Calls the deployed contract associated with a given address.
* @param _nextMessageContext Message context to be used for the call.
* @param _gasLimit Amount of gas to be passed into this call.
* @param _contract OVM address to be called.
* @param _calldata Data to send along with the call.
* @return _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call.
*/
function _callContract(
MessageContext memory _nextMessageContext,
uint256 _gasLimit,
address _contract,
bytes memory _calldata,
MessageType _messageType
)
internal
returns (
bool _success,
bytes memory _returndata
)
{
// We reserve addresses of the form 0xdeaddeaddead...NNNN for the container contracts in L2
// geth. So, we block calls to these addresses since they are not safe to run as an OVM
// contract itself.
if (
(uint256(_contract) &
uint256(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000))
== uint256(CONTAINER_CONTRACT_PREFIX)
) {
// solhint-disable-next-line max-line-length
// EVM does not return data in the success case, see: https://github.com/ethereum/go-ethereum/blob/aae7660410f0ef90279e14afaaf2f429fdc2a186/core/vm/instructions.go#L600-L604
return (true, hex'');
}
// Both 0x0000... and the EVM precompiles have the same address on L1 and L2 -->
// no trie lookup needed.
address codeContractAddress =
uint(_contract) < 100
? _contract
: _getAccountEthAddress(_contract);
return _handleExternalMessage(
_nextMessageContext,
_gasLimit,
codeContractAddress,
_calldata,
_messageType
);
}
/**
* Handles all interactions which involve the execution manager calling out to untrusted code
* (both calls and creates). Ensures that OVM-related measures are enforced, including L2 gas
* refunds, nuisance gas, and flagged reversions.
*
* @param _nextMessageContext Message context to be used for the external message.
* @param _gasLimit Amount of gas to be passed into this message. NOTE: this argument is
* overwritten in some cases to avoid stack-too-deep.
* @param _contract OVM address being called or deployed to
* @param _data Data for the message (either calldata or creation code)
* @param _messageType What type of ovmOPCODE this message corresponds to.
* @return Whether or not the message (either a call or deployment) succeeded.
* @return Data returned by the message.
*/
function _handleExternalMessage(
MessageContext memory _nextMessageContext,
// NOTE: this argument is overwritten in some cases to avoid stack-too-deep.
uint256 _gasLimit,
address _contract,
bytes memory _data,
MessageType _messageType
)
internal
returns (
bool,
bytes memory
)
{
uint256 messageValue = _nextMessageContext.ovmCALLVALUE;
// If there is value in this message, we need to transfer the ETH over before switching
// contexts.
if (
messageValue > 0
&& _isValueType(_messageType)
) {
// Handle out-of-intrinsic gas consistent with EVM behavior -- the subcall "appears to
// revert" if we don't have enough gas to transfer the ETH.
// Similar to dynamic gas cost of value exceeding gas here:
// solhint-disable-next-line max-line-length
// https://github.com/ethereum/go-ethereum/blob/c503f98f6d5e80e079c1d8a3601d188af2a899da/core/vm/interpreter.go#L268-L273
if (gasleft() < CALL_WITH_VALUE_INTRINSIC_GAS) {
return (false, hex"");
}
// If there *is* enough gas to transfer ETH, then we need to make sure this amount of
// gas is reserved (i.e. not given to the _contract.call below) to guarantee that
// _handleExternalMessage can't run out of gas. In particular, in the event that
// the call fails, we will need to transfer the ETH back to the sender.
// Taking the lesser of _gasLimit and gasleft() - CALL_WITH_VALUE_INTRINSIC_GAS
// guarantees that the second _attemptForcedEthTransfer below, if needed, always has
// enough gas to succeed.
_gasLimit = Math.min(
_gasLimit,
gasleft() - CALL_WITH_VALUE_INTRINSIC_GAS // Cannot overflow due to the above check.
);
// Now transfer the value of the call.
// The target is interpreted to be the next message's ovmADDRESS account.
bool transferredOvmEth = _attemptForcedEthTransfer(
_nextMessageContext.ovmADDRESS,
messageValue
);
// If the ETH transfer fails (should only be possible in the case of insufficient
// balance), then treat this as a revert. This mirrors EVM behavior, see
// solhint-disable-next-line max-line-length
// https://github.com/ethereum/go-ethereum/blob/2dee31930c9977af2a9fcb518fb9838aa609a7cf/core/vm/evm.go#L298
if (!transferredOvmEth) {
return (false, hex"");
}
}
// We need to switch over to our next message context for the duration of this call.
MessageContext memory prevMessageContext = messageContext;
_switchMessageContext(prevMessageContext, _nextMessageContext);
// Nuisance gas is a system used to bound the ability for an attacker to make fraud proofs
// expensive by touching a lot of different accounts or storage slots. Since most contracts
// only use a few storage slots during any given transaction, this shouldn't be a limiting
// factor.
uint256 prevNuisanceGasLeft = messageRecord.nuisanceGasLeft;
uint256 nuisanceGasLimit = _getNuisanceGasLimit(_gasLimit);
messageRecord.nuisanceGasLeft = nuisanceGasLimit;
// Make the call and make sure to pass in the gas limit. Another instance of hidden
// complexity. `_contract` is guaranteed to be a safe contract, meaning its return/revert
// behavior can be controlled. In particular, we enforce that flags are passed through
// revert data as to retrieve execution metadata that would normally be reverted out of
// existence.
bool success;
bytes memory returndata;
if (_isCreateType(_messageType)) {
// safeCREATE() is a function which replicates a CREATE message, but uses return values
// Which match that of CALL (i.e. bool, bytes). This allows many security checks to be
// to be shared between untrusted call and create call frames.
(success, returndata) = address(this).call{gas: _gasLimit}(
abi.encodeWithSelector(
this.safeCREATE.selector,
_data,
_contract
)
);
} else {
(success, returndata) = _contract.call{gas: _gasLimit}(_data);
}
// If the message threw an exception, its value should be returned back to the sender.
// So, we force it back, BEFORE returning the messageContext to the previous addresses.
// This operation is part of the reason we "reserved the intrinsic gas" above.
if (
messageValue > 0
&& _isValueType(_messageType)
&& !success
) {
bool transferredOvmEth = _attemptForcedEthTransfer(
prevMessageContext.ovmADDRESS,
messageValue
);
// Since we transferred it in above and the call reverted, the transfer back should
// always pass. This code path should NEVER be triggered since we sent `messageValue`
// worth of OVM_ETH into the target and reserved sufficient gas to execute the transfer,
// but in case there is some edge case which has been missed, we revert the entire frame
// (and its parent) to make sure the ETH gets sent back.
if (!transferredOvmEth) {
_revertWithFlag(RevertFlag.OUT_OF_GAS);
}
}
// Switch back to the original message context now that we're out of the call and all
// OVM_ETH is in the right place.
_switchMessageContext(_nextMessageContext, prevMessageContext);
// Assuming there were no reverts, the message record should be accurate here. We'll update
// this value in the case of a revert.
uint256 nuisanceGasLeft = messageRecord.nuisanceGasLeft;
// Reverts at this point are completely OK, but we need to make a few updates based on the
// information passed through the revert.
if (success == false) {
(
RevertFlag flag,
uint256 nuisanceGasLeftPostRevert,
uint256 ovmGasRefund,
bytes memory returndataFromFlag
) = _decodeRevertData(returndata);
// INVALID_STATE_ACCESS is the only flag that triggers an immediate abort of the
// parent EVM message. This behavior is necessary because INVALID_STATE_ACCESS must
// halt any further transaction execution that could impact the execution result.
if (flag == RevertFlag.INVALID_STATE_ACCESS) {
_revertWithFlag(flag);
}
// INTENTIONAL_REVERT, UNSAFE_BYTECODE, STATIC_VIOLATION, and CREATOR_NOT_ALLOWED aren't
// dependent on the input state, so we can just handle them like standard reverts.
// Our only change here is to record the gas refund reported by the call (enforced by
// safety checking).
if (
flag == RevertFlag.INTENTIONAL_REVERT
|| flag == RevertFlag.UNSAFE_BYTECODE
|| flag == RevertFlag.STATIC_VIOLATION
|| flag == RevertFlag.CREATOR_NOT_ALLOWED
) {
transactionRecord.ovmGasRefund = ovmGasRefund;
}
// INTENTIONAL_REVERT needs to pass up the user-provided return data encoded into the
// flag, *not* the full encoded flag. Additionally, we surface custom error messages
// to developers in the case of unsafe creations for improved devex.
// All other revert types return no data.
if (
flag == RevertFlag.INTENTIONAL_REVERT
|| flag == RevertFlag.UNSAFE_BYTECODE
) {
returndata = returndataFromFlag;
} else {
returndata = hex'';
}
// Reverts mean we need to use up whatever "nuisance gas" was used by the call.
// EXCEEDS_NUISANCE_GAS explicitly reduces the remaining nuisance gas for this message
// to zero. OUT_OF_GAS is a "pseudo" flag given that messages return no data when they
// run out of gas, so we have to treat this like EXCEEDS_NUISANCE_GAS. All other flags
// will simply pass up the remaining nuisance gas.
nuisanceGasLeft = nuisanceGasLeftPostRevert;
}
// We need to reset the nuisance gas back to its original value minus the amount used here.
messageRecord.nuisanceGasLeft = prevNuisanceGasLeft - (nuisanceGasLimit - nuisanceGasLeft);
return (
success,
returndata
);
}
/**
* Handles the creation-specific safety measures required for OVM contract deployment.
* This function sanitizes the return types for creation messages to match calls (bool, bytes),
* by being an external function which the EM can call, that mimics the success/fail case of the
* CREATE.
* This allows for consistent handling of both types of messages in _handleExternalMessage().
* Having this step occur as a separate call frame also allows us to easily revert the
* contract deployment in the event that the code is unsafe.
*
* @param _creationCode Code to pass into CREATE for deployment.
* @param _address OVM address being deployed to.
*/
function safeCREATE(
bytes memory _creationCode,
address _address
)
external
{
// The only way this should callable is from within _createContract(),
// and it should DEFINITELY not be callable by a non-EM code contract.
if (msg.sender != address(this)) {
return;
}
// Check that there is not already code at this address.
if (_hasEmptyAccount(_address) == false) {
// Note: in the EVM, this case burns all allotted gas. For improved
// developer experience, we do return the remaining gas.
_revertWithFlag(
RevertFlag.CREATE_COLLISION
);
}
// Check the creation bytecode against the OVM_SafetyChecker.
if (ovmSafetyChecker.isBytecodeSafe(_creationCode) == false) {
// Note: in the EVM, this case burns all allotted gas. For improved
// developer experience, we do return the remaining gas.
_revertWithFlag(
RevertFlag.UNSAFE_BYTECODE,
// solhint-disable-next-line max-line-length
Lib_ErrorUtils.encodeRevertString("Contract creation code contains unsafe opcodes. Did you use the right compiler or pass an unsafe constructor argument?")
);
}
// We always need to initialize the contract with the default account values.
_initPendingAccount(_address);
// Actually execute the EVM create message.
// NOTE: The inline assembly below means we can NOT make any evm calls between here and then
address ethAddress = Lib_EthUtils.createContract(_creationCode);
if (ethAddress == address(0)) {
// If the creation fails, the EVM lets us grab its revert data. This may contain a
// revert flag to be used above in _handleExternalMessage, so we pass the revert data
// back up unmodified.
assembly {
returndatacopy(0,0,returndatasize())
revert(0, returndatasize())
}
}
// Again simply checking that the deployed code is safe too. Contracts can generate
// arbitrary deployment code, so there's no easy way to analyze this beforehand.
bytes memory deployedCode = Lib_EthUtils.getCode(ethAddress);
if (ovmSafetyChecker.isBytecodeSafe(deployedCode) == false) {
_revertWithFlag(
RevertFlag.UNSAFE_BYTECODE,
// solhint-disable-next-line max-line-length
Lib_ErrorUtils.encodeRevertString("Constructor attempted to deploy unsafe bytecode.")
);
}
// Contract creation didn't need to be reverted and the bytecode is safe. We finish up by
// associating the desired address with the newly created contract's code hash and address.
_commitPendingAccount(
_address,
ethAddress,
Lib_EthUtils.getCodeHash(ethAddress)
);
}
/******************************************
* Internal Functions: Value Manipulation *
******************************************/
/**
* Invokes an ovmCALL to OVM_ETH.transfer on behalf of the current ovmADDRESS, allowing us to
* force movement of OVM_ETH in correspondence with ETH's native value functionality.
* WARNING: this will send on behalf of whatever the messageContext.ovmADDRESS is in storage
* at the time of the call.
* NOTE: In the future, this could be optimized to directly invoke EM._setContractStorage(...).
* @param _to Amount of OVM_ETH to be sent.
* @param _value Amount of OVM_ETH to send.
* @return _success Whether or not the transfer worked.
*/
function _attemptForcedEthTransfer(
address _to,
uint256 _value
)
internal
returns(
bool _success
)
{
bytes memory transferCalldata = abi.encodeWithSignature(
"transfer(address,uint256)",
_to,
_value
);
// OVM_ETH inherits from the UniswapV2ERC20 standard. In this implementation, its return
// type is a boolean. However, the implementation always returns true if it does not revert
// Thus, success of the call frame is sufficient to infer success of the transfer itself.
(bool success, ) = ovmCALL(
gasleft(),
Lib_PredeployAddresses.OVM_ETH,
0,
transferCalldata
);
return success;
}
/******************************************
* Internal Functions: State Manipulation *
******************************************/
/**
* Checks whether an account exists within the OVM_StateManager.
* @param _address Address of the account to check.
* @return _exists Whether or not the account exists.
*/
function _hasAccount(
address _address
)
internal
returns (
bool _exists
)
{
_checkAccountLoad(_address);
return ovmStateManager.hasAccount(_address);
}
/**
* Checks whether a known empty account exists within the OVM_StateManager.
* @param _address Address of the account to check.
* @return _exists Whether or not the account empty exists.
*/
function _hasEmptyAccount(
address _address
)
internal
returns (
bool _exists
)
{
_checkAccountLoad(_address);
return ovmStateManager.hasEmptyAccount(_address);
}
/**
* Sets the nonce of an account.
* @param _address Address of the account to modify.
* @param _nonce New account nonce.
*/
function _setAccountNonce(
address _address,
uint256 _nonce
)
internal
{
_checkAccountChange(_address);
ovmStateManager.setAccountNonce(_address, _nonce);
}
/**
* Gets the nonce of an account.
* @param _address Address of the account to access.
* @return _nonce Nonce of the account.
*/
function _getAccountNonce(
address _address
)
internal
returns (
uint256 _nonce
)
{
_checkAccountLoad(_address);
return ovmStateManager.getAccountNonce(_address);
}
/**
* Retrieves the Ethereum address of an account.
* @param _address Address of the account to access.
* @return _ethAddress Corresponding Ethereum address.
*/
function _getAccountEthAddress(
address _address
)
internal
returns (
address _ethAddress
)
{
_checkAccountLoad(_address);
return ovmStateManager.getAccountEthAddress(_address);
}
/**
* Creates the default account object for the given address.
* @param _address Address of the account create.
*/
function _initPendingAccount(
address _address
)
internal
{
// Although it seems like `_checkAccountChange` would be more appropriate here, we don't
// actually consider an account "changed" until it's inserted into the state (in this case
// by `_commitPendingAccount`).
_checkAccountLoad(_address);
ovmStateManager.initPendingAccount(_address);
}
/**
* Stores additional relevant data for a new account, thereby "committing" it to the state.
* This function is only called during `ovmCREATE` and `ovmCREATE2` after a successful contract
* creation.
* @param _address Address of the account to commit.
* @param _ethAddress Address of the associated deployed contract.
* @param _codeHash Hash of the code stored at the address.
*/
function _commitPendingAccount(
address _address,
address _ethAddress,
bytes32 _codeHash
)
internal
{
_checkAccountChange(_address);
ovmStateManager.commitPendingAccount(
_address,
_ethAddress,
_codeHash
);
}
/**
* Retrieves the value of a storage slot.
* @param _contract Address of the contract to query.
* @param _key 32 byte key of the storage slot.
* @return _value 32 byte storage slot value.
*/
function _getContractStorage(
address _contract,
bytes32 _key
)
internal
returns (
bytes32 _value
)
{
_checkContractStorageLoad(_contract, _key);
return ovmStateManager.getContractStorage(_contract, _key);
}
/**
* Sets the value of a storage slot.
* @param _contract Address of the contract to modify.
* @param _key 32 byte key of the storage slot.
* @param _value 32 byte storage slot value.
*/
function _putContractStorage(
address _contract,
bytes32 _key,
bytes32 _value
)
internal
{
// We don't set storage if the value didn't change. Although this acts as a convenient
// optimization, it's also necessary to avoid the case in which a contract with no storage
// attempts to store the value "0" at any key. Putting this value (and therefore requiring
// that the value be committed into the storage trie after execution) would incorrectly
// modify the storage root.
if (_getContractStorage(_contract, _key) == _value) {
return;
}
_checkContractStorageChange(_contract, _key);
ovmStateManager.putContractStorage(_contract, _key, _value);
}
/**
* Validation whenever a contract needs to be loaded. Checks that the account exists, charges
* nuisance gas if the account hasn't been loaded before.
* @param _address Address of the account to load.
*/
function _checkAccountLoad(
address _address
)
internal
{
// See `_checkContractStorageLoad` for more information.
if (gasleft() < MIN_GAS_FOR_INVALID_STATE_ACCESS) {
_revertWithFlag(RevertFlag.OUT_OF_GAS);
}
// See `_checkContractStorageLoad` for more information.
if (ovmStateManager.hasAccount(_address) == false) {
_revertWithFlag(RevertFlag.INVALID_STATE_ACCESS);
}
// Check whether the account has been loaded before and mark it as loaded if not. We need
// this because "nuisance gas" only applies to the first time that an account is loaded.
(
bool _wasAccountAlreadyLoaded
) = ovmStateManager.testAndSetAccountLoaded(_address);
// If we hadn't already loaded the account, then we'll need to charge "nuisance gas" based
// on the size of the contract code.
if (_wasAccountAlreadyLoaded == false) {
_useNuisanceGas(
(Lib_EthUtils.getCodeSize(_getAccountEthAddress(_address))
* NUISANCE_GAS_PER_CONTRACT_BYTE) + MIN_NUISANCE_GAS_PER_CONTRACT
);
}
}
/**
* Validation whenever a contract needs to be changed. Checks that the account exists, charges
* nuisance gas if the account hasn't been changed before.
* @param _address Address of the account to change.
*/
function _checkAccountChange(
address _address
)
internal
{
// Start by checking for a load as we only want to charge nuisance gas proportional to
// contract size once.
_checkAccountLoad(_address);
// Check whether the account has been changed before and mark it as changed if not. We need
// this because "nuisance gas" only applies to the first time that an account is changed.
(
bool _wasAccountAlreadyChanged
) = ovmStateManager.testAndSetAccountChanged(_address);
// If we hadn't already loaded the account, then we'll need to charge "nuisance gas" based
// on the size of the contract code.
if (_wasAccountAlreadyChanged == false) {
ovmStateManager.incrementTotalUncommittedAccounts();
_useNuisanceGas(
(Lib_EthUtils.getCodeSize(_getAccountEthAddress(_address))
* NUISANCE_GAS_PER_CONTRACT_BYTE) + MIN_NUISANCE_GAS_PER_CONTRACT
);
}
}
/**
* Validation whenever a slot needs to be loaded. Checks that the account exists, charges
* nuisance gas if the slot hasn't been loaded before.
* @param _contract Address of the account to load from.
* @param _key 32 byte key to load.
*/
function _checkContractStorageLoad(
address _contract,
bytes32 _key
)
internal
{
// Another case of hidden complexity. If we didn't enforce this requirement, then a
// contract could pass in just enough gas to cause the INVALID_STATE_ACCESS check to fail
// on L1 but not on L2. A contract could use this behavior to prevent the
// OVM_ExecutionManager from detecting an invalid state access. Reverting with OUT_OF_GAS
// allows us to also charge for the full message nuisance gas, because you deserve that for
// trying to break the contract in this way.
if (gasleft() < MIN_GAS_FOR_INVALID_STATE_ACCESS) {
_revertWithFlag(RevertFlag.OUT_OF_GAS);
}
// We need to make sure that the transaction isn't trying to access storage that hasn't
// been provided to the OVM_StateManager. We'll immediately abort if this is the case.
// We know that we have enough gas to do this check because of the above test.
if (ovmStateManager.hasContractStorage(_contract, _key) == false) {
_revertWithFlag(RevertFlag.INVALID_STATE_ACCESS);
}
// Check whether the slot has been loaded before and mark it as loaded if not. We need
// this because "nuisance gas" only applies to the first time that a slot is loaded.
(
bool _wasContractStorageAlreadyLoaded
) = ovmStateManager.testAndSetContractStorageLoaded(_contract, _key);
// If we hadn't already loaded the account, then we'll need to charge some fixed amount of
// "nuisance gas".
if (_wasContractStorageAlreadyLoaded == false) {
_useNuisanceGas(NUISANCE_GAS_SLOAD);
}
}
/**
* Validation whenever a slot needs to be changed. Checks that the account exists, charges
* nuisance gas if the slot hasn't been changed before.
* @param _contract Address of the account to change.
* @param _key 32 byte key to change.
*/
function _checkContractStorageChange(
address _contract,
bytes32 _key
)
internal
{
// Start by checking for load to make sure we have the storage slot and that we charge the
// "nuisance gas" necessary to prove the storage slot state.
_checkContractStorageLoad(_contract, _key);
// Check whether the slot has been changed before and mark it as changed if not. We need
// this because "nuisance gas" only applies to the first time that a slot is changed.
(
bool _wasContractStorageAlreadyChanged
) = ovmStateManager.testAndSetContractStorageChanged(_contract, _key);
// If we hadn't already changed the account, then we'll need to charge some fixed amount of
// "nuisance gas".
if (_wasContractStorageAlreadyChanged == false) {
// Changing a storage slot means that we're also going to have to change the
// corresponding account, so do an account change check.
_checkAccountChange(_contract);
ovmStateManager.incrementTotalUncommittedContractStorage();
_useNuisanceGas(NUISANCE_GAS_SSTORE);
}
}
/************************************
* Internal Functions: Revert Logic *
************************************/
/**
* Simple encoding for revert data.
* @param _flag Flag to revert with.
* @param _data Additional user-provided revert data.
* @return _revertdata Encoded revert data.
*/
function _encodeRevertData(
RevertFlag _flag,
bytes memory _data
)
internal
view
returns (
bytes memory _revertdata
)
{
// Out of gas and create exceptions will fundamentally return no data, so simulating it
// shouldn't either.
if (
_flag == RevertFlag.OUT_OF_GAS
) {
return bytes("");
}
// INVALID_STATE_ACCESS doesn't need to return any data other than the flag.
if (_flag == RevertFlag.INVALID_STATE_ACCESS) {
return abi.encode(
_flag,
0,
0,
bytes("")
);
}
// Just ABI encode the rest of the parameters.
return abi.encode(
_flag,
messageRecord.nuisanceGasLeft,
transactionRecord.ovmGasRefund,
_data
);
}
/**
* Simple decoding for revert data.
* @param _revertdata Revert data to decode.
* @return _flag Flag used to revert.
* @return _nuisanceGasLeft Amount of nuisance gas unused by the message.
* @return _ovmGasRefund Amount of gas refunded during the message.
* @return _data Additional user-provided revert data.
*/
function _decodeRevertData(
bytes memory _revertdata
)
internal
pure
returns (
RevertFlag _flag,
uint256 _nuisanceGasLeft,
uint256 _ovmGasRefund,
bytes memory _data
)
{
// A length of zero means the call ran out of gas, just return empty data.
if (_revertdata.length == 0) {
return (
RevertFlag.OUT_OF_GAS,
0,
0,
bytes("")
);
}
// ABI decode the incoming data.
return abi.decode(_revertdata, (RevertFlag, uint256, uint256, bytes));
}
/**
* Causes a message to revert or abort.
* @param _flag Flag to revert with.
* @param _data Additional user-provided data.
*/
function _revertWithFlag(
RevertFlag _flag,
bytes memory _data
)
internal
view
{
bytes memory revertdata = _encodeRevertData(
_flag,
_data
);
assembly {
revert(add(revertdata, 0x20), mload(revertdata))
}
}
/**
* Causes a message to revert or abort.
* @param _flag Flag to revert with.
*/
function _revertWithFlag(
RevertFlag _flag
)
internal
{
_revertWithFlag(_flag, bytes(""));
}
/******************************************
* Internal Functions: Nuisance Gas Logic *
******************************************/
/**
* Computes the nuisance gas limit from the gas limit.
* @dev This function is currently using a naive implementation whereby the nuisance gas limit
* is set to exactly equal the lesser of the gas limit or remaining gas. It's likely that
* this implementation is perfectly fine, but we may change this formula later.
* @param _gasLimit Gas limit to compute from.
* @return _nuisanceGasLimit Computed nuisance gas limit.
*/
function _getNuisanceGasLimit(
uint256 _gasLimit
)
internal
view
returns (
uint256 _nuisanceGasLimit
)
{
return _gasLimit < gasleft() ? _gasLimit : gasleft();
}
/**
* Uses a certain amount of nuisance gas.
* @param _amount Amount of nuisance gas to use.
*/
function _useNuisanceGas(
uint256 _amount
)
internal
{
// Essentially the same as a standard OUT_OF_GAS, except we also retain a record of the gas
// refund to be given at the end of the transaction.
if (messageRecord.nuisanceGasLeft < _amount) {
_revertWithFlag(RevertFlag.EXCEEDS_NUISANCE_GAS);
}
messageRecord.nuisanceGasLeft -= _amount;
}
/************************************
* Internal Functions: Gas Metering *
************************************/
/**
* Checks whether a transaction needs to start a new epoch and does so if necessary.
* @param _timestamp Transaction timestamp.
*/
function _checkNeedsNewEpoch(
uint256 _timestamp
)
internal
{
if (
_timestamp >= (
_getGasMetadata(GasMetadataKey.CURRENT_EPOCH_START_TIMESTAMP)
+ gasMeterConfig.secondsPerEpoch
)
) {
_putGasMetadata(
GasMetadataKey.CURRENT_EPOCH_START_TIMESTAMP,
_timestamp
);
_putGasMetadata(
GasMetadataKey.PREV_EPOCH_SEQUENCER_QUEUE_GAS,
_getGasMetadata(
GasMetadataKey.CUMULATIVE_SEQUENCER_QUEUE_GAS
)
);
_putGasMetadata(
GasMetadataKey.PREV_EPOCH_L1TOL2_QUEUE_GAS,
_getGasMetadata(
GasMetadataKey.CUMULATIVE_L1TOL2_QUEUE_GAS
)
);
}
}
/**
* Validates the input values of a transaction.
* @return _valid Whether or not the transaction data is valid.
*/
function _isValidInput(
Lib_OVMCodec.Transaction memory _transaction
)
view
internal
returns (
bool
)
{
// Prevent reentrancy to run():
// This check prevents calling run with the default ovmNumber.
// Combined with the first check in run():
// if (transactionContext.ovmNUMBER != DEFAULT_UINT256) { return; }
// It should be impossible to re-enter since run() returns before any other call frames are
// created. Since this value is already being written to storage, we save much gas compared
// to using the standard nonReentrant pattern.
if (_transaction.blockNumber == DEFAULT_UINT256) {
return false;
}
if (_isValidGasLimit(_transaction.gasLimit, _transaction.l1QueueOrigin) == false) {
return false;
}
return true;
}
/**
* Validates the gas limit for a given transaction.
* @param _gasLimit Gas limit provided by the transaction.
* param _queueOrigin Queue from which the transaction originated.
* @return _valid Whether or not the gas limit is valid.
*/
function _isValidGasLimit(
uint256 _gasLimit,
Lib_OVMCodec.QueueOrigin // _queueOrigin
)
view
internal
returns (
bool _valid
)
{
// Always have to be below the maximum gas limit.
if (_gasLimit > gasMeterConfig.maxTransactionGasLimit) {
return false;
}
// Always have to be above the minimum gas limit.
if (_gasLimit < gasMeterConfig.minTransactionGasLimit) {
return false;
}
// TEMPORARY: Gas metering is disabled for minnet.
return true;
// GasMetadataKey cumulativeGasKey;
// GasMetadataKey prevEpochGasKey;
// if (_queueOrigin == Lib_OVMCodec.QueueOrigin.SEQUENCER_QUEUE) {
// cumulativeGasKey = GasMetadataKey.CUMULATIVE_SEQUENCER_QUEUE_GAS;
// prevEpochGasKey = GasMetadataKey.PREV_EPOCH_SEQUENCER_QUEUE_GAS;
// } else {
// cumulativeGasKey = GasMetadataKey.CUMULATIVE_L1TOL2_QUEUE_GAS;
// prevEpochGasKey = GasMetadataKey.PREV_EPOCH_L1TOL2_QUEUE_GAS;
// }
// return (
// (
// _getGasMetadata(cumulativeGasKey)
// - _getGasMetadata(prevEpochGasKey)
// + _gasLimit
// ) < gasMeterConfig.maxGasPerQueuePerEpoch
// );
}
/**
* Updates the cumulative gas after a transaction.
* @param _gasUsed Gas used by the transaction.
* @param _queueOrigin Queue from which the transaction originated.
*/
function _updateCumulativeGas(
uint256 _gasUsed,
Lib_OVMCodec.QueueOrigin _queueOrigin
)
internal
{
GasMetadataKey cumulativeGasKey;
if (_queueOrigin == Lib_OVMCodec.QueueOrigin.SEQUENCER_QUEUE) {
cumulativeGasKey = GasMetadataKey.CUMULATIVE_SEQUENCER_QUEUE_GAS;
} else {
cumulativeGasKey = GasMetadataKey.CUMULATIVE_L1TOL2_QUEUE_GAS;
}
_putGasMetadata(
cumulativeGasKey,
(
_getGasMetadata(cumulativeGasKey)
+ gasMeterConfig.minTransactionGasLimit
+ _gasUsed
- transactionRecord.ovmGasRefund
)
);
}
/**
* Retrieves the value of a gas metadata key.
* @param _key Gas metadata key to retrieve.
* @return _value Value stored at the given key.
*/
function _getGasMetadata(
GasMetadataKey _key
)
internal
returns (
uint256 _value
)
{
return uint256(_getContractStorage(
GAS_METADATA_ADDRESS,
bytes32(uint256(_key))
));
}
/**
* Sets the value of a gas metadata key.
* @param _key Gas metadata key to set.
* @param _value Value to store at the given key.
*/
function _putGasMetadata(
GasMetadataKey _key,
uint256 _value
)
internal
{
_putContractStorage(
GAS_METADATA_ADDRESS,
bytes32(uint256(_key)),
bytes32(uint256(_value))
);
}
/*****************************************
* Internal Functions: Execution Context *
*****************************************/
/**
* Swaps over to a new message context.
* @param _prevMessageContext Context we're switching from.
* @param _nextMessageContext Context we're switching to.
*/
function _switchMessageContext(
MessageContext memory _prevMessageContext,
MessageContext memory _nextMessageContext
)
internal
{
// These conditionals allow us to avoid unneccessary SSTOREs. However, they do mean that
// the current storage value for the messageContext MUST equal the _prevMessageContext
// argument, or an SSTORE might be erroneously skipped.
if (_prevMessageContext.ovmCALLER != _nextMessageContext.ovmCALLER) {
messageContext.ovmCALLER = _nextMessageContext.ovmCALLER;
}
if (_prevMessageContext.ovmADDRESS != _nextMessageContext.ovmADDRESS) {
messageContext.ovmADDRESS = _nextMessageContext.ovmADDRESS;
}
if (_prevMessageContext.isStatic != _nextMessageContext.isStatic) {
messageContext.isStatic = _nextMessageContext.isStatic;
}
if (_prevMessageContext.ovmCALLVALUE != _nextMessageContext.ovmCALLVALUE) {
messageContext.ovmCALLVALUE = _nextMessageContext.ovmCALLVALUE;
}
}
/**
* Initializes the execution context.
* @param _transaction OVM transaction being executed.
*/
function _initContext(
Lib_OVMCodec.Transaction memory _transaction
)
internal
{
transactionContext.ovmTIMESTAMP = _transaction.timestamp;
transactionContext.ovmNUMBER = _transaction.blockNumber;
transactionContext.ovmTXGASLIMIT = _transaction.gasLimit;
transactionContext.ovmL1QUEUEORIGIN = _transaction.l1QueueOrigin;
transactionContext.ovmL1TXORIGIN = _transaction.l1TxOrigin;
transactionContext.ovmGASLIMIT = gasMeterConfig.maxGasPerQueuePerEpoch;
messageRecord.nuisanceGasLeft = _getNuisanceGasLimit(_transaction.gasLimit);
}
/**
* Resets the transaction and message context.
*/
function _resetContext()
internal
{
transactionContext.ovmL1TXORIGIN = DEFAULT_ADDRESS;
transactionContext.ovmTIMESTAMP = DEFAULT_UINT256;
transactionContext.ovmNUMBER = DEFAULT_UINT256;
transactionContext.ovmGASLIMIT = DEFAULT_UINT256;
transactionContext.ovmTXGASLIMIT = DEFAULT_UINT256;
transactionContext.ovmL1QUEUEORIGIN = Lib_OVMCodec.QueueOrigin.SEQUENCER_QUEUE;
transactionRecord.ovmGasRefund = DEFAULT_UINT256;
messageContext.ovmCALLER = DEFAULT_ADDRESS;
messageContext.ovmADDRESS = DEFAULT_ADDRESS;
messageContext.isStatic = false;
messageRecord.nuisanceGasLeft = DEFAULT_UINT256;
// Reset the ovmStateManager.
ovmStateManager = iOVM_StateManager(address(0));
}
/******************************************
* Internal Functions: Message Typechecks *
******************************************/
/**
* Returns whether or not the given message type is a CREATE-type.
* @param _messageType the message type in question.
*/
function _isCreateType(
MessageType _messageType
)
internal
pure
returns(
bool
)
{
return (
_messageType == MessageType.ovmCREATE
|| _messageType == MessageType.ovmCREATE2
);
}
/**
* Returns whether or not the given message type (potentially) requires the transfer of ETH
* value along with the message.
* @param _messageType the message type in question.
*/
function _isValueType(
MessageType _messageType
)
internal
pure
returns(
bool
)
{
// ovmSTATICCALL and ovmDELEGATECALL types do not accept or transfer value.
return (
_messageType == MessageType.ovmCALL
|| _messageType == MessageType.ovmCREATE
|| _messageType == MessageType.ovmCREATE2
);
}
/*****************************
* L2-only Helper Functions *
*****************************/
/**
* Unreachable helper function for simulating eth_calls with an OVM message context.
* This function will throw an exception in all cases other than when used as a custom
* entrypoint in L2 Geth to simulate eth_call.
* @param _transaction the message transaction to simulate.
* @param _from the OVM account the simulated call should be from.
* @param _value the amount of ETH value to send.
* @param _ovmStateManager the address of the OVM_StateManager precompile in the L2 state.
*/
function simulateMessage(
Lib_OVMCodec.Transaction memory _transaction,
address _from,
uint256 _value,
iOVM_StateManager _ovmStateManager
)
external
returns (
bytes memory
)
{
// Prevent this call from having any effect unless in a custom-set VM frame
require(msg.sender == address(0));
// Initialize the EM's internal state, ignoring nuisance gas.
ovmStateManager = _ovmStateManager;
_initContext(_transaction);
messageRecord.nuisanceGasLeft = uint(-1);
// Set the ovmADDRESS to the _from so that the subsequent call frame "comes from" them.
messageContext.ovmADDRESS = _from;
// Execute the desired message.
bool isCreate = _transaction.entrypoint == address(0);
if (isCreate) {
(address created, bytes memory revertData) = ovmCREATE(_transaction.data);
if (created == address(0)) {
return abi.encode(false, revertData);
} else {
// The eth_call RPC endpoint for to = undefined will return the deployed bytecode
// in the success case, differing from standard create messages.
return abi.encode(true, Lib_EthUtils.getCode(created));
}
} else {
(bool success, bytes memory returndata) = ovmCALL(
_transaction.gasLimit,
_transaction.entrypoint,
_value,
_transaction.data
);
return abi.encode(success, returndata);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
/* Interface Imports */
import { iOVM_SafetyChecker } from "../../iOVM/execution/iOVM_SafetyChecker.sol";
/**
* @title OVM_SafetyChecker
* @dev The Safety Checker verifies that contracts deployed on L2 do not contain any
* "unsafe" operations. An operation is considered unsafe if it would access state variables which
* are specific to the environment (ie. L1 or L2) in which it is executed, as this could be used
* to "escape the sandbox" of the OVM, resulting in non-deterministic fraud proofs.
* That is, an attacker would be able to "prove fraud" on an honestly applied transaction.
* Note that a "safe" contract requires opcodes to appear in a particular pattern;
* omission of "unsafe" opcodes is necessary, but not sufficient.
*
* Compiler used: solc
* Runtime target: EVM
*/
contract OVM_SafetyChecker is iOVM_SafetyChecker {
/********************
* Public Functions *
********************/
/**
* Returns whether or not all of the provided bytecode is safe.
* @param _bytecode The bytecode to safety check.
* @return `true` if the bytecode is safe, `false` otherwise.
*/
function isBytecodeSafe(
bytes memory _bytecode
)
override
external
pure
returns (
bool
)
{
// autogenerated by gen_safety_checker_constants.py
// number of bytes to skip for each opcode
uint256[8] memory opcodeSkippableBytes = [
uint256(0x0001010101010101010101010000000001010101010101010101010101010000),
uint256(0x0100000000000000000000000000000000000000010101010101000000010100),
uint256(0x0000000000000000000000000000000001010101000000010101010100000000),
uint256(0x0203040500000000000000000000000000000000000000000000000000000000),
uint256(0x0101010101010101010101010101010101010101010101010101010101010101),
uint256(0x0101010101000000000000000000000000000000000000000000000000000000),
uint256(0x0000000000000000000000000000000000000000000000000000000000000000),
uint256(0x0000000000000000000000000000000000000000000000000000000000000000)
];
// Mask to gate opcode specific cases
// solhint-disable-next-line max-line-length
uint256 opcodeGateMask = ~uint256(0xffffffffffffffffffffffe000000000fffffffff070ffff9c0ffffec000f001);
// Halting opcodes
// solhint-disable-next-line max-line-length
uint256 opcodeHaltingMask = ~uint256(0x4008000000000000000000000000000000000000004000000000000000000001);
// PUSH opcodes
uint256 opcodePushMask = ~uint256(0xffffffff000000000000000000000000);
uint256 codeLength;
uint256 _pc;
assembly {
_pc := add(_bytecode, 0x20)
}
codeLength = _pc + _bytecode.length;
do {
// current opcode: 0x00...0xff
uint256 opNum;
/* solhint-disable max-line-length */
// inline assembly removes the extra add + bounds check
assembly {
let word := mload(_pc) //load the next 32 bytes at pc into word
// Look up number of bytes to skip from opcodeSkippableBytes and then update indexInWord
// E.g. the 02030405 in opcodeSkippableBytes is the number of bytes to skip for PUSH1->4
// We repeat this 6 times, thus we can only skip bytes for up to PUSH4 ((1+4) * 6 = 30 < 32).
// If we see an opcode that is listed as 0 skippable bytes e.g. PUSH5,
// then we will get stuck on that indexInWord and then opNum will be set to the PUSH5 opcode.
let indexInWord := byte(0, mload(add(opcodeSkippableBytes, byte(0, word))))
indexInWord := add(indexInWord, byte(0, mload(add(opcodeSkippableBytes, byte(indexInWord, word)))))
indexInWord := add(indexInWord, byte(0, mload(add(opcodeSkippableBytes, byte(indexInWord, word)))))
indexInWord := add(indexInWord, byte(0, mload(add(opcodeSkippableBytes, byte(indexInWord, word)))))
indexInWord := add(indexInWord, byte(0, mload(add(opcodeSkippableBytes, byte(indexInWord, word)))))
indexInWord := add(indexInWord, byte(0, mload(add(opcodeSkippableBytes, byte(indexInWord, word)))))
_pc := add(_pc, indexInWord)
opNum := byte(indexInWord, word)
}
/* solhint-enable max-line-length */
// + push opcodes
// + stop opcodes [STOP(0x00),JUMP(0x56),RETURN(0xf3),INVALID(0xfe)]
// + caller opcode CALLER(0x33)
// + blacklisted opcodes
uint256 opBit = 1 << opNum;
if (opBit & opcodeGateMask == 0) {
if (opBit & opcodePushMask == 0) {
// all pushes are valid opcodes
// subsequent bytes are not opcodes. Skip them.
_pc += (opNum - 0x5e); // PUSH1 is 0x60, so opNum-0x5f = PUSHed bytes and we
// +1 to skip the _pc++; line below in order to save gas ((-0x5f + 1) = -0x5e)
continue;
} else if (opBit & opcodeHaltingMask == 0) {
// STOP or JUMP or RETURN or INVALID (Note: REVERT is blacklisted, so not
// included here)
// We are now inside unreachable code until we hit a JUMPDEST!
do {
_pc++;
assembly {
opNum := byte(0, mload(_pc))
}
// encountered a JUMPDEST
if (opNum == 0x5b) break;
// skip PUSHed bytes
// opNum-0x5f = PUSHed bytes (PUSH1 is 0x60)
if ((1 << opNum) & opcodePushMask == 0) _pc += (opNum - 0x5f);
} while (_pc < codeLength);
// opNum is 0x5b, so we don't continue here since the pc++ is fine
} else if (opNum == 0x33) { // Caller opcode
uint256 firstOps; // next 32 bytes of bytecode
uint256 secondOps; // following 32 bytes of bytecode
assembly {
firstOps := mload(_pc)
// 37 bytes total, 5 left over --> 32 - 5 bytes = 27 bytes = 216 bits
secondOps := shr(216, mload(add(_pc, 0x20)))
}
// Call identity precompile
// CALLER POP PUSH1 0x00 PUSH1 0x04 GAS CALL
// 32 - 8 bytes = 24 bytes = 192
if ((firstOps >> 192) == 0x3350600060045af1) {
_pc += 8;
// Call EM and abort execution if instructed
// CALLER PUSH1 0x00 SWAP1 GAS CALL PC PUSH1 0x0E ADD JUMPI RETURNDATASIZE
// PUSH1 0x00 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x00 REVERT JUMPDEST
// RETURNDATASIZE PUSH1 0x01 EQ ISZERO PC PUSH1 0x0a ADD JUMPI PUSH1 0x01 PUSH1
// 0x00 RETURN JUMPDEST
// solhint-disable-next-line max-line-length
} else if (firstOps == 0x336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760 && secondOps == 0x016000f35b) {
_pc += 37;
} else {
return false;
}
continue;
} else {
// encountered a non-whitelisted opcode!
return false;
}
}
_pc++;
} while (_pc < codeLength);
return true;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/* Interface Imports */
import { iOVM_StateManager } from "../../iOVM/execution/iOVM_StateManager.sol";
/**
* @title OVM_StateManager
* @dev The State Manager contract holds all storage values for contracts in the OVM. It can only be
* written to by the Execution Manager and State Transitioner. It runs on L1 during the setup and
* execution of a fraud proof.
* The same logic runs on L2, but has been implemented as a precompile in the L2 go-ethereum client
* (see https://github.com/ethereum-optimism/go-ethereum/blob/master/core/vm/ovm_state_manager.go).
*
* Compiler used: solc
* Runtime target: EVM
*/
contract OVM_StateManager is iOVM_StateManager {
/*************
* Constants *
*************/
bytes32 constant internal EMPTY_ACCOUNT_STORAGE_ROOT =
0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421;
bytes32 constant internal EMPTY_ACCOUNT_CODE_HASH =
0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
bytes32 constant internal STORAGE_XOR_VALUE =
0xFEEDFACECAFEBEEFFEEDFACECAFEBEEFFEEDFACECAFEBEEFFEEDFACECAFEBEEF;
/*************
* Variables *
*************/
address override public owner;
address override public ovmExecutionManager;
mapping (address => Lib_OVMCodec.Account) internal accounts;
mapping (address => mapping (bytes32 => bytes32)) internal contractStorage;
mapping (address => mapping (bytes32 => bool)) internal verifiedContractStorage;
mapping (bytes32 => ItemState) internal itemStates;
uint256 internal totalUncommittedAccounts;
uint256 internal totalUncommittedContractStorage;
/***************
* Constructor *
***************/
/**
* @param _owner Address of the owner of this contract.
*/
constructor(
address _owner
)
{
owner = _owner;
}
/**********************
* Function Modifiers *
**********************/
/**
* Simple authentication, this contract should only be accessible to the owner
* (which is expected to be the State Transitioner during `PRE_EXECUTION`
* or the OVM_ExecutionManager during transaction execution.
*/
modifier authenticated() {
// owner is the State Transitioner
require(
msg.sender == owner || msg.sender == ovmExecutionManager,
"Function can only be called by authenticated addresses"
);
_;
}
/********************
* Public Functions *
********************/
/**
* Checks whether a given address is allowed to modify this contract.
* @param _address Address to check.
* @return Whether or not the address can modify this contract.
*/
function isAuthenticated(
address _address
)
override
public
view
returns (
bool
)
{
return (_address == owner || _address == ovmExecutionManager);
}
/**
* Sets the address of the OVM_ExecutionManager.
* @param _ovmExecutionManager Address of the OVM_ExecutionManager.
*/
function setExecutionManager(
address _ovmExecutionManager
)
override
public
authenticated
{
ovmExecutionManager = _ovmExecutionManager;
}
/**
* Inserts an account into the state.
* @param _address Address of the account to insert.
* @param _account Account to insert for the given address.
*/
function putAccount(
address _address,
Lib_OVMCodec.Account memory _account
)
override
public
authenticated
{
accounts[_address] = _account;
}
/**
* Marks an account as empty.
* @param _address Address of the account to mark.
*/
function putEmptyAccount(
address _address
)
override
public
authenticated
{
Lib_OVMCodec.Account storage account = accounts[_address];
account.storageRoot = EMPTY_ACCOUNT_STORAGE_ROOT;
account.codeHash = EMPTY_ACCOUNT_CODE_HASH;
}
/**
* Retrieves an account from the state.
* @param _address Address of the account to retrieve.
* @return Account for the given address.
*/
function getAccount(
address _address
)
override
public
view
returns (
Lib_OVMCodec.Account memory
)
{
return accounts[_address];
}
/**
* Checks whether the state has a given account.
* @param _address Address of the account to check.
* @return Whether or not the state has the account.
*/
function hasAccount(
address _address
)
override
public
view
returns (
bool
)
{
return accounts[_address].codeHash != bytes32(0);
}
/**
* Checks whether the state has a given known empty account.
* @param _address Address of the account to check.
* @return Whether or not the state has the empty account.
*/
function hasEmptyAccount(
address _address
)
override
public
view
returns (
bool
)
{
return (
accounts[_address].codeHash == EMPTY_ACCOUNT_CODE_HASH
&& accounts[_address].nonce == 0
);
}
/**
* Sets the nonce of an account.
* @param _address Address of the account to modify.
* @param _nonce New account nonce.
*/
function setAccountNonce(
address _address,
uint256 _nonce
)
override
public
authenticated
{
accounts[_address].nonce = _nonce;
}
/**
* Gets the nonce of an account.
* @param _address Address of the account to access.
* @return Nonce of the account.
*/
function getAccountNonce(
address _address
)
override
public
view
returns (
uint256
)
{
return accounts[_address].nonce;
}
/**
* Retrieves the Ethereum address of an account.
* @param _address Address of the account to access.
* @return Corresponding Ethereum address.
*/
function getAccountEthAddress(
address _address
)
override
public
view
returns (
address
)
{
return accounts[_address].ethAddress;
}
/**
* Retrieves the storage root of an account.
* @param _address Address of the account to access.
* @return Corresponding storage root.
*/
function getAccountStorageRoot(
address _address
)
override
public
view
returns (
bytes32
)
{
return accounts[_address].storageRoot;
}
/**
* Initializes a pending account (during CREATE or CREATE2) with the default values.
* @param _address Address of the account to initialize.
*/
function initPendingAccount(
address _address
)
override
public
authenticated
{
Lib_OVMCodec.Account storage account = accounts[_address];
account.nonce = 1;
account.storageRoot = EMPTY_ACCOUNT_STORAGE_ROOT;
account.codeHash = EMPTY_ACCOUNT_CODE_HASH;
account.isFresh = true;
}
/**
* Finalizes the creation of a pending account (during CREATE or CREATE2).
* @param _address Address of the account to finalize.
* @param _ethAddress Address of the account's associated contract on Ethereum.
* @param _codeHash Hash of the account's code.
*/
function commitPendingAccount(
address _address,
address _ethAddress,
bytes32 _codeHash
)
override
public
authenticated
{
Lib_OVMCodec.Account storage account = accounts[_address];
account.ethAddress = _ethAddress;
account.codeHash = _codeHash;
}
/**
* Checks whether an account has already been retrieved, and marks it as retrieved if not.
* @param _address Address of the account to check.
* @return Whether or not the account was already loaded.
*/
function testAndSetAccountLoaded(
address _address
)
override
public
authenticated
returns (
bool
)
{
return _testAndSetItemState(
_getItemHash(_address),
ItemState.ITEM_LOADED
);
}
/**
* Checks whether an account has already been modified, and marks it as modified if not.
* @param _address Address of the account to check.
* @return Whether or not the account was already modified.
*/
function testAndSetAccountChanged(
address _address
)
override
public
authenticated
returns (
bool
)
{
return _testAndSetItemState(
_getItemHash(_address),
ItemState.ITEM_CHANGED
);
}
/**
* Attempts to mark an account as committed.
* @param _address Address of the account to commit.
* @return Whether or not the account was committed.
*/
function commitAccount(
address _address
)
override
public
authenticated
returns (
bool
)
{
bytes32 item = _getItemHash(_address);
if (itemStates[item] != ItemState.ITEM_CHANGED) {
return false;
}
itemStates[item] = ItemState.ITEM_COMMITTED;
totalUncommittedAccounts -= 1;
return true;
}
/**
* Increments the total number of uncommitted accounts.
*/
function incrementTotalUncommittedAccounts()
override
public
authenticated
{
totalUncommittedAccounts += 1;
}
/**
* Gets the total number of uncommitted accounts.
* @return Total uncommitted accounts.
*/
function getTotalUncommittedAccounts()
override
public
view
returns (
uint256
)
{
return totalUncommittedAccounts;
}
/**
* Checks whether a given account was changed during execution.
* @param _address Address to check.
* @return Whether or not the account was changed.
*/
function wasAccountChanged(
address _address
)
override
public
view
returns (
bool
)
{
bytes32 item = _getItemHash(_address);
return itemStates[item] >= ItemState.ITEM_CHANGED;
}
/**
* Checks whether a given account was committed after execution.
* @param _address Address to check.
* @return Whether or not the account was committed.
*/
function wasAccountCommitted(
address _address
)
override
public
view
returns (
bool
)
{
bytes32 item = _getItemHash(_address);
return itemStates[item] >= ItemState.ITEM_COMMITTED;
}
/************************************
* Public Functions: Storage Access *
************************************/
/**
* Changes a contract storage slot value.
* @param _contract Address of the contract to modify.
* @param _key 32 byte storage slot key.
* @param _value 32 byte storage slot value.
*/
function putContractStorage(
address _contract,
bytes32 _key,
bytes32 _value
)
override
public
authenticated
{
// A hilarious optimization. `SSTORE`ing a value of `bytes32(0)` is common enough that it's
// worth populating this with a non-zero value in advance (during the fraud proof
// initialization phase) to cut the execution-time cost down to 5000 gas.
contractStorage[_contract][_key] = _value ^ STORAGE_XOR_VALUE;
// Only used when initially populating the contract storage. OVM_ExecutionManager will
// perform a `hasContractStorage` INVALID_STATE_ACCESS check before putting any contract
// storage because writing to zero when the actual value is nonzero causes a gas
// discrepancy. Could be moved into a new `putVerifiedContractStorage` function, or
// something along those lines.
if (verifiedContractStorage[_contract][_key] == false) {
verifiedContractStorage[_contract][_key] = true;
}
}
/**
* Retrieves a contract storage slot value.
* @param _contract Address of the contract to access.
* @param _key 32 byte storage slot key.
* @return 32 byte storage slot value.
*/
function getContractStorage(
address _contract,
bytes32 _key
)
override
public
view
returns (
bytes32
)
{
// Storage XOR system doesn't work for newly created contracts that haven't set this
// storage slot value yet.
if (
verifiedContractStorage[_contract][_key] == false
&& accounts[_contract].isFresh
) {
return bytes32(0);
}
// See `putContractStorage` for more information about the XOR here.
return contractStorage[_contract][_key] ^ STORAGE_XOR_VALUE;
}
/**
* Checks whether a contract storage slot exists in the state.
* @param _contract Address of the contract to access.
* @param _key 32 byte storage slot key.
* @return Whether or not the key was set in the state.
*/
function hasContractStorage(
address _contract,
bytes32 _key
)
override
public
view
returns (
bool
)
{
return verifiedContractStorage[_contract][_key] || accounts[_contract].isFresh;
}
/**
* Checks whether a storage slot has already been retrieved, and marks it as retrieved if not.
* @param _contract Address of the contract to check.
* @param _key 32 byte storage slot key.
* @return Whether or not the slot was already loaded.
*/
function testAndSetContractStorageLoaded(
address _contract,
bytes32 _key
)
override
public
authenticated
returns (
bool
)
{
return _testAndSetItemState(
_getItemHash(_contract, _key),
ItemState.ITEM_LOADED
);
}
/**
* Checks whether a storage slot has already been modified, and marks it as modified if not.
* @param _contract Address of the contract to check.
* @param _key 32 byte storage slot key.
* @return Whether or not the slot was already modified.
*/
function testAndSetContractStorageChanged(
address _contract,
bytes32 _key
)
override
public
authenticated
returns (
bool
)
{
return _testAndSetItemState(
_getItemHash(_contract, _key),
ItemState.ITEM_CHANGED
);
}
/**
* Attempts to mark a storage slot as committed.
* @param _contract Address of the account to commit.
* @param _key 32 byte slot key to commit.
* @return Whether or not the slot was committed.
*/
function commitContractStorage(
address _contract,
bytes32 _key
)
override
public
authenticated
returns (
bool
)
{
bytes32 item = _getItemHash(_contract, _key);
if (itemStates[item] != ItemState.ITEM_CHANGED) {
return false;
}
itemStates[item] = ItemState.ITEM_COMMITTED;
totalUncommittedContractStorage -= 1;
return true;
}
/**
* Increments the total number of uncommitted storage slots.
*/
function incrementTotalUncommittedContractStorage()
override
public
authenticated
{
totalUncommittedContractStorage += 1;
}
/**
* Gets the total number of uncommitted storage slots.
* @return Total uncommitted storage slots.
*/
function getTotalUncommittedContractStorage()
override
public
view
returns (
uint256
)
{
return totalUncommittedContractStorage;
}
/**
* Checks whether a given storage slot was changed during execution.
* @param _contract Address to check.
* @param _key Key of the storage slot to check.
* @return Whether or not the storage slot was changed.
*/
function wasContractStorageChanged(
address _contract,
bytes32 _key
)
override
public
view
returns (
bool
)
{
bytes32 item = _getItemHash(_contract, _key);
return itemStates[item] >= ItemState.ITEM_CHANGED;
}
/**
* Checks whether a given storage slot was committed after execution.
* @param _contract Address to check.
* @param _key Key of the storage slot to check.
* @return Whether or not the storage slot was committed.
*/
function wasContractStorageCommitted(
address _contract,
bytes32 _key
)
override
public
view
returns (
bool
)
{
bytes32 item = _getItemHash(_contract, _key);
return itemStates[item] >= ItemState.ITEM_COMMITTED;
}
/**********************
* Internal Functions *
**********************/
/**
* Generates a unique hash for an address.
* @param _address Address to generate a hash for.
* @return Unique hash for the given address.
*/
function _getItemHash(
address _address
)
internal
pure
returns (
bytes32
)
{
return keccak256(abi.encodePacked(_address));
}
/**
* Generates a unique hash for an address/key pair.
* @param _contract Address to generate a hash for.
* @param _key Key to generate a hash for.
* @return Unique hash for the given pair.
*/
function _getItemHash(
address _contract,
bytes32 _key
)
internal
pure
returns (
bytes32
)
{
return keccak256(abi.encodePacked(
_contract,
_key
));
}
/**
* Checks whether an item is in a particular state (ITEM_LOADED or ITEM_CHANGED) and sets the
* item to the provided state if not.
* @param _item 32 byte item ID to check.
* @param _minItemState Minimum state that must be satisfied by the item.
* @return Whether or not the item was already in the state.
*/
function _testAndSetItemState(
bytes32 _item,
ItemState _minItemState
)
internal
returns (
bool
)
{
bool wasItemState = itemStates[_item] >= _minItemState;
if (wasItemState == false) {
itemStates[_item] = _minItemState;
}
return wasItemState;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
/* Interface Imports */
import { iOVM_StateManager } from "../../iOVM/execution/iOVM_StateManager.sol";
import { iOVM_StateManagerFactory } from "../../iOVM/execution/iOVM_StateManagerFactory.sol";
/* Contract Imports */
import { OVM_StateManager } from "./OVM_StateManager.sol";
/**
* @title OVM_StateManagerFactory
* @dev The State Manager Factory is called by a State Transitioner's init code, to create a new
* State Manager for use in the Fraud Verification process.
*
* Compiler used: solc
* Runtime target: EVM
*/
contract OVM_StateManagerFactory is iOVM_StateManagerFactory {
/********************
* Public Functions *
********************/
/**
* Creates a new OVM_StateManager
* @param _owner Owner of the created contract.
* @return New OVM_StateManager instance.
*/
function create(
address _owner
)
override
public
returns (
iOVM_StateManager
)
{
return new OVM_StateManager(_owner);
}
}
// SPDX-License-Identifier: CC0-1.0
/* ERC1820 Pseudo-introspection Registry Contract
* This standard defines a universal registry smart contract where any address (contract or regular
* account) can register which interface it supports and which smart contract is responsible for its
* implementation.
*
* Written in 2019 by Jordi Baylina and Jacques Dafflon
*
* To the extent possible under law, the author(s) have dedicated all copyright and related and
* neighboring rights to this software to the public domain worldwide. This software is distributed
* without any warranty.
*
* You should have received a copy of the CC0 Public Domain Dedication along with this software.
* If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
pragma solidity >0.5.0 <0.8.0;
/// @dev The interface a contract MUST implement if it is the implementer of
/// some (other) interface for any address other than itself.
interface ERC1820ImplementerInterface {
/// @notice Indicates whether the contract implements the interface 'interfaceHash' for the
/// address 'addr' or not.
/// @param interfaceHash keccak256 hash of the name of the interface
/// @param addr Address for which the contract will implement the interface
/// @return ERC1820_ACCEPT_MAGIC only if the contract implements 'interfaceHash' for the address
/// 'addr'.
function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view
returns(bytes32);
}
/**
* @title ERC1820 Pseudo-introspection Registry Contract
* @author Jordi Baylina and Jacques Dafflon
* @dev This contract is the official implementation of the ERC1820 Registry
* For more details, see https://eips.ethereum.org/EIPS/eip-1820
*
* Compiler used: optimistic-solc
* Runtime target: OVM
*/
contract ERC1820Registry {
bytes4 constant internal INVALID_ID = 0xffffffff;
bytes4 constant internal ERC165ID = 0x01ffc9a7;
bytes32 constant internal ERC1820_ACCEPT_MAGIC =
keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));
mapping(address => mapping(bytes32 => address)) internal interfaces;
mapping(address => address) internal managers;
mapping(address => mapping(bytes4 => bool)) internal erc165Cached;
/// @notice Indicates a contract is the 'implementer' of 'interfaceHash' for 'addr'.
event InterfaceImplementerSet(
address indexed addr,
bytes32 indexed interfaceHash,
address indexed implementer);
/// @notice Indicates 'newManager' is the address of the new manager for 'addr'.
event ManagerChanged(address indexed addr, address indexed newManager);
/// @notice Query if an address implements an interface and through which contract.
/// @param _addr Address being queried for the implementer of an interface.
// (If '_addr' is the zero address then 'msg.sender' is assumed.)
/// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient'
// interface.
/// @return The address of the contract which implements the interface '_interfaceHash' for
// '_addr' or '0' if '_addr' did not register an implementer for this interface.
function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view
returns (address) {
address addr = _addr == address(0) ? msg.sender : _addr;
if (isERC165Interface(_interfaceHash)) {
bytes4 erc165InterfaceHash = bytes4(_interfaceHash);
return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : address(0);
}
return interfaces[addr][_interfaceHash];
}
/// @notice Sets the contract which implements a specific interface for an address.
// Only the manager defined for that address can set it.
// (Each address is the manager for itself until it sets a new manager.)
/// @param _addr Address for which to set the interface.
// (If '_addr' is the zero address then 'msg.sender' is assumed.)
/// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
// e.g. 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient'
// interface.
/// @param _implementer Contract address implementing '_interfaceHash' for '_addr'.
function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer)
external {
address addr = _addr == address(0) ? msg.sender : _addr;
require(getManager(addr) == msg.sender, "Not the manager");
require(!isERC165Interface(_interfaceHash), "Must not be an ERC165 hash");
if (_implementer != address(0) && _implementer != msg.sender) {
require(
ERC1820ImplementerInterface(_implementer)
.canImplementInterfaceForAddress(_interfaceHash, addr) == ERC1820_ACCEPT_MAGIC,
"Does not implement the interface"
);
}
interfaces[addr][_interfaceHash] = _implementer;
emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);
}
/// @notice Sets '_newManager' as manager for '_addr'.
// The new manager will be able to call 'setInterfaceImplementer' for '_addr'.
/// @param _addr Address for which to set the new manager.
/// @param _newManager Address of the new manager for 'addr'.
// (Pass '0x0' to reset the manager to '_addr'.)
function setManager(address _addr, address _newManager) external {
require(getManager(_addr) == msg.sender, "Not the manager");
managers[_addr] = _newManager == _addr ? address(0) : _newManager;
emit ManagerChanged(_addr, _newManager);
}
/// @notice Get the manager of an address.
/// @param _addr Address for which to return the manager.
/// @return Address of the manager for a given address.
function getManager(address _addr) public view returns(address) {
// By default the manager of an address is the same address
if (managers[_addr] == address(0)) {
return _addr;
} else {
return managers[_addr];
}
}
/// @notice Compute the keccak256 hash of an interface given its name.
/// @param _interfaceName Name of the interface.
/// @return The keccak256 hash of an interface name.
function interfaceHash(string calldata _interfaceName) external pure returns(bytes32) {
return keccak256(abi.encodePacked(_interfaceName));
}
/* --- ERC165 Related Functions --- */
/* --- Developed in collaboration with William Entriken. --- */
/// @notice Updates the cache with whether the contract implements an ERC165 interface or not.
/// @param _contract Address of the contract for which to update the cache.
/// @param _interfaceId ERC165 interface for which to update the cache.
function updateERC165Cache(address _contract, bytes4 _interfaceId) external {
interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(
_contract, _interfaceId) ? _contract : address(0);
erc165Cached[_contract][_interfaceId] = true;
}
/// @notice Checks whether a contract implements an ERC165 interface or not.
// If the result is not cached a direct lookup on the contract address is performed.
// If the result is not cached or the cached value is out-of-date, the cache MUST be updated
// manually by calling
// 'updateERC165Cache' with the contract address.
/// @param _contract Address of the contract to check.
/// @param _interfaceId ERC165 interface to check.
/// @return True if '_contract' implements '_interfaceId', false otherwise.
function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view
returns (bool) {
if (!erc165Cached[_contract][_interfaceId]) {
return implementsERC165InterfaceNoCache(_contract, _interfaceId);
}
return interfaces[_contract][_interfaceId] == _contract;
}
/// @notice Checks whether a contract implements an ERC165 interface or not without using nor
// updating the cache.
/// @param _contract Address of the contract to check.
/// @param _interfaceId ERC165 interface to check.
/// @return True if '_contract' implements '_interfaceId', false otherwise.
function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view
returns (bool) {
uint256 success;
uint256 result;
(success, result) = noThrowCall(_contract, ERC165ID);
if (success == 0 || result == 0) {
return false;
}
(success, result) = noThrowCall(_contract, INVALID_ID);
if (success == 0 || result != 0) {
return false;
}
(success, result) = noThrowCall(_contract, _interfaceId);
if (success == 1 && result == 1) {
return true;
}
return false;
}
/// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.
/// @param _interfaceHash The hash to check.
/// @return True if '_interfaceHash' is an ERC165 interface (ending with 28 zeroes),
// false otherwise.
function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {
// solhint-disable-next-line max-line-length
return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;
}
/// @dev Make a call on a contract without throwing if the function does not exist.
function noThrowCall(address _contract, bytes4 _interfaceId)
internal view returns (uint256 success, uint256 result)
{
bytes4 erc165ID = ERC165ID;
assembly {
let x := mload(0x40) // Find empty storage location using "free memory pointer"
mstore(x, erc165ID) // Place signature at beginning of empty storage
mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature
success := staticcall(
30000, // 30k gas
_contract, // To addr
x, // Inputs are stored at location x
0x24, // Inputs are 36 (4 + 32) bytes long
x, // Store output over input (saves space)
0x20 // Outputs are 32 bytes long
)
result := mload(x) // Load the result
}
}
}
...@@ -11,7 +11,6 @@ import { iOVM_DeployerWhitelist } from "../../iOVM/predeploys/iOVM_DeployerWhite ...@@ -11,7 +11,6 @@ import { iOVM_DeployerWhitelist } from "../../iOVM/predeploys/iOVM_DeployerWhite
* which are allowed to deploy contracts on Layer2. The Execution Manager will only allow an * which are allowed to deploy contracts on Layer2. The Execution Manager will only allow an
* ovmCREATE or ovmCREATE2 operation to proceed if the deployer's address whitelisted. * ovmCREATE or ovmCREATE2 operation to proceed if the deployer's address whitelisted.
* *
* Compiler used: optimistic-solc
* Runtime target: OVM * Runtime target: OVM
*/ */
contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist { contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
......
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_ECDSAContractAccount } from "../../iOVM/predeploys/iOVM_ECDSAContractAccount.sol";
/* Library Imports */
import { Lib_EIP155Tx } from "../../libraries/codec/Lib_EIP155Tx.sol";
import { Lib_ExecutionManagerWrapper } from
"../../libraries/wrappers/Lib_ExecutionManagerWrapper.sol";
import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployAddresses.sol";
/* Contract Imports */
import { OVM_ETH } from "../predeploys/OVM_ETH.sol";
/* External Imports */
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { ECDSA } from "@openzeppelin/contracts/cryptography/ECDSA.sol";
/**
* @title OVM_ECDSAContractAccount
* @dev The ECDSA Contract Account can be used as the implementation for a ProxyEOA deployed by the
* ovmCREATEEOA operation. It enables backwards compatibility with Ethereum's Layer 1, by
* providing EIP155 formatted transaction encodings.
*
* Compiler used: optimistic-solc
* Runtime target: OVM
*/
contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
/*************
* Libraries *
*************/
using Lib_EIP155Tx for Lib_EIP155Tx.EIP155Tx;
/*************
* Constants *
*************/
// TODO: should be the amount sufficient to cover the gas costs of all of the transactions up
// to and including the CALL/CREATE which forms the entrypoint of the transaction.
uint256 constant EXECUTION_VALIDATION_GAS_OVERHEAD = 25000;
/********************
* Public Functions *
********************/
/**
* No-op fallback mirrors behavior of calling an EOA on L1.
*/
fallback()
external
payable
{
return;
}
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(
bytes32 hash,
bytes memory signature
)
public
view
returns (
bytes4 magicValue
)
{
return ECDSA.recover(hash, signature) == address(this) ?
this.isValidSignature.selector :
bytes4(0);
}
/**
* Executes a signed transaction.
* @param _transaction Signed EIP155 transaction.
* @return Whether or not the call returned (rather than reverted).
* @return Data returned by the call.
*/
function execute(
Lib_EIP155Tx.EIP155Tx memory _transaction
)
override
public
returns (
bool,
bytes memory
)
{
// Address of this contract within the ovm (ovmADDRESS) should be the same as the
// recovered address of the user who signed this message. This is how we manage to shim
// account abstraction even though the user isn't a contract.
require(
_transaction.sender() == Lib_ExecutionManagerWrapper.ovmADDRESS(),
"Signature provided for EOA transaction execution is invalid."
);
require(
_transaction.chainId == Lib_ExecutionManagerWrapper.ovmCHAINID(),
"Transaction signed with wrong chain ID"
);
// Need to make sure that the transaction nonce is right.
require(
_transaction.nonce == Lib_ExecutionManagerWrapper.ovmGETNONCE(),
"Transaction nonce does not match the expected nonce."
);
// TEMPORARY: Disable gas checks for mainnet.
// // Need to make sure that the gas is sufficient to execute the transaction.
// require(
// gasleft() >= SafeMath.add(transaction.gasLimit, EXECUTION_VALIDATION_GAS_OVERHEAD),
// "Gas is not sufficient to execute the transaction."
// );
// Transfer fee to relayer.
require(
OVM_ETH(Lib_PredeployAddresses.OVM_ETH).transfer(
Lib_PredeployAddresses.SEQUENCER_FEE_WALLET,
SafeMath.mul(_transaction.gasLimit, _transaction.gasPrice)
),
"Fee was not transferred to relayer."
);
if (_transaction.isCreate) {
// TEMPORARY: Disable value transfer for contract creations.
require(
_transaction.value == 0,
"Value transfer in contract creation not supported."
);
(address created, bytes memory revertdata) = Lib_ExecutionManagerWrapper.ovmCREATE(
_transaction.data
);
// Return true if the contract creation succeeded, false w/ revertdata otherwise.
if (created != address(0)) {
return (true, abi.encode(created));
} else {
return (false, revertdata);
}
} else {
// We only want to bump the nonce for `ovmCALL` because `ovmCREATE` automatically bumps
// the nonce of the calling account. Normally an EOA would bump the nonce for both
// cases, but since this is a contract we'd end up bumping the nonce twice.
Lib_ExecutionManagerWrapper.ovmINCREMENTNONCE();
// NOTE: Upgrades are temporarily disabled because users can, in theory, modify their
// EOA so that they don't have to pay any fees to the sequencer. Function will remain
// disabled until a robust solution is in place.
require(
_transaction.to != Lib_ExecutionManagerWrapper.ovmADDRESS(),
"Calls to self are disabled until upgradability is re-enabled."
);
return _transaction.to.call{value: _transaction.value}(_transaction.data);
}
}
}
...@@ -13,7 +13,6 @@ import { IWETH9 } from "../../libraries/standards/IWETH9.sol"; ...@@ -13,7 +13,6 @@ import { IWETH9 } from "../../libraries/standards/IWETH9.sol";
* @dev The ETH predeploy provides an ERC20 interface for ETH deposited to Layer 2. Note that * @dev The ETH predeploy provides an ERC20 interface for ETH deposited to Layer 2. Note that
* unlike on Layer 1, Layer 2 accounts do not have a balance field. * unlike on Layer 1, Layer 2 accounts do not have a balance field.
* *
* Compiler used: optimistic-solc
* Runtime target: OVM * Runtime target: OVM
*/ */
contract OVM_ETH is L2StandardERC20, IWETH9 { contract OVM_ETH is L2StandardERC20, IWETH9 {
......
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
/* Library Imports */
import { Lib_ErrorUtils } from "../../libraries/utils/Lib_ErrorUtils.sol";
/**
* @title OVM_ExecutionManagerWrapper
* @dev This contract is a thin wrapper around the `kall` builtin. By making this contract a
* predeployed contract, we can restrict evm solc incompatibility to this one contract. Other
* contracts will typically call this contract via `Lib_ExecutionManagerWrapper`.
*
* Compiler used: optimistic-solc
* Runtime target: OVM
*/
contract OVM_ExecutionManagerWrapper {
/*********************
* Fallback Function *
*********************/
fallback()
external
payable
{
// DO NOTHING FOR NOW
}
}
...@@ -11,7 +11,6 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; ...@@ -11,7 +11,6 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
* transactions. When the system is more congested, the l2 gas price will increase and fees * transactions. When the system is more congested, the l2 gas price will increase and fees
* will also increase as a result. * will also increase as a result.
* *
* Compiler used: optimistic-solc
* Runtime target: OVM * Runtime target: OVM
*/ */
contract OVM_GasPriceOracle is Ownable { contract OVM_GasPriceOracle is Ownable {
......
...@@ -13,8 +13,6 @@ pragma solidity >0.5.0 <0.8.0; ...@@ -13,8 +13,6 @@ pragma solidity >0.5.0 <0.8.0;
* because there is no corresponding operation in the EVM which the the optimistic solidity compiler * because there is no corresponding operation in the EVM which the the optimistic solidity compiler
* can be replaced with a call to the ExecutionManager's ovmL1TXORIGIN() function. * can be replaced with a call to the ExecutionManager's ovmL1TXORIGIN() function.
* *
*
* Compiler used: solc
* Runtime target: OVM * Runtime target: OVM
*/ */
contract OVM_L1MessageSender { contract OVM_L1MessageSender {
......
...@@ -11,7 +11,6 @@ import { iOVM_L2ToL1MessagePasser } from "../../iOVM/predeploys/iOVM_L2ToL1Messa ...@@ -11,7 +11,6 @@ import { iOVM_L2ToL1MessagePasser } from "../../iOVM/predeploys/iOVM_L2ToL1Messa
* _verifyStorageProof function, which verifies the existence of the transaction hash in this * _verifyStorageProof function, which verifies the existence of the transaction hash in this
* contract's `sentMessages` mapping. * contract's `sentMessages` mapping.
* *
* Compiler used: optimistic-solc
* Runtime target: OVM * Runtime target: OVM
*/ */
contract OVM_L2ToL1MessagePasser is iOVM_L2ToL1MessagePasser { contract OVM_L2ToL1MessagePasser is iOVM_L2ToL1MessagePasser {
......
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
/* Library Imports */
import { Lib_Bytes32Utils } from "../../libraries/utils/Lib_Bytes32Utils.sol";
import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployAddresses.sol";
import { Lib_ExecutionManagerWrapper } from
"../../libraries/wrappers/Lib_ExecutionManagerWrapper.sol";
/**
* @title OVM_ProxyEOA
* @dev The Proxy EOA contract uses a delegate call to execute the logic in an implementation
* contract. In combination with the logic implemented in the ECDSA Contract Account, this enables
* a form of upgradable 'account abstraction' on layer 2.
*
* Compiler used: optimistic-solc
* Runtime target: OVM
*/
contract OVM_ProxyEOA {
/**********
* Events *
**********/
event Upgraded(
address indexed implementation
);
/*************
* Constants *
*************/
// solhint-disable-next-line max-line-length
bytes32 constant IMPLEMENTATION_KEY = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; //bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1);
/*********************
* Fallback Function *
*********************/
fallback()
external
payable
{
(bool success, bytes memory returndata) = getImplementation().delegatecall(msg.data);
if (success) {
assembly {
return(add(returndata, 0x20), mload(returndata))
}
} else {
assembly {
revert(add(returndata, 0x20), mload(returndata))
}
}
}
// WARNING: We use the deployed bytecode of this contract as a template to create ProxyEOA
// contracts. As a result, we must *not* perform any constructor logic. Use initialization
// functions if necessary.
/********************
* Public Functions *
********************/
/**
* Changes the implementation address.
* @param _implementation New implementation address.
*/
function upgrade(
address _implementation
)
external
{
require(
msg.sender == Lib_ExecutionManagerWrapper.ovmADDRESS(),
"EOAs can only upgrade their own EOA implementation."
);
_setImplementation(_implementation);
emit Upgraded(_implementation);
}
/**
* Gets the address of the current implementation.
* @return Current implementation address.
*/
function getImplementation()
public
view
returns (
address
)
{
bytes32 addr32;
assembly {
addr32 := sload(IMPLEMENTATION_KEY)
}
address implementation = Lib_Bytes32Utils.toAddress(addr32);
if (implementation == address(0)) {
return Lib_PredeployAddresses.ECDSA_CONTRACT_ACCOUNT;
} else {
return implementation;
}
}
/**********************
* Internal Functions *
**********************/
function _setImplementation(
address _implementation
)
internal
{
bytes32 addr32 = Lib_Bytes32Utils.fromAddress(_implementation);
assembly {
sstore(IMPLEMENTATION_KEY, addr32)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_EIP155Tx } from "../../libraries/codec/Lib_EIP155Tx.sol";
import { Lib_ExecutionManagerWrapper } from
"../../libraries/wrappers/Lib_ExecutionManagerWrapper.sol";
import { iOVM_ECDSAContractAccount } from "../../iOVM/predeploys/iOVM_ECDSAContractAccount.sol";
/**
* @title OVM_SequencerEntrypoint
* @dev The Sequencer Entrypoint is a predeploy which, despite its name, can in fact be called by
* any account. It accepts a more efficient compressed calldata format, which it decompresses and
* encodes to the standard EIP155 transaction format.
* Compiler used: optimistic-solc
* Runtime target: OVM
*/
contract OVM_SequencerEntrypoint {
/*************
* Libraries *
*************/
using Lib_EIP155Tx for Lib_EIP155Tx.EIP155Tx;
/*********************
* Fallback Function *
*********************/
/**
* Expects an RLP-encoded EIP155 transaction as input. See the EIP for a more detailed
* description of this transaction format:
* https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md
*/
fallback()
external
{
// We use this twice, so it's more gas efficient to store a copy of it (barely).
bytes memory encodedTx = msg.data;
// Decode the tx with the correct chain ID.
Lib_EIP155Tx.EIP155Tx memory transaction = Lib_EIP155Tx.decode(
encodedTx,
Lib_ExecutionManagerWrapper.ovmCHAINID()
);
// Value is computed on the fly. Keep it in the stack to save some gas.
address target = transaction.sender();
bool isEmptyContract;
assembly {
isEmptyContract := iszero(extcodesize(target))
}
// If the account is empty, deploy the default EOA to that address.
if (isEmptyContract) {
Lib_ExecutionManagerWrapper.ovmCREATEEOA(
transaction.hash(),
transaction.recoveryParam,
transaction.r,
transaction.s
);
}
// Forward the transaction over to the EOA.
(bool success, bytes memory returndata) = target.call(
abi.encodeWithSelector(iOVM_ECDSAContractAccount.execute.selector, transaction)
);
if (success) {
assembly {
return(add(returndata, 0x20), mload(returndata))
}
} else {
assembly {
revert(add(returndata, 0x20), mload(returndata))
}
}
}
}
...@@ -13,7 +13,6 @@ import { OVM_L2StandardBridge } from "../bridge/tokens/OVM_L2StandardBridge.sol" ...@@ -13,7 +13,6 @@ import { OVM_L2StandardBridge } from "../bridge/tokens/OVM_L2StandardBridge.sol"
* @dev Simple holding contract for fees paid to the Sequencer. Likely to be replaced in the future * @dev Simple holding contract for fees paid to the Sequencer. Likely to be replaced in the future
* but "good enough for now". * but "good enough for now".
* *
* Compiler used: optimistic-solc
* Runtime target: OVM * Runtime target: OVM
*/ */
contract OVM_SequencerFeeVault { contract OVM_SequencerFeeVault {
......
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
import { iOVM_BondManager } from "../../iOVM/verification/iOVM_BondManager.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/// Minimal contract to be inherited by contracts consumed by users that provide
/// data for fraud proofs
abstract contract Abs_FraudContributor is Lib_AddressResolver {
/// Decorate your functions with this modifier to store how much total gas was
/// consumed by the sender, to reward users fairly
modifier contributesToFraudProof(bytes32 preStateRoot, bytes32 txHash) {
uint256 startGas = gasleft();
_;
uint256 gasSpent = startGas - gasleft();
iOVM_BondManager(resolve("OVM_BondManager"))
.recordGasSpent(preStateRoot, txHash, msg.sender, gasSpent);
}
}
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0; pragma solidity >0.5.0 <0.8.0;
/* Library Imports */
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */ /* Interface Imports */
import { iOVM_BondManager, Errors, ERC20 } from "../../iOVM/verification/iOVM_BondManager.sol"; import { iOVM_BondManager } from "../../iOVM/verification/iOVM_BondManager.sol";
import { iOVM_FraudVerifier } from "../../iOVM/verification/iOVM_FraudVerifier.sol";
/* Contract Imports */
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/** /**
* @title OVM_BondManager * @title OVM_BondManager
* @dev The Bond Manager contract handles deposits in the form of an ERC20 token from bonded * @dev This contract is, for now, a stub of the "real" OVM_BondManager that does nothing but
* Proposers. It also handles the accounting of gas costs spent by a Verifier during the course of a * allow the "OVM_Proposer" to submit state root batches.
* fraud proof. In the event of a successful fraud proof, the fraudulent Proposer's bond is slashed,
* and the Verifier's gas costs are refunded.
* *
* Compiler used: solc
* Runtime target: EVM * Runtime target: EVM
*/ */
contract OVM_BondManager is iOVM_BondManager, Lib_AddressResolver { contract OVM_BondManager is iOVM_BondManager, Lib_AddressResolver {
/****************************
* Constants and Parameters *
****************************/
/// The period to find the earliest fraud proof for a publisher
uint256 public constant multiFraudProofPeriod = 7 days;
/// The dispute period
uint256 public constant disputePeriodSeconds = 7 days;
/// The minimum collateral a sequencer must post
uint256 public constant requiredCollateral = 1 ether;
/*******************************************
* Contract Variables: Contract References *
*******************************************/
/// The bond token
ERC20 immutable public token;
/********************************************
* Contract Variables: Internal Accounting *
*******************************************/
/// The bonds posted by each proposer
mapping(address => Bond) public bonds;
/// For each pre-state root, there's an array of witnessProviders that must be rewarded
/// for posting witnesses
mapping(bytes32 => Rewards) public witnessProviders;
/***************
* Constructor *
***************/
/// Initializes with a ERC20 token to be used for the fidelity bonds
/// and with the Address Manager
constructor( constructor(
ERC20 _token,
address _libAddressManager address _libAddressManager
) )
Lib_AddressResolver(_libAddressManager) Lib_AddressResolver(_libAddressManager)
{ {}
token = _token;
}
/********************
* Public Functions *
********************/
/// Adds `who` to the list of witnessProviders for the provided `preStateRoot`.
function recordGasSpent(bytes32 _preStateRoot, bytes32 _txHash, address who, uint256 gasSpent)
override public {
// The sender must be the transitioner that corresponds to the claimed pre-state root
address transitioner =
address(iOVM_FraudVerifier(resolve("OVM_FraudVerifier"))
.getStateTransitioner(_preStateRoot, _txHash));
require(transitioner == msg.sender, Errors.ONLY_TRANSITIONER);
witnessProviders[_preStateRoot].total += gasSpent;
witnessProviders[_preStateRoot].gasSpent[who] += gasSpent;
}
/// Slashes + distributes rewards or frees up the sequencer's bond, only called by
/// `FraudVerifier.finalizeFraudVerification`
function finalize(bytes32 _preStateRoot, address publisher, uint256 timestamp) override public {
require(msg.sender == resolve("OVM_FraudVerifier"), Errors.ONLY_FRAUD_VERIFIER);
require(witnessProviders[_preStateRoot].canClaim == false, Errors.ALREADY_FINALIZED);
// allow users to claim from that state root's
// pool of collateral (effectively slashing the sequencer)
witnessProviders[_preStateRoot].canClaim = true;
Bond storage bond = bonds[publisher];
if (bond.firstDisputeAt == 0) {
bond.firstDisputeAt = block.timestamp;
bond.earliestDisputedStateRoot = _preStateRoot;
bond.earliestTimestamp = timestamp;
} else if (
// only update the disputed state root for the publisher if it's within
// the dispute period _and_ if it's before the previous one
block.timestamp < bond.firstDisputeAt + multiFraudProofPeriod &&
timestamp < bond.earliestTimestamp
) {
bond.earliestDisputedStateRoot = _preStateRoot;
bond.earliestTimestamp = timestamp;
}
// if the fraud proof's dispute period does not intersect with the function recordGasSpent(
// withdrawal's timestamp, then the user should not be slashed bytes32 _preStateRoot,
// e.g if a user at day 10 submits a withdrawal, and a fraud proof bytes32 _txHash,
// from day 1 gets published, the user won't be slashed since day 8 (1d + 7d) address _who,
// is before the user started their withdrawal. on the contrary, if the user uint256 _gasSpent
// had started their withdrawal at, say, day 6, they would be slashed )
if ( override
bond.withdrawalTimestamp != 0 && public
uint256(bond.withdrawalTimestamp) > timestamp + disputePeriodSeconds && {}
bond.state == State.WITHDRAWING
) { function finalize(
return; bytes32 _preStateRoot,
} address _publisher,
uint256 _timestamp
// slash! )
bond.state = State.NOT_COLLATERALIZED; override
} public
{}
/// Sequencers call this function to post collateral which will be used for
/// the `appendBatch` call function deposit()
function deposit() override public { override
require( public
token.transferFrom(msg.sender, address(this), requiredCollateral), {}
Errors.ERC20_ERR
); function startWithdrawal()
override
// This cannot overflow public
bonds[msg.sender].state = State.COLLATERALIZED; {}
}
function finalizeWithdrawal()
/// Starts the withdrawal for a publisher override
function startWithdrawal() override public { public
Bond storage bond = bonds[msg.sender]; {}
require(bond.withdrawalTimestamp == 0, Errors.WITHDRAWAL_PENDING);
require(bond.state == State.COLLATERALIZED, Errors.WRONG_STATE); function claim(
address _who
bond.state = State.WITHDRAWING; )
bond.withdrawalTimestamp = uint32(block.timestamp); override
} public
{}
/// Finalizes a pending withdrawal from a publisher
function finalizeWithdrawal() override public {
Bond storage bond = bonds[msg.sender];
require(
block.timestamp >= uint256(bond.withdrawalTimestamp) + disputePeriodSeconds,
Errors.TOO_EARLY
);
require(bond.state == State.WITHDRAWING, Errors.SLASHED);
// refunds!
bond.state = State.NOT_COLLATERALIZED;
bond.withdrawalTimestamp = 0;
require(
token.transfer(msg.sender, requiredCollateral),
Errors.ERC20_ERR
);
}
/// Claims the user's reward for the witnesses they provided for the earliest
/// disputed state root of the designated publisher
function claim(address who) override public {
Bond storage bond = bonds[who];
require(
block.timestamp >= bond.firstDisputeAt + multiFraudProofPeriod,
Errors.WAIT_FOR_DISPUTES
);
// reward the earliest state root for this publisher
bytes32 _preStateRoot = bond.earliestDisputedStateRoot;
Rewards storage rewards = witnessProviders[_preStateRoot];
// only allow claiming if fraud was proven in `finalize`
require(rewards.canClaim, Errors.CANNOT_CLAIM);
// proportional allocation - only reward 50% (rest gets locked in the
// contract forever
uint256 amount = (requiredCollateral * rewards.gasSpent[msg.sender]) / (2 * rewards.total);
// reset the user's spent gas so they cannot double claim
rewards.gasSpent[msg.sender] = 0;
// transfer
require(token.transfer(msg.sender, amount), Errors.ERC20_ERR);
}
/// Checks if the user is collateralized function isCollateralized(
function isCollateralized(address who) override public view returns (bool) { address _who
return bonds[who].state == State.COLLATERALIZED; )
override
public
view
returns (
bool
)
{
// Only authenticate sequencer to submit state root batches.
return _who == resolve("OVM_Proposer");
} }
/// Gets how many witnesses the user has provided for the state root function getGasSpent(
function getGasSpent(bytes32 preStateRoot, address who) override public view returns (uint256) { bytes32 _preStateRoot,
return witnessProviders[preStateRoot].gasSpent[who]; address _who
)
override
public
pure
returns (
uint256
)
{
return 0;
} }
} }
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */
import { iOVM_FraudVerifier } from "../../iOVM/verification/iOVM_FraudVerifier.sol";
import { iOVM_StateTransitioner } from "../../iOVM/verification/iOVM_StateTransitioner.sol";
import { iOVM_StateTransitionerFactory } from
"../../iOVM/verification/iOVM_StateTransitionerFactory.sol";
import { iOVM_BondManager } from "../../iOVM/verification/iOVM_BondManager.sol";
import { iOVM_StateCommitmentChain } from "../../iOVM/chain/iOVM_StateCommitmentChain.sol";
import { iOVM_CanonicalTransactionChain } from
"../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
/* Contract Imports */
import { Abs_FraudContributor } from "./Abs_FraudContributor.sol";
/**
* @title OVM_FraudVerifier
* @dev The Fraud Verifier contract coordinates the entire fraud proof verification process.
* If the fraud proof was successful it prunes any state batches from State Commitment Chain
* which were published after the fraudulent state root.
*
* Compiler used: solc
* Runtime target: EVM
*/
contract OVM_FraudVerifier is Lib_AddressResolver, Abs_FraudContributor, iOVM_FraudVerifier {
/*******************************************
* Contract Variables: Internal Accounting *
*******************************************/
mapping (bytes32 => iOVM_StateTransitioner) internal transitioners;
/***************
* Constructor *
***************/
/**
* @param _libAddressManager Address of the Address Manager.
*/
constructor(
address _libAddressManager
)
Lib_AddressResolver(_libAddressManager)
{}
/***************************************
* Public Functions: Transition Status *
***************************************/
/**
* Retrieves the state transitioner for a given root.
* @param _preStateRoot State root to query a transitioner for.
* @return _transitioner Corresponding state transitioner contract.
*/
function getStateTransitioner(
bytes32 _preStateRoot,
bytes32 _txHash
)
override
public
view
returns (
iOVM_StateTransitioner _transitioner
)
{
return transitioners[keccak256(abi.encodePacked(_preStateRoot, _txHash))];
}
/****************************************
* Public Functions: Fraud Verification *
****************************************/
/**
* Begins the fraud verification process.
* @param _preStateRoot State root before the fraudulent transaction.
* @param _preStateRootBatchHeader Batch header for the provided pre-state root.
* @param _preStateRootProof Inclusion proof for the provided pre-state root.
* @param _transaction OVM transaction claimed to be fraudulent.
* @param _txChainElement OVM transaction chain element.
* @param _transactionBatchHeader Batch header for the provided transaction.
* @param _transactionProof Inclusion proof for the provided transaction.
*/
function initializeFraudVerification(
bytes32 _preStateRoot,
Lib_OVMCodec.ChainBatchHeader memory _preStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _preStateRootProof,
Lib_OVMCodec.Transaction memory _transaction,
Lib_OVMCodec.TransactionChainElement memory _txChainElement,
Lib_OVMCodec.ChainBatchHeader memory _transactionBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _transactionProof
)
override
public
contributesToFraudProof(_preStateRoot, Lib_OVMCodec.hashTransaction(_transaction))
{
bytes32 _txHash = Lib_OVMCodec.hashTransaction(_transaction);
if (_hasStateTransitioner(_preStateRoot, _txHash)) {
return;
}
iOVM_StateCommitmentChain ovmStateCommitmentChain =
iOVM_StateCommitmentChain(resolve("OVM_StateCommitmentChain"));
iOVM_CanonicalTransactionChain ovmCanonicalTransactionChain =
iOVM_CanonicalTransactionChain(resolve("OVM_CanonicalTransactionChain"));
require(
ovmStateCommitmentChain.verifyStateCommitment(
_preStateRoot,
_preStateRootBatchHeader,
_preStateRootProof
),
"Invalid pre-state root inclusion proof."
);
require(
ovmCanonicalTransactionChain.verifyTransaction(
_transaction,
_txChainElement,
_transactionBatchHeader,
_transactionProof
),
"Invalid transaction inclusion proof."
);
require (
// solhint-disable-next-line max-line-length
_preStateRootBatchHeader.prevTotalElements + _preStateRootProof.index + 1 == _transactionBatchHeader.prevTotalElements + _transactionProof.index,
"Pre-state root global index must equal to the transaction root global index."
);
_deployTransitioner(_preStateRoot, _txHash, _preStateRootProof.index);
emit FraudProofInitialized(
_preStateRoot,
_preStateRootProof.index,
_txHash,
msg.sender
);
}
/**
* Finalizes the fraud verification process.
* @param _preStateRoot State root before the fraudulent transaction.
* @param _preStateRootBatchHeader Batch header for the provided pre-state root.
* @param _preStateRootProof Inclusion proof for the provided pre-state root.
* @param _txHash The transaction for the state root
* @param _postStateRoot State root after the fraudulent transaction.
* @param _postStateRootBatchHeader Batch header for the provided post-state root.
* @param _postStateRootProof Inclusion proof for the provided post-state root.
*/
function finalizeFraudVerification(
bytes32 _preStateRoot,
Lib_OVMCodec.ChainBatchHeader memory _preStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _preStateRootProof,
bytes32 _txHash,
bytes32 _postStateRoot,
Lib_OVMCodec.ChainBatchHeader memory _postStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _postStateRootProof
)
override
public
contributesToFraudProof(_preStateRoot, _txHash)
{
iOVM_StateTransitioner transitioner = getStateTransitioner(_preStateRoot, _txHash);
iOVM_StateCommitmentChain ovmStateCommitmentChain =
iOVM_StateCommitmentChain(resolve("OVM_StateCommitmentChain"));
require(
transitioner.isComplete() == true,
"State transition process must be completed prior to finalization."
);
require (
// solhint-disable-next-line max-line-length
_postStateRootBatchHeader.prevTotalElements + _postStateRootProof.index == _preStateRootBatchHeader.prevTotalElements + _preStateRootProof.index + 1,
"Post-state root global index must equal to the pre state root global index plus one."
);
require(
ovmStateCommitmentChain.verifyStateCommitment(
_preStateRoot,
_preStateRootBatchHeader,
_preStateRootProof
),
"Invalid pre-state root inclusion proof."
);
require(
ovmStateCommitmentChain.verifyStateCommitment(
_postStateRoot,
_postStateRootBatchHeader,
_postStateRootProof
),
"Invalid post-state root inclusion proof."
);
// If the post state root did not match, then there was fraud and we should delete the batch
require(
_postStateRoot != transitioner.getPostStateRoot(),
"State transition has not been proven fraudulent."
);
_cancelStateTransition(_postStateRootBatchHeader, _preStateRoot);
// TEMPORARY: Remove the transitioner; for minnet.
transitioners[keccak256(abi.encodePacked(_preStateRoot, _txHash))] =
iOVM_StateTransitioner(0x0000000000000000000000000000000000000000);
emit FraudProofFinalized(
_preStateRoot,
_preStateRootProof.index,
_txHash,
msg.sender
);
}
/************************************
* Internal Functions: Verification *
************************************/
/**
* Checks whether a transitioner already exists for a given pre-state root.
* @param _preStateRoot Pre-state root to check.
* @return _exists Whether or not we already have a transitioner for the root.
*/
function _hasStateTransitioner(
bytes32 _preStateRoot,
bytes32 _txHash
)
internal
view
returns (
bool _exists
)
{
return address(getStateTransitioner(_preStateRoot, _txHash)) != address(0);
}
/**
* Deploys a new state transitioner.
* @param _preStateRoot Pre-state root to initialize the transitioner with.
* @param _txHash Hash of the transaction this transitioner will execute.
* @param _stateTransitionIndex Index of the transaction in the chain.
*/
function _deployTransitioner(
bytes32 _preStateRoot,
bytes32 _txHash,
uint256 _stateTransitionIndex
)
internal
{
transitioners[keccak256(abi.encodePacked(_preStateRoot, _txHash))] =
iOVM_StateTransitionerFactory(
resolve("OVM_StateTransitionerFactory")
).create(
address(libAddressManager),
_stateTransitionIndex,
_preStateRoot,
_txHash
);
}
/**
* Removes a state transition from the state commitment chain.
* @param _postStateRootBatchHeader Header for the post-state root.
* @param _preStateRoot Pre-state root hash.
*/
function _cancelStateTransition(
Lib_OVMCodec.ChainBatchHeader memory _postStateRootBatchHeader,
bytes32 _preStateRoot
)
internal
{
iOVM_StateCommitmentChain ovmStateCommitmentChain =
iOVM_StateCommitmentChain(resolve("OVM_StateCommitmentChain"));
iOVM_BondManager ovmBondManager = iOVM_BondManager(resolve("OVM_BondManager"));
// Delete the state batch.
ovmStateCommitmentChain.deleteStateBatch(
_postStateRootBatchHeader
);
// Get the timestamp and publisher for that block.
(uint256 timestamp, address publisher) =
abi.decode(_postStateRootBatchHeader.extraData, (uint256, address));
// Slash the bonds at the bond manager.
ovmBondManager.finalize(
_preStateRoot,
publisher,
timestamp
);
}
}
// SPDX-License-Identifier: MIT
// @unsupported: ovm
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_EthUtils } from "../../libraries/utils/Lib_EthUtils.sol";
import { Lib_Bytes32Utils } from "../../libraries/utils/Lib_Bytes32Utils.sol";
import { Lib_BytesUtils } from "../../libraries/utils/Lib_BytesUtils.sol";
import { Lib_SecureMerkleTrie } from "../../libraries/trie/Lib_SecureMerkleTrie.sol";
import { Lib_RLPWriter } from "../../libraries/rlp/Lib_RLPWriter.sol";
import { Lib_RLPReader } from "../../libraries/rlp/Lib_RLPReader.sol";
/* Interface Imports */
import { iOVM_StateTransitioner } from "../../iOVM/verification/iOVM_StateTransitioner.sol";
import { iOVM_BondManager } from "../../iOVM/verification/iOVM_BondManager.sol";
import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol";
import { iOVM_StateManager } from "../../iOVM/execution/iOVM_StateManager.sol";
import { iOVM_StateManagerFactory } from "../../iOVM/execution/iOVM_StateManagerFactory.sol";
/* Contract Imports */
import { Abs_FraudContributor } from "./Abs_FraudContributor.sol";
/**
* @title OVM_StateTransitioner
* @dev The State Transitioner coordinates the execution of a state transition during the evaluation
* of a fraud proof. It feeds verified input to the Execution Manager's run(), and controls a
* State Manager (which is uniquely created for each fraud proof).
* Once a fraud proof has been initialized, this contract is provided with the pre-state root and
* verifies that the OVM storage slots committed to the State Mangager are contained in that state
* This contract controls the State Manager and Execution Manager, and uses them to calculate the
* post-state root by applying the transaction. The Fraud Verifier can then check for fraud by
* comparing the calculated post-state root with the proposed post-state root.
*
* Compiler used: solc
* Runtime target: EVM
*/
contract OVM_StateTransitioner is Lib_AddressResolver, Abs_FraudContributor, iOVM_StateTransitioner
{
/*******************
* Data Structures *
*******************/
enum TransitionPhase {
PRE_EXECUTION,
POST_EXECUTION,
COMPLETE
}
/*******************************************
* Contract Variables: Contract References *
*******************************************/
iOVM_StateManager public ovmStateManager;
/*******************************************
* Contract Variables: Internal Accounting *
*******************************************/
bytes32 internal preStateRoot;
bytes32 internal postStateRoot;
TransitionPhase public phase;
uint256 internal stateTransitionIndex;
bytes32 internal transactionHash;
/*************
* Constants *
*************/
// solhint-disable-next-line max-line-length
bytes32 internal constant EMPTY_ACCOUNT_CODE_HASH = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line max-line-length
bytes32 internal constant EMPTY_ACCOUNT_STORAGE_ROOT = 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421;
/***************
* Constructor *
***************/
/**
* @param _libAddressManager Address of the Address Manager.
* @param _stateTransitionIndex Index of the state transition being verified.
* @param _preStateRoot State root before the transition was executed.
* @param _transactionHash Hash of the executed transaction.
*/
constructor(
address _libAddressManager,
uint256 _stateTransitionIndex,
bytes32 _preStateRoot,
bytes32 _transactionHash
)
Lib_AddressResolver(_libAddressManager)
{
stateTransitionIndex = _stateTransitionIndex;
preStateRoot = _preStateRoot;
postStateRoot = _preStateRoot;
transactionHash = _transactionHash;
ovmStateManager = iOVM_StateManagerFactory(resolve("OVM_StateManagerFactory"))
.create(address(this));
}
/**********************
* Function Modifiers *
**********************/
/**
* Checks that a function is only run during a specific phase.
* @param _phase Phase the function must run within.
*/
modifier onlyDuringPhase(
TransitionPhase _phase
) {
require(
phase == _phase,
"Function must be called during the correct phase."
);
_;
}
/**********************************
* Public Functions: State Access *
**********************************/
/**
* Retrieves the state root before execution.
* @return _preStateRoot State root before execution.
*/
function getPreStateRoot()
override
external
view
returns (
bytes32 _preStateRoot
)
{
return preStateRoot;
}
/**
* Retrieves the state root after execution.
* @return _postStateRoot State root after execution.
*/
function getPostStateRoot()
override
external
view
returns (
bytes32 _postStateRoot
)
{
return postStateRoot;
}
/**
* Checks whether the transitioner is complete.
* @return _complete Whether or not the transition process is finished.
*/
function isComplete()
override
external
view
returns (
bool _complete
)
{
return phase == TransitionPhase.COMPLETE;
}
/***********************************
* Public Functions: Pre-Execution *
***********************************/
/**
* Allows a user to prove the initial state of a contract.
* @param _ovmContractAddress Address of the contract on the OVM.
* @param _ethContractAddress Address of the corresponding contract on L1.
* @param _stateTrieWitness Proof of the account state.
*/
function proveContractState(
address _ovmContractAddress,
address _ethContractAddress,
bytes memory _stateTrieWitness
)
override
external
onlyDuringPhase(TransitionPhase.PRE_EXECUTION)
contributesToFraudProof(preStateRoot, transactionHash)
{
// Exit quickly to avoid unnecessary work.
require(
(
ovmStateManager.hasAccount(_ovmContractAddress) == false
&& ovmStateManager.hasEmptyAccount(_ovmContractAddress) == false
),
"Account state has already been proven."
);
// Function will fail if the proof is not a valid inclusion or exclusion proof.
(
bool exists,
bytes memory encodedAccount
) = Lib_SecureMerkleTrie.get(
abi.encodePacked(_ovmContractAddress),
_stateTrieWitness,
preStateRoot
);
if (exists == true) {
// Account exists, this was an inclusion proof.
Lib_OVMCodec.EVMAccount memory account = Lib_OVMCodec.decodeEVMAccount(
encodedAccount
);
address ethContractAddress = _ethContractAddress;
if (account.codeHash == EMPTY_ACCOUNT_CODE_HASH) {
// Use a known empty contract to prevent an attack in which a user provides a
// contract address here and then later deploys code to it.
ethContractAddress = 0x0000000000000000000000000000000000000000;
} else {
// Otherwise, make sure that the code at the provided eth address matches the hash
// of the code stored on L2.
require(
Lib_EthUtils.getCodeHash(ethContractAddress) == account.codeHash,
// solhint-disable-next-line max-line-length
"OVM_StateTransitioner: Provided L1 contract code hash does not match L2 contract code hash."
);
}
ovmStateManager.putAccount(
_ovmContractAddress,
Lib_OVMCodec.Account({
nonce: account.nonce,
balance: account.balance,
storageRoot: account.storageRoot,
codeHash: account.codeHash,
ethAddress: ethContractAddress,
isFresh: false
})
);
} else {
// Account does not exist, this was an exclusion proof.
ovmStateManager.putEmptyAccount(_ovmContractAddress);
}
}
/**
* Allows a user to prove the initial state of a contract storage slot.
* @param _ovmContractAddress Address of the contract on the OVM.
* @param _key Claimed account slot key.
* @param _storageTrieWitness Proof of the storage slot.
*/
function proveStorageSlot(
address _ovmContractAddress,
bytes32 _key,
bytes memory _storageTrieWitness
)
override
external
onlyDuringPhase(TransitionPhase.PRE_EXECUTION)
contributesToFraudProof(preStateRoot, transactionHash)
{
// Exit quickly to avoid unnecessary work.
require(
ovmStateManager.hasContractStorage(_ovmContractAddress, _key) == false,
"Storage slot has already been proven."
);
require(
ovmStateManager.hasAccount(_ovmContractAddress) == true,
"Contract must be verified before proving a storage slot."
);
bytes32 storageRoot = ovmStateManager.getAccountStorageRoot(_ovmContractAddress);
bytes32 value;
if (storageRoot == EMPTY_ACCOUNT_STORAGE_ROOT) {
// Storage trie was empty, so the user is always allowed to insert zero-byte values.
value = bytes32(0);
} else {
// Function will fail if the proof is not a valid inclusion or exclusion proof.
(
bool exists,
bytes memory encodedValue
) = Lib_SecureMerkleTrie.get(
abi.encodePacked(_key),
_storageTrieWitness,
storageRoot
);
if (exists == true) {
// Inclusion proof.
// Stored values are RLP encoded, with leading zeros removed.
value = Lib_BytesUtils.toBytes32PadLeft(
Lib_RLPReader.readBytes(encodedValue)
);
} else {
// Exclusion proof, can only be zero bytes.
value = bytes32(0);
}
}
ovmStateManager.putContractStorage(
_ovmContractAddress,
_key,
value
);
}
/*******************************
* Public Functions: Execution *
*******************************/
/**
* Executes the state transition.
* @param _transaction OVM transaction to execute.
*/
function applyTransaction(
Lib_OVMCodec.Transaction memory _transaction
)
override
external
onlyDuringPhase(TransitionPhase.PRE_EXECUTION)
contributesToFraudProof(preStateRoot, transactionHash)
{
require(
Lib_OVMCodec.hashTransaction(_transaction) == transactionHash,
"Invalid transaction provided."
);
// We require gas to complete the logic here in run() before/after execution,
// But must ensure the full _tx.gasLimit can be given to the ovmCALL (determinism)
// This includes 1/64 of the gas getting lost because of EIP-150 (lost twice--first
// going into EM, then going into the code contract).
require(
// 1032/1000 = 1.032 = (64/63)^2 rounded up
gasleft() >= 100000 + _transaction.gasLimit * 1032 / 1000,
"Not enough gas to execute transaction deterministically."
);
iOVM_ExecutionManager ovmExecutionManager =
iOVM_ExecutionManager(resolve("OVM_ExecutionManager"));
// We call `setExecutionManager` right before `run` (and not earlier) just in case the
// OVM_ExecutionManager address was updated between the time when this contract was created
// and when `applyTransaction` was called.
ovmStateManager.setExecutionManager(address(ovmExecutionManager));
// `run` always succeeds *unless* the user hasn't provided enough gas to `applyTransaction`
// or an INVALID_STATE_ACCESS flag was triggered. Either way, we won't get beyond this line
// if that's the case.
ovmExecutionManager.run(_transaction, address(ovmStateManager));
// Prevent the Execution Manager from calling this SM again.
ovmStateManager.setExecutionManager(address(0));
phase = TransitionPhase.POST_EXECUTION;
}
/************************************
* Public Functions: Post-Execution *
************************************/
/**
* Allows a user to commit the final state of a contract.
* @param _ovmContractAddress Address of the contract on the OVM.
* @param _stateTrieWitness Proof of the account state.
*/
function commitContractState(
address _ovmContractAddress,
bytes memory _stateTrieWitness
)
override
external
onlyDuringPhase(TransitionPhase.POST_EXECUTION)
contributesToFraudProof(preStateRoot, transactionHash)
{
require(
ovmStateManager.getTotalUncommittedContractStorage() == 0,
"All storage must be committed before committing account states."
);
require (
ovmStateManager.commitAccount(_ovmContractAddress) == true,
"Account state wasn't changed or has already been committed."
);
Lib_OVMCodec.Account memory account = ovmStateManager.getAccount(_ovmContractAddress);
postStateRoot = Lib_SecureMerkleTrie.update(
abi.encodePacked(_ovmContractAddress),
Lib_OVMCodec.encodeEVMAccount(
Lib_OVMCodec.toEVMAccount(account)
),
_stateTrieWitness,
postStateRoot
);
// Emit an event to help clients figure out the proof ordering.
emit AccountCommitted(
_ovmContractAddress
);
}
/**
* Allows a user to commit the final state of a contract storage slot.
* @param _ovmContractAddress Address of the contract on the OVM.
* @param _key Claimed account slot key.
* @param _storageTrieWitness Proof of the storage slot.
*/
function commitStorageSlot(
address _ovmContractAddress,
bytes32 _key,
bytes memory _storageTrieWitness
)
override
external
onlyDuringPhase(TransitionPhase.POST_EXECUTION)
contributesToFraudProof(preStateRoot, transactionHash)
{
require(
ovmStateManager.commitContractStorage(_ovmContractAddress, _key) == true,
"Storage slot value wasn't changed or has already been committed."
);
Lib_OVMCodec.Account memory account = ovmStateManager.getAccount(_ovmContractAddress);
bytes32 value = ovmStateManager.getContractStorage(_ovmContractAddress, _key);
account.storageRoot = Lib_SecureMerkleTrie.update(
abi.encodePacked(_key),
Lib_RLPWriter.writeBytes(
Lib_Bytes32Utils.removeLeadingZeros(value)
),
_storageTrieWitness,
account.storageRoot
);
ovmStateManager.putAccount(_ovmContractAddress, account);
// Emit an event to help clients figure out the proof ordering.
emit ContractStorageCommitted(
_ovmContractAddress,
_key
);
}
/**********************************
* Public Functions: Finalization *
**********************************/
/**
* Finalizes the transition process.
*/
function completeTransition()
override
external
onlyDuringPhase(TransitionPhase.POST_EXECUTION)
{
require(
ovmStateManager.getTotalUncommittedAccounts() == 0,
"All accounts must be committed before completing a transition."
);
require(
ovmStateManager.getTotalUncommittedContractStorage() == 0,
"All storage must be committed before completing a transition."
);
phase = TransitionPhase.COMPLETE;
}
}
// SPDX-License-Identifier: MIT
// @unsupported: ovm
pragma solidity >0.5.0 <0.8.0;
/* Library Imports */
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */
import { iOVM_StateTransitioner } from "../../iOVM/verification/iOVM_StateTransitioner.sol";
import { iOVM_StateTransitionerFactory } from
"../../iOVM/verification/iOVM_StateTransitionerFactory.sol";
import { iOVM_FraudVerifier } from "../../iOVM/verification/iOVM_FraudVerifier.sol";
/* Contract Imports */
import { OVM_StateTransitioner } from "./OVM_StateTransitioner.sol";
/**
* @title OVM_StateTransitionerFactory
* @dev The State Transitioner Factory is used by the Fraud Verifier to create a new State
* Transitioner during the initialization of a fraud proof.
*
* Compiler used: solc
* Runtime target: EVM
*/
contract OVM_StateTransitionerFactory is iOVM_StateTransitionerFactory, Lib_AddressResolver {
/***************
* Constructor *
***************/
constructor(
address _libAddressManager
)
Lib_AddressResolver(_libAddressManager)
{}
/********************
* Public Functions *
********************/
/**
* Creates a new OVM_StateTransitioner
* @param _libAddressManager Address of the Address Manager.
* @param _stateTransitionIndex Index of the state transition being verified.
* @param _preStateRoot State root before the transition was executed.
* @param _transactionHash Hash of the executed transaction.
* @return New OVM_StateTransitioner instance.
*/
function create(
address _libAddressManager,
uint256 _stateTransitionIndex,
bytes32 _preStateRoot,
bytes32 _transactionHash
)
override
public
returns (
iOVM_StateTransitioner
)
{
require(
msg.sender == resolve("OVM_FraudVerifier"),
"Create can only be done by the OVM_FraudVerifier."
);
return new OVM_StateTransitioner(
_libAddressManager,
_stateTransitionIndex,
_preStateRoot,
_transactionHash
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
interface iOVM_ExecutionManager {
/**********
* Enums *
*********/
enum RevertFlag {
OUT_OF_GAS,
INTENTIONAL_REVERT,
EXCEEDS_NUISANCE_GAS,
INVALID_STATE_ACCESS,
UNSAFE_BYTECODE,
CREATE_COLLISION,
STATIC_VIOLATION,
CREATOR_NOT_ALLOWED
}
enum GasMetadataKey {
CURRENT_EPOCH_START_TIMESTAMP,
CUMULATIVE_SEQUENCER_QUEUE_GAS,
CUMULATIVE_L1TOL2_QUEUE_GAS,
PREV_EPOCH_SEQUENCER_QUEUE_GAS,
PREV_EPOCH_L1TOL2_QUEUE_GAS
}
enum MessageType {
ovmCALL,
ovmSTATICCALL,
ovmDELEGATECALL,
ovmCREATE,
ovmCREATE2
}
/***********
* Structs *
***********/
struct GasMeterConfig {
uint256 minTransactionGasLimit;
uint256 maxTransactionGasLimit;
uint256 maxGasPerQueuePerEpoch;
uint256 secondsPerEpoch;
}
struct GlobalContext {
uint256 ovmCHAINID;
}
struct TransactionContext {
Lib_OVMCodec.QueueOrigin ovmL1QUEUEORIGIN;
uint256 ovmTIMESTAMP;
uint256 ovmNUMBER;
uint256 ovmGASLIMIT;
uint256 ovmTXGASLIMIT;
address ovmL1TXORIGIN;
}
struct TransactionRecord {
uint256 ovmGasRefund;
}
struct MessageContext {
address ovmCALLER;
address ovmADDRESS;
uint256 ovmCALLVALUE;
bool isStatic;
}
struct MessageRecord {
uint256 nuisanceGasLeft;
}
/************************************
* Transaction Execution Entrypoint *
************************************/
function run(
Lib_OVMCodec.Transaction calldata _transaction,
address _txStateManager
) external returns (bytes memory);
/*******************
* Context Opcodes *
*******************/
function ovmCALLER() external view returns (address _caller);
function ovmADDRESS() external view returns (address _address);
function ovmCALLVALUE() external view returns (uint _callValue);
function ovmTIMESTAMP() external view returns (uint256 _timestamp);
function ovmNUMBER() external view returns (uint256 _number);
function ovmGASLIMIT() external view returns (uint256 _gasLimit);
function ovmCHAINID() external view returns (uint256 _chainId);
/**********************
* L2 Context Opcodes *
**********************/
function ovmL1QUEUEORIGIN() external view returns (Lib_OVMCodec.QueueOrigin _queueOrigin);
function ovmL1TXORIGIN() external view returns (address _l1TxOrigin);
/*******************
* Halting Opcodes *
*******************/
function ovmREVERT(bytes memory _data) external;
/*****************************
* Contract Creation Opcodes *
*****************************/
function ovmCREATE(bytes memory _bytecode) external
returns (address _contract, bytes memory _revertdata);
function ovmCREATE2(bytes memory _bytecode, bytes32 _salt) external
returns (address _contract, bytes memory _revertdata);
/*******************************
* Account Abstraction Opcodes *
******************************/
function ovmGETNONCE() external returns (uint256 _nonce);
function ovmINCREMENTNONCE() external;
function ovmCREATEEOA(bytes32 _messageHash, uint8 _v, bytes32 _r, bytes32 _s) external;
/****************************
* Contract Calling Opcodes *
****************************/
// Valueless ovmCALL for maintaining backwards compatibility with legacy OVM bytecode.
function ovmCALL(uint256 _gasLimit, address _address, bytes memory _calldata) external
returns (bool _success, bytes memory _returndata);
function ovmCALL(uint256 _gasLimit, address _address, uint256 _value, bytes memory _calldata)
external returns (bool _success, bytes memory _returndata);
function ovmSTATICCALL(uint256 _gasLimit, address _address, bytes memory _calldata) external
returns (bool _success, bytes memory _returndata);
function ovmDELEGATECALL(uint256 _gasLimit, address _address, bytes memory _calldata) external
returns (bool _success, bytes memory _returndata);
/****************************
* Contract Storage Opcodes *
****************************/
function ovmSLOAD(bytes32 _key) external returns (bytes32 _value);
function ovmSSTORE(bytes32 _key, bytes32 _value) external;
/*************************
* Contract Code Opcodes *
*************************/
function ovmEXTCODECOPY(address _contract, uint256 _offset, uint256 _length) external
returns (bytes memory _code);
function ovmEXTCODESIZE(address _contract) external returns (uint256 _size);
function ovmEXTCODEHASH(address _contract) external returns (bytes32 _hash);
/*********************
* ETH Value Opcodes *
*********************/
function ovmBALANCE(address _contract) external returns (uint256 _balance);
function ovmSELFBALANCE() external returns (uint256 _balance);
/***************************************
* Public Functions: Execution Context *
***************************************/
function getMaxTransactionGasLimit() external view returns (uint _maxTransactionGasLimit);
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
/**
* @title iOVM_SafetyChecker
*/
interface iOVM_SafetyChecker {
/********************
* Public Functions *
********************/
function isBytecodeSafe(bytes calldata _bytecode) external pure returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/**
* @title iOVM_StateManager
*/
interface iOVM_StateManager {
/*******************
* Data Structures *
*******************/
enum ItemState {
ITEM_UNTOUCHED,
ITEM_LOADED,
ITEM_CHANGED,
ITEM_COMMITTED
}
/***************************
* Public Functions: Misc *
***************************/
function isAuthenticated(address _address) external view returns (bool);
/***************************
* Public Functions: Setup *
***************************/
function owner() external view returns (address _owner);
function ovmExecutionManager() external view returns (address _ovmExecutionManager);
function setExecutionManager(address _ovmExecutionManager) external;
/************************************
* Public Functions: Account Access *
************************************/
function putAccount(address _address, Lib_OVMCodec.Account memory _account) external;
function putEmptyAccount(address _address) external;
function getAccount(address _address) external view
returns (Lib_OVMCodec.Account memory _account);
function hasAccount(address _address) external view returns (bool _exists);
function hasEmptyAccount(address _address) external view returns (bool _exists);
function setAccountNonce(address _address, uint256 _nonce) external;
function getAccountNonce(address _address) external view returns (uint256 _nonce);
function getAccountEthAddress(address _address) external view returns (address _ethAddress);
function getAccountStorageRoot(address _address) external view returns (bytes32 _storageRoot);
function initPendingAccount(address _address) external;
function commitPendingAccount(address _address, address _ethAddress, bytes32 _codeHash)
external;
function testAndSetAccountLoaded(address _address) external
returns (bool _wasAccountAlreadyLoaded);
function testAndSetAccountChanged(address _address) external
returns (bool _wasAccountAlreadyChanged);
function commitAccount(address _address) external returns (bool _wasAccountCommitted);
function incrementTotalUncommittedAccounts() external;
function getTotalUncommittedAccounts() external view returns (uint256 _total);
function wasAccountChanged(address _address) external view returns (bool);
function wasAccountCommitted(address _address) external view returns (bool);
/************************************
* Public Functions: Storage Access *
************************************/
function putContractStorage(address _contract, bytes32 _key, bytes32 _value) external;
function getContractStorage(address _contract, bytes32 _key) external view
returns (bytes32 _value);
function hasContractStorage(address _contract, bytes32 _key) external view
returns (bool _exists);
function testAndSetContractStorageLoaded(address _contract, bytes32 _key) external
returns (bool _wasContractStorageAlreadyLoaded);
function testAndSetContractStorageChanged(address _contract, bytes32 _key) external
returns (bool _wasContractStorageAlreadyChanged);
function commitContractStorage(address _contract, bytes32 _key) external
returns (bool _wasContractStorageCommitted);
function incrementTotalUncommittedContractStorage() external;
function getTotalUncommittedContractStorage() external view returns (uint256 _total);
function wasContractStorageChanged(address _contract, bytes32 _key) external view
returns (bool);
function wasContractStorageCommitted(address _contract, bytes32 _key) external view
returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
/* Contract Imports */
import { iOVM_StateManager } from "./iOVM_StateManager.sol";
/**
* @title iOVM_StateManagerFactory
*/
interface iOVM_StateManagerFactory {
/***************************************
* Public Functions: Contract Creation *
***************************************/
function create(
address _owner
)
external
returns (
iOVM_StateManager _ovmStateManager
);
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_EIP155Tx } from "../../libraries/codec/Lib_EIP155Tx.sol";
/**
* @title iOVM_ECDSAContractAccount
*/
interface iOVM_ECDSAContractAccount {
/********************
* Public Functions *
********************/
function execute(
Lib_EIP155Tx.EIP155Tx memory _transaction
)
external
returns (
bool,
bytes memory
);
}
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0; pragma solidity >0.5.0 <0.8.0;
interface ERC20 {
function transfer(address, uint256) external returns (bool);
function transferFrom(address, address, uint256) external returns (bool);
}
/// All the errors which may be encountered on the bond manager
library Errors {
string constant ERC20_ERR = "BondManager: Could not post bond";
// solhint-disable-next-line max-line-length
string constant ALREADY_FINALIZED = "BondManager: Fraud proof for this pre-state root has already been finalized";
string constant SLASHED = "BondManager: Cannot finalize withdrawal, you probably got slashed";
string constant WRONG_STATE = "BondManager: Wrong bond state for proposer";
string constant CANNOT_CLAIM = "BondManager: Cannot claim yet. Dispute must be finalized first";
string constant WITHDRAWAL_PENDING = "BondManager: Withdrawal already pending";
string constant TOO_EARLY = "BondManager: Too early to finalize your withdrawal";
// solhint-disable-next-line max-line-length
string constant ONLY_TRANSITIONER = "BondManager: Only the transitioner for this pre-state root may call this function";
// solhint-disable-next-line max-line-length
string constant ONLY_FRAUD_VERIFIER = "BondManager: Only the fraud verifier may call this function";
// solhint-disable-next-line max-line-length
string constant ONLY_STATE_COMMITMENT_CHAIN = "BondManager: Only the state commitment chain may call this function";
string constant WAIT_FOR_DISPUTES = "BondManager: Wait for other potential disputes";
}
/** /**
* @title iOVM_BondManager * @title iOVM_BondManager
*/ */
interface iOVM_BondManager { interface iOVM_BondManager {
/*******************
* Data Structures *
*******************/
/// The lifecycle of a proposer's bond
enum State {
// Before depositing or after getting slashed, a user is uncollateralized
NOT_COLLATERALIZED,
// After depositing, a user is collateralized
COLLATERALIZED,
// After a user has initiated a withdrawal
WITHDRAWING
}
/// A bond posted by a proposer
struct Bond {
// The user's state
State state;
// The timestamp at which a proposer issued their withdrawal request
uint32 withdrawalTimestamp;
// The time when the first disputed was initiated for this bond
uint256 firstDisputeAt;
// The earliest observed state root for this bond which has had fraud
bytes32 earliestDisputedStateRoot;
// The state root's timestamp
uint256 earliestTimestamp;
}
// Per pre-state root, store the number of state provisions that were made
// and how many of these calls were made by each user. Payouts will then be
// claimed by users proportionally for that dispute.
struct Rewards {
// Flag to check if rewards for a fraud proof are claimable
bool canClaim;
// Total number of `recordGasSpent` calls made
uint256 total;
// The gas spent by each user to provide witness data. The sum of all
// values inside this map MUST be equal to the value of `total`
mapping(address => uint256) gasSpent;
}
/******************** /********************
* Public Functions * * Public Functions *
********************/ ********************/
......
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/* Interface Imports */
import { iOVM_StateTransitioner } from "./iOVM_StateTransitioner.sol";
/**
* @title iOVM_FraudVerifier
*/
interface iOVM_FraudVerifier {
/**********
* Events *
**********/
event FraudProofInitialized(
bytes32 _preStateRoot,
uint256 _preStateRootIndex,
bytes32 _transactionHash,
address _who
);
event FraudProofFinalized(
bytes32 _preStateRoot,
uint256 _preStateRootIndex,
bytes32 _transactionHash,
address _who
);
/***************************************
* Public Functions: Transition Status *
***************************************/
function getStateTransitioner(bytes32 _preStateRoot, bytes32 _txHash) external view
returns (iOVM_StateTransitioner _transitioner);
/****************************************
* Public Functions: Fraud Verification *
****************************************/
function initializeFraudVerification(
bytes32 _preStateRoot,
Lib_OVMCodec.ChainBatchHeader calldata _preStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof calldata _preStateRootProof,
Lib_OVMCodec.Transaction calldata _transaction,
Lib_OVMCodec.TransactionChainElement calldata _txChainElement,
Lib_OVMCodec.ChainBatchHeader calldata _transactionBatchHeader,
Lib_OVMCodec.ChainInclusionProof calldata _transactionProof
) external;
function finalizeFraudVerification(
bytes32 _preStateRoot,
Lib_OVMCodec.ChainBatchHeader calldata _preStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof calldata _preStateRootProof,
bytes32 _txHash,
bytes32 _postStateRoot,
Lib_OVMCodec.ChainBatchHeader calldata _postStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof calldata _postStateRootProof
) external;
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/**
* @title iOVM_StateTransitioner
*/
interface iOVM_StateTransitioner {
/**********
* Events *
**********/
event AccountCommitted(
address _address
);
event ContractStorageCommitted(
address _address,
bytes32 _key
);
/**********************************
* Public Functions: State Access *
**********************************/
function getPreStateRoot() external view returns (bytes32 _preStateRoot);
function getPostStateRoot() external view returns (bytes32 _postStateRoot);
function isComplete() external view returns (bool _complete);
/***********************************
* Public Functions: Pre-Execution *
***********************************/
function proveContractState(
address _ovmContractAddress,
address _ethContractAddress,
bytes calldata _stateTrieWitness
) external;
function proveStorageSlot(
address _ovmContractAddress,
bytes32 _key,
bytes calldata _storageTrieWitness
) external;
/*******************************
* Public Functions: Execution *
*******************************/
function applyTransaction(
Lib_OVMCodec.Transaction calldata _transaction
) external;
/************************************
* Public Functions: Post-Execution *
************************************/
function commitContractState(
address _ovmContractAddress,
bytes calldata _stateTrieWitness
) external;
function commitStorageSlot(
address _ovmContractAddress,
bytes32 _key,
bytes calldata _storageTrieWitness
) external;
/**********************************
* Public Functions: Finalization *
**********************************/
function completeTransition() external;
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
/* Contract Imports */
import { iOVM_StateTransitioner } from "./iOVM_StateTransitioner.sol";
/**
* @title iOVM_StateTransitionerFactory
*/
interface iOVM_StateTransitionerFactory {
/***************************************
* Public Functions: Contract Creation *
***************************************/
function create(
address _proxyManager,
uint256 _stateTransitionIndex,
bytes32 _preStateRoot,
bytes32 _transactionHash
)
external
returns (
iOVM_StateTransitioner _ovmStateTransitioner
);
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_RLPReader } from "../rlp/Lib_RLPReader.sol";
import { Lib_RLPWriter } from "../rlp/Lib_RLPWriter.sol";
/**
* @title Lib_EIP155Tx
* @dev A simple library for dealing with the transaction type defined by EIP155:
* https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md
*/
library Lib_EIP155Tx {
/***********
* Structs *
***********/
// Struct representing an EIP155 transaction. See EIP link above for more information.
struct EIP155Tx {
// These fields correspond to the actual RLP-encoded fields specified by EIP155.
uint256 nonce;
uint256 gasPrice;
uint256 gasLimit;
address to;
uint256 value;
bytes data;
uint8 v;
bytes32 r;
bytes32 s;
// Chain ID to associate this transaction with. Used all over the place, seemed easier to
// set this once when we create the transaction rather than providing it as an input to
// each function. I don't see a strong need to have a transaction with a mutable chain ID.
uint256 chainId;
// The ECDSA "recovery parameter," should always be 0 or 1. EIP155 specifies that:
// `v = {0,1} + CHAIN_ID * 2 + 35`
// Where `{0,1}` is a stand in for our `recovery_parameter`. Now computing our formula for
// the recovery parameter:
// 1. `v = {0,1} + CHAIN_ID * 2 + 35`
// 2. `v = recovery_parameter + CHAIN_ID * 2 + 35`
// 3. `v - CHAIN_ID * 2 - 35 = recovery_parameter`
// So we're left with the final formula:
// `recovery_parameter = v - CHAIN_ID * 2 - 35`
// NOTE: This variable is a uint8 because `v` is inherently limited to a uint8. If we
// didn't use a uint8, then recovery_parameter would always be a negative number for chain
// IDs greater than 110 (`255 - 110 * 2 - 35 = 0`). So we need to wrap around to support
// anything larger.
uint8 recoveryParam;
// Whether or not the transaction is a creation. Necessary because we can't make an address
// "nil". Using the zero address creates a potential conflict if the user did actually
// intend to send a transaction to the zero address.
bool isCreate;
}
// Lets us use nicer syntax.
using Lib_EIP155Tx for EIP155Tx;
/**********************
* Internal Functions *
**********************/
/**
* Decodes an EIP155 transaction and attaches a given Chain ID.
* Transaction *must* be RLP-encoded.
* @param _encoded RLP-encoded EIP155 transaction.
* @param _chainId Chain ID to assocaite with this transaction.
* @return Parsed transaction.
*/
function decode(
bytes memory _encoded,
uint256 _chainId
)
internal
pure
returns (
EIP155Tx memory
)
{
Lib_RLPReader.RLPItem[] memory decoded = Lib_RLPReader.readList(_encoded);
// Note formula above about how recoveryParam is computed.
uint8 v = uint8(Lib_RLPReader.readUint256(decoded[6]));
uint8 recoveryParam = uint8(v - 2 * _chainId - 35);
// Recovery param being anything other than 0 or 1 indicates that we have the wrong chain
// ID.
require(
recoveryParam < 2,
"Lib_EIP155Tx: Transaction signed with wrong chain ID"
);
// Creations can be detected by looking at the byte length here.
bool isCreate = Lib_RLPReader.readBytes(decoded[3]).length == 0;
return EIP155Tx({
nonce: Lib_RLPReader.readUint256(decoded[0]),
gasPrice: Lib_RLPReader.readUint256(decoded[1]),
gasLimit: Lib_RLPReader.readUint256(decoded[2]),
to: Lib_RLPReader.readAddress(decoded[3]),
value: Lib_RLPReader.readUint256(decoded[4]),
data: Lib_RLPReader.readBytes(decoded[5]),
v: v,
r: Lib_RLPReader.readBytes32(decoded[7]),
s: Lib_RLPReader.readBytes32(decoded[8]),
chainId: _chainId,
recoveryParam: recoveryParam,
isCreate: isCreate
});
}
/**
* Encodes an EIP155 transaction into RLP.
* @param _transaction EIP155 transaction to encode.
* @param _includeSignature Whether or not to encode the signature.
* @return RLP-encoded transaction.
*/
function encode(
EIP155Tx memory _transaction,
bool _includeSignature
)
internal
pure
returns (
bytes memory
)
{
bytes[] memory raw = new bytes[](9);
raw[0] = Lib_RLPWriter.writeUint(_transaction.nonce);
raw[1] = Lib_RLPWriter.writeUint(_transaction.gasPrice);
raw[2] = Lib_RLPWriter.writeUint(_transaction.gasLimit);
// We write the encoding of empty bytes when the transaction is a creation, *not* the zero
// address as one might assume.
if (_transaction.isCreate) {
raw[3] = Lib_RLPWriter.writeBytes("");
} else {
raw[3] = Lib_RLPWriter.writeAddress(_transaction.to);
}
raw[4] = Lib_RLPWriter.writeUint(_transaction.value);
raw[5] = Lib_RLPWriter.writeBytes(_transaction.data);
if (_includeSignature) {
raw[6] = Lib_RLPWriter.writeUint(_transaction.v);
raw[7] = Lib_RLPWriter.writeBytes32(_transaction.r);
raw[8] = Lib_RLPWriter.writeBytes32(_transaction.s);
} else {
// Chain ID *is* included in the unsigned transaction.
raw[6] = Lib_RLPWriter.writeUint(_transaction.chainId);
raw[7] = Lib_RLPWriter.writeBytes("");
raw[8] = Lib_RLPWriter.writeBytes("");
}
return Lib_RLPWriter.writeList(raw);
}
/**
* Computes the hash of an EIP155 transaction. Assumes that you don't want to include the
* signature in this hash because that's a very uncommon usecase. If you really want to include
* the signature, just encode with the signature and take the hash yourself.
*/
function hash(
EIP155Tx memory _transaction
)
internal
pure
returns (
bytes32
)
{
return keccak256(
_transaction.encode(false)
);
}
/**
* Computes the sender of an EIP155 transaction.
* @param _transaction EIP155 transaction to get a sender for.
* @return Address corresponding to the private key that signed this transaction.
*/
function sender(
EIP155Tx memory _transaction
)
internal
pure
returns (
address
)
{
return ecrecover(
_transaction.hash(),
_transaction.recoveryParam + 27,
_transaction.r,
_transaction.s
);
}
}
...@@ -8,16 +8,11 @@ library Lib_PredeployAddresses { ...@@ -8,16 +8,11 @@ library Lib_PredeployAddresses {
address internal constant L2_TO_L1_MESSAGE_PASSER = 0x4200000000000000000000000000000000000000; address internal constant L2_TO_L1_MESSAGE_PASSER = 0x4200000000000000000000000000000000000000;
address internal constant L1_MESSAGE_SENDER = 0x4200000000000000000000000000000000000001; address internal constant L1_MESSAGE_SENDER = 0x4200000000000000000000000000000000000001;
address internal constant DEPLOYER_WHITELIST = 0x4200000000000000000000000000000000000002; address internal constant DEPLOYER_WHITELIST = 0x4200000000000000000000000000000000000002;
address internal constant ECDSA_CONTRACT_ACCOUNT = 0x4200000000000000000000000000000000000003;
address internal constant SEQUENCER_ENTRYPOINT = 0x4200000000000000000000000000000000000005;
address payable internal constant OVM_ETH = 0x4200000000000000000000000000000000000006; address payable internal constant OVM_ETH = 0x4200000000000000000000000000000000000006;
// solhint-disable-next-line max-line-length // solhint-disable-next-line max-line-length
address internal constant L2_CROSS_DOMAIN_MESSENGER = 0x4200000000000000000000000000000000000007; address internal constant L2_CROSS_DOMAIN_MESSENGER = 0x4200000000000000000000000000000000000007;
address internal constant LIB_ADDRESS_MANAGER = 0x4200000000000000000000000000000000000008; address internal constant LIB_ADDRESS_MANAGER = 0x4200000000000000000000000000000000000008;
address internal constant PROXY_EOA = 0x4200000000000000000000000000000000000009; address internal constant PROXY_EOA = 0x4200000000000000000000000000000000000009;
// solhint-disable-next-line max-line-length
address internal constant EXECUTION_MANAGER_WRAPPER = 0x420000000000000000000000000000000000000B;
address internal constant SEQUENCER_FEE_WALLET = 0x4200000000000000000000000000000000000011;
address internal constant ERC1820_REGISTRY = 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24;
address internal constant L2_STANDARD_BRIDGE = 0x4200000000000000000000000000000000000010; address internal constant L2_STANDARD_BRIDGE = 0x4200000000000000000000000000000000000010;
address internal constant SEQUENCER_FEE_WALLET = 0x4200000000000000000000000000000000000011;
} }
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/**
* @title Lib_ErrorUtils
*/
library Lib_ErrorUtils {
/**********************
* Internal Functions *
**********************/
/**
* Encodes an error string into raw solidity-style revert data.
* (i.e. ascii bytes, prefixed with bytes4(keccak("Error(string))"))
* Ref: https://docs.soliditylang.org/en/v0.8.2/control-structures.html?highlight=Error(string)
* #panic-via-assert-and-error-via-require
* @param _reason Reason for the reversion.
* @return Standard solidity revert data for the given reason.
*/
function encodeRevertString(
string memory _reason
)
internal
pure
returns (
bytes memory
)
{
return abi.encodeWithSignature(
"Error(string)",
_reason
);
}
}
// SPDX-License-Identifier: MIT
// @unsupported: ovm
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_RLPWriter } from "../rlp/Lib_RLPWriter.sol";
import { Lib_Bytes32Utils } from "./Lib_Bytes32Utils.sol";
/**
* @title Lib_EthUtils
*/
library Lib_EthUtils {
/**********************
* Internal Functions *
**********************/
/**
* Gets the code for a given address.
* @param _address Address to get code for.
* @param _offset Offset to start reading from.
* @param _length Number of bytes to read.
* @return Code read from the contract.
*/
function getCode(
address _address,
uint256 _offset,
uint256 _length
)
internal
view
returns (
bytes memory
)
{
bytes memory code;
assembly {
code := mload(0x40)
mstore(0x40, add(code, add(_length, 0x20)))
mstore(code, _length)
extcodecopy(_address, add(code, 0x20), _offset, _length)
}
return code;
}
/**
* Gets the full code for a given address.
* @param _address Address to get code for.
* @return Full code of the contract.
*/
function getCode(
address _address
)
internal
view
returns (
bytes memory
)
{
return getCode(
_address,
0,
getCodeSize(_address)
);
}
/**
* Gets the size of a contract's code in bytes.
* @param _address Address to get code size for.
* @return Size of the contract's code in bytes.
*/
function getCodeSize(
address _address
)
internal
view
returns (
uint256
)
{
uint256 codeSize;
assembly {
codeSize := extcodesize(_address)
}
return codeSize;
}
/**
* Gets the hash of a contract's code.
* @param _address Address to get a code hash for.
* @return Hash of the contract's code.
*/
function getCodeHash(
address _address
)
internal
view
returns (
bytes32
)
{
bytes32 codeHash;
assembly {
codeHash := extcodehash(_address)
}
return codeHash;
}
/**
* Creates a contract with some given initialization code.
* @param _code Contract initialization code.
* @return Address of the created contract.
*/
function createContract(
bytes memory _code
)
internal
returns (
address
)
{
address created;
assembly {
created := create(
0,
add(_code, 0x20),
mload(_code)
)
}
return created;
}
/**
* Computes the address that would be generated by CREATE.
* @param _creator Address creating the contract.
* @param _nonce Creator's nonce.
* @return Address to be generated by CREATE.
*/
function getAddressForCREATE(
address _creator,
uint256 _nonce
)
internal
pure
returns (
address
)
{
bytes[] memory encoded = new bytes[](2);
encoded[0] = Lib_RLPWriter.writeAddress(_creator);
encoded[1] = Lib_RLPWriter.writeUint(_nonce);
bytes memory encodedList = Lib_RLPWriter.writeList(encoded);
return Lib_Bytes32Utils.toAddress(keccak256(encodedList));
}
/**
* Computes the address that would be generated by CREATE2.
* @param _creator Address creating the contract.
* @param _bytecode Bytecode of the contract to be created.
* @param _salt 32 byte salt value mixed into the hash.
* @return Address to be generated by CREATE2.
*/
function getAddressForCREATE2(
address _creator,
bytes memory _bytecode,
bytes32 _salt
)
internal
pure
returns (
address
)
{
bytes32 hashedData = keccak256(abi.encodePacked(
byte(0xff),
_creator,
_salt,
keccak256(_bytecode)
));
return Lib_Bytes32Utils.toAddress(hashedData);
}
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
/* Library Imports */
import { Lib_ErrorUtils } from "../utils/Lib_ErrorUtils.sol";
import { Lib_PredeployAddresses } from "../constants/Lib_PredeployAddresses.sol";
/**
* @title Lib_ExecutionManagerWrapper
* @dev This library acts as a utility for easily calling the OVM_ExecutionManagerWrapper, the
* predeployed contract which exposes the `kall` builtin. Effectively, this contract allows the
* user to trigger OVM opcodes by directly calling the OVM_ExecutionManger.
*
* Compiler used: solc
* Runtime target: OVM
*/
library Lib_ExecutionManagerWrapper {
/**********************
* Internal Functions *
**********************/
/**
* Performs a safe ovmCREATE call.
* @param _bytecode Code for the new contract.
* @return Address of the created contract.
*/
function ovmCREATE(
bytes memory _bytecode
)
internal
returns (
address,
bytes memory
)
{
bytes memory returndata = _callWrapperContract(
abi.encodeWithSignature(
"ovmCREATE(bytes)",
_bytecode
)
);
return abi.decode(returndata, (address, bytes));
}
/**
* Performs a safe ovmGETNONCE call.
* @return Result of calling ovmGETNONCE.
*/
function ovmGETNONCE()
internal
returns (
uint256
)
{
bytes memory returndata = _callWrapperContract(
abi.encodeWithSignature(
"ovmGETNONCE()"
)
);
return abi.decode(returndata, (uint256));
}
/**
* Performs a safe ovmINCREMENTNONCE call.
*/
function ovmINCREMENTNONCE()
internal
{
_callWrapperContract(
abi.encodeWithSignature(
"ovmINCREMENTNONCE()"
)
);
}
/**
* Performs a safe ovmCREATEEOA call.
* @param _messageHash Message hash which was signed by EOA
* @param _v v value of signature (0 or 1)
* @param _r r value of signature
* @param _s s value of signature
*/
function ovmCREATEEOA(
bytes32 _messageHash,
uint8 _v,
bytes32 _r,
bytes32 _s
)
internal
{
_callWrapperContract(
abi.encodeWithSignature(
"ovmCREATEEOA(bytes32,uint8,bytes32,bytes32)",
_messageHash,
_v,
_r,
_s
)
);
}
/**
* Calls the ovmL1TXORIGIN opcode.
* @return Address that sent this message from L1.
*/
function ovmL1TXORIGIN()
internal
returns (
address
)
{
bytes memory returndata = _callWrapperContract(
abi.encodeWithSignature(
"ovmL1TXORIGIN()"
)
);
return abi.decode(returndata, (address));
}
/**
* Calls the ovmCHAINID opcode.
* @return Chain ID of the current network.
*/
function ovmCHAINID()
internal
returns (
uint256
)
{
bytes memory returndata = _callWrapperContract(
abi.encodeWithSignature(
"ovmCHAINID()"
)
);
return abi.decode(returndata, (uint256));
}
/**
* Performs a safe ovmADDRESS call.
* @return Result of calling ovmADDRESS.
*/
function ovmADDRESS()
internal
returns (
address
)
{
bytes memory returndata = _callWrapperContract(
abi.encodeWithSignature(
"ovmADDRESS()"
)
);
return abi.decode(returndata, (address));
}
/**
* Calls the value-enabled ovmCALL opcode.
* @param _gasLimit Amount of gas to be passed into this call.
* @param _address Address of the contract to call.
* @param _value ETH value to pass with the call.
* @param _calldata Data to send along with the call.
* @return _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call.
*/
function ovmCALL(
uint256 _gasLimit,
address _address,
uint256 _value,
bytes memory _calldata
)
internal
returns (
bool,
bytes memory
)
{
bytes memory returndata = _callWrapperContract(
abi.encodeWithSignature(
"ovmCALL(uint256,address,uint256,bytes)",
_gasLimit,
_address,
_value,
_calldata
)
);
return abi.decode(returndata, (bool, bytes));
}
/**
* Calls the ovmBALANCE opcode.
* @param _address OVM account to query the balance of.
* @return Balance of the account.
*/
function ovmBALANCE(
address _address
)
internal
returns (
uint256
)
{
bytes memory returndata = _callWrapperContract(
abi.encodeWithSignature(
"ovmBALANCE(address)",
_address
)
);
return abi.decode(returndata, (uint256));
}
/**
* Calls the ovmCALLVALUE opcode.
* @return Value of the current call frame.
*/
function ovmCALLVALUE()
internal
returns (
uint256
)
{
bytes memory returndata = _callWrapperContract(
abi.encodeWithSignature(
"ovmCALLVALUE()"
)
);
return abi.decode(returndata, (uint256));
}
/*********************
* Private Functions *
*********************/
/**
* Performs an ovm interaction and the necessary safety checks.
* @param _calldata Data to send to the OVM_ExecutionManager (encoded with sighash).
* @return Data sent back by the OVM_ExecutionManager.
*/
function _callWrapperContract(
bytes memory _calldata
)
private
returns (
bytes memory
)
{
(bool success, bytes memory returndata) =
Lib_PredeployAddresses.EXECUTION_MANAGER_WRAPPER.delegatecall(_calldata);
if (success == true) {
return returndata;
} else {
assembly {
revert(add(returndata, 0x20), mload(returndata))
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
/* Interface Imports */
import { iOVM_BondManager } from "../../iOVM/verification/iOVM_BondManager.sol";
/* Contract Imports */
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/**
* @title mockOVM_BondManager
*/
contract mockOVM_BondManager is iOVM_BondManager, Lib_AddressResolver {
constructor(
address _libAddressManager
)
Lib_AddressResolver(_libAddressManager)
{}
function recordGasSpent(
bytes32 _preStateRoot,
bytes32 _txHash,
address _who,
uint256 _gasSpent
)
override
public
{}
function finalize(
bytes32 _preStateRoot,
address _publisher,
uint256 _timestamp
)
override
public
{}
function deposit()
override
public
{}
function startWithdrawal()
override
public
{}
function finalizeWithdrawal()
override
public
{}
function claim(
address _who
)
override
public
{}
function isCollateralized(
address _who
)
override
public
view
returns (
bool
)
{
// Only authenticate sequencer to submit state root batches.
return _who == resolve("OVM_Proposer");
}
function getGasSpent(
bytes32, // _preStateRoot,
address // _who
)
override
public
pure
returns (
uint256
)
{
return 0;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
contract Helper_ModifiableStorage {
mapping (address => address) private target;
constructor(
address _target
)
{
target[address(this)] = _target;
}
fallback()
external
{
(bool success, bytes memory returndata) = target[address(this)].delegatecall(msg.data);
if (success) {
assembly {
return(add(returndata, 0x20), mload(returndata))
}
} else {
assembly {
revert(add(returndata, 0x20), mload(returndata))
}
}
}
function __setStorageSlot(
bytes32 _key,
bytes32 _value
)
public
{
assembly {
sstore(_key, _value)
}
}
function __getStorageSlot(
bytes32 _key
)
public
view
returns (
bytes32 _value
)
{
bytes32 value;
assembly {
value := sload(_key)
}
return value;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
import { Helper_SimpleProxy } from "./Helper_SimpleProxy.sol";
contract Helper_PredeployCaller is Helper_SimpleProxy {
function callPredeploy(
address _predeploy,
bytes memory _data
)
public
{
if (msg.sender == owner) {
makeExternalCall(_predeploy, _data);
} else {
makeExternalCall(target, msg.data);
}
}
function callPredeployAbi(
address _predeploy,
bytes memory _data
)
public
returns (
bytes memory
)
{
bool success;
bytes memory returndata;
if (msg.sender == owner) {
(success, returndata) = _predeploy.call(_data);
} else {
(success, returndata) = target.call(msg.data);
}
require(success, "Predeploy call reverted");
return returndata;
}
function getL1MessageSender(
address _predeploy,
bytes memory _data
)
public
returns (
address
)
{
callPredeploy(_predeploy, _data);
return address(0); // unused: silence compiler
}
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Logging */
import { console } from "hardhat/console.sol";
/**
* @title Helper_TestRunner
*/
contract Helper_TestRunner {
struct TestStep {
string functionName;
bytes functionData;
bool expectedReturnStatus;
bytes expectedReturnData;
bool onlyValidateFlag;
}
function runSingleTestStep(
TestStep memory _step
)
public
{
bytes32 namehash = keccak256(abi.encodePacked(_step.functionName));
if (namehash == keccak256("evmRETURN")) {
bytes memory functionData = _step.functionData;
assembly {
return(add(functionData, 0x20), mload(functionData))
}
}
if (namehash == keccak256("evmREVERT")) {
bytes memory functionData = _step.functionData;
assembly {
revert(add(functionData, 0x20), mload(functionData))
}
}
if (namehash == keccak256("evmINVALID")) {
assembly {
invalid()
}
}
(bool success, bytes memory returndata) = address(msg.sender).call(_step.functionData);
if (success != _step.expectedReturnStatus) {
if (success == true) {
console.log("ERROR: Expected function to revert, but function returned successfully");
console.log("Offending Step: %s", _step.functionName);
console.log("Return Data:");
console.logBytes(returndata);
console.log("");
} else {
(
uint256 _flag,
uint256 _nuisanceGasLeft,
uint256 _ovmGasRefund,
bytes memory _data
) = _decodeRevertData(returndata);
console.log("ERROR: Expected function to return successfully, but function reverted");
console.log("Offending Step: %s", _step.functionName);
console.log("Flag: %s", _flag);
console.log("Nuisance Gas Left: %s", _nuisanceGasLeft);
console.log("OVM Gas Refund: %s", _ovmGasRefund);
console.log("Extra Data:");
console.logBytes(_data);
console.log("");
}
_failStep();
}
if (keccak256(returndata) != keccak256(_step.expectedReturnData)) {
if (success == true) {
console.log("ERROR: Actual return data does not match expected return data");
console.log("Offending Step: %s", _step.functionName);
console.log("Expected:");
console.logBytes(_step.expectedReturnData);
console.log("Actual:");
console.logBytes(returndata);
console.log("");
_failStep();
} else {
(
uint256 _expectedFlag,
uint256 _expectedNuisanceGasLeft,
uint256 _expectedOvmGasRefund,
bytes memory _expectedData
) = _decodeRevertData(_step.expectedReturnData);
(
uint256 _flag,
uint256 _nuisanceGasLeft,
uint256 _ovmGasRefund,
bytes memory _data
) = _decodeRevertData(returndata);
if (
_step.onlyValidateFlag
) {
if (
_expectedFlag != _flag
) {
console.log("ERROR: Actual revert flag does not match expected revert flag data");
console.log("Offending Step: %s", _step.functionName);
console.log("Expected Flag: %s", _expectedFlag);
console.log("Actual Flag: %s", _flag);
_failStep();
}
} else {
console.log("ERROR: Actual revert flag data does not match expected revert flag data");
console.log("Offending Step: %s", _step.functionName);
console.log("Expected Flag: %s", _expectedFlag);
console.log("Actual Flag: %s", _flag);
console.log("Expected Nuisance Gas Left: %s", _expectedNuisanceGasLeft);
console.log("Actual Nuisance Gas Left: %s", _nuisanceGasLeft);
console.log("Expected OVM Gas Refund: %s", _expectedOvmGasRefund);
console.log("Actual OVM Gas Refund: %s", _ovmGasRefund);
console.log("Expected Extra Data:");
console.logBytes(_expectedData);
console.log("Actual Extra Data:");
console.logBytes(_data);
console.log("");
_failStep();
}
}
}
if (success == false) {
assembly {
revert(add(returndata, 0x20), mload(returndata))
}
}
}
function runMultipleTestSteps(
TestStep[] memory _steps
)
public
{
for (uint256 i = 0; i < _steps.length; i++) {
runSingleTestStep(_steps[i]);
}
}
function _decodeRevertData(
bytes memory _revertdata
)
internal
pure
returns (
uint256 _flag,
uint256 _nuisanceGasLeft,
uint256 _ovmGasRefund,
bytes memory _data
)
{
if (_revertdata.length == 0) {
return (
0,
0,
0,
bytes('')
);
}
return abi.decode(_revertdata, (uint256, uint256, uint256, bytes));
}
function _failStep()
internal
pure
{
revert("Test step failed.");
}
}
contract Helper_TestRunner_CREATE is Helper_TestRunner {
constructor(
bytes memory _bytecode,
TestStep[] memory _steps
)
{
if (_steps.length > 0) {
runMultipleTestSteps(_steps);
} else {
assembly {
return(add(_bytecode, 0x20), mload(_bytecode))
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
import { OVM_BondManager } from "./../optimistic-ethereum/OVM/verification/OVM_BondManager.sol";
contract Mock_FraudVerifier {
OVM_BondManager bondManager;
mapping (bytes32 => address) transitioners;
function setBondManager(OVM_BondManager _bondManager) public {
bondManager = _bondManager;
}
function setStateTransitioner(bytes32 preStateRoot, bytes32 txHash, address addr) public {
transitioners[keccak256(abi.encodePacked(preStateRoot, txHash))] = addr;
}
function getStateTransitioner(
bytes32 _preStateRoot,
bytes32 _txHash
)
public
view
returns (
address
)
{
return transitioners[keccak256(abi.encodePacked(_preStateRoot, _txHash))];
}
function finalize(bytes32 _preStateRoot, address publisher, uint256 timestamp) public {
bondManager.finalize(_preStateRoot, publisher, timestamp);
}
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_EIP155Tx } from "../../optimistic-ethereum/libraries/codec/Lib_EIP155Tx.sol";
/**
* @title TestLib_EIP155Tx
*/
contract TestLib_EIP155Tx {
function decode(
bytes memory _encoded,
uint256 _chainId
)
public
pure
returns (
Lib_EIP155Tx.EIP155Tx memory
)
{
return Lib_EIP155Tx.decode(
_encoded,
_chainId
);
}
function encode(
Lib_EIP155Tx.EIP155Tx memory _transaction,
bool _includeSignature
)
public
pure
returns (
bytes memory
)
{
return Lib_EIP155Tx.encode(
_transaction,
_includeSignature
);
}
function hash(
Lib_EIP155Tx.EIP155Tx memory _transaction
)
public
pure
returns (
bytes32
)
{
return Lib_EIP155Tx.hash(
_transaction
);
}
function sender(
Lib_EIP155Tx.EIP155Tx memory _transaction
)
public
pure
returns (
address
)
{
return Lib_EIP155Tx.sender(
_transaction
);
}
}
// SPDX-License-Identifier: MIT
// @unsupported: ovm
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_EthUtils } from "../../optimistic-ethereum/libraries/utils/Lib_EthUtils.sol";
/**
* @title TestLib_EthUtils
*/
contract TestLib_EthUtils {
function getCode(
address _address,
uint256 _offset,
uint256 _length
)
public
view
returns (
bytes memory _code
)
{
return Lib_EthUtils.getCode(
_address,
_offset,
_length
);
}
function getCode(
address _address
)
public
view
returns (
bytes memory _code
)
{
return Lib_EthUtils.getCode(
_address
);
}
function getCodeSize(
address _address
)
public
view
returns (
uint256 _codeSize
)
{
return Lib_EthUtils.getCodeSize(
_address
);
}
function getCodeHash(
address _address
)
public
view
returns (
bytes32 _codeHash
)
{
return Lib_EthUtils.getCodeHash(
_address
);
}
function createContract(
bytes memory _code
)
public
returns (
address _created
)
{
return Lib_EthUtils.createContract(
_code
);
}
function getAddressForCREATE(
address _creator,
uint256 _nonce
)
public
pure
returns (
address _address
)
{
return Lib_EthUtils.getAddressForCREATE(
_creator,
_nonce
);
}
function getAddressForCREATE2(
address _creator,
bytes memory _bytecode,
bytes32 _salt
)
public
pure
returns (address _address)
{
return Lib_EthUtils.getAddressForCREATE2(
_creator,
_bytecode,
_salt
);
}
}
...@@ -21,12 +21,6 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -21,12 +21,6 @@ const deployFn: DeployFunction = async (hre) => {
address: predeploys.OVM_L2CrossDomainMessenger, address: predeploys.OVM_L2CrossDomainMessenger,
}) })
await registerAddress({
hre,
name: 'OVM_DecompressionPrecompileAddress',
address: predeploys.OVM_SequencerEntrypoint,
})
await registerAddress({ await registerAddress({
hre, hre,
name: 'OVM_Sequencer', name: 'OVM_Sequencer',
...@@ -38,12 +32,6 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -38,12 +32,6 @@ const deployFn: DeployFunction = async (hre) => {
name: 'OVM_Proposer', name: 'OVM_Proposer',
address: (hre as any).deployConfig.ovmProposerAddress, address: (hre as any).deployConfig.ovmProposerAddress,
}) })
await registerAddress({
hre,
name: 'OVM_L2BatchMessageRelayer',
address: (hre as any).deployConfig.ovmRelayerAddress,
})
} }
deployFn.tags = ['Lib_AddressManager', 'required'] deployFn.tags = ['Lib_AddressManager', 'required']
......
...@@ -16,7 +16,7 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -16,7 +16,7 @@ const deployFn: DeployFunction = async (hre) => {
} }
) )
const result = await deploy('mockOVM_BondManager', { const result = await deploy('OVM_BondManager', {
from: deployer, from: deployer,
args: [Lib_AddressManager.address], args: [Lib_AddressManager.address],
log: true, log: true,
...@@ -30,6 +30,6 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -30,6 +30,6 @@ const deployFn: DeployFunction = async (hre) => {
} }
deployFn.dependencies = ['Lib_AddressManager'] deployFn.dependencies = ['Lib_AddressManager']
deployFn.tags = ['mockOVM_BondManager'] deployFn.tags = ['OVM_BondManager']
export default deployFn export default deployFn
/* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types'
/* Imports: Internal */
import {
deployAndRegister,
getDeployedContract,
} from '../src/hardhat-deploy-ethers'
const deployFn: DeployFunction = async (hre) => {
const Lib_AddressManager = await getDeployedContract(
hre,
'Lib_AddressManager'
)
await deployAndRegister({
hre,
name: 'OVM_ExecutionManager',
args: [
Lib_AddressManager.address,
{
minTransactionGasLimit: (hre as any).deployConfig
.emMinTransactionGasLimit,
maxTransactionGasLimit: (hre as any).deployConfig
.emMaxTransactionGasLimit,
maxGasPerQueuePerEpoch: (hre as any).deployConfig
.emMaxGasPerQueuePerEpoch,
secondsPerEpoch: (hre as any).deployConfig.emSecondsPerEpoch,
},
{
ovmCHAINID: (hre as any).deployConfig.emOvmChainId,
},
],
})
}
deployFn.dependencies = ['Lib_AddressManager']
deployFn.tags = ['OVM_ExecutionManager']
export default deployFn
/* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types'
/* Imports: Internal */
import {
deployAndRegister,
getDeployedContract,
} from '../src/hardhat-deploy-ethers'
const deployFn: DeployFunction = async (hre) => {
const Lib_AddressManager = await getDeployedContract(
hre,
'Lib_AddressManager'
)
await deployAndRegister({
hre,
name: 'OVM_FraudVerifier',
args: [Lib_AddressManager.address],
})
}
deployFn.dependencies = ['Lib_AddressManager']
deployFn.tags = ['OVM_FraudVerifier']
export default deployFn
/* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types'
/* Imports: Internal */
import { deployAndRegister } from '../src/hardhat-deploy-ethers'
const deployFn: DeployFunction = async (hre) => {
await deployAndRegister({
hre,
name: 'OVM_StateManagerFactory',
args: [],
})
}
deployFn.tags = ['OVM_FraudVerifier']
export default deployFn
/* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types'
/* Imports: Internal */
import {
deployAndRegister,
getDeployedContract,
} from '../src/hardhat-deploy-ethers'
const deployFn: DeployFunction = async (hre) => {
const Lib_AddressManager = await getDeployedContract(
hre,
'Lib_AddressManager'
)
await deployAndRegister({
hre,
name: 'OVM_StateTransitionerFactory',
args: [Lib_AddressManager.address],
})
}
deployFn.dependencies = ['Lib_AddressManager']
deployFn.tags = ['OVM_StateTransitionerFactory']
export default deployFn
/* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types'
/* Imports: Internal */
import { deployAndRegister } from '../src/hardhat-deploy-ethers'
const deployFn: DeployFunction = async (hre) => {
await deployAndRegister({
hre,
name: 'OVM_SafetyChecker',
args: [],
})
}
deployFn.tags = ['OVM_SafetyChecker']
export default deployFn
/* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types'
/* Imports: Internal */
import {
deployAndRegister,
getDeployedContract,
} from '../src/hardhat-deploy-ethers'
const deployFn: DeployFunction = async (hre) => {
const Lib_AddressManager = await getDeployedContract(
hre,
'Lib_AddressManager'
)
await deployAndRegister({
hre,
name: 'OVM_L1MultiMessageRelayer',
args: [Lib_AddressManager.address],
})
}
deployFn.dependencies = ['Lib_AddressManager']
deployFn.tags = ['OVM_L1MultiMessageRelayer']
export default deployFn
...@@ -6,9 +6,6 @@ export type Network = 'goerli' | 'kovan' | 'mainnet' ...@@ -6,9 +6,6 @@ export type Network = 'goerli' | 'kovan' | 'mainnet'
interface L1Contracts { interface L1Contracts {
addressManager: Contract addressManager: Contract
canonicalTransactionChain: Contract canonicalTransactionChain: Contract
executionManager: Contract
fraudVerifier: Contract
multiMessageRelayer: Contract
stateCommitmentChain: Contract stateCommitmentChain: Contract
xDomainMessengerProxy: Contract xDomainMessengerProxy: Contract
bondManager: Contract bondManager: Contract
...@@ -20,9 +17,6 @@ interface L2Contracts { ...@@ -20,9 +17,6 @@ interface L2Contracts {
messagePasser: Contract messagePasser: Contract
messageSender: Contract messageSender: Contract
deployerWhiteList: Contract deployerWhiteList: Contract
ecdsaContractAccount: Contract
sequencerEntrypoint: Contract
erc1820Registry: Contract
addressManager: Contract addressManager: Contract
} }
...@@ -70,19 +64,13 @@ export const connectL1Contracts = async ( ...@@ -70,19 +64,13 @@ export const connectL1Contracts = async (
canonicalTransactionChain: toEthersContract( canonicalTransactionChain: toEthersContract(
l1ContractData.OVM_CanonicalTransactionChain l1ContractData.OVM_CanonicalTransactionChain
), ),
executionManager: toEthersContract(l1ContractData.OVM_ExecutionManager),
fraudVerifier: toEthersContract(l1ContractData.OVM_FraudVerifier),
multiMessageRelayer: toEthersContract(
l1ContractData.OVM_L1MultiMessageRelayer
),
stateCommitmentChain: toEthersContract( stateCommitmentChain: toEthersContract(
l1ContractData.OVM_StateCommitmentChain l1ContractData.OVM_StateCommitmentChain
), ),
xDomainMessengerProxy: toEthersContract( xDomainMessengerProxy: toEthersContract(
l1ContractData.Proxy__OVM_L1CrossDomainMessenger l1ContractData.Proxy__OVM_L1CrossDomainMessenger
), ),
// TODO: update this with actual bond manager when its ready bondManager: toEthersContract(l1ContractData.OVM_BondManager),
bondManager: toEthersContract(l1ContractData.mockOVM_BondManager),
} }
} }
...@@ -109,13 +97,6 @@ export const connectL2Contracts = async ( ...@@ -109,13 +97,6 @@ export const connectL2Contracts = async (
messagePasser: toEthersContract(l2ContractData.OVM_L2ToL1MessagePasser), messagePasser: toEthersContract(l2ContractData.OVM_L2ToL1MessagePasser),
messageSender: toEthersContract(l2ContractData.OVM_L1MessageSender), messageSender: toEthersContract(l2ContractData.OVM_L1MessageSender),
deployerWhiteList: toEthersContract(l2ContractData.OVM_DeployerWhitelist), deployerWhiteList: toEthersContract(l2ContractData.OVM_DeployerWhitelist),
ecdsaContractAccount: toEthersContract(
l2ContractData.OVM_ECDSAContractAccount
),
sequencerEntrypoint: toEthersContract(
l2ContractData.OVM_SequencerEntrypoint
),
erc1820Registry: toEthersContract(l2ContractData.ERC1820Registry),
addressManager: toEthersContract(l2ContractData.Lib_AddressManager), addressManager: toEthersContract(l2ContractData.Lib_AddressManager),
} }
} }
...@@ -10,42 +10,24 @@ import { Network } from './connect-contracts' ...@@ -10,42 +10,24 @@ import { Network } from './connect-contracts'
const Mainnet__Lib_AddressManager = require('../deployments/mainnet/Lib_AddressManager.json') const Mainnet__Lib_AddressManager = require('../deployments/mainnet/Lib_AddressManager.json')
const Mainnet__OVM_CanonicalTransactionChain = require('../deployments/mainnet/OVM_CanonicalTransactionChain.json') const Mainnet__OVM_CanonicalTransactionChain = require('../deployments/mainnet/OVM_CanonicalTransactionChain.json')
const Mainnet__OVM_ExecutionManager = require('../deployments/mainnet/OVM_ExecutionManager.json')
const Mainnet__OVM_FraudVerifier = require('../deployments/mainnet/OVM_FraudVerifier.json')
const Mainnet__OVM_L1CrossDomainMessenger = require('../deployments/mainnet/OVM_L1CrossDomainMessenger.json') const Mainnet__OVM_L1CrossDomainMessenger = require('../deployments/mainnet/OVM_L1CrossDomainMessenger.json')
const Mainnet__OVM_L1MultiMessageRelayer = require('../deployments/mainnet/OVM_L1MultiMessageRelayer.json')
const Mainnet__OVM_SafetyChecker = require('../deployments/mainnet/OVM_SafetyChecker.json')
const Mainnet__OVM_StateCommitmentChain = require('../deployments/mainnet/OVM_StateCommitmentChain.json') const Mainnet__OVM_StateCommitmentChain = require('../deployments/mainnet/OVM_StateCommitmentChain.json')
const Mainnet__OVM_StateManagerFactory = require('../deployments/mainnet/OVM_StateManagerFactory.json')
const Mainnet__OVM_StateTransitionerFactory = require('../deployments/mainnet/OVM_StateTransitionerFactory.json')
const Mainnet__Proxy__OVM_L1CrossDomainMessenger = require('../deployments/mainnet/Proxy__OVM_L1CrossDomainMessenger.json') const Mainnet__Proxy__OVM_L1CrossDomainMessenger = require('../deployments/mainnet/Proxy__OVM_L1CrossDomainMessenger.json')
const Mainnet__mockOVM_BondManager = require('../deployments/mainnet/mockOVM_BondManager.json') const Mainnet__OVM_BondManager = require('../deployments/mainnet/mockOVM_BondManager.json')
const Kovan__Lib_AddressManager = require('../deployments/kovan/Lib_AddressManager.json') const Kovan__Lib_AddressManager = require('../deployments/kovan/Lib_AddressManager.json')
const Kovan__OVM_CanonicalTransactionChain = require('../deployments/kovan/OVM_CanonicalTransactionChain.json') const Kovan__OVM_CanonicalTransactionChain = require('../deployments/kovan/OVM_CanonicalTransactionChain.json')
const Kovan__OVM_ExecutionManager = require('../deployments/kovan/OVM_ExecutionManager.json')
const Kovan__OVM_FraudVerifier = require('../deployments/kovan/OVM_FraudVerifier.json')
const Kovan__OVM_L1CrossDomainMessenger = require('../deployments/kovan/OVM_L1CrossDomainMessenger.json') const Kovan__OVM_L1CrossDomainMessenger = require('../deployments/kovan/OVM_L1CrossDomainMessenger.json')
const Kovan__OVM_L1MultiMessageRelayer = require('../deployments/kovan/OVM_L1MultiMessageRelayer.json')
const Kovan__OVM_SafetyChecker = require('../deployments/kovan/OVM_SafetyChecker.json')
const Kovan__OVM_StateCommitmentChain = require('../deployments/kovan/OVM_StateCommitmentChain.json') const Kovan__OVM_StateCommitmentChain = require('../deployments/kovan/OVM_StateCommitmentChain.json')
const Kovan__OVM_StateManagerFactory = require('../deployments/kovan/OVM_StateManagerFactory.json')
const Kovan__OVM_StateTransitionerFactory = require('../deployments/kovan/OVM_StateTransitionerFactory.json')
const Kovan__Proxy__OVM_L1CrossDomainMessenger = require('../deployments/kovan/Proxy__OVM_L1CrossDomainMessenger.json') const Kovan__Proxy__OVM_L1CrossDomainMessenger = require('../deployments/kovan/Proxy__OVM_L1CrossDomainMessenger.json')
const Kovan__mockOVM_BondManager = require('../deployments/kovan/mockOVM_BondManager.json') const Kovan__OVM_BondManager = require('../deployments/kovan/mockOVM_BondManager.json')
const Goerli__Lib_AddressManager = require('../deployments/goerli/Lib_AddressManager.json') const Goerli__Lib_AddressManager = require('../deployments/goerli/Lib_AddressManager.json')
const Goerli__OVM_CanonicalTransactionChain = require('../deployments/goerli/OVM_CanonicalTransactionChain.json') const Goerli__OVM_CanonicalTransactionChain = require('../deployments/goerli/OVM_CanonicalTransactionChain.json')
const Goerli__OVM_ExecutionManager = require('../deployments/goerli/OVM_ExecutionManager.json')
const Goerli__OVM_FraudVerifier = require('../deployments/goerli/OVM_FraudVerifier.json')
const Goerli__OVM_L1CrossDomainMessenger = require('../deployments/goerli/OVM_L1CrossDomainMessenger.json') const Goerli__OVM_L1CrossDomainMessenger = require('../deployments/goerli/OVM_L1CrossDomainMessenger.json')
const Goerli__OVM_L1MultiMessageRelayer = require('../deployments/goerli/OVM_L1MultiMessageRelayer.json')
const Goerli__OVM_SafetyChecker = require('../deployments/goerli/OVM_SafetyChecker.json')
const Goerli__OVM_StateCommitmentChain = require('../deployments/goerli/OVM_StateCommitmentChain.json') const Goerli__OVM_StateCommitmentChain = require('../deployments/goerli/OVM_StateCommitmentChain.json')
const Goerli__OVM_StateManagerFactory = require('../deployments/goerli/OVM_StateManagerFactory.json')
const Goerli__OVM_StateTransitionerFactory = require('../deployments/goerli/OVM_StateTransitionerFactory.json')
const Goerli__Proxy__OVM_L1CrossDomainMessenger = require('../deployments/goerli/Proxy__OVM_L1CrossDomainMessenger.json') const Goerli__Proxy__OVM_L1CrossDomainMessenger = require('../deployments/goerli/Proxy__OVM_L1CrossDomainMessenger.json')
const Goerli__mockOVM_BondManager = require('../deployments/goerli/mockOVM_BondManager.json') const Goerli__OVM_BondManager = require('../deployments/goerli/mockOVM_BondManager.json')
export const getL1ContractData = (network: Network) => { export const getL1ContractData = (network: Network) => {
return { return {
...@@ -59,55 +41,25 @@ export const getL1ContractData = (network: Network) => { ...@@ -59,55 +41,25 @@ export const getL1ContractData = (network: Network) => {
kovan: Kovan__OVM_CanonicalTransactionChain, kovan: Kovan__OVM_CanonicalTransactionChain,
goerli: Goerli__OVM_CanonicalTransactionChain, goerli: Goerli__OVM_CanonicalTransactionChain,
}[network], }[network],
OVM_ExecutionManager: {
mainnet: Mainnet__OVM_ExecutionManager,
kovan: Kovan__OVM_ExecutionManager,
goerli: Goerli__OVM_ExecutionManager,
}[network],
OVM_FraudVerifier: {
mainnet: Mainnet__OVM_FraudVerifier,
kovan: Kovan__OVM_FraudVerifier,
goerli: Goerli__OVM_FraudVerifier,
}[network],
OVM_L1CrossDomainMessenger: { OVM_L1CrossDomainMessenger: {
mainnet: Mainnet__OVM_L1CrossDomainMessenger, mainnet: Mainnet__OVM_L1CrossDomainMessenger,
kovan: Kovan__OVM_L1CrossDomainMessenger, kovan: Kovan__OVM_L1CrossDomainMessenger,
goerli: Goerli__OVM_L1CrossDomainMessenger, goerli: Goerli__OVM_L1CrossDomainMessenger,
}[network], }[network],
OVM_L1MultiMessageRelayer: {
mainnet: Mainnet__OVM_L1MultiMessageRelayer,
kovan: Kovan__OVM_L1MultiMessageRelayer,
goerli: Goerli__OVM_L1MultiMessageRelayer,
}[network],
OVM_SafetyChecker: {
mainnet: Mainnet__OVM_SafetyChecker,
kovan: Kovan__OVM_SafetyChecker,
goerli: Goerli__OVM_SafetyChecker,
}[network],
OVM_StateCommitmentChain: { OVM_StateCommitmentChain: {
mainnet: Mainnet__OVM_StateCommitmentChain, mainnet: Mainnet__OVM_StateCommitmentChain,
kovan: Kovan__OVM_StateCommitmentChain, kovan: Kovan__OVM_StateCommitmentChain,
goerli: Goerli__OVM_StateCommitmentChain, goerli: Goerli__OVM_StateCommitmentChain,
}[network], }[network],
OVM_StateManagerFactory: {
mainnet: Mainnet__OVM_StateManagerFactory,
kovan: Kovan__OVM_StateManagerFactory,
goerli: Goerli__OVM_StateManagerFactory,
}[network],
OVM_StateTransitionerFactory: {
mainnet: Mainnet__OVM_StateTransitionerFactory,
kovan: Kovan__OVM_StateTransitionerFactory,
goerli: Goerli__OVM_StateTransitionerFactory,
}[network],
Proxy__OVM_L1CrossDomainMessenger: { Proxy__OVM_L1CrossDomainMessenger: {
mainnet: Mainnet__Proxy__OVM_L1CrossDomainMessenger, mainnet: Mainnet__Proxy__OVM_L1CrossDomainMessenger,
kovan: Kovan__Proxy__OVM_L1CrossDomainMessenger, kovan: Kovan__Proxy__OVM_L1CrossDomainMessenger,
goerli: Goerli__Proxy__OVM_L1CrossDomainMessenger, goerli: Goerli__Proxy__OVM_L1CrossDomainMessenger,
}[network], }[network],
mockOVM_BondManager: { OVM_BondManager: {
mainnet: Mainnet__mockOVM_BondManager, mainnet: Mainnet__OVM_BondManager,
kovan: Kovan__mockOVM_BondManager, kovan: Kovan__OVM_BondManager,
goerli: Goerli__mockOVM_BondManager, goerli: Goerli__OVM_BondManager,
}[network], }[network],
} }
} }
...@@ -117,9 +69,6 @@ const OVM_L2CrossDomainMessenger = require('../artifacts/contracts/optimistic-et ...@@ -117,9 +69,6 @@ const OVM_L2CrossDomainMessenger = require('../artifacts/contracts/optimistic-et
const OVM_L2ToL1MessagePasser = require('../artifacts/contracts/optimistic-ethereum/OVM/predeploys/OVM_L2ToL1MessagePasser.sol/OVM_L2ToL1MessagePasser.json') const OVM_L2ToL1MessagePasser = require('../artifacts/contracts/optimistic-ethereum/OVM/predeploys/OVM_L2ToL1MessagePasser.sol/OVM_L2ToL1MessagePasser.json')
const OVM_L1MessageSender = require('../artifacts/contracts/optimistic-ethereum/OVM/predeploys/OVM_L1MessageSender.sol/OVM_L1MessageSender.json') const OVM_L1MessageSender = require('../artifacts/contracts/optimistic-ethereum/OVM/predeploys/OVM_L1MessageSender.sol/OVM_L1MessageSender.json')
const OVM_DeployerWhitelist = require('../artifacts/contracts/optimistic-ethereum/OVM/predeploys/OVM_DeployerWhitelist.sol/OVM_DeployerWhitelist.json') const OVM_DeployerWhitelist = require('../artifacts/contracts/optimistic-ethereum/OVM/predeploys/OVM_DeployerWhitelist.sol/OVM_DeployerWhitelist.json')
const OVM_ECDSAContractAccount = require('../artifacts/contracts/optimistic-ethereum/OVM/predeploys/OVM_ECDSAContractAccount.sol/OVM_ECDSAContractAccount.json')
const OVM_SequencerEntrypoint = require('../artifacts/contracts/optimistic-ethereum/OVM/predeploys/OVM_SequencerEntrypoint.sol/OVM_SequencerEntrypoint.json')
const ERC1820Registry = require('../artifacts/contracts/optimistic-ethereum/OVM/predeploys/ERC1820Registry.sol/ERC1820Registry.json')
const Lib_AddressManager = require('../artifacts/contracts/optimistic-ethereum/libraries/resolver/Lib_AddressManager.sol/Lib_AddressManager.json') const Lib_AddressManager = require('../artifacts/contracts/optimistic-ethereum/libraries/resolver/Lib_AddressManager.sol/Lib_AddressManager.json')
export const getL2ContractData = () => { export const getL2ContractData = () => {
...@@ -144,18 +93,6 @@ export const getL2ContractData = () => { ...@@ -144,18 +93,6 @@ export const getL2ContractData = () => {
abi: OVM_DeployerWhitelist.abi, abi: OVM_DeployerWhitelist.abi,
address: l2Addresses.OVM_DeployerWhitelist, address: l2Addresses.OVM_DeployerWhitelist,
}, },
OVM_ECDSAContractAccount: {
abi: OVM_ECDSAContractAccount.abi,
address: l2Addresses.OVM_ECDSAContractAccount,
},
OVM_SequencerEntrypoint: {
abi: OVM_SequencerEntrypoint.abi,
address: l2Addresses.OVM_SequencerEntrypoint,
},
ERC1820Registry: {
abi: ERC1820Registry.abi,
address: l2Addresses.ERC1820Registry,
},
Lib_AddressManager: { Lib_AddressManager: {
abi: Lib_AddressManager.abi, abi: Lib_AddressManager.abi,
address: l2Addresses.Lib_AddressManager, address: l2Addresses.Lib_AddressManager,
......
...@@ -9,28 +9,6 @@ import { predeploys } from '../predeploys' ...@@ -9,28 +9,6 @@ import { predeploys } from '../predeploys'
export interface RollupDeployConfig { export interface RollupDeployConfig {
deploymentSigner: Signer deploymentSigner: Signer
ovmGasMeteringConfig: {
minTransactionGasLimit: number
maxTransactionGasLimit: number
maxGasPerQueuePerEpoch: number
secondsPerEpoch: number
}
ovmGlobalContext: {
ovmCHAINID: number
L2CrossDomainMessengerAddress: string
}
transactionChainConfig: {
sequencer: string | Signer
forceInclusionPeriodSeconds: number
forceInclusionPeriodBlocks: number
}
stateChainConfig: {
fraudProofWindowSeconds: number
sequencerPublishWindowSeconds: number
}
l1CrossDomainMessengerConfig: {
relayerAddress?: string | Signer
}
whitelistConfig: { whitelistConfig: {
owner: string | Signer owner: string | Signer
allowArbitraryContractDeployment: boolean allowArbitraryContractDeployment: boolean
...@@ -59,16 +37,6 @@ export const makeContractDeployConfig = async ( ...@@ -59,16 +37,6 @@ export const makeContractDeployConfig = async (
config: RollupDeployConfig, config: RollupDeployConfig,
AddressManager: Contract AddressManager: Contract
): Promise<ContractDeployConfig> => { ): Promise<ContractDeployConfig> => {
const _sendTx = async (
txPromise: Promise<TransactionResponse>
): Promise<TransactionResponse> => {
const res = await txPromise
if (config.waitForReceipts) {
await res.wait()
}
return res
}
return { return {
OVM_L2CrossDomainMessenger: { OVM_L2CrossDomainMessenger: {
factory: getContractFactory('OVM_L2CrossDomainMessenger'), factory: getContractFactory('OVM_L2CrossDomainMessenger'),
...@@ -93,46 +61,10 @@ export const makeContractDeployConfig = async ( ...@@ -93,46 +61,10 @@ export const makeContractDeployConfig = async (
factory: getContractFactory('OVM_L2ToL1MessagePasser'), factory: getContractFactory('OVM_L2ToL1MessagePasser'),
params: [], params: [],
}, },
OVM_SafetyChecker: {
factory: getContractFactory('OVM_SafetyChecker'),
params: [],
},
OVM_ExecutionManager: {
factory: getContractFactory('OVM_ExecutionManager'),
params: [
AddressManager.address,
config.ovmGasMeteringConfig,
config.ovmGlobalContext,
],
},
OVM_StateManager: {
factory: getContractFactory('OVM_StateManager'),
params: [await config.deploymentSigner.getAddress()],
afterDeploy: async (contracts): Promise<void> => {
await _sendTx(
contracts.OVM_StateManager.setExecutionManager(
contracts.OVM_ExecutionManager.address,
config.deployOverrides
)
)
},
},
OVM_ECDSAContractAccount: {
factory: getContractFactory('OVM_ECDSAContractAccount'),
},
OVM_SequencerEntrypoint: {
factory: getContractFactory('OVM_SequencerEntrypoint'),
},
OVM_ETH: { OVM_ETH: {
factory: getContractFactory('OVM_ETH'), factory: getContractFactory('OVM_ETH'),
params: [], params: [],
}, },
OVM_ProxyEOA: {
factory: getContractFactory('OVM_ProxyEOA'),
},
OVM_ExecutionManagerWrapper: {
factory: getContractFactory('OVM_ExecutionManagerWrapper'),
},
OVM_GasPriceOracle: { OVM_GasPriceOracle: {
factory: getContractFactory('OVM_GasPriceOracle'), factory: getContractFactory('OVM_GasPriceOracle'),
params: [ params: [
......
...@@ -11,15 +11,10 @@ export const predeploys = { ...@@ -11,15 +11,10 @@ export const predeploys = {
OVM_L2ToL1MessagePasser: '0x4200000000000000000000000000000000000000', OVM_L2ToL1MessagePasser: '0x4200000000000000000000000000000000000000',
OVM_L1MessageSender: '0x4200000000000000000000000000000000000001', OVM_L1MessageSender: '0x4200000000000000000000000000000000000001',
OVM_DeployerWhitelist: '0x4200000000000000000000000000000000000002', OVM_DeployerWhitelist: '0x4200000000000000000000000000000000000002',
OVM_ECDSAContractAccount: '0x4200000000000000000000000000000000000003',
OVM_SequencerEntrypoint: '0x4200000000000000000000000000000000000005',
OVM_ETH: '0x4200000000000000000000000000000000000006', OVM_ETH: '0x4200000000000000000000000000000000000006',
OVM_L2CrossDomainMessenger: '0x4200000000000000000000000000000000000007', OVM_L2CrossDomainMessenger: '0x4200000000000000000000000000000000000007',
Lib_AddressManager: '0x4200000000000000000000000000000000000008', Lib_AddressManager: '0x4200000000000000000000000000000000000008',
OVM_ProxyEOA: '0x4200000000000000000000000000000000000009',
OVM_ExecutionManagerWrapper: '0x420000000000000000000000000000000000000B',
OVM_GasPriceOracle: '0x420000000000000000000000000000000000000F', OVM_GasPriceOracle: '0x420000000000000000000000000000000000000F',
OVM_SequencerFeeVault: '0x4200000000000000000000000000000000000011',
OVM_L2StandardBridge: '0x4200000000000000000000000000000000000010', OVM_L2StandardBridge: '0x4200000000000000000000000000000000000010',
ERC1820Registry: '0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24', OVM_SequencerFeeVault: '0x4200000000000000000000000000000000000011',
} }
...@@ -99,45 +99,17 @@ export const makeStateDump = async (cfg: RollupDeployConfig): Promise<any> => { ...@@ -99,45 +99,17 @@ export const makeStateDump = async (cfg: RollupDeployConfig): Promise<any> => {
let config: RollupDeployConfig = { let config: RollupDeployConfig = {
deploymentSigner: signer, deploymentSigner: signer,
ovmGasMeteringConfig: {
minTransactionGasLimit: 0,
maxTransactionGasLimit: 11_000_000,
maxGasPerQueuePerEpoch: 1_000_000_000_000,
secondsPerEpoch: 0,
},
ovmGlobalContext: {
ovmCHAINID: 420,
L2CrossDomainMessengerAddress: predeploys.OVM_L2CrossDomainMessenger,
},
transactionChainConfig: {
sequencer: signer,
forceInclusionPeriodSeconds: 600,
forceInclusionPeriodBlocks: 600 / 12,
},
stateChainConfig: {
fraudProofWindowSeconds: 600,
sequencerPublishWindowSeconds: 60_000,
},
whitelistConfig: { whitelistConfig: {
owner: signer, owner: signer,
allowArbitraryContractDeployment: true, allowArbitraryContractDeployment: true,
}, },
l1CrossDomainMessengerConfig: {},
dependencies: [ dependencies: [
'ERC1820Registry',
'Lib_AddressManager', 'Lib_AddressManager',
'OVM_DeployerWhitelist', 'OVM_DeployerWhitelist',
'OVM_L1MessageSender', 'OVM_L1MessageSender',
'OVM_L2ToL1MessagePasser', 'OVM_L2ToL1MessagePasser',
'OVM_ProxyEOA',
'OVM_ECDSAContractAccount',
'OVM_SequencerEntrypoint',
'OVM_L2CrossDomainMessenger', 'OVM_L2CrossDomainMessenger',
'OVM_SafetyChecker',
'OVM_ExecutionManager',
'OVM_StateManager',
'OVM_ETH', 'OVM_ETH',
'OVM_ExecutionManagerWrapper',
'OVM_GasPriceOracle', 'OVM_GasPriceOracle',
'OVM_SequencerFeeVault', 'OVM_SequencerFeeVault',
'OVM_L2StandardBridge', 'OVM_L2StandardBridge',
...@@ -206,13 +178,5 @@ export const makeStateDump = async (cfg: RollupDeployConfig): Promise<any> => { ...@@ -206,13 +178,5 @@ export const makeStateDump = async (cfg: RollupDeployConfig): Promise<any> => {
) )
} }
dump.accounts['OVM_GasMetadata'] = {
address: '0x06a506a506a506a506a506a506a506a506a506a5',
code: '0x00',
codeHash: keccak256('0x00'),
storage: {},
abi: [],
}
return dump return dump
} }
...@@ -6,11 +6,6 @@ import * as types from 'hardhat/internal/core/params/argumentTypes' ...@@ -6,11 +6,6 @@ import * as types from 'hardhat/internal/core/params/argumentTypes'
const DEFAULT_L1_BLOCK_TIME_SECONDS = 15 const DEFAULT_L1_BLOCK_TIME_SECONDS = 15
const DEFAULT_CTC_FORCE_INCLUSION_PERIOD_SECONDS = 60 * 60 * 24 * 30 // 30 days const DEFAULT_CTC_FORCE_INCLUSION_PERIOD_SECONDS = 60 * 60 * 24 * 30 // 30 days
const DEFAULT_CTC_MAX_TRANSACTION_GAS_LIMIT = 11_000_000 const DEFAULT_CTC_MAX_TRANSACTION_GAS_LIMIT = 11_000_000
const DEFAULT_EM_MIN_TRANSACTION_GAS_LIMIT = 50_000
const DEFAULT_EM_MAX_TRANSACTION_GAS_LIMIT = 11_000_000
const DEFAULT_EM_MAX_GAS_PER_QUEUE_PER_EPOCH = 250_000_000
const DEFAULT_EM_SECONDS_PER_EPOCH = 0
const DEFAULT_EM_OVM_CHAIN_ID = 420
const DEFAULT_SCC_FRAUD_PROOF_WINDOW = 60 * 60 * 24 * 7 // 7 days const DEFAULT_SCC_FRAUD_PROOF_WINDOW = 60 * 60 * 24 * 7 // 7 days
const DEFAULT_SCC_SEQUENCER_PUBLISH_WINDOW = 60 * 30 // 30 minutes const DEFAULT_SCC_SEQUENCER_PUBLISH_WINDOW = 60 * 30 // 30 minutes
...@@ -33,36 +28,6 @@ task('deploy') ...@@ -33,36 +28,6 @@ task('deploy')
DEFAULT_CTC_MAX_TRANSACTION_GAS_LIMIT, DEFAULT_CTC_MAX_TRANSACTION_GAS_LIMIT,
types.int types.int
) )
.addOptionalParam(
'emMinTransactionGasLimit',
'Minimum allowed transaction gas limit.',
DEFAULT_EM_MIN_TRANSACTION_GAS_LIMIT,
types.int
)
.addOptionalParam(
'emMaxTransactionGasLimit',
'Maximum allowed transaction gas limit.',
DEFAULT_EM_MAX_TRANSACTION_GAS_LIMIT,
types.int
)
.addOptionalParam(
'emMaxGasPerQueuePerEpoch',
'Maximum gas allowed in a given queue for each epoch.',
DEFAULT_EM_MAX_GAS_PER_QUEUE_PER_EPOCH,
types.int
)
.addOptionalParam(
'emSecondsPerEpoch',
'Number of seconds in each epoch.',
DEFAULT_EM_SECONDS_PER_EPOCH,
types.int
)
.addOptionalParam(
'emOvmChainId',
'Chain ID for the L2 network.',
DEFAULT_EM_OVM_CHAIN_ID,
types.int
)
.addOptionalParam( .addOptionalParam(
'sccFraudProofWindow', 'sccFraudProofWindow',
'Number of seconds until a transaction is considered finalized.', 'Number of seconds until a transaction is considered finalized.',
...@@ -87,12 +52,6 @@ task('deploy') ...@@ -87,12 +52,6 @@ task('deploy')
undefined, undefined,
types.string types.string
) )
.addOptionalParam(
'ovmRelayerAddress',
'Address of the message relayer. Must be provided or this deployment will fail.',
undefined,
types.string
)
.addOptionalParam( .addOptionalParam(
'ovmAddressManagerOwner', 'ovmAddressManagerOwner',
'Address that will own the Lib_AddressManager. Must be provided or this deployment will fail.', 'Address that will own the Lib_AddressManager. Must be provided or this deployment will fail.',
...@@ -116,7 +75,6 @@ task('deploy') ...@@ -116,7 +75,6 @@ task('deploy')
validateAddressArg('ovmSequencerAddress') validateAddressArg('ovmSequencerAddress')
validateAddressArg('ovmProposerAddress') validateAddressArg('ovmProposerAddress')
validateAddressArg('ovmRelayerAddress')
validateAddressArg('ovmAddressManagerOwner') validateAddressArg('ovmAddressManagerOwner')
args.ctcForceInclusionPeriodBlocks = Math.floor( args.ctcForceInclusionPeriodBlocks = Math.floor(
......
...@@ -11,9 +11,6 @@ describe('connectL1Contracts', () => { ...@@ -11,9 +11,6 @@ describe('connectL1Contracts', () => {
const l1ContractNames = [ const l1ContractNames = [
'addressManager', 'addressManager',
'canonicalTransactionChain', 'canonicalTransactionChain',
'executionManager',
'fraudVerifier',
'multiMessageRelayer',
'stateCommitmentChain', 'stateCommitmentChain',
'xDomainMessengerProxy', 'xDomainMessengerProxy',
'bondManager', 'bondManager',
...@@ -25,9 +22,6 @@ describe('connectL1Contracts', () => { ...@@ -25,9 +22,6 @@ describe('connectL1Contracts', () => {
'messagePasser', 'messagePasser',
'messageSender', 'messageSender',
'deployerWhiteList', 'deployerWhiteList',
'ecdsaContractAccount',
'sequencerEntrypoint',
'erc1820Registry',
'addressManager', 'addressManager',
] ]
......
import { expect } from '../../../setup'
/* External Imports */
import { ethers, waffle } from 'hardhat'
import { ContractFactory, Contract, Wallet, BigNumber, utils } from 'ethers'
import { MockContract, smockit } from '@eth-optimism/smock'
import { toPlainObject } from 'lodash'
/* Internal Imports */
import { LibEIP155TxStruct, DEFAULT_EIP155_TX } from '../../../helpers'
import { predeploys } from '../../../../src'
describe('OVM_ECDSAContractAccount', () => {
let wallet: Wallet
before(async () => {
const provider = waffle.provider
;[wallet] = provider.getWallets()
})
let Mock__OVM_ExecutionManager: MockContract
let Mock__OVM_ETH: MockContract
before(async () => {
Mock__OVM_ExecutionManager = await smockit('OVM_ExecutionManager', {
address: predeploys.OVM_ExecutionManagerWrapper,
})
Mock__OVM_ETH = await smockit('OVM_ETH', {
address: predeploys.OVM_ETH,
})
})
let Factory__OVM_ECDSAContractAccount: ContractFactory
before(async () => {
Factory__OVM_ECDSAContractAccount = await ethers.getContractFactory(
'OVM_ECDSAContractAccount'
)
})
let OVM_ECDSAContractAccount: Contract
beforeEach(async () => {
OVM_ECDSAContractAccount = await Factory__OVM_ECDSAContractAccount.deploy()
})
beforeEach(async () => {
Mock__OVM_ExecutionManager.smocked.ovmCHAINID.will.return.with(420)
Mock__OVM_ExecutionManager.smocked.ovmGETNONCE.will.return.with(100)
Mock__OVM_ExecutionManager.smocked.ovmADDRESS.will.return.with(
await wallet.getAddress()
)
Mock__OVM_ETH.smocked.transfer.will.return.with(true)
})
describe('fallback', async () => {
it('should successfully accept value sent to it', async () => {
await expect(
wallet.sendTransaction({
to: OVM_ECDSAContractAccount.address,
value: 1,
})
).to.not.be.reverted
})
})
describe('execute()', () => {
it(`should successfully execute an EIP155Transaction`, async () => {
const transaction = DEFAULT_EIP155_TX
const encodedTransaction = await wallet.signTransaction(transaction)
await OVM_ECDSAContractAccount.execute(
LibEIP155TxStruct(encodedTransaction)
)
})
it(`should ovmCREATE if EIP155Transaction.to is zero address`, async () => {
const transaction = { ...DEFAULT_EIP155_TX, to: '' }
const encodedTransaction = await wallet.signTransaction(transaction)
await OVM_ECDSAContractAccount.execute(
LibEIP155TxStruct(encodedTransaction)
)
const ovmCREATE: any =
Mock__OVM_ExecutionManager.smocked.ovmCREATE.calls[0]
expect(ovmCREATE._bytecode).to.equal(transaction.data)
})
it(`should revert on invalid signature`, async () => {
const transaction = DEFAULT_EIP155_TX
const encodedTransaction = ethers.utils.serializeTransaction(
transaction,
'0x' + '00'.repeat(65)
)
await expect(
OVM_ECDSAContractAccount.execute(LibEIP155TxStruct(encodedTransaction))
).to.be.revertedWith(
'Signature provided for EOA transaction execution is invalid.'
)
})
it(`should revert on incorrect nonce`, async () => {
const transaction = { ...DEFAULT_EIP155_TX, nonce: 99 }
const encodedTransaction = await wallet.signTransaction(transaction)
await expect(
OVM_ECDSAContractAccount.execute(LibEIP155TxStruct(encodedTransaction))
).to.be.revertedWith(
'Transaction nonce does not match the expected nonce.'
)
})
it(`should revert on incorrect chainId`, async () => {
const transaction = { ...DEFAULT_EIP155_TX, chainId: 421 }
const encodedTransaction = await wallet.signTransaction(transaction)
await expect(
OVM_ECDSAContractAccount.execute(LibEIP155TxStruct(encodedTransaction))
).to.be.revertedWith('Transaction signed with wrong chain ID')
})
// TEMPORARY: Skip gas checks for mainnet.
it.skip(`should revert on insufficient gas`, async () => {
const transaction = { ...DEFAULT_EIP155_TX, gasLimit: 200000000 }
const encodedTransaction = await wallet.signTransaction(transaction)
const tx = LibEIP155TxStruct(encodedTransaction)
await expect(
OVM_ECDSAContractAccount.execute(tx, {
gasLimit: 40000000,
})
).to.be.revertedWith('Gas is not sufficient to execute the transaction.')
})
it(`should revert if fee is not transferred to the relayer`, async () => {
const transaction = DEFAULT_EIP155_TX
const encodedTransaction = await wallet.signTransaction(transaction)
Mock__OVM_ETH.smocked.transfer.will.return.with(false)
const tx = LibEIP155TxStruct(encodedTransaction)
await expect(OVM_ECDSAContractAccount.execute(tx)).to.be.revertedWith(
'Fee was not transferred to relayer.'
)
})
it(`should transfer value if value is greater than 0`, async () => {
const value = 100
const valueRecipient = '0x' + '34'.repeat(20)
const transaction = {
...DEFAULT_EIP155_TX,
to: valueRecipient,
value,
data: '0x',
}
const encodedTransaction = await wallet.signTransaction(transaction)
// fund the contract account
await wallet.sendTransaction({
to: OVM_ECDSAContractAccount.address,
value: value * 10,
gasLimit: 1_000_000,
})
const receipientBalanceBefore = await wallet.provider.getBalance(
valueRecipient
)
await OVM_ECDSAContractAccount.execute(
LibEIP155TxStruct(encodedTransaction)
)
const recipientBalanceAfter = await wallet.provider.getBalance(
valueRecipient
)
expect(
recipientBalanceAfter.sub(receipientBalanceBefore).toNumber()
).to.eq(value)
})
it(`should revert if trying to send value with a contract creation`, async () => {
const transaction = { ...DEFAULT_EIP155_TX, value: 1234, to: '' }
const encodedTransaction = await wallet.signTransaction(transaction)
await expect(
OVM_ECDSAContractAccount.execute(LibEIP155TxStruct(encodedTransaction))
).to.be.revertedWith('Value transfer in contract creation not supported.')
})
// NOTE: Upgrades are disabled for now but will be re-enabled at a later point in time. See
// comment in OVM_ECDSAContractAccount.sol for additional information.
it(`should revert if trying call itself`, async () => {
const transaction = {
...DEFAULT_EIP155_TX,
to: wallet.address,
}
const encodedTransaction = await wallet.signTransaction(transaction)
await expect(
OVM_ECDSAContractAccount.execute(LibEIP155TxStruct(encodedTransaction))
).to.be.revertedWith(
'Calls to self are disabled until upgradability is re-enabled.'
)
})
})
describe('isValidSignature()', () => {
const message = '0x42'
const messageHash = ethers.utils.hashMessage(message)
it(`should revert for a malformed signature`, async () => {
await expect(
OVM_ECDSAContractAccount.isValidSignature(messageHash, '0xdeadbeef')
).to.be.revertedWith('ECDSA: invalid signature length')
})
it(`should return 0 for an invalid signature`, async () => {
const signature = await wallet.signMessage(message)
const bytes = await OVM_ECDSAContractAccount.isValidSignature(
messageHash,
signature
)
expect(bytes).to.equal('0x00000000')
})
// NOTE: There is no good way to unit test verifying a valid signature
// An integration test exists testing this instead
})
})
import { expect } from '../../../setup'
/* External Imports */
import { ethers, waffle } from 'hardhat'
import { ContractFactory, Contract, Signer, Wallet } from 'ethers'
import { MockContract, smockit } from '@eth-optimism/smock'
import { toPlainObject } from 'lodash'
/* Internal Imports */
import { predeploys } from '../../../../src'
import { DEFAULT_EIP155_TX, LibEIP155TxStruct } from '../../../helpers'
describe('OVM_ProxyEOA', () => {
let signer: Signer
let wallet: Wallet
before(async () => {
;[signer] = await ethers.getSigners()
const provider = waffle.provider
;[wallet] = provider.getWallets()
})
let Mock__OVM_ExecutionManager: MockContract
let Mock__OVM_ECDSAContractAccount: MockContract
before(async () => {
Mock__OVM_ExecutionManager = await smockit('OVM_ExecutionManager', {
address: predeploys.OVM_ExecutionManagerWrapper,
})
Mock__OVM_ECDSAContractAccount = await smockit('OVM_ECDSAContractAccount', {
address: predeploys.OVM_ECDSAContractAccount,
})
})
let Factory__OVM_ProxyEOA: ContractFactory
before(async () => {
Factory__OVM_ProxyEOA = await ethers.getContractFactory('OVM_ProxyEOA')
})
let OVM_ProxyEOA: Contract
beforeEach(async () => {
OVM_ProxyEOA = await Factory__OVM_ProxyEOA.deploy()
})
describe('getImplementation()', () => {
it(`should be created with implementation at predeploy address`, async () => {
expect(await OVM_ProxyEOA.getImplementation()).to.equal(
predeploys.OVM_ECDSAContractAccount
)
})
})
describe('upgrade()', () => {
it(`should upgrade the proxy implementation`, async () => {
const newImpl = `0x${'81'.repeat(20)}`
Mock__OVM_ExecutionManager.smocked.ovmADDRESS.will.return.with(
await signer.getAddress()
)
await expect(OVM_ProxyEOA.upgrade(newImpl)).to.not.be.reverted
expect(await OVM_ProxyEOA.getImplementation()).to.equal(newImpl)
})
it(`should not allow upgrade of the proxy implementation by another account`, async () => {
const newImpl = `0x${'81'.repeat(20)}`
Mock__OVM_ExecutionManager.smocked.ovmADDRESS.will.return.with(
ethers.constants.AddressZero
)
await expect(OVM_ProxyEOA.upgrade(newImpl)).to.be.revertedWith(
'EOAs can only upgrade their own EOA implementation'
)
})
})
describe('fallback()', () => {
it(`should call delegateCall with right calldata`, async () => {
const transaction = { ...DEFAULT_EIP155_TX }
const encodedTransaction = await wallet.signTransaction(transaction)
const data = Mock__OVM_ECDSAContractAccount.interface.encodeFunctionData(
'execute',
[LibEIP155TxStruct(encodedTransaction)]
)
await signer.sendTransaction({
to: OVM_ProxyEOA.address,
data,
})
const call = toPlainObject(
Mock__OVM_ECDSAContractAccount.smocked.execute.calls[0]
)
const _transaction = call._transaction
expect(_transaction[0]).to.deep.equal(transaction.nonce)
expect(_transaction.nonce).to.deep.equal(transaction.nonce)
expect(_transaction.gasPrice).to.deep.equal(transaction.gasPrice)
expect(_transaction.gasLimit).to.deep.equal(transaction.gasLimit)
expect(_transaction.to).to.deep.equal(transaction.to)
expect(_transaction.data).to.deep.equal(transaction.data)
expect(_transaction.isCreate).to.deep.equal(false)
})
it.skip(`should return data from fallback`, async () => {
// TODO: test return data from fallback
})
it.skip(`should revert in fallback`, async () => {
// TODO: test reversion from fallback
})
})
})
...@@ -65,7 +65,10 @@ describe('OVM_L1CrossDomainMessenger', () => { ...@@ -65,7 +65,10 @@ describe('OVM_L1CrossDomainMessenger', () => {
await ethers.getContractFactory('Helper_SimpleProxy') await ethers.getContractFactory('Helper_SimpleProxy')
) )
Mock__OVM_L2CrossDomainMessenger = await smockit( Mock__OVM_L2CrossDomainMessenger = await smockit(
await ethers.getContractFactory('OVM_L2CrossDomainMessenger') await ethers.getContractFactory('OVM_L2CrossDomainMessenger'),
{
address: predeploys.OVM_L2CrossDomainMessenger,
}
) )
Mock__OVM_StateCommitmentChain = await smockit( Mock__OVM_StateCommitmentChain = await smockit(
await ethers.getContractFactory('OVM_StateCommitmentChain') await ethers.getContractFactory('OVM_StateCommitmentChain')
......
import { expect } from '../../../../setup'
/* External Imports */
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers'
import { smockit, MockContract } from '@eth-optimism/smock'
import { toHexString } from '@eth-optimism/core-utils'
/* Internal Imports */
import {
makeAddressManager,
NON_ZERO_ADDRESS,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS,
DUMMY_BATCH_PROOFS,
} from '../../../../helpers'
describe('OVM_L1MultiMessageRelayer', () => {
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let AddressManager: Contract
let Factory__OVM_L1MultiMessageRelayer: ContractFactory
let Mock__OVM_L1CrossDomainMessenger: MockContract
let messages: any[]
before(async () => {
// We do all the 'reusable setup' in here, ie. creating factories, mocks and setting addresses
// for everything but the contract under test
AddressManager = await makeAddressManager()
// create a mock for the L1CrossDomainMessenger implementation
Mock__OVM_L1CrossDomainMessenger = await smockit(
await ethers.getContractFactory('OVM_L1CrossDomainMessenger')
)
// set the address of the mock contract to target
await AddressManager.setAddress(
// 'Proxy__OVM_L1CrossDomainMessenger' is the string used by the contract under test to lookup
// the target contract. On mainnet the target is a proxy which points to the implementation of
// the L1CrossDomainMessenger.
// In order to keep the tests simple, we skip the proxy here, and point directly to the impl.
'Proxy__OVM_L1CrossDomainMessenger',
Mock__OVM_L1CrossDomainMessenger.address
)
// set the signer as the address required by access control
await AddressManager.setAddress(
'OVM_L2BatchMessageRelayer',
signer.getAddress()
)
// define a dummy proof to satisfy the abi
const dummyProof = {
stateRoot: NON_NULL_BYTES32,
stateRootBatchHeader: DUMMY_BATCH_HEADERS[0],
stateRootProof: DUMMY_BATCH_PROOFS[0],
stateTrieWitness: toHexString('some bytes'),
storageTrieWitness: toHexString('some more bytes'),
}
// create a few dummy messages to relay
const m1 = {
target: '0x1100000000000000000000000000000000000000',
message: NON_NULL_BYTES32,
sender: '0x2200000000000000000000000000000000000000',
messageNonce: 1,
proof: dummyProof,
}
const m2 = {
target: '0x1100000000000000000000000000000000000000',
message: NON_NULL_BYTES32,
sender: '0x2200000000000000000000000000000000000000',
messageNonce: 2,
proof: dummyProof,
}
const m3 = {
target: '0x1100000000000000000000000000000000000000',
message: NON_NULL_BYTES32,
sender: '0x2200000000000000000000000000000000000000',
messageNonce: 2,
proof: dummyProof,
}
messages = [m1, m2, m3]
})
let OVM_L1MultiMessageRelayer: Contract
beforeEach(async () => {
// setup a factory and deploy a new test-contract for each unit test
Factory__OVM_L1MultiMessageRelayer = await ethers.getContractFactory(
'OVM_L1MultiMessageRelayer'
)
OVM_L1MultiMessageRelayer = await Factory__OVM_L1MultiMessageRelayer.deploy(
AddressManager.address
)
// set the address of the OVM_L1MultiMessageRelayer, which the OVM_L1CrossDomainMessenger will
// check in its onlyRelayer modifier.
// The string currently used in the AddressManager is 'OVM_L2MessageRelayer'
await AddressManager.setAddress(
'OVM_L2MessageRelayer',
OVM_L1MultiMessageRelayer.address
)
// set the mock return value
Mock__OVM_L1CrossDomainMessenger.smocked.relayMessage.will.return()
})
describe('batchRelayMessages', () => {
it('Successfully relay multiple messages', async () => {
await OVM_L1MultiMessageRelayer.batchRelayMessages(messages)
await expect(
Mock__OVM_L1CrossDomainMessenger.smocked.relayMessage.calls.length
).to.deep.equal(messages.length)
})
it('should revert if called by the wrong account', async () => {
// set the wrong address to use for ACL
await AddressManager.setAddress(
'OVM_L2BatchMessageRelayer',
NON_ZERO_ADDRESS
)
await expect(
OVM_L1MultiMessageRelayer.batchRelayMessages(messages)
).to.be.revertedWith(
'OVM_L1MultiMessageRelayer: Function can only be called by the OVM_L2BatchMessageRelayer'
)
})
})
})
...@@ -20,8 +20,6 @@ import { ...@@ -20,8 +20,6 @@ import {
getNextBlockNumber, getNextBlockNumber,
} from '../../../helpers' } from '../../../helpers'
import { predeploys } from '../../../../src'
// Still have some duplication from OVM_CanonicalTransactionChain.spec.ts, but it's so minimal that // Still have some duplication from OVM_CanonicalTransactionChain.spec.ts, but it's so minimal that
// this is probably cleaner for now. Particularly since we're planning to move all of this out into // this is probably cleaner for now. Particularly since we're planning to move all of this out into
// core-utils soon anyway. // core-utils soon anyway.
...@@ -46,7 +44,6 @@ describe('[GAS BENCHMARK] OVM_CanonicalTransactionChain', () => { ...@@ -46,7 +44,6 @@ describe('[GAS BENCHMARK] OVM_CanonicalTransactionChain', () => {
}) })
let AddressManager: Contract let AddressManager: Contract
let Mock__OVM_ExecutionManager: MockContract
let Mock__OVM_StateCommitmentChain: MockContract let Mock__OVM_StateCommitmentChain: MockContract
before(async () => { before(async () => {
AddressManager = await makeAddressManager() AddressManager = await makeAddressManager()
...@@ -54,34 +51,16 @@ describe('[GAS BENCHMARK] OVM_CanonicalTransactionChain', () => { ...@@ -54,34 +51,16 @@ describe('[GAS BENCHMARK] OVM_CanonicalTransactionChain', () => {
'OVM_Sequencer', 'OVM_Sequencer',
await sequencer.getAddress() await sequencer.getAddress()
) )
await AddressManager.setAddress(
'OVM_DecompressionPrecompileAddress',
predeploys.OVM_SequencerEntrypoint
)
Mock__OVM_ExecutionManager = await smockit(
await ethers.getContractFactory('OVM_ExecutionManager')
)
Mock__OVM_StateCommitmentChain = await smockit( Mock__OVM_StateCommitmentChain = await smockit(
await ethers.getContractFactory('OVM_StateCommitmentChain') await ethers.getContractFactory('OVM_StateCommitmentChain')
) )
await setProxyTarget(
AddressManager,
'OVM_ExecutionManager',
Mock__OVM_ExecutionManager
)
await setProxyTarget( await setProxyTarget(
AddressManager, AddressManager,
'OVM_StateCommitmentChain', 'OVM_StateCommitmentChain',
Mock__OVM_StateCommitmentChain Mock__OVM_StateCommitmentChain
) )
Mock__OVM_ExecutionManager.smocked.getMaxTransactionGasLimit.will.return.with(
MAX_GAS_LIMIT
)
}) })
let Factory__OVM_CanonicalTransactionChain: ContractFactory let Factory__OVM_CanonicalTransactionChain: ContractFactory
......
...@@ -95,7 +95,6 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -95,7 +95,6 @@ describe('OVM_CanonicalTransactionChain', () => {
}) })
let AddressManager: Contract let AddressManager: Contract
let Mock__OVM_ExecutionManager: MockContract
let Mock__OVM_StateCommitmentChain: MockContract let Mock__OVM_StateCommitmentChain: MockContract
before(async () => { before(async () => {
AddressManager = await makeAddressManager() AddressManager = await makeAddressManager()
...@@ -103,34 +102,16 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -103,34 +102,16 @@ describe('OVM_CanonicalTransactionChain', () => {
'OVM_Sequencer', 'OVM_Sequencer',
await sequencer.getAddress() await sequencer.getAddress()
) )
await AddressManager.setAddress(
'OVM_DecompressionPrecompileAddress',
predeploys.OVM_SequencerEntrypoint
)
Mock__OVM_ExecutionManager = await smockit(
await ethers.getContractFactory('OVM_ExecutionManager')
)
Mock__OVM_StateCommitmentChain = await smockit( Mock__OVM_StateCommitmentChain = await smockit(
await ethers.getContractFactory('OVM_StateCommitmentChain') await ethers.getContractFactory('OVM_StateCommitmentChain')
) )
await setProxyTarget(
AddressManager,
'OVM_ExecutionManager',
Mock__OVM_ExecutionManager
)
await setProxyTarget( await setProxyTarget(
AddressManager, AddressManager,
'OVM_StateCommitmentChain', 'OVM_StateCommitmentChain',
Mock__OVM_StateCommitmentChain Mock__OVM_StateCommitmentChain
) )
Mock__OVM_ExecutionManager.smocked.getMaxTransactionGasLimit.will.return.with(
MAX_GAS_LIMIT
)
}) })
let Factory__OVM_CanonicalTransactionChain: ContractFactory let Factory__OVM_CanonicalTransactionChain: ContractFactory
...@@ -608,7 +589,7 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -608,7 +589,7 @@ describe('OVM_CanonicalTransactionChain', () => {
}) })
it('should successfully verify against a valid sequencer transaction', async () => { it('should successfully verify against a valid sequencer transaction', async () => {
const entrypoint = predeploys.OVM_SequencerEntrypoint const entrypoint = ethers.constants.AddressZero
const gasLimit = MAX_GAS_LIMIT const gasLimit = MAX_GAS_LIMIT
const data = '0x' + '12'.repeat(1234) const data = '0x' + '12'.repeat(1234)
const timestamp = (await getEthTime(ethers.provider)) - 10 const timestamp = (await getEthTime(ethers.provider)) - 10
......
import { expect } from '../../../setup'
import { deployContractCode } from '../../../helpers/utils'
/* External Imports */
import { ethers } from 'hardhat'
import { Contract, ContractFactory, Signer } from 'ethers'
import { smockit, MockContract } from '@eth-optimism/smock'
/* Internal Imports */
import {
makeAddressManager,
NON_ZERO_ADDRESS,
NON_NULL_BYTES32,
GasMeasurement,
} from '../../../helpers'
const DUMMY_GASMETERCONFIG = {
minTransactionGasLimit: 0,
maxTransactionGasLimit: NON_NULL_BYTES32,
maxGasPerQueuePerEpoch: NON_NULL_BYTES32,
secondsPerEpoch: NON_NULL_BYTES32,
}
const DUMMY_GLOBALCONTEXT = {
ovmCHAINID: 420,
}
const QUEUE_ORIGIN = {
SEQUENCER_QUEUE: 0,
L1TOL2_QUEUE: 1,
}
const DUMMY_TRANSACTION = {
timestamp: 111111111111,
blockNumber: 20,
l1QueueOrigin: QUEUE_ORIGIN.SEQUENCER_QUEUE,
l1TxOrigin: NON_ZERO_ADDRESS,
entrypoint: NON_ZERO_ADDRESS, // update this below
gasLimit: 10_000_000,
data: 0,
}
describe('OVM_ExecutionManager gas consumption', () => {
let wallet: Signer
before(async () => {
;[wallet] = await ethers.getSigners()
})
let Factory__OVM_ExecutionManager: ContractFactory
let MOCK__STATE_MANAGER: MockContract
let AddressManager: Contract
let targetContractAddress: string
let gasMeasurement: GasMeasurement
before(async () => {
Factory__OVM_ExecutionManager = await ethers.getContractFactory(
'OVM_ExecutionManager'
)
// Deploy a simple contract that just returns successfully with no data
targetContractAddress = await deployContractCode(
'60206001f3',
wallet,
10_000_000
)
DUMMY_TRANSACTION.entrypoint = targetContractAddress
AddressManager = await makeAddressManager()
// deploy the state manager and mock it for the state transitioner
MOCK__STATE_MANAGER = await smockit(
await (
await ethers.getContractFactory('OVM_StateManager')
).deploy(NON_ZERO_ADDRESS)
)
// Setup the SM to satisfy all the checks executed during EM.run()
MOCK__STATE_MANAGER.smocked.isAuthenticated.will.return.with(true)
MOCK__STATE_MANAGER.smocked.getAccountEthAddress.will.return.with(
targetContractAddress
)
MOCK__STATE_MANAGER.smocked.hasAccount.will.return.with(true)
MOCK__STATE_MANAGER.smocked.testAndSetAccountLoaded.will.return.with(true)
await AddressManager.setAddress(
'OVM_StateManagerFactory',
MOCK__STATE_MANAGER.address
)
gasMeasurement = new GasMeasurement()
await gasMeasurement.init(wallet)
})
let OVM_ExecutionManager: Contract
beforeEach(async () => {
OVM_ExecutionManager = (
await Factory__OVM_ExecutionManager.deploy(
AddressManager.address,
DUMMY_GASMETERCONFIG,
DUMMY_GLOBALCONTEXT
)
).connect(wallet)
})
describe('Measure cost of a very simple contract [ @skip-on-coverage ]', async () => {
it('Gas cost of run', async () => {
const gasCost = await gasMeasurement.getGasCost(
OVM_ExecutionManager,
'run',
[DUMMY_TRANSACTION, MOCK__STATE_MANAGER.address]
)
console.log(`calculated gas cost of ${gasCost}`)
const benchmark: number = 110_000
expect(gasCost).to.be.lte(benchmark)
expect(gasCost).to.be.gte(
benchmark - 1_000,
'Gas cost has significantly decreased, consider updating the benchmark to reflect the change'
)
})
})
})
/* Internal Imports */
import {
ExecutionManagerTestRunner,
TestDefinition,
NON_NULL_BYTES32,
OVM_TX_GAS_LIMIT,
} from '../../../../helpers'
const globalContext = {
ovmCHAINID: 420,
}
const transactionContext = {
ovmTIMESTAMP: 12341234,
ovmNUMBER: 13371337,
ovmGASLIMIT: 45674567,
ovmTXGASLIMIT: 78907890,
ovmL1QUEUEORIGIN: 1,
ovmL1TXORIGIN: '0x1234123412341234123412341234123412341234',
}
const messageContext = {
ovmCALLER: '0x6789678967896789678967896789678967896789',
ovmADDRESS: '0x4567456745674567456745674567456745674567',
}
const test_contextOpcodes: TestDefinition = {
name: 'unit tests for basic getter opcodes',
preState: {
ExecutionManager: {
ovmStateManager: '$OVM_STATE_MANAGER',
ovmSafetyChecker: '$OVM_SAFETY_CHECKER',
messageRecord: {
nuisanceGasLeft: OVM_TX_GAS_LIMIT,
},
globalContext,
transactionContext,
messageContext,
},
StateManager: {
owner: '$OVM_EXECUTION_MANAGER',
accounts: {
$DUMMY_OVM_ADDRESS_1: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
},
},
},
parameters: [
// TODO: re-enable when we can unwrap tests' ovmCALL
// {
// name: 'gets ovmCALLER',
// steps: [
// {
// functionName: 'ovmCALLER',
// expectedReturnValue: messageContext.ovmCALLER
// }
// ],
// },
// {
// name: 'gets ovmADDRESS',
// steps: [
// {
// functionName: 'ovmADDRESS',
// expectedReturnValue: messageContext.ovmADDRESS
// }
// ],
// },
{
name: 'gets ovmTIMESTAMP',
steps: [
{
functionName: 'ovmTIMESTAMP',
expectedReturnValue: transactionContext.ovmTIMESTAMP,
},
],
},
{
name: 'gets ovmNUMBER',
steps: [
{
functionName: 'ovmNUMBER',
expectedReturnValue: transactionContext.ovmNUMBER,
},
],
},
{
name: 'gets ovmGASLIMIT',
steps: [
{
functionName: 'ovmGASLIMIT',
expectedReturnValue: transactionContext.ovmGASLIMIT,
},
],
},
{
name: 'gets ovmL1QUEUEORIGIN',
steps: [
{
functionName: 'ovmL1QUEUEORIGIN',
expectedReturnValue: transactionContext.ovmL1QUEUEORIGIN,
},
],
},
{
name: 'gets ovmL1TXORIGIN',
steps: [
{
functionName: 'ovmL1TXORIGIN',
expectedReturnValue: transactionContext.ovmL1TXORIGIN,
},
],
},
{
name: 'gets ovmCHAINID',
steps: [
{
functionName: 'ovmCHAINID',
expectedReturnValue: globalContext.ovmCHAINID,
},
],
},
],
}
const runner = new ExecutionManagerTestRunner()
runner.run(test_contextOpcodes)
/* Internal Imports */
import { constants } from 'ethers'
import {
ExecutionManagerTestRunner,
TestDefinition,
OVM_TX_GAS_LIMIT,
NON_NULL_BYTES32,
VERIFIED_EMPTY_CONTRACT_HASH,
NUISANCE_GAS_COSTS,
Helper_TestRunner_BYTELEN,
} from '../../../../helpers'
const CREATED_CONTRACT_1 = '0x2bda4a99d5be88609d23b1e4ab5d1d34fb1c2feb'
const FRESH_CALL_NUISANCE_GAS_COST =
Helper_TestRunner_BYTELEN *
NUISANCE_GAS_COSTS.NUISANCE_GAS_PER_CONTRACT_BYTE +
NUISANCE_GAS_COSTS.MIN_NUISANCE_GAS_PER_CONTRACT
const test_nuisanceGas: TestDefinition = {
name: 'Basic tests for nuisance gas',
preState: {
ExecutionManager: {
ovmStateManager: '$OVM_STATE_MANAGER',
ovmSafetyChecker: '$OVM_SAFETY_CHECKER',
messageRecord: {
nuisanceGasLeft: OVM_TX_GAS_LIMIT,
},
},
StateManager: {
owner: '$OVM_EXECUTION_MANAGER',
accounts: {
$DUMMY_OVM_ADDRESS_1: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_2: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_3: {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20),
},
[CREATED_CONTRACT_1]: {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20),
},
},
},
},
subTests: [
{
name: 'ovmCALL consumes nuisance gas of CODESIZE * NUISANCE_GAS_PER_CONTRACT_BYTE',
postState: {
ExecutionManager: {
messageRecord: {
nuisanceGasLeft: OVM_TX_GAS_LIMIT - FRESH_CALL_NUISANCE_GAS_COST,
},
},
},
parameters: [
{
name: 'single ovmCALL',
steps: [
// do a non-nuisance-gas-consuming opcode (test runner auto-wraps in ovmCALL)
{
functionName: 'ovmADDRESS',
expectedReturnValue: '$DUMMY_OVM_ADDRESS_1',
},
],
},
{
name: 'nested ovmCALL, same address',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [],
},
expectedReturnStatus: true,
},
],
},
],
},
{
name: 'ovmCALL consumes nuisance gas of CODESIZE * NUISANCE_GAS_PER_CONTRACT_BYTE twice for two unique ovmCALLS',
postState: {
ExecutionManager: {
messageRecord: {
nuisanceGasLeft:
OVM_TX_GAS_LIMIT - 2 * FRESH_CALL_NUISANCE_GAS_COST,
},
},
},
parameters: [
{
name: 'directly nested ovmCALL',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [],
},
expectedReturnStatus: true,
},
],
},
{
name: 'with a call to previously called contract too',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
],
},
{
name: 'ovmCALL consumes all allotted nuisance gas if code contract throws unknown exception',
postState: {
ExecutionManager: {
messageRecord: {
nuisanceGasLeft:
OVM_TX_GAS_LIMIT -
FRESH_CALL_NUISANCE_GAS_COST -
OVM_TX_GAS_LIMIT / 2,
},
},
},
parameters: [
{
name: 'give 1/2 gas to evmINVALID',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'evmINVALID',
},
],
},
expectedReturnStatus: true,
expectedReturnValue: {
ovmSuccess: false,
returnData: '0x',
},
},
],
},
],
},
{
name: 'ovmCREATE consumes all allotted nuisance gas if creation code throws data-less exception',
parameters: [
{
name: 'give 1/2 gas to ovmCALL => ovmCREATE, evmINVALID',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
target: '$DUMMY_OVM_ADDRESS_1',
gasLimit: OVM_TX_GAS_LIMIT / 2,
subSteps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'evmINVALID',
},
],
},
expectedReturnStatus: true,
expectedReturnValue: constants.AddressZero,
},
],
},
expectedReturnStatus: true,
},
],
},
],
},
],
}
const runner = new ExecutionManagerTestRunner()
runner.run(test_nuisanceGas)
/* External Imports */
import { fromHexString } from '@eth-optimism/core-utils'
/* Internal Imports */
import {
ExecutionManagerTestRunner,
TestDefinition,
OVM_TX_GAS_LIMIT,
NON_NULL_BYTES32,
VERIFIED_EMPTY_CONTRACT_HASH,
} from '../../../../helpers'
import { getContractDefinition } from '../../../../../src'
const test_ovmCREATEEOA: TestDefinition = {
name: 'Basic tests for CREATEEOA',
preState: {
ExecutionManager: {
ovmStateManager: '$OVM_STATE_MANAGER',
ovmSafetyChecker: '$OVM_SAFETY_CHECKER',
messageRecord: {
nuisanceGasLeft: OVM_TX_GAS_LIMIT,
},
},
StateManager: {
owner: '$OVM_EXECUTION_MANAGER',
accounts: {
$DUMMY_OVM_ADDRESS_1: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_2: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
'0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff': {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20),
},
},
verifiedContractStorage: {
$DUMMY_OVM_ADDRESS_1: {
[NON_NULL_BYTES32]: true,
},
},
},
},
parameters: [
{
name: 'ovmCREATEEOA, ovmEXTCODESIZE(CREATED)',
steps: [
{
functionName: 'ovmCREATEEOA',
functionParams: {
_messageHash:
'0x92d658d25f963af824e9d4bd533c165773d4a694a67d88135d119d5bca97c001',
_v: 1,
_r: '0x73757c671fae2c3fb6825766c724b7715720bda4b309d3612f2c623364556967',
_s: '0x2fc9b7222783390b9f10e22e92a52871beaff2613193d6e2dbf18d0e2d2eb8ff',
},
expectedReturnStatus: true,
expectedReturnValue: undefined,
},
{
functionName: 'ovmGETNONCE',
expectedReturnValue: 0,
},
{
functionName: 'ovmEXTCODESIZE',
functionParams: {
address: '0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff',
},
expectedReturnStatus: true,
expectedReturnValue: fromHexString(
getContractDefinition('OVM_ProxyEOA').deployedBytecode
).length,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => ovmGETNONCE',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmGETNONCE',
expectedReturnValue: 0,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => ovmINCREMENTNONCEx3 => ovmGETNONCE',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmINCREMENTNONCE',
expectedReturnStatus: true,
},
{
functionName: 'ovmINCREMENTNONCE',
expectedReturnStatus: true,
},
{
functionName: 'ovmINCREMENTNONCE',
expectedReturnStatus: true,
},
{
functionName: 'ovmGETNONCE',
expectedReturnValue: 3,
},
],
},
expectedReturnStatus: true,
},
],
},
],
}
const runner = new ExecutionManagerTestRunner()
runner.run(test_ovmCREATEEOA)
/* Internal Imports */
import {
ExecutionManagerTestRunner,
TestDefinition,
OVM_TX_GAS_LIMIT,
NON_NULL_BYTES32,
DUMMY_BYTECODE,
VERIFIED_EMPTY_CONTRACT_HASH,
} from '../../../../helpers'
const CREATED_CONTRACT_1 = '0x2bda4a99d5be88609d23b1e4ab5d1d34fb1c2feb'
const CREATED_CONTRACT_2 = '0xe0d8be8101f36ebe6b01abacec884422c39a1f62'
const test_ovmDELEGATECALL: TestDefinition = {
name: 'Basic tests for ovmDELEGATECALL',
preState: {
ExecutionManager: {
ovmStateManager: '$OVM_STATE_MANAGER',
ovmSafetyChecker: '$OVM_SAFETY_CHECKER',
messageRecord: {
nuisanceGasLeft: OVM_TX_GAS_LIMIT,
},
},
StateManager: {
owner: '$OVM_EXECUTION_MANAGER',
accounts: {
$DUMMY_OVM_ADDRESS_1: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_2: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_3: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_4: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
[CREATED_CONTRACT_1]: {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20),
},
[CREATED_CONTRACT_2]: {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20),
},
},
verifiedContractStorage: {
$DUMMY_OVM_ADDRESS_1: {
[NON_NULL_BYTES32]: true,
},
},
},
},
parameters: [
{
name: 'ovmCALL(ADDRESS_1) => ovmDELEGATECALL(ADDRESS_2) => ovmADDRESS',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmADDRESS',
expectedReturnValue: '$DUMMY_OVM_ADDRESS_1',
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => ovmCALL(ADDRESS_2) => ovmDELEGATECALL(ADDRESS_3) => ovmCALLER',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmCALLER',
expectedReturnValue: '$DUMMY_OVM_ADDRESS_1',
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => (ovmDELEGATECALL(ADDRESS_2) => ovmSSTORE) + ovmSLOAD',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmSSTORE',
functionParams: {
key: NON_NULL_BYTES32,
value: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
{
functionName: 'ovmSLOAD',
functionParams: {
key: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
expectedReturnValue: NON_NULL_BYTES32,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => (ovmDELEGATECALL(ADDRESS_2) => ovmCREATE)',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmCREATE',
functionParams: {
bytecode: DUMMY_BYTECODE,
},
expectedReturnStatus: true,
expectedReturnValue: CREATED_CONTRACT_1,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => ovmCALL(ADDRESS_2) => ovmDELEGATECALL(ADDRESS_3) => ovmDELEGATECALL(ADDRESS_4) => ovmCALLER',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_3',
subSteps: [
{
functionName: 'ovmCALLER',
expectedReturnValue: '$DUMMY_OVM_ADDRESS_1',
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => ovmCALL(ADDRESS_2) => ovmDELEGATECALL(ADDRESS_3) => ovmDELEGATECALL(ADDRESS_4) => ovmADDRESS',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_3',
subSteps: [
{
functionName: 'ovmADDRESS',
expectedReturnValue: '$DUMMY_OVM_ADDRESS_2',
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => ovmCALL(ADDRESS_2) => ovmDELEGATECALL(ADDRESS_3) => ovmDELEGATECALL(ADDRESS_4) => ovmCREATE',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_3',
subSteps: [
{
functionName: 'ovmCREATE',
functionParams: {
bytecode: DUMMY_BYTECODE,
},
expectedReturnStatus: true,
expectedReturnValue: CREATED_CONTRACT_2,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
],
}
const runner = new ExecutionManagerTestRunner()
runner.run(test_ovmDELEGATECALL)
/* Internal Imports */
import {
ExecutionManagerTestRunner,
TestDefinition,
OVM_TX_GAS_LIMIT,
NON_NULL_BYTES32,
REVERT_FLAGS,
} from '../../../../helpers'
const test_ovmREVERT: TestDefinition = {
name: 'Basic tests for ovmREVERT',
preState: {
ExecutionManager: {
ovmStateManager: '$OVM_STATE_MANAGER',
ovmSafetyChecker: '$OVM_SAFETY_CHECKER',
messageRecord: {
nuisanceGasLeft: OVM_TX_GAS_LIMIT,
},
},
StateManager: {
owner: '$OVM_EXECUTION_MANAGER',
accounts: {
$DUMMY_OVM_ADDRESS_1: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
},
},
},
parameters: [
{
name: 'ovmCALL => ovmREVERT',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmREVERT',
revertData: '0xdeadbeef',
expectedReturnStatus: false,
expectedReturnValue: {
flag: REVERT_FLAGS.INTENTIONAL_REVERT,
data: '0xdeadbeef',
nuisanceGasLeft: OVM_TX_GAS_LIMIT / 2,
ovmGasRefund: 0,
},
},
],
},
expectedReturnStatus: false,
expectedReturnValue: '0xdeadbeef',
},
],
},
// TODO: fix this. only way to do it is manually set up and call ovmREVERT directly inside a context which mirrors that during creation.
// {
// name: "ovmREVERT inside ovmCREATE ?",
// parameters: [
// {
// steps: [
// {
// functionName: "ovmCALL",
// functionParams: [
// GAS_LIMIT / 2,
// "$DUMMY_OVM_ADDRESS_1",
// [
// {
// functionName: "ovmCREATE",
// functionParams: [
// USELESS_BYTECODE,
// false, // "create will be successful?"
// [
// {
// functionName: "ovmREVERT",
// functionParams: [ "0xdeadbeef" ],
// expectedReturnStatus: false,
// expectedReturnValues: [ "0x00" ] // no return values for reversion in constructor
// },
// // TODO: check internally flagged storage here
// ]
// ],
// expectedReturnStatus: true,
// expectedReturnValues: [ CREATED_CONTRACT_1 ]
// }
// ],
// ],
// expectedReturnStatus: true,
// expectedReturnValues: []
// }
// ]
// }
// ]
// }
],
}
const runner = new ExecutionManagerTestRunner()
runner.run(test_ovmREVERT)
/* External Imports */
import { ethers } from 'ethers'
/* Internal Imports */
import {
ExecutionManagerTestRunner,
TestDefinition,
OVM_TX_GAS_LIMIT,
NON_NULL_BYTES32,
getStorageXOR,
} from '../../../../helpers'
const test_ovmSLOAD: TestDefinition = {
name: 'Basic tests for ovmSLOAD',
preState: {
ExecutionManager: {
ovmStateManager: '$OVM_STATE_MANAGER',
ovmSafetyChecker: '$OVM_SAFETY_CHECKER',
messageRecord: {
nuisanceGasLeft: OVM_TX_GAS_LIMIT,
},
},
StateManager: {
owner: '$OVM_EXECUTION_MANAGER',
accounts: {
$DUMMY_OVM_ADDRESS_1: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_2: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
},
contractStorage: {
$DUMMY_OVM_ADDRESS_1: {
[NON_NULL_BYTES32]: getStorageXOR(ethers.constants.HashZero),
},
},
verifiedContractStorage: {
$DUMMY_OVM_ADDRESS_1: {
[NON_NULL_BYTES32]: true,
},
},
},
},
parameters: [
{
name: 'ovmCALL => ovmSLOAD',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmSLOAD',
functionParams: {
key: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
expectedReturnValue: ethers.constants.HashZero,
},
],
},
expectedReturnStatus: true,
},
],
},
],
}
const runner = new ExecutionManagerTestRunner()
runner.run(test_ovmSLOAD)
/* External Imports */
import { ethers } from 'ethers'
/* Internal Imports */
import {
ExecutionManagerTestRunner,
TestDefinition,
OVM_TX_GAS_LIMIT,
NON_NULL_BYTES32,
REVERT_FLAGS,
DUMMY_BYTECODE,
getStorageXOR,
} from '../../../../helpers'
const test_ovmSTATICCALL: TestDefinition = {
name: 'Basic tests for ovmSTATICCALL',
preState: {
ExecutionManager: {
ovmStateManager: '$OVM_STATE_MANAGER',
ovmSafetyChecker: '$OVM_SAFETY_CHECKER',
messageRecord: {
nuisanceGasLeft: OVM_TX_GAS_LIMIT,
},
},
StateManager: {
owner: '$OVM_EXECUTION_MANAGER',
accounts: {
$DUMMY_OVM_ADDRESS_1: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_2: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_3: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
},
contractStorage: {
$DUMMY_OVM_ADDRESS_1: {
[NON_NULL_BYTES32]: getStorageXOR(ethers.constants.HashZero),
},
$DUMMY_OVM_ADDRESS_3: {
[NON_NULL_BYTES32]: getStorageXOR(ethers.constants.HashZero),
},
},
verifiedContractStorage: {
$DUMMY_OVM_ADDRESS_1: {
[NON_NULL_BYTES32]: true,
},
$DUMMY_OVM_ADDRESS_3: {
[NON_NULL_BYTES32]: true,
},
},
},
},
parameters: [
{
name: 'ovmSTATICCALL => ovmSSTORE',
steps: [
{
functionName: 'ovmSTATICCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmSSTORE',
functionParams: {
key: ethers.constants.HashZero,
value: ethers.constants.HashZero,
},
expectedReturnStatus: false,
expectedReturnValue: {
flag: REVERT_FLAGS.STATIC_VIOLATION,
nuisanceGasLeft: OVM_TX_GAS_LIMIT / 2,
},
},
],
},
expectedReturnStatus: false,
},
],
},
{
name: 'ovmSTATICCALL => ovmSLOAD',
steps: [
{
functionName: 'ovmSTATICCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmSLOAD',
functionParams: {
key: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
expectedReturnValue: ethers.constants.HashZero,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmSTATICCALL => ovmCREATE',
steps: [
{
functionName: 'ovmSTATICCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmCREATE',
functionParams: {
bytecode: DUMMY_BYTECODE,
},
expectedReturnStatus: false,
expectedReturnValue: {
flag: REVERT_FLAGS.STATIC_VIOLATION,
nuisanceGasLeft: OVM_TX_GAS_LIMIT / 2,
},
},
],
},
expectedReturnStatus: false,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => ovmSTATICCALL(ADDRESS_2) => ovmCALLER',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmSTATICCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmCALLER',
expectedReturnValue: '$DUMMY_OVM_ADDRESS_1',
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => ovmSTATICCALL(ADDRESS_2) => ovmADDRESS',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmSTATICCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmADDRESS',
expectedReturnValue: '$DUMMY_OVM_ADDRESS_2',
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => ovmSTATICCALL(ADDRESS_2) => ovmCALL(ADDRESS_3) => ovmSSTORE',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmSTATICCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: '$DUMMY_OVM_ADDRESS_3',
subSteps: [
{
functionName: 'ovmSSTORE',
functionParams: {
key: ethers.constants.HashZero,
value: ethers.constants.HashZero,
},
expectedReturnStatus: false,
expectedReturnValue: {
flag: REVERT_FLAGS.STATIC_VIOLATION,
nuisanceGasLeft: OVM_TX_GAS_LIMIT / 2,
},
},
],
},
expectedReturnStatus: false,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => ovmSTATICCALL(ADDRESS_2) => ovmCALL(ADDRESS_3) => ovmSLOAD',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmSTATICCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: '$DUMMY_OVM_ADDRESS_3',
subSteps: [
{
functionName: 'ovmSLOAD',
functionParams: {
key: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
expectedReturnValue: ethers.constants.HashZero,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => ovmSTATICCALL(ADDRESS_2) => ovmCALL(ADDRESS_3) => ovmCREATE',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmSTATICCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: '$DUMMY_OVM_ADDRESS_3',
subSteps: [
{
functionName: 'ovmCREATE',
functionParams: {
bytecode: DUMMY_BYTECODE,
},
expectedReturnStatus: false,
expectedReturnValue: {
flag: REVERT_FLAGS.STATIC_VIOLATION,
nuisanceGasLeft: OVM_TX_GAS_LIMIT / 2,
},
},
],
},
expectedReturnStatus: false,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
],
}
const runner = new ExecutionManagerTestRunner()
runner.run(test_ovmSTATICCALL)
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from 'hardhat'
import { Contract } from 'ethers'
/* Internal Imports */
import { SAFETY_CHECKER_TEST_JSON } from '../../../data'
describe('OVM_SafetyChecker', () => {
let OVM_SafetyChecker: Contract
before(async () => {
const Factory__OVM_SafetyChecker = await ethers.getContractFactory(
'OVM_SafetyChecker'
)
OVM_SafetyChecker = await Factory__OVM_SafetyChecker.deploy()
})
describe('isBytecodeSafe()', () => {
for (const testName of Object.keys(SAFETY_CHECKER_TEST_JSON)) {
const test = SAFETY_CHECKER_TEST_JSON[testName]
it(`should correctly classify: ${testName}`, async () => {
expect(await OVM_SafetyChecker.isBytecodeSafe(test.in)).to.equal(
test.out
)
})
}
})
})
import '../../../setup'
/* External Imports */
import { ethers } from 'hardhat'
import { constants, Contract, ContractFactory, Signer } from 'ethers'
import _ from 'lodash'
/* Internal Imports */
import {
DUMMY_ACCOUNTS,
DUMMY_BYTES32,
EMPTY_ACCOUNT_CODE_HASH,
NON_ZERO_ADDRESS,
NON_NULL_BYTES32,
STORAGE_XOR_VALUE,
GasMeasurement,
} from '../../../helpers'
const DUMMY_ACCOUNT = DUMMY_ACCOUNTS[0]
const DUMMY_KEY = DUMMY_BYTES32[0]
const DUMMY_VALUE_1 = DUMMY_BYTES32[1]
const DUMMY_VALUE_2 = DUMMY_BYTES32[2]
describe('OVM_StateManager gas consumption [ @skip-on-coverage ]', () => {
let owner: Signer
before(async () => {
;[owner] = await ethers.getSigners()
})
let Factory__OVM_StateManager: ContractFactory
let gasMeasurement: GasMeasurement
before(async () => {
Factory__OVM_StateManager = await ethers.getContractFactory(
'OVM_StateManager'
)
gasMeasurement = new GasMeasurement()
await gasMeasurement.init(owner)
})
let OVM_StateManager: Contract
beforeEach(async () => {
OVM_StateManager = (
await Factory__OVM_StateManager.deploy(await owner.getAddress())
).connect(owner)
await OVM_StateManager.setExecutionManager(
gasMeasurement.GasMeasurementContract.address
)
})
const measure = (
methodName: string,
methodArgs: Array<any> = [],
doFirst: () => Promise<any> = async () => {
return
}
) => {
it('measured consumption!', async () => {
await doFirst()
const gasCost = await gasMeasurement.getGasCost(
OVM_StateManager,
methodName,
methodArgs
)
console.log(` calculated gas cost of ${gasCost}`)
})
}
const setupFreshAccount = async () => {
await OVM_StateManager.putAccount(DUMMY_ACCOUNT.address, {
...DUMMY_ACCOUNT.data,
isFresh: true,
})
}
const setupNonFreshAccount = async () => {
await OVM_StateManager.putAccount(DUMMY_ACCOUNT.address, DUMMY_ACCOUNT.data)
}
const putSlot = async (value: string) => {
await OVM_StateManager.putContractStorage(
DUMMY_ACCOUNT.address,
DUMMY_KEY,
value
)
}
describe('ItemState testAndSetters', () => {
describe('testAndSetAccountLoaded', () => {
describe('when account ItemState is ITEM_UNTOUCHED', () => {
measure('testAndSetAccountLoaded', [NON_ZERO_ADDRESS])
})
describe('when account ItemState is ITEM_LOADED', () => {
measure('testAndSetAccountLoaded', [NON_ZERO_ADDRESS], async () => {
await OVM_StateManager.testAndSetAccountLoaded(NON_ZERO_ADDRESS)
})
})
describe('when account ItemState is ITEM_CHANGED', () => {
measure('testAndSetAccountLoaded', [NON_ZERO_ADDRESS], async () => {
await OVM_StateManager.testAndSetAccountChanged(NON_ZERO_ADDRESS)
})
})
})
describe('testAndSetAccountChanged', () => {
describe('when account ItemState is ITEM_UNTOUCHED', () => {
measure('testAndSetAccountChanged', [NON_ZERO_ADDRESS])
})
describe('when account ItemState is ITEM_LOADED', () => {
measure('testAndSetAccountChanged', [NON_ZERO_ADDRESS], async () => {
await OVM_StateManager.testAndSetAccountLoaded(NON_ZERO_ADDRESS)
})
})
describe('when account ItemState is ITEM_CHANGED', () => {
measure('testAndSetAccountChanged', [NON_ZERO_ADDRESS], async () => {
await OVM_StateManager.testAndSetAccountChanged(NON_ZERO_ADDRESS)
})
})
})
describe('testAndSetContractStorageLoaded', () => {
describe('when storage ItemState is ITEM_UNTOUCHED', () => {
measure('testAndSetContractStorageLoaded', [
NON_ZERO_ADDRESS,
DUMMY_KEY,
])
})
describe('when storage ItemState is ITEM_LOADED', () => {
measure(
'testAndSetContractStorageLoaded',
[NON_ZERO_ADDRESS, DUMMY_KEY],
async () => {
await OVM_StateManager.testAndSetContractStorageLoaded(
NON_ZERO_ADDRESS,
DUMMY_KEY
)
}
)
})
describe('when storage ItemState is ITEM_CHANGED', () => {
measure(
'testAndSetContractStorageLoaded',
[NON_ZERO_ADDRESS, DUMMY_KEY],
async () => {
await OVM_StateManager.testAndSetContractStorageChanged(
NON_ZERO_ADDRESS,
DUMMY_KEY
)
}
)
})
})
describe('testAndSetContractStorageChanged', () => {
describe('when storage ItemState is ITEM_UNTOUCHED', () => {
measure('testAndSetContractStorageChanged', [
NON_ZERO_ADDRESS,
DUMMY_KEY,
])
})
describe('when storage ItemState is ITEM_LOADED', () => {
measure(
'testAndSetContractStorageChanged',
[NON_ZERO_ADDRESS, DUMMY_KEY],
async () => {
await OVM_StateManager.testAndSetContractStorageLoaded(
NON_ZERO_ADDRESS,
DUMMY_KEY
)
}
)
})
describe('when storage ItemState is ITEM_CHANGED', () => {
measure(
'testAndSetContractStorageChanged',
[NON_ZERO_ADDRESS, DUMMY_KEY],
async () => {
await OVM_StateManager.testAndSetContractStorageChanged(
NON_ZERO_ADDRESS,
DUMMY_KEY
)
}
)
})
})
})
describe('incrementTotalUncommittedAccounts', () => {
describe('when totalUncommittedAccounts is 0', () => {
measure('incrementTotalUncommittedAccounts')
})
describe('when totalUncommittedAccounts is nonzero', () => {
const doFirst = async () => {
await OVM_StateManager.incrementTotalUncommittedAccounts()
}
measure('incrementTotalUncommittedAccounts', [], doFirst)
})
})
describe('incrementTotalUncommittedContractStorage', () => {
describe('when totalUncommittedContractStorage is 0', () => {
measure('incrementTotalUncommittedContractStorage')
})
describe('when totalUncommittedContractStorage is nonzero', () => {
const doFirst = async () => {
await OVM_StateManager.incrementTotalUncommittedContractStorage()
}
measure('incrementTotalUncommittedContractStorage', [], doFirst)
})
})
describe('hasAccount', () => {
describe('when it does have the account', () => {
const doFirst = async () => {
await OVM_StateManager.putAccount(
DUMMY_ACCOUNT.address,
DUMMY_ACCOUNT.data
)
}
measure('hasAccount', [DUMMY_ACCOUNT.address], doFirst)
})
})
describe('hasEmptyAccount', () => {
describe('when it does have an empty account', () => {
measure('hasEmptyAccount', [DUMMY_ACCOUNT.address], async () => {
await OVM_StateManager.putAccount(DUMMY_ACCOUNT.address, {
...DUMMY_ACCOUNT.data,
codeHash: EMPTY_ACCOUNT_CODE_HASH,
})
})
})
describe('when it has an account which is not emtpy', () => {
measure('hasEmptyAccount', [DUMMY_ACCOUNT.address], async () => {
await OVM_StateManager.putAccount(
DUMMY_ACCOUNT.address,
DUMMY_ACCOUNT.data
)
})
})
})
describe('setAccountNonce', () => {
describe('when the nonce is 0 and set to 0', () => {
measure('setAccountNonce', [DUMMY_ACCOUNT.address, 0], async () => {
await OVM_StateManager.putAccount(DUMMY_ACCOUNT.address, {
...DUMMY_ACCOUNT.data,
nonce: 0,
})
})
})
describe('when the nonce is 0 and set to nonzero', () => {
measure('setAccountNonce', [DUMMY_ACCOUNT.address, 1], async () => {
await OVM_StateManager.putAccount(DUMMY_ACCOUNT.address, {
...DUMMY_ACCOUNT.data,
nonce: 0,
})
})
})
describe('when the nonce is nonzero and set to 0', () => {
measure('setAccountNonce', [DUMMY_ACCOUNT.address, 0], async () => {
await OVM_StateManager.putAccount(DUMMY_ACCOUNT.address, {
...DUMMY_ACCOUNT.data,
nonce: 1,
})
})
})
describe('when the nonce is nonzero and set to nonzero', () => {
measure('setAccountNonce', [DUMMY_ACCOUNT.address, 2], async () => {
await OVM_StateManager.putAccount(DUMMY_ACCOUNT.address, {
...DUMMY_ACCOUNT.data,
nonce: 1,
})
})
})
})
describe('getAccountNonce', () => {
describe('when the nonce is 0', () => {
measure('getAccountNonce', [DUMMY_ACCOUNT.address])
})
describe('when the nonce is nonzero', () => {
measure('getAccountNonce', [DUMMY_ACCOUNT.address], async () => {
await OVM_StateManager.putAccount(DUMMY_ACCOUNT.address, {
...DUMMY_ACCOUNT.data,
nonce: 1,
})
})
})
})
describe('getAccountEthAddress', () => {
describe('when the ethAddress is a random address', () => {
measure('getAccountEthAddress', [DUMMY_ACCOUNT.address], async () => {
await OVM_StateManager.putAccount(DUMMY_ACCOUNT.address, {
...DUMMY_ACCOUNT.data,
ethAddress: NON_ZERO_ADDRESS,
})
})
})
describe('when the ethAddress is zero', () => {
measure('getAccountEthAddress', [DUMMY_ACCOUNT.address], async () => {
await OVM_StateManager.putAccount(DUMMY_ACCOUNT.address, {
...DUMMY_ACCOUNT.data,
ethAddress: constants.AddressZero,
})
})
})
})
describe('initPendingAccount', () => {
// note: this method should only be accessibl if _hasEmptyAccount is true, so it should always be empty nonce etc
measure('initPendingAccount', [NON_ZERO_ADDRESS])
})
describe('commitPendingAccount', () => {
// this should only set ethAddress and codeHash from ZERO to NONZERO, so one case should be sufficient
measure(
'commitPendingAccount',
[NON_ZERO_ADDRESS, NON_ZERO_ADDRESS, NON_NULL_BYTES32],
async () => {
await OVM_StateManager.initPendingAccount(NON_ZERO_ADDRESS)
}
)
})
describe('getContractStorage', () => {
// confirm with kelvin that this covers all cases
describe('when the account isFresh', () => {
describe('when the storage slot value has not been set', () => {
measure(
'getContractStorage',
[DUMMY_ACCOUNT.address, DUMMY_KEY],
setupFreshAccount
)
})
describe('when the storage slot has already been set', () => {
describe('when the storage slot value is STORAGE_XOR_VALUE', () => {
measure(
'getContractStorage',
[DUMMY_ACCOUNT.address, DUMMY_KEY],
async () => {
await setupFreshAccount()
await putSlot(STORAGE_XOR_VALUE)
}
)
})
describe('when the storage slot value is something other than STORAGE_XOR_VALUE', () => {
measure(
'getContractStorage',
[DUMMY_ACCOUNT.address, DUMMY_KEY],
async () => {
await setupFreshAccount()
await putSlot(DUMMY_VALUE_1)
}
)
})
})
})
describe('when the account is not fresh', () => {
describe('when the storage slot value is STORAGE_XOR_VALUE', () => {
measure(
'getContractStorage',
[DUMMY_ACCOUNT.address, DUMMY_KEY],
async () => {
await setupNonFreshAccount()
await putSlot(STORAGE_XOR_VALUE)
}
)
})
describe('when the storage slot value is something other than STORAGE_XOR_VALUE', () => {
measure(
'getContractStorage',
[DUMMY_ACCOUNT.address, DUMMY_KEY],
async () => {
await setupNonFreshAccount()
await putSlot(DUMMY_VALUE_1)
}
)
})
})
})
describe('putContractStorage', () => {
const relevantValues = [DUMMY_VALUE_1, DUMMY_VALUE_2, STORAGE_XOR_VALUE]
for (const preValue of relevantValues) {
for (const postValue of relevantValues) {
describe(`when overwriting ${preValue} with ${postValue}`, () => {
measure(
'putContractStorage',
[NON_ZERO_ADDRESS, DUMMY_KEY, postValue],
async () => {
await OVM_StateManager.putContractStorage(
NON_ZERO_ADDRESS,
DUMMY_KEY,
preValue
)
}
)
})
}
}
})
describe('hasContractStorage', () => {
describe('when the account is fresh', () => {
describe('when the storage slot has not been set', () => {
measure(
'hasContractStorage',
[DUMMY_ACCOUNT.address, DUMMY_KEY],
setupFreshAccount
)
})
describe('when the slot has already been set', () => {
measure(
'hasContractStorage',
[DUMMY_ACCOUNT.address, DUMMY_KEY],
async () => {
await setupFreshAccount()
await OVM_StateManager.putContractStorage(
DUMMY_ACCOUNT.address,
DUMMY_KEY,
DUMMY_VALUE_1
)
}
)
})
})
describe('when the account is not fresh', () => {
measure(
'hasContractStorage',
[DUMMY_ACCOUNT.address, DUMMY_KEY],
async () => {
await OVM_StateManager.putContractStorage(
DUMMY_ACCOUNT.address,
DUMMY_KEY,
DUMMY_VALUE_1
)
}
)
})
})
})
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from 'hardhat'
import { Contract, ContractFactory, Signer, BigNumber, constants } from 'ethers'
import _ from 'lodash'
/* Internal Imports */
import {
DUMMY_ACCOUNTS,
DUMMY_BYTES32,
EMPTY_ACCOUNT_CODE_HASH,
KECCAK_256_NULL,
} from '../../../helpers'
describe('OVM_StateManager', () => {
let signer1: Signer
let signer2: Signer
let signer3: Signer
before(async () => {
;[signer1, signer2, signer3] = await ethers.getSigners()
})
let Factory__OVM_StateManager: ContractFactory
before(async () => {
Factory__OVM_StateManager = await ethers.getContractFactory(
'OVM_StateManager'
)
})
let OVM_StateManager: Contract
beforeEach(async () => {
OVM_StateManager = (
await Factory__OVM_StateManager.deploy(await signer1.getAddress())
).connect(signer1)
})
describe('setExecutionManager', () => {
describe('when called by the current owner', () => {
beforeEach(async () => {
OVM_StateManager = OVM_StateManager.connect(signer1)
})
it('should change the current OVM_ExecutionManager', async () => {
await expect(
OVM_StateManager.connect(signer1).setExecutionManager(
await signer2.getAddress()
)
).to.not.be.reverted
expect(await OVM_StateManager.ovmExecutionManager()).to.equal(
await signer2.getAddress()
)
})
})
describe('when called by the current OVM_ExecutionManager', () => {
beforeEach(async () => {
await OVM_StateManager.connect(signer1).setExecutionManager(
await signer2.getAddress()
)
})
it('should change the current OVM_ExecutionManager', async () => {
await expect(
OVM_StateManager.connect(signer2).setExecutionManager(
await signer3.getAddress()
)
).to.not.be.reverted
expect(await OVM_StateManager.ovmExecutionManager()).to.equal(
await signer3.getAddress()
)
})
})
describe('when called by any other account', () => {
beforeEach(async () => {
OVM_StateManager = OVM_StateManager.connect(signer1)
})
it('should revert', async () => {
await expect(
OVM_StateManager.connect(signer3).setExecutionManager(
await signer3.getAddress()
)
).to.be.revertedWith(
'Function can only be called by authenticated addresses'
)
})
})
})
describe('putAccount', () => {
it('should be able to store an OVM account', async () => {
await expect(
OVM_StateManager.putAccount(
DUMMY_ACCOUNTS[0].address,
DUMMY_ACCOUNTS[0].data
)
).to.not.be.reverted
})
it('should be able to overwrite an OVM account', async () => {
await OVM_StateManager.putAccount(
DUMMY_ACCOUNTS[0].address,
DUMMY_ACCOUNTS[0].data
)
await expect(
OVM_StateManager.putAccount(
DUMMY_ACCOUNTS[0].address,
DUMMY_ACCOUNTS[1].data
)
).to.not.be.reverted
})
})
describe('getAccount', () => {
it('should be able to retrieve an OVM account', async () => {
await OVM_StateManager.putAccount(
DUMMY_ACCOUNTS[0].address,
DUMMY_ACCOUNTS[0].data
)
expect(
_.toPlainObject(
await OVM_StateManager.callStatic.getAccount(
DUMMY_ACCOUNTS[0].address
)
)
).to.deep.include(DUMMY_ACCOUNTS[0].data)
})
it('should be able to retrieve an overwritten OVM account', async () => {
await OVM_StateManager.putAccount(
DUMMY_ACCOUNTS[0].address,
DUMMY_ACCOUNTS[0].data
)
await OVM_StateManager.putAccount(
DUMMY_ACCOUNTS[0].address,
DUMMY_ACCOUNTS[1].data
)
expect(
_.toPlainObject(
await OVM_StateManager.callStatic.getAccount(
DUMMY_ACCOUNTS[0].address
)
)
).to.deep.include(DUMMY_ACCOUNTS[1].data)
})
})
describe('hasAccount', () => {
describe('when the account exists', () => {
beforeEach(async () => {
await OVM_StateManager.putAccount(
DUMMY_ACCOUNTS[0].address,
DUMMY_ACCOUNTS[0].data
)
})
it('should return true', async () => {
expect(
await OVM_StateManager.callStatic.hasAccount(
DUMMY_ACCOUNTS[0].address
)
).to.equal(true)
})
})
describe('when the account does not exist', () => {
it('should return false', async () => {
expect(
await OVM_StateManager.callStatic.hasAccount(
DUMMY_ACCOUNTS[0].address
)
).to.equal(false)
})
})
})
describe('hasEmptyAccount', () => {
describe('when the account has the EMPTY_ACCOUNT_CODE_HASH', () => {
beforeEach(async () => {
await OVM_StateManager.putAccount(DUMMY_ACCOUNTS[0].address, {
...DUMMY_ACCOUNTS[0].data,
nonce: 0,
codeHash: EMPTY_ACCOUNT_CODE_HASH,
})
})
it('should return true', async () => {
expect(
await OVM_StateManager.callStatic.hasEmptyAccount(
DUMMY_ACCOUNTS[0].address
)
).to.equal(true)
})
})
describe('when the account has a different non-zero codehash', () => {
beforeEach(async () => {
await OVM_StateManager.putAccount(
DUMMY_ACCOUNTS[0].address,
DUMMY_ACCOUNTS[0].data
)
})
it('should return false', async () => {
expect(
await OVM_StateManager.callStatic.hasEmptyAccount(
DUMMY_ACCOUNTS[0].address
)
).to.equal(false)
})
})
describe('when the account does not exist', () => {
it('should return false', async () => {
expect(
await OVM_StateManager.callStatic.hasEmptyAccount(
DUMMY_ACCOUNTS[0].address
)
).to.equal(false)
})
})
})
describe('setAccountNonce', () => {
it('should change the account nonce', async () => {
await expect(
OVM_StateManager.setAccountNonce(DUMMY_ACCOUNTS[0].address, 1234)
).to.not.be.reverted
})
})
describe('getAccountNonce', () => {
describe('when the account exists', () => {
beforeEach(async () => {
await OVM_StateManager.putAccount(
DUMMY_ACCOUNTS[0].address,
DUMMY_ACCOUNTS[0].data
)
})
it('should return the current nonce', async () => {
expect(
await OVM_StateManager.callStatic.getAccountNonce(
DUMMY_ACCOUNTS[0].address
)
).to.equal(DUMMY_ACCOUNTS[0].data.nonce)
})
describe('when the nonce has been modified', () => {
beforeEach(async () => {
await OVM_StateManager.setAccountNonce(
DUMMY_ACCOUNTS[0].address,
1234
)
})
it('should return the updated nonce', async () => {
expect(
await OVM_StateManager.callStatic.getAccountNonce(
DUMMY_ACCOUNTS[0].address
)
).to.equal(1234)
})
})
})
describe('when the account does not exist', () => {
it('should return zero', async () => {
expect(
await OVM_StateManager.callStatic.getAccountNonce(
DUMMY_ACCOUNTS[0].address
)
).to.equal(0)
})
})
})
describe('getAccountEthAddress', () => {
describe('when the account exists', () => {
beforeEach(async () => {
await OVM_StateManager.putAccount(
DUMMY_ACCOUNTS[0].address,
DUMMY_ACCOUNTS[0].data
)
})
it('should return the account eth address', async () => {
expect(
await OVM_StateManager.getAccountEthAddress(DUMMY_ACCOUNTS[0].address)
).to.equal(DUMMY_ACCOUNTS[0].data.ethAddress)
})
})
describe('when the account does not exist', () => {
it('should return the zero address', async () => {
expect(
await OVM_StateManager.getAccountEthAddress(DUMMY_ACCOUNTS[0].address)
).to.equal(constants.AddressZero)
})
})
})
describe('initPendingAccount', () => {
it('should set the initial account values', async () => {
await expect(
OVM_StateManager.initPendingAccount(DUMMY_ACCOUNTS[0].address)
).to.not.be.reverted
expect(
_.toPlainObject(
await OVM_StateManager.callStatic.getAccount(
DUMMY_ACCOUNTS[0].address
)
)
).to.deep.include({
nonce: BigNumber.from(1),
codeHash: KECCAK_256_NULL,
isFresh: true,
})
})
})
describe('commitPendingAccount', () => {
it('should set the remaining account values', async () => {
await expect(
OVM_StateManager.commitPendingAccount(
DUMMY_ACCOUNTS[0].address,
DUMMY_ACCOUNTS[0].data.ethAddress,
DUMMY_ACCOUNTS[0].data.codeHash
)
).to.not.be.reverted
expect(
_.toPlainObject(
await OVM_StateManager.callStatic.getAccount(
DUMMY_ACCOUNTS[0].address
)
)
).to.deep.include({
ethAddress: DUMMY_ACCOUNTS[0].data.ethAddress,
codeHash: DUMMY_ACCOUNTS[0].data.codeHash,
})
})
})
describe('testAndSetAccountChanged', () => {
describe('when the account has not yet been changed', () => {
it('should return false', async () => {
expect(
await OVM_StateManager.callStatic.testAndSetAccountChanged(
DUMMY_ACCOUNTS[0].address
)
).to.equal(false)
})
})
describe('when the account has been changed', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetAccountChanged(
DUMMY_ACCOUNTS[0].address
)
})
it('should return true', async () => {
expect(
await OVM_StateManager.callStatic.testAndSetAccountChanged(
DUMMY_ACCOUNTS[0].address
)
).to.equal(true)
})
})
})
describe('testAndSetAccountLoaded', () => {
describe('when the account has not yet been loaded', () => {
it('should return false', async () => {
expect(
await OVM_StateManager.callStatic.testAndSetAccountLoaded(
DUMMY_ACCOUNTS[0].address
)
).to.equal(false)
})
})
describe('when the account has been loaded', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetAccountLoaded(
DUMMY_ACCOUNTS[0].address
)
})
it('should return true', async () => {
expect(
await OVM_StateManager.callStatic.testAndSetAccountLoaded(
DUMMY_ACCOUNTS[0].address
)
).to.equal(true)
})
})
describe('when the account has been changed', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetAccountChanged(
DUMMY_ACCOUNTS[0].address
)
})
it('should return true', async () => {
expect(
await OVM_StateManager.callStatic.testAndSetAccountLoaded(
DUMMY_ACCOUNTS[0].address
)
).to.equal(true)
})
})
})
describe('commitAccount', () => {
describe('when the account has not been touched', () => {
it('should return false', async () => {
expect(
await OVM_StateManager.callStatic.commitAccount(
DUMMY_ACCOUNTS[0].address
)
).to.equal(false)
})
})
describe('when the account has been loaded but not changed', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetAccountLoaded(
DUMMY_ACCOUNTS[0].address
)
})
it('should return false', async () => {
expect(
await OVM_StateManager.callStatic.commitAccount(
DUMMY_ACCOUNTS[0].address
)
).to.equal(false)
})
})
describe('when the account has been changed', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetAccountChanged(
DUMMY_ACCOUNTS[0].address
)
})
it('should return true', async () => {
expect(
await OVM_StateManager.callStatic.commitAccount(
DUMMY_ACCOUNTS[0].address
)
).to.equal(true)
})
})
describe('when the account has already been committed', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetAccountChanged(
DUMMY_ACCOUNTS[0].address
)
await OVM_StateManager.commitAccount(DUMMY_ACCOUNTS[0].address)
})
it('should return false', async () => {
expect(
await OVM_StateManager.callStatic.commitAccount(
DUMMY_ACCOUNTS[0].address
)
).to.equal(false)
})
})
})
describe('incrementTotalUncommittedAccounts', () => {
it('should update the total uncommitted accounts', async () => {
await expect(OVM_StateManager.incrementTotalUncommittedAccounts()).to.not
.be.reverted
})
})
describe('getTotalUncommittedAccounts', () => {
describe('when the total count has not been changed', () => {
it('should return zero', async () => {
expect(
await OVM_StateManager.callStatic.getTotalUncommittedAccounts()
).to.equal(0)
})
})
describe('when the count has been incremented', () => {
describe('one time', () => {
beforeEach(async () => {
await OVM_StateManager.incrementTotalUncommittedAccounts()
})
it('should return one', async () => {
expect(
await OVM_StateManager.callStatic.getTotalUncommittedAccounts()
).to.equal(1)
})
describe('when an account has been committed', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetAccountChanged(
DUMMY_ACCOUNTS[0].address
)
await OVM_StateManager.commitAccount(DUMMY_ACCOUNTS[0].address)
})
it('should return zero', async () => {
expect(
await OVM_StateManager.callStatic.getTotalUncommittedAccounts()
).to.equal(0)
})
})
})
describe('ten times', () => {
beforeEach(async () => {
for (let i = 0; i < 10; i++) {
await OVM_StateManager.incrementTotalUncommittedAccounts()
}
})
it('should return one', async () => {
expect(
await OVM_StateManager.callStatic.getTotalUncommittedAccounts()
).to.equal(10)
})
describe('when an account has been committed', () => {
describe('one time', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetAccountChanged(
DUMMY_ACCOUNTS[0].address
)
await OVM_StateManager.commitAccount(DUMMY_ACCOUNTS[0].address)
})
it('should return nine', async () => {
expect(
await OVM_StateManager.callStatic.getTotalUncommittedAccounts()
).to.equal(9)
})
})
})
})
})
})
describe('putContractStorage', () => {
it('should be able to insert a storage slot for a given contract', async () => {
await expect(
OVM_StateManager.putContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0],
DUMMY_BYTES32[1]
)
).to.not.be.reverted
})
it('should be able to overwrite a storage slot for a given contract', async () => {
await OVM_StateManager.putContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0],
DUMMY_BYTES32[1]
)
await expect(
OVM_StateManager.putContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0],
DUMMY_BYTES32[2]
)
).to.not.be.reverted
})
})
describe('getContractStorage', () => {
it('should be able to retrieve a storage slot for a given contract', async () => {
await OVM_StateManager.putContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0],
DUMMY_BYTES32[1]
)
expect(
await OVM_StateManager.callStatic.getContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
).to.equal(DUMMY_BYTES32[1])
})
it('should be able to retrieve an overwritten storage slot for a given contract', async () => {
await OVM_StateManager.putContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0],
DUMMY_BYTES32[1]
)
await OVM_StateManager.putContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0],
DUMMY_BYTES32[2]
)
expect(
await OVM_StateManager.callStatic.getContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
).to.equal(DUMMY_BYTES32[2])
})
})
describe('hasContractStorage', () => {
describe('when the storage slot has not been verified', () => {
it('should return false', async () => {
expect(
await OVM_StateManager.hasContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
).to.equal(false)
})
})
describe('when the storage slot has been verified', () => {
beforeEach(async () => {
await OVM_StateManager.putContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0],
DUMMY_BYTES32[1]
)
})
it('should return true', async () => {
expect(
await OVM_StateManager.hasContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
).to.equal(true)
})
})
describe('when the account is newly created', () => {
beforeEach(async () => {
await OVM_StateManager.initPendingAccount(DUMMY_ACCOUNTS[0].address)
})
it('should return true for any slot', async () => {
for (const DUMMY_KEY of DUMMY_BYTES32) {
expect(
await OVM_StateManager.hasContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_KEY
)
).to.equal(true)
}
})
})
})
describe('testAndSetContractStorageChanged', () => {
describe('when the storage slot has not been touched', () => {
it('should return false', async () => {
expect(
await OVM_StateManager.callStatic.testAndSetContractStorageChanged(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
).to.equal(false)
})
})
describe('when the storage slot has been loaded but not changed', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetContractStorageLoaded(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
})
it('should return false', async () => {
expect(
await OVM_StateManager.callStatic.testAndSetContractStorageChanged(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
).to.equal(false)
})
})
describe('when the storage slot has been changed', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetContractStorageChanged(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
})
it('should return true', async () => {
expect(
await OVM_StateManager.callStatic.testAndSetContractStorageChanged(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
).to.equal(true)
})
})
describe('when the storage slot has been committed', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetContractStorageChanged(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
await OVM_StateManager.commitContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
})
it('should return true', async () => {
expect(
await OVM_StateManager.callStatic.testAndSetContractStorageChanged(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
).to.equal(true)
})
})
})
describe('testAndSetContractStorageLoaded', () => {
describe('when the storage slot has not been touched', () => {
it('should return false', async () => {
expect(
await OVM_StateManager.callStatic.testAndSetContractStorageLoaded(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
).to.equal(false)
})
})
describe('when the storage slot has already been loaded', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetContractStorageLoaded(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
})
it('should return true', async () => {
expect(
await OVM_StateManager.callStatic.testAndSetContractStorageLoaded(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
).to.equal(true)
})
})
describe('when the storage slot has already been changed', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetContractStorageChanged(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
})
it('should return true', async () => {
expect(
await OVM_StateManager.callStatic.testAndSetContractStorageLoaded(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
).to.equal(true)
})
})
describe('when the storage slot has been committed', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetContractStorageChanged(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
await OVM_StateManager.commitContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
})
it('should return true', async () => {
expect(
await OVM_StateManager.callStatic.testAndSetContractStorageLoaded(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
).to.equal(true)
})
})
})
describe('commitContractStorage', () => {
describe('when the storage slot has not been touched', () => {
it('should return false', async () => {
expect(
await OVM_StateManager.callStatic.commitContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
).to.equal(false)
})
})
describe('when the storage slot has been loaded', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetContractStorageLoaded(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
})
it('should return false', async () => {
expect(
await OVM_StateManager.callStatic.commitContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
).to.equal(false)
})
})
describe('when the storage slot has been changed', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetContractStorageChanged(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
})
it('should return true', async () => {
expect(
await OVM_StateManager.callStatic.commitContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
).to.equal(true)
})
})
describe('when the storage slot has been committed', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetContractStorageChanged(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
await OVM_StateManager.commitContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
})
it('should return false', async () => {
expect(
await OVM_StateManager.callStatic.commitContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
).to.equal(false)
})
})
})
describe('incrementTotalUncommittedContractStorage', () => {
it('should update the total uncommitted storage slots', async () => {
await expect(OVM_StateManager.incrementTotalUncommittedContractStorage())
.to.not.be.reverted
})
})
describe('getTotalUncommittedContractStorage', () => {
describe('when the total count has not been changed', () => {
it('should return zero', async () => {
expect(
await OVM_StateManager.callStatic.getTotalUncommittedContractStorage()
).to.equal(0)
})
})
describe('when the count has been incremented', () => {
describe('one time', () => {
beforeEach(async () => {
await OVM_StateManager.incrementTotalUncommittedContractStorage()
})
it('should return one', async () => {
expect(
await OVM_StateManager.callStatic.getTotalUncommittedContractStorage()
).to.equal(1)
})
describe('when a storage slot has been committed', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetContractStorageChanged(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
await OVM_StateManager.commitContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
})
it('should return zero', async () => {
expect(
await OVM_StateManager.callStatic.getTotalUncommittedContractStorage()
).to.equal(0)
})
})
})
describe('ten times', () => {
beforeEach(async () => {
for (let i = 0; i < 10; i++) {
await OVM_StateManager.incrementTotalUncommittedContractStorage()
}
})
it('should return ten', async () => {
expect(
await OVM_StateManager.callStatic.getTotalUncommittedContractStorage()
).to.equal(10)
})
describe('when a storage slot has been committed', () => {
describe('one time', () => {
beforeEach(async () => {
await OVM_StateManager.testAndSetContractStorageChanged(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
await OVM_StateManager.commitContractStorage(
DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0]
)
})
it('should return nine', async () => {
expect(
await OVM_StateManager.callStatic.getTotalUncommittedContractStorage()
).to.equal(9)
})
})
})
})
})
})
})
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from 'hardhat'
import { ContractFactory, Contract } from 'ethers'
import { MockContract, smockit } from '@eth-optimism/smock'
import { NON_ZERO_ADDRESS } from '../../../helpers/constants'
const callPredeployStatic = async (
Helper_PredeployCaller: Contract,
predeploy: Contract,
functionName: string,
functionParams?: any[]
): Promise<any> => {
return Helper_PredeployCaller.callStatic[functionName](
predeploy.address,
predeploy.interface.encodeFunctionData(functionName, functionParams || [])
)
}
// TODO: rewrite this test to bypass the execution manager
describe.skip('OVM_L1MessageSender', () => {
let Mock__OVM_ExecutionManager: MockContract
before(async () => {
Mock__OVM_ExecutionManager = await smockit(
await ethers.getContractFactory('OVM_ExecutionManager')
)
})
let Helper_PredeployCaller: Contract
before(async () => {
Helper_PredeployCaller = await (
await ethers.getContractFactory('Helper_PredeployCaller')
).deploy()
Helper_PredeployCaller.setTarget(Mock__OVM_ExecutionManager.address)
})
let Factory__OVM_L1MessageSender: ContractFactory
before(async () => {
Factory__OVM_L1MessageSender = await ethers.getContractFactory(
'OVM_L1MessageSender'
)
})
let OVM_L1MessageSender: Contract
beforeEach(async () => {
OVM_L1MessageSender = await Factory__OVM_L1MessageSender.deploy()
})
describe('getL1MessageSender', () => {
before(async () => {
Mock__OVM_ExecutionManager.smocked.ovmL1TXORIGIN.will.return.with(
NON_ZERO_ADDRESS
)
})
it('should return the L1 message sender', async () => {
expect(
await callPredeployStatic(
Helper_PredeployCaller,
OVM_L1MessageSender,
'getL1MessageSender'
)
).to.equal(NON_ZERO_ADDRESS)
})
})
})
import { expect } from '../../../setup'
/* External Imports */
import { waffle, ethers } from 'hardhat'
import { ContractFactory, Wallet, Contract, Signer } from 'ethers'
import { smockit, MockContract, unbind } from '@eth-optimism/smock'
import { toPlainObject } from 'lodash'
/* Internal Imports */
import { DEFAULT_EIP155_TX, LibEIP155TxStruct } from '../../../helpers'
import {
getContractInterface,
predeploys,
getContractFactory,
} from '../../../../src'
describe('OVM_SequencerEntrypoint', () => {
const iOVM_ECDSAContractAccount = getContractInterface(
'OVM_ECDSAContractAccount'
)
let wallet: Wallet
before(async () => {
const provider = waffle.provider
;[wallet] = provider.getWallets()
})
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let Mock__OVM_ExecutionManager: MockContract
before(async () => {
Mock__OVM_ExecutionManager = await smockit('OVM_ExecutionManager', {
address: predeploys.OVM_ExecutionManagerWrapper,
})
Mock__OVM_ExecutionManager.smocked.ovmCHAINID.will.return.with(420)
Mock__OVM_ExecutionManager.smocked.ovmCREATEEOA.will.return()
})
let Factory__OVM_SequencerEntrypoint: ContractFactory
before(async () => {
Factory__OVM_SequencerEntrypoint = await ethers.getContractFactory(
'OVM_SequencerEntrypoint'
)
})
let OVM_SequencerEntrypoint: Contract
beforeEach(async () => {
OVM_SequencerEntrypoint = await Factory__OVM_SequencerEntrypoint.deploy()
})
describe('fallback()', async () => {
it('should call ovmCREATEEOA when ovmEXTCODESIZE returns 0', async () => {
const transaction = DEFAULT_EIP155_TX
const encodedTransaction = await wallet.signTransaction(transaction)
// Just unbind the smock in case it's there during this test for some reason.
await unbind(await wallet.getAddress())
await signer.sendTransaction({
to: OVM_SequencerEntrypoint.address,
data: encodedTransaction,
})
const call: any = Mock__OVM_ExecutionManager.smocked.ovmCREATEEOA.calls[0]
const eoaAddress = ethers.utils.recoverAddress(call._messageHash, {
v: call._v + 27,
r: call._r,
s: call._s,
})
expect(eoaAddress).to.equal(await wallet.getAddress())
})
it('should call EIP155', async () => {
const transaction = DEFAULT_EIP155_TX
const encodedTransaction = await wallet.signTransaction(transaction)
const Mock__wallet = await smockit(iOVM_ECDSAContractAccount, {
address: await wallet.getAddress(),
})
await signer.sendTransaction({
to: OVM_SequencerEntrypoint.address,
data: encodedTransaction,
})
const call = toPlainObject(Mock__wallet.smocked.execute.calls[0])
const _transaction = call._transaction
expect(_transaction[0]).to.deep.equal(transaction.nonce)
expect(_transaction.nonce).to.deep.equal(transaction.nonce)
expect(_transaction.gasPrice).to.deep.equal(transaction.gasPrice)
expect(_transaction.gasLimit).to.deep.equal(transaction.gasLimit)
expect(_transaction.to).to.deep.equal(transaction.to)
expect(_transaction.data).to.deep.equal(transaction.data)
expect(_transaction.isCreate).to.deep.equal(false)
})
it('should send correct calldata if tx is a create', async () => {
const transaction = { ...DEFAULT_EIP155_TX, to: '' }
const encodedTransaction = await wallet.signTransaction(transaction)
const Mock__wallet = await smockit(iOVM_ECDSAContractAccount, {
address: await wallet.getAddress(),
})
await signer.sendTransaction({
to: OVM_SequencerEntrypoint.address,
data: encodedTransaction,
})
const call = toPlainObject(Mock__wallet.smocked.execute.calls[0])
const _transaction = call._transaction
expect(_transaction[0]).to.deep.equal(transaction.nonce)
expect(_transaction.nonce).to.deep.equal(transaction.nonce)
expect(_transaction.gasPrice).to.deep.equal(transaction.gasPrice)
expect(_transaction.gasLimit).to.deep.equal(transaction.gasLimit)
expect(_transaction.to).to.deep.equal(ethers.constants.AddressZero)
expect(_transaction.data).to.deep.equal(transaction.data)
expect(_transaction.isCreate).to.deep.equal(true)
})
})
})
...@@ -3,11 +3,10 @@ import { expect } from '../../../setup' ...@@ -3,11 +3,10 @@ import { expect } from '../../../setup'
/* Imports: External */ /* Imports: External */
import hre from 'hardhat' import hre from 'hardhat'
import { MockContract, smockit } from '@eth-optimism/smock' import { MockContract, smockit } from '@eth-optimism/smock'
import { Contract, Signer, constants } from 'ethers' import { Contract, Signer } from 'ethers'
/* Imports: Internal */ /* Imports: Internal */
import { predeploys } from '../../../../src' import { predeploys } from '../../../../src'
import { getContractFactory } from '@nomiclabs/hardhat-ethers/types'
describe('OVM_SequencerFeeVault', () => { describe('OVM_SequencerFeeVault', () => {
let signer1: Signer let signer1: Signer
...@@ -24,7 +23,6 @@ describe('OVM_SequencerFeeVault', () => { ...@@ -24,7 +23,6 @@ describe('OVM_SequencerFeeVault', () => {
Mock__OVM_L2StandardBridge = await smockit('OVM_L2StandardBridge', { Mock__OVM_L2StandardBridge = await smockit('OVM_L2StandardBridge', {
address: predeploys.OVM_L2StandardBridge, address: predeploys.OVM_L2StandardBridge,
}) })
console.log(await Mock__OVM_L2StandardBridge.getAddress)
}) })
let OVM_SequencerFeeVault: Contract let OVM_SequencerFeeVault: Contract
......
import { expect } from '../../../setup' import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { waffle, ethers as deployer } from 'hardhat' import { ethers } from 'hardhat'
import { smoddit, smockit } from '@eth-optimism/smock' import { Signer, Contract } from 'ethers'
import { ethers, Contract, BigNumber } from 'ethers'
/* Internal Imports */ /* Internal Imports */
import { mineBlock } from '../../../helpers' import { makeAddressManager } from '../../../helpers'
describe('OVM_BondManager', () => { describe('OVM_BondManager', () => {
const provider = waffle.provider let sequencer: Signer
const wallets = provider.getWallets() let nonSequencer: Signer
before(async () => {
let bondManager: Contract ;[sequencer, nonSequencer] = await ethers.getSigners()
let token: Contract
let manager: Contract
let fraudVerifier: Contract
const publisher = wallets[0].address
const stateTransitioner = wallets[3]
const witnessProvider = wallets[4]
const witnessProvider2 = wallets[5]
const sender = wallets[0].address
const txHash = ethers.constants.HashZero
const ONE_WEEK = 3600 * 24 * 7
const preStateRoot =
'0x1111111111111111111111111111111111111111111111111111111111111111'
const amount = ethers.utils.parseEther('1')
const half = amount.div(2)
beforeEach(async () => {
// deploy the address manager
manager = await (
await deployer.getContractFactory('Lib_AddressManager')
).deploy()
// deploy the state manager and mock it for the state transitioner
const stateManagerFactory = await smockit(
await (
await deployer.getContractFactory('OVM_StateManagerFactory')
).deploy()
)
stateManagerFactory.smocked.create.will.return.with(
ethers.constants.AddressZero
)
await manager.setAddress(
'OVM_StateManagerFactory',
stateManagerFactory.address
)
// deploy the fraud verifier and mock its pre-state root transitioner
fraudVerifier = await (
await deployer.getContractFactory('Mock_FraudVerifier')
).deploy()
await fraudVerifier.setStateTransitioner(
preStateRoot,
txHash,
stateTransitioner.address
)
await manager.setAddress('OVM_FraudVerifier', fraudVerifier.address)
// deploy a test erc20 token to be used for the bonds
token = await (await deployer.getContractFactory('TestERC20')).deploy()
await token.mint(sender, ethers.utils.parseEther('100'))
bondManager = await (
await smoddit('OVM_BondManager')
).deploy(token.address, manager.address)
await manager.setAddress('OVM_BondManager', bondManager.address)
await fraudVerifier.setBondManager(bondManager.address)
}) })
describe('collateral management', () => { let AddressManager: Contract
let balanceBefore: BigNumber before(async () => {
AddressManager = await makeAddressManager()
beforeEach(async () => {
await token.approve(bondManager.address, ethers.constants.MaxUint256)
balanceBefore = await token.balanceOf(sender)
await bondManager.deposit()
})
it('can deposit', async () => {
const balanceAfter = await token.balanceOf(sender)
expect(balanceAfter).to.be.eq(balanceBefore.sub(amount))
const bond = await bondManager.bonds(sender)
expect(bond.state).to.eq(State.COLLATERALIZED)
expect(bond.withdrawalTimestamp).to.eq(0)
})
it('isCollateralized is true after depositing', async () => {
expect(await bondManager.isCollateralized(sender)).to.be.true
})
it('isCollateralized is false after starting a withdrawal', async () => {
await bondManager.startWithdrawal()
expect(await bondManager.isCollateralized(sender)).to.be.false
})
it('can start a withdrawal', async () => {
await bondManager.startWithdrawal()
const bond = await bondManager.bonds(sender)
expect(bond.state).to.eq(State.WITHDRAWING)
expect(bond.withdrawalTimestamp).to.not.eq(0)
})
it('can only withdraw after the dispute period', async () => {
await bondManager.startWithdrawal()
await expect(bondManager.finalizeWithdrawal()).to.be.revertedWith(
Errors.TOO_EARLY
)
const { withdrawalTimestamp } = await bondManager.bonds(sender)
const timestamp = withdrawalTimestamp + ONE_WEEK
await mineBlock(deployer.provider, timestamp)
balanceBefore = await token.balanceOf(sender)
await bondManager.finalizeWithdrawal()
const bond = await bondManager.bonds(sender)
expect(bond.state).to.eq(State.NOT_COLLATERALIZED)
expect(bond.withdrawalTimestamp).to.eq(0)
expect(await token.balanceOf(sender)).to.eq(balanceBefore.add(amount))
})
it('is not collateralized after withdrawing', async () => {
await bondManager.startWithdrawal()
const { withdrawalTimestamp } = await bondManager.bonds(sender)
const timestamp = withdrawalTimestamp + ONE_WEEK
await mineBlock(deployer.provider, timestamp)
await bondManager.finalizeWithdrawal()
expect(await bondManager.isCollateralized(sender)).to.be.false
})
}) })
describe('dispute resolution', () => { let OVM_BondManager: Contract
const user1Gas = [382100, 500000] before(async () => {
const user2Gas = 100000 OVM_BondManager = await (
await ethers.getContractFactory('OVM_BondManager')
const totalUser1Gas = user1Gas[0] + user1Gas[1] ).deploy(AddressManager.address)
const totalGas = totalUser1Gas + user2Gas
beforeEach(async () => {
await bondManager
.connect(stateTransitioner)
.recordGasSpent(
preStateRoot,
txHash,
witnessProvider.address,
user1Gas[0]
)
await bondManager
.connect(stateTransitioner)
.recordGasSpent(
preStateRoot,
txHash,
witnessProvider.address,
user1Gas[1]
)
await bondManager
.connect(stateTransitioner)
.recordGasSpent(
preStateRoot,
txHash,
witnessProvider2.address,
user2Gas
)
})
describe('post witnesses', () => {
it('can post witnesses from the transitioner for a state root', async () => {
const reward = await bondManager.witnessProviders(preStateRoot)
expect(reward.canClaim).to.be.false
expect(reward.total).to.be.equal(totalGas)
expect(
await bondManager.getGasSpent(preStateRoot, witnessProvider.address)
).to.be.equal(totalUser1Gas)
expect(
await bondManager.getGasSpent(preStateRoot, witnessProvider2.address)
).to.be.equal(user2Gas)
})
it('cannot post witnesses from non-transitioners for that state root', async () => {
await expect(
bondManager.recordGasSpent(
preStateRoot,
txHash,
witnessProvider.address,
100
)
).to.be.revertedWith(Errors.ONLY_TRANSITIONER)
})
})
it('cannot claim before canClaim is set', async () => {
await expect(bondManager.claim(publisher)).to.be.revertedWith(
Errors.CANNOT_CLAIM
)
})
describe('claims', () => {
let timestamp: number
// prepare by setting the claim flag and linking the publisher to the state root
beforeEach(async () => {
// deposit the collateral to be distributed
await token.approve(bondManager.address, ethers.constants.MaxUint256)
await bondManager.deposit()
// smodify the canClaim value to true to test claiming
const block = await provider.getBlock('latest')
timestamp = block.timestamp
await bondManager.smodify.put({
witnessProviders: {
[preStateRoot]: {
canClaim: true,
},
},
bonds: {
[publisher]: {
earliestDisputedStateRoot: preStateRoot,
firstDisputeAt: timestamp,
},
},
})
const reward = await bondManager.witnessProviders(preStateRoot)
expect(reward.canClaim).to.be.true
})
it('cannot claim before time for other disputes has passed', async () => {
await expect(
bondManager.connect(witnessProvider).claim(publisher)
).to.be.revertedWith(Errors.WAIT_FOR_DISPUTES)
})
it('rewards get paid out proportionally', async () => {
await mineBlock(deployer.provider, timestamp + ONE_WEEK)
// One will get 2/3rds of the bond, the other will get 1/3rd
const balanceBefore1 = await token.balanceOf(witnessProvider.address)
const balanceBefore2 = await token.balanceOf(witnessProvider2.address)
await bondManager.connect(witnessProvider).claim(publisher)
await bondManager.connect(witnessProvider2).claim(publisher)
const balanceAfter1 = await token.balanceOf(witnessProvider.address) AddressManager.setAddress('OVM_Proposer', await sequencer.getAddress())
const balanceAfter2 = await token.balanceOf(witnessProvider2.address) })
expect(balanceAfter1).to.be.eq(
balanceBefore1.add(half.mul(totalUser1Gas).div(totalGas))
)
expect(balanceAfter2).to.be.eq(
balanceBefore2.add(half.mul(user2Gas).div(totalGas))
)
})
it('cannot double claim', async () => {
await mineBlock(deployer.provider, timestamp + ONE_WEEK)
const balance1 = await token.balanceOf(witnessProvider.address)
await bondManager.connect(witnessProvider).claim(publisher)
const balance2 = await token.balanceOf(witnessProvider.address)
expect(balance2).to.be.eq(
balance1.add(half.mul(totalUser1Gas).div(totalGas))
)
// re-claiming does not give the user any extra funds describe('isCollateralized', () => {
await bondManager.connect(witnessProvider).claim(publisher) it('should return true for OVM_Proposer', async () => {
const balance3 = await token.balanceOf(witnessProvider.address) expect(
expect(balance3).to.be.eq(balance2) await OVM_BondManager.isCollateralized(await sequencer.getAddress())
}) ).to.equal(true)
}) })
describe('finalize', () => { it('should return false for non-sequencer', async () => {
beforeEach(async () => { expect(
await token.approve(bondManager.address, ethers.constants.MaxUint256) await OVM_BondManager.isCollateralized(await nonSequencer.getAddress())
await bondManager.deposit() ).to.equal(false)
})
it('only fraud verifier can finalize', async () => {
await expect(
bondManager.finalize(preStateRoot, sender, 0)
).to.be.revertedWith(Errors.ONLY_FRAUD_VERIFIER)
})
it('proving fraud allows claiming', async () => {
await fraudVerifier.finalize(preStateRoot, sender, 0)
expect((await bondManager.witnessProviders(preStateRoot)).canClaim).to
.be.true
// cannot double finalize
await expect(
fraudVerifier.finalize(preStateRoot, sender, 0)
).to.be.revertedWith(Errors.ALREADY_FINALIZED)
})
it("proving fraud cancels pending withdrawals if the withdrawal was during the batch's proving window", async () => {
await bondManager.startWithdrawal()
const { withdrawalTimestamp } = await bondManager.bonds(sender)
const timestamp = withdrawalTimestamp + ONE_WEEK
// a dispute is created about a block that intersects
const disputeTimestamp = withdrawalTimestamp - 100
await fraudVerifier.finalize(preStateRoot, sender, disputeTimestamp)
await mineBlock(deployer.provider, timestamp)
await expect(bondManager.finalizeWithdrawal()).to.be.revertedWith(
Errors.SLASHED
)
})
it('proving fraud late does not cancel pending withdrawals', async () => {
await bondManager.startWithdrawal()
const { withdrawalTimestamp } = await bondManager.bonds(sender)
// a dispute is created, but since the fraud period is already over
// it doesn't matter
const disputeTimestamp = withdrawalTimestamp - ONE_WEEK - 1
await fraudVerifier.finalize(preStateRoot, sender, disputeTimestamp)
const finalizeWithdrawalTimestamp = withdrawalTimestamp + ONE_WEEK
await mineBlock(deployer.provider, finalizeWithdrawalTimestamp)
await bondManager.finalizeWithdrawal()
})
it('proving fraud prevents starting a withdrawal due to slashing', async () => {
await fraudVerifier.finalize(preStateRoot, sender, 0)
await expect(bondManager.startWithdrawal()).to.be.revertedWith(
Errors.WRONG_STATE
)
})
describe('same publisher commits fraud multiple times', async () => {
let timestamp: number
const root1 =
'0x0000000000000000000000000000000000000000000000000000000000000000'
const ts1 = 100
const root2 =
'0x0000000000000000000000000000000000000000000000000000000000000001'
const ts2 = 110
beforeEach(async () => {
await fraudVerifier.finalize(root2, sender, ts2)
const block = await provider.getBlock('latest')
timestamp = block.timestamp
})
it('initial dispute data is stored', async () => {
const bond = await bondManager.bonds(sender)
expect(bond.firstDisputeAt).to.be.equal(timestamp)
expect(bond.earliestTimestamp).to.be.equal(ts2)
expect(bond.earliestDisputedStateRoot).to.be.equal(root2)
})
it('earlier dispute replaces initial data', async () => {
await fraudVerifier.finalize(root1, sender, ts1)
const bond = await bondManager.bonds(sender)
expect(bond.firstDisputeAt).to.be.equal(timestamp)
expect(bond.earliestTimestamp).to.be.equal(ts1)
expect(bond.earliestDisputedStateRoot).to.be.equal(root1)
})
it('earlier dispute does not replace initial data if not in time', async () => {
await mineBlock(deployer.provider, timestamp + ONE_WEEK)
await fraudVerifier.finalize(root1, sender, ts1)
const bond = await bondManager.bonds(sender)
expect(bond.firstDisputeAt).to.be.equal(timestamp)
expect(bond.earliestTimestamp).to.be.equal(ts2)
expect(bond.earliestDisputedStateRoot).to.be.equal(root2)
})
it('later dispute does not replace initial data', async () => {
await fraudVerifier.finalize(root1, sender, ts2 + 1)
const bond = await bondManager.bonds(sender)
expect(bond.firstDisputeAt).to.be.equal(timestamp)
expect(bond.earliestTimestamp).to.be.equal(ts2)
expect(bond.earliestDisputedStateRoot).to.be.equal(root2)
})
})
}) })
}) })
}) })
enum State {
// Before depositing or after getting slashed, a user is uncollateralized
NOT_COLLATERALIZED,
// After depositing, a user is collateralized
COLLATERALIZED,
// After a user has initiated a withdrawal
WITHDRAWING,
}
// Errors from the bond manager smart contract
enum Errors {
ERC20_ERR = 'BondManager: Could not post bond',
ALREADY_FINALIZED = 'BondManager: Fraud proof for this pre-state root has already been finalized',
SLASHED = 'BondManager: Cannot finalize withdrawal, you probably got slashed',
WRONG_STATE = 'BondManager: Wrong bond state for proposer',
CANNOT_CLAIM = 'BondManager: Cannot claim yet. Dispute must be finalized first',
WITHDRAWAL_PENDING = 'BondManager: Withdrawal already pending',
TOO_EARLY = 'BondManager: Too early to finalize your withdrawal',
ONLY_TRANSITIONER = 'BondManager: Only the transitioner for this pre-state root may call this function',
ONLY_FRAUD_VERIFIER = 'BondManager: Only the fraud verifier may call this function',
ONLY_STATE_COMMITMENT_CHAIN = 'BondManager: Only the state commitment chain may call this function',
WAIT_FOR_DISPUTES = 'BondManager: Wait for other potential disputes',
}
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from 'hardhat'
import { ContractFactory, Contract, BigNumber } from 'ethers'
import { smockit, MockContract } from '@eth-optimism/smock'
/* Internal Imports */
import {
makeAddressManager,
setProxyTarget,
DUMMY_BATCH_HEADERS,
DUMMY_BATCH_PROOFS,
DUMMY_OVM_TRANSACTIONS,
NON_NULL_BYTES32,
hashTransaction,
} from '../../../helpers'
const DUMMY_TX_CHAIN_ELEMENTS = [...Array(10).keys()].map((i) => {
return {
isSequenced: false,
queueIndex: BigNumber.from(0),
timestamp: BigNumber.from(i),
blockNumber: BigNumber.from(0),
txData: ethers.constants.HashZero,
}
})
const DUMMY_HASH = hashTransaction(DUMMY_OVM_TRANSACTIONS[0])
const DUMMY_BATCH_PROOFS_WITH_INDEX = [
{
index: 11,
siblings: [ethers.constants.HashZero],
},
]
describe('OVM_FraudVerifier', () => {
let AddressManager: Contract
before(async () => {
AddressManager = await makeAddressManager()
})
let Mock__OVM_StateCommitmentChain: MockContract
let Mock__OVM_CanonicalTransactionChain: MockContract
let Mock__OVM_StateTransitioner: MockContract
let Mock__OVM_StateTransitionerFactory: MockContract
let Mock__OVM_BondManager: MockContract
before(async () => {
Mock__OVM_StateCommitmentChain = await smockit(
await ethers.getContractFactory('OVM_StateCommitmentChain')
)
Mock__OVM_CanonicalTransactionChain = await smockit(
await ethers.getContractFactory('OVM_CanonicalTransactionChain')
)
Mock__OVM_StateTransitioner = await smockit(
await ethers.getContractFactory('OVM_StateTransitioner')
)
Mock__OVM_StateTransitionerFactory = await smockit(
await ethers.getContractFactory('OVM_StateTransitionerFactory')
)
Mock__OVM_BondManager = await smockit(
await ethers.getContractFactory('OVM_BondManager')
)
await setProxyTarget(
AddressManager,
'OVM_StateCommitmentChain',
Mock__OVM_StateCommitmentChain
)
await setProxyTarget(
AddressManager,
'OVM_CanonicalTransactionChain',
Mock__OVM_CanonicalTransactionChain
)
await setProxyTarget(
AddressManager,
'OVM_StateTransitionerFactory',
Mock__OVM_StateTransitionerFactory
)
await setProxyTarget(
AddressManager,
'OVM_BondManager',
Mock__OVM_BondManager
)
Mock__OVM_StateTransitionerFactory.smocked.create.will.return.with(
Mock__OVM_StateTransitioner.address
)
})
let Factory__OVM_FraudVerifier: ContractFactory
before(async () => {
Factory__OVM_FraudVerifier = await ethers.getContractFactory(
'OVM_FraudVerifier'
)
})
let OVM_FraudVerifier: Contract
beforeEach(async () => {
OVM_FraudVerifier = await Factory__OVM_FraudVerifier.deploy(
AddressManager.address
)
})
describe('initializeFraudVerification', () => {
describe('when provided an invalid pre-state root inclusion proof', () => {
before(() => {
Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with(
false
)
})
it('should revert', async () => {
await expect(
OVM_FraudVerifier.initializeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_OVM_TRANSACTIONS[0],
DUMMY_TX_CHAIN_ELEMENTS[0],
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0]
)
).to.be.revertedWith('Invalid pre-state root inclusion proof.')
})
})
describe('when provided a valid pre-state root inclusion proof', () => {
before(() => {
Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with(
true
)
})
describe('when provided an invalid transaction inclusion proof', () => {
before(() => {
Mock__OVM_CanonicalTransactionChain.smocked.verifyTransaction.will.return.with(
false
)
})
it('should revert', async () => {
await expect(
OVM_FraudVerifier.initializeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_OVM_TRANSACTIONS[0],
DUMMY_TX_CHAIN_ELEMENTS[0],
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0]
)
).to.be.revertedWith('Invalid transaction inclusion proof.')
})
})
describe('when provided a valid transaction inclusion proof', () => {
before(() => {
Mock__OVM_CanonicalTransactionChain.smocked.verifyTransaction.will.return.with(
true
)
})
it('should deploy a new state transitioner', async () => {
await expect(
OVM_FraudVerifier.initializeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_OVM_TRANSACTIONS[0],
DUMMY_TX_CHAIN_ELEMENTS[0],
DUMMY_BATCH_HEADERS[0],
{
...DUMMY_BATCH_PROOFS[0],
index: DUMMY_BATCH_PROOFS[0].index + 1,
}
)
).to.not.be.reverted
expect(
await OVM_FraudVerifier.getStateTransitioner(
ethers.constants.HashZero,
DUMMY_HASH
)
).to.equal(Mock__OVM_StateTransitioner.address)
})
it('should revert when provided with a incorrect transaction root global index', async () => {
await expect(
OVM_FraudVerifier.initializeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_OVM_TRANSACTIONS[0],
DUMMY_TX_CHAIN_ELEMENTS[0],
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS_WITH_INDEX[0]
)
).to.be.revertedWith(
'Pre-state root global index must equal to the transaction root global index.'
)
})
})
})
})
describe('finalizeFraudVerification', () => {
beforeEach(async () => {
Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with(
true
)
Mock__OVM_CanonicalTransactionChain.smocked.verifyTransaction.will.return.with(
true
)
await OVM_FraudVerifier.initializeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_OVM_TRANSACTIONS[0],
DUMMY_TX_CHAIN_ELEMENTS[0],
DUMMY_BATCH_HEADERS[0],
{
...DUMMY_BATCH_PROOFS[0],
index: DUMMY_BATCH_PROOFS[0].index + 1,
}
)
})
describe('when the transition process is not complete', () => {
before(async () => {
Mock__OVM_StateTransitioner.smocked.isComplete.will.return.with(false)
})
it('should revert', async () => {
await expect(
OVM_FraudVerifier.finalizeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_HASH,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0]
)
).to.be.revertedWith(
'State transition process must be completed prior to finalization.'
)
})
})
describe('when the transition process is complete', () => {
before(() => {
Mock__OVM_StateTransitioner.smocked.isComplete.will.return.with(true)
})
describe('when provided an invalid post-state root index', () => {
const batchProof = {
...DUMMY_BATCH_PROOFS[0],
index: DUMMY_BATCH_PROOFS[0].index + 2,
}
it('should revert', async () => {
await expect(
OVM_FraudVerifier.finalizeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_HASH,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
batchProof
)
).to.be.revertedWith(
'Post-state root global index must equal to the pre state root global index plus one.'
)
})
})
describe('when provided a valid post-state root index', () => {
const batchProof = {
...DUMMY_BATCH_PROOFS[0],
index: DUMMY_BATCH_PROOFS[0].index + 1,
}
describe('when provided an invalid pre-state root inclusion proof', () => {
beforeEach(() => {
Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with(
false
)
})
it('should revert', async () => {
await expect(
OVM_FraudVerifier.finalizeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_HASH,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
batchProof
)
).to.be.revertedWith('Invalid pre-state root inclusion proof.')
})
})
describe('when provided a valid pre-state root inclusion proof', () => {
before(() => {
Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with(
true
)
})
describe('when provided an invalid post-state root inclusion proof', () => {
beforeEach(() => {
Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with(
(stateRoot: string, ...args: any) => {
return stateRoot !== NON_NULL_BYTES32
}
)
})
it('should revert', async () => {
await expect(
OVM_FraudVerifier.finalizeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_HASH,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
batchProof
)
).to.be.revertedWith('Invalid post-state root inclusion proof.')
})
})
describe('when provided a valid post-state root inclusion proof', () => {
before(() => {
Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with(
true
)
})
describe('when the provided post-state root does not differ from the computed one', () => {
before(() => {
Mock__OVM_StateTransitioner.smocked.getPostStateRoot.will.return.with(
NON_NULL_BYTES32
)
})
it('should revert', async () => {
await expect(
OVM_FraudVerifier.finalizeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_HASH,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
batchProof
)
).to.be.revertedWith(
'State transition has not been proven fraudulent.'
)
})
})
describe('when the provided post-state root differs from the computed one', () => {
before(() => {
Mock__OVM_StateTransitioner.smocked.getPostStateRoot.will.return.with(
ethers.constants.HashZero
)
})
it('should succeed and attempt to delete a state batch', async () => {
await OVM_FraudVerifier.finalizeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_HASH,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
batchProof
)
expect(
Mock__OVM_StateCommitmentChain.smocked.deleteStateBatch
.calls[0]
).to.deep.equal([
Object.values(DUMMY_BATCH_HEADERS[0]).map((value) => {
return Number.isInteger(value)
? BigNumber.from(value)
: value
}),
])
})
})
})
})
})
describe('multiple fraud proofs for the same pre-execution state', () => {
let state2: any
const DUMMY_HASH_2 = hashTransaction(DUMMY_OVM_TRANSACTIONS[1])
beforeEach(async () => {
state2 = await smockit(
await ethers.getContractFactory('OVM_StateTransitioner')
)
Mock__OVM_StateTransitionerFactory.smocked.create.will.return.with(
state2.address
)
Mock__OVM_StateTransitioner.smocked.getPostStateRoot.will.return.with(
ethers.constants.HashZero
)
state2.smocked.getPostStateRoot.will.return.with(
ethers.constants.HashZero
)
})
it('creates multiple state transitioners per tx hash', async () => {
await expect(
OVM_FraudVerifier.initializeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_OVM_TRANSACTIONS[1],
DUMMY_TX_CHAIN_ELEMENTS[0],
DUMMY_BATCH_HEADERS[0],
{
...DUMMY_BATCH_PROOFS[0],
index: DUMMY_BATCH_PROOFS[0].index + 1,
}
)
).to.not.be.reverted
expect(
await OVM_FraudVerifier.getStateTransitioner(
ethers.constants.HashZero,
DUMMY_HASH
)
).to.equal(Mock__OVM_StateTransitioner.address)
expect(
await OVM_FraudVerifier.getStateTransitioner(
ethers.constants.HashZero,
DUMMY_HASH_2
)
).to.equal(state2.address)
})
const batchProof = {
...DUMMY_BATCH_PROOFS[0],
index: DUMMY_BATCH_PROOFS[0].index + 1,
}
// TODO: Appears to be failing because of a bug in smock.
it.skip('Case 1: allows proving fraud on the same pre-state root twice', async () => {
// finalize previous fraud
await OVM_FraudVerifier.finalizeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_HASH,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
batchProof
)
// start new fraud
await OVM_FraudVerifier.initializeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_OVM_TRANSACTIONS[1],
DUMMY_TX_CHAIN_ELEMENTS[1],
DUMMY_BATCH_HEADERS[1],
{
...DUMMY_BATCH_PROOFS[0],
index: DUMMY_BATCH_PROOFS[0].index + 1,
}
)
// finalize it as well
await OVM_FraudVerifier.finalizeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_HASH_2,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[1],
batchProof
)
// the new batch was deleted
expect(
Mock__OVM_StateCommitmentChain.smocked.deleteStateBatch.calls[0]
).to.deep.equal([
Object.values(DUMMY_BATCH_HEADERS[1]).map((value) => {
return Number.isInteger(value) ? BigNumber.from(value) : value
}),
])
})
// TODO: Appears to be failing because of a bug in smock.
it.skip('Case 2: does not get blocked by the first transitioner', async () => {
// start new fraud
await OVM_FraudVerifier.initializeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_OVM_TRANSACTIONS[1],
DUMMY_TX_CHAIN_ELEMENTS[1],
DUMMY_BATCH_HEADERS[1],
{
...DUMMY_BATCH_PROOFS[0],
index: DUMMY_BATCH_PROOFS[0].index + 1,
}
)
// finalize the new fraud first
await OVM_FraudVerifier.finalizeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_HASH_2,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[1],
batchProof
)
// the new fraud's batch was deleted
expect(
Mock__OVM_StateCommitmentChain.smocked.deleteStateBatch.calls[0]
).to.deep.equal([
Object.values(DUMMY_BATCH_HEADERS[1]).map((value) => {
return Number.isInteger(value) ? BigNumber.from(value) : value
}),
])
// finalize previous fraud
await OVM_FraudVerifier.finalizeFraudVerification(
ethers.constants.HashZero,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_HASH,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
batchProof
)
// the old fraud's batch was deleted
expect(
Mock__OVM_StateCommitmentChain.smocked.deleteStateBatch.calls[0]
).to.deep.equal([
Object.values(DUMMY_BATCH_HEADERS[0]).map((value) => {
return Number.isInteger(value) ? BigNumber.from(value) : value
}),
])
})
})
})
})
})
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from 'hardhat'
import { BigNumber, constants, Contract } from 'ethers'
import * as rlp from 'rlp'
/* Internal Imports */
import {
makeAddressManager,
NON_NULL_BYTES32,
NON_ZERO_ADDRESS,
setProxyTarget,
TrieTestGenerator,
} from '../../../helpers'
import {
MockContract,
smockit,
ModifiableContract,
smoddit,
ModifiableContractFactory,
} from '@eth-optimism/smock'
import { toHexString } from '@eth-optimism/core-utils'
describe('OVM_StateTransitioner', () => {
let AddressManager: Contract
before(async () => {
AddressManager = await makeAddressManager()
})
let Mock__OVM_ExecutionManager: MockContract
let Mock__OVM_StateManagerFactory: MockContract
let Mock__OVM_StateManager: MockContract
let Mock__OVM_BondManager: MockContract
before(async () => {
Mock__OVM_ExecutionManager = await smockit(
await ethers.getContractFactory('OVM_ExecutionManager')
)
Mock__OVM_StateManagerFactory = await smockit(
await ethers.getContractFactory('OVM_StateManagerFactory')
)
Mock__OVM_StateManager = await smockit(
await ethers.getContractFactory('OVM_StateManager')
)
Mock__OVM_BondManager = await smockit(
await ethers.getContractFactory('OVM_BondManager')
)
await setProxyTarget(
AddressManager,
'OVM_BondManager',
Mock__OVM_BondManager
)
Mock__OVM_BondManager.smocked.recordGasSpent.will.return()
await setProxyTarget(
AddressManager,
'OVM_ExecutionManager',
Mock__OVM_ExecutionManager
)
await setProxyTarget(
AddressManager,
'OVM_StateManagerFactory',
Mock__OVM_StateManagerFactory
)
Mock__OVM_StateManagerFactory.smocked.create.will.return.with(
Mock__OVM_StateManager.address
)
Mock__OVM_StateManager.smocked.putAccount.will.return()
})
let Factory__OVM_StateTransitioner: ModifiableContractFactory
before(async () => {
Factory__OVM_StateTransitioner = await smoddit('OVM_StateTransitioner')
})
let OVM_StateTransitioner: ModifiableContract
beforeEach(async () => {
OVM_StateTransitioner = await Factory__OVM_StateTransitioner.deploy(
AddressManager.address,
0,
ethers.constants.HashZero,
ethers.constants.HashZero
)
})
describe('proveContractState', () => {
const ovmContractAddress = NON_ZERO_ADDRESS
let ethContractAddress = constants.AddressZero
let account: any
beforeEach(() => {
Mock__OVM_StateManager.smocked.hasAccount.will.return.with(false)
Mock__OVM_StateManager.smocked.hasEmptyAccount.will.return.with(false)
account = {
nonce: 0,
balance: 0,
storageRoot: ethers.constants.HashZero,
codeHash: ethers.constants.HashZero,
}
})
describe('when provided a valid code hash', () => {
beforeEach(async () => {
ethContractAddress = OVM_StateTransitioner.address
account.codeHash = ethers.utils.keccak256(
await ethers.provider.getCode(OVM_StateTransitioner.address)
)
})
describe('when provided an invalid account inclusion proof', () => {
const proof = '0x'
it('should revert', async () => {
await expect(
OVM_StateTransitioner.proveContractState(
ovmContractAddress,
ethContractAddress,
proof
)
).to.be.reverted
})
})
describe('when provided a valid account inclusion proof', () => {
let proof: string
beforeEach(async () => {
const generator = await TrieTestGenerator.fromAccounts({
accounts: [
{
...account,
address: ovmContractAddress,
},
],
secure: true,
})
const test = await generator.makeAccountProofTest(ovmContractAddress)
proof = test.accountTrieWitness
OVM_StateTransitioner = await Factory__OVM_StateTransitioner.deploy(
AddressManager.address,
0,
test.accountTrieRoot,
ethers.constants.HashZero
)
})
it('should put the account in the state manager', async () => {
await OVM_StateTransitioner.proveContractState(
ovmContractAddress,
ethContractAddress,
proof
)
expect(
Mock__OVM_StateManager.smocked.putAccount.calls[0]
).to.deep.equal([
NON_ZERO_ADDRESS,
[
BigNumber.from(account.nonce),
BigNumber.from(account.balance),
account.storageRoot,
account.codeHash,
ethContractAddress,
false,
],
])
})
})
})
})
describe('proveStorageSlot', () => {
beforeEach(() => {
Mock__OVM_StateManager.smocked.hasContractStorage.will.return.with(false)
})
describe('when the corresponding account is not proven', () => {
beforeEach(() => {
Mock__OVM_StateManager.smocked.hasAccount.will.return.with(false)
})
it('should revert', async () => {
await expect(
OVM_StateTransitioner.proveStorageSlot(
NON_ZERO_ADDRESS,
NON_NULL_BYTES32,
'0x'
)
).to.be.revertedWith(
'Contract must be verified before proving a storage slot.'
)
})
})
describe('when the corresponding account is proven', () => {
beforeEach(() => {
Mock__OVM_StateManager.smocked.hasAccount.will.return.with(true)
})
describe('when provided an invalid slot inclusion proof', () => {
const key = ethers.utils.keccak256('0x1234')
const val = ethers.utils.keccak256('0x5678')
const proof = '0x'
beforeEach(async () => {
const generator = await TrieTestGenerator.fromNodes({
nodes: [
{
key,
val: '0x' + rlp.encode(val).toString('hex'),
},
],
secure: true,
})
const test = await generator.makeInclusionProofTest(0)
Mock__OVM_StateManager.smocked.getAccountStorageRoot.will.return.with(
test.root
)
})
it('should revert', async () => {
await expect(
OVM_StateTransitioner.proveStorageSlot(
constants.AddressZero,
key,
proof
)
).to.be.reverted
})
})
describe('when provided a valid slot inclusion proof', () => {
const key = ethers.utils.keccak256('0x1234')
const val = ethers.utils.keccak256('0x5678')
let proof: string
beforeEach(async () => {
const generator = await TrieTestGenerator.fromNodes({
nodes: [
{
key,
val: '0x' + rlp.encode(val).toString('hex'),
},
],
secure: true,
})
const test = await generator.makeInclusionProofTest(0)
proof = test.proof
Mock__OVM_StateManager.smocked.getAccountStorageRoot.will.return.with(
test.root
)
})
it('should insert the storage slot', async () => {
await expect(
OVM_StateTransitioner.proveStorageSlot(
constants.AddressZero,
key,
proof
)
).to.not.be.reverted
expect(
Mock__OVM_StateManager.smocked.putContractStorage.calls[0]
).to.deep.equal([constants.AddressZero, key, val])
})
})
})
})
describe('applyTransaction', () => {
it('Blocks execution if insufficient gas provided', async () => {
const gasLimit = 500_000
const transaction = {
timestamp: '0x12',
blockNumber: '0x34',
l1QueueOrigin: '0x00',
l1TxOrigin: constants.AddressZero,
entrypoint: constants.AddressZero,
gasLimit: toHexString(gasLimit),
data: '0x1234',
}
const transactionHash = ethers.utils.keccak256(
ethers.utils.solidityPack(
[
'uint256',
'uint256',
'uint8',
'address',
'address',
'uint256',
'bytes',
],
[
transaction.timestamp,
transaction.blockNumber,
transaction.l1QueueOrigin,
transaction.l1TxOrigin,
transaction.entrypoint,
transaction.gasLimit,
transaction.data,
]
)
)
await OVM_StateTransitioner.smodify.put({
phase: 0,
transactionHash,
})
await expect(
OVM_StateTransitioner.applyTransaction(transaction, {
gasLimit: 30_000,
})
).to.be.revertedWith(
`Not enough gas to execute transaction deterministically`
)
})
})
describe('commitContractState', () => {
beforeEach(async () => {
await OVM_StateTransitioner.smodify.put({
phase: 1,
})
})
const ovmContractAddress = NON_ZERO_ADDRESS
let account: any
beforeEach(() => {
account = {
nonce: 0,
balance: 0,
storageRoot: ethers.constants.HashZero,
codeHash: ethers.constants.HashZero,
ethAddress: constants.AddressZero,
isFresh: false,
}
Mock__OVM_StateManager.smocked.hasAccount.will.return.with(false)
Mock__OVM_StateManager.smocked.getAccount.will.return.with(account)
})
describe('when the account was not changed or has already been committed', () => {
before(() => {
Mock__OVM_StateManager.smocked.getTotalUncommittedContractStorage.will.return.with(
0
)
Mock__OVM_StateManager.smocked.commitAccount.will.return.with(false)
})
it('should revert', async () => {
await expect(
OVM_StateTransitioner.commitContractState(ovmContractAddress, '0x')
).to.be.revertedWith(
`Account state wasn't changed or has already been committed`
)
})
})
describe('when the account was changed or has not already been committed', () => {
before(() => {
Mock__OVM_StateManager.smocked.commitAccount.will.return.with(true)
})
describe('when given an valid update proof', () => {
let proof: string
let postStateRoot: string
beforeEach(async () => {
const generator = await TrieTestGenerator.fromAccounts({
accounts: [
{
...account,
nonce: 10,
address: ovmContractAddress,
},
],
secure: true,
})
const test = await generator.makeAccountUpdateTest(
ovmContractAddress,
account
)
proof = test.accountTrieWitness
postStateRoot = test.newAccountTrieRoot
await OVM_StateTransitioner.smodify.put({
postStateRoot: test.accountTrieRoot,
})
})
it('should update the post state root', async () => {
await expect(
OVM_StateTransitioner.commitContractState(ovmContractAddress, proof)
).to.not.be.reverted
expect(await OVM_StateTransitioner.getPostStateRoot()).to.equal(
postStateRoot
)
})
})
})
})
describe('commitStorageSlot', () => {
beforeEach(async () => {
await OVM_StateTransitioner.smodify.put({
phase: 1,
})
})
const ovmContractAddress = NON_ZERO_ADDRESS
let account: any
const key = ethers.utils.keccak256('0x1234')
const val = ethers.utils.keccak256('0x5678')
const newVal = ethers.utils.keccak256('0x4321')
beforeEach(() => {
account = {
nonce: 0,
balance: 0,
storageRoot: ethers.constants.HashZero,
codeHash: ethers.constants.HashZero,
}
Mock__OVM_StateManager.smocked.getAccount.will.return.with({
...account,
ethAddress: constants.AddressZero,
isFresh: false,
})
Mock__OVM_StateManager.smocked.getContractStorage.will.return.with(val)
})
describe('when the slot was not changed or was already committed', () => {
beforeEach(() => {
Mock__OVM_StateManager.smocked.commitContractStorage.will.return.with(
false
)
})
it('should revert', async () => {
await expect(
OVM_StateTransitioner.commitStorageSlot(ovmContractAddress, key, '0x')
).to.be.revertedWith(
`Storage slot value wasn't changed or has already been committed.`
)
})
})
describe('when the slot was changed or not already committed', () => {
beforeEach(() => {
Mock__OVM_StateManager.smocked.commitContractStorage.will.return.with(
true
)
})
describe('with a valid proof', () => {
let storageTrieProof: string
beforeEach(async () => {
const storageGenerator = await TrieTestGenerator.fromNodes({
nodes: [
{
key,
val: '0x' + rlp.encode(val).toString('hex'),
},
],
secure: true,
})
const storageTest = await storageGenerator.makeNodeUpdateTest(
key,
'0x' + rlp.encode(newVal).toString('hex')
)
const generator = await TrieTestGenerator.fromAccounts({
accounts: [
{
...account,
storageRoot: storageTest.root,
address: ovmContractAddress,
},
],
secure: true,
})
const test = await generator.makeAccountUpdateTest(
ovmContractAddress,
{
...account,
storageRoot: storageTest.newRoot,
}
)
Mock__OVM_StateManager.smocked.getAccount.will.return.with({
...account,
storageRoot: storageTest.root,
ethAddress: constants.AddressZero,
isFresh: false,
})
storageTrieProof = storageTest.proof
await OVM_StateTransitioner.smodify.put({
postStateRoot: test.accountTrieRoot,
})
})
it('should commit the slot and update the state', async () => {
await expect(
OVM_StateTransitioner.commitStorageSlot(
ovmContractAddress,
key,
storageTrieProof
)
).to.not.be.reverted
})
})
})
})
describe('completeTransition', () => {
beforeEach(async () => {
await OVM_StateTransitioner.smodify.put({
phase: 1,
})
})
describe('when there are uncommitted accounts', () => {
beforeEach(() => {
Mock__OVM_StateManager.smocked.getTotalUncommittedAccounts.will.return.with(
1
)
Mock__OVM_StateManager.smocked.getTotalUncommittedContractStorage.will.return.with(
0
)
})
it('should revert', async () => {
await expect(
OVM_StateTransitioner.completeTransition()
).to.be.revertedWith(
'All accounts must be committed before completing a transition.'
)
})
})
describe('when there are uncommitted storage slots', () => {
beforeEach(() => {
Mock__OVM_StateManager.smocked.getTotalUncommittedAccounts.will.return.with(
0
)
Mock__OVM_StateManager.smocked.getTotalUncommittedContractStorage.will.return.with(
1
)
})
it('should revert', async () => {
await expect(
OVM_StateTransitioner.completeTransition()
).to.be.revertedWith(
'All storage must be committed before completing a transition.'
)
})
})
describe('when all state changes are committed', () => {
beforeEach(() => {
Mock__OVM_StateManager.smocked.getTotalUncommittedAccounts.will.return.with(
0
)
Mock__OVM_StateManager.smocked.getTotalUncommittedContractStorage.will.return.with(
0
)
})
it('should complete the transition', async () => {
await expect(OVM_StateTransitioner.completeTransition()).to.not.be
.reverted
expect(await OVM_StateTransitioner.isComplete()).to.equal(true)
})
})
})
})
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from 'hardhat'
import { ContractFactory, Contract, constants, Signer } from 'ethers'
import { MockContract, smockit } from '@eth-optimism/smock'
/* Internal Imports */
import {
makeAddressManager,
DUMMY_OVM_TRANSACTIONS,
hashTransaction,
} from '../../../helpers'
const DUMMY_HASH = hashTransaction(DUMMY_OVM_TRANSACTIONS[0])
describe('OVM_StateTransitionerFactory', () => {
let signer1: Signer
before(async () => {
;[signer1] = await ethers.getSigners()
})
let AddressManager: Contract
before(async () => {
AddressManager = await makeAddressManager()
})
let Factory__OVM_StateTransitionerFactory: ContractFactory
before(async () => {
Factory__OVM_StateTransitionerFactory = await ethers.getContractFactory(
'OVM_StateTransitionerFactory'
)
})
let OVM_StateTransitionerFactory: Contract
let Mock__OVM_StateManagerFactory: MockContract
beforeEach(async () => {
OVM_StateTransitionerFactory =
await Factory__OVM_StateTransitionerFactory.deploy(AddressManager.address)
Mock__OVM_StateManagerFactory = await smockit('OVM_StateManagerFactory')
Mock__OVM_StateManagerFactory.smocked.create.will.return.with(
ethers.constants.AddressZero
)
await AddressManager.setAddress(
'OVM_StateManagerFactory',
Mock__OVM_StateManagerFactory.address
)
})
describe('create', () => {
describe('when the sender is not the OVM_FraudVerifier', () => {
beforeEach(async () => {
await AddressManager.setAddress(
'OVM_FraudVerifier',
constants.AddressZero
)
})
it('should revert', async () => {
await expect(
OVM_StateTransitionerFactory.create(
AddressManager.address,
ethers.constants.HashZero,
ethers.constants.HashZero,
DUMMY_HASH
)
).to.be.revertedWith(
'Create can only be done by the OVM_FraudVerifier.'
)
})
})
describe('when the sender is the OVM_FraudVerifier', () => {
beforeEach(async () => {
await AddressManager.setAddress(
'OVM_FraudVerifier',
await signer1.getAddress()
)
})
it('should not revert', async () => {
await expect(
OVM_StateTransitionerFactory.connect(signer1).create(
AddressManager.address,
ethers.constants.HashZero,
ethers.constants.HashZero,
DUMMY_HASH
)
).to.not.be.reverted
})
})
})
})
import '../../../setup'
/* Internal Imports */
import { Lib_EIP155Tx_TEST_JSON } from '../../../data'
import { runJsonTest } from '../../../helpers'
// Currently running tests from here:
// https://github.com/ethereumjs/ethereumjs-tx/blob/master/test/ttTransactionTestEip155VitaliksTests.json
describe('Lib_EIP155Tx', () => {
describe('JSON tests', () => {
runJsonTest('TestLib_EIP155Tx', Lib_EIP155Tx_TEST_JSON)
})
})
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from 'hardhat'
import { Contract, Signer, constants } from 'ethers'
import { fromHexString, toHexString } from '@eth-optimism/core-utils'
// Leaving this here for now. If it's sufficiently useful we can throw it in core-utils.
const getHexSlice = (
input: Buffer | string,
start: number,
length: number
): string => {
return toHexString(fromHexString(input).slice(start, start + length))
}
describe('Lib_EthUtils', () => {
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let Lib_EthUtils: Contract
before(async () => {
Lib_EthUtils = await (
await ethers.getContractFactory('TestLib_EthUtils')
).deploy()
})
describe('getCode(address,uint256,uint256)', () => {
describe('when the contract does not exist', () => {
const address = constants.AddressZero
describe('when offset = 0', () => {
const offset = 0
it('should return length zero bytes', async () => {
const length = 100
expect(
await Lib_EthUtils['getCode(address,uint256,uint256)'](
address,
offset,
length
)
).to.equal('0x' + '00'.repeat(length))
})
})
describe('when offset > 0', () => {
const offset = 50
it('should return length zero bytes', async () => {
const length = 100
expect(
await Lib_EthUtils['getCode(address,uint256,uint256)'](
address,
offset,
length
)
).to.equal('0x' + '00'.repeat(length))
})
})
})
describe('when the account is an EOA', () => {
let address: string
before(async () => {
address = await signer.getAddress()
})
describe('when offset = 0', () => {
const offset = 0
it('should return length zero bytes', async () => {
const length = 100
expect(
await Lib_EthUtils['getCode(address,uint256,uint256)'](
address,
offset,
length
)
).to.equal('0x' + '00'.repeat(length))
})
})
describe('when offset > 0', () => {
const offset = 50
it('should return length zero bytes', async () => {
const length = 100
expect(
await Lib_EthUtils['getCode(address,uint256,uint256)'](
address,
offset,
length
)
).to.equal('0x' + '00'.repeat(length))
})
})
})
describe('when the contract exists', () => {
let address: string
let code: string
let codeLength: number
before(async () => {
address = Lib_EthUtils.address
code = await ethers.provider.getCode(address)
codeLength = fromHexString(code).length
})
describe('when offset = 0', () => {
const offset = 0
describe('when length = 0', () => {
const length = 0
it('should return empty', async () => {
expect(
await Lib_EthUtils['getCode(address,uint256,uint256)'](
address,
offset,
length
)
).to.equal('0x')
})
})
describe('when 0 < length < extcodesize(contract)', () => {
let length: number
before(async () => {
length = Math.floor(codeLength / 2)
})
it('should return N bytes from the start of code', async () => {
expect(
await Lib_EthUtils['getCode(address,uint256,uint256)'](
address,
offset,
length
)
).to.equal(getHexSlice(code, offset, length))
})
})
describe('when length = extcodesize(contract)', () => {
let length: number
before(async () => {
length = codeLength
})
it('should return the full contract code', async () => {
expect(
await Lib_EthUtils['getCode(address,uint256,uint256)'](
address,
offset,
length
)
).to.equal(code)
})
})
describe('when length > extcodesize(contract)', () => {
let length: number
before(async () => {
length = codeLength * 2
})
it('should return the full contract code padded to length with zero bytes', async () => {
expect(
await Lib_EthUtils['getCode(address,uint256,uint256)'](
address,
offset,
length
)
).to.equal(code + '00'.repeat(codeLength))
})
})
})
describe('when 0 < offset < extcodesize(contract)', () => {
let offset: number
before(async () => {
offset = Math.floor(codeLength / 2)
})
describe('when length = 0', () => {
const length = 0
it('should return empty', async () => {
expect(
await Lib_EthUtils['getCode(address,uint256,uint256)'](
address,
offset,
length
)
).to.equal('0x')
})
})
describe('when 0 < length < extcodesize(contract) - offset', () => {
let length: number
before(async () => {
length = Math.floor((codeLength - offset) / 2)
})
it('should return the selected bytes', async () => {
expect(
await Lib_EthUtils['getCode(address,uint256,uint256)'](
address,
offset,
length
)
).to.equal(getHexSlice(code, offset, length))
})
})
describe('when length = extcodesize(contract) - offset', () => {
let length: number
before(async () => {
length = codeLength - offset
})
it('should return the selected bytes', async () => {
expect(
await Lib_EthUtils['getCode(address,uint256,uint256)'](
address,
offset,
length
)
).to.equal(getHexSlice(code, offset, length))
})
})
describe('when length > extcodesize(contract) - offset', () => {
let length: number
let extraLength: number
before(async () => {
length = (codeLength - offset) * 2
extraLength = length - (codeLength - offset)
})
it('should return the selected bytes padded to length with zero bytes', async () => {
expect(
await Lib_EthUtils['getCode(address,uint256,uint256)'](
address,
offset,
length
)
).to.equal(
getHexSlice(code, offset, codeLength - offset) +
'00'.repeat(extraLength)
)
})
})
})
describe('offset >= extcodesize(contract)', () => {
let offset: number
before(async () => {
offset = codeLength * 2
})
describe('when length = 0', () => {
const length = 0
it('should return empty', async () => {
expect(
await Lib_EthUtils['getCode(address,uint256,uint256)'](
address,
offset,
length
)
).to.equal('0x')
})
})
describe('when length > 0', () => {
let length: number
before(async () => {
length = codeLength * 2
})
it('should return length zero bytes', async () => {
expect(
await Lib_EthUtils['getCode(address,uint256,uint256)'](
address,
offset,
length
)
).to.equal('0x' + '00'.repeat(length))
})
})
})
})
})
/* eslint-disable @typescript-eslint/no-empty-function */
describe('getCode(address)', () => {
describe('when the contract does not exist', () => {})
describe('when the account is an EOA', () => {})
describe('when the contract exists', () => {})
})
describe('getCodeSize', () => {
describe('when the contract does not exist', () => {})
describe('when the account is an EOA', () => {})
describe('when the contract exists', () => {})
})
describe('getCodeHash', () => {
describe('when the contract does not exist', () => {})
describe('when the account is an EOA', () => {})
describe('when the contract exists', () => {})
})
describe('createContract', () => {
describe('it should create the contract', () => {})
})
describe('getAddressForCREATE', () => {
describe('when the nonce is zero', () => {
describe('it should return the correct address', () => {})
})
describe('when the nonce is > 0', () => {
describe('it should return the correct address', () => {})
})
})
describe('getAddressForCREATE2', () => {
describe('when the bytecode is not empty', () => {
describe('when the salt is not zero', () => {
describe('it should return the correct address', () => {})
})
describe('when the salt is zero', () => {
describe('it should return the correct address', () => {})
})
})
describe('when the bytecode is empty', () => {
describe('when the salt is not zero', () => {
describe('it should return the correct address', () => {})
})
describe('when the salt is zero', () => {
describe('it should return the correct address', () => {})
})
})
})
/* eslint-enable @typescript-eslint/no-empty-function */
})
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from 'hardhat'
import { Signer, Contract } from 'ethers'
/* Internal Imports */
import { makeAddressManager } from '../../../helpers'
describe('mockOVM_BondManager', () => {
let sequencer: Signer
let nonSequencer: Signer
before(async () => {
;[sequencer, nonSequencer] = await ethers.getSigners()
})
let AddressManager: Contract
before(async () => {
AddressManager = await makeAddressManager()
})
let mockOVM_BondManager: Contract
before(async () => {
mockOVM_BondManager = await (
await ethers.getContractFactory('mockOVM_BondManager')
).deploy(AddressManager.address)
AddressManager.setAddress('OVM_Proposer', await sequencer.getAddress())
})
describe('isCollateralized', () => {
it('should return true for OVM_Proposer', async () => {
expect(
await mockOVM_BondManager.isCollateralized(await sequencer.getAddress())
).to.equal(true)
})
it('should return false for non-sequencer', async () => {
expect(
await mockOVM_BondManager.isCollateralized(
await nonSequencer.getAddress()
)
).to.equal(false)
})
})
})
...@@ -3,7 +3,6 @@ export { tests as Lib_RLPReader_TEST_JSON } from './json/libraries/rlp/Lib_RLPRe ...@@ -3,7 +3,6 @@ export { tests as Lib_RLPReader_TEST_JSON } from './json/libraries/rlp/Lib_RLPRe
export { tests as Lib_Bytes32Utils_TEST_JSON } from './json/libraries/utils/Lib_Bytes32Utils.test.json' export { tests as Lib_Bytes32Utils_TEST_JSON } from './json/libraries/utils/Lib_Bytes32Utils.test.json'
export { tests as Lib_BytesUtils_TEST_JSON } from './json/libraries/utils/Lib_BytesUtils.test.json' export { tests as Lib_BytesUtils_TEST_JSON } from './json/libraries/utils/Lib_BytesUtils.test.json'
export { tests as Lib_MerkleTrie_TEST_JSON } from './json/libraries/trie/Lib_MerkleTrie.test.json' export { tests as Lib_MerkleTrie_TEST_JSON } from './json/libraries/trie/Lib_MerkleTrie.test.json'
export { tests as Lib_EIP155Tx_TEST_JSON } from './json/libraries/codec/Lib_EIP155Tx.test.json'
export { tests as Lib_OVMCodec_TEST_JSON } from './json/libraries/codec/Lib_OVMCodec.test.json' export { tests as Lib_OVMCodec_TEST_JSON } from './json/libraries/codec/Lib_OVMCodec.test.json'
export { tests as CREATE2_TEST_JSON } from './json/create2.test.json' export { tests as CREATE2_TEST_JSON } from './json/create2.test.json'
export { tests as SAFETY_CHECKER_TEST_JSON } from './json/safety-checker.test.json' export { tests as SAFETY_CHECKER_TEST_JSON } from './json/safety-checker.test.json'
{
"tests": {
"decode": {
"vitalik test 0": {
"in": [
"0xf864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d",
1
],
"out": [
[
"0x00",
"0x04a817c800",
"0x5208",
"0x3535353535353535353535353535353535353535",
"0x00",
"0x",
"0x25",
"0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d",
"0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d",
"0x01",
"0x00",
false
]
]
},
"vitalik test 1": {
"in": [
"0xf867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10",
1
],
"out": [
[
"0x08",
"0x04a817c808",
"0x02e248",
"0x3535353535353535353535353535353535353535",
"0x0200",
"0x",
"0x25",
"0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12",
"0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10",
"0x01",
"0x00",
false
]
]
},
"vitalik test 2": {
"in": [
"0xf867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb",
1
],
"out": [
[
"0x09",
"0x04a817c809",
"0x033450",
"0x3535353535353535353535353535353535353535",
"0x02d9",
"0x",
"0x25",
"0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb",
"0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb",
"0x01",
"0x00",
false
]
]
},
"vitalik test 3": {
"in": [
"0xf864018504a817c80182a410943535353535353535353535353535353535353535018025a0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bcaa0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6",
1
],
"out": [
[
"0x01",
"0x04a817c801",
"0xa410",
"0x3535353535353535353535353535353535353535",
"0x01",
"0x",
"0x25",
"0x489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bca",
"0x489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6",
"0x01",
"0x00",
false
]
]
},
"vitalik test 4": {
"in": [
"0xf864028504a817c80282f618943535353535353535353535353535353535353535088025a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5",
1
],
"out": [
[
"0x02",
"0x04a817c802",
"0xf618",
"0x3535353535353535353535353535353535353535",
"0x08",
"0x",
"0x25",
"0x2d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5",
"0x2d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5",
"0x01",
"0x00",
false
]
]
},
"vitalik test 5": {
"in": [
"0xf865038504a817c803830148209435353535353535353535353535353535353535351b8025a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de",
1
],
"out": [
[
"0x03",
"0x04a817c803",
"0x014820",
"0x3535353535353535353535353535353535353535",
"0x1b",
"0x",
"0x25",
"0x2a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0",
"0x2a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de",
"0x01",
"0x00",
false
]
]
},
"vitalik test 6": {
"in": [
"0xf865048504a817c80483019a28943535353535353535353535353535353535353535408025a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060",
1
],
"out": [
[
"0x04",
"0x04a817c804",
"0x019a28",
"0x3535353535353535353535353535353535353535",
"0x40",
"0x",
"0x25",
"0x13600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063",
"0x13600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060",
"0x01",
"0x00",
false
]
]
},
"vitalik test 7": {
"in": [
"0xf865058504a817c8058301ec309435353535353535353535353535353535353535357d8025a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1",
1
],
"out": [
[
"0x05",
"0x04a817c805",
"0x01ec30",
"0x3535353535353535353535353535353535353535",
"0x7d",
"0x",
"0x25",
"0x4eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1",
"0x4eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1",
"0x01",
"0x00",
false
]
]
},
"vitalik test 8": {
"in": [
"0xf865058504a817c8058301ec309435353535353535353535353535353535353535357d8025a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1",
1
],
"out": [
[
"0x05",
"0x04a817c805",
"0x01ec30",
"0x3535353535353535353535353535353535353535",
"0x7d",
"0x",
"0x25",
"0x4eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1",
"0x4eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1",
"0x01",
"0x00",
false
]
]
},
"vitalik test 9": {
"in": [
"0xf866068504a817c80683023e3894353535353535353535353535353535353535353581d88025a06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2fa06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d",
1
],
"out": [
[
"0x06",
"0x04a817c806",
"0x023e38",
"0x3535353535353535353535353535353535353535",
"0xd8",
"0x",
"0x25",
"0x6455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2f",
"0x6455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d",
"0x01",
"0x00",
false
]
]
},
"vitalik test 10": {
"in": [
"0xf867078504a817c807830290409435353535353535353535353535353535353535358201578025a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021",
1
],
"out": [
[
"0x07",
"0x04a817c807",
"0x029040",
"0x3535353535353535353535353535353535353535",
"0x0157",
"0x",
"0x25",
"0x52f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021",
"0x52f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021",
"0x01",
"0x00",
false
]
]
}
},
"encode": {
"vitalik test 0": {
"in": [
[
"0x00",
"0x04a817c800",
"0x5208",
"0x3535353535353535353535353535353535353535",
"0x00",
"0x",
"0x25",
"0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d",
"0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d",
"0x01",
"0x00",
false
],
true
],
"out": [
"0xf864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"
]
},
"vitalik test 1": {
"in": [
[
"0x08",
"0x04a817c808",
"0x02e248",
"0x3535353535353535353535353535353535353535",
"0x0200",
"0x",
"0x25",
"0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12",
"0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10",
"0x01",
"0x00",
false
],
true
],
"out": [
"0xf867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10"
]
},
"vitalik test 2": {
"in": [
[
"0x09",
"0x04a817c809",
"0x033450",
"0x3535353535353535353535353535353535353535",
"0x02d9",
"0x",
"0x25",
"0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb",
"0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb",
"0x01",
"0x00",
false
],
true
],
"out": [
"0xf867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb"
]
},
"vitalik test 3": {
"in": [
[
"0x01",
"0x04a817c801",
"0xa410",
"0x3535353535353535353535353535353535353535",
"0x01",
"0x",
"0x25",
"0x489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bca",
"0x489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6",
"0x01",
"0x00",
false
],
true
],
"out": [
"0xf864018504a817c80182a410943535353535353535353535353535353535353535018025a0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bcaa0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6"
]
},
"vitalik test 4": {
"in": [
[
"0x02",
"0x04a817c802",
"0xf618",
"0x3535353535353535353535353535353535353535",
"0x08",
"0x",
"0x25",
"0x2d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5",
"0x2d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5",
"0x01",
"0x00",
false
],
true
],
"out": [
"0xf864028504a817c80282f618943535353535353535353535353535353535353535088025a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5"
]
},
"vitalik test 5": {
"in": [
[
"0x03",
"0x04a817c803",
"0x014820",
"0x3535353535353535353535353535353535353535",
"0x1b",
"0x",
"0x25",
"0x2a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0",
"0x2a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de",
"0x01",
"0x00",
false
],
true
],
"out": [
"0xf865038504a817c803830148209435353535353535353535353535353535353535351b8025a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de"
]
},
"vitalik test 6": {
"in": [
[
"0x04",
"0x04a817c804",
"0x019a28",
"0x3535353535353535353535353535353535353535",
"0x40",
"0x",
"0x25",
"0x13600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063",
"0x13600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060",
"0x01",
"0x00",
false
],
true
],
"out": [
"0xf865048504a817c80483019a28943535353535353535353535353535353535353535408025a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060"
]
},
"vitalik test 7": {
"in": [
[
"0x05",
"0x04a817c805",
"0x01ec30",
"0x3535353535353535353535353535353535353535",
"0x7d",
"0x",
"0x25",
"0x4eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1",
"0x4eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1",
"0x01",
"0x00",
false
],
true
],
"out": [
"0xf865058504a817c8058301ec309435353535353535353535353535353535353535357d8025a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1"
]
},
"vitalik test 8": {
"in": [
[
"0x05",
"0x04a817c805",
"0x01ec30",
"0x3535353535353535353535353535353535353535",
"0x7d",
"0x",
"0x25",
"0x4eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1",
"0x4eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1",
"0x01",
"0x00",
false
],
true
],
"out": [
"0xf865058504a817c8058301ec309435353535353535353535353535353535353535357d8025a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1"
]
},
"vitalik test 9": {
"in": [
[
"0x06",
"0x04a817c806",
"0x023e38",
"0x3535353535353535353535353535353535353535",
"0xd8",
"0x",
"0x25",
"0x6455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2f",
"0x6455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d",
"0x01",
"0x00",
false
],
true
],
"out": [
"0xf866068504a817c80683023e3894353535353535353535353535353535353535353581d88025a06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2fa06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d"
]
},
"vitalik test 10": {
"in": [
[
"0x07",
"0x04a817c807",
"0x029040",
"0x3535353535353535353535353535353535353535",
"0x0157",
"0x",
"0x25",
"0x52f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021",
"0x52f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021",
"0x01",
"0x00",
false
],
true
],
"out": [
"0xf867078504a817c807830290409435353535353535353535353535353535353535358201578025a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021"
]
}
},
"sender": {
"vitalik test 0": {
"in": [
[
"0x00",
"0x04a817c800",
"0x5208",
"0x3535353535353535353535353535353535353535",
"0x00",
"0x",
"0x25",
"0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d",
"0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d",
"0x01",
"0x00",
false
]
],
"out": [
"0xf0f6f18bca1b28cd68e4357452947e021241e9ce"
]
},
"vitalik test 1": {
"in": [
[
"0x08",
"0x04a817c808",
"0x02e248",
"0x3535353535353535353535353535353535353535",
"0x0200",
"0x",
"0x25",
"0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12",
"0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10",
"0x01",
"0x00",
false
]
],
"out": [
"0x9bddad43f934d313c2b79ca28a432dd2b7281029"
]
},
"vitalik test 2": {
"in": [
[
"0x09",
"0x04a817c809",
"0x033450",
"0x3535353535353535353535353535353535353535",
"0x02d9",
"0x",
"0x25",
"0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb",
"0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb",
"0x01",
"0x00",
false
]
],
"out": [
"0x3c24d7329e92f84f08556ceb6df1cdb0104ca49f"
]
},
"vitalik test 3": {
"in": [
[
"0x01",
"0x04a817c801",
"0xa410",
"0x3535353535353535353535353535353535353535",
"0x01",
"0x",
"0x25",
"0x489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bca",
"0x489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6",
"0x01",
"0x00",
false
]
],
"out": [
"0x23ef145a395ea3fa3deb533b8a9e1b4c6c25d112"
]
},
"vitalik test 4": {
"in": [
[
"0x02",
"0x04a817c802",
"0xf618",
"0x3535353535353535353535353535353535353535",
"0x08",
"0x",
"0x25",
"0x2d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5",
"0x2d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5",
"0x01",
"0x00",
false
]
],
"out": [
"0x2e485e0c23b4c3c542628a5f672eeab0ad4888be"
]
},
"vitalik test 5": {
"in": [
[
"0x03",
"0x04a817c803",
"0x014820",
"0x3535353535353535353535353535353535353535",
"0x1b",
"0x",
"0x25",
"0x2a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0",
"0x2a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de",
"0x01",
"0x00",
false
]
],
"out": [
"0x82a88539669a3fd524d669e858935de5e5410cf0"
]
},
"vitalik test 6": {
"in": [
[
"0x04",
"0x04a817c804",
"0x019a28",
"0x3535353535353535353535353535353535353535",
"0x40",
"0x",
"0x25",
"0x13600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063",
"0x13600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060",
"0x01",
"0x00",
false
]
],
"out": [
"0xf9358f2538fd5ccfeb848b64a96b743fcc930554"
]
},
"vitalik test 7": {
"in": [
[
"0x05",
"0x04a817c805",
"0x01ec30",
"0x3535353535353535353535353535353535353535",
"0x7d",
"0x",
"0x25",
"0x4eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1",
"0x4eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1",
"0x01",
"0x00",
false
]
],
"out": [
"0xa8f7aba377317440bc5b26198a363ad22af1f3a4"
]
},
"vitalik test 8": {
"in": [
[
"0x05",
"0x04a817c805",
"0x01ec30",
"0x3535353535353535353535353535353535353535",
"0x7d",
"0x",
"0x25",
"0x4eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1",
"0x4eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1",
"0x01",
"0x00",
false
]
],
"out": [
"0xa8f7aba377317440bc5b26198a363ad22af1f3a4"
]
},
"vitalik test 9": {
"in": [
[
"0x06",
"0x04a817c806",
"0x023e38",
"0x3535353535353535353535353535353535353535",
"0xd8",
"0x",
"0x25",
"0x6455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2f",
"0x6455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d",
"0x01",
"0x00",
false
]
],
"out": [
"0xf1f571dc362a0e5b2696b8e775f8491d3e50de35"
]
},
"vitalik test 10": {
"in": [
[
"0x07",
"0x04a817c807",
"0x029040",
"0x3535353535353535353535353535353535353535",
"0x0157",
"0x",
"0x25",
"0x52f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021",
"0x52f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021",
"0x01",
"0x00",
false
]
],
"out": [
"0xd37922162ab7cea97c97a87551ed02c9a38b7332"
]
}
}
}
}
export * from './test-runner'
export * from './test.types'
export * from './json-test-runner' export * from './json-test-runner'
import { expect } from '../../setup'
/* External Imports */
import { ethers } from 'hardhat'
import { Contract, BigNumber, ContractFactory } from 'ethers'
import { cloneDeep, merge } from 'lodash'
import { smoddit, smockit, ModifiableContract } from '@eth-optimism/smock'
/* Internal Imports */
import {
TestDefinition,
ParsedTestStep,
TestParameter,
TestStep,
TestStep_CALLType,
TestStep_Run,
isRevertFlagError,
isTestStep_SSTORE,
isTestStep_SLOAD,
isTestStep_CALLType,
isTestStep_CREATE,
isTestStep_CREATE2,
isTestStep_CREATEEOA,
isTestStep_Context,
isTestStep_evm,
isTestStep_Run,
isTestStep_EXTCODESIZE,
isTestStep_EXTCODEHASH,
isTestStep_EXTCODECOPY,
isTestStep_BALANCE,
isTestStep_REVERT,
isTestStep_CALL,
} from './test.types'
import { encodeRevertData, REVERT_FLAGS } from '../codec'
import {
OVM_TX_GAS_LIMIT,
RUN_OVM_TEST_GAS,
NON_NULL_BYTES32,
} from '../constants'
import { getStorageXOR } from '../'
import { UNSAFE_BYTECODE } from '../dummy'
import { getContractFactory, predeploys } from '../../../src'
export class ExecutionManagerTestRunner {
private snapshot: string
private contracts: {
OVM_SafetyChecker: Contract
OVM_StateManager: ModifiableContract
OVM_ExecutionManager: ModifiableContract
Helper_TestRunner: Contract
Factory__Helper_TestRunner_CREATE: ContractFactory
OVM_DeployerWhitelist: Contract
OVM_ProxyEOA: Contract
OVM_ETH: Contract
} = {
OVM_SafetyChecker: undefined,
OVM_StateManager: undefined,
OVM_ExecutionManager: undefined,
Helper_TestRunner: undefined,
Factory__Helper_TestRunner_CREATE: undefined,
OVM_DeployerWhitelist: undefined,
OVM_ProxyEOA: undefined,
OVM_ETH: undefined,
}
// Default pre-state with contract deployer whitelist NOT initialized.
private defaultPreState = {
StateManager: {
owner: '$OVM_EXECUTION_MANAGER',
accounts: {
[predeploys.OVM_DeployerWhitelist]: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_DEPLOYER_WHITELIST',
},
[predeploys.OVM_ETH]: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_ETH',
},
[predeploys.OVM_ProxyEOA]: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_PROXY_EOA',
},
},
contractStorage: {
[predeploys.OVM_DeployerWhitelist]: {
'0x0000000000000000000000000000000000000000000000000000000000000000':
{
getStorageXOR: true,
value: ethers.constants.HashZero,
},
},
},
verifiedContractStorage: {
[predeploys.OVM_DeployerWhitelist]: {
'0x0000000000000000000000000000000000000000000000000000000000000000':
true,
},
},
},
ExecutionManager: {
transactionRecord: {
ovmGasRefund: 0,
},
},
}
public run(test: TestDefinition) {
;(test.preState = merge(
cloneDeep(this.defaultPreState),
cloneDeep(test.preState)
// eslint-disable-next-line no-sequences
)),
(test.postState = test.postState || {})
describe(`OVM_ExecutionManager Test: ${test.name}`, () => {
test.subTests?.map((subTest) => {
this.run({
...subTest,
preState: merge(
cloneDeep(test.preState),
cloneDeep(subTest.preState)
),
postState: merge(
cloneDeep(test.postState),
cloneDeep(subTest.postState)
),
})
})
test.parameters?.map((parameter) => {
beforeEach(async () => {
await this.initContracts()
})
let replacedTest: TestDefinition
let replacedParameter: TestParameter
beforeEach(async () => {
replacedTest = this.setPlaceholderStrings(test)
replacedParameter = this.setPlaceholderStrings(parameter)
})
beforeEach(async () => {
await this.contracts.OVM_StateManager.smodify.put({
accounts: {
[this.contracts.Helper_TestRunner.address]: {
nonce: 0,
codeHash: NON_NULL_BYTES32,
ethAddress: this.contracts.Helper_TestRunner.address,
},
},
})
})
beforeEach(async () => {
await this.contracts.OVM_ExecutionManager.smodify.put(
replacedTest.preState.ExecutionManager
)
await this.contracts.OVM_StateManager.smodify.put(
replacedTest.preState.StateManager
)
})
afterEach(async () => {
expect(
await this.contracts.OVM_ExecutionManager.smodify.check(
replacedTest.postState.ExecutionManager
)
).to.equal(true)
expect(
await this.contracts.OVM_StateManager.smodify.check(
replacedTest.postState.StateManager
)
).to.equal(true)
})
let itfn: any = it
if (parameter.focus) {
itfn = it.only
} else if (parameter.skip) {
itfn = it.skip
}
itfn(`should execute: ${parameter.name}`, async () => {
try {
for (const step of replacedParameter.steps) {
await this.runTestStep(step)
}
} catch (err) {
if (parameter.expectInvalidStateAccess) {
expect(err.toString()).to.contain(
'VM Exception while processing transaction: revert'
)
} else {
throw err
}
}
})
})
})
}
private async initContracts() {
if (this.snapshot) {
await ethers.provider.send('evm_revert', [this.snapshot])
this.snapshot = await ethers.provider.send('evm_snapshot', [])
return
}
const AddressManager = await (
await ethers.getContractFactory('Lib_AddressManager')
).deploy()
const SafetyChecker = await (
await ethers.getContractFactory('OVM_SafetyChecker')
).deploy()
const MockSafetyChecker = await smockit(SafetyChecker)
MockSafetyChecker.smocked.isBytecodeSafe.will.return.with(
(bytecode: string) => {
return bytecode !== UNSAFE_BYTECODE
}
)
this.contracts.OVM_SafetyChecker = MockSafetyChecker
await AddressManager.setAddress(
'OVM_SafetyChecker',
this.contracts.OVM_SafetyChecker.address
)
const DeployerWhitelist = await getContractFactory(
'OVM_DeployerWhitelist',
AddressManager.signer
).deploy()
this.contracts.OVM_DeployerWhitelist = DeployerWhitelist
const OvmEth = await getContractFactory(
'OVM_ETH',
AddressManager.signer
).deploy()
this.contracts.OVM_ETH = OvmEth
this.contracts.OVM_ProxyEOA = await getContractFactory(
'OVM_ProxyEOA',
AddressManager.signer
).deploy()
this.contracts.OVM_ExecutionManager = await (
await smoddit('OVM_ExecutionManager')
).deploy(
AddressManager.address,
{
minTransactionGasLimit: 0,
maxTransactionGasLimit: 1_000_000_000,
maxGasPerQueuePerEpoch: 1_000_000_000_000,
secondsPerEpoch: 600,
},
{
ovmCHAINID: 420,
}
)
this.contracts.OVM_StateManager = await (
await smoddit('OVM_StateManager')
).deploy(await this.contracts.OVM_ExecutionManager.signer.getAddress())
await this.contracts.OVM_StateManager.setExecutionManager(
this.contracts.OVM_ExecutionManager.address
)
this.contracts.Helper_TestRunner = await (
await ethers.getContractFactory('Helper_TestRunner')
).deploy()
this.contracts.Factory__Helper_TestRunner_CREATE =
await ethers.getContractFactory('Helper_TestRunner_CREATE')
this.snapshot = await ethers.provider.send('evm_snapshot', [])
}
public static getDummyAddress(placeholder: string): string {
return '0x' + (placeholder.split('$DUMMY_OVM_ADDRESS_')[1] + '0').repeat(20)
}
private setPlaceholderStrings(obj: any) {
const getReplacementString = (kv: string): string => {
if (kv === '$OVM_EXECUTION_MANAGER') {
return this.contracts.OVM_ExecutionManager.address
} else if (kv === '$OVM_STATE_MANAGER') {
return this.contracts.OVM_StateManager.address
} else if (kv === '$OVM_SAFETY_CHECKER') {
return this.contracts.OVM_SafetyChecker.address
} else if (kv === '$OVM_CALL_HELPER') {
return this.contracts.Helper_TestRunner.address
} else if (kv === '$OVM_DEPLOYER_WHITELIST') {
return this.contracts.OVM_DeployerWhitelist.address
} else if (kv === '$OVM_ETH') {
return this.contracts.OVM_ETH.address
} else if (kv === '$OVM_PROXY_EOA') {
return this.contracts.OVM_ProxyEOA.address
} else if (kv.startsWith('$DUMMY_OVM_ADDRESS_')) {
return ExecutionManagerTestRunner.getDummyAddress(kv)
} else {
return kv
}
}
let ret: any = cloneDeep(obj)
if (Array.isArray(ret)) {
ret = ret.map((element: any) => {
return this.setPlaceholderStrings(element)
})
} else if (typeof ret === 'object' && ret !== null) {
if (ret.getStorageXOR) {
// Special case allowing us to set prestate with an object which will be
// padded to 32 bytes and XORd with STORAGE_XOR_VALUE
return getStorageXOR(
ethers.utils.hexZeroPad(getReplacementString(ret.value), 32)
)
}
for (const key of Object.keys(ret)) {
const replacedKey = getReplacementString(key)
if (replacedKey !== key) {
ret[replacedKey] = ret[key]
delete ret[key]
}
ret[replacedKey] = this.setPlaceholderStrings(ret[replacedKey])
}
} else if (typeof ret === 'string') {
ret = getReplacementString(ret)
}
return ret
}
private async runTestStep(step: TestStep | TestStep_Run) {
if (isTestStep_Run(step)) {
let calldata: string
if (step.functionParams.data) {
calldata = step.functionParams.data
} else {
const runStep: TestStep_CALLType = {
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: ExecutionManagerTestRunner.getDummyAddress(
'$DUMMY_OVM_ADDRESS_1'
),
subSteps: step.functionParams.subSteps,
},
expectedReturnStatus: true,
}
calldata = this.encodeFunctionData(runStep)
}
const toRun = this.contracts.OVM_ExecutionManager.run(
{
timestamp: step.functionParams.timestamp,
blockNumber: 0,
l1QueueOrigin: step.functionParams.queueOrigin,
l1TxOrigin: step.functionParams.origin,
entrypoint: step.functionParams.entrypoint,
gasLimit: step.functionParams.gasLimit,
data: calldata,
},
this.contracts.OVM_StateManager.address,
{ gasLimit: step.suppliedGas || RUN_OVM_TEST_GAS }
)
if (!!step.expectedRevertValue) {
await expect(toRun).to.be.revertedWith(step.expectedRevertValue)
} else {
await toRun
}
} else {
await this.contracts.OVM_ExecutionManager[
'ovmCALL(uint256,address,uint256,bytes)'
](
OVM_TX_GAS_LIMIT,
ExecutionManagerTestRunner.getDummyAddress('$DUMMY_OVM_ADDRESS_1'),
0,
this.contracts.Helper_TestRunner.interface.encodeFunctionData(
'runSingleTestStep',
[this.parseTestStep(step)]
),
{ gasLimit: RUN_OVM_TEST_GAS }
)
}
}
private parseTestStep(step: TestStep): ParsedTestStep {
return {
functionName: step.functionName,
functionData: this.encodeFunctionData(step),
expectedReturnStatus: this.getReturnStatus(step),
expectedReturnData: this.encodeExpectedReturnData(step),
onlyValidateFlag: this.shouldStepOnlyValidateFlag(step),
}
}
private shouldStepOnlyValidateFlag(step: TestStep): boolean {
if (!!(step as any).expectedReturnValue) {
if (!!((step as any).expectedReturnValue as any).onlyValidateFlag) {
return true
}
}
return false
}
private getReturnStatus(step: TestStep): boolean {
if (isTestStep_evm(step)) {
return false
} else if (isTestStep_Context(step)) {
return true
} else if (isTestStep_CALLType(step)) {
if (
isRevertFlagError(step.expectedReturnValue) &&
(step.expectedReturnValue.flag === REVERT_FLAGS.INVALID_STATE_ACCESS ||
step.expectedReturnValue.flag === REVERT_FLAGS.STATIC_VIOLATION ||
step.expectedReturnValue.flag === REVERT_FLAGS.CREATOR_NOT_ALLOWED)
) {
return step.expectedReturnStatus
} else {
return true
}
} else {
return step.expectedReturnStatus
}
}
private encodeFunctionData(step: TestStep): string {
if (isTestStep_evm(step)) {
if (isRevertFlagError(step.returnData)) {
return encodeRevertData(
step.returnData.flag,
step.returnData.data,
step.returnData.nuisanceGasLeft,
step.returnData.ovmGasRefund
)
} else {
return step.returnData || '0x'
}
}
let functionParams: any[] = []
if (
isTestStep_SSTORE(step) ||
isTestStep_SLOAD(step) ||
isTestStep_EXTCODESIZE(step) ||
isTestStep_EXTCODEHASH(step) ||
isTestStep_EXTCODECOPY(step) ||
isTestStep_BALANCE(step) ||
isTestStep_CREATEEOA(step)
) {
functionParams = Object.values(step.functionParams)
} else if (isTestStep_CALLType(step)) {
const innnerCalldata =
step.functionParams.calldata ||
this.contracts.Helper_TestRunner.interface.encodeFunctionData(
'runMultipleTestSteps',
[
step.functionParams.subSteps.map((subStep) => {
return this.parseTestStep(subStep)
}),
]
)
// only ovmCALL accepts a value parameter.
if (isTestStep_CALL(step)) {
functionParams = [
step.functionParams.gasLimit,
step.functionParams.target,
step.functionParams.value || 0,
innnerCalldata,
]
} else {
functionParams = [
step.functionParams.gasLimit,
step.functionParams.target,
innnerCalldata,
]
}
} else if (isTestStep_CREATE(step)) {
functionParams = [
this.contracts.Factory__Helper_TestRunner_CREATE.getDeployTransaction(
step.functionParams.bytecode || '0x',
step.functionParams.subSteps?.map((subStep) => {
return this.parseTestStep(subStep)
}) || []
).data,
]
} else if (isTestStep_CREATE2(step)) {
functionParams = [
this.contracts.Factory__Helper_TestRunner_CREATE.getDeployTransaction(
step.functionParams.bytecode || '0x',
step.functionParams.subSteps?.map((subStep) => {
return this.parseTestStep(subStep)
}) || []
).data,
step.functionParams.salt,
]
} else if (isTestStep_REVERT(step)) {
functionParams = [step.revertData || '0x']
}
// legacy ovmCALL causes multiple matching functions without the full signature
let functionName
if (step.functionName === 'ovmCALL') {
functionName = 'ovmCALL(uint256,address,uint256,bytes)'
} else {
functionName = step.functionName
}
return this.contracts.OVM_ExecutionManager.interface.encodeFunctionData(
functionName,
functionParams
)
}
private encodeExpectedReturnData(step: TestStep): string {
if (isTestStep_evm(step)) {
return '0x'
}
if (isRevertFlagError(step.expectedReturnValue)) {
return encodeRevertData(
step.expectedReturnValue.flag,
step.expectedReturnValue.data,
step.expectedReturnValue.nuisanceGasLeft,
step.expectedReturnValue.ovmGasRefund
)
}
if (isTestStep_REVERT(step)) {
return step.expectedReturnValue || '0x'
}
let returnData: any[] = []
if (isTestStep_CALLType(step)) {
if (step.expectedReturnValue === '0x00') {
return step.expectedReturnValue
} else if (
typeof step.expectedReturnValue === 'string' ||
step.expectedReturnValue === undefined
) {
returnData = [
step.expectedReturnStatus,
step.expectedReturnValue || '0x',
]
} else {
returnData = [
step.expectedReturnValue.ovmSuccess,
step.expectedReturnValue.returnData,
]
}
} else if (BigNumber.isBigNumber(step.expectedReturnValue)) {
returnData = [step.expectedReturnValue.toHexString()]
} else if (step.expectedReturnValue !== undefined) {
if (step.expectedReturnValue === '0x00') {
return step.expectedReturnValue
} else {
returnData = [step.expectedReturnValue]
}
}
if (isTestStep_CREATE(step) || isTestStep_CREATE2(step)) {
if (!isRevertFlagError(step.expectedReturnValue)) {
if (typeof step.expectedReturnValue === 'string') {
returnData = [step.expectedReturnValue, '0x']
} else {
returnData = [
step.expectedReturnValue.address,
step.expectedReturnValue.revertData || '0x',
]
}
}
}
// legacy ovmCALL causes multiple matching functions without the full signature
let functionName
if (step.functionName === 'ovmCALL') {
functionName = 'ovmCALL(uint256,address,uint256,bytes)'
} else {
functionName = step.functionName
}
return this.contracts.OVM_ExecutionManager.interface.encodeFunctionResult(
functionName,
returnData
)
}
}
/* External Imports */
import { BigNumber } from 'ethers'
export type ContextOpcode =
| 'ovmCALLER'
| 'ovmNUMBER'
| 'ovmADDRESS'
| 'ovmL1TXORIGIN'
| 'ovmL1QUEUEORIGIN'
| 'ovmTIMESTAMP'
| 'ovmGASLIMIT'
| 'ovmCHAINID'
| 'ovmGETNONCE'
| 'ovmCALLVALUE'
type CallOpcode = 'ovmCALL' | 'ovmSTATICCALL' | 'ovmDELEGATECALL'
type RevertFlagError = {
flag: number
nuisanceGasLeft?: number
ovmGasRefund?: number
data?: string
onlyValidateFlag?: boolean
}
interface TestStep_evm {
functionName: 'evmRETURN' | 'evmREVERT' | 'evmINVALID'
returnData?: string | RevertFlagError
}
interface TestStep_Context {
functionName: ContextOpcode
expectedReturnValue: string | number | BigNumber
}
interface TestStep_REVERT {
functionName: 'ovmREVERT'
revertData?: string
expectedReturnStatus: boolean
expectedReturnValue?: string | RevertFlagError
}
interface TestStep_EXTCODESIZE {
functionName: 'ovmEXTCODESIZE'
functionParams: {
address: string
}
expectedReturnStatus: boolean
expectedReturnValue: number | RevertFlagError
}
interface TestStep_EXTCODEHASH {
functionName: 'ovmEXTCODEHASH'
functionParams: {
address: string
}
expectedReturnStatus: boolean
expectedReturnValue: string | RevertFlagError
}
interface TestStep_EXTCODECOPY {
functionName: 'ovmEXTCODECOPY'
functionParams: {
address: string
offset: number
length: number
}
expectedReturnStatus: boolean
expectedReturnValue: string | RevertFlagError
}
interface TestStep_BALANCE {
functionName: 'ovmBALANCE'
functionParams: {
address: string
}
expectedReturnStatus: boolean
expectedReturnValue: number | RevertFlagError
}
interface TestStep_SSTORE {
functionName: 'ovmSSTORE'
functionParams: {
key: string
value: string
}
expectedReturnStatus: boolean
expectedReturnValue?: RevertFlagError
}
interface TestStep_SLOAD {
functionName: 'ovmSLOAD'
functionParams: {
key: string
}
expectedReturnStatus: boolean
expectedReturnValue: string | RevertFlagError
}
interface TestStep_INCREMENTNONCE {
functionName: 'ovmINCREMENTNONCE'
expectedReturnStatus: boolean
expectedReturnValue?: RevertFlagError
}
export interface TestStep_CALLType {
functionName: CallOpcode
functionParams: {
gasLimit: number | BigNumber
target: string
value?: number | BigNumber
calldata?: string
subSteps?: TestStep[]
}
expectedReturnStatus: boolean
expectedReturnValue?:
| string
| RevertFlagError
| { ovmSuccess: boolean; returnData: string }
}
interface TestStep_CREATE {
functionName: 'ovmCREATE'
functionParams: {
bytecode?: string
subSteps?: TestStep[]
}
expectedReturnStatus: boolean
expectedReturnValue:
| string
| {
address: string
revertData: string
}
| RevertFlagError
}
interface TestStep_CREATE2 {
functionName: 'ovmCREATE2'
functionParams: {
salt: string
bytecode?: string
subSteps?: TestStep[]
}
expectedReturnStatus: boolean
expectedReturnValue:
| string
| {
address: string
revertData: string
}
| RevertFlagError
}
interface TestStep_CREATEEOA {
functionName: 'ovmCREATEEOA'
functionParams: {
_messageHash: string
_v: number
_r: string
_s: string
}
expectedReturnStatus: boolean
expectedReturnValue: string | RevertFlagError
}
export interface TestStep_Run {
functionName: 'run'
suppliedGas?: number
functionParams: {
timestamp: number
queueOrigin: number
entrypoint: string
origin: string
msgSender: string
gasLimit: number
data?: string
subSteps?: TestStep[]
}
expectedRevertValue?: string
}
export type TestStep =
| TestStep_Context
| TestStep_SSTORE
| TestStep_SLOAD
| TestStep_INCREMENTNONCE
| TestStep_CALLType
| TestStep_CREATE
| TestStep_CREATE2
| TestStep_CREATEEOA
| TestStep_EXTCODESIZE
| TestStep_EXTCODEHASH
| TestStep_EXTCODECOPY
| TestStep_BALANCE
| TestStep_REVERT
| TestStep_evm
export interface ParsedTestStep {
functionName: string
functionData: string
expectedReturnStatus: boolean
expectedReturnData: string
onlyValidateFlag: boolean
}
export const isRevertFlagError = (
expectedReturnValue: any
): expectedReturnValue is RevertFlagError => {
return (
typeof expectedReturnValue === 'object' &&
expectedReturnValue !== null &&
expectedReturnValue.flag !== undefined
)
}
export const isTestStep_evm = (step: TestStep): step is TestStep_evm => {
return ['evmRETURN', 'evmREVERT', 'evmINVALID'].includes(step.functionName)
}
export const isTestStep_Context = (
step: TestStep
): step is TestStep_Context => {
return [
'ovmCALLER',
'ovmNUMBER',
'ovmADDRESS',
'ovmL1TXORIGIN',
'ovmTIMESTAMP',
'ovmGASLIMIT',
'ovmCHAINID',
'ovmL1QUEUEORIGIN',
'ovmGETNONCE',
'ovmCALLVALUE',
].includes(step.functionName)
}
export const isTestStep_SSTORE = (step: TestStep): step is TestStep_SSTORE => {
return step.functionName === 'ovmSSTORE'
}
export const isTestStep_SLOAD = (step: TestStep): step is TestStep_SLOAD => {
return step.functionName === 'ovmSLOAD'
}
export const isTestStep_INCREMENTNONCE = (
step: TestStep
): step is TestStep_INCREMENTNONCE => {
return step.functionName === 'ovmINCREMENTNONCE'
}
export const isTestStep_EXTCODESIZE = (
step: TestStep
): step is TestStep_EXTCODESIZE => {
return step.functionName === 'ovmEXTCODESIZE'
}
export const isTestStep_EXTCODEHASH = (
step: TestStep
): step is TestStep_EXTCODEHASH => {
return step.functionName === 'ovmEXTCODEHASH'
}
export const isTestStep_EXTCODECOPY = (
step: TestStep
): step is TestStep_EXTCODECOPY => {
return step.functionName === 'ovmEXTCODECOPY'
}
export const isTestStep_BALANCE = (
step: TestStep
): step is TestStep_BALANCE => {
return step.functionName === 'ovmBALANCE'
}
export const isTestStep_REVERT = (step: TestStep): step is TestStep_REVERT => {
return step.functionName === 'ovmREVERT'
}
export const isTestStep_CALLType = (
step: TestStep
): step is TestStep_CALLType => {
return ['ovmCALL', 'ovmSTATICCALL', 'ovmDELEGATECALL'].includes(
step.functionName
)
}
export const isTestStep_CALL = (step: TestStep): boolean => {
return step.functionName === 'ovmCALL'
}
export const isTestStep_CREATE = (step: TestStep): step is TestStep_CREATE => {
return step.functionName === 'ovmCREATE'
}
export const isTestStep_CREATEEOA = (
step: TestStep
): step is TestStep_CREATEEOA => {
return step.functionName === 'ovmCREATEEOA'
}
export const isTestStep_CREATE2 = (
step: TestStep
): step is TestStep_CREATE2 => {
return step.functionName === 'ovmCREATE2'
}
export const isTestStep_Run = (
step: TestStep | TestStep_Run
): step is TestStep_Run => {
return step.functionName === 'run'
}
interface TestState {
ExecutionManager: any
StateManager: any
}
export interface TestParameter {
name: string
steps: Array<TestStep | TestStep_Run>
expectInvalidStateAccess?: boolean
focus?: boolean
skip?: boolean
}
export interface TestDefinition {
name: string
focus?: boolean
preState?: Partial<TestState>
postState?: Partial<TestState>
parameters?: TestParameter[]
subTests?: TestDefinition[]
}
...@@ -104,7 +104,7 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet< ...@@ -104,7 +104,7 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet<
nextTxPointer nextTxPointer
) )
const decoded = maybeDecodeSequencerBatchTransaction( const decoded = decodeSequencerBatchTransaction(
sequencerTransaction, sequencerTransaction,
l2ChainId l2ChainId
) )
...@@ -116,12 +116,12 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet< ...@@ -116,12 +116,12 @@ export const handleEventsSequencerBatchAppended: EventHandlerSet<
batchIndex: extraData.batchIndex.toNumber(), batchIndex: extraData.batchIndex.toNumber(),
blockNumber: BigNumber.from(context.blockNumber).toNumber(), blockNumber: BigNumber.from(context.blockNumber).toNumber(),
timestamp: BigNumber.from(context.timestamp).toNumber(), timestamp: BigNumber.from(context.timestamp).toNumber(),
gasLimit: BigNumber.from(extraData.gasLimit).toString(), gasLimit: BigNumber.from(0).toString(),
target: SEQUENCER_ENTRYPOINT_ADDRESS, target: constants.AddressZero,
origin: null, origin: null,
data: toHexString(sequencerTransaction), data: toHexString(sequencerTransaction),
queueOrigin: 'sequencer', queueOrigin: 'sequencer',
value: decoded ? decoded.value : '0x0', value: decoded.value,
queueIndex: null, queueIndex: null,
decoded, decoded,
confirmed: true, confirmed: true,
...@@ -249,27 +249,23 @@ const parseSequencerBatchTransaction = ( ...@@ -249,27 +249,23 @@ const parseSequencerBatchTransaction = (
return calldata.slice(offset + 3, offset + 3 + transactionLength) return calldata.slice(offset + 3, offset + 3 + transactionLength)
} }
const maybeDecodeSequencerBatchTransaction = ( const decodeSequencerBatchTransaction = (
transaction: Buffer, transaction: Buffer,
l2ChainId: number l2ChainId: number
): DecodedSequencerBatchTransaction | null => { ): DecodedSequencerBatchTransaction => {
try { const decodedTx = ethers.utils.parseTransaction(transaction)
const decodedTx = ethers.utils.parseTransaction(transaction)
return { return {
nonce: BigNumber.from(decodedTx.nonce).toString(), nonce: BigNumber.from(decodedTx.nonce).toString(),
gasPrice: BigNumber.from(decodedTx.gasPrice).toString(), gasPrice: BigNumber.from(decodedTx.gasPrice).toString(),
gasLimit: BigNumber.from(decodedTx.gasLimit).toString(), gasLimit: BigNumber.from(decodedTx.gasLimit).toString(),
value: toRpcHexString(decodedTx.value), value: toRpcHexString(decodedTx.value),
target: toHexString(decodedTx.to), // Maybe null this out for creations? target: decodedTx.to ? toHexString(decodedTx.to) : null,
data: toHexString(decodedTx.data), data: toHexString(decodedTx.data),
sig: { sig: {
v: parseSignatureVParam(decodedTx.v, l2ChainId), v: parseSignatureVParam(decodedTx.v, l2ChainId),
r: toHexString(decodedTx.r), r: toHexString(decodedTx.r),
s: toHexString(decodedTx.s), s: toHexString(decodedTx.s),
}, },
}
} catch (err) {
return null
} }
} }
/* Imports: External */ /* Imports: External */
import { BigNumber, constants, ethers } from 'ethers' import { BigNumber, ethers } from 'ethers'
import { serialize } from '@ethersproject/transactions' import { serialize } from '@ethersproject/transactions'
/* Imports: Internal */ /* Imports: Internal */
...@@ -11,7 +11,6 @@ import { ...@@ -11,7 +11,6 @@ import {
} from '../../../types' } from '../../../types'
import { import {
padHexString, padHexString,
SEQUENCER_ENTRYPOINT_ADDRESS,
SEQUENCER_GAS_LIMIT, SEQUENCER_GAS_LIMIT,
parseSignatureVParam, parseSignatureVParam,
} from '../../../utils' } from '../../../utils'
...@@ -58,8 +57,8 @@ export const handleSequencerBlock = { ...@@ -58,8 +57,8 @@ export const handleSequencerBlock = {
transactionEntry = { transactionEntry = {
...transactionEntry, ...transactionEntry,
gasLimit: `${SEQUENCER_GAS_LIMIT}`, // ? gasLimit: BigNumber.from(0).toString(),
target: SEQUENCER_ENTRYPOINT_ADDRESS, target: ethers.constants.AddressZero,
origin: null, origin: null,
data: serialize( data: serialize(
{ {
......
...@@ -32,7 +32,6 @@ export interface OptimismContracts { ...@@ -32,7 +32,6 @@ export interface OptimismContracts {
Lib_AddressManager: Contract Lib_AddressManager: Contract
OVM_StateCommitmentChain: Contract OVM_StateCommitmentChain: Contract
OVM_CanonicalTransactionChain: Contract OVM_CanonicalTransactionChain: Contract
OVM_ExecutionManager: Contract
} }
export const loadOptimismContracts = async ( export const loadOptimismContracts = async (
...@@ -55,10 +54,6 @@ export const loadOptimismContracts = async ( ...@@ -55,10 +54,6 @@ export const loadOptimismContracts = async (
name: 'OVM_CanonicalTransactionChain', name: 'OVM_CanonicalTransactionChain',
interface: 'iOVM_CanonicalTransactionChain', interface: 'iOVM_CanonicalTransactionChain',
}, },
{
name: 'OVM_ExecutionManager',
interface: 'iOVM_ExecutionManager',
},
] ]
const contracts = {} const contracts = {}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment