Commit b02010d1 authored by Kelvin Fichter's avatar Kelvin Fichter

Added basic tests for message passing contracts

parents dcfd3df9 669ed02c
#!/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,12 +4,12 @@ import * as path from 'path' ...@@ -4,12 +4,12 @@ import * as path from 'path'
import * as mkdirp from 'mkdirp' import * as mkdirp from 'mkdirp'
/* Internal Imports */ /* Internal Imports */
import { makeStateDump } from '../src' import { makeStateDump } from '../src/contract-dumps'
;(async () => { ;(async () => {
const outdir = path.resolve(__dirname, '../build/dumps') const outdir = path.resolve(__dirname, '../build/dumps')
const outfile = path.join(outdir, 'state-dump.latest.json') const outfile = path.join(outdir, 'state-dump.latest.json')
mkdirp.sync(outdir) mkdirp.sync(outdir)
const dump = await makeStateDump() const dump = await makeStateDump()
fs.writeFileSync(outfile, JSON.stringify(dump)) fs.writeFileSync(outfile, JSON.stringify(dump, null, 4))
})() })()
...@@ -67,7 +67,6 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount { ...@@ -67,7 +67,6 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
decodedTx.nonce == Lib_SafeExecutionManagerWrapper.safeGETNONCE(ovmExecutionManager) + 1, decodedTx.nonce == Lib_SafeExecutionManagerWrapper.safeGETNONCE(ovmExecutionManager) + 1,
"Transaction nonce does not match the expected nonce." "Transaction nonce does not match the expected nonce."
); );
Lib_SafeExecutionManagerWrapper.safeSETNONCE(ovmExecutionManager, decodedTx.nonce);
// Contract creations are signalled by sending a transaction to the zero address. // Contract creations are signalled by sending a transaction to the zero address.
if (decodedTx.target == address(0)) { if (decodedTx.target == address(0)) {
...@@ -81,6 +80,11 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount { ...@@ -81,6 +80,11 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
// initialization. Always return `true` for our success value here. // initialization. Always return `true` for our success value here.
return (true, abi.encode(created)); return (true, abi.encode(created));
} else { } 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_SafeExecutionManagerWrapper.safeSETNONCE(ovmExecutionManager, decodedTx.nonce);
return Lib_SafeExecutionManagerWrapper.safeCALL( return Lib_SafeExecutionManagerWrapper.safeCALL(
ovmExecutionManager, ovmExecutionManager,
decodedTx.gasLimit, decodedTx.gasLimit,
......
...@@ -16,7 +16,6 @@ contract OVM_BaseCrossDomainMessenger is iOVM_BaseCrossDomainMessenger { ...@@ -16,7 +16,6 @@ contract OVM_BaseCrossDomainMessenger is iOVM_BaseCrossDomainMessenger {
mapping (bytes32 => bool) public receivedMessages; mapping (bytes32 => bool) public receivedMessages;
mapping (bytes32 => bool) public sentMessages; mapping (bytes32 => bool) public sentMessages;
address public targetMessengerAddress;
uint256 public messageNonce; uint256 public messageNonce;
address public xDomainMessageSender; address public xDomainMessageSender;
...@@ -25,22 +24,6 @@ contract OVM_BaseCrossDomainMessenger is iOVM_BaseCrossDomainMessenger { ...@@ -25,22 +24,6 @@ contract OVM_BaseCrossDomainMessenger is iOVM_BaseCrossDomainMessenger {
* Public Functions * * Public Functions *
********************/ ********************/
/**
* Sets the target messenger address.
* @dev Currently, this function is public and therefore allows anyone to modify the target
* messenger for a given xdomain messenger contract. Obviously this shouldn't be allowed,
* but we still need to determine an adequate mechanism for updating this address.
* @param _targetMessengerAddress New messenger address.
*/
function setTargetMessengerAddress(
address _targetMessengerAddress
)
override
public
{
targetMessengerAddress = _targetMessengerAddress;
}
/** /**
* Sends a cross domain message to the target messenger. * Sends a cross domain message to the target messenger.
* @param _target Target contract address. * @param _target Target contract address.
...@@ -92,8 +75,8 @@ contract OVM_BaseCrossDomainMessenger is iOVM_BaseCrossDomainMessenger { ...@@ -92,8 +75,8 @@ contract OVM_BaseCrossDomainMessenger is iOVM_BaseCrossDomainMessenger {
bytes memory bytes memory
) )
{ {
return abi.encodeWithSelector( return abi.encodeWithSignature(
bytes4(keccak256(bytes("relayMessage(address,address,bytes,uint256)"))), "relayMessage(address,address,bytes,uint256)",
_target, _target,
_sender, _sender,
_message, _message,
......
...@@ -16,6 +16,9 @@ import { iOVM_StateCommitmentChain } from "../../iOVM/chain/iOVM_StateCommitment ...@@ -16,6 +16,9 @@ import { iOVM_StateCommitmentChain } from "../../iOVM/chain/iOVM_StateCommitment
/* Contract Imports */ /* Contract Imports */
import { OVM_BaseCrossDomainMessenger } from "./OVM_BaseCrossDomainMessenger.sol"; import { OVM_BaseCrossDomainMessenger } from "./OVM_BaseCrossDomainMessenger.sol";
/* Logging Imports */
import { console } from "@nomiclabs/buidler/console.sol";
/** /**
* @title OVM_L1CrossDomainMessenger * @title OVM_L1CrossDomainMessenger
*/ */
...@@ -163,15 +166,13 @@ contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, OVM_BaseCros ...@@ -163,15 +166,13 @@ contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, OVM_BaseCros
bool bool
) )
{ {
// TODO: We *must* verify that the batch timestamp is sufficiently old. return (
// However, this requires that we first add timestamps to state batches ovmStateCommitmentChain.insideFraudProofWindow(_proof.stateRootBatchHeader) == false
// and account for that change in various tests. Change of that size is && ovmStateCommitmentChain.verifyElement(
// out of scope for this ticket, so "TODO" for now.
return ovmStateCommitmentChain.verifyElement(
abi.encodePacked(_proof.stateRoot), abi.encodePacked(_proof.stateRoot),
_proof.stateRootBatchHeader, _proof.stateRootBatchHeader,
_proof.stateRootProof _proof.stateRootProof
)
); );
} }
...@@ -219,7 +220,7 @@ contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, OVM_BaseCros ...@@ -219,7 +220,7 @@ contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, OVM_BaseCros
abi.encodePacked(storageKey), abi.encodePacked(storageKey),
abi.encodePacked(uint256(1)), abi.encodePacked(uint256(1)),
_proof.storageTrieWitness, _proof.storageTrieWitness,
_proof.stateRoot account.storageRoot
); );
} }
...@@ -236,7 +237,7 @@ contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, OVM_BaseCros ...@@ -236,7 +237,7 @@ contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, OVM_BaseCros
internal internal
{ {
ovmCanonicalTransactionChain.enqueue( ovmCanonicalTransactionChain.enqueue(
targetMessengerAddress, resolve("OVM_L2CrossDomainMessenger"),
_gasLimit, _gasLimit,
_message _message
); );
......
...@@ -13,6 +13,9 @@ import { iOVM_L2ToL1MessagePasser } from "../../iOVM/precompiles/iOVM_L2ToL1Mess ...@@ -13,6 +13,9 @@ import { iOVM_L2ToL1MessagePasser } from "../../iOVM/precompiles/iOVM_L2ToL1Mess
/* Contract Imports */ /* Contract Imports */
import { OVM_BaseCrossDomainMessenger } from "./OVM_BaseCrossDomainMessenger.sol"; import { OVM_BaseCrossDomainMessenger } from "./OVM_BaseCrossDomainMessenger.sol";
/* Logging Imports */
import { console } from "@nomiclabs/buidler/console.sol";
/** /**
* @title OVM_L2CrossDomainMessenger * @title OVM_L2CrossDomainMessenger
*/ */
...@@ -104,7 +107,7 @@ contract OVM_L2CrossDomainMessenger is iOVM_L2CrossDomainMessenger, OVM_BaseCros ...@@ -104,7 +107,7 @@ contract OVM_L2CrossDomainMessenger is iOVM_L2CrossDomainMessenger, OVM_BaseCros
) )
{ {
return ( return (
ovmL1MessageSender.getL1MessageSender() == targetMessengerAddress ovmL1MessageSender.getL1MessageSender() == resolve("OVM_L1CrossDomainMessenger")
); );
} }
......
...@@ -19,6 +19,13 @@ import { OVM_BaseChain } from "./OVM_BaseChain.sol"; ...@@ -19,6 +19,13 @@ import { OVM_BaseChain } from "./OVM_BaseChain.sol";
*/ */
contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, Lib_AddressResolver { contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, Lib_AddressResolver {
/*************
* Constants *
*************/
uint256 constant public FRAUD_PROOF_WINDOW = 7 days;
/******************************************* /*******************************************
* Contract Variables: Contract References * * Contract Variables: Contract References *
*******************************************/ *******************************************/
...@@ -75,7 +82,9 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, L ...@@ -75,7 +82,9 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, L
_appendBatch( _appendBatch(
elements, elements,
abi.encodePacked(block.timestamp) abi.encode(
block.timestamp
)
); );
} }
...@@ -94,6 +103,39 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, L ...@@ -94,6 +103,39 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, L
"State batches can only be deleted by the OVM_FraudVerifier." "State batches can only be deleted by the OVM_FraudVerifier."
); );
require(
insideFraudProofWindow(_batchHeader),
"State batches can only be deleted within the fraud proof window."
);
_deleteBatch(_batchHeader); _deleteBatch(_batchHeader);
} }
/**********************************
* Public Functions: Batch Status *
**********************************/
function insideFraudProofWindow(
Lib_OVMCodec.ChainBatchHeader memory _batchHeader
)
override
public
view
returns (
bool _inside
)
{
uint256 timestamp = abi.decode(
_batchHeader.extraData,
(uint256)
);
require(
timestamp != 0,
"Batch header timestamp cannot be zero"
);
return timestamp + FRAUD_PROOF_WINDOW > block.timestamp;
}
} }
...@@ -27,8 +27,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -27,8 +27,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
* External Contract References * * External Contract References *
********************************/ ********************************/
iOVM_SafetyChecker public ovmSafetyChecker; iOVM_SafetyChecker internal ovmSafetyChecker;
iOVM_StateManager public ovmStateManager; iOVM_StateManager internal ovmStateManager;
/******************************* /*******************************
...@@ -63,11 +63,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -63,11 +63,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
* @param _libAddressManager Address of the Address Manager. * @param _libAddressManager Address of the Address Manager.
*/ */
constructor( constructor(
address _libAddressManager address _libAddressManager,
GasMeterConfig memory _gasMeterConfig,
GlobalContext memory _globalContext
) )
Lib_AddressResolver(_libAddressManager) Lib_AddressResolver(_libAddressManager)
{ {
ovmSafetyChecker = iOVM_SafetyChecker(resolve("OVM_SafetyChecker")); ovmSafetyChecker = iOVM_SafetyChecker(resolve("OVM_SafetyChecker"));
gasMeterConfig = _gasMeterConfig;
globalContext = _globalContext;
} }
...@@ -120,10 +124,18 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -120,10 +124,18 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
override override
public public
{ {
// Store our OVM_StateManager instance (significantly easier than attempting to pass the address // Store our OVM_StateManager instance (significantly easier than attempting to pass the
// around in calldata). // address around in calldata).
ovmStateManager = iOVM_StateManager(_ovmStateManager); 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(
msg.sender == ovmStateManager.owner(),
"Only the owner of the ovmStateManager can call this function"
);
// Check whether we need to start a new epoch, do so if necessary. // Check whether we need to start a new epoch, do so if necessary.
_checkNeedsNewEpoch(_transaction.timestamp); _checkNeedsNewEpoch(_transaction.timestamp);
...@@ -150,6 +162,9 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -150,6 +162,9 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
// Wipe the execution context. // Wipe the execution context.
_resetContext(); _resetContext();
// Reset the ovmStateManager.
ovmStateManager = iOVM_StateManager(address(0));
} }
...@@ -494,7 +509,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -494,7 +509,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
MessageContext memory nextMessageContext = messageContext; MessageContext memory nextMessageContext = messageContext;
nextMessageContext.ovmCALLER = nextMessageContext.ovmADDRESS; nextMessageContext.ovmCALLER = nextMessageContext.ovmADDRESS;
nextMessageContext.ovmADDRESS = _address; nextMessageContext.ovmADDRESS = _address;
nextMessageContext.isCreation = false;
bool isStaticEntrypoint = false; bool isStaticEntrypoint = false;
return _callContract( return _callContract(
...@@ -532,7 +546,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -532,7 +546,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
nextMessageContext.ovmCALLER = nextMessageContext.ovmADDRESS; nextMessageContext.ovmCALLER = nextMessageContext.ovmADDRESS;
nextMessageContext.ovmADDRESS = _address; nextMessageContext.ovmADDRESS = _address;
nextMessageContext.isStatic = true; nextMessageContext.isStatic = true;
nextMessageContext.isCreation = false;
bool isStaticEntrypoint = true; bool isStaticEntrypoint = true;
return _callContract( return _callContract(
...@@ -567,7 +580,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -567,7 +580,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
{ {
// DELEGATECALL does not change anything about the message context. // DELEGATECALL does not change anything about the message context.
MessageContext memory nextMessageContext = messageContext; MessageContext memory nextMessageContext = messageContext;
nextMessageContext.isCreation = false;
bool isStaticEntrypoint = false; bool isStaticEntrypoint = false;
return _callContract( return _callContract(
...@@ -753,19 +765,12 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -753,19 +765,12 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
// We always need to initialize the contract with the default account values. // We always need to initialize the contract with the default account values.
_initPendingAccount(_address); _initPendingAccount(_address);
// We're going into a contract creation, so we need to set this flag to get the correct
// revert behavior.
messageContext.isCreation = true;
// Actually deploy the contract and retrieve its address. This step is hiding a lot of // Actually deploy the contract and retrieve its address. This step is hiding a lot of
// complexity because we need to ensure that contract creation *never* reverts by itself. // complexity because we need to ensure that contract creation *never* reverts by itself.
// We cover this partially by storing a revert flag and returning (instead of reverting) // We cover this partially by storing a revert flag and returning (instead of reverting)
// when we know that we're inside a contract's creation code. // when we know that we're inside a contract's creation code.
address ethAddress = Lib_EthUtils.createContract(_bytecode); address ethAddress = Lib_EthUtils.createContract(_bytecode);
// Now reset this flag so we go back to normal revert behavior.
messageContext.isCreation = false;
// Contract creation returns the zero address when it fails, which should only be possible // Contract creation returns the zero address when it fails, which should only be possible
// if the user intentionally runs out of gas. However, we might still have a bit of gas // if the user intentionally runs out of gas. However, we might still have a bit of gas
// left over since contract calls can only be passed 63/64ths of total gas, so we need to // left over since contract calls can only be passed 63/64ths of total gas, so we need to
...@@ -818,7 +823,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -818,7 +823,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
) )
{ {
// We always update the nonce of the creating account, even if the creation fails. // We always update the nonce of the creating account, even if the creation fails.
_setAccountNonce(ovmADDRESS(), 1); _setAccountNonce(ovmADDRESS(), _getAccountNonce(ovmADDRESS()) + 1);
// We're stepping into a CREATE or CREATE2, so we need to update ADDRESS to point // 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. // to the contract's associated address and CALLER to point to the previous ADDRESS.
...@@ -1394,7 +1399,12 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -1394,7 +1399,12 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
// *single* byte, something the OVM_ExecutionManager will not return in any other case. // *single* byte, something the OVM_ExecutionManager will not return in any other case.
// We're thereby allowed to communicate failure without allowing contracts to trick us into // We're thereby allowed to communicate failure without allowing contracts to trick us into
// thinking there was a failure. // thinking there was a failure.
if (messageContext.isCreation) { bool isCreation;
assembly {
isCreation := eq(extcodesize(caller()), 0)
}
if (isCreation) {
messageRecord.revertFlag = _flag; messageRecord.revertFlag = _flag;
assembly { assembly {
...@@ -1550,7 +1560,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -1550,7 +1560,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
_getGasMetadata(cumulativeGasKey) _getGasMetadata(cumulativeGasKey)
- _getGasMetadata(prevEpochGasKey) - _getGasMetadata(prevEpochGasKey)
+ _gasLimit + _gasLimit
) > gasMeterConfig.maxGasPerQueuePerEpoch ) < gasMeterConfig.maxGasPerQueuePerEpoch
); );
} }
...@@ -1650,11 +1660,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -1650,11 +1660,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
if (_prevMessageContext.isStatic != _nextMessageContext.isStatic) { if (_prevMessageContext.isStatic != _nextMessageContext.isStatic) {
messageContext.isStatic = _nextMessageContext.isStatic; messageContext.isStatic = _nextMessageContext.isStatic;
} }
// Avoid unnecessary the SSTORE.
if (_prevMessageContext.isCreation != _nextMessageContext.isCreation) {
messageContext.isCreation = _nextMessageContext.isCreation;
}
} }
/** /**
...@@ -1672,6 +1677,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -1672,6 +1677,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
transactionContext.ovmL1QUEUEORIGIN = _transaction.l1QueueOrigin; transactionContext.ovmL1QUEUEORIGIN = _transaction.l1QueueOrigin;
transactionContext.ovmL1TXORIGIN = _transaction.l1Txorigin; transactionContext.ovmL1TXORIGIN = _transaction.l1Txorigin;
transactionContext.ovmGASLIMIT = gasMeterConfig.maxGasPerQueuePerEpoch; transactionContext.ovmGASLIMIT = gasMeterConfig.maxGasPerQueuePerEpoch;
messageRecord.nuisanceGasLeft = _getNuisanceGasLimit(_transaction.gasLimit);
} }
/** /**
......
...@@ -12,22 +12,124 @@ contract OVM_SafetyChecker is iOVM_SafetyChecker { ...@@ -12,22 +12,124 @@ contract OVM_SafetyChecker is iOVM_SafetyChecker {
/******************** /********************
* Public Functions * * Public Functions *
********************/ ********************/
/** /**
* Checks that a given bytecode string is considered safe. * Returns whether or not all of the provided bytecode is safe.
* @param _bytecode Bytecode string to check. * @param _bytecode The bytecode to safety check.
* @return _safe Whether or not the bytecode is safe. * @return `true` if the bytecode is safe, `false` otherwise.
*/ */
function isBytecodeSafe( function isBytecodeSafe(
bytes memory _bytecode bytes memory _bytecode
) )
override override
public external
view view
returns ( returns (bool)
bool _safe
)
{ {
// 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
uint256 opcodeGateMask = ~uint256(0xffffffffffffffffffffffe000000000fffffffff070ffff9c0ffffec000f001);
// Halting opcodes
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;
// 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)
}
// + 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
if ((1 << opNum) & opcodePushMask == 0) _pc += (opNum - 0x5f); // opNum-0x5f = PUSHed bytes (PUSH1 is 0x60)
} 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)
// 32 - 4 bytes = 28 bytes = 224 bits
secondOps := shr(224, 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 0x1d ADD EQ JUMPI RETURNDATASIZE PUSH1 0x00 DUP1 RETURNDATACOPY PUSH1 0x00 REVERT JUMPDEST PUSH1 0x01 PUSH1 0x00 RETURN JUMPDEST
} else if (firstOps == 0x336000905af158601d0157586012013d600114573d6000803e3d6000fd5b6001 && secondOps == 0x6000f35b) {
_pc += 36;
} else {
return false;
}
continue;
} else {
// encountered a non-whitelisted opcode!
return false;
}
}
_pc++;
} while (_pc < codeLength);
return true; return true;
} }
} }
...@@ -25,8 +25,8 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -25,8 +25,8 @@ contract OVM_StateManager is iOVM_StateManager {
* Contract Variables: Contract References * * Contract Variables: Contract References *
*******************************************/ *******************************************/
address public owner; address override public owner;
address public ovmExecutionManager; address override public ovmExecutionManager;
/**************************************** /****************************************
......
...@@ -304,6 +304,7 @@ contract OVM_StateTransitioner is iOVM_StateTransitioner, Lib_AddressResolver { ...@@ -304,6 +304,7 @@ contract OVM_StateTransitioner is iOVM_StateTransitioner, Lib_AddressResolver {
) )
override override
public public
onlyDuringPhase(TransitionPhase.PRE_EXECUTION)
{ {
require( require(
Lib_OVMCodec.hashTransaction(_transaction) == transactionHash, Lib_OVMCodec.hashTransaction(_transaction) == transactionHash,
......
...@@ -11,14 +11,6 @@ interface iOVM_BaseCrossDomainMessenger { ...@@ -11,14 +11,6 @@ interface iOVM_BaseCrossDomainMessenger {
* Public Functions * * Public Functions *
********************/ ********************/
/**
* Sets the target messenger address.
* @param _targetMessengerAddress New messenger address.
*/
function setTargetMessengerAddress(
address _targetMessengerAddress
) external;
/** /**
* Sends a cross domain message to the target messenger. * Sends a cross domain message to the target messenger.
* @param _target Target contract address. * @param _target Target contract address.
......
...@@ -19,4 +19,10 @@ interface iOVM_StateCommitmentChain is iOVM_BaseChain { ...@@ -19,4 +19,10 @@ interface iOVM_StateCommitmentChain is iOVM_BaseChain {
function appendStateBatch(bytes32[] calldata _batch) external; function appendStateBatch(bytes32[] calldata _batch) external;
function deleteStateBatch(Lib_OVMCodec.ChainBatchHeader memory _batchHeader) external; function deleteStateBatch(Lib_OVMCodec.ChainBatchHeader memory _batchHeader) external;
/**********************************
* Public Functions: Batch Status *
**********************************/
function insideFraudProofWindow(Lib_OVMCodec.ChainBatchHeader memory _batchHeader) external view returns (bool _inside);
} }
...@@ -62,7 +62,6 @@ interface iOVM_ExecutionManager { ...@@ -62,7 +62,6 @@ interface iOVM_ExecutionManager {
address ovmCALLER; address ovmCALLER;
address ovmADDRESS; address ovmADDRESS;
bool isStatic; bool isStatic;
bool isCreation;
} }
struct MessageRecord { struct MessageRecord {
......
...@@ -10,5 +10,5 @@ interface iOVM_SafetyChecker { ...@@ -10,5 +10,5 @@ interface iOVM_SafetyChecker {
* Public Functions * * Public Functions *
********************/ ********************/
function isBytecodeSafe(bytes memory _bytecode) external view returns (bool _safe); function isBytecodeSafe(bytes memory _bytecode) external view returns (bool);
} }
...@@ -26,6 +26,8 @@ interface iOVM_StateManager { ...@@ -26,6 +26,8 @@ interface iOVM_StateManager {
* Public Functions: Setup * * Public Functions: Setup *
***************************/ ***************************/
function owner() external view returns (address _owner);
function ovmExecutionManager() external view returns (address _ovmExecutionManager);
function setExecutionManager(address _ovmExecutionManager) external; function setExecutionManager(address _ovmExecutionManager) external;
......
// SPDX-License-Identifier: UNLICENSED // SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0; pragma solidity ^0.7.0;
/* Contract Imports */
import { Ownable } from "./Lib_Ownable.sol";
/** /**
* @title Lib_AddressManager * @title Lib_AddressManager
*/ */
contract Lib_AddressManager { contract Lib_AddressManager is Ownable {
/******************************************* /*******************************************
* Contract Variables: Internal Accounting * * Contract Variables: Internal Accounting *
...@@ -22,6 +25,7 @@ contract Lib_AddressManager { ...@@ -22,6 +25,7 @@ contract Lib_AddressManager {
address _address address _address
) )
public public
onlyOwner
{ {
addresses[_getNameHash(_name)] = _address; addresses[_getNameHash(_name)] = _address;
} }
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @title Ownable
* @dev Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol
*/
abstract contract Ownable {
/*************
* Variables *
*************/
address public owner;
/**********
* Events *
**********/
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/***************
* Constructor *
***************/
constructor()
internal
{
owner = msg.sender;
emit OwnershipTransferred(address(0), owner);
}
/**********************
* Function Modifiers *
**********************/
modifier onlyOwner() {
require(
owner == msg.sender,
"Ownable: caller is not the owner"
);
_;
}
/********************
* Public Functions *
********************/
function renounceOwnership()
public
virtual
onlyOwner
{
emit OwnershipTransferred(owner, address(0));
owner = address(0);
}
function transferOwnership(address _newOwner)
public
virtual
onlyOwner
{
require(
_newOwner != address(0),
"Ownable: new owner cannot be the zero address"
);
emit OwnershipTransferred(owner, _newOwner);
owner = _newOwner;
}
}
...@@ -389,6 +389,10 @@ library Lib_RLPReader { ...@@ -389,6 +389,10 @@ library Lib_RLPReader {
address address
) )
{ {
if (_in.length == 1) {
return address(0);
}
require( require(
_in.length == 21, _in.length == 21,
"Invalid RLP address value." "Invalid RLP address value."
......
...@@ -6,8 +6,6 @@ import { Lib_BytesUtils } from "../utils/Lib_BytesUtils.sol"; ...@@ -6,8 +6,6 @@ import { Lib_BytesUtils } from "../utils/Lib_BytesUtils.sol";
import { Lib_RLPReader } from "../rlp/Lib_RLPReader.sol"; import { Lib_RLPReader } from "../rlp/Lib_RLPReader.sol";
import { Lib_RLPWriter } from "../rlp/Lib_RLPWriter.sol"; import { Lib_RLPWriter } from "../rlp/Lib_RLPWriter.sol";
import { console } from "@nomiclabs/buidler/console.sol";
/** /**
* @title Lib_MerkleTrie * @title Lib_MerkleTrie
*/ */
......
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_ECDSAContractAccount } from "../../iOVM/accounts/iOVM_ECDSAContractAccount.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_ECDSAUtils } from "../../libraries/utils/Lib_ECDSAUtils.sol";
import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol";
/**
* @title mockOVM_ECDSAContractAccount
*/
contract mockOVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
/********************
* Public Functions *
********************/
/**
* Executes a signed transaction.
* @param _transaction Signed EOA transaction.
* @param _signatureType Hashing scheme used for the transaction (e.g., ETH signed message).
* @param _v Signature `v` parameter.
* @param _r Signature `r` parameter.
* @param _s Signature `s` parameter.
* @return _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call.
*/
function execute(
bytes memory _transaction,
Lib_OVMCodec.EOASignatureType _signatureType,
uint8 _v,
bytes32 _r,
bytes32 _s
)
override
public
returns (
bool _success,
bytes memory _returndata
)
{
address ovmExecutionManager = msg.sender;
// Skip signature validation in this mock.
Lib_OVMCodec.EOATransaction memory decodedTx = Lib_OVMCodec.decodeEOATransaction(_transaction);
// Contract creations are signalled by sending a transaction to the zero address.
if (decodedTx.target == address(0)) {
address created = Lib_SafeExecutionManagerWrapper.safeCREATE(
ovmExecutionManager,
decodedTx.gasLimit,
decodedTx.data
);
// EVM doesn't tell us whether a contract creation failed, even if it reverted during
// initialization. Always return `true` for our success value here.
return (true, abi.encode(created));
} else {
return Lib_SafeExecutionManagerWrapper.safeCALL(
ovmExecutionManager,
decodedTx.gasLimit,
decodedTx.target,
decodedTx.data
);
}
}
}
...@@ -29,6 +29,7 @@ contract mockOVM_CrossDomainMessenger is OVM_BaseCrossDomainMessenger { ...@@ -29,6 +29,7 @@ contract mockOVM_CrossDomainMessenger is OVM_BaseCrossDomainMessenger {
**********************/ **********************/
ReceivedMessage[] internal fullReceivedMessages; ReceivedMessage[] internal fullReceivedMessages;
address internal targetMessengerAddress;
uint256 internal lastRelayedMessage; uint256 internal lastRelayedMessage;
uint256 internal delay; uint256 internal delay;
...@@ -51,6 +52,21 @@ contract mockOVM_CrossDomainMessenger is OVM_BaseCrossDomainMessenger { ...@@ -51,6 +52,21 @@ contract mockOVM_CrossDomainMessenger is OVM_BaseCrossDomainMessenger {
* Public Functions * * Public Functions *
********************/ ********************/
/**
* Sets the target messenger address.
* @dev Currently, this function is public and therefore allows anyone to modify the target
* messenger for a given xdomain messenger contract. Obviously this shouldn't be allowed,
* but we still need to determine an adequate mechanism for updating this address.
* @param _targetMessengerAddress New messenger address.
*/
function setTargetMessengerAddress(
address _targetMessengerAddress
)
public
{
targetMessengerAddress = _targetMessengerAddress;
}
/** /**
* Sends a message to another mock xdomain messenger. * Sends a message to another mock xdomain messenger.
* @param _target Target for the message. * @param _target Target for the message.
......
{ {
"name": "optimism", "name": "@eth-optimism/contracts",
"version": "1.0.0", "version": "0.0.2-alpha.1",
"main": "index.js", "main": "build/src/index.js",
"files": [
"build/**/*.js",
"build/contracts/*",
"build/dumps/*json",
"build/artifacts/*json"
],
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"build": "yarn run build:contracts && yarn run build:typescript && yarn run build:copy", "build": "yarn run build:contracts && yarn run build:typescript && yarn run build:copy && yarn run build:dump",
"build:typescript": "tsc -p .", "build:typescript": "tsc -p .",
"build:contracts": "buidler compile", "build:contracts": "buidler compile",
"build:dump": "ts-node \"bin/take-dump.ts\"", "build:dump": "ts-node \"bin/take-dump.ts\"",
...@@ -17,6 +23,9 @@ ...@@ -17,6 +23,9 @@
"lint:fix:typescript": "prettier --config prettier-config.json --write \"buidler.config.ts\" \"{src,test}/**/*.ts\"", "lint:fix:typescript": "prettier --config prettier-config.json --write \"buidler.config.ts\" \"{src,test}/**/*.ts\"",
"clean": "rm -rf ./artifacts ./build ./cache" "clean": "rm -rf ./artifacts ./build ./cache"
}, },
"dependencies": {
"ethers": "5.0.0"
},
"devDependencies": { "devDependencies": {
"@eth-optimism/smock": "^0.0.2", "@eth-optimism/smock": "^0.0.2",
"@nomiclabs/buidler": "^1.4.4", "@nomiclabs/buidler": "^1.4.4",
...@@ -31,11 +40,10 @@ ...@@ -31,11 +40,10 @@
"chai": "^4.2.0", "chai": "^4.2.0",
"copyfiles": "^2.3.0", "copyfiles": "^2.3.0",
"ethereum-waffle": "3.0.0", "ethereum-waffle": "3.0.0",
"ethers": "5.0.0",
"fs-extra": "^9.0.1", "fs-extra": "^9.0.1",
"ganache-core": "^2.12.1", "ganache-core": "^2.12.1",
"lodash": "^4.17.20", "lodash": "^4.17.20",
"merkle-patricia-tree": "git+https://github.com/kfichter/merkle-patricia-tree", "merkle-patricia-tree": "^4.0.0",
"mkdirp": "^1.0.4", "mkdirp": "^1.0.4",
"mocha": "^8.1.1", "mocha": "^8.1.1",
"prettier": "^2.1.2", "prettier": "^2.1.2",
......
...@@ -12,6 +12,9 @@ export interface RollupDeployConfig { ...@@ -12,6 +12,9 @@ export interface RollupDeployConfig {
maxGasPerQueuePerEpoch: number maxGasPerQueuePerEpoch: number
secondsPerEpoch: number secondsPerEpoch: number
} }
ovmGlobalContext: {
ovmCHAINID: number
}
transactionChainConfig: { transactionChainConfig: {
sequencer: string | Signer sequencer: string | Signer
forceInclusionPeriodSeconds: number forceInclusionPeriodSeconds: number
...@@ -20,6 +23,7 @@ export interface RollupDeployConfig { ...@@ -20,6 +23,7 @@ export interface RollupDeployConfig {
owner: string | Signer owner: string | Signer
allowArbitraryContractDeployment: boolean allowArbitraryContractDeployment: boolean
} }
dependencies?: string[]
} }
export interface ContractDeployParameters { export interface ContractDeployParameters {
...@@ -64,9 +68,29 @@ export const makeContractDeployConfig = async ( ...@@ -64,9 +68,29 @@ export const makeContractDeployConfig = async (
factory: getContractFactory('OVM_StateCommitmentChain'), factory: getContractFactory('OVM_StateCommitmentChain'),
params: [AddressManager.address], params: [AddressManager.address],
}, },
OVM_DeployerWhitelist: {
factory: getContractFactory('OVM_DeployerWhitelist'),
params: [],
},
OVM_L1MessageSender: {
factory: getContractFactory('OVM_L1MessageSender'),
params: [],
},
OVM_L2ToL1MessagePasser: {
factory: getContractFactory('OVM_L2ToL1MessagePasser'),
params: [],
},
OVM_SafetyChecker: {
factory: getContractFactory('OVM_SafetyChecker'),
params: [],
},
OVM_ExecutionManager: { OVM_ExecutionManager: {
factory: getContractFactory('OVM_ExecutionManager'), factory: getContractFactory('OVM_ExecutionManager'),
params: [AddressManager.address], params: [
AddressManager.address,
config.ovmGasMeteringConfig,
config.ovmGlobalContext,
],
}, },
OVM_StateManager: { OVM_StateManager: {
factory: getContractFactory('OVM_StateManager'), factory: getContractFactory('OVM_StateManager'),
...@@ -91,5 +115,11 @@ export const makeContractDeployConfig = async ( ...@@ -91,5 +115,11 @@ export const makeContractDeployConfig = async (
OVM_StateTransitionerFactory: { OVM_StateTransitionerFactory: {
factory: getContractFactory('OVM_StateTransitionerFactory'), factory: getContractFactory('OVM_StateTransitionerFactory'),
}, },
OVM_ECDSAContractAccount: {
factory: getContractFactory('OVM_ECDSAContractAccount'),
},
mockOVM_ECDSAContractAccount: {
factory: getContractFactory('mockOVM_ECDSAContractAccount'),
},
} }
} }
...@@ -38,20 +38,32 @@ export const deploy = async ( ...@@ -38,20 +38,32 @@ export const deploy = async (
for (const [name, contractDeployParameters] of Object.entries( for (const [name, contractDeployParameters] of Object.entries(
contractDeployConfig contractDeployConfig
)) { )) {
if (config.dependencies && !config.dependencies.includes(name)) {
continue
}
const SimpleProxy = await Factory__SimpleProxy.deploy() const SimpleProxy = await Factory__SimpleProxy.deploy()
await AddressManager.setAddress(name, SimpleProxy.address) await AddressManager.setAddress(name, SimpleProxy.address)
contracts[`Proxy__${name}`] = SimpleProxy
try { try {
contracts[name] = await contractDeployParameters.factory contracts[name] = await contractDeployParameters.factory
.connect(config.deploymentSigner) .connect(config.deploymentSigner)
.deploy(...contractDeployParameters.params) .deploy(...(contractDeployParameters.params || []))
await SimpleProxy.setTarget(contracts[name].address) await SimpleProxy.setTarget(contracts[name].address)
} catch (err) { } catch (err) {
failedDeployments.push(name) failedDeployments.push(name)
} }
} }
for (const contractDeployParameters of Object.values(contractDeployConfig)) { for (const [name, contractDeployParameters] of Object.entries(
contractDeployConfig
)) {
if (config.dependencies && !config.dependencies.includes(name)) {
continue
}
if (contractDeployParameters.afterDeploy) { if (contractDeployParameters.afterDeploy) {
await contractDeployParameters.afterDeploy(contracts) await contractDeployParameters.afterDeploy(contracts)
} }
......
...@@ -2,57 +2,29 @@ ...@@ -2,57 +2,29 @@
import * as path from 'path' import * as path from 'path'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import * as Ganache from 'ganache-core' import * as Ganache from 'ganache-core'
import { keccak256 } from 'ethers/lib/utils'
/* Internal Imports */ /* Internal Imports */
import { deploy, RollupDeployConfig } from './contract-deployment' import { deploy, RollupDeployConfig } from './contract-deployment'
import { fromHexString, toHexString, remove0x } from '../test/helpers/utils'
import { getContractDefinition } from './contract-defs' import { getContractDefinition } from './contract-defs'
import { keccak256 } from 'ethers/lib/utils'
type Accounts = Array<{
originalAddress: string
address: string
code: string
}>
interface StorageDump { interface StorageDump {
[key: string]: string [key: string]: string
} }
export interface StateDump { export interface StateDump {
contracts: {
ovmExecutionManager: string
ovmStateManager: string
}
accounts: { accounts: {
[address: string]: { [name: string]: {
balance: number address: string
nonce: number
code: string code: string
codeHash: string
storage: StorageDump storage: StorageDump
abi: any
} }
} }
} }
/**
* Finds the addresses of all accounts changed in the state.
* @param cStateManager Instance of the callback-based internal vm StateManager.
* @returns Array of changed addresses.
*/
const getChangedAccounts = async (cStateManager: any): Promise<string[]> => {
return new Promise<string[]>((resolve, reject) => {
const accounts: string[] = []
const stream = cStateManager._trie.createReadStream()
stream.on('data', (val: any) => {
accounts.push(val.key.toString('hex'))
})
stream.on('end', () => {
resolve(accounts)
})
})
}
/** /**
* Generates a storage dump for a given address. * Generates a storage dump for a given address.
* @param cStateManager Instance of the callback-based internal vm StateManager. * @param cStateManager Instance of the callback-based internal vm StateManager.
...@@ -90,15 +62,23 @@ const getStorageDump = async ( ...@@ -90,15 +62,23 @@ const getStorageDump = async (
*/ */
const sanitizeStorageDump = ( const sanitizeStorageDump = (
storageDump: StorageDump, storageDump: StorageDump,
accounts: Accounts accounts: Array<{
originalAddress: string
deadAddress: string
}>
): StorageDump => { ): StorageDump => {
for (const account of accounts) {
account.originalAddress = remove0x(account.originalAddress).toLowerCase()
account.deadAddress = remove0x(account.deadAddress).toLowerCase()
}
for (const [key, value] of Object.entries(storageDump)) { for (const [key, value] of Object.entries(storageDump)) {
let parsedKey = key let parsedKey = key
let parsedValue = value let parsedValue = value
for (const account of accounts) { for (const account of accounts) {
const re = new RegExp(`${account.originalAddress}`, 'g') const re = new RegExp(`${account.originalAddress}`, 'g')
parsedValue = parsedValue.replace(re, account.address) parsedValue = parsedValue.replace(re, account.deadAddress)
parsedKey = parsedKey.replace(re, account.address) parsedKey = parsedKey.replace(re, account.deadAddress)
} }
if (parsedKey !== key) { if (parsedKey !== key) {
...@@ -135,6 +115,9 @@ export const makeStateDump = async (): Promise<any> => { ...@@ -135,6 +115,9 @@ export const makeStateDump = async (): Promise<any> => {
maxGasPerQueuePerEpoch: 1_000_000_000_000, maxGasPerQueuePerEpoch: 1_000_000_000_000,
secondsPerEpoch: 600, secondsPerEpoch: 600,
}, },
ovmGlobalContext: {
ovmCHAINID: 420,
},
transactionChainConfig: { transactionChainConfig: {
sequencer: signer, sequencer: signer,
forceInclusionPeriodSeconds: 600, forceInclusionPeriodSeconds: 600,
...@@ -143,94 +126,76 @@ export const makeStateDump = async (): Promise<any> => { ...@@ -143,94 +126,76 @@ export const makeStateDump = async (): Promise<any> => {
owner: signer, owner: signer,
allowArbitraryContractDeployment: true, allowArbitraryContractDeployment: true,
}, },
dependencies: [
'Lib_AddressManager',
'OVM_DeployerWhitelist',
'OVM_L1MessageSender',
'OVM_L2ToL1MessagePasser',
'OVM_L2CrossDomainMessenger',
'OVM_SafetyChecker',
'OVM_ExecutionManager',
'OVM_StateManager',
'mockOVM_ECDSAContractAccount',
],
}
const precompiles = {
OVM_L2ToL1MessagePasser: '0x4200000000000000000000000000000000000000',
OVM_L1MessageSender: '0x4200000000000000000000000000000000000001',
OVM_DeployerWhitelist: '0x4200000000000000000000000000000000000002',
} }
const deploymentResult = await deploy(config) const deploymentResult = await deploy(config)
deploymentResult.contracts['Lib_AddressManager'] =
deploymentResult.AddressManager
if (deploymentResult.failedDeployments.length > 0) {
throw new Error(
`Could not generate state dump, deploy failed for: ${deploymentResult.failedDeployments}`
)
}
const pStateManager = ganache.engine.manager.state.blockchain.vm.pStateManager const pStateManager = ganache.engine.manager.state.blockchain.vm.pStateManager
const cStateManager = pStateManager._wrapped const cStateManager = pStateManager._wrapped
const ovmExecutionManagerOriginalAddress = deploymentResult.contracts.OVM_ExecutionManager.address const dump: StateDump = {
.slice(2) accounts: {},
.toLowerCase() }
const ovmExecutionManagerAddress = 'c0dec0dec0dec0dec0dec0dec0dec0dec0de0000'
const ovmStateManagerOriginalAddress = deploymentResult.contracts.OVM_StateManager.address for (let i = 0; i < Object.keys(deploymentResult.contracts).length; i++) {
.slice(2) const name = Object.keys(deploymentResult.contracts)[i]
.toLowerCase() const contract = deploymentResult.contracts[name]
const ovmStateManagerAddress = 'c0dec0dec0dec0dec0dec0dec0dec0dec0de0001'
const l2ToL1MessagePasserDef = getContractDefinition( const codeBuf = await pStateManager.getContractCode(
'OVM_L2ToL1MessagePasser' fromHexString(contract.address)
)
const l2ToL1MessagePasserHash = keccak256(
l2ToL1MessagePasserDef.deployedBytecode
) )
const l2ToL1MessagePasserAddress = '4200000000000000000000000000000000000000' const code = toHexString(codeBuf)
const l1MessageSenderDef = getContractDefinition('OVM_L1MessageSender')
const l1MessageSenderHash = keccak256(l1MessageSenderDef.deployedBytecode)
const l1MessageSenderAddress = '4200000000000000000000000000000000000001'
const changedAccounts = await getChangedAccounts(cStateManager) const deadAddress =
precompiles[name] ||
`0xdeaddeaddeaddeaddeaddeaddeaddeaddead${i.toString(16).padStart(4, '0')}`
let deadAddressIndex = 0 dump.accounts[name] = {
const accounts: Accounts = [] address: deadAddress,
for (const originalAddress of changedAccounts) {
const code = (
await pStateManager.getContractCode(originalAddress)
).toString('hex')
const codeHash = keccak256('0x' + code)
if (code.length === 0) {
continue
}
// Sorry for this one!
let address = originalAddress
if (codeHash === l2ToL1MessagePasserHash) {
address = l2ToL1MessagePasserAddress
} else if (codeHash === l1MessageSenderHash) {
address = l1MessageSenderAddress
} else if (originalAddress === ovmExecutionManagerOriginalAddress) {
address = ovmExecutionManagerAddress
} else if (originalAddress === ovmStateManagerOriginalAddress) {
address = ovmStateManagerAddress
} else {
address = `deaddeaddeaddeaddeaddeaddeaddeaddead${deadAddressIndex
.toString(16)
.padStart(4, '0')}`
deadAddressIndex++
}
accounts.push({
originalAddress,
address,
code, code,
}) codeHash: keccak256(code),
storage: await getStorageDump(cStateManager, contract.address),
abi: getContractDefinition(name.replace('Proxy__', '')).abi,
}
} }
const dump: StateDump = { const addressMap = Object.keys(dump.accounts).map((name) => {
contracts: { return {
ovmExecutionManager: '0x' + ovmExecutionManagerAddress, originalAddress: deploymentResult.contracts[name].address,
ovmStateManager: '0x' + ovmStateManagerAddress, deadAddress: dump.accounts[name].address,
},
accounts: {},
} }
})
for (const account of accounts) { for (const name of Object.keys(dump.accounts)) {
const storageDump = sanitizeStorageDump( dump.accounts[name].storage = sanitizeStorageDump(
await getStorageDump(cStateManager, account.originalAddress), dump.accounts[name].storage,
accounts addressMap
) )
dump.accounts[account.address] = {
balance: 0,
nonce: 0,
code: account.code,
storage: storageDump,
}
} }
return dump return dump
......
export * from './contract-defs' export * from './contract-defs'
export * from './contract-dumps' export { getLatestStateDump, StateDump } from './contract-dumps'
export * from './contract-deployment' export * from './contract-deployment'
This diff is collapsed.
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Signer, ContractFactory, Contract, BigNumber } from 'ethers'
import { smockit, MockContract } from '@eth-optimism/smock'
/* Internal Imports */
import {
makeAddressManager,
setProxyTarget,
NON_NULL_BYTES32,
ZERO_ADDRESS,
NON_ZERO_ADDRESS,
NULL_BYTES32,
DUMMY_BATCH_HEADERS,
DUMMY_BATCH_PROOFS,
TrieTestGenerator,
toHexString,
} from '../../../helpers'
import { getContractInterface } from '../../../../src'
import { keccak256 } from 'ethers/lib/utils'
const getXDomainCalldata = (
sender: string,
target: string,
message: string,
messageNonce: number
): string => {
return getContractInterface(
'OVM_L2CrossDomainMessenger'
).encodeFunctionData('relayMessage', [target, sender, message, messageNonce])
}
describe('OVM_L1CrossDomainMessenger', () => {
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let AddressManager: Contract
before(async () => {
AddressManager = await makeAddressManager()
})
let Mock__TargetContract: MockContract
let Mock__OVM_L2CrossDomainMessenger: MockContract
let Mock__OVM_CanonicalTransactionChain: MockContract
let Mock__OVM_StateCommitmentChain: MockContract
before(async () => {
Mock__TargetContract = smockit(
await ethers.getContractFactory('Helper_SimpleProxy')
)
Mock__OVM_L2CrossDomainMessenger = smockit(
await ethers.getContractFactory('OVM_L2CrossDomainMessenger')
)
Mock__OVM_CanonicalTransactionChain = smockit(
await ethers.getContractFactory('OVM_CanonicalTransactionChain')
)
Mock__OVM_StateCommitmentChain = smockit(
await ethers.getContractFactory('OVM_StateCommitmentChain')
)
await AddressManager.setAddress(
'OVM_L2CrossDomainMessenger',
Mock__OVM_L2CrossDomainMessenger.address
)
await setProxyTarget(
AddressManager,
'OVM_CanonicalTransactionChain',
Mock__OVM_CanonicalTransactionChain
)
await setProxyTarget(
AddressManager,
'OVM_StateCommitmentChain',
Mock__OVM_StateCommitmentChain
)
})
let Factory__OVM_L1CrossDomainMessenger: ContractFactory
before(async () => {
Factory__OVM_L1CrossDomainMessenger = await ethers.getContractFactory(
'OVM_L1CrossDomainMessenger'
)
})
let OVM_L1CrossDomainMessenger: Contract
beforeEach(async () => {
OVM_L1CrossDomainMessenger = await Factory__OVM_L1CrossDomainMessenger.deploy(
AddressManager.address
)
})
describe('sendMessage', () => {
const target = NON_ZERO_ADDRESS
const message = NON_NULL_BYTES32
const gasLimit = 100_000
it('should be able to send a single message', async () => {
await expect(
OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit)
).to.not.be.reverted
expect(
Mock__OVM_CanonicalTransactionChain.smocked.enqueue.calls[0]
).to.deep.equal([
Mock__OVM_L2CrossDomainMessenger.address,
BigNumber.from(gasLimit),
getXDomainCalldata(await signer.getAddress(), target, message, 0),
])
})
it('should be able to send the same message twice', async () => {
await OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit)
await expect(
OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit)
).to.not.be.reverted
})
})
describe('replayMessage', () => {
const target = NON_ZERO_ADDRESS
const message = NON_NULL_BYTES32
const gasLimit = 100_000
it('should revert if the message does not exist', async () => {
await expect(
OVM_L1CrossDomainMessenger.replayMessage(
target,
await signer.getAddress(),
message,
0,
gasLimit
)
).to.be.revertedWith('Provided message has not already been sent.')
})
it('should succeed if the message exists', async () => {
await OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit)
await expect(
OVM_L1CrossDomainMessenger.replayMessage(
target,
await signer.getAddress(),
message,
0,
gasLimit
)
).to.not.be.reverted
})
})
describe('relayMessage', () => {
let target: string
let message: string
let sender: string
let proof: any
before(async () => {
target = Mock__TargetContract.address
message = Mock__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
])
sender = await signer.getAddress()
const calldata = getXDomainCalldata(sender, target, message, 0)
const precompile = '0x4200000000000000000000000000000000000000'
const storageKey = keccak256(keccak256(calldata) + '00'.repeat(32))
const storageGenerator = await TrieTestGenerator.fromNodes({
nodes: [
{
key: storageKey,
val: '0x' + '01'.padStart(64, '0'),
},
],
secure: true,
})
const generator = await TrieTestGenerator.fromAccounts({
accounts: [
{
address: precompile,
nonce: 0,
balance: 0,
codeHash: keccak256('0x1234'),
storageRoot: toHexString(storageGenerator._trie.root),
},
],
secure: true,
})
proof = {
stateRoot: toHexString(generator._trie.root),
stateRootBatchHeader: DUMMY_BATCH_HEADERS[0],
stateRootProof: DUMMY_BATCH_PROOFS[0],
stateTrieWitness: (await generator.makeAccountProofTest(precompile))
.accountTrieWitness,
storageTrieWitness: (
await storageGenerator.makeInclusionProofTest(storageKey)
).proof,
}
})
beforeEach(async () => {
Mock__OVM_StateCommitmentChain.smocked.verifyElement.will.return.with(
true
)
Mock__OVM_StateCommitmentChain.smocked.insideFraudProofWindow.will.return.with(
false
)
})
it('should revert if still inside the fraud proof window', async () => {
Mock__OVM_StateCommitmentChain.smocked.insideFraudProofWindow.will.return.with(
true
)
const proof = {
stateRoot: NULL_BYTES32,
stateRootBatchHeader: DUMMY_BATCH_HEADERS[0],
stateRootProof: DUMMY_BATCH_PROOFS[0],
stateTrieWitness: '0x',
storageTrieWitness: '0x',
}
await expect(
OVM_L1CrossDomainMessenger.relayMessage(
target,
sender,
message,
0,
proof
)
).to.be.revertedWith('Provided message could not be verified.')
})
it('should revert if provided an invalid state root proof', async () => {
Mock__OVM_StateCommitmentChain.smocked.verifyElement.will.return.with(
false
)
const proof = {
stateRoot: NULL_BYTES32,
stateRootBatchHeader: DUMMY_BATCH_HEADERS[0],
stateRootProof: DUMMY_BATCH_PROOFS[0],
stateTrieWitness: '0x',
storageTrieWitness: '0x',
}
await expect(
OVM_L1CrossDomainMessenger.relayMessage(
target,
sender,
message,
0,
proof
)
).to.be.revertedWith('Provided message could not be verified.')
})
it('should revert if provided an invalid storage trie witness', async () => {
await expect(
OVM_L1CrossDomainMessenger.relayMessage(target, sender, message, 0, {
...proof,
storageTrieWitness: '0x',
})
).to.be.reverted
})
it('should revert if provided an invalid state trie witness', async () => {
await expect(
OVM_L1CrossDomainMessenger.relayMessage(target, sender, message, 0, {
...proof,
stateTrieWitness: '0x',
})
).to.be.reverted
})
it('should send a call to the target contract', async () => {
await OVM_L1CrossDomainMessenger.relayMessage(
target,
sender,
message,
0,
proof
)
expect(Mock__TargetContract.smocked.setTarget.calls[0]).to.deep.equal([
NON_ZERO_ADDRESS,
])
})
it('should revert if trying to send the same message twice', async () => {
await OVM_L1CrossDomainMessenger.relayMessage(
target,
sender,
message,
0,
proof
)
await expect(
OVM_L1CrossDomainMessenger.relayMessage(
target,
sender,
message,
0,
proof
)
).to.be.revertedWith('Provided message has already been received.')
})
})
})
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Signer, ContractFactory, Contract } from 'ethers'
import { smockit, MockContract } from '@eth-optimism/smock'
/* Internal Imports */
import {
makeAddressManager,
setProxyTarget,
NON_NULL_BYTES32,
ZERO_ADDRESS,
NON_ZERO_ADDRESS,
} from '../../../helpers'
import { getContractInterface } from '../../../../src'
const getXDomainCalldata = (
sender: string,
target: string,
message: string,
messageNonce: number
): string => {
return getContractInterface(
'OVM_L2CrossDomainMessenger'
).encodeFunctionData('relayMessage', [target, sender, message, messageNonce])
}
describe('OVM_L2CrossDomainMessenger', () => {
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let AddressManager: Contract
before(async () => {
AddressManager = await makeAddressManager()
})
let Mock__TargetContract: MockContract
let Mock__OVM_L1CrossDomainMessenger: MockContract
let Mock__OVM_L1MessageSender: MockContract
let Mock__OVM_L2ToL1MessagePasser: MockContract
before(async () => {
Mock__TargetContract = smockit(
await ethers.getContractFactory('Helper_SimpleProxy')
)
Mock__OVM_L1CrossDomainMessenger = smockit(
await ethers.getContractFactory('OVM_L1CrossDomainMessenger')
)
Mock__OVM_L1MessageSender = smockit(
await ethers.getContractFactory('OVM_L1MessageSender')
)
Mock__OVM_L2ToL1MessagePasser = smockit(
await ethers.getContractFactory('OVM_L2ToL1MessagePasser')
)
await AddressManager.setAddress(
'OVM_L1CrossDomainMessenger',
Mock__OVM_L1CrossDomainMessenger.address
)
await setProxyTarget(
AddressManager,
'OVM_L1MessageSender',
Mock__OVM_L1MessageSender
)
await setProxyTarget(
AddressManager,
'OVM_L2ToL1MessagePasser',
Mock__OVM_L2ToL1MessagePasser
)
})
let Factory__OVM_L2CrossDomainMessenger: ContractFactory
before(async () => {
Factory__OVM_L2CrossDomainMessenger = await ethers.getContractFactory(
'OVM_L2CrossDomainMessenger'
)
})
let OVM_L2CrossDomainMessenger: Contract
beforeEach(async () => {
OVM_L2CrossDomainMessenger = await Factory__OVM_L2CrossDomainMessenger.deploy(
AddressManager.address
)
})
describe('sendMessage', () => {
const target = NON_ZERO_ADDRESS
const message = NON_NULL_BYTES32
const gasLimit = 100_000
it('should be able to send a single message', async () => {
await expect(
OVM_L2CrossDomainMessenger.sendMessage(target, message, gasLimit)
).to.not.be.reverted
expect(
Mock__OVM_L2ToL1MessagePasser.smocked.passMessageToL1.calls[0]
).to.deep.equal([
getXDomainCalldata(await signer.getAddress(), target, message, 0),
])
})
it('should be able to send the same message twice', async () => {
await OVM_L2CrossDomainMessenger.sendMessage(target, message, gasLimit)
await expect(
OVM_L2CrossDomainMessenger.sendMessage(target, message, gasLimit)
).to.not.be.reverted
})
})
describe('relayMessage', () => {
let target: string
let message: string
let sender: string
before(async () => {
target = Mock__TargetContract.address
message = Mock__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
])
sender = await signer.getAddress()
})
beforeEach(async () => {
Mock__OVM_L1MessageSender.smocked.getL1MessageSender.will.return.with(
Mock__OVM_L1CrossDomainMessenger.address
)
})
it('should revert if the L1 message sender is not the OVM_L1CrossDomainMessenger', async () => {
Mock__OVM_L1MessageSender.smocked.getL1MessageSender.will.return.with(
ZERO_ADDRESS
)
await expect(
OVM_L2CrossDomainMessenger.relayMessage(target, sender, message, 0)
).to.be.revertedWith('Provided message could not be verified.')
})
it('should send a call to the target contract', async () => {
await OVM_L2CrossDomainMessenger.relayMessage(target, sender, message, 0)
expect(Mock__TargetContract.smocked.setTarget.calls[0]).to.deep.equal([
NON_ZERO_ADDRESS,
])
})
it('should revert if trying to send the same message twice', async () => {
Mock__OVM_L1MessageSender.smocked.getL1MessageSender.will.return.with(
Mock__OVM_L1CrossDomainMessenger.address
)
await OVM_L2CrossDomainMessenger.relayMessage(target, sender, message, 0)
await expect(
OVM_L2CrossDomainMessenger.relayMessage(target, sender, message, 0)
).to.be.revertedWith('Provided message has already been received.')
})
})
})
...@@ -13,8 +13,9 @@ import { ...@@ -13,8 +13,9 @@ import {
ZERO_ADDRESS, ZERO_ADDRESS,
toHexString32, toHexString32,
getEthTime, getEthTime,
NULL_BYTES32,
} from '../../../helpers' } from '../../../helpers'
import { keccak256 } from 'ethers/lib/utils' import { keccak256, defaultAbiCoder } from 'ethers/lib/utils'
describe('OVM_StateCommitmentChain', () => { describe('OVM_StateCommitmentChain', () => {
let signer: Signer let signer: Signer
...@@ -106,7 +107,7 @@ describe('OVM_StateCommitmentChain', () => { ...@@ -106,7 +107,7 @@ describe('OVM_StateCommitmentChain', () => {
batchRoot: keccak256(NON_NULL_BYTES32), batchRoot: keccak256(NON_NULL_BYTES32),
batchSize: 1, batchSize: 1,
prevTotalElements: 0, prevTotalElements: 0,
extraData: '0x', extraData: NULL_BYTES32,
} }
beforeEach(async () => { beforeEach(async () => {
...@@ -114,7 +115,10 @@ describe('OVM_StateCommitmentChain', () => { ...@@ -114,7 +115,10 @@ describe('OVM_StateCommitmentChain', () => {
batch.length batch.length
) )
await OVM_StateCommitmentChain.appendStateBatch(batch) await OVM_StateCommitmentChain.appendStateBatch(batch)
batchHeader.extraData = toHexString32(await getEthTime(ethers.provider)) batchHeader.extraData = defaultAbiCoder.encode(
['uint256'],
[await getEthTime(ethers.provider)]
)
}) })
describe('when the sender is not the OVM_FraudVerifier', () => { describe('when the sender is not the OVM_FraudVerifier', () => {
...@@ -156,7 +160,7 @@ describe('OVM_StateCommitmentChain', () => { ...@@ -156,7 +160,7 @@ describe('OVM_StateCommitmentChain', () => {
await expect( await expect(
OVM_StateCommitmentChain.deleteStateBatch({ OVM_StateCommitmentChain.deleteStateBatch({
...batchHeader, ...batchHeader,
extraData: '0x1234', extraData: '0x' + '22'.repeat(32),
}) })
).to.be.revertedWith('Invalid batch header.') ).to.be.revertedWith('Invalid batch header.')
}) })
......
...@@ -16,6 +16,8 @@ import { ...@@ -16,6 +16,8 @@ import {
const CREATED_CONTRACT_1 = '0x2bda4a99d5be88609d23b1e4ab5d1d34fb1c2feb' const CREATED_CONTRACT_1 = '0x2bda4a99d5be88609d23b1e4ab5d1d34fb1c2feb'
const CREATED_CONTRACT_2 = '0x2bda4a99d5be88609d23b1e4ab5d1d34fb1c2feb' const CREATED_CONTRACT_2 = '0x2bda4a99d5be88609d23b1e4ab5d1d34fb1c2feb'
const CREATED_CONTRACT_BY_2_1 = '0xe0d8be8101f36ebe6b01abacec884422c39a1f62'
const CREATED_CONTRACT_BY_2_2 = '0x15ac629e1a3866b17179ee4ae86de5cbda744335'
const NESTED_CREATED_CONTRACT = '0xcb964b3f4162a0d4f5c997b40e19da5a546bc36f' const NESTED_CREATED_CONTRACT = '0xcb964b3f4162a0d4f5c997b40e19da5a546bc36f'
const DUMMY_REVERT_DATA = const DUMMY_REVERT_DATA =
'0xdeadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420' '0xdeadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420'
...@@ -49,6 +51,14 @@ const test_ovmCREATE: TestDefinition = { ...@@ -49,6 +51,14 @@ const test_ovmCREATE: TestDefinition = {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH, codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20), ethAddress: '0x' + '00'.repeat(20),
}, },
[CREATED_CONTRACT_BY_2_1]: {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20),
},
[CREATED_CONTRACT_BY_2_2]: {
codeHash: '0x' + '01'.repeat(32),
ethAddress: '0x' + '00'.repeat(20),
},
[NESTED_CREATED_CONTRACT]: { [NESTED_CREATED_CONTRACT]: {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH, codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20), ethAddress: '0x' + '00'.repeat(20),
...@@ -527,6 +537,38 @@ const test_ovmCREATE: TestDefinition = { ...@@ -527,6 +537,38 @@ const test_ovmCREATE: TestDefinition = {
}, },
], ],
}, },
{
name: 'ovmCREATE => ovmCREATE => ovmCALL(ADDRESS_NONEXIST)',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmCREATE',
functionParams: {
bytecode: '0x',
},
expectedReturnStatus: true,
expectedReturnValue: ZERO_ADDRESS,
},
],
},
expectedReturnStatus: true,
expectedReturnValue: CREATED_CONTRACT_BY_2_1,
},
],
},
expectedReturnStatus: true,
},
],
},
{ {
name: 'ovmCREATE => ovmCREATE => ovmCALL(ADDRESS_NONEXIST)', name: 'ovmCREATE => ovmCREATE => ovmCALL(ADDRESS_NONEXIST)',
expectInvalidStateAccess: true, expectInvalidStateAccess: true,
...@@ -563,6 +605,39 @@ const test_ovmCREATE: TestDefinition = { ...@@ -563,6 +605,39 @@ const test_ovmCREATE: TestDefinition = {
}, },
], ],
}, },
{
name: 'OZ-AUDIT: ovmCREATE => ((ovmCREATE => ovmADDRESS), ovmREVERT)',
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmADDRESS',
expectedReturnValue: NESTED_CREATED_CONTRACT,
},
],
},
expectedReturnStatus: true,
expectedReturnValue: NESTED_CREATED_CONTRACT,
},
{
functionName: 'ovmREVERT',
revertData: DUMMY_REVERT_DATA,
expectedReturnStatus: true,
expectedReturnValue: '0x00',
},
],
},
expectedReturnStatus: true,
expectedReturnValue: ZERO_ADDRESS,
},
],
},
{ {
name: 'ovmCREATE => OUT_OF_GAS', name: 'ovmCREATE => OUT_OF_GAS',
steps: [ steps: [
......
...@@ -17,7 +17,7 @@ describe('OVM_SafetyChecker', () => { ...@@ -17,7 +17,7 @@ describe('OVM_SafetyChecker', () => {
OVM_SafetyChecker = await Factory__OVM_SafetyChecker.deploy() OVM_SafetyChecker = await Factory__OVM_SafetyChecker.deploy()
}) })
describe.skip('isBytecodeSafe()', () => { describe('isBytecodeSafe()', () => {
for (const testName of Object.keys(SAFETY_CHECKER_TEST_JSON)) { for (const testName of Object.keys(SAFETY_CHECKER_TEST_JSON)) {
const test = SAFETY_CHECKER_TEST_JSON[testName] const test = SAFETY_CHECKER_TEST_JSON[testName]
it(`should correctly classify: ${testName}`, async () => { it(`should correctly classify: ${testName}`, async () => {
......
...@@ -4,7 +4,7 @@ import { expect } from '../../setup' ...@@ -4,7 +4,7 @@ import { expect } from '../../setup'
import { ethers } from '@nomiclabs/buidler' import { ethers } from '@nomiclabs/buidler'
import { Contract, BigNumber, ContractFactory } from 'ethers' import { Contract, BigNumber, ContractFactory } from 'ethers'
import { cloneDeep, merge } from 'lodash' import { cloneDeep, merge } from 'lodash'
import { smoddit, ModifiableContract } from '@eth-optimism/smock' import { smoddit, smockit, ModifiableContract } from '@eth-optimism/smock'
/* Internal Imports */ /* Internal Imports */
import { import {
...@@ -148,10 +148,15 @@ export class ExecutionManagerTestRunner { ...@@ -148,10 +148,15 @@ export class ExecutionManagerTestRunner {
await ethers.getContractFactory('Lib_AddressManager') await ethers.getContractFactory('Lib_AddressManager')
).deploy() ).deploy()
this.contracts.OVM_SafetyChecker = await ( const SafetyChecker = await (
await ethers.getContractFactory('OVM_SafetyChecker') await ethers.getContractFactory('OVM_SafetyChecker')
).deploy() ).deploy()
const MockSafetyChecker = smockit(SafetyChecker)
MockSafetyChecker.smocked.isBytecodeSafe.will.return.with(true)
this.contracts.OVM_SafetyChecker = MockSafetyChecker
await AddressManager.setAddress( await AddressManager.setAddress(
'OVM_SafetyChecker', 'OVM_SafetyChecker',
this.contracts.OVM_SafetyChecker.address this.contracts.OVM_SafetyChecker.address
...@@ -159,11 +164,25 @@ export class ExecutionManagerTestRunner { ...@@ -159,11 +164,25 @@ export class ExecutionManagerTestRunner {
this.contracts.OVM_ExecutionManager = await ( this.contracts.OVM_ExecutionManager = await (
await smoddit('OVM_ExecutionManager') await smoddit('OVM_ExecutionManager')
).deploy(AddressManager.address) ).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 ( this.contracts.OVM_StateManager = await (
await smoddit('OVM_StateManager') await smoddit('OVM_StateManager')
).deploy(this.contracts.OVM_ExecutionManager.address) ).deploy(await this.contracts.OVM_ExecutionManager.signer.getAddress())
await this.contracts.OVM_StateManager.setExecutionManager(
this.contracts.OVM_ExecutionManager.address
)
this.contracts.Helper_TestRunner = await ( this.contracts.Helper_TestRunner = await (
await ethers.getContractFactory('Helper_TestRunner') await ethers.getContractFactory('Helper_TestRunner')
......
...@@ -20,8 +20,7 @@ ...@@ -20,8 +20,7 @@
"node_modules/@types" "node_modules/@types"
] ]
}, },
"include": ["*.ts", "**/*.ts", "artifacts/*.json"], "include": ["src/**/*.ts", "artifacts/*.json"],
"exclude": ["./build", "node_modules", "test"],
"files": [ "files": [
"./buidler.config.ts", "./buidler.config.ts",
"./buidler-env.d.ts", "./buidler-env.d.ts",
......
This diff is collapsed.
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