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; ...@@ -11,7 +11,7 @@ const sequencerKey = env.SEQUENCER_PRIVATE_KEY;
let SEQUENCER_ADDRESS = env.SEQUENCER_ADDRESS; let SEQUENCER_ADDRESS = env.SEQUENCER_ADDRESS;
const web3Url = env.L1_NODE_WEB3_URL || 'http://127.0.0.1:8545'; 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 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 MAX_GAS_PER_QUEUE_PER_EPOCH = env.MAX_GAS_PER_QUEUE_PER_EPOCH || 250000000;
const SECONDS_PER_EPOCH = env.SECONDS_PER_EPOCH || 0; const SECONDS_PER_EPOCH = env.SECONDS_PER_EPOCH || 0;
let WHITELIST_OWNER = env.WHITELIST_OWNER; let WHITELIST_OWNER = env.WHITELIST_OWNER;
...@@ -22,6 +22,7 @@ const SEQUENCER_PUBLISH_WINDOW_SECONDS = env.SEQUENCER_PUBLISH_WINDOW_SECONDS || ...@@ -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 CHAIN_ID = env.CHAIN_ID || 420; // layer 2 chainid
const USE_LEDGER = env.USE_LEDGER || false; const USE_LEDGER = env.USE_LEDGER || false;
const HD_PATH = env.HD_PATH || utils.defaultPath; const HD_PATH = env.HD_PATH || utils.defaultPath;
const BLOCK_TIME_SECONDS = env.BLOCK_TIME_SECONDS || 15;
const L2_CROSS_DOMAIN_MESSENGER_ADDRESS = const L2_CROSS_DOMAIN_MESSENGER_ADDRESS =
env.L2_CROSS_DOMAIN_MESSENGER_ADDRESS || '0x4200000000000000000000000000000000000007'; env.L2_CROSS_DOMAIN_MESSENGER_ADDRESS || '0x4200000000000000000000000000000000000007';
...@@ -57,6 +58,7 @@ const L2_CROSS_DOMAIN_MESSENGER_ADDRESS = ...@@ -57,6 +58,7 @@ const L2_CROSS_DOMAIN_MESSENGER_ADDRESS =
transactionChainConfig: { transactionChainConfig: {
forceInclusionPeriodSeconds: FORCE_INCLUSION_PERIOD_SECONDS, forceInclusionPeriodSeconds: FORCE_INCLUSION_PERIOD_SECONDS,
sequencer: SEQUENCER_ADDRESS, sequencer: SEQUENCER_ADDRESS,
forceInclusionPeriodBlocks: Math.ceil(FORCE_INCLUSION_PERIOD_SECONDS/BLOCK_TIME_SECONDS),
}, },
stateChainConfig: { stateChainConfig: {
fraudProofWindowSeconds: FRAUD_PROOF_WINDOW_SECONDS, fraudProofWindowSeconds: FRAUD_PROOF_WINDOW_SECONDS,
...@@ -66,6 +68,9 @@ const L2_CROSS_DOMAIN_MESSENGER_ADDRESS = ...@@ -66,6 +68,9 @@ const L2_CROSS_DOMAIN_MESSENGER_ADDRESS =
ovmCHAINID: CHAIN_ID, ovmCHAINID: CHAIN_ID,
L2CrossDomainMessengerAddress: L2_CROSS_DOMAIN_MESSENGER_ADDRESS L2CrossDomainMessengerAddress: L2_CROSS_DOMAIN_MESSENGER_ADDRESS
}, },
l1CrossDomainMessengerConfig: {
relayerAddress: SEQUENCER_ADDRESS,
},
ovmGasMeteringConfig: { ovmGasMeteringConfig: {
minTransactionGasLimit: MIN_TRANSACTION_GAS_LIMIT, minTransactionGasLimit: MIN_TRANSACTION_GAS_LIMIT,
maxTransactionGasLimit: MAX_TRANSACTION_GAS_LIMIT, maxTransactionGasLimit: MAX_TRANSACTION_GAS_LIMIT,
......
...@@ -5,42 +5,32 @@ pragma experimental ABIEncoderV2; ...@@ -5,42 +5,32 @@ pragma experimental ABIEncoderV2;
/* Library Imports */ /* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol"; import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol"; import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_MerkleUtils } from "../../libraries/utils/Lib_MerkleUtils.sol"; import { Lib_MerkleTree } from "../../libraries/utils/Lib_MerkleTree.sol";
import { Lib_RingBuffer, iRingBufferOverwriter } from "../../libraries/utils/Lib_RingBuffer.sol"; import { Lib_Math } from "../../libraries/utils/Lib_Math.sol";
/* Interface Imports */ /* Interface Imports */
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol"; import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
import { iOVM_ChainStorageContainer } from "../../iOVM/chain/iOVM_ChainStorageContainer.sol";
/* Contract Imports */ /* Contract Imports */
import { OVM_ExecutionManager } from "../execution/OVM_ExecutionManager.sol"; import { OVM_ExecutionManager } from "../execution/OVM_ExecutionManager.sol";
library Math {
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
if (x < y) {
return x;
}
return y;
}
}
/** /**
* @title OVM_CanonicalTransactionChain * @title OVM_CanonicalTransactionChain
*/ */
contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_AddressResolver { contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_AddressResolver {
using Lib_RingBuffer for Lib_RingBuffer.RingBuffer;
/************* /*************
* Constants * * Constants *
*************/ *************/
// L2 tx gas-related
uint256 constant public MIN_ROLLUP_TX_GAS = 20000; uint256 constant public MIN_ROLLUP_TX_GAS = 20000;
uint256 constant public MAX_ROLLUP_TX_SIZE = 10000; uint256 constant public MAX_ROLLUP_TX_SIZE = 10000;
uint256 constant public L2_GAS_DISCOUNT_DIVISOR = 10; uint256 constant public L2_GAS_DISCOUNT_DIVISOR = 10;
// Encoding constants (all in bytes) // Encoding-related (all in bytes)
uint256 constant internal BATCH_CONTEXT_SIZE = 16; uint256 constant internal BATCH_CONTEXT_SIZE = 16;
uint256 constant internal BATCH_CONTEXT_LENGTH_POS = 12; uint256 constant internal BATCH_CONTEXT_LENGTH_POS = 12;
uint256 constant internal BATCH_CONTEXT_START_POS = 15; uint256 constant internal BATCH_CONTEXT_START_POS = 15;
...@@ -53,9 +43,9 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -53,9 +43,9 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
*************/ *************/
uint256 internal forceInclusionPeriodSeconds; uint256 internal forceInclusionPeriodSeconds;
uint256 internal forceInclusionPeriodBlocks;
uint256 internal maxTransactionGasLimit;
uint256 internal lastOVMTimestamp; uint256 internal lastOVMTimestamp;
Lib_RingBuffer.RingBuffer internal batches;
Lib_RingBuffer.RingBuffer internal queue;
/*************** /***************
...@@ -64,11 +54,15 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -64,11 +54,15 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
constructor( constructor(
address _libAddressManager, address _libAddressManager,
uint256 _forceInclusionPeriodSeconds uint256 _forceInclusionPeriodSeconds,
uint256 _forceInclusionPeriodBlocks,
uint256 _maxTransactionGasLimit
) )
Lib_AddressResolver(_libAddressManager) Lib_AddressResolver(_libAddressManager)
{ {
forceInclusionPeriodSeconds = _forceInclusionPeriodSeconds; forceInclusionPeriodSeconds = _forceInclusionPeriodSeconds;
forceInclusionPeriodBlocks = _forceInclusionPeriodBlocks;
maxTransactionGasLimit = _maxTransactionGasLimit;
} }
...@@ -77,22 +71,34 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -77,22 +71,34 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
********************/ ********************/
/** /**
* @inheritdoc iOVM_CanonicalTransactionChain * Accesses the batch storage container.
* @return Reference to the batch storage container.
*/ */
function init() function batches()
override
public public
view
returns (
iOVM_ChainStorageContainer
)
{ {
batches.init( return iOVM_ChainStorageContainer(
16, resolve("OVM_ChainStorageContainer:CTC:batches")
Lib_OVMCodec.RING_BUFFER_CTC_BATCHES,
iRingBufferOverwriter(resolve("OVM_StateCommitmentChain"))
); );
}
queue.init( /**
16, * Accesses the queue storage container.
Lib_OVMCodec.RING_BUFFER_CTC_QUEUE, * @return Reference to the queue storage container.
iRingBufferOverwriter(resolve("OVM_StateCommitmentChain")) */
function queue()
public
view
returns (
iOVM_ChainStorageContainer
)
{
return iOVM_ChainStorageContainer(
resolve("OVM_ChainStorageContainer:CTC:queue")
); );
} }
...@@ -107,7 +113,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -107,7 +113,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
uint256 _totalElements uint256 _totalElements
) )
{ {
(uint40 totalElements,) = _getBatchExtraData(); (uint40 totalElements,,,) = _getBatchExtraData();
return uint256(totalElements); return uint256(totalElements);
} }
...@@ -122,7 +128,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -122,7 +128,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
uint256 _totalBatches uint256 _totalBatches
) )
{ {
return uint256(batches.getLength()); return batches().length();
} }
/** /**
...@@ -136,7 +142,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -136,7 +142,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
uint40 uint40
) )
{ {
(, uint40 nextQueueIndex) = _getBatchExtraData(); (, uint40 nextQueueIndex,,) = _getBatchExtraData();
return nextQueueIndex; return nextQueueIndex;
} }
...@@ -154,8 +160,8 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -154,8 +160,8 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
) )
{ {
uint40 trueIndex = uint40(_index * 2); uint40 trueIndex = uint40(_index * 2);
bytes32 queueRoot = queue.get(trueIndex); bytes32 queueRoot = queue().get(trueIndex);
bytes32 timestampAndBlockNumber = queue.get(trueIndex + 1); bytes32 timestampAndBlockNumber = queue().get(trueIndex + 1);
uint40 elementTimestamp; uint40 elementTimestamp;
uint40 elementBlockNumber; uint40 elementBlockNumber;
...@@ -182,7 +188,24 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -182,7 +188,24 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
uint40 uint40
) )
{ {
return _getQueueLength() - getNextQueueIndex(); return getQueueLength() - getNextQueueIndex();
}
/**
* @inheritdoc iOVM_CanonicalTransactionChain
*/
function getQueueLength()
override
public
view
returns (
uint40
)
{
// The underlying queue data structure stores 2 elements
// per insertion, so to get the real queue length we need
// to divide by 2. See the usage of `push2(..)`.
return uint40(queue().length() / 2);
} }
/** /**
...@@ -198,7 +221,12 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -198,7 +221,12 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
{ {
require( require(
_data.length <= MAX_ROLLUP_TX_SIZE, _data.length <= MAX_ROLLUP_TX_SIZE,
"Transaction exceeds maximum rollup transaction data size." "Transaction data size exceeds maximum for rollup transaction."
);
require(
_gasLimit <= maxTransactionGasLimit,
"Transaction gas limit exceeds maximum for rollup transaction."
); );
require( require(
...@@ -241,12 +269,12 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -241,12 +269,12 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
timestampAndBlockNumber := or(timestampAndBlockNumber, shl(40, number())) timestampAndBlockNumber := or(timestampAndBlockNumber, shl(40, number()))
} }
queue.push2( queue().push2(
transactionHash, transactionHash,
timestampAndBlockNumber timestampAndBlockNumber
); );
uint40 queueIndex = queue.getLength() / 2; uint256 queueIndex = queue().length() / 2;
emit TransactionEnqueued( emit TransactionEnqueued(
msg.sender, msg.sender,
_target, _target,
...@@ -269,7 +297,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -269,7 +297,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
// Disable `appendQueueBatch` for minnet // Disable `appendQueueBatch` for minnet
revert("appendQueueBatch is currently disabled."); revert("appendQueueBatch is currently disabled.");
_numQueuedTransactions = Math.min(_numQueuedTransactions, getNumPendingQueueElements()); _numQueuedTransactions = Lib_Math.min(_numQueuedTransactions, getNumPendingQueueElements());
require( require(
_numQueuedTransactions > 0, _numQueuedTransactions > 0,
"Must append more than zero transactions." "Must append more than zero transactions."
...@@ -290,10 +318,14 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -290,10 +318,14 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
nextQueueIndex++; nextQueueIndex++;
} }
Lib_OVMCodec.QueueElement memory lastElement = getQueueElement(nextQueueIndex - 1);
_appendBatch( _appendBatch(
Lib_MerkleUtils.getMerkleRoot(leaves), Lib_MerkleTree.getMerkleRoot(leaves),
_numQueuedTransactions, _numQueuedTransactions,
_numQueuedTransactions _numQueuedTransactions,
lastElement.timestamp,
lastElement.blockNumber
); );
emit QueueBatchAppended( emit QueueBatchAppended(
...@@ -310,17 +342,17 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -310,17 +342,17 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
override override
public public
{ {
uint40 shouldStartAtBatch; uint40 shouldStartAtElement;
uint24 totalElementsToAppend; uint24 totalElementsToAppend;
uint24 numContexts; uint24 numContexts;
assembly { assembly {
shouldStartAtBatch := shr(216, calldataload(4)) shouldStartAtElement := shr(216, calldataload(4))
totalElementsToAppend := shr(232, calldataload(9)) totalElementsToAppend := shr(232, calldataload(9))
numContexts := shr(232, calldataload(12)) numContexts := shr(232, calldataload(12))
} }
require( require(
shouldStartAtBatch == getTotalElements(), shouldStartAtElement == getTotalElements(),
"Actual batch start index does not match expected start index." "Actual batch start index does not match expected start index."
); );
...@@ -350,50 +382,83 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -350,50 +382,83 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
"Not enough BatchContexts provided." "Not enough BatchContexts provided."
); );
// Get queue length for future comparison/
uint40 queueLength = getQueueLength();
// Initialize the array of canonical chain leaves that we will append.
bytes32[] memory leaves = new bytes32[](totalElementsToAppend); bytes32[] memory leaves = new bytes32[](totalElementsToAppend);
uint32 transactionIndex = 0; // Each leaf index corresponds to a tx, either sequenced or enqueued.
uint32 numSequencerTransactionsProcessed = 0; uint32 leafIndex = 0;
// Counter for number of sequencer transactions appended so far.
uint32 numSequencerTransactions = 0;
// We will sequentially append leaves which are pointers to the queue.
// The initial queue index is what is currently in storage.
uint40 nextQueueIndex = getNextQueueIndex(); uint40 nextQueueIndex = getNextQueueIndex();
uint40 queueLength = _getQueueLength();
BatchContext memory curContext;
for (uint32 i = 0; i < numContexts; i++) { for (uint32 i = 0; i < numContexts; i++) {
BatchContext memory context = _getBatchContext(i); BatchContext memory nextContext = _getBatchContext(i);
if (i == 0) {
_validateFirstBatchContext(nextContext);
}
_validateNextBatchContext(curContext, nextContext, nextQueueIndex);
curContext = nextContext;
for (uint32 j = 0; j < context.numSequencedTransactions; j++) { for (uint32 j = 0; j < curContext.numSequencedTransactions; j++) {
uint256 txDataLength; uint256 txDataLength;
assembly { assembly {
txDataLength := shr(232, calldataload(nextTransactionPtr)) txDataLength := shr(232, calldataload(nextTransactionPtr))
} }
leaves[transactionIndex] = _getSequencerLeafHash(context, nextTransactionPtr, txDataLength); leaves[leafIndex] = _getSequencerLeafHash(curContext, nextTransactionPtr, txDataLength);
nextTransactionPtr += uint40(TX_DATA_HEADER_SIZE + txDataLength); nextTransactionPtr += uint40(TX_DATA_HEADER_SIZE + txDataLength);
numSequencerTransactionsProcessed++; numSequencerTransactions++;
transactionIndex++; leafIndex++;
} }
for (uint32 j = 0; j < context.numSubsequentQueueTransactions; j++) { for (uint32 j = 0; j < curContext.numSubsequentQueueTransactions; j++) {
require(nextQueueIndex < queueLength, "Not enough queued transactions to append."); require(nextQueueIndex < queueLength, "Not enough queued transactions to append.");
leaves[transactionIndex] = _getQueueLeafHash(nextQueueIndex); leaves[leafIndex] = _getQueueLeafHash(nextQueueIndex);
nextQueueIndex++; nextQueueIndex++;
transactionIndex++; leafIndex++;
} }
} }
_validateFinalBatchContext(curContext);
require( require(
calldataSize == nextTransactionPtr, calldataSize == nextTransactionPtr,
"Not all sequencer transactions were processed." "Not all sequencer transactions were processed."
); );
require( require(
transactionIndex == totalElementsToAppend, leafIndex == totalElementsToAppend,
"Actual transaction index does not match expected total elements to append." "Actual transaction index does not match expected total elements to append."
); );
uint40 numQueuedTransactions = totalElementsToAppend - numSequencerTransactionsProcessed; // Generate the required metadata that we need to append this batch
uint40 numQueuedTransactions = totalElementsToAppend - numSequencerTransactions;
uint40 timestamp;
uint40 blockNumber;
if (curContext.numSubsequentQueueTransactions == 0) {
// The last element is a sequencer tx, therefore pull timestamp and block number from the last context.
timestamp = uint40(curContext.timestamp);
blockNumber = uint40(curContext.blockNumber);
} else {
// The last element is a queue tx, therefore pull timestamp and block number from the queue element.
Lib_OVMCodec.QueueElement memory lastElement = getQueueElement(nextQueueIndex - 1);
timestamp = lastElement.timestamp;
blockNumber = lastElement.blockNumber;
}
_appendBatch( _appendBatch(
Lib_MerkleUtils.getMerkleRoot(leaves), Lib_MerkleTree.getMerkleRoot(leaves),
totalElementsToAppend, totalElementsToAppend,
numQueuedTransactions numQueuedTransactions,
timestamp,
blockNumber
); );
emit SequencerBatchAppended( emit SequencerBatchAppended(
...@@ -485,23 +550,31 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -485,23 +550,31 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
internal internal
view view
returns ( returns (
uint40,
uint40,
uint40, uint40,
uint40 uint40
) )
{ {
bytes27 extraData = batches.getExtraData(); bytes27 extraData = batches().getGlobalMetadata();
uint40 totalElements; uint40 totalElements;
uint40 nextQueueIndex; uint40 nextQueueIndex;
uint40 lastTimestamp;
uint40 lastBlockNumber;
assembly { assembly {
extraData := shr(40, extraData) extraData := shr(40, extraData)
totalElements := and(extraData, 0x000000000000000000000000000000000000000000000000000000FFFFFFFFFF) totalElements := and(extraData, 0x000000000000000000000000000000000000000000000000000000FFFFFFFFFF)
nextQueueIndex := shr(40, and(extraData, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000)) nextQueueIndex := shr(40, and(extraData, 0x00000000000000000000000000000000000000000000FFFFFFFFFF0000000000))
lastTimestamp := shr(80, and(extraData, 0x0000000000000000000000000000000000FFFFFFFFFF00000000000000000000))
lastBlockNumber := shr(120, and(extraData, 0x000000000000000000000000FFFFFFFFFF000000000000000000000000000000))
} }
return ( return (
totalElements, totalElements,
nextQueueIndex nextQueueIndex,
lastTimestamp,
lastBlockNumber
); );
} }
...@@ -509,11 +582,15 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -509,11 +582,15 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
* Encodes the batch context for the extra data. * Encodes the batch context for the extra data.
* @param _totalElements Total number of elements submitted. * @param _totalElements Total number of elements submitted.
* @param _nextQueueIndex Index of the next queue element. * @param _nextQueueIndex Index of the next queue element.
* @param _timestamp Timestamp for the last batch.
* @param _blockNumber Block number of the last batch.
* @return Encoded batch context. * @return Encoded batch context.
*/ */
function _makeBatchExtraData( function _makeBatchExtraData(
uint40 _totalElements, uint40 _totalElements,
uint40 _nextQueueIndex uint40 _nextQueueIndex,
uint40 _timestamp,
uint40 _blockNumber
) )
internal internal
pure pure
...@@ -525,6 +602,8 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -525,6 +602,8 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
assembly { assembly {
extraData := _totalElements extraData := _totalElements
extraData := or(extraData, shl(40, _nextQueueIndex)) extraData := or(extraData, shl(40, _nextQueueIndex))
extraData := or(extraData, shl(80, _timestamp))
extraData := or(extraData, shl(120, _blockNumber))
extraData := shl(40, extraData) extraData := shl(40, extraData)
} }
...@@ -570,7 +649,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -570,7 +649,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
// The underlying queue data structure stores 2 elements // The underlying queue data structure stores 2 elements
// per insertion, so to get the real queue length we need // per insertion, so to get the real queue length we need
// to divide by 2. See the usage of `push2(..)`. // to divide by 2. See the usage of `push2(..)`.
return queue.getLength() / 2; return uint40(queue().length() / 2);
} }
/** /**
...@@ -664,18 +743,22 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -664,18 +743,22 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
* @param _transactionRoot Root of the transaction tree for this batch. * @param _transactionRoot Root of the transaction tree for this batch.
* @param _batchSize Number of elements in the batch. * @param _batchSize Number of elements in the batch.
* @param _numQueuedTransactions Number of queue transactions in the batch. * @param _numQueuedTransactions Number of queue transactions in the batch.
* @param _timestamp The latest batch timestamp.
* @param _blockNumber The latest batch blockNumber.
*/ */
function _appendBatch( function _appendBatch(
bytes32 _transactionRoot, bytes32 _transactionRoot,
uint256 _batchSize, uint256 _batchSize,
uint256 _numQueuedTransactions uint256 _numQueuedTransactions,
uint40 _timestamp,
uint40 _blockNumber
) )
internal internal
{ {
(uint40 totalElements, uint40 nextQueueIndex) = _getBatchExtraData(); (uint40 totalElements, uint40 nextQueueIndex, uint40 lastTimestamp, uint40 lastBlockNumber) = _getBatchExtraData();
Lib_OVMCodec.ChainBatchHeader memory header = Lib_OVMCodec.ChainBatchHeader({ Lib_OVMCodec.ChainBatchHeader memory header = Lib_OVMCodec.ChainBatchHeader({
batchIndex: batches.getLength(), batchIndex: batches().length(),
batchRoot: _transactionRoot, batchRoot: _transactionRoot,
batchSize: _batchSize, batchSize: _batchSize,
prevTotalElements: totalElements, prevTotalElements: totalElements,
...@@ -693,44 +776,97 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -693,44 +776,97 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
bytes32 batchHeaderHash = Lib_OVMCodec.hashBatchHeader(header); bytes32 batchHeaderHash = Lib_OVMCodec.hashBatchHeader(header);
bytes27 latestBatchContext = _makeBatchExtraData( bytes27 latestBatchContext = _makeBatchExtraData(
totalElements + uint40(header.batchSize), totalElements + uint40(header.batchSize),
nextQueueIndex + uint40(_numQueuedTransactions) nextQueueIndex + uint40(_numQueuedTransactions),
_timestamp,
_blockNumber
); );
batches.push(batchHeaderHash, latestBatchContext); batches().push(batchHeaderHash, latestBatchContext);
} }
/** /**
* Checks that a given batch context is valid. * Checks that the first batch context in a sequencer submission is valid
* @param _context Batch context to validate. * @param _firstContext The batch context to validate.
* @param _nextQueueIndex Index of the next queue element to process.
*/ */
function _validateBatchContext( function _validateFirstBatchContext(
BatchContext memory _context, BatchContext memory _firstContext
uint40 _nextQueueIndex
) )
internal internal
view view
{ {
if (getNumPendingQueueElements() == 0) { // If there are existing elements, this batch must come later.
return; if (getTotalElements() > 0) {
(,, uint40 lastTimestamp, uint40 lastBlockNumber) = _getBatchExtraData();
require(_firstContext.blockNumber >= lastBlockNumber, "Context block number is lower than last submitted.");
require(_firstContext.timestamp >= lastTimestamp, "Context timestamp is lower than last submitted.");
} }
// Sequencer cannot submit contexts which are more than the force inclusion period old.
require(_firstContext.timestamp + forceInclusionPeriodSeconds >= block.timestamp, "Context timestamp too far in the past.");
require(_firstContext.blockNumber + forceInclusionPeriodBlocks >= block.number, "Context block number too far in the past.");
}
Lib_OVMCodec.QueueElement memory nextQueueElement = getQueueElement(_nextQueueIndex); /**
* Checks that a given batch context is valid based on its previous context, and the next queue elemtent.
* @param _prevContext The previously validated batch context.
* @param _nextContext The batch context to validate with this call.
* @param _nextQueueIndex Index of the next queue element to process for the _nextContext's subsequentQueueElements.
*/
function _validateNextBatchContext(
BatchContext memory _prevContext,
BatchContext memory _nextContext,
uint40 _nextQueueIndex
)
internal
view
{
// All sequencer transactions' times must increase from the previous ones.
require( require(
block.timestamp < nextQueueElement.timestamp + forceInclusionPeriodSeconds, _nextContext.timestamp >= _prevContext.timestamp,
"Older queue batches must be processed before a new sequencer batch." "Context timestamp values must monotonically increase."
); );
require( require(
_context.timestamp <= nextQueueElement.timestamp, _nextContext.blockNumber >= _prevContext.blockNumber,
"Sequencer transactions timestamp too high." "Context blockNumber values must monotonically increase."
); );
require( // If there are some queue elements pending:
_context.blockNumber <= nextQueueElement.blockNumber, if (getQueueLength() - _nextQueueIndex > 0) {
"Sequencer transactions blockNumber too high." Lib_OVMCodec.QueueElement memory nextQueueElement = getQueueElement(_nextQueueIndex);
);
// If the force inclusion period has passed for an enqueued transaction, it MUST be the next chain element.
require(
block.timestamp < nextQueueElement.timestamp + forceInclusionPeriodSeconds,
"Previously enqueued batches have expired and must be appended before a new sequencer batch."
);
// Just like sequencer transaction times must be increasing relative to each other,
// We also require that they be increasing relative to any interspersed queue elements.
require(
_nextContext.timestamp <= nextQueueElement.timestamp,
"Sequencer transaction timestamp exceeds that of next queue element."
);
require(
_nextContext.blockNumber <= nextQueueElement.blockNumber,
"Sequencer transaction blockNumber exceeds that of next queue element."
);
}
}
/**
* Checks that the final batch context in a sequencer submission is valid.
* @param _finalContext The batch context to validate.
*/
function _validateFinalBatchContext(
BatchContext memory _finalContext
)
internal
view
{
// Batches cannot be added from the future, or subsequent enqueue() contexts would violate monotonicity.
require(_finalContext.timestamp <= block.timestamp, "Context timestamp is from the future.");
require(_finalContext.blockNumber <= block.number, "Context block number is from the future.");
} }
/** /**
...@@ -874,16 +1010,17 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad ...@@ -874,16 +1010,17 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
) )
{ {
require( require(
Lib_OVMCodec.hashBatchHeader(_batchHeader) == batches.get(uint32(_batchHeader.batchIndex)), Lib_OVMCodec.hashBatchHeader(_batchHeader) == batches().get(uint32(_batchHeader.batchIndex)),
"Invalid batch header." "Invalid batch header."
); );
require( require(
Lib_MerkleUtils.verify( Lib_MerkleTree.verify(
_batchHeader.batchRoot, _batchHeader.batchRoot,
_element, _element,
_proof.index, _proof.index,
_proof.siblings _proof.siblings,
_batchHeader.batchSize
), ),
"Invalid inclusion proof." "Invalid inclusion proof."
); );
......
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; ...@@ -5,22 +5,22 @@ pragma experimental ABIEncoderV2;
/* Library Imports */ /* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol"; import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol"; import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_MerkleUtils } from "../../libraries/utils/Lib_MerkleUtils.sol"; import { Lib_MerkleTree } from "../../libraries/utils/Lib_MerkleTree.sol";
import { Lib_RingBuffer, iRingBufferOverwriter } from "../../libraries/utils/Lib_RingBuffer.sol";
/* Interface Imports */ /* Interface Imports */
import { iOVM_FraudVerifier } from "../../iOVM/verification/iOVM_FraudVerifier.sol"; import { iOVM_FraudVerifier } from "../../iOVM/verification/iOVM_FraudVerifier.sol";
import { iOVM_StateCommitmentChain } from "../../iOVM/chain/iOVM_StateCommitmentChain.sol"; import { iOVM_StateCommitmentChain } from "../../iOVM/chain/iOVM_StateCommitmentChain.sol";
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol"; import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
import { iOVM_BondManager } from "../../iOVM/verification/iOVM_BondManager.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'; import '@openzeppelin/contracts/math/SafeMath.sol';
/** /**
* @title OVM_StateCommitmentChain * @title OVM_StateCommitmentChain
*/ */
contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverwriter, Lib_AddressResolver { contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, Lib_AddressResolver {
using Lib_RingBuffer for Lib_RingBuffer.RingBuffer;
/************* /*************
* Constants * * Constants *
...@@ -30,15 +30,6 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw ...@@ -30,15 +30,6 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
uint256 public SEQUENCER_PUBLISH_WINDOW; uint256 public SEQUENCER_PUBLISH_WINDOW;
/*************
* Variables *
*************/
uint256 internal lastDeletableIndex;
uint256 internal lastDeletableTimestamp;
Lib_RingBuffer.RingBuffer internal batches;
/*************** /***************
* Constructor * * Constructor *
***************/ ***************/
...@@ -50,7 +41,9 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw ...@@ -50,7 +41,9 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
address _libAddressManager, address _libAddressManager,
uint256 _fraudProofWindow, uint256 _fraudProofWindow,
uint256 _sequencerPublishWindow uint256 _sequencerPublishWindow
) Lib_AddressResolver(_libAddressManager) { )
Lib_AddressResolver(_libAddressManager)
{
FRAUD_PROOF_WINDOW = _fraudProofWindow; FRAUD_PROOF_WINDOW = _fraudProofWindow;
SEQUENCER_PUBLISH_WINDOW = _sequencerPublishWindow; SEQUENCER_PUBLISH_WINDOW = _sequencerPublishWindow;
} }
...@@ -61,16 +54,18 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw ...@@ -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() function batches()
override
public public
view
returns (
iOVM_ChainStorageContainer
)
{ {
batches.init( return iOVM_ChainStorageContainer(
16, resolve("OVM_ChainStorageContainer:SCC:batches")
Lib_OVMCodec.RING_BUFFER_SCC_BATCHES,
iRingBufferOverwriter(address(this))
); );
} }
...@@ -100,7 +95,7 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw ...@@ -100,7 +95,7 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
uint256 _totalBatches uint256 _totalBatches
) )
{ {
return uint256(batches.getLength()); return batches().length();
} }
/** /**
...@@ -207,11 +202,12 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw ...@@ -207,11 +202,12 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
); );
require( require(
Lib_MerkleUtils.verify( Lib_MerkleTree.verify(
_batchHeader.batchRoot, _batchHeader.batchRoot,
abi.encodePacked(_element), _element,
_proof.index, _proof.index,
_proof.siblings _proof.siblings,
_batchHeader.batchSize
), ),
"Invalid inclusion proof." "Invalid inclusion proof."
); );
...@@ -244,69 +240,6 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw ...@@ -244,69 +240,6 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
return SafeMath.add(timestamp, FRAUD_PROOF_WINDOW) > block.timestamp; 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 * * Internal Functions *
...@@ -325,7 +258,7 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw ...@@ -325,7 +258,7 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
uint40 uint40
) )
{ {
bytes27 extraData = batches.getExtraData(); bytes27 extraData = batches().getGlobalMetadata();
uint40 totalElements; uint40 totalElements;
uint40 lastSequencerTimestamp; uint40 lastSequencerTimestamp;
...@@ -394,15 +327,10 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw ...@@ -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({ Lib_OVMCodec.ChainBatchHeader memory batchHeader = Lib_OVMCodec.ChainBatchHeader({
batchIndex: getTotalBatches(), batchIndex: getTotalBatches(),
batchRoot: Lib_MerkleUtils.getMerkleRoot(elements), batchRoot: Lib_MerkleTree.getMerkleRoot(_batch),
batchSize: elements.length, batchSize: _batch.length,
prevTotalElements: totalElements, prevTotalElements: totalElements,
extraData: _extraData extraData: _extraData
}); });
...@@ -415,7 +343,7 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw ...@@ -415,7 +343,7 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
batchHeader.extraData batchHeader.extraData
); );
batches.push( batches().push(
Lib_OVMCodec.hashBatchHeader(batchHeader), Lib_OVMCodec.hashBatchHeader(batchHeader),
_makeBatchExtraData( _makeBatchExtraData(
uint40(batchHeader.prevTotalElements + batchHeader.batchSize), uint40(batchHeader.prevTotalElements + batchHeader.batchSize),
...@@ -434,7 +362,7 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw ...@@ -434,7 +362,7 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
internal internal
{ {
require( require(
_batchHeader.batchIndex < batches.getLength(), _batchHeader.batchIndex < batches().length(),
"Invalid batch index." "Invalid batch index."
); );
...@@ -443,8 +371,8 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw ...@@ -443,8 +371,8 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
"Invalid batch header." "Invalid batch header."
); );
batches.deleteElementsAfterInclusive( batches().deleteElementsAfterInclusive(
uint40(_batchHeader.batchIndex), _batchHeader.batchIndex,
_makeBatchExtraData( _makeBatchExtraData(
uint40(_batchHeader.prevTotalElements), uint40(_batchHeader.prevTotalElements),
0 0
...@@ -471,6 +399,6 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw ...@@ -471,6 +399,6 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverw
bool 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" ...@@ -15,6 +15,7 @@ import { iOVM_SafetyChecker } from "../../iOVM/execution/iOVM_SafetyChecker.sol"
/* Contract Imports */ /* Contract Imports */
import { OVM_ECDSAContractAccount } from "../accounts/OVM_ECDSAContractAccount.sol"; import { OVM_ECDSAContractAccount } from "../accounts/OVM_ECDSAContractAccount.sol";
import { OVM_ProxyEOA } from "../accounts/OVM_ProxyEOA.sol"; import { OVM_ProxyEOA } from "../accounts/OVM_ProxyEOA.sol";
import { OVM_DeployerWhitelist } from "../precompiles/OVM_DeployerWhitelist.sol";
/** /**
* @title OVM_ExecutionManager * @title OVM_ExecutionManager
...@@ -169,8 +170,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -169,8 +170,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
return; 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(); uint256 gasProvided = gasleft();
// Run the transaction, make sure to meter the gas usage.
ovmCALL( ovmCALL(
_transaction.gasLimit - gasMeterConfig.minTransactionGasLimit, _transaction.gasLimit - gasMeterConfig.minTransactionGasLimit,
_transaction.entrypoint, _transaction.entrypoint,
...@@ -359,6 +362,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -359,6 +362,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
// Creator is always the current ADDRESS. // Creator is always the current ADDRESS.
address creator = ovmADDRESS(); address creator = ovmADDRESS();
// Check that the deployer is whitelisted, or
// that arbitrary contract deployment has been enabled.
_checkDeployerAllowed(creator);
// Generate the correct CREATE address. // Generate the correct CREATE address.
address contractAddress = Lib_EthUtils.getAddressForCREATE( address contractAddress = Lib_EthUtils.getAddressForCREATE(
creator, creator,
...@@ -392,6 +399,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -392,6 +399,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
// Creator is always the current ADDRESS. // Creator is always the current ADDRESS.
address creator = ovmADDRESS(); address creator = ovmADDRESS();
// Check that the deployer is whitelisted, or
// that arbitrary contract deployment has been enabled.
_checkDeployerAllowed(creator);
// Generate the correct CREATE2 address. // Generate the correct CREATE2 address.
address contractAddress = Lib_EthUtils.getAddressForCREATE2( address contractAddress = Lib_EthUtils.getAddressForCREATE2(
creator, creator,
...@@ -839,7 +850,32 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -839,7 +850,32 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
return gasMeterConfig.maxTransactionGasLimit; 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 * * Internal Functions: Contract Interaction *
...@@ -998,13 +1034,14 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -998,13 +1034,14 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
_revertWithFlag(flag); _revertWithFlag(flag);
} }
// INTENTIONAL_REVERT, UNSAFE_BYTECODE, and STATIC_VIOLATION aren't dependent on the // INTENTIONAL_REVERT, UNSAFE_BYTECODE, STATIC_VIOLATION, and CREATOR_NOT_ALLOWED aren't
// input state, so we can just handle them like standard reverts. Our only change here // 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). // is to record the gas refund reported by the call (enforced by safety checking).
if ( if (
flag == RevertFlag.INTENTIONAL_REVERT flag == RevertFlag.INTENTIONAL_REVERT
|| flag == RevertFlag.UNSAFE_BYTECODE || flag == RevertFlag.UNSAFE_BYTECODE
|| flag == RevertFlag.STATIC_VIOLATION || flag == RevertFlag.STATIC_VIOLATION
|| flag == RevertFlag.CREATOR_NOT_ALLOWED
) { ) {
transactionRecord.ovmGasRefund = ovmGasRefund; transactionRecord.ovmGasRefund = ovmGasRefund;
} }
......
...@@ -6,7 +6,7 @@ import { Lib_Bytes32Utils } from "../../libraries/utils/Lib_Bytes32Utils.sol"; ...@@ -6,7 +6,7 @@ import { Lib_Bytes32Utils } from "../../libraries/utils/Lib_Bytes32Utils.sol";
/* Interface Imports */ /* Interface Imports */
import { iOVM_DeployerWhitelist } from "../../iOVM/precompiles/iOVM_DeployerWhitelist.sol"; 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 * @title OVM_DeployerWhitelist
...@@ -31,16 +31,14 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist { ...@@ -31,16 +31,14 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
* Blocks functions to anyone except the contract owner. * Blocks functions to anyone except the contract owner.
*/ */
modifier onlyOwner() { modifier onlyOwner() {
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
address owner = Lib_Bytes32Utils.toAddress( address owner = Lib_Bytes32Utils.toAddress(
ovmExecutionManager.ovmSLOAD( Lib_SafeExecutionManagerWrapper.safeSLOAD(
KEY_OWNER KEY_OWNER
) )
); );
require( Lib_SafeExecutionManagerWrapper.safeREQUIRE(
ovmExecutionManager.ovmCALLER() == owner, Lib_SafeExecutionManagerWrapper.safeCALLER() == owner,
"Function can only be called by the owner of this contract." "Function can only be called by the owner of this contract."
); );
_; _;
...@@ -63,30 +61,45 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist { ...@@ -63,30 +61,45 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
override override
public public
{ {
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
bool initialized = Lib_Bytes32Utils.toBool( bool initialized = Lib_Bytes32Utils.toBool(
ovmExecutionManager.ovmSLOAD(KEY_INITIALIZED) Lib_SafeExecutionManagerWrapper.safeSLOAD(KEY_INITIALIZED)
); );
if (initialized == true) { if (initialized == true) {
return; return;
} }
ovmExecutionManager.ovmSSTORE( Lib_SafeExecutionManagerWrapper.safeSSTORE(
KEY_INITIALIZED, KEY_INITIALIZED,
Lib_Bytes32Utils.fromBool(true) Lib_Bytes32Utils.fromBool(true)
); );
ovmExecutionManager.ovmSSTORE( Lib_SafeExecutionManagerWrapper.safeSSTORE(
KEY_OWNER, KEY_OWNER,
Lib_Bytes32Utils.fromAddress(_owner) Lib_Bytes32Utils.fromAddress(_owner)
); );
ovmExecutionManager.ovmSSTORE( Lib_SafeExecutionManagerWrapper.safeSSTORE(
KEY_ALLOW_ARBITRARY_DEPLOYMENT, KEY_ALLOW_ARBITRARY_DEPLOYMENT,
Lib_Bytes32Utils.fromBool(_allowArbitraryDeployment) 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. * Adds or removes an address from the deployment whitelist.
* @param _deployer Address to update permissions for. * @param _deployer Address to update permissions for.
...@@ -100,9 +113,7 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist { ...@@ -100,9 +113,7 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
public public
onlyOwner onlyOwner
{ {
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender); Lib_SafeExecutionManagerWrapper.safeSSTORE(
ovmExecutionManager.ovmSSTORE(
Lib_Bytes32Utils.fromAddress(_deployer), Lib_Bytes32Utils.fromAddress(_deployer),
Lib_Bytes32Utils.fromBool(_isWhitelisted) Lib_Bytes32Utils.fromBool(_isWhitelisted)
); );
...@@ -119,9 +130,7 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist { ...@@ -119,9 +130,7 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
public public
onlyOwner onlyOwner
{ {
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender); Lib_SafeExecutionManagerWrapper.safeSSTORE(
ovmExecutionManager.ovmSSTORE(
KEY_OWNER, KEY_OWNER,
Lib_Bytes32Utils.fromAddress(_owner) Lib_Bytes32Utils.fromAddress(_owner)
); );
...@@ -138,9 +147,7 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist { ...@@ -138,9 +147,7 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
public public
onlyOwner onlyOwner
{ {
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender); Lib_SafeExecutionManagerWrapper.safeSSTORE(
ovmExecutionManager.ovmSSTORE(
KEY_ALLOW_ARBITRARY_DEPLOYMENT, KEY_ALLOW_ARBITRARY_DEPLOYMENT,
Lib_Bytes32Utils.fromBool(_allowArbitraryDeployment) Lib_Bytes32Utils.fromBool(_allowArbitraryDeployment)
); );
...@@ -172,10 +179,8 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist { ...@@ -172,10 +179,8 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
bool _allowed bool _allowed
) )
{ {
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
bool initialized = Lib_Bytes32Utils.toBool( bool initialized = Lib_Bytes32Utils.toBool(
ovmExecutionManager.ovmSLOAD(KEY_INITIALIZED) Lib_SafeExecutionManagerWrapper.safeSLOAD(KEY_INITIALIZED)
); );
if (initialized == false) { if (initialized == false) {
...@@ -183,7 +188,7 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist { ...@@ -183,7 +188,7 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
} }
bool allowArbitraryDeployment = Lib_Bytes32Utils.toBool( bool allowArbitraryDeployment = Lib_Bytes32Utils.toBool(
ovmExecutionManager.ovmSLOAD(KEY_ALLOW_ARBITRARY_DEPLOYMENT) Lib_SafeExecutionManagerWrapper.safeSLOAD(KEY_ALLOW_ARBITRARY_DEPLOYMENT)
); );
if (allowArbitraryDeployment == true) { if (allowArbitraryDeployment == true) {
...@@ -191,7 +196,7 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist { ...@@ -191,7 +196,7 @@ contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
} }
bool isWhitelisted = Lib_Bytes32Utils.toBool( bool isWhitelisted = Lib_Bytes32Utils.toBool(
ovmExecutionManager.ovmSLOAD( Lib_SafeExecutionManagerWrapper.safeSLOAD(
Lib_Bytes32Utils.fromAddress(_deployer) Lib_Bytes32Utils.fromAddress(_deployer)
) )
); );
......
...@@ -321,6 +321,15 @@ contract OVM_StateTransitioner is Lib_AddressResolver, OVM_FraudContributor, iOV ...@@ -321,6 +321,15 @@ contract OVM_StateTransitioner is Lib_AddressResolver, OVM_FraudContributor, iOV
"Invalid transaction provided." "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")); iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(resolve("OVM_ExecutionManager"));
// We call `setExecutionManager` right before `run` (and not earlier) just in case the // We call `setExecutionManager` right before `run` (and not earlier) just in case the
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.7.0; pragma solidity ^0.7.0;
/* Library Imports */
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */ /* Interface Imports */
import { iOVM_StateTransitioner } from "../../iOVM/verification/iOVM_StateTransitioner.sol"; import { iOVM_StateTransitioner } from "../../iOVM/verification/iOVM_StateTransitioner.sol";
import { iOVM_StateTransitionerFactory } from "../../iOVM/verification/iOVM_StateTransitionerFactory.sol"; import { iOVM_StateTransitionerFactory } from "../../iOVM/verification/iOVM_StateTransitionerFactory.sol";
import { iOVM_FraudVerifier } from "../../iOVM/verification/iOVM_FraudVerifier.sol";
/* Contract Imports */ /* Contract Imports */
import { OVM_StateTransitioner } from "./OVM_StateTransitioner.sol"; import { OVM_StateTransitioner } from "./OVM_StateTransitioner.sol";
...@@ -11,7 +15,10 @@ import { OVM_StateTransitioner } from "./OVM_StateTransitioner.sol"; ...@@ -11,7 +15,10 @@ import { OVM_StateTransitioner } from "./OVM_StateTransitioner.sol";
/** /**
* @title OVM_StateTransitionerFactory * @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 * * Public Functions: Contract Creation *
...@@ -37,6 +44,10 @@ contract OVM_StateTransitionerFactory is iOVM_StateTransitionerFactory { ...@@ -37,6 +44,10 @@ contract OVM_StateTransitionerFactory is iOVM_StateTransitionerFactory {
iOVM_StateTransitioner _ovmStateTransitioner iOVM_StateTransitioner _ovmStateTransitioner
) )
{ {
require(
msg.sender == resolve("OVM_FraudVerifier"),
"Create can only be done by the OVM_FraudVerifier."
);
return new OVM_StateTransitioner( return new OVM_StateTransitioner(
_libAddressManager, _libAddressManager,
_stateTransitionIndex, _stateTransitionIndex,
......
...@@ -60,11 +60,6 @@ interface iOVM_CanonicalTransactionChain { ...@@ -60,11 +60,6 @@ interface iOVM_CanonicalTransactionChain {
* Public Functions * * Public Functions *
********************/ ********************/
/**
* Initializes this contract.
*/
function init() external;
/** /**
* Retrieves the total number of elements submitted. * Retrieves the total number of elements submitted.
* @return _totalElements Total submitted elements. * @return _totalElements Total submitted elements.
...@@ -109,6 +104,18 @@ interface iOVM_CanonicalTransactionChain { ...@@ -109,6 +104,18 @@ interface iOVM_CanonicalTransactionChain {
uint40 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. * Gets the queue element at a particular index.
* @param _index Index of the queue element to access. * @param _index Index of the queue element to access.
...@@ -148,13 +155,13 @@ interface iOVM_CanonicalTransactionChain { ...@@ -148,13 +155,13 @@ interface iOVM_CanonicalTransactionChain {
/** /**
* Allows the sequencer to append a batch of transactions. * Allows the sequencer to append a batch of transactions.
* @dev This function uses a custom encoding scheme for efficiency reasons. * @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 _totalElementsToAppend Total number of batch elements we expect to append.
* .param _contexts Array of batch contexts. * .param _contexts Array of batch contexts.
* .param _transactionDataFields Array of raw transaction data. * .param _transactionDataFields Array of raw transaction data.
*/ */
function appendSequencerBatch( function appendSequencerBatch(
// uint40 _shouldStartAtBatch, // uint40 _shouldStartAtElement,
// uint24 _totalElementsToAppend, // uint24 _totalElementsToAppend,
// BatchContext[] _contexts, // BatchContext[] _contexts,
// bytes[] _transactionDataFields // 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 { ...@@ -27,15 +27,11 @@ interface iOVM_StateCommitmentChain {
bytes32 _batchRoot bytes32 _batchRoot
); );
/******************** /********************
* Public Functions * * Public Functions *
********************/ ********************/
/**
* Initializes this contract.
*/
function init() external;
/** /**
* Retrieves the total number of elements submitted. * Retrieves the total number of elements submitted.
* @return _totalElements Total submitted elements. * @return _totalElements Total submitted elements.
...@@ -119,21 +115,4 @@ interface iOVM_StateCommitmentChain { ...@@ -119,21 +115,4 @@ interface iOVM_StateCommitmentChain {
returns ( returns (
bool _inside 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 { ...@@ -19,7 +19,8 @@ interface iOVM_ExecutionManager {
UNSAFE_BYTECODE, UNSAFE_BYTECODE,
CREATE_COLLISION, CREATE_COLLISION,
STATIC_VIOLATION, STATIC_VIOLATION,
CREATE_EXCEPTION CREATE_EXCEPTION,
CREATOR_NOT_ALLOWED
} }
enum GasMetadataKey { enum GasMetadataKey {
......
...@@ -11,6 +11,7 @@ interface iOVM_DeployerWhitelist { ...@@ -11,6 +11,7 @@ interface iOVM_DeployerWhitelist {
********************/ ********************/
function initialize(address _owner, bool _allowArbitraryDeployment) external; function initialize(address _owner, bool _allowArbitraryDeployment) external;
function getOwner() external returns (address _owner);
function setWhitelistedDeployer(address _deployer, bool _isWhitelisted) external; function setWhitelistedDeployer(address _deployer, bool _isWhitelisted) external;
function setOwner(address _newOwner) external; function setOwner(address _newOwner) external;
function setAllowArbitraryDeployment(bool _allowArbitraryDeployment) 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; pragma solidity ^0.7.0;
interface iRingBufferOverwriter {
function canOverwrite(bytes32 _id, uint256 _index) external returns (bool);
}
library Lib_RingBuffer { library Lib_RingBuffer {
using Lib_RingBuffer for RingBuffer; using Lib_RingBuffer for RingBuffer;
...@@ -17,12 +13,11 @@ library Lib_RingBuffer { ...@@ -17,12 +13,11 @@ library Lib_RingBuffer {
} }
struct RingBuffer { struct RingBuffer {
bytes32 id;
iRingBufferOverwriter overwriter;
bytes32 contextA; bytes32 contextA;
bytes32 contextB; bytes32 contextB;
Buffer bufferA; Buffer bufferA;
Buffer bufferB; Buffer bufferB;
uint256 nextOverwritableIndex;
} }
struct RingBufferContext { struct RingBufferContext {
...@@ -48,26 +43,6 @@ library Lib_RingBuffer { ...@@ -48,26 +43,6 @@ library Lib_RingBuffer {
* Internal Functions * * 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. * Pushes a single element to the buffer.
* @param _self Buffer to access. * @param _self Buffer to access.
...@@ -91,15 +66,7 @@ library Lib_RingBuffer { ...@@ -91,15 +66,7 @@ library Lib_RingBuffer {
// Check if we need to expand the buffer. // Check if we need to expand the buffer.
if (ctx.globalIndex - ctx.currResetIndex >= currBuffer.length) { if (ctx.globalIndex - ctx.currResetIndex >= currBuffer.length) {
bool canOverwrite; if (ctx.currResetIndex < _self.nextOverwritableIndex) {
try _self.overwriter.canOverwrite(_self.id, ctx.currResetIndex) returns (bool _canOverwrite) {
canOverwrite = _canOverwrite;
} catch {
// Just in case canOverwrite is broken.
canOverwrite = false;
}
if (canOverwrite) {
// We're going to overwrite the inactive buffer. // We're going to overwrite the inactive buffer.
// Bump the buffer index, reset the delete offset, and set our reset indices. // Bump the buffer index, reset the delete offset, and set our reset indices.
ctx.currBufferIndex++; ctx.currBufferIndex++;
...@@ -280,6 +247,24 @@ library Lib_RingBuffer { ...@@ -280,6 +247,24 @@ library Lib_RingBuffer {
_self.setContext(ctx); _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. * Retrieves the current global index.
* @param _self Buffer to access. * @param _self Buffer to access.
...@@ -298,6 +283,22 @@ library Lib_RingBuffer { ...@@ -298,6 +283,22 @@ library Lib_RingBuffer {
return ctx.globalIndex; 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. * Retrieves the current global extra data.
* @param _self Buffer to access. * @param _self Buffer to access.
......
...@@ -14,6 +14,7 @@ contract Helper_TestRunner { ...@@ -14,6 +14,7 @@ contract Helper_TestRunner {
bytes functionData; bytes functionData;
bool expectedReturnStatus; bool expectedReturnStatus;
bytes expectedReturnData; bytes expectedReturnData;
bool onlyValidateFlag;
} }
function runSingleTestStep( function runSingleTestStep(
...@@ -67,7 +68,7 @@ contract Helper_TestRunner { ...@@ -67,7 +68,7 @@ contract Helper_TestRunner {
console.log(""); console.log("");
} }
revert("Test step failed."); _failStep();
} }
if (keccak256(returndata) != keccak256(_step.expectedReturnData)) { if (keccak256(returndata) != keccak256(_step.expectedReturnData)) {
...@@ -79,6 +80,8 @@ contract Helper_TestRunner { ...@@ -79,6 +80,8 @@ contract Helper_TestRunner {
console.log("Actual:"); console.log("Actual:");
console.logBytes(returndata); console.logBytes(returndata);
console.log(""); console.log("");
_failStep();
} else { } else {
( (
uint256 _expectedFlag, uint256 _expectedFlag,
...@@ -94,22 +97,35 @@ contract Helper_TestRunner { ...@@ -94,22 +97,35 @@ contract Helper_TestRunner {
bytes memory _data bytes memory _data
) = _decodeRevertData(returndata); ) = _decodeRevertData(returndata);
console.log("ERROR: Actual revert flag data does not match expected revert flag data"); if (
console.log("Offending Step: %s", _step.functionName); _step.onlyValidateFlag
console.log("Expected Flag: %s", _expectedFlag); ) {
console.log("Actual Flag: %s", _flag); if (
console.log("Expected Nuisance Gas Left: %s", _expectedNuisanceGasLeft); _expectedFlag != _flag
console.log("Actual Nuisance Gas Left: %s", _nuisanceGasLeft); ) {
console.log("Expected OVM Gas Refund: %s", _expectedOvmGasRefund); console.log("ERROR: Actual revert flag does not match expected revert flag data");
console.log("Actual OVM Gas Refund: %s", _ovmGasRefund); console.log("Offending Step: %s", _step.functionName);
console.log("Expected Extra Data:"); console.log("Expected Flag: %s", _expectedFlag);
console.logBytes(_expectedData); console.log("Actual Flag: %s", _flag);
console.log("Actual Extra Data:"); _failStep();
console.logBytes(_data); }
console.log(""); } 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)) { if (success == false || (success == true && returndata.length == 1)) {
...@@ -156,6 +172,12 @@ contract Helper_TestRunner { ...@@ -156,6 +172,12 @@ contract Helper_TestRunner {
return abi.decode(_revertdata, (uint256, uint256, uint256, bytes)); return abi.decode(_revertdata, (uint256, uint256, uint256, bytes));
} }
function _failStep()
internal
{
revert("Test step failed.");
}
} }
contract Helper_TestRunner_CREATE is Helper_TestRunner { contract Helper_TestRunner_CREATE is Helper_TestRunner {
......
...@@ -3,58 +3,46 @@ pragma solidity ^0.7.0; ...@@ -3,58 +3,46 @@ pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
/* Library Imports */ /* 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( function getMerkleRoot(
bytes32[] memory _hashes bytes32[] memory _elements
) )
public public
view view
returns ( returns (
bytes32 _root bytes32
) )
{ {
return Lib_MerkleUtils.getMerkleRoot( return Lib_MerkleTree.getMerkleRoot(
_hashes
);
}
function getMerkleRoot(
bytes[] memory _elements
)
public
view
returns (
bytes32 _root
)
{
return Lib_MerkleUtils.getMerkleRoot(
_elements _elements
); );
} }
function verify( function verify(
bytes32 _root, bytes32 _root,
bytes memory _leaf, bytes32 _leaf,
uint256 _path, uint256 _index,
bytes32[] memory _siblings bytes32[] memory _siblings,
uint256 _totalLeaves
) )
public public
pure pure
returns ( returns (
bool _verified bool
) )
{ {
return Lib_MerkleUtils.verify( return Lib_MerkleTree.verify(
_root, _root,
_leaf, _leaf,
_path, _index,
_siblings _siblings,
_totalLeaves
); );
} }
} }
...@@ -3,7 +3,7 @@ pragma solidity ^0.7.0; ...@@ -3,7 +3,7 @@ pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
/* Library Imports */ /* 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 * @title TestLib_RingBuffer
...@@ -13,20 +13,6 @@ contract TestLib_RingBuffer { ...@@ -13,20 +13,6 @@ contract TestLib_RingBuffer {
Lib_RingBuffer.RingBuffer internal buf; Lib_RingBuffer.RingBuffer internal buf;
function init(
uint256 _initialBufferSize,
bytes32 _id,
iRingBufferOverwriter _overwriter
)
public
{
buf.init(
_initialBufferSize,
_id,
_overwriter
);
}
function push( function push(
bytes32 _value, bytes32 _value,
bytes27 _extraData bytes27 _extraData
......
{ {
"name": "@eth-optimism/contracts", "name": "@eth-optimism/contracts",
"version": "0.0.2-alpha.14", "version": "0.0.2-alpha.15",
"main": "build/src/index.js", "main": "build/src/index.js",
"files": [ "files": [
"build/**/*.js", "build/**/*.js",
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
"fs-extra": "^9.0.1", "fs-extra": "^9.0.1",
"lodash": "^4.17.20", "lodash": "^4.17.20",
"merkle-patricia-tree": "^4.0.0", "merkle-patricia-tree": "^4.0.0",
"merkletreejs": "^0.2.12",
"mkdirp": "^1.0.4", "mkdirp": "^1.0.4",
"mocha": "^8.1.1", "mocha": "^8.1.1",
"prettier": "^2.1.2", "prettier": "^2.1.2",
......
...@@ -20,11 +20,15 @@ export interface RollupDeployConfig { ...@@ -20,11 +20,15 @@ export interface RollupDeployConfig {
transactionChainConfig: { transactionChainConfig: {
sequencer: string | Signer sequencer: string | Signer
forceInclusionPeriodSeconds: number forceInclusionPeriodSeconds: number
forceInclusionPeriodBlocks: number
} }
stateChainConfig: { stateChainConfig: {
fraudProofWindowSeconds: number fraudProofWindowSeconds: number
sequencerPublishWindowSeconds: number sequencerPublishWindowSeconds: number
} }
l1CrossDomainMessengerConfig: {
relayerAddress?: string | Signer
}
ethConfig: { ethConfig: {
initialAmount: number initialAmount: number
} }
...@@ -32,6 +36,7 @@ export interface RollupDeployConfig { ...@@ -32,6 +36,7 @@ export interface RollupDeployConfig {
owner: string | Signer owner: string | Signer
allowArbitraryContractDeployment: boolean allowArbitraryContractDeployment: boolean
} }
addressManager?: string
deployOverrides?: Overrides deployOverrides?: Overrides
dependencies?: string[] dependencies?: string[]
} }
...@@ -58,6 +63,14 @@ export const makeContractDeployConfig = async ( ...@@ -58,6 +63,14 @@ export const makeContractDeployConfig = async (
OVM_L1CrossDomainMessenger: { OVM_L1CrossDomainMessenger: {
factory: getContractFactory('OVM_L1CrossDomainMessenger'), factory: getContractFactory('OVM_L1CrossDomainMessenger'),
params: [], 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: { Proxy__OVM_L1CrossDomainMessenger: {
factory: getContractFactory('Lib_ResolvedDelegateProxy'), factory: getContractFactory('Lib_ResolvedDelegateProxy'),
...@@ -80,8 +93,10 @@ export const makeContractDeployConfig = async ( ...@@ -80,8 +93,10 @@ export const makeContractDeployConfig = async (
params: [ params: [
AddressManager.address, AddressManager.address,
config.transactionChainConfig.forceInclusionPeriodSeconds, config.transactionChainConfig.forceInclusionPeriodSeconds,
config.transactionChainConfig.forceInclusionPeriodBlocks,
config.ovmGasMeteringConfig.maxTransactionGasLimit,
], ],
afterDeploy: async (contracts): Promise<void> => { afterDeploy: async (): Promise<void> => {
const sequencer = config.transactionChainConfig.sequencer const sequencer = config.transactionChainConfig.sequencer
const sequencerAddress = const sequencerAddress =
typeof sequencer === 'string' typeof sequencer === 'string'
...@@ -93,7 +108,6 @@ export const makeContractDeployConfig = async ( ...@@ -93,7 +108,6 @@ export const makeContractDeployConfig = async (
) )
await AddressManager.setAddress('OVM_Sequencer', sequencerAddress) await AddressManager.setAddress('OVM_Sequencer', sequencerAddress)
await AddressManager.setAddress('Sequencer', sequencerAddress) await AddressManager.setAddress('Sequencer', sequencerAddress)
await contracts.OVM_CanonicalTransactionChain.init()
}, },
}, },
OVM_StateCommitmentChain: { OVM_StateCommitmentChain: {
...@@ -103,9 +117,6 @@ export const makeContractDeployConfig = async ( ...@@ -103,9 +117,6 @@ export const makeContractDeployConfig = async (
config.stateChainConfig.fraudProofWindowSeconds, config.stateChainConfig.fraudProofWindowSeconds,
config.stateChainConfig.sequencerPublishWindowSeconds, config.stateChainConfig.sequencerPublishWindowSeconds,
], ],
afterDeploy: async (contracts): Promise<void> => {
await contracts.OVM_StateCommitmentChain.init()
},
}, },
OVM_DeployerWhitelist: { OVM_DeployerWhitelist: {
factory: getContractFactory('OVM_DeployerWhitelist'), factory: getContractFactory('OVM_DeployerWhitelist'),
...@@ -142,6 +153,7 @@ export const makeContractDeployConfig = async ( ...@@ -142,6 +153,7 @@ export const makeContractDeployConfig = async (
}, },
OVM_StateManagerFactory: { OVM_StateManagerFactory: {
factory: getContractFactory('OVM_StateManagerFactory'), factory: getContractFactory('OVM_StateManagerFactory'),
params: [],
}, },
OVM_FraudVerifier: { OVM_FraudVerifier: {
factory: getContractFactory('OVM_FraudVerifier'), factory: getContractFactory('OVM_FraudVerifier'),
...@@ -149,6 +161,7 @@ export const makeContractDeployConfig = async ( ...@@ -149,6 +161,7 @@ export const makeContractDeployConfig = async (
}, },
OVM_StateTransitionerFactory: { OVM_StateTransitionerFactory: {
factory: getContractFactory('OVM_StateTransitionerFactory'), factory: getContractFactory('OVM_StateTransitionerFactory'),
params: [AddressManager.address],
}, },
OVM_ECDSAContractAccount: { OVM_ECDSAContractAccount: {
factory: getContractFactory('OVM_ECDSAContractAccount'), factory: getContractFactory('OVM_ECDSAContractAccount'),
...@@ -170,5 +183,17 @@ export const makeContractDeployConfig = async ( ...@@ -170,5 +183,17 @@ export const makeContractDeployConfig = async (
factory: getContractFactory('OVM_ETH'), factory: getContractFactory('OVM_ETH'),
params: [config.ethConfig.initialAmount, 'Ether', 18, '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 { ...@@ -16,10 +16,23 @@ export interface DeployResult {
export const deploy = async ( export const deploy = async (
config: RollupDeployConfig config: RollupDeployConfig
): Promise<DeployResult> => { ): Promise<DeployResult> => {
const AddressManager: Contract = await getContractFactory( let AddressManager: Contract
'Lib_AddressManager',
config.deploymentSigner if (config.addressManager) {
).deploy() // 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( const contractDeployConfig = await makeContractDeployConfig(
config, config,
...@@ -47,6 +60,7 @@ export const deploy = async ( ...@@ -47,6 +60,7 @@ export const deploy = async (
) )
await AddressManager.setAddress(name, contracts[name].address) await AddressManager.setAddress(name, contracts[name].address)
} catch (err) { } catch (err) {
console.error(`Error deploying ${name}: ${err}`)
failedDeployments.push(name) failedDeployments.push(name)
} }
} }
......
...@@ -126,6 +126,7 @@ export const makeStateDump = async (): Promise<any> => { ...@@ -126,6 +126,7 @@ export const makeStateDump = async (): Promise<any> => {
transactionChainConfig: { transactionChainConfig: {
sequencer: signer, sequencer: signer,
forceInclusionPeriodSeconds: 600, forceInclusionPeriodSeconds: 600,
forceInclusionPeriodBlocks: 600 / 12,
}, },
stateChainConfig: { stateChainConfig: {
fraudProofWindowSeconds: 600, fraudProofWindowSeconds: 600,
...@@ -135,6 +136,7 @@ export const makeStateDump = async (): Promise<any> => { ...@@ -135,6 +136,7 @@ export const makeStateDump = async (): Promise<any> => {
owner: signer, owner: signer,
allowArbitraryContractDeployment: true, allowArbitraryContractDeployment: true,
}, },
l1CrossDomainMessengerConfig: {},
ethConfig: { ethConfig: {
initialAmount: 0, initialAmount: 0,
}, },
......
...@@ -2,22 +2,24 @@ import { expect } from '../../../setup' ...@@ -2,22 +2,24 @@ import { expect } from '../../../setup'
/* External Imports */ /* External Imports */
import { ethers } from '@nomiclabs/buidler' import { ethers } from '@nomiclabs/buidler'
import { Signer, ContractFactory, Contract, BigNumber } from 'ethers' import { Signer, ContractFactory, Contract, BigNumber, providers } from 'ethers'
import { TransactionResponse } from '@ethersproject/abstract-provider' import { TransactionResponse } from '@ethersproject/abstract-provider'
import { smockit, MockContract } from '@eth-optimism/smock' import { smockit, MockContract } from '@eth-optimism/smock'
import _, { times } from 'lodash' import _ from 'lodash'
/* Internal Imports */ /* Internal Imports */
import { import {
makeAddressManager, makeAddressManager,
setProxyTarget, setProxyTarget,
FORCE_INCLUSION_PERIOD_SECONDS, FORCE_INCLUSION_PERIOD_SECONDS,
FORCE_INCLUSION_PERIOD_BLOCKS,
setEthTime, setEthTime,
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
remove0x, remove0x,
getEthTime, getEthTime,
getNextBlockNumber, getNextBlockNumber,
increaseEthTime, increaseEthTime,
getBlockTime,
ZERO_ADDRESS, ZERO_ADDRESS,
} from '../../../helpers' } from '../../../helpers'
import { defaultAbiCoder, keccak256 } from 'ethers/lib/utils' import { defaultAbiCoder, keccak256 } from 'ethers/lib/utils'
...@@ -77,17 +79,17 @@ interface BatchContext { ...@@ -77,17 +79,17 @@ interface BatchContext {
} }
interface AppendSequencerBatchParams { interface AppendSequencerBatchParams {
shouldStartAtBatch: number // 5 bytes -- starts at batch shouldStartAtElement: number // 5 bytes -- starts at batch
totalElementsToAppend: number // 3 bytes -- total_elements_to_append totalElementsToAppend: number // 3 bytes -- total_elements_to_append
contexts: BatchContext[] // total_elements[fixed_size[]] contexts: BatchContext[] // total_elements[fixed_size[]]
transactions: string[] // total_size_bytes[],total_size_bytes[] transactions: string[] // total_size_bytes[],total_size_bytes[]
} }
const encodeAppendSequencerBatch = (b: AppendSequencerBatchParams): string => { const encodeAppendSequencerBatch = (b: AppendSequencerBatchParams): string => {
let encoding: string const encodedShouldStartAtElement = remove0x(
const encodedShouldStartAtBatch = remove0x( BigNumber.from(b.shouldStartAtElement).toHexString()
BigNumber.from(b.shouldStartAtBatch).toHexString()
).padStart(10, '0') ).padStart(10, '0')
const encodedTotalElementsToAppend = remove0x( const encodedTotalElementsToAppend = remove0x(
BigNumber.from(b.totalElementsToAppend).toHexString() BigNumber.from(b.totalElementsToAppend).toHexString()
).padStart(6, '0') ).padStart(6, '0')
...@@ -95,6 +97,7 @@ const encodeAppendSequencerBatch = (b: AppendSequencerBatchParams): string => { ...@@ -95,6 +97,7 @@ const encodeAppendSequencerBatch = (b: AppendSequencerBatchParams): string => {
const encodedContextsHeader = remove0x( const encodedContextsHeader = remove0x(
BigNumber.from(b.contexts.length).toHexString() BigNumber.from(b.contexts.length).toHexString()
).padStart(6, '0') ).padStart(6, '0')
const encodedContexts = const encodedContexts =
encodedContextsHeader + encodedContextsHeader +
b.contexts.reduce((acc, cur) => acc + encodeBatchContext(cur), '') b.contexts.reduce((acc, cur) => acc + encodeBatchContext(cur), '')
...@@ -107,8 +110,9 @@ const encodeAppendSequencerBatch = (b: AppendSequencerBatchParams): string => { ...@@ -107,8 +110,9 @@ const encodeAppendSequencerBatch = (b: AppendSequencerBatchParams): string => {
).padStart(6, '0') ).padStart(6, '0')
return acc + encodedTxDataHeader + remove0x(cur) return acc + encodedTxDataHeader + remove0x(cur)
}, '') }, '')
return ( return (
encodedShouldStartAtBatch + encodedShouldStartAtElement +
encodedTotalElementsToAppend + encodedTotalElementsToAppend +
encodedContexts + encodedContexts +
encodedTransactionData encodedTransactionData
...@@ -187,26 +191,55 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -187,26 +191,55 @@ describe('OVM_CanonicalTransactionChain', () => {
Mock__OVM_StateCommitmentChain Mock__OVM_StateCommitmentChain
) )
Mock__OVM_StateCommitmentChain.smocked.canOverwrite.will.return.with(false)
Mock__OVM_ExecutionManager.smocked.getMaxTransactionGasLimit.will.return.with( Mock__OVM_ExecutionManager.smocked.getMaxTransactionGasLimit.will.return.with(
MAX_GAS_LIMIT MAX_GAS_LIMIT
) )
}) })
let Factory__OVM_CanonicalTransactionChain: ContractFactory let Factory__OVM_CanonicalTransactionChain: ContractFactory
let Factory__OVM_ChainStorageContainer: ContractFactory
before(async () => { before(async () => {
Factory__OVM_CanonicalTransactionChain = await ethers.getContractFactory( Factory__OVM_CanonicalTransactionChain = await ethers.getContractFactory(
'OVM_CanonicalTransactionChain' 'OVM_CanonicalTransactionChain'
) )
Factory__OVM_ChainStorageContainer = await ethers.getContractFactory(
'OVM_ChainStorageContainer'
)
}) })
let OVM_CanonicalTransactionChain: Contract let OVM_CanonicalTransactionChain: Contract
beforeEach(async () => { beforeEach(async () => {
OVM_CanonicalTransactionChain = await Factory__OVM_CanonicalTransactionChain.deploy( OVM_CanonicalTransactionChain = await Factory__OVM_CanonicalTransactionChain.deploy(
AddressManager.address, AddressManager.address,
FORCE_INCLUSION_PERIOD_SECONDS FORCE_INCLUSION_PERIOD_SECONDS,
FORCE_INCLUSION_PERIOD_BLOCKS,
MAX_GAS_LIMIT
)
const batches = await Factory__OVM_ChainStorageContainer.deploy(
AddressManager.address,
'OVM_CanonicalTransactionChain'
)
const queue = await Factory__OVM_ChainStorageContainer.deploy(
AddressManager.address,
'OVM_CanonicalTransactionChain'
)
await AddressManager.setAddress(
'OVM_ChainStorageContainer:CTC:batches',
batches.address
)
await AddressManager.setAddress(
'OVM_ChainStorageContainer:CTC:queue',
queue.address
)
await AddressManager.setAddress(
'OVM_CanonicalTransactionChain',
OVM_CanonicalTransactionChain.address
) )
await OVM_CanonicalTransactionChain.init()
}) })
describe('enqueue', () => { describe('enqueue', () => {
...@@ -221,7 +254,17 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -221,7 +254,17 @@ describe('OVM_CanonicalTransactionChain', () => {
await expect( await expect(
OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data) OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data)
).to.be.revertedWith( ).to.be.revertedWith(
'Transaction exceeds maximum rollup transaction data size.' 'Transaction data size exceeds maximum for rollup transaction.'
)
})
it('should revert when trying to enqueue a transaction with a higher gasLimit than the max', async () => {
const data = '0x1234567890'
await expect(
OVM_CanonicalTransactionChain.enqueue(target, MAX_GAS_LIMIT + 1, data)
).to.be.revertedWith(
'Transaction gas limit exceeds maximum for rollup transaction.'
) )
}) })
...@@ -518,12 +561,11 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -518,12 +561,11 @@ describe('OVM_CanonicalTransactionChain', () => {
await OVM_CanonicalTransactionChain.enqueue(entrypoint, gasLimit, data) await OVM_CanonicalTransactionChain.enqueue(entrypoint, gasLimit, data)
const blockNumber = await ethers.provider.getBlockNumber() const blockNumber = await ethers.provider.getBlockNumber()
await increaseEthTime(ethers.provider, FORCE_INCLUSION_PERIOD_SECONDS * 2)
await appendSequencerBatch( await appendSequencerBatch(
OVM_CanonicalTransactionChain.connect(sequencer), OVM_CanonicalTransactionChain.connect(sequencer),
{ {
shouldStartAtBatch: 0, shouldStartAtElement: 0,
totalElementsToAppend: 1, totalElementsToAppend: 1,
contexts: [ contexts: [
{ {
...@@ -627,7 +669,7 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -627,7 +669,7 @@ describe('OVM_CanonicalTransactionChain', () => {
await appendSequencerBatch( await appendSequencerBatch(
OVM_CanonicalTransactionChain.connect(sequencer), OVM_CanonicalTransactionChain.connect(sequencer),
{ {
shouldStartAtBatch: 0, shouldStartAtElement: 0,
totalElementsToAppend: 1, totalElementsToAppend: 1,
contexts: [ contexts: [
{ {
...@@ -688,7 +730,7 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -688,7 +730,7 @@ describe('OVM_CanonicalTransactionChain', () => {
// do two batch appends for no reason // do two batch appends for no reason
await appendSequencerBatch(OVM_CanonicalTransactionChain, { await appendSequencerBatch(OVM_CanonicalTransactionChain, {
shouldStartAtBatch: 0, shouldStartAtElement: 0,
totalElementsToAppend: 1, totalElementsToAppend: 1,
contexts: [ contexts: [
{ {
...@@ -701,7 +743,7 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -701,7 +743,7 @@ describe('OVM_CanonicalTransactionChain', () => {
transactions: ['0x1234'], transactions: ['0x1234'],
}) })
await appendSequencerBatch(OVM_CanonicalTransactionChain, { await appendSequencerBatch(OVM_CanonicalTransactionChain, {
shouldStartAtBatch: 1, shouldStartAtElement: 1,
totalElementsToAppend: 1, totalElementsToAppend: 1,
contexts: [ contexts: [
{ {
...@@ -723,7 +765,7 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -723,7 +765,7 @@ describe('OVM_CanonicalTransactionChain', () => {
) )
} }
const res = await appendSequencerBatch(OVM_CanonicalTransactionChain, { const res = await appendSequencerBatch(OVM_CanonicalTransactionChain, {
shouldStartAtBatch: 2, shouldStartAtElement: 2,
totalElementsToAppend: numTxs, totalElementsToAppend: numTxs,
contexts: [ contexts: [
{ {
...@@ -751,7 +793,7 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -751,7 +793,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber: 0, blockNumber: 0,
}, },
], ],
shouldStartAtBatch: 1234, shouldStartAtElement: 1234,
totalElementsToAppend: 1, totalElementsToAppend: 1,
}) })
).to.be.revertedWith( ).to.be.revertedWith(
...@@ -760,6 +802,9 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -760,6 +802,9 @@ describe('OVM_CanonicalTransactionChain', () => {
}) })
it('should revert if not all sequencer transactions are processed', async () => { it('should revert if not all sequencer transactions are processed', async () => {
const timestamp = await getEthTime(ethers.provider)
const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1
await expect( await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, { appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234', '0x1234'], transactions: ['0x1234', '0x1234'],
...@@ -767,11 +812,11 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -767,11 +812,11 @@ describe('OVM_CanonicalTransactionChain', () => {
{ {
numSequencedTransactions: 0, numSequencedTransactions: 0,
numSubsequentQueueTransactions: 0, numSubsequentQueueTransactions: 0,
timestamp: 0, timestamp,
blockNumber: 0, blockNumber,
}, },
], ],
shouldStartAtBatch: 0, shouldStartAtElement: 0,
totalElementsToAppend: 1, totalElementsToAppend: 1,
}) })
).to.be.revertedWith('Not all sequencer transactions were processed.') ).to.be.revertedWith('Not all sequencer transactions were processed.')
...@@ -789,7 +834,7 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -789,7 +834,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber: 0, blockNumber: 0,
}, },
], ],
shouldStartAtBatch: 0, shouldStartAtElement: 0,
totalElementsToAppend: 1, totalElementsToAppend: 1,
}) })
).to.be.revertedWith('Function can only be called by the Sequencer.') ).to.be.revertedWith('Function can only be called by the Sequencer.')
...@@ -800,7 +845,7 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -800,7 +845,7 @@ describe('OVM_CanonicalTransactionChain', () => {
appendSequencerBatch(OVM_CanonicalTransactionChain, { appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'], transactions: ['0x1234'],
contexts: [], contexts: [],
shouldStartAtBatch: 0, shouldStartAtElement: 0,
totalElementsToAppend: 1, totalElementsToAppend: 1,
}) })
).to.be.revertedWith('Must provide at least one batch context.') ).to.be.revertedWith('Must provide at least one batch context.')
...@@ -818,87 +863,541 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -818,87 +863,541 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber: 0, blockNumber: 0,
}, },
], ],
shouldStartAtBatch: 0, shouldStartAtElement: 0,
totalElementsToAppend: 0, totalElementsToAppend: 0,
}) })
).to.be.revertedWith('Must append at least one element.') ).to.be.revertedWith('Must append at least one element.')
}) })
for (const size of ELEMENT_TEST_SIZES) { describe('Sad path cases', () => {
describe(`when appending ${size} sequencer transactions`, () => { const target = NON_ZERO_ADDRESS
const target = NON_ZERO_ADDRESS const gasLimit = 500_000
const gasLimit = 500_000 const data = '0x' + '12'.repeat(1234)
const data = '0x' + '12'.repeat(1234)
beforeEach(async () => { describe('when the sequencer attempts to add more queue transactions than exist', () => {
await OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data) it('reverts when there are zero transactions in the queue', async () => {
const timestamp = await getEthTime(ethers.provider)
const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 1,
timestamp,
blockNumber,
},
],
shouldStartAtElement: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith('Not enough queued transactions to append.')
})
it('reverts when there are insufficient (but nonzero) transactions in the queue', async () => {
const timestamp = await getEthTime(ethers.provider)
const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1
const numEnqueues = 7
for (let i = 0; i < numEnqueues; i++) {
await OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data)
}
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: numEnqueues + 1,
timestamp,
blockNumber,
},
],
shouldStartAtElement: 0,
totalElementsToAppend: numEnqueues + 1,
})
).to.be.revertedWith('Not enough queued transactions to append.')
})
})
describe('when the sequencer attempts to add transactions which are not monotonically increasing', () => {
describe('when the sequencer transactions themselves have out-of-order times', () => {
it('should revert when adding two out-of-order-timestamp sequencer elements', async () => {
const timestamp = await getEthTime(ethers.provider)
const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234', '0x5678'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp: timestamp + 1,
blockNumber,
},
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber,
},
],
shouldStartAtElement: 0,
totalElementsToAppend: 2,
})
).to.be.revertedWith(
'Context timestamp values must monotonically increase.'
)
})
it('should revert when adding two out-of-order-blocknumber sequencer elements', async () => {
const timestamp = await getEthTime(ethers.provider)
const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234', '0x5678'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber: blockNumber + 1,
},
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber,
},
],
shouldStartAtElement: 0,
totalElementsToAppend: 2,
})
).to.be.revertedWith(
'Context blockNumber values must monotonically increase.'
)
})
})
describe('when the elements are out-of-order with regards to pending queue elements', async () => {
describe('adding a single sequencer transaction with a single pending queue element', () => {
beforeEach(async () => {
// enqueue a single element so that it is pending, but do not yet apply it
await OVM_CanonicalTransactionChain.enqueue(
target,
gasLimit,
data
)
})
it('should revert if the first context timestamp is > the head queue element timestamp', async () => {
const timestamp = (await getEthTime(ethers.provider)) + 100
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp: timestamp,
blockNumber: 0,
},
],
shouldStartAtElement: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
'Sequencer transaction timestamp exceeds that of next queue element.'
)
})
it('should revert if the context block number is > the head queue element block number', async () => {
const timestamp = (await getEthTime(ethers.provider)) - 100
const blockNumber =
(await getNextBlockNumber(ethers.provider)) + 100
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp: timestamp,
blockNumber: blockNumber,
},
],
shouldStartAtElement: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
'Sequencer transaction blockNumber exceeds that of next queue element.'
)
})
})
describe('adding multiple sequencer transactions with multiple pending queue elements', () => {
const numQueuedTransactions = 10
let queueElements = []
let validContexts = []
beforeEach(async () => {
for (let i = 0; i < numQueuedTransactions; i++) {
await OVM_CanonicalTransactionChain.enqueue(
target,
gasLimit,
data
)
queueElements[
i
] = await OVM_CanonicalTransactionChain.getQueueElement(i)
// this is a valid context for this TX
validContexts[i] = {
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 1,
timestamp: queueElements[i].timestamp,
blockNumber: queueElements[i].blockNumber,
}
}
})
it('does not revert for valid context', async () => {
await appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: new Array(numQueuedTransactions).fill('0x1234'),
contexts: validContexts,
shouldStartAtElement: 0,
totalElementsToAppend: 2 * numQueuedTransactions,
})
})
it('reverts if wrong timestamp in middle', async () => {
let invalidTimestampContexts = [...validContexts]
// put a bigger timestamp early
invalidTimestampContexts[6].timestamp =
invalidTimestampContexts[8].timestamp
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: new Array(numQueuedTransactions).fill('0x1234'),
contexts: invalidTimestampContexts,
shouldStartAtElement: 0,
totalElementsToAppend: 2 * numQueuedTransactions,
})
).to.be.revertedWith(
'Sequencer transaction timestamp exceeds that of next queue element.'
)
})
it('reverts if wrong block number in the middle', async () => {
let invalidBlockNumberContexts = [...validContexts]
// put a bigger block number early
invalidBlockNumberContexts[6].blockNumber =
invalidBlockNumberContexts[8].blockNumber
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: new Array(numQueuedTransactions).fill('0x1234'),
contexts: invalidBlockNumberContexts,
shouldStartAtElement: 0,
totalElementsToAppend: 2 * numQueuedTransactions,
})
).to.be.revertedWith(
'Sequencer transaction blockNumber exceeds that of next queue element.'
)
})
})
})
})
describe('when the sequencer attempts to add transactions with out-of-bounds times', async () => {
describe('when trying to add elements from the future', () => {
it('reverts on initial timestamp in the future', async () => {
const timestamp = (await getEthTime(ethers.provider)) + 100_000_000
const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber,
},
],
shouldStartAtElement: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith('Context timestamp is from the future.')
})
it('reverts on non-initial timestamp in the future', async () => {
const timestamp = await getEthTime(ethers.provider)
const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234', '0x1234'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber,
},
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp: timestamp + 100_000_000,
blockNumber,
},
],
shouldStartAtElement: 0,
totalElementsToAppend: 2,
})
).to.be.revertedWith('Context timestamp is from the future.')
})
it('reverts on initial blocknumber in the future', async () => {
const timestamp = await getEthTime(ethers.provider)
const blockNumber = (await getNextBlockNumber(ethers.provider)) + 1
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber,
},
],
shouldStartAtElement: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith('Context block number is from the future.')
})
}) })
})
it('should revert if a queue element needs to be processed', async () => { describe('when trying to add elements which are older than the force inclusion period', async () => {
it('reverts for a timestamp older than the f.i.p. ago', async () => {
const timestamp = await getEthTime(ethers.provider)
await increaseEthTime( await increaseEthTime(
ethers.provider, ethers.provider,
FORCE_INCLUSION_PERIOD_SECONDS * 2 FORCE_INCLUSION_PERIOD_SECONDS + 1
) )
const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1
await expect( await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, { appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'], transactions: ['0x1234'],
contexts: [ contexts: [
{ {
numSequencedTransactions: 0, numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0, numSubsequentQueueTransactions: 0,
timestamp: 0, timestamp,
blockNumber: 0, blockNumber,
}, },
], ],
shouldStartAtBatch: 0, shouldStartAtElement: 0,
totalElementsToAppend: 1, totalElementsToAppend: 1,
}) })
).to.be.revertedWith( ).to.be.revertedWith('Context timestamp too far in the past.')
'Older queue batches must be processed before a new sequencer batch.'
)
}) })
it('should revert if the context timestamp is <= the head queue element timestamp', async () => { it('reverts for a blockNumber older than the f.i.p. ago', async () => {
const timestamp = (await getEthTime(ethers.provider)) + 1000 const timestamp = await getEthTime(ethers.provider)
for (let i = 0; i < FORCE_INCLUSION_PERIOD_BLOCKS + 1; i++) {
await ethers.provider.send('evm_mine', [])
}
await expect( await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, { appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'], transactions: ['0x1234'],
contexts: [ contexts: [
{ {
numSequencedTransactions: 0, numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0, numSubsequentQueueTransactions: 0,
timestamp: timestamp, timestamp,
blockNumber: 0, blockNumber: 0,
}, },
], ],
shouldStartAtBatch: 0, shouldStartAtElement: 0,
totalElementsToAppend: 1, totalElementsToAppend: 1,
}) })
).to.be.revertedWith('Sequencer transactions timestamp too high.') ).to.be.revertedWith('Context timestamp too far in the past.')
}) })
})
it('should revert if the context block number is <= the head queue element block number', async () => { describe('when trying to add elements which are older than already existing CTC elements', () => {
const timestamp = (await getEthTime(ethers.provider)) - 100 let timestamp
const blockNumber = (await getNextBlockNumber(ethers.provider)) + 100 let blockNumber
describe('when the most recent CTC element is a sequencer transaction', () => {
await expect( beforeEach(async () => {
appendSequencerBatch(OVM_CanonicalTransactionChain, { timestamp = await getEthTime(ethers.provider)
blockNumber = (await getNextBlockNumber(ethers.provider)) - 1
await appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'], transactions: ['0x1234'],
contexts: [ contexts: [
{ {
numSequencedTransactions: 0, numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0, numSubsequentQueueTransactions: 0,
timestamp: timestamp, timestamp,
blockNumber: blockNumber, blockNumber,
}, },
], ],
shouldStartAtBatch: 0, shouldStartAtElement: 0,
totalElementsToAppend: 1, totalElementsToAppend: 1,
}) })
).to.be.revertedWith('Sequencer transactions blockNumber too high.') })
it('reverts if timestamp is older than previous one', async () => {
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp: timestamp - 1,
blockNumber,
},
],
shouldStartAtElement: 1,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
'Context timestamp is lower than last submitted.'
)
})
it('reverts if block number is older than previous one', async () => {
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber: blockNumber - 1,
},
],
shouldStartAtElement: 1,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
'Context block number is lower than last submitted.'
)
})
}) })
describe('when the previous transaction is a queue transaction', () => {
beforeEach(async () => {
// enqueue
timestamp = await getEthTime(ethers.provider)
blockNumber = await getNextBlockNumber(ethers.provider)
await OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data)
await appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 1, // final element will be CTC
timestamp,
blockNumber,
},
],
shouldStartAtElement: 0,
totalElementsToAppend: 2,
})
})
it('reverts if timestamp is older than previous one', async () => {
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp: timestamp - 1,
blockNumber,
},
],
shouldStartAtElement: 2,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
'Context timestamp is lower than last submitted.'
)
})
it('reverts if block number is older than previous one', async () => {
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber: blockNumber - 1,
},
],
shouldStartAtElement: 2,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
'Context block number is lower than last submitted.'
)
})
})
})
it('should revert if a queue element has expired and needs to be included', async () => {
// enqueue a tx
await OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data)
// increase time past force inclusion period
await increaseEthTime(
ethers.provider,
FORCE_INCLUSION_PERIOD_SECONDS * 2
)
const validTimestamp = (await getBlockTime(ethers.provider)) + 100
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp: validTimestamp,
blockNumber: 0,
},
],
shouldStartAtElement: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
'Previously enqueued batches have expired and must be appended before a new sequencer batch.'
)
})
})
for (const size of ELEMENT_TEST_SIZES) {
const target = NON_ZERO_ADDRESS
const gasLimit = 500_000
const data = '0x' + '12'.repeat(1234)
describe(`Happy path: when appending ${size} sequencer transactions`, () => {
describe('when not inserting queue elements in between', () => { describe('when not inserting queue elements in between', () => {
describe('when using a single batch context', () => { describe('when using a single batch context', () => {
let contexts: any[] let contexts: any[]
...@@ -927,7 +1426,7 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -927,7 +1426,7 @@ describe('OVM_CanonicalTransactionChain', () => {
appendSequencerBatch(OVM_CanonicalTransactionChain, { appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions, transactions,
contexts, contexts,
shouldStartAtBatch: 0, shouldStartAtElement: 0,
totalElementsToAppend: size, totalElementsToAppend: size,
}) })
) )
...@@ -978,7 +1477,7 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -978,7 +1477,7 @@ describe('OVM_CanonicalTransactionChain', () => {
appendSequencerBatch(OVM_CanonicalTransactionChain, { appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions, transactions,
contexts, contexts,
shouldStartAtBatch: 0, shouldStartAtElement: 0,
totalElementsToAppend: size * 2, totalElementsToAppend: size * 2,
}) })
) )
...@@ -990,11 +1489,8 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -990,11 +1489,8 @@ describe('OVM_CanonicalTransactionChain', () => {
}) })
}) })
describe(`between every ${Math.max( const spacing = Math.max(Math.floor(size / 4), 1)
Math.floor(size / 8), describe(`between every ${spacing} sequencer transaction`, () => {
1
)} sequencer transaction`, () => {
const spacing = Math.max(Math.floor(size / 8), 1)
let contexts: any[] let contexts: any[]
let transactions: any[] let transactions: any[]
beforeEach(async () => { beforeEach(async () => {
...@@ -1021,7 +1517,7 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -1021,7 +1517,7 @@ describe('OVM_CanonicalTransactionChain', () => {
appendSequencerBatch(OVM_CanonicalTransactionChain, { appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions, transactions,
contexts, contexts,
shouldStartAtBatch: 0, shouldStartAtElement: 0,
totalElementsToAppend: size + spacing, totalElementsToAppend: size + spacing,
}) })
) )
...@@ -1061,15 +1557,16 @@ describe('OVM_CanonicalTransactionChain', () => { ...@@ -1061,15 +1557,16 @@ describe('OVM_CanonicalTransactionChain', () => {
return '0x' + '12' + '34'.repeat(idx) return '0x' + '12' + '34'.repeat(idx)
}) })
await appendSequencerBatch( const res = await appendSequencerBatch(
OVM_CanonicalTransactionChain.connect(sequencer), OVM_CanonicalTransactionChain.connect(sequencer),
{ {
transactions, transactions,
contexts, contexts,
shouldStartAtBatch: 0, shouldStartAtElement: 0,
totalElementsToAppend: size, totalElementsToAppend: size,
} }
) )
await res.wait()
}) })
it(`should return ${size}`, async () => { it(`should return ${size}`, async () => {
......
...@@ -11,7 +11,6 @@ import { ...@@ -11,7 +11,6 @@ import {
setProxyTarget, setProxyTarget,
NON_NULL_BYTES32, NON_NULL_BYTES32,
ZERO_ADDRESS, ZERO_ADDRESS,
toHexString32,
getEthTime, getEthTime,
NULL_BYTES32, NULL_BYTES32,
increaseEthTime, increaseEthTime,
...@@ -62,10 +61,15 @@ describe('OVM_StateCommitmentChain', () => { ...@@ -62,10 +61,15 @@ describe('OVM_StateCommitmentChain', () => {
}) })
let Factory__OVM_StateCommitmentChain: ContractFactory let Factory__OVM_StateCommitmentChain: ContractFactory
let Factory__OVM_ChainStorageContainer: ContractFactory
before(async () => { before(async () => {
Factory__OVM_StateCommitmentChain = await ethers.getContractFactory( Factory__OVM_StateCommitmentChain = await ethers.getContractFactory(
'OVM_StateCommitmentChain' 'OVM_StateCommitmentChain'
) )
Factory__OVM_ChainStorageContainer = await ethers.getContractFactory(
'OVM_ChainStorageContainer'
)
}) })
let OVM_StateCommitmentChain: Contract let OVM_StateCommitmentChain: Contract
...@@ -75,7 +79,21 @@ describe('OVM_StateCommitmentChain', () => { ...@@ -75,7 +79,21 @@ describe('OVM_StateCommitmentChain', () => {
60 * 60 * 24 * 7, // 1 week fraud proof window 60 * 60 * 24 * 7, // 1 week fraud proof window
60 * 30 // 30 minute sequencer publish 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', () => { describe('appendStateBatch', () => {
...@@ -176,7 +194,7 @@ describe('OVM_StateCommitmentChain', () => { ...@@ -176,7 +194,7 @@ describe('OVM_StateCommitmentChain', () => {
const batch = [NON_NULL_BYTES32] const batch = [NON_NULL_BYTES32]
const batchHeader = { const batchHeader = {
batchIndex: 0, batchIndex: 0,
batchRoot: keccak256(NON_NULL_BYTES32), batchRoot: NON_NULL_BYTES32,
batchSize: 1, batchSize: 1,
prevTotalElements: 0, prevTotalElements: 0,
extraData: NULL_BYTES32, extraData: NULL_BYTES32,
......
...@@ -22,6 +22,18 @@ const NESTED_CREATED_CONTRACT = '0xcb964b3f4162a0d4f5c997b40e19da5a546bc36f' ...@@ -22,6 +22,18 @@ const NESTED_CREATED_CONTRACT = '0xcb964b3f4162a0d4f5c997b40e19da5a546bc36f'
const DUMMY_REVERT_DATA = const DUMMY_REVERT_DATA =
'0xdeadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420' '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 = { const test_ovmCREATE: TestDefinition = {
name: 'Basic tests for ovmCREATE', name: 'Basic tests for ovmCREATE',
preState: { preState: {
...@@ -656,6 +668,218 @@ const test_ovmCREATE: TestDefinition = { ...@@ -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() const runner = new ExecutionManagerTestRunner()
......
...@@ -101,6 +101,27 @@ const test_run: TestDefinition = { ...@@ -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 { ...@@ -15,6 +15,7 @@ import {
setProxyTarget, setProxyTarget,
TrieTestGenerator, TrieTestGenerator,
ZERO_ADDRESS, ZERO_ADDRESS,
numberToHexString,
} from '../../../helpers' } from '../../../helpers'
import { import {
MockContract, MockContract,
...@@ -271,7 +272,54 @@ describe('OVM_StateTransitioner', () => { ...@@ -271,7 +272,54 @@ describe('OVM_StateTransitioner', () => {
}) })
describe('applyTransaction', () => { 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', () => { 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 = { ...@@ -43,4 +43,5 @@ export const REVERT_FLAGS = {
CREATE_COLLISION: 6, CREATE_COLLISION: 6,
STATIC_VIOLATION: 7, STATIC_VIOLATION: 7,
CREATE_EXCEPTION: 8, CREATE_EXCEPTION: 8,
CREATOR_NOT_ALLOWED: 9,
} }
...@@ -17,6 +17,7 @@ export const DEFAULT_ACCOUNTS_BUIDLER = defaultAccounts.map((account) => { ...@@ -17,6 +17,7 @@ export const DEFAULT_ACCOUNTS_BUIDLER = defaultAccounts.map((account) => {
export const OVM_TX_GAS_LIMIT = 10_000_000 export const OVM_TX_GAS_LIMIT = 10_000_000
export const RUN_OVM_TEST_GAS = 20_000_000 export const RUN_OVM_TEST_GAS = 20_000_000
export const FORCE_INCLUSION_PERIOD_SECONDS = 600 export const FORCE_INCLUSION_PERIOD_SECONDS = 600
export const FORCE_INCLUSION_PERIOD_BLOCKS = 600 / 12
export const NULL_BYTES32 = makeHexString('00', 32) export const NULL_BYTES32 = makeHexString('00', 32)
export const NON_NULL_BYTES32 = makeHexString('11', 32) export const NON_NULL_BYTES32 = makeHexString('11', 32)
......
...@@ -35,7 +35,9 @@ import { ...@@ -35,7 +35,9 @@ import {
OVM_TX_GAS_LIMIT, OVM_TX_GAS_LIMIT,
RUN_OVM_TEST_GAS, RUN_OVM_TEST_GAS,
NON_NULL_BYTES32, NON_NULL_BYTES32,
NULL_BYTES32,
} from '../constants' } from '../constants'
import { getStorageXOR } from '../'
export class ExecutionManagerTestRunner { export class ExecutionManagerTestRunner {
private snapshot: string private snapshot: string
...@@ -45,17 +47,47 @@ export class ExecutionManagerTestRunner { ...@@ -45,17 +47,47 @@ export class ExecutionManagerTestRunner {
OVM_ExecutionManager: ModifiableContract OVM_ExecutionManager: ModifiableContract
Helper_TestRunner: Contract Helper_TestRunner: Contract
Factory__Helper_TestRunner_CREATE: ContractFactory Factory__Helper_TestRunner_CREATE: ContractFactory
OVM_DeployerWhitelist: Contract
} = { } = {
OVM_SafetyChecker: undefined, OVM_SafetyChecker: undefined,
OVM_StateManager: undefined, OVM_StateManager: undefined,
OVM_ExecutionManager: undefined, OVM_ExecutionManager: undefined,
Helper_TestRunner: undefined, Helper_TestRunner: undefined,
Factory__Helper_TestRunner_CREATE: 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) { public run(test: TestDefinition) {
test.preState = test.preState || {} ;(test.preState = merge(
test.postState = test.postState || {} cloneDeep(this.defaultPreState),
cloneDeep(test.preState)
)),
(test.postState = test.postState || {})
describe(`OVM_ExecutionManager Test: ${test.name}`, () => { describe(`OVM_ExecutionManager Test: ${test.name}`, () => {
test.subTests?.map((subTest) => { test.subTests?.map((subTest) => {
...@@ -164,6 +196,12 @@ export class ExecutionManagerTestRunner { ...@@ -164,6 +196,12 @@ export class ExecutionManagerTestRunner {
this.contracts.OVM_SafetyChecker.address this.contracts.OVM_SafetyChecker.address
) )
const DeployerWhitelist = await (
await ethers.getContractFactory('OVM_DeployerWhitelist')
).deploy()
this.contracts.OVM_DeployerWhitelist = DeployerWhitelist
this.contracts.OVM_ExecutionManager = await ( this.contracts.OVM_ExecutionManager = await (
await smoddit('OVM_ExecutionManager') await smoddit('OVM_ExecutionManager')
).deploy( ).deploy(
...@@ -211,6 +249,8 @@ export class ExecutionManagerTestRunner { ...@@ -211,6 +249,8 @@ export class ExecutionManagerTestRunner {
return this.contracts.OVM_SafetyChecker.address return this.contracts.OVM_SafetyChecker.address
} else if (kv === '$OVM_CALL_HELPER') { } else if (kv === '$OVM_CALL_HELPER') {
return this.contracts.Helper_TestRunner.address 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_')) { } else if (kv.startsWith('$DUMMY_OVM_ADDRESS_')) {
return ExecutionManagerTestRunner.getDummyAddress(kv) return ExecutionManagerTestRunner.getDummyAddress(kv)
} else { } else {
...@@ -262,7 +302,7 @@ export class ExecutionManagerTestRunner { ...@@ -262,7 +302,7 @@ export class ExecutionManagerTestRunner {
calldata = this.encodeFunctionData(runStep) calldata = this.encodeFunctionData(runStep)
} }
await this.contracts.OVM_ExecutionManager.run( const toRun = this.contracts.OVM_ExecutionManager.run(
{ {
timestamp: step.functionParams.timestamp, timestamp: step.functionParams.timestamp,
blockNumber: 0, blockNumber: 0,
...@@ -273,8 +313,13 @@ export class ExecutionManagerTestRunner { ...@@ -273,8 +313,13 @@ export class ExecutionManagerTestRunner {
data: calldata, data: calldata,
}, },
this.contracts.OVM_StateManager.address, 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 { } else {
await this.contracts.OVM_ExecutionManager.ovmCALL( await this.contracts.OVM_ExecutionManager.ovmCALL(
OVM_TX_GAS_LIMIT, OVM_TX_GAS_LIMIT,
...@@ -294,9 +339,19 @@ export class ExecutionManagerTestRunner { ...@@ -294,9 +339,19 @@ export class ExecutionManagerTestRunner {
functionData: this.encodeFunctionData(step), functionData: this.encodeFunctionData(step),
expectedReturnStatus: this.getReturnStatus(step), expectedReturnStatus: this.getReturnStatus(step),
expectedReturnData: this.encodeExpectedReturnData(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 { private getReturnStatus(step: TestStep): boolean {
if (isTestStep_evm(step)) { if (isTestStep_evm(step)) {
return false return false
...@@ -306,7 +361,8 @@ export class ExecutionManagerTestRunner { ...@@ -306,7 +361,8 @@ export class ExecutionManagerTestRunner {
if ( if (
isRevertFlagError(step.expectedReturnValue) && isRevertFlagError(step.expectedReturnValue) &&
(step.expectedReturnValue.flag === REVERT_FLAGS.INVALID_STATE_ACCESS || (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 return step.expectedReturnStatus
} else { } else {
...@@ -365,6 +421,16 @@ export class ExecutionManagerTestRunner { ...@@ -365,6 +421,16 @@ export class ExecutionManagerTestRunner {
}) || [] }) || []
).data, ).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)) { } else if (isTestStep_REVERT(step)) {
functionParams = [step.revertData || '0x'] functionParams = [step.revertData || '0x']
} }
......
...@@ -19,6 +19,7 @@ type RevertFlagError = { ...@@ -19,6 +19,7 @@ type RevertFlagError = {
nuisanceGasLeft?: number nuisanceGasLeft?: number
ovmGasRefund?: number ovmGasRefund?: number
data?: string data?: string
onlyValidateFlag?: boolean
} }
interface TestStep_evm { interface TestStep_evm {
...@@ -145,6 +146,7 @@ interface TestStep_CREATEEOA { ...@@ -145,6 +146,7 @@ interface TestStep_CREATEEOA {
export interface TestStep_Run { export interface TestStep_Run {
functionName: 'run' functionName: 'run'
suppliedGas?: number
functionParams: { functionParams: {
timestamp: number timestamp: number
queueOrigin: number queueOrigin: number
...@@ -155,6 +157,7 @@ export interface TestStep_Run { ...@@ -155,6 +157,7 @@ export interface TestStep_Run {
data?: string data?: string
subSteps?: TestStep[] subSteps?: TestStep[]
} }
expectedRevertValue?: string
} }
export type TestStep = export type TestStep =
...@@ -177,6 +180,7 @@ export interface ParsedTestStep { ...@@ -177,6 +180,7 @@ export interface ParsedTestStep {
functionData: string functionData: string
expectedReturnStatus: boolean expectedReturnStatus: boolean
expectedReturnData: string expectedReturnData: string
onlyValidateFlag: boolean
} }
export const isRevertFlagError = ( export const isRevertFlagError = (
......
...@@ -19,9 +19,12 @@ export const increaseEthTime = async ( ...@@ -19,9 +19,12 @@ export const increaseEthTime = async (
export const getBlockTime = async ( export const getBlockTime = async (
provider: any, provider: any,
block: number block?: number
): Promise<number> => { ): Promise<number> => {
await provider.send('evm_mine', []) await provider.send('evm_mine', [])
if (!!block) {
block = await getNextBlockNumber(provider)
}
return (await provider.getBlock(block)).timestamp return (await provider.getBlock(block)).timestamp
} }
......
...@@ -2123,6 +2123,11 @@ buffer-from@^1.0.0: ...@@ -2123,6 +2123,11 @@ buffer-from@^1.0.0:
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== 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: buffer-to-arraybuffer@^0.0.5:
version "0.0.5" version "0.0.5"
resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" 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: ...@@ -2686,6 +2691,11 @@ crypto-browserify@3.12.0:
randombytes "^2.0.0" randombytes "^2.0.0"
randomfill "^1.0.3" 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: d@1, d@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
...@@ -4792,6 +4802,11 @@ is-buffer@^1.1.5: ...@@ -4792,6 +4802,11 @@ is-buffer@^1.1.5:
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== 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: is-buffer@~2.0.3:
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623"
...@@ -5736,6 +5751,11 @@ merge-descriptors@1.0.1: ...@@ -5736,6 +5751,11 @@ merge-descriptors@1.0.1:
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= 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: merkle-patricia-tree@3.0.0, merkle-patricia-tree@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-3.0.0.tgz#448d85415565df72febc33ca362b8b614f5a58f8" 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: ...@@ -5776,6 +5796,17 @@ merkle-patricia-tree@^4.0.0:
rlp "^2.2.4" rlp "^2.2.4"
semaphore-async-await "^1.5.1" 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: methods@~1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
...@@ -7986,6 +8017,11 @@ tough-cookie@~2.5.0: ...@@ -7986,6 +8017,11 @@ tough-cookie@~2.5.0:
psl "^1.1.28" psl "^1.1.28"
punycode "^2.1.1" 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: trim-repeated@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" 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