Commit d34a12fb authored by Karl Floersch's avatar Karl Floersch Committed by GitHub

Code freeze audit integrations (#163)

* Separates storage from SCC and CTC (#151)

* First pass version

* More minor tweaks for tests to pass

* Add authentication

* Minor config updates

* Fix lint error

* Fix naming changes per review

* Enable Deployer Whitelist (#119)

* first pass, test runner updated

* add ability to only validate flag, test passes

* all tests passing

* clean up console.logs

* enforce gas refund preservation

* more cleanup/import removal

* whitelisted -> allowed

* first pass, test runner updated

* add ability to only validate flag, test passes

* all tests passing

* clean up console.logs

* enforce gas refund preservation

* more cleanup/import removal

* whitelisted -> allowed

* remove whitespace

* Restrict StateTransitionerFactory (#140)

* added msg sender check

* add create test

* cleanup

* add param

* add addressmanager.address param

* CTC Chain Monotonicity Fixes (#93)

* [wip] Fix block time logic

* some sad path and happy tests passing

* more progress

* first pass sad cases tested

* cleanup, adding empty tests

* more reversion tests

* rename shouldstartat}

* add final couple tests

* enable more tests

* cleanup

* remove .only

* textual cleanup

* make queue length public

* improve structure, comments

* update deploy config

* address nits
Co-authored-by: default avatarKarl Floersch <karl@karlfloersch.com>

* fix declarations, lint (#152)

* Adds river's new Merkle tree implementation, with some cleanup (#148)

* Reverts an accidental breaking merge

* Added new merkle tree impl

* add comments

* Final cleanups and merge
Co-authored-by: default avatarBen Jones <ben@pseudonym.party>

* Fix run gas Lower Bound (#94)

* added the check

* add test

* lower OVM TX size for Kovan

* re-remove gas check

* update gas vals slightly

* lint

* lint

* Merge master into freeze integration branch  (#153)

* update solidity version to ^0.7.0 (#122)

* update solc version to ^0.7.0

* interfaces back to solidity >0.6.0 <0.8.0

* update solc to 0.7.6

* back to 0.7.4

* upgrade to 0.7.6, fix EXTCODESIZE check

* versions >0.5.0 <0.8.0 for xdomain msgers

* ctc: disable appendQueueBatch (#150)

* ctc: disable appendSequencerBatch

* typo: fix

* re-enable verifyQueueTransaction test:

* add explicit test for verifying queue elements against either append
Co-authored-by: default avatarBen Jones <ben@pseudonym.party>

* fix up test

* remove .only
Co-authored-by: default avatarAlina <alina@optimism.io>
Co-authored-by: default avatarMark Tyneway <mark.tyneway@gmail.com>

* add check, simple test, update deploy (#154)

* go back to first name (#155)

* lint

* fix js number error

* add error logging to help debug deploy

* [code freeze] Fix deploy script (#156)

* fix deploy script

* add block time config

* ensure value is integer

* lint

* remove console logs from deploy

* Moves gas check to applyTransaction (#161)

* move to OVM_ST, pass test

* remove old test because functionality moved

* linting

* remove leaf hasing

* use safe EXEMRG wrapper (#162)

* use safeREQUIRE

* add owner getter

* relayer: add to config (#160)

* relayer: add to config

* lint: fix

* Fix minor error in test config
Co-authored-by: default avatarKelvin Fichter <kelvinfichter@gmail.com>
Co-authored-by: default avatarben-chain <ben@pseudonym.party>
Co-authored-by: default avatarAlina <alina@optimism.io>
Co-authored-by: default avatarMark Tyneway <mark.tyneway@gmail.com>
Co-authored-by: default avatarKevin Ho <kevinjho1996@gmail.com>
parent d2a7aeb7
......@@ -11,7 +11,7 @@ const sequencerKey = env.SEQUENCER_PRIVATE_KEY;
let SEQUENCER_ADDRESS = env.SEQUENCER_ADDRESS;
const web3Url = env.L1_NODE_WEB3_URL || 'http://127.0.0.1:8545';
const MIN_TRANSACTION_GAS_LIMIT = env.MIN_TRANSACTION_GAS_LIMIT || 50000;
const MAX_TRANSACTION_GAS_LIMIT = env.MAX_TRANSACTION_GAS_LIMIT || 12000000;
const MAX_TRANSACTION_GAS_LIMIT = env.MAX_TRANSACTION_GAS_LIMIT || Math.floor(9000000 * 63/64 - 100000); // = 8_759_375, Default max for Kovan
const MAX_GAS_PER_QUEUE_PER_EPOCH = env.MAX_GAS_PER_QUEUE_PER_EPOCH || 250000000;
const SECONDS_PER_EPOCH = env.SECONDS_PER_EPOCH || 0;
let WHITELIST_OWNER = env.WHITELIST_OWNER;
......@@ -22,6 +22,7 @@ const SEQUENCER_PUBLISH_WINDOW_SECONDS = env.SEQUENCER_PUBLISH_WINDOW_SECONDS ||
const CHAIN_ID = env.CHAIN_ID || 420; // layer 2 chainid
const USE_LEDGER = env.USE_LEDGER || false;
const HD_PATH = env.HD_PATH || utils.defaultPath;
const BLOCK_TIME_SECONDS = env.BLOCK_TIME_SECONDS || 15;
const L2_CROSS_DOMAIN_MESSENGER_ADDRESS =
env.L2_CROSS_DOMAIN_MESSENGER_ADDRESS || '0x4200000000000000000000000000000000000007';
......@@ -57,6 +58,7 @@ const L2_CROSS_DOMAIN_MESSENGER_ADDRESS =
transactionChainConfig: {
forceInclusionPeriodSeconds: FORCE_INCLUSION_PERIOD_SECONDS,
sequencer: SEQUENCER_ADDRESS,
forceInclusionPeriodBlocks: Math.ceil(FORCE_INCLUSION_PERIOD_SECONDS/BLOCK_TIME_SECONDS),
},
stateChainConfig: {
fraudProofWindowSeconds: FRAUD_PROOF_WINDOW_SECONDS,
......@@ -66,6 +68,9 @@ const L2_CROSS_DOMAIN_MESSENGER_ADDRESS =
ovmCHAINID: CHAIN_ID,
L2CrossDomainMessengerAddress: L2_CROSS_DOMAIN_MESSENGER_ADDRESS
},
l1CrossDomainMessengerConfig: {
relayerAddress: SEQUENCER_ADDRESS,
},
ovmGasMeteringConfig: {
minTransactionGasLimit: MIN_TRANSACTION_GAS_LIMIT,
maxTransactionGasLimit: MAX_TRANSACTION_GAS_LIMIT,
......
pragma solidity ^0.7.0;
/* Library Imports */
import { Lib_RingBuffer } from "../../libraries/utils/Lib_RingBuffer.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */
import { iOVM_ChainStorageContainer } from "../../iOVM/chain/iOVM_ChainStorageContainer.sol";
/**
* @title OVM_ChainStorageContainer
*/
contract OVM_ChainStorageContainer is iOVM_ChainStorageContainer, Lib_AddressResolver {
/*************
* Libraries *
*************/
using Lib_RingBuffer for Lib_RingBuffer.RingBuffer;
/*************
* Variables *
*************/
string public owner;
Lib_RingBuffer.RingBuffer internal buffer;
/***************
* Constructor *
***************/
/**
* @param _libAddressManager Address of the Address Manager.
* @param _owner Name of the contract that owns this container (will be resolved later).
*/
constructor(
address _libAddressManager,
string memory _owner
)
Lib_AddressResolver(_libAddressManager)
{
owner = _owner;
}
/**********************
* Function Modifiers *
**********************/
modifier onlyOwner() {
require(
msg.sender == resolve(owner),
"OVM_ChainStorageContainer: Function can only be called by the owner."
);
_;
}
/********************
* Public Functions *
********************/
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
function setGlobalMetadata(
bytes27 _globalMetadata
)
override
public
onlyOwner
{
return buffer.setExtraData(_globalMetadata);
}
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
function getGlobalMetadata()
override
public
view
returns (
bytes27
)
{
return buffer.getExtraData();
}
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
function length()
override
public
view
returns (
uint256
)
{
return uint256(buffer.getLength());
}
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
function push(
bytes32 _object
)
override
public
onlyOwner
{
buffer.push(_object);
}
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
function push(
bytes32 _object,
bytes27 _globalMetadata
)
override
public
onlyOwner
{
buffer.push(_object, _globalMetadata);
}
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
function push2(
bytes32 _objectA,
bytes32 _objectB
)
override
public
onlyOwner
{
buffer.push2(_objectA, _objectB);
}
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
function push2(
bytes32 _objectA,
bytes32 _objectB,
bytes27 _globalMetadata
)
override
public
onlyOwner
{
buffer.push2(_objectA, _objectB, _globalMetadata);
}
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
function get(
uint256 _index
)
override
public
view
returns (
bytes32
)
{
return buffer.get(uint40(_index));
}
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
function deleteElementsAfterInclusive(
uint256 _index
)
override
public
onlyOwner
{
buffer.deleteElementsAfterInclusive(
uint40(_index)
);
}
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
function deleteElementsAfterInclusive(
uint256 _index,
bytes27 _globalMetadata
)
override
public
onlyOwner
{
buffer.deleteElementsAfterInclusive(
uint40(_index),
_globalMetadata
);
}
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
function setNextOverwritableIndex(
uint256 _index
)
override
public
onlyOwner
{
buffer.nextOverwritableIndex = _index;
}
}
......@@ -5,22 +5,22 @@ 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_MerkleUtils } from "../../libraries/utils/Lib_MerkleUtils.sol";
import { Lib_RingBuffer, iRingBufferOverwriter } from "../../libraries/utils/Lib_RingBuffer.sol";
import { Lib_MerkleTree } from "../../libraries/utils/Lib_MerkleTree.sol";
/* Interface Imports */
import { iOVM_FraudVerifier } from "../../iOVM/verification/iOVM_FraudVerifier.sol";
import { iOVM_StateCommitmentChain } from "../../iOVM/chain/iOVM_StateCommitmentChain.sol";
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
import { iOVM_BondManager } from "../../iOVM/verification/iOVM_BondManager.sol";
import { iOVM_ChainStorageContainer } from "../../iOVM/chain/iOVM_ChainStorageContainer.sol";
/* External Imports */
import '@openzeppelin/contracts/math/SafeMath.sol';
/**
* @title OVM_StateCommitmentChain
*/
contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverwriter, Lib_AddressResolver {
using Lib_RingBuffer for Lib_RingBuffer.RingBuffer;
contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, Lib_AddressResolver {
/*************
* Constants *
......@@ -30,15 +30,6 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
uint256 public SEQUENCER_PUBLISH_WINDOW;
/*************
* Variables *
*************/
uint256 internal lastDeletableIndex;
uint256 internal lastDeletableTimestamp;
Lib_RingBuffer.RingBuffer internal batches;
/***************
* Constructor *
***************/
......@@ -50,7 +41,9 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
address _libAddressManager,
uint256 _fraudProofWindow,
uint256 _sequencerPublishWindow
) Lib_AddressResolver(_libAddressManager) {
)
Lib_AddressResolver(_libAddressManager)
{
FRAUD_PROOF_WINDOW = _fraudProofWindow;
SEQUENCER_PUBLISH_WINDOW = _sequencerPublishWindow;
}
......@@ -61,16 +54,18 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
********************/
/**
* @inheritdoc iOVM_StateCommitmentChain
* Accesses the batch storage container.
* @return Reference to the batch storage container.
*/
function init()
override
function batches()
public
view
returns (
iOVM_ChainStorageContainer
)
{
batches.init(
16,
Lib_OVMCodec.RING_BUFFER_SCC_BATCHES,
iRingBufferOverwriter(address(this))
return iOVM_ChainStorageContainer(
resolve("OVM_ChainStorageContainer:SCC:batches")
);
}
......@@ -100,7 +95,7 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
uint256 _totalBatches
)
{
return uint256(batches.getLength());
return batches().length();
}
/**
......@@ -207,11 +202,12 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
);
require(
Lib_MerkleUtils.verify(
Lib_MerkleTree.verify(
_batchHeader.batchRoot,
abi.encodePacked(_element),
_element,
_proof.index,
_proof.siblings
_proof.siblings,
_batchHeader.batchSize
),
"Invalid inclusion proof."
);
......@@ -244,69 +240,6 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
return SafeMath.add(timestamp, FRAUD_PROOF_WINDOW) > block.timestamp;
}
/**
* @inheritdoc iOVM_StateCommitmentChain
*/
function setLastOverwritableIndex(
Lib_OVMCodec.ChainBatchHeader memory _stateBatchHeader,
Lib_OVMCodec.Transaction memory _transaction,
Lib_OVMCodec.TransactionChainElement memory _txChainElement,
Lib_OVMCodec.ChainBatchHeader memory _txBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _txInclusionProof
)
override
public
{
require(
_isValidBatchHeader(_stateBatchHeader),
"Invalid batch header."
);
require(
insideFraudProofWindow(_stateBatchHeader) == false,
"Batch header must be outside of fraud proof window to be overwritable."
);
require(
_stateBatchHeader.batchIndex > lastDeletableIndex,
"Batch index must be greater than last overwritable index."
);
require(
iOVM_CanonicalTransactionChain(resolve("OVM_CanonicalTransactionChain")).verifyTransaction(
_transaction,
_txChainElement,
_txBatchHeader,
_txInclusionProof
),
"Invalid transaction proof."
);
lastDeletableIndex = _stateBatchHeader.batchIndex;
lastDeletableTimestamp = _transaction.timestamp;
}
/**
* @inheritdoc iRingBufferOverwriter
*/
function canOverwrite(
bytes32 _id,
uint256 _index
)
override
public
view
returns (
bool
)
{
if (_id == Lib_OVMCodec.RING_BUFFER_CTC_QUEUE) {
return iOVM_CanonicalTransactionChain(resolve("OVM_CanonicalTransactionChain")).getQueueElement(_index / 2).timestamp < lastDeletableTimestamp;
} else {
return _index < lastDeletableIndex;
}
}
/**********************
* Internal Functions *
......@@ -325,7 +258,7 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
uint40
)
{
bytes27 extraData = batches.getExtraData();
bytes27 extraData = batches().getGlobalMetadata();
uint40 totalElements;
uint40 lastSequencerTimestamp;
......@@ -394,15 +327,10 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
);
}
bytes[] memory elements = new bytes[](_batch.length);
for (uint256 i = 0; i < _batch.length; i++) {
elements[i] = abi.encodePacked(_batch[i]);
}
Lib_OVMCodec.ChainBatchHeader memory batchHeader = Lib_OVMCodec.ChainBatchHeader({
batchIndex: getTotalBatches(),
batchRoot: Lib_MerkleUtils.getMerkleRoot(elements),
batchSize: elements.length,
batchRoot: Lib_MerkleTree.getMerkleRoot(_batch),
batchSize: _batch.length,
prevTotalElements: totalElements,
extraData: _extraData
});
......@@ -415,7 +343,7 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
batchHeader.extraData
);
batches.push(
batches().push(
Lib_OVMCodec.hashBatchHeader(batchHeader),
_makeBatchExtraData(
uint40(batchHeader.prevTotalElements + batchHeader.batchSize),
......@@ -434,7 +362,7 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
internal
{
require(
_batchHeader.batchIndex < batches.getLength(),
_batchHeader.batchIndex < batches().length(),
"Invalid batch index."
);
......@@ -443,8 +371,8 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
"Invalid batch header."
);
batches.deleteElementsAfterInclusive(
uint40(_batchHeader.batchIndex),
batches().deleteElementsAfterInclusive(
_batchHeader.batchIndex,
_makeBatchExtraData(
uint40(_batchHeader.prevTotalElements),
0
......@@ -471,6 +399,6 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
bool
)
{
return Lib_OVMCodec.hashBatchHeader(_batchHeader) == batches.get(uint40(_batchHeader.batchIndex));
return Lib_OVMCodec.hashBatchHeader(_batchHeader) == batches().get(_batchHeader.batchIndex);
}
}
......@@ -15,6 +15,7 @@ import { iOVM_SafetyChecker } from "../../iOVM/execution/iOVM_SafetyChecker.sol"
/* Contract Imports */
import { OVM_ECDSAContractAccount } from "../accounts/OVM_ECDSAContractAccount.sol";
import { OVM_ProxyEOA } from "../accounts/OVM_ProxyEOA.sol";
import { OVM_DeployerWhitelist } from "../precompiles/OVM_DeployerWhitelist.sol";
/**
* @title OVM_ExecutionManager
......@@ -169,8 +170,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
return;
}
// Run the transaction, make sure to meter the gas usage.
// 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.
ovmCALL(
_transaction.gasLimit - gasMeterConfig.minTransactionGasLimit,
_transaction.entrypoint,
......@@ -359,6 +362,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
// 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,
......@@ -392,6 +399,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
// 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,
......@@ -839,7 +850,32 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
return gasMeterConfig.maxTransactionGasLimit;
}
/********************************************
* Public Functions: Deployment Witelisting *
********************************************/
/**
* Checks whether the given address is on the whitelst to ovmCREATE/ovmCREATE2, and reverts if not.
* @param _deployerAddress Address attempting to deploy a contract.
*/
function _checkDeployerAllowed(
address _deployerAddress
)
internal
{
// From an OVM semanitcs perspectibe, this will appear the identical to
// the deployer ovmCALLing the whitelist. This is fine--in a sense, we are forcing them to.
(bool success, bytes memory data) = ovmCALL(
gasleft(),
0x4200000000000000000000000000000000000002,
abi.encodeWithSignature("isDeployerAllowed(address)", _deployerAddress)
);
bool isAllowed = abi.decode(data, (bool));
if (!isAllowed || !success) {
_revertWithFlag(RevertFlag.CREATOR_NOT_ALLOWED);
}
}
/********************************************
* Internal Functions: Contract Interaction *
......@@ -998,13 +1034,14 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
_revertWithFlag(flag);
}
// INTENTIONAL_REVERT, UNSAFE_BYTECODE, and STATIC_VIOLATION aren't dependent on the
// input state, so we can just handle them like standard reverts. Our only change here
// 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;
}
......
......@@ -6,7 +6,7 @@ import { Lib_Bytes32Utils } from "../../libraries/utils/Lib_Bytes32Utils.sol";
/* Interface Imports */
import { iOVM_DeployerWhitelist } from "../../iOVM/precompiles/iOVM_DeployerWhitelist.sol";
import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol";
import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol";
/**
* @title OVM_DeployerWhitelist
......@@ -31,16 +31,14 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
* Blocks functions to anyone except the contract owner.
*/
modifier onlyOwner() {
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
address owner = Lib_Bytes32Utils.toAddress(
ovmExecutionManager.ovmSLOAD(
Lib_SafeExecutionManagerWrapper.safeSLOAD(
KEY_OWNER
)
);
require(
ovmExecutionManager.ovmCALLER() == owner,
Lib_SafeExecutionManagerWrapper.safeREQUIRE(
Lib_SafeExecutionManagerWrapper.safeCALLER() == owner,
"Function can only be called by the owner of this contract."
);
_;
......@@ -63,30 +61,45 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
override
public
{
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
bool initialized = Lib_Bytes32Utils.toBool(
ovmExecutionManager.ovmSLOAD(KEY_INITIALIZED)
Lib_SafeExecutionManagerWrapper.safeSLOAD(KEY_INITIALIZED)
);
if (initialized == true) {
return;
}
ovmExecutionManager.ovmSSTORE(
Lib_SafeExecutionManagerWrapper.safeSSTORE(
KEY_INITIALIZED,
Lib_Bytes32Utils.fromBool(true)
);
ovmExecutionManager.ovmSSTORE(
Lib_SafeExecutionManagerWrapper.safeSSTORE(
KEY_OWNER,
Lib_Bytes32Utils.fromAddress(_owner)
);
ovmExecutionManager.ovmSSTORE(
Lib_SafeExecutionManagerWrapper.safeSSTORE(
KEY_ALLOW_ARBITRARY_DEPLOYMENT,
Lib_Bytes32Utils.fromBool(_allowArbitraryDeployment)
);
}
/**
* Gets the owner of the whitelist.
*/
function getOwner()
override
public
returns(
address
)
{
return Lib_Bytes32Utils.toAddress(
Lib_SafeExecutionManagerWrapper.safeSLOAD(
KEY_OWNER
)
);
}
/**
* Adds or removes an address from the deployment whitelist.
* @param _deployer Address to update permissions for.
......@@ -100,9 +113,7 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
public
onlyOwner
{
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
ovmExecutionManager.ovmSSTORE(
Lib_SafeExecutionManagerWrapper.safeSSTORE(
Lib_Bytes32Utils.fromAddress(_deployer),
Lib_Bytes32Utils.fromBool(_isWhitelisted)
);
......@@ -119,9 +130,7 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
public
onlyOwner
{
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
ovmExecutionManager.ovmSSTORE(
Lib_SafeExecutionManagerWrapper.safeSSTORE(
KEY_OWNER,
Lib_Bytes32Utils.fromAddress(_owner)
);
......@@ -138,9 +147,7 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
public
onlyOwner
{
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
ovmExecutionManager.ovmSSTORE(
Lib_SafeExecutionManagerWrapper.safeSSTORE(
KEY_ALLOW_ARBITRARY_DEPLOYMENT,
Lib_Bytes32Utils.fromBool(_allowArbitraryDeployment)
);
......@@ -172,10 +179,8 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
bool _allowed
)
{
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
bool initialized = Lib_Bytes32Utils.toBool(
ovmExecutionManager.ovmSLOAD(KEY_INITIALIZED)
Lib_SafeExecutionManagerWrapper.safeSLOAD(KEY_INITIALIZED)
);
if (initialized == false) {
......@@ -183,7 +188,7 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
}
bool allowArbitraryDeployment = Lib_Bytes32Utils.toBool(
ovmExecutionManager.ovmSLOAD(KEY_ALLOW_ARBITRARY_DEPLOYMENT)
Lib_SafeExecutionManagerWrapper.safeSLOAD(KEY_ALLOW_ARBITRARY_DEPLOYMENT)
);
if (allowArbitraryDeployment == true) {
......@@ -191,7 +196,7 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
}
bool isWhitelisted = Lib_Bytes32Utils.toBool(
ovmExecutionManager.ovmSLOAD(
Lib_SafeExecutionManagerWrapper.safeSLOAD(
Lib_Bytes32Utils.fromAddress(_deployer)
)
);
......
......@@ -321,6 +321,15 @@ contract OVM_StateTransitioner is Lib_AddressResolver, OVM_FraudContributor, iOV
"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(
gasleft() >= 100000 + _transaction.gasLimit * 1032 / 1000, // 1032/1000 = 1.032 = (64/63)^2 rounded up
"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
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.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";
......@@ -11,7 +15,10 @@ import { OVM_StateTransitioner } from "./OVM_StateTransitioner.sol";
/**
* @title OVM_StateTransitionerFactory
*/
contract OVM_StateTransitionerFactory is iOVM_StateTransitionerFactory {
contract OVM_StateTransitionerFactory is iOVM_StateTransitionerFactory, Lib_AddressResolver {
constructor( address _libAddressManager)
Lib_AddressResolver(_libAddressManager){}
/***************************************
* Public Functions: Contract Creation *
......@@ -37,6 +44,10 @@ contract OVM_StateTransitionerFactory is iOVM_StateTransitionerFactory {
iOVM_StateTransitioner _ovmStateTransitioner
)
{
require(
msg.sender == resolve("OVM_FraudVerifier"),
"Create can only be done by the OVM_FraudVerifier."
);
return new OVM_StateTransitioner(
_libAddressManager,
_stateTransitionIndex,
......
......@@ -60,11 +60,6 @@ interface iOVM_CanonicalTransactionChain {
* Public Functions *
********************/
/**
* Initializes this contract.
*/
function init() external;
/**
* Retrieves the total number of elements submitted.
* @return _totalElements Total submitted elements.
......@@ -109,6 +104,18 @@ interface iOVM_CanonicalTransactionChain {
uint40
);
/**
* Retrieves the length of the queue, including
* both pending and canonical transactions.
* @return Length of the queue.
*/
function getQueueLength()
external
view
returns (
uint40
);
/**
* Gets the queue element at a particular index.
* @param _index Index of the queue element to access.
......@@ -148,13 +155,13 @@ interface iOVM_CanonicalTransactionChain {
/**
* Allows the sequencer to append a batch of transactions.
* @dev This function uses a custom encoding scheme for efficiency reasons.
* .param _shouldStartAtBatch Specific batch we expect to start appending to.
* .param _shouldStartAtElement Specific batch we expect to start appending to.
* .param _totalElementsToAppend Total number of batch elements we expect to append.
* .param _contexts Array of batch contexts.
* .param _transactionDataFields Array of raw transaction data.
*/
function appendSequencerBatch(
// uint40 _shouldStartAtBatch,
// uint40 _shouldStartAtElement,
// uint24 _totalElementsToAppend,
// BatchContext[] _contexts,
// bytes[] _transactionDataFields
......
pragma solidity ^0.7.0;
/**
* @title iOVM_ChainStorageContainer
*/
interface iOVM_ChainStorageContainer {
/********************
* Public Functions *
********************/
/**
* Sets the container's global metadata field. We're using `bytes27` here because we use five
* bytes to maintain the length of the underlying data structure, meaning we have an extra
* 27 bytes to store arbitrary data.
* @param _globalMetadata New global metadata to set.
*/
function setGlobalMetadata(
bytes27 _globalMetadata
)
external;
/**
* Retrieves the container's global metadata field.
* @return Container global metadata field.
*/
function getGlobalMetadata()
external
view
returns (
bytes27
);
/**
* Retrieves the number of objects stored in the container.
* @return Number of objects in the container.
*/
function length()
external
view
returns (
uint256
);
/**
* Pushes an object into the container.
* @param _object A 32 byte value to insert into the container.
*/
function push(
bytes32 _object
)
external;
/**
* Pushes an object into the container. Function allows setting the global metadata since
* we'll need to touch the "length" storage slot anyway, which also contains the global
* metadata (it's an optimization).
* @param _object A 32 byte value to insert into the container.
* @param _globalMetadata New global metadata for the container.
*/
function push(
bytes32 _object,
bytes27 _globalMetadata
)
external;
/**
* Pushes two objects into the container at the same time. A useful optimization.
* @param _objectA First 32 byte value to insert into the container.
* @param _objectB Second 32 byte value to insert into the container.
*/
function push2(
bytes32 _objectA,
bytes32 _objectB
)
external;
/**
* Pushes two objects into the container at the same time. Also allows setting the global
* metadata field.
* @param _objectA First 32 byte value to insert into the container.
* @param _objectB Second 32 byte value to insert into the container.
* @param _globalMetadata New global metadata for the container.
*/
function push2(
bytes32 _objectA,
bytes32 _objectB,
bytes27 _globalMetadata
)
external;
/**
* Retrieves an object from the container.
* @param _index Index of the particular object to access.
* @return 32 byte object value.
*/
function get(
uint256 _index
)
external
view
returns (
bytes32
);
/**
* Removes all objects after and including a given index.
* @param _index Object index to delete from.
*/
function deleteElementsAfterInclusive(
uint256 _index
)
external;
/**
* Removes all objects after and including a given index. Also allows setting the global
* metadata field.
* @param _index Object index to delete from.
* @param _globalMetadata New global metadata for the container.
*/
function deleteElementsAfterInclusive(
uint256 _index,
bytes27 _globalMetadata
)
external;
/**
* Marks an index as overwritable, meaing the underlying buffer can start to write values over
* any objects before and including the given index.
*/
function setNextOverwritableIndex(
uint256 _index
)
external;
}
......@@ -27,15 +27,11 @@ interface iOVM_StateCommitmentChain {
bytes32 _batchRoot
);
/********************
* Public Functions *
********************/
/**
* Initializes this contract.
*/
function init() external;
/**
* Retrieves the total number of elements submitted.
* @return _totalElements Total submitted elements.
......@@ -119,21 +115,4 @@ interface iOVM_StateCommitmentChain {
returns (
bool _inside
);
/**
* Sets the last batch index that can be deleted.
* @param _stateBatchHeader Proposed batch header that can be deleted.
* @param _transaction Transaction to verify.
* @param _txChainElement Transaction chain element corresponding to the transaction.
* @param _txBatchHeader Header of the batch the transaction was included in.
* @param _txInclusionProof Inclusion proof for the provided transaction chain element.
*/
function setLastOverwritableIndex(
Lib_OVMCodec.ChainBatchHeader memory _stateBatchHeader,
Lib_OVMCodec.Transaction memory _transaction,
Lib_OVMCodec.TransactionChainElement memory _txChainElement,
Lib_OVMCodec.ChainBatchHeader memory _txBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _txInclusionProof
)
external;
}
......@@ -19,7 +19,8 @@ interface iOVM_ExecutionManager {
UNSAFE_BYTECODE,
CREATE_COLLISION,
STATIC_VIOLATION,
CREATE_EXCEPTION
CREATE_EXCEPTION,
CREATOR_NOT_ALLOWED
}
enum GasMetadataKey {
......
......@@ -11,6 +11,7 @@ interface iOVM_DeployerWhitelist {
********************/
function initialize(address _owner, bool _allowArbitraryDeployment) external;
function getOwner() external returns (address _owner);
function setWhitelistedDeployer(address _deployer, bool _isWhitelisted) external;
function setOwner(address _newOwner) external;
function setAllowArbitraryDeployment(bool _allowArbitraryDeployment) external;
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @title Lib_Math
*/
library Lib_Math {
/**********************
* Internal Functions *
**********************/
/**
* Calculates the minumum of two numbers.
* @param _x First number to compare.
* @param _y Second number to compare.
* @return Lesser of the two numbers.
*/
function min(
uint256 _x,
uint256 _y
)
internal
pure
returns (
uint256
)
{
if (_x < _y) {
return _x;
}
return _y;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @title Lib_MerkleTree
* @author River Keefer
*/
library Lib_MerkleTree {
/**********************
* Internal Functions *
**********************/
/**
* Calculates a merkle root for a list of 32-byte leaf hashes. WARNING: If the number
* of leaves passed in is not a power of two, it pads out the tree with zero hashes.
* If you do not know the original length of elements for the tree you are verifying,
* then this may allow empty leaves past _elements.length to pass a verification check down the line.
* @param _elements Array of hashes from which to generate a merkle root.
* @return Merkle root of the leaves, with zero hashes for non-powers-of-two (see above).
*/
function getMerkleRoot(
bytes32[] memory _elements
)
internal
view
returns (
bytes32
)
{
require(
_elements.length > 0,
"Lib_MerkleTree: Must provide at least one leaf hash."
);
if (_elements.length == 0) {
return _elements[0];
}
uint256[16] memory defaults = [
0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563,
0x633dc4d7da7256660a892f8f1604a44b5432649cc8ec5cb3ced4c4e6ac94dd1d,
0x890740a8eb06ce9be422cb8da5cdafc2b58c0a5e24036c578de2a433c828ff7d,
0x3b8ec09e026fdc305365dfc94e189a81b38c7597b3d941c279f042e8206e0bd8,
0xecd50eee38e386bd62be9bedb990706951b65fe053bd9d8a521af753d139e2da,
0xdefff6d330bb5403f63b14f33b578274160de3a50df4efecf0e0db73bcdd3da5,
0x617bdd11f7c0a11f49db22f629387a12da7596f9d1704d7465177c63d88ec7d7,
0x292c23a9aa1d8bea7e2435e555a4a60e379a5a35f3f452bae60121073fb6eead,
0xe1cea92ed99acdcb045a6726b2f87107e8a61620a232cf4d7d5b5766b3952e10,
0x7ad66c0a68c72cb89e4fb4303841966e4062a76ab97451e3b9fb526a5ceb7f82,
0xe026cc5a4aed3c22a58cbd3d2ac754c9352c5436f638042dca99034e83636516,
0x3d04cffd8b46a874edf5cfae63077de85f849a660426697b06a829c70dd1409c,
0xad676aa337a485e4728a0b240d92b3ef7b3c372d06d189322bfd5f61f1e7203e,
0xa2fca4a49658f9fab7aa63289c91b7c7b6c832a6d0e69334ff5b0a3483d09dab,
0x4ebfd9cd7bca2505f7bef59cc1c12ecc708fff26ae4af19abe852afe9e20c862,
0x2def10d13dd169f550f578bda343d9717a138562e0093b380a1120789d53cf10
];
// Reserve memory space for our hashes.
bytes memory buf = new bytes(64);
// We'll need to keep track of left and right siblings.
bytes32 leftSibling;
bytes32 rightSibling;
// Number of non-empty nodes at the current depth.
uint256 rowSize = _elements.length;
// Current depth, counting from 0 at the leaves
uint256 depth = 0;
// Common sub-expressions
uint256 halfRowSize; // rowSize / 2
bool rowSizeIsOdd; // rowSize % 2 == 1
while (rowSize > 1) {
halfRowSize = rowSize / 2;
rowSizeIsOdd = rowSize % 2 == 1;
for (uint256 i = 0; i < halfRowSize; i++) {
leftSibling = _elements[(2 * i) ];
rightSibling = _elements[(2 * i) + 1];
assembly {
mstore(add(buf, 32), leftSibling )
mstore(add(buf, 64), rightSibling)
}
_elements[i] = keccak256(buf);
}
if (rowSizeIsOdd) {
leftSibling = _elements[rowSize - 1];
rightSibling = bytes32(defaults[depth]);
assembly {
mstore(add(buf, 32), leftSibling)
mstore(add(buf, 64), rightSibling)
}
_elements[halfRowSize] = keccak256(buf);
}
rowSize = halfRowSize + (rowSizeIsOdd ? 1 : 0);
depth++;
}
return _elements[0];
}
/**
* Verifies a merkle branch for the given leaf hash. Assumes the original length
* of leaves generated is a known, correct input, and does not return true for indices
* extending past that index (even if _siblings would be otherwise valid.)
* @param _root The Merkle root to verify against.
* @param _leaf The leaf hash to verify inclusion of.
* @param _index The index in the tree of this leaf.
* @param _siblings Array of sibline nodes in the inclusion proof, starting from depth 0 (bottom of the tree).
* @param _totalLeaves The total number of leaves originally passed into.
* @return Whether or not the merkle branch and leaf passes verification.
*/
function verify(
bytes32 _root,
bytes32 _leaf,
uint256 _index,
bytes32[] memory _siblings,
uint256 _totalLeaves
)
internal
pure
returns (
bool
)
{
require(
_totalLeaves > 0,
"Lib_MerkleTree: Total leaves must be greater than zero."
);
require(
_index < _totalLeaves,
"Lib_MerkleTree: Index out of bounds."
);
require(
_siblings.length == _ceilLog2(_totalLeaves),
"Lib_MerkleTree: Total siblings does not correctly correspond to total leaves."
);
bytes32 computedRoot = _leaf;
for (uint256 i = 0; i < _siblings.length; i++) {
if ((_index & 1) == 1) {
computedRoot = keccak256(
abi.encodePacked(
_siblings[i],
computedRoot
)
);
} else {
computedRoot = keccak256(
abi.encodePacked(
computedRoot,
_siblings[i]
)
);
}
_index >>= 1;
}
return _root == computedRoot;
}
/*********************
* Private Functions *
*********************/
/**
* Calculates the integer ceiling of the log base 2 of an input.
* @param _in Unsigned input to calculate the log.
* @return ceil(log_base_2(_in))
*/
function _ceilLog2(
uint256 _in
)
private
pure
returns (
uint256
)
{
require(
_in > 0,
"Lib_MerkleTree: Cannot compute ceil(log_2) of 0."
);
if (_in == 1) {
return 0;
}
// Find the highest set bit (will be floor(log_2)).
// Borrowed with <3 from https://github.com/ethereum/solidity-examples
uint256 val = _in;
uint256 highest = 0;
for (uint8 i = 128; i >= 1; i >>= 1) {
if (val & (uint(1) << i) - 1 << i != 0) {
highest += i;
val >>= i;
}
}
// Increment by one if this is not a perfect logarithm.
if ((uint(1) << highest) != _in) {
highest += 1;
}
return highest;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/**
* @title Lib_MerkleUtils
*/
library Lib_MerkleUtils {
function getMerkleRoot(
bytes32[] memory _hashes
)
internal
pure
returns (
bytes32 _root
)
{
require(
_hashes.length > 0,
"Must provide at least one leaf hash."
);
if (_hashes.length == 1) {
return _hashes[0];
}
bytes32[] memory defaultHashes = _getDefaultHashes(_hashes.length);
bytes32[] memory nodes = _hashes;
if (_hashes.length % 2 == 1) {
nodes = new bytes32[](_hashes.length + 1);
for (uint256 i = 0; i < _hashes.length; i++) {
nodes[i] = _hashes[i];
}
}
uint256 currentLevel = 0;
uint256 nextLevelSize = _hashes.length;
if (nextLevelSize % 2 == 1) {
nodes[nextLevelSize] = defaultHashes[currentLevel];
nextLevelSize += 1;
}
while (nextLevelSize > 1) {
currentLevel += 1;
for (uint256 i = 0; i < nextLevelSize / 2; i++) {
nodes[i] = _getParentHash(
nodes[i*2],
nodes[i*2 + 1]
);
}
nextLevelSize = nextLevelSize / 2;
if (nextLevelSize % 2 == 1 && nextLevelSize != 1) {
nodes[nextLevelSize] = defaultHashes[currentLevel];
nextLevelSize += 1;
}
}
return nodes[0];
}
function getMerkleRoot(
bytes[] memory _elements
)
internal
view
returns (
bytes32 _root
)
{
bytes32[] memory hashes = new bytes32[](_elements.length);
for (uint256 i = 0; i < _elements.length; i++) {
hashes[i] = keccak256(_elements[i]);
}
return getMerkleRoot(hashes);
}
function verify(
bytes32 _root,
bytes32 _leaf,
uint256 _path,
bytes32[] memory _siblings
)
internal
pure
returns (
bool _verified
)
{
bytes32 computedRoot = _leaf;
for (uint256 i = 0; i < _siblings.length; i++) {
bytes32 sibling = _siblings[i];
bool isRightSibling = uint8(_path >> i & 1) == 0;
if (isRightSibling) {
computedRoot = _getParentHash(computedRoot, sibling);
} else {
computedRoot = _getParentHash(sibling, computedRoot);
}
}
return computedRoot == _root;
}
function verify(
bytes32 _root,
bytes memory _leaf,
uint256 _path,
bytes32[] memory _siblings
)
internal
pure
returns (
bool _verified
)
{
return verify(
_root,
keccak256(_leaf),
_path,
_siblings
);
}
function _getDefaultHashes(
uint256 _length
)
private
pure
returns (
bytes32[] memory _defaultHashes
)
{
bytes32[] memory defaultHashes = new bytes32[](_length);
defaultHashes[0] = keccak256(abi.encodePacked(uint256(0)));
for (uint256 i = 1; i < defaultHashes.length; i++) {
defaultHashes[i] = keccak256(abi.encodePacked(
defaultHashes[i-1],
defaultHashes[i-1]
));
}
return defaultHashes;
}
function _getParentHash(
bytes32 _leftChildHash,
bytes32 _rightChildHash
)
private
pure
returns (
bytes32 _hash
)
{
return keccak256(abi.encodePacked(_leftChildHash, _rightChildHash));
}
}
\ No newline at end of file
pragma solidity ^0.7.0;
interface iRingBufferOverwriter {
function canOverwrite(bytes32 _id, uint256 _index) external returns (bool);
}
library Lib_RingBuffer {
using Lib_RingBuffer for RingBuffer;
......@@ -17,12 +13,11 @@ library Lib_RingBuffer {
}
struct RingBuffer {
bytes32 id;
iRingBufferOverwriter overwriter;
bytes32 contextA;
bytes32 contextB;
Buffer bufferA;
Buffer bufferB;
uint256 nextOverwritableIndex;
}
struct RingBufferContext {
......@@ -48,26 +43,6 @@ library Lib_RingBuffer {
* Internal Functions *
**********************/
/**
* Utility for initializing a ring buffer object.
* @param _self Buffer to access.
* @param _initialBufferSize Initial length of both sub-buffers.
* @param _overwriter Address of the overwriter contract.
*/
function init(
RingBuffer storage _self,
uint256 _initialBufferSize,
bytes32 _id,
iRingBufferOverwriter _overwriter
)
internal
{
_self.bufferA.length = _initialBufferSize;
_self.bufferB.length = _initialBufferSize;
_self.id = _id;
_self.overwriter = _overwriter;
}
/**
* Pushes a single element to the buffer.
* @param _self Buffer to access.
......@@ -91,15 +66,7 @@ library Lib_RingBuffer {
// Check if we need to expand the buffer.
if (ctx.globalIndex - ctx.currResetIndex >= currBuffer.length) {
bool canOverwrite;
try _self.overwriter.canOverwrite(_self.id, ctx.currResetIndex) returns (bool _canOverwrite) {
canOverwrite = _canOverwrite;
} catch {
// Just in case canOverwrite is broken.
canOverwrite = false;
}
if (canOverwrite) {
if (ctx.currResetIndex < _self.nextOverwritableIndex) {
// We're going to overwrite the inactive buffer.
// Bump the buffer index, reset the delete offset, and set our reset indices.
ctx.currBufferIndex++;
......@@ -280,6 +247,24 @@ library Lib_RingBuffer {
_self.setContext(ctx);
}
/**
* Deletes all elements after (and including) a given index.
* @param _self Buffer to access.
* @param _index Index of the element to delete from (inclusive).
*/
function deleteElementsAfterInclusive(
RingBuffer storage _self,
uint40 _index
)
internal
{
RingBufferContext memory ctx = _self.getContext();
_self.deleteElementsAfterInclusive(
_index,
ctx.extraData
);
}
/**
* Retrieves the current global index.
* @param _self Buffer to access.
......@@ -298,6 +283,22 @@ library Lib_RingBuffer {
return ctx.globalIndex;
}
/**
* Changes current global extra data.
* @param _self Buffer to access.
* @param _extraData New global extra data.
*/
function setExtraData(
RingBuffer storage _self,
bytes27 _extraData
)
internal
{
RingBufferContext memory ctx = _self.getContext();
ctx.extraData = _extraData;
_self.setContext(ctx);
}
/**
* Retrieves the current global extra data.
* @param _self Buffer to access.
......
......@@ -14,6 +14,7 @@ contract Helper_TestRunner {
bytes functionData;
bool expectedReturnStatus;
bytes expectedReturnData;
bool onlyValidateFlag;
}
function runSingleTestStep(
......@@ -67,7 +68,7 @@ contract Helper_TestRunner {
console.log("");
}
revert("Test step failed.");
_failStep();
}
if (keccak256(returndata) != keccak256(_step.expectedReturnData)) {
......@@ -79,6 +80,8 @@ contract Helper_TestRunner {
console.log("Actual:");
console.logBytes(returndata);
console.log("");
_failStep();
} else {
(
uint256 _expectedFlag,
......@@ -94,22 +97,35 @@ contract Helper_TestRunner {
bytes memory _data
) = _decodeRevertData(returndata);
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("");
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();
}
}
revert("Test step failed.");
}
if (success == false || (success == true && returndata.length == 1)) {
......@@ -156,6 +172,12 @@ contract Helper_TestRunner {
return abi.decode(_revertdata, (uint256, uint256, uint256, bytes));
}
function _failStep()
internal
{
revert("Test step failed.");
}
}
contract Helper_TestRunner_CREATE is Helper_TestRunner {
......
......@@ -3,58 +3,46 @@ pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_MerkleUtils } from "../../optimistic-ethereum/libraries/utils/Lib_MerkleUtils.sol";
import { Lib_MerkleTree } from "../../optimistic-ethereum/libraries/utils/Lib_MerkleTree.sol";
/**
* @title TestLib_MerkleUtils
* @title TestLib_MerkleTree
*/
contract TestLib_MerkleUtils {
contract TestLib_MerkleTree {
function getMerkleRoot(
bytes32[] memory _hashes
bytes32[] memory _elements
)
public
view
returns (
bytes32 _root
bytes32
)
{
return Lib_MerkleUtils.getMerkleRoot(
_hashes
);
}
function getMerkleRoot(
bytes[] memory _elements
)
public
view
returns (
bytes32 _root
)
{
return Lib_MerkleUtils.getMerkleRoot(
return Lib_MerkleTree.getMerkleRoot(
_elements
);
}
function verify(
bytes32 _root,
bytes memory _leaf,
uint256 _path,
bytes32[] memory _siblings
bytes32 _leaf,
uint256 _index,
bytes32[] memory _siblings,
uint256 _totalLeaves
)
public
pure
returns (
bool _verified
bool
)
{
return Lib_MerkleUtils.verify(
return Lib_MerkleTree.verify(
_root,
_leaf,
_path,
_siblings
_index,
_siblings,
_totalLeaves
);
}
}
......@@ -3,7 +3,7 @@ pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_RingBuffer, iRingBufferOverwriter } from "../../optimistic-ethereum/libraries/utils/Lib_RingBuffer.sol";
import { Lib_RingBuffer } from "../../optimistic-ethereum/libraries/utils/Lib_RingBuffer.sol";
/**
* @title TestLib_RingBuffer
......@@ -13,20 +13,6 @@ contract TestLib_RingBuffer {
Lib_RingBuffer.RingBuffer internal buf;
function init(
uint256 _initialBufferSize,
bytes32 _id,
iRingBufferOverwriter _overwriter
)
public
{
buf.init(
_initialBufferSize,
_id,
_overwriter
);
}
function push(
bytes32 _value,
bytes27 _extraData
......
{
"name": "@eth-optimism/contracts",
"version": "0.0.2-alpha.14",
"version": "0.0.2-alpha.15",
"main": "build/src/index.js",
"files": [
"build/**/*.js",
......@@ -54,6 +54,7 @@
"fs-extra": "^9.0.1",
"lodash": "^4.17.20",
"merkle-patricia-tree": "^4.0.0",
"merkletreejs": "^0.2.12",
"mkdirp": "^1.0.4",
"mocha": "^8.1.1",
"prettier": "^2.1.2",
......
......@@ -20,11 +20,15 @@ export interface RollupDeployConfig {
transactionChainConfig: {
sequencer: string | Signer
forceInclusionPeriodSeconds: number
forceInclusionPeriodBlocks: number
}
stateChainConfig: {
fraudProofWindowSeconds: number
sequencerPublishWindowSeconds: number
}
l1CrossDomainMessengerConfig: {
relayerAddress?: string | Signer
}
ethConfig: {
initialAmount: number
}
......@@ -32,6 +36,7 @@ export interface RollupDeployConfig {
owner: string | Signer
allowArbitraryContractDeployment: boolean
}
addressManager?: string
deployOverrides?: Overrides
dependencies?: string[]
}
......@@ -58,6 +63,14 @@ export const makeContractDeployConfig = async (
OVM_L1CrossDomainMessenger: {
factory: getContractFactory('OVM_L1CrossDomainMessenger'),
params: [],
afterDeploy: async (contracts): Promise<void> => {
if (config.l1CrossDomainMessengerConfig.relayerAddress) {
const relayer = config.l1CrossDomainMessengerConfig.relayerAddress
const address =
typeof relayer === 'string' ? relayer : await relayer.getAddress()
await AddressManager.setAddress('OVM_L2MessageRelayer', address)
}
},
},
Proxy__OVM_L1CrossDomainMessenger: {
factory: getContractFactory('Lib_ResolvedDelegateProxy'),
......@@ -80,8 +93,10 @@ export const makeContractDeployConfig = async (
params: [
AddressManager.address,
config.transactionChainConfig.forceInclusionPeriodSeconds,
config.transactionChainConfig.forceInclusionPeriodBlocks,
config.ovmGasMeteringConfig.maxTransactionGasLimit,
],
afterDeploy: async (contracts): Promise<void> => {
afterDeploy: async (): Promise<void> => {
const sequencer = config.transactionChainConfig.sequencer
const sequencerAddress =
typeof sequencer === 'string'
......@@ -93,7 +108,6 @@ export const makeContractDeployConfig = async (
)
await AddressManager.setAddress('OVM_Sequencer', sequencerAddress)
await AddressManager.setAddress('Sequencer', sequencerAddress)
await contracts.OVM_CanonicalTransactionChain.init()
},
},
OVM_StateCommitmentChain: {
......@@ -103,9 +117,6 @@ export const makeContractDeployConfig = async (
config.stateChainConfig.fraudProofWindowSeconds,
config.stateChainConfig.sequencerPublishWindowSeconds,
],
afterDeploy: async (contracts): Promise<void> => {
await contracts.OVM_StateCommitmentChain.init()
},
},
OVM_DeployerWhitelist: {
factory: getContractFactory('OVM_DeployerWhitelist'),
......@@ -142,6 +153,7 @@ export const makeContractDeployConfig = async (
},
OVM_StateManagerFactory: {
factory: getContractFactory('OVM_StateManagerFactory'),
params: [],
},
OVM_FraudVerifier: {
factory: getContractFactory('OVM_FraudVerifier'),
......@@ -149,6 +161,7 @@ export const makeContractDeployConfig = async (
},
OVM_StateTransitionerFactory: {
factory: getContractFactory('OVM_StateTransitionerFactory'),
params: [AddressManager.address],
},
OVM_ECDSAContractAccount: {
factory: getContractFactory('OVM_ECDSAContractAccount'),
......@@ -170,5 +183,17 @@ export const makeContractDeployConfig = async (
factory: getContractFactory('OVM_ETH'),
params: [config.ethConfig.initialAmount, 'Ether', 18, 'ETH'],
},
'OVM_ChainStorageContainer:CTC:batches': {
factory: getContractFactory('OVM_ChainStorageContainer'),
params: [AddressManager.address, 'OVM_CanonicalTransactionChain'],
},
'OVM_ChainStorageContainer:CTC:queue': {
factory: getContractFactory('OVM_ChainStorageContainer'),
params: [AddressManager.address, 'OVM_CanonicalTransactionChain'],
},
'OVM_ChainStorageContainer:SCC:batches': {
factory: getContractFactory('OVM_ChainStorageContainer'),
params: [AddressManager.address, 'OVM_StateCommitmentChain'],
},
}
}
......@@ -16,10 +16,23 @@ export interface DeployResult {
export const deploy = async (
config: RollupDeployConfig
): Promise<DeployResult> => {
const AddressManager: Contract = await getContractFactory(
'Lib_AddressManager',
config.deploymentSigner
).deploy()
let AddressManager: Contract
if (config.addressManager) {
// console.log(`Connecting to existing address manager.`) //console.logs currently break our deployer
AddressManager = getContractFactory(
'Lib_AddressManager',
config.deploymentSigner
).attach(config.addressManager)
} else {
// console.log(
// `Address manager wasn't provided, so we're deploying a new one.`
// ) //console.logs currently break our deployer
AddressManager = await getContractFactory(
'Lib_AddressManager',
config.deploymentSigner
).deploy()
}
const contractDeployConfig = await makeContractDeployConfig(
config,
......@@ -47,6 +60,7 @@ export const deploy = async (
)
await AddressManager.setAddress(name, contracts[name].address)
} catch (err) {
console.error(`Error deploying ${name}: ${err}`)
failedDeployments.push(name)
}
}
......
......@@ -126,6 +126,7 @@ export const makeStateDump = async (): Promise<any> => {
transactionChainConfig: {
sequencer: signer,
forceInclusionPeriodSeconds: 600,
forceInclusionPeriodBlocks: 600 / 12,
},
stateChainConfig: {
fraudProofWindowSeconds: 600,
......@@ -135,6 +136,7 @@ export const makeStateDump = async (): Promise<any> => {
owner: signer,
allowArbitraryContractDeployment: true,
},
l1CrossDomainMessengerConfig: {},
ethConfig: {
initialAmount: 0,
},
......
......@@ -11,7 +11,6 @@ import {
setProxyTarget,
NON_NULL_BYTES32,
ZERO_ADDRESS,
toHexString32,
getEthTime,
NULL_BYTES32,
increaseEthTime,
......@@ -62,10 +61,15 @@ describe('OVM_StateCommitmentChain', () => {
})
let Factory__OVM_StateCommitmentChain: ContractFactory
let Factory__OVM_ChainStorageContainer: ContractFactory
before(async () => {
Factory__OVM_StateCommitmentChain = await ethers.getContractFactory(
'OVM_StateCommitmentChain'
)
Factory__OVM_ChainStorageContainer = await ethers.getContractFactory(
'OVM_ChainStorageContainer'
)
})
let OVM_StateCommitmentChain: Contract
......@@ -75,7 +79,21 @@ describe('OVM_StateCommitmentChain', () => {
60 * 60 * 24 * 7, // 1 week fraud proof window
60 * 30 // 30 minute sequencer publish window
)
await OVM_StateCommitmentChain.init()
const batches = await Factory__OVM_ChainStorageContainer.deploy(
AddressManager.address,
'OVM_StateCommitmentChain'
)
await AddressManager.setAddress(
'OVM_ChainStorageContainer:SCC:batches',
batches.address
)
await AddressManager.setAddress(
'OVM_StateCommitmentChain',
OVM_StateCommitmentChain.address
)
})
describe('appendStateBatch', () => {
......@@ -176,7 +194,7 @@ describe('OVM_StateCommitmentChain', () => {
const batch = [NON_NULL_BYTES32]
const batchHeader = {
batchIndex: 0,
batchRoot: keccak256(NON_NULL_BYTES32),
batchRoot: NON_NULL_BYTES32,
batchSize: 1,
prevTotalElements: 0,
extraData: NULL_BYTES32,
......
......@@ -22,6 +22,18 @@ const NESTED_CREATED_CONTRACT = '0xcb964b3f4162a0d4f5c997b40e19da5a546bc36f'
const DUMMY_REVERT_DATA =
'0xdeadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420'
const NON_WHITELISTED_DEPLOYER = '0x1234123412341234123412341234123412341234'
const NON_WHITELISTED_DEPLOYER_KEY =
'0x0000000000000000000000001234123412341234123412341234123412341234'
const CREATED_BY_NON_WHITELISTED_DEPLOYER =
'0x794e4aa3be128b0fc01ba12543b70bf9d77072fc'
const WHITELISTED_DEPLOYER = '0x3456345634563456345634563456345634563456'
const WHITELISTED_DEPLOYER_KEY =
'0x0000000000000000000000003456345634563456345634563456345634563456'
const CREATED_BY_WHITELISTED_DEPLOYER =
'0x9f397a91ccb7cc924d1585f1053bc697d30f343f'
const test_ovmCREATE: TestDefinition = {
name: 'Basic tests for ovmCREATE',
preState: {
......@@ -656,6 +668,218 @@ const test_ovmCREATE: TestDefinition = {
],
},
],
subTests: [
{
name: 'Deployer whitelist tests',
preState: {
StateManager: {
accounts: {
[NON_WHITELISTED_DEPLOYER]: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
[WHITELISTED_DEPLOYER]: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
[CREATED_BY_WHITELISTED_DEPLOYER]: {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20),
},
},
contractStorage: {
['0x4200000000000000000000000000000000000002']: {
// initialized? true
'0x0000000000000000000000000000000000000000000000000000000000000010': getStorageXOR(
'0x' + '00'.repeat(31) + '01'
),
// allowArbitraryDeployment? false
'0x0000000000000000000000000000000000000000000000000000000000000012': getStorageXOR(
NULL_BYTES32
),
// non-whitelisted deployer is whitelisted? false
[NON_WHITELISTED_DEPLOYER_KEY]: getStorageXOR(NULL_BYTES32),
// whitelisted deployer is whitelisted? true
[WHITELISTED_DEPLOYER_KEY]: getStorageXOR(
'0x' + '00'.repeat(31) + '01'
),
},
},
verifiedContractStorage: {
['0x4200000000000000000000000000000000000002']: {
'0x0000000000000000000000000000000000000000000000000000000000000010': 1,
'0x0000000000000000000000000000000000000000000000000000000000000012': 1,
[NON_WHITELISTED_DEPLOYER_KEY]: 1,
[WHITELISTED_DEPLOYER_KEY]: 1,
},
},
},
},
parameters: [
{
name: 'ovmCREATE by WHITELISTED_DEPLOYER',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: WHITELISTED_DEPLOYER,
subSteps: [
{
functionName: 'ovmCREATE',
functionParams: {
bytecode: DUMMY_BYTECODE,
},
expectedReturnStatus: true,
expectedReturnValue: CREATED_BY_WHITELISTED_DEPLOYER,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCREATE by NON_WHITELISTED_DEPLOYER',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: NON_WHITELISTED_DEPLOYER,
subSteps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [],
},
expectedReturnStatus: false,
expectedReturnValue: {
flag: REVERT_FLAGS.CREATOR_NOT_ALLOWED,
onlyValidateFlag: true,
},
},
],
},
expectedReturnStatus: true,
expectedReturnValue: {
ovmSuccess: false,
returnData: '0x',
},
},
],
},
{
name: 'ovmCREATE2 by NON_WHITELISTED_DEPLOYER',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: NON_WHITELISTED_DEPLOYER,
subSteps: [
{
functionName: 'ovmCREATE2',
functionParams: {
salt: NULL_BYTES32,
bytecode: '0x',
},
expectedReturnStatus: false,
expectedReturnValue: {
flag: REVERT_FLAGS.CREATOR_NOT_ALLOWED,
onlyValidateFlag: true,
},
},
],
},
expectedReturnStatus: true,
expectedReturnValue: {
ovmSuccess: false,
returnData: '0x',
},
},
],
},
],
},
{
name: 'Deployer whitelist tests',
preState: {
StateManager: {
accounts: {
[NON_WHITELISTED_DEPLOYER]: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
[WHITELISTED_DEPLOYER]: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
[CREATED_BY_NON_WHITELISTED_DEPLOYER]: {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20),
},
},
contractStorage: {
['0x4200000000000000000000000000000000000002']: {
// initialized? true
'0x0000000000000000000000000000000000000000000000000000000000000010': getStorageXOR(
'0x' + '00'.repeat(31) + '01'
),
// allowArbitraryDeployment? true
'0x0000000000000000000000000000000000000000000000000000000000000012': getStorageXOR(
'0x' + '00'.repeat(31) + '01'
),
// non-whitelisted deployer is whitelisted? false
[NON_WHITELISTED_DEPLOYER_KEY]: getStorageXOR(NULL_BYTES32),
// whitelisted deployer is whitelisted? true
[WHITELISTED_DEPLOYER_KEY]: getStorageXOR(
'0x' + '00'.repeat(31) + '01'
),
},
},
verifiedContractStorage: {
['0x4200000000000000000000000000000000000002']: {
'0x0000000000000000000000000000000000000000000000000000000000000010': 1,
'0x0000000000000000000000000000000000000000000000000000000000000012': 1,
[NON_WHITELISTED_DEPLOYER_KEY]: 1,
[WHITELISTED_DEPLOYER_KEY]: 1,
},
},
},
},
subTests: [
{
name: 'when arbitrary contract deployment is enabled',
parameters: [
{
name: 'ovmCREATE by NON_WHITELISTED_DEPLOYER',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: OVM_TX_GAS_LIMIT / 2,
target: NON_WHITELISTED_DEPLOYER,
subSteps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [],
},
expectedReturnStatus: true,
expectedReturnValue: CREATED_BY_NON_WHITELISTED_DEPLOYER,
},
],
},
expectedReturnStatus: true,
},
],
},
],
},
],
},
],
}
const runner = new ExecutionManagerTestRunner()
......
......@@ -101,6 +101,27 @@ const test_run: TestDefinition = {
},
],
},
// This functionality has moved to the OVM_StateTransitioner,
// but leaving here for future reference on how to use this feature of the EM TestRunner.
// {
// name: 'run with insufficient gas supplied',
// steps: [
// {
// functionName: 'run',
// suppliedGas: OVM_TX_GAS_LIMIT / 2,
// functionParams: {
// timestamp: 0,
// queueOrigin: 0,
// entrypoint: '$OVM_CALL_HELPER',
// origin: ZERO_ADDRESS,
// msgSender: ZERO_ADDRESS,
// gasLimit: OVM_TX_GAS_LIMIT,
// subSteps: [],
// },
// expectedRevertValue: 'Not enough gas to execute deterministically',
// },
// ],
// },
],
}
......
......@@ -15,6 +15,7 @@ import {
setProxyTarget,
TrieTestGenerator,
ZERO_ADDRESS,
numberToHexString,
} from '../../../helpers'
import {
MockContract,
......@@ -271,7 +272,54 @@ describe('OVM_StateTransitioner', () => {
})
describe('applyTransaction', () => {
// TODO
it('Blocks execution if insufficient gas provided', async () => {
const gasLimit = 500_000
const transaction = {
timestamp: '0x12',
blockNumber: '0x34',
l1QueueOrigin: '0x00',
l1TxOrigin: ZERO_ADDRESS,
entrypoint: ZERO_ADDRESS,
gasLimit: numberToHexString(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,
]
)
)
OVM_StateTransitioner.smodify.set({
phase: 0,
transactionHash,
})
await expect(
OVM_StateTransitioner.applyTransaction(transaction, {
gasLimit: 30_000,
})
).to.be.revertedWith(
`Not enough gas to execute transaction deterministically`
)
})
})
describe('commitContractState', () => {
......
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { ContractFactory, Contract, BigNumber } from 'ethers'
/* Internal Imports */
import {
makeAddressManager,
ZERO_ADDRESS,
DUMMY_OVM_TRANSACTIONS,
NULL_BYTES32,
hashTransaction,
} from '../../../helpers'
const DUMMY_HASH = hashTransaction(DUMMY_OVM_TRANSACTIONS[0])
describe('OVM_StateTransitionerFactory', () => {
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
beforeEach(async () => {
OVM_StateTransitionerFactory = await Factory__OVM_StateTransitionerFactory.deploy(
AddressManager.address
)
})
describe('create', () => {
describe('when the sender is not the OVM_FraudVerifier', () => {
before(async () => {
await AddressManager.setAddress('OVM_FraudVerifier', ZERO_ADDRESS)
})
it('should revert', async () => {
await expect(
OVM_StateTransitionerFactory.create(
AddressManager.address,
NULL_BYTES32,
NULL_BYTES32,
DUMMY_HASH
)
).to.be.revertedWith(
'Create can only be done by the OVM_FraudVerifier.'
)
})
})
})
})
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, BigNumber } from 'ethers'
import { MerkleTree } from 'merkletreejs'
/* Internal Imports */
import {
fromHexString,
NON_NULL_BYTES32,
NULL_BYTES32,
toHexString,
} from '../../../helpers'
const NODE_COUNTS = [
2,
3,
7,
9,
13,
63,
64,
123,
128,
129,
255,
1021,
1023,
1024,
]
const hash = (el: Buffer | string): Buffer => {
return Buffer.from(ethers.utils.keccak256(el).slice(2), 'hex')
}
const fillDefaultHashes = (elements: string[]): string[] => {
const filled: string[] = []
for (let i = 0; i < Math.pow(2, Math.ceil(Math.log2(elements.length))); i++) {
if (i < elements.length) {
filled.push(elements[i])
} else {
filled.push(ethers.utils.keccak256('0x' + '00'.repeat(32)))
}
}
return filled
}
describe('Lib_MerkleTree', () => {
let Lib_MerkleTree: Contract
before(async () => {
Lib_MerkleTree = await (
await ethers.getContractFactory('TestLib_MerkleTree')
).deploy()
})
describe('getMerkleRoot', () => {
describe('when no elements are provided', () => {
const elements = []
it('should revert', async () => {
await expect(Lib_MerkleTree.getMerkleRoot(elements)).to.be.revertedWith(
'Lib_MerkleTree: Must provide at least one leaf hash.'
)
})
})
describe('when a single element is provided', () => {
const elements = [ethers.utils.keccak256('0x1234')]
it('should return the input element', async () => {
expect(await Lib_MerkleTree.getMerkleRoot(elements)).to.equal(
elements[0]
)
})
})
describe('when more than one element is provided', () => {
for (const size of NODE_COUNTS) {
it(`should generate the correct root when ${size} elements are provided`, async () => {
const elements = [...Array(size)].map((_, i) => {
return ethers.utils.keccak256(BigNumber.from(i).toHexString())
})
const bufs = fillDefaultHashes(elements).map((element) => {
return fromHexString(element)
})
const tree = new MerkleTree(bufs, hash)
expect(await Lib_MerkleTree.getMerkleRoot(bufs)).to.equal(
toHexString(tree.getRoot())
)
})
}
})
})
describe('verify', () => {
describe('when total elements is zero', () => {
const totalLeaves = 0
it('should revert', async () => {
await expect(
Lib_MerkleTree.verify(NULL_BYTES32, NULL_BYTES32, 0, [], totalLeaves)
).to.be.revertedWith(
'Lib_MerkleTree: Total leaves must be greater than zero.'
)
})
})
describe('when an index is out of bounds', () => {
const totalLeaves = 1
const index = 2
it('should revert', async () => {
await expect(
Lib_MerkleTree.verify(
NULL_BYTES32,
NULL_BYTES32,
index,
[],
totalLeaves
)
).to.be.revertedWith('Lib_MerkleTree: Index out of bounds.')
})
})
describe('when total siblings does not match provided total leaves', () => {
const totalLeaves = 8
const siblings = [NULL_BYTES32, NULL_BYTES32]
it('should revert', async () => {
await expect(
Lib_MerkleTree.verify(
NULL_BYTES32,
NULL_BYTES32,
0,
siblings,
totalLeaves
)
).to.be.revertedWith(
'Lib_MerkleTree: Total siblings does not correctly correspond to total leaves.'
)
})
})
describe('with valid proof for a single element', () => {
const root = NON_NULL_BYTES32
const leaf = NON_NULL_BYTES32
const index = 0
const siblings = []
const totalLeaves = 1
it('should succeed', async () => {
expect(
await Lib_MerkleTree.verify(root, leaf, index, siblings, totalLeaves)
).to.equal(true)
})
})
describe('with valid proof for more than one element', () => {
for (const size of NODE_COUNTS) {
describe(`for a tree with ${size} total elements`, () => {
const elements = [...Array(size)].map((_, i) => {
return ethers.utils.keccak256(BigNumber.from(i).toHexString())
})
const bufs = fillDefaultHashes(elements).map((element) => {
return fromHexString(element)
})
const tree = new MerkleTree(bufs, hash)
for (let i = 0; i < size; i += Math.ceil(size / 8)) {
it(`should verify a proof for the ${i}(th/st/rd, whatever) element`, async () => {
const proof = tree.getProof(bufs[i], i).map((element) => {
return element.data
})
expect(
await Lib_MerkleTree.verify(
tree.getRoot(),
bufs[i],
i,
proof,
size
)
).to.equal(true)
})
}
})
}
})
})
})
......@@ -43,4 +43,5 @@ export const REVERT_FLAGS = {
CREATE_COLLISION: 6,
STATIC_VIOLATION: 7,
CREATE_EXCEPTION: 8,
CREATOR_NOT_ALLOWED: 9,
}
......@@ -17,6 +17,7 @@ export const DEFAULT_ACCOUNTS_BUIDLER = defaultAccounts.map((account) => {
export const OVM_TX_GAS_LIMIT = 10_000_000
export const RUN_OVM_TEST_GAS = 20_000_000
export const FORCE_INCLUSION_PERIOD_SECONDS = 600
export const FORCE_INCLUSION_PERIOD_BLOCKS = 600 / 12
export const NULL_BYTES32 = makeHexString('00', 32)
export const NON_NULL_BYTES32 = makeHexString('11', 32)
......
......@@ -35,7 +35,9 @@ import {
OVM_TX_GAS_LIMIT,
RUN_OVM_TEST_GAS,
NON_NULL_BYTES32,
NULL_BYTES32,
} from '../constants'
import { getStorageXOR } from '../'
export class ExecutionManagerTestRunner {
private snapshot: string
......@@ -45,17 +47,47 @@ export class ExecutionManagerTestRunner {
OVM_ExecutionManager: ModifiableContract
Helper_TestRunner: Contract
Factory__Helper_TestRunner_CREATE: ContractFactory
OVM_DeployerWhitelist: Contract
} = {
OVM_SafetyChecker: undefined,
OVM_StateManager: undefined,
OVM_ExecutionManager: undefined,
Helper_TestRunner: undefined,
Factory__Helper_TestRunner_CREATE: undefined,
OVM_DeployerWhitelist: undefined,
}
// Default pre-state with contract deployer whitelist NOT initialized.
private defaultPreState = {
StateManager: {
owner: '$OVM_EXECUTION_MANAGER',
accounts: {
['0x4200000000000000000000000000000000000002']: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_DEPLOYER_WHITELIST',
},
},
contractStorage: {
['0x4200000000000000000000000000000000000002']: {
'0x0000000000000000000000000000000000000000000000000000000000000010': getStorageXOR(
NULL_BYTES32
),
},
},
verifiedContractStorage: {
['0x4200000000000000000000000000000000000002']: {
'0x0000000000000000000000000000000000000000000000000000000000000010': true,
},
},
},
}
public run(test: TestDefinition) {
test.preState = test.preState || {}
test.postState = test.postState || {}
;(test.preState = merge(
cloneDeep(this.defaultPreState),
cloneDeep(test.preState)
)),
(test.postState = test.postState || {})
describe(`OVM_ExecutionManager Test: ${test.name}`, () => {
test.subTests?.map((subTest) => {
......@@ -164,6 +196,12 @@ export class ExecutionManagerTestRunner {
this.contracts.OVM_SafetyChecker.address
)
const DeployerWhitelist = await (
await ethers.getContractFactory('OVM_DeployerWhitelist')
).deploy()
this.contracts.OVM_DeployerWhitelist = DeployerWhitelist
this.contracts.OVM_ExecutionManager = await (
await smoddit('OVM_ExecutionManager')
).deploy(
......@@ -211,6 +249,8 @@ export class ExecutionManagerTestRunner {
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.startsWith('$DUMMY_OVM_ADDRESS_')) {
return ExecutionManagerTestRunner.getDummyAddress(kv)
} else {
......@@ -262,7 +302,7 @@ export class ExecutionManagerTestRunner {
calldata = this.encodeFunctionData(runStep)
}
await this.contracts.OVM_ExecutionManager.run(
const toRun = this.contracts.OVM_ExecutionManager.run(
{
timestamp: step.functionParams.timestamp,
blockNumber: 0,
......@@ -273,8 +313,13 @@ export class ExecutionManagerTestRunner {
data: calldata,
},
this.contracts.OVM_StateManager.address,
{ gasLimit: RUN_OVM_TEST_GAS }
{ 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(
OVM_TX_GAS_LIMIT,
......@@ -294,9 +339,19 @@ export class ExecutionManagerTestRunner {
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
......@@ -306,7 +361,8 @@ export class ExecutionManagerTestRunner {
if (
isRevertFlagError(step.expectedReturnValue) &&
(step.expectedReturnValue.flag === REVERT_FLAGS.INVALID_STATE_ACCESS ||
step.expectedReturnValue.flag === REVERT_FLAGS.STATIC_VIOLATION)
step.expectedReturnValue.flag === REVERT_FLAGS.STATIC_VIOLATION ||
step.expectedReturnValue.flag === REVERT_FLAGS.CREATOR_NOT_ALLOWED)
) {
return step.expectedReturnStatus
} else {
......@@ -365,6 +421,16 @@ export class ExecutionManagerTestRunner {
}) || []
).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']
}
......
......@@ -19,6 +19,7 @@ type RevertFlagError = {
nuisanceGasLeft?: number
ovmGasRefund?: number
data?: string
onlyValidateFlag?: boolean
}
interface TestStep_evm {
......@@ -145,6 +146,7 @@ interface TestStep_CREATEEOA {
export interface TestStep_Run {
functionName: 'run'
suppliedGas?: number
functionParams: {
timestamp: number
queueOrigin: number
......@@ -155,6 +157,7 @@ export interface TestStep_Run {
data?: string
subSteps?: TestStep[]
}
expectedRevertValue?: string
}
export type TestStep =
......@@ -177,6 +180,7 @@ export interface ParsedTestStep {
functionData: string
expectedReturnStatus: boolean
expectedReturnData: string
onlyValidateFlag: boolean
}
export const isRevertFlagError = (
......
......@@ -19,9 +19,12 @@ export const increaseEthTime = async (
export const getBlockTime = async (
provider: any,
block: number
block?: number
): Promise<number> => {
await provider.send('evm_mine', [])
if (!!block) {
block = await getNextBlockNumber(provider)
}
return (await provider.getBlock(block)).timestamp
}
......
......@@ -2123,6 +2123,11 @@ buffer-from@^1.0.0:
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
buffer-reverse@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-reverse/-/buffer-reverse-1.0.1.tgz#49283c8efa6f901bc01fa3304d06027971ae2f60"
integrity sha1-SSg8jvpvkBvAH6MwTQYCeXGuL2A=
buffer-to-arraybuffer@^0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a"
......@@ -2686,6 +2691,11 @@ crypto-browserify@3.12.0:
randombytes "^2.0.0"
randomfill "^1.0.3"
crypto-js@^3.1.9-1:
version "3.3.0"
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b"
integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==
d@1, d@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
......@@ -4792,6 +4802,11 @@ is-buffer@^1.1.5:
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
is-buffer@^2.0.3:
version "2.0.5"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191"
integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
is-buffer@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623"
......@@ -5736,6 +5751,11 @@ merge-descriptors@1.0.1:
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
merkle-lib@^2.0.10:
version "2.0.10"
resolved "https://registry.yarnpkg.com/merkle-lib/-/merkle-lib-2.0.10.tgz#82b8dbae75e27a7785388b73f9d7725d0f6f3326"
integrity sha1-grjbrnXieneFOItz+ddyXQ9vMyY=
merkle-patricia-tree@3.0.0, merkle-patricia-tree@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-3.0.0.tgz#448d85415565df72febc33ca362b8b614f5a58f8"
......@@ -5776,6 +5796,17 @@ merkle-patricia-tree@^4.0.0:
rlp "^2.2.4"
semaphore-async-await "^1.5.1"
merkletreejs@^0.2.12:
version "0.2.12"
resolved "https://registry.yarnpkg.com/merkletreejs/-/merkletreejs-0.2.12.tgz#5d1302339777941f16dd3227d8d9789dcba03655"
integrity sha512-2w8t/s0Io+qwbG/M8gokb0c9MkTnPtnseJFnMFxd153MwMIaLUzBP9bB4uZYxwOu0wesk5QbuEp/86lO+dlb6A==
dependencies:
buffer-reverse "^1.0.1"
crypto-js "^3.1.9-1"
is-buffer "^2.0.3"
merkle-lib "^2.0.10"
treeify "^1.1.0"
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
......@@ -7986,6 +8017,11 @@ tough-cookie@~2.5.0:
psl "^1.1.28"
punycode "^2.1.1"
treeify@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8"
integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==
trim-repeated@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21"
......
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