Commit 093ec7eb authored by smartcontracts's avatar smartcontracts Committed by GitHub

feat: CanonicalTransactionChain gas optimizations (#228)

* Final gas optimizations for CTC

* delete and re-init

* Fix issue with tests failing

* Use encoding/decoding from core-utils

* dev: Use eth-optimism/core-utils where applicable (#281)

* Use core-utils where applicable

* Further reduce utils

* Update ovmCREATEEOA.spec.ts
Co-authored-by: default avatarMark Tyneway <mark.tyneway@gmail.com>

* remove unused import

* Revert "remove unused import"

This reverts commit 1df87926e7a3ca8ebdf7448ad5f0395fe7766ff7.

* actually fix imports

* A few more comments

* address PR feedback - add comments, renaming vars

* Optimization: Conditional Monotonicity Checking (#307)

* trying it

* cleanup

* remove duplicate comment

* fix docstring

* fix var name

* missing period
Co-authored-by: default avatarBen Jones <ben@pseudonym.party>
Co-authored-by: default avatarMaurelian <maurelian@protonmail.ch>
Co-authored-by: default avatarMark Tyneway <mark.tyneway@gmail.com>
Co-authored-by: default avatarKevin Ho <kevinjho1996@gmail.com>
parent 61b227d3
......@@ -208,24 +208,10 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
Lib_OVMCodec.QueueElement memory _element
)
{
iOVM_ChainStorageContainer ctcQueue = queue();
uint40 trueIndex = uint40(_index * 2);
bytes32 queueRoot = ctcQueue.get(trueIndex);
bytes32 timestampAndBlockNumber = ctcQueue.get(trueIndex + 1);
uint40 elementTimestamp;
uint40 elementBlockNumber;
assembly {
elementTimestamp := and(timestampAndBlockNumber, 0x000000000000000000000000000000000000000000000000000000FFFFFFFFFF)
elementBlockNumber := shr(40, and(timestampAndBlockNumber, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000))
}
return Lib_OVMCodec.QueueElement({
queueRoot: queueRoot,
timestamp: elementTimestamp,
blockNumber: elementBlockNumber
});
return _getQueueElement(
_index,
queue()
);
}
/**
......@@ -256,10 +242,9 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
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);
return _getQueueLength(
queue()
);
}
/**
......@@ -326,20 +311,23 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
timestampAndBlockNumber := or(timestampAndBlockNumber, shl(40, number()))
}
iOVM_ChainStorageContainer ctcQueue = queue();
iOVM_ChainStorageContainer queueRef = queue();
ctcQueue.push2(
queueRef.push2(
transactionHash,
timestampAndBlockNumber
);
uint256 queueIndex = ctcQueue.length() / 2;
// The underlying queue data structure stores 2 elements
// per insertion, so to get the real queue length we need
// to divide by 2 and subtract 1. See the usage of `push2(..)`.
uint256 queueIndex = queueRef.length() / 2 - 1;
emit TransactionEnqueued(
msg.sender,
_target,
_gasLimit,
_data,
queueIndex - 1,
queueIndex,
block.timestamp
);
}
......@@ -354,45 +342,45 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
override
public
{
// Disable `appendQueueBatch` for mainnet
// TEMPORARY: Disable `appendQueueBatch` for minnet
revert("appendQueueBatch is currently disabled.");
_numQueuedTransactions = Lib_Math.min(_numQueuedTransactions, getNumPendingQueueElements());
require(
_numQueuedTransactions > 0,
"Must append more than zero transactions."
);
bytes32[] memory leaves = new bytes32[](_numQueuedTransactions);
uint40 nextQueueIndex = getNextQueueIndex();
for (uint256 i = 0; i < _numQueuedTransactions; i++) {
if (msg.sender != resolve("OVM_Sequencer")) {
Lib_OVMCodec.QueueElement memory el = getQueueElement(nextQueueIndex);
require(
el.timestamp + forceInclusionPeriodSeconds < block.timestamp,
"Queue transactions cannot be submitted during the sequencer inclusion period."
);
}
leaves[i] = _getQueueLeafHash(nextQueueIndex);
nextQueueIndex++;
}
Lib_OVMCodec.QueueElement memory lastElement = getQueueElement(nextQueueIndex - 1);
_appendBatch(
Lib_MerkleTree.getMerkleRoot(leaves),
_numQueuedTransactions,
_numQueuedTransactions,
lastElement.timestamp,
lastElement.blockNumber
);
emit QueueBatchAppended(
nextQueueIndex - _numQueuedTransactions,
_numQueuedTransactions,
getTotalElements()
);
// _numQueuedTransactions = Lib_Math.min(_numQueuedTransactions, getNumPendingQueueElements());
// require(
// _numQueuedTransactions > 0,
// "Must append more than zero transactions."
// );
// bytes32[] memory leaves = new bytes32[](_numQueuedTransactions);
// uint40 nextQueueIndex = getNextQueueIndex();
// for (uint256 i = 0; i < _numQueuedTransactions; i++) {
// if (msg.sender != resolve("OVM_Sequencer")) {
// Lib_OVMCodec.QueueElement memory el = getQueueElement(nextQueueIndex);
// require(
// el.timestamp + forceInclusionPeriodSeconds < block.timestamp,
// "Queue transactions cannot be submitted during the sequencer inclusion period."
// );
// }
// leaves[i] = _getQueueLeafHash(nextQueueIndex);
// nextQueueIndex++;
// }
// Lib_OVMCodec.QueueElement memory lastElement = getQueueElement(nextQueueIndex - 1);
// _appendBatch(
// Lib_MerkleTree.getMerkleRoot(leaves),
// _numQueuedTransactions,
// _numQueuedTransactions,
// lastElement.timestamp,
// lastElement.blockNumber
// );
// emit QueueBatchAppended(
// nextQueueIndex - _numQueuedTransactions,
// _numQueuedTransactions,
// getTotalElements()
// );
}
/**
......@@ -437,64 +425,98 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
);
uint40 nextTransactionPtr = uint40(BATCH_CONTEXT_START_POS + BATCH_CONTEXT_SIZE * numContexts);
uint256 calldataSize;
assembly {
calldataSize := calldatasize()
}
require(
calldataSize >= nextTransactionPtr,
msg.data.length >= nextTransactionPtr,
"Not enough BatchContexts provided."
);
// Get queue length for future comparison/
uint40 queueLength = getQueueLength();
// Take a reference to the queue and its length so we don't have to keep resolving it.
// Length isn't going to change during the course of execution, so it's fine to simply
// resolve this once at the start. Saves gas.
iOVM_ChainStorageContainer queueRef = queue();
uint40 queueLength = _getQueueLength(queueRef);
// Reserve some memory to save gas on hashing later on. This is a relatively safe estimate
// for the average transaction size that will prevent having to resize this chunk of memory
// later on. Saves gas.
bytes memory hashMemory = new bytes((msg.data.length / totalElementsToAppend) * 2);
// Initialize the array of canonical chain leaves that we will append.
bytes32[] memory leaves = new bytes32[](totalElementsToAppend);
// Each leaf index corresponds to a tx, either sequenced or enqueued.
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();
BatchContext memory curContext;
for (uint32 i = 0; i < numContexts; i++) {
BatchContext memory nextContext = _getBatchContext(i);
if (i == 0) {
// Execute a special check for the first batch.
_validateFirstBatchContext(nextContext);
}
_validateNextBatchContext(curContext, nextContext, nextQueueIndex);
// Execute this check on every single batch, including the first one.
_validateNextBatchContext(
curContext,
nextContext,
nextQueueIndex,
queueLength,
queueRef
);
// Now we can update our current context.
curContext = nextContext;
// Process sequencer transactions first.
for (uint32 j = 0; j < curContext.numSequencedTransactions; j++) {
uint256 txDataLength;
assembly {
txDataLength := shr(232, calldataload(nextTransactionPtr))
}
leaves[leafIndex] = _getSequencerLeafHash(curContext, nextTransactionPtr, txDataLength);
leaves[leafIndex] = _getSequencerLeafHash(
curContext,
nextTransactionPtr,
txDataLength,
hashMemory
);
nextTransactionPtr += uint40(TX_DATA_HEADER_SIZE + txDataLength);
numSequencerTransactions++;
leafIndex++;
}
// Now process any subsequent queue transactions.
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[leafIndex] = _getQueueLeafHash(nextQueueIndex);
nextQueueIndex++;
leafIndex++;
}
}
_validateFinalBatchContext(curContext);
_validateFinalBatchContext(
curContext,
nextQueueIndex,
queueLength,
queueRef
);
require(
calldataSize == nextTransactionPtr,
msg.data.length == nextTransactionPtr,
"Not all sequencer transactions were processed."
);
......@@ -513,7 +535,14 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
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);
// curContext.numSubsequentQueueTransactions > 0 which means that we've processed at least one queue element.
// We increment nextQueueIndex after processing each queue element,
// so the index of the last element we processed is nextQueueIndex - 1.
Lib_OVMCodec.QueueElement memory lastElement = _getQueueElement(
nextQueueIndex - 1,
queueRef
);
blockTimestamp = lastElement.timestamp;
blockNumber = lastElement.blockNumber;
}
......@@ -705,11 +734,50 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
);
}
/**
* Gets the queue element at a particular index.
* @param _index Index of the queue element to access.
* @return _element Queue element at the given index.
*/
function _getQueueElement(
uint256 _index,
iOVM_ChainStorageContainer _queueRef
)
internal
view
returns (
Lib_OVMCodec.QueueElement memory _element
)
{
// The underlying queue data structure stores 2 elements
// per insertion, so to get the actual desired queue index
// we need to multiply by 2. See the usage of `push2(..)`.
(
bytes32 transactionHash,
bytes32 timestampAndBlockNumber
) = _queueRef.get2(uint40(_index * 2));
uint40 elementTimestamp;
uint40 elementBlockNumber;
assembly {
elementTimestamp := and(timestampAndBlockNumber, 0x000000000000000000000000000000000000000000000000000000FFFFFFFFFF)
elementBlockNumber := shr(40, and(timestampAndBlockNumber, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000))
}
return Lib_OVMCodec.QueueElement({
transactionHash: transactionHash,
timestamp: elementTimestamp,
blockNumber: elementBlockNumber
});
}
/**
* Retrieves the length of the queue.
* @return Length of the queue.
*/
function _getQueueLength()
function _getQueueLength(
iOVM_ChainStorageContainer _queueRef
)
internal
view
returns (
......@@ -719,7 +787,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
// 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);
return uint40(_queueRef.length() / 2);
}
/**
......@@ -732,7 +800,8 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
function _getSequencerLeafHash(
BatchContext memory _context,
uint256 _nextTransactionPtr,
uint256 _txDataLength
uint256 _txDataLength,
bytes memory _hashMemory
)
internal
pure
......@@ -740,14 +809,17 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
bytes32
)
{
// Only allocate more memory if we didn't reserve enough to begin with.
if (BYTES_TILL_TX_DATA + _txDataLength > _hashMemory.length) {
_hashMemory = new bytes(BYTES_TILL_TX_DATA + _txDataLength);
}
bytes memory chainElement = new bytes(BYTES_TILL_TX_DATA + _txDataLength);
uint256 ctxTimestamp = _context.timestamp;
uint256 ctxBlockNumber = _context.blockNumber;
bytes32 leafHash;
assembly {
let chainElementStart := add(chainElement, 0x20)
let chainElementStart := add(_hashMemory, 0x20)
// Set the first byte equal to `1` to indicate this is a sequencer chain element.
// This distinguishes sequencer ChainElements from queue ChainElements because
......@@ -825,10 +897,11 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
)
internal
{
iOVM_ChainStorageContainer batchesRef = batches();
(uint40 totalElements, uint40 nextQueueIndex,,) = _getBatchExtraData();
Lib_OVMCodec.ChainBatchHeader memory header = Lib_OVMCodec.ChainBatchHeader({
batchIndex: batches().length(),
batchIndex: batchesRef.length(),
batchRoot: _transactionRoot,
batchSize: _batchSize,
prevTotalElements: totalElements,
......@@ -851,7 +924,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
_blockNumber
);
batches().push(batchHeaderHash, latestBatchContext);
batchesRef.push(batchHeaderHash, latestBatchContext);
}
/**
......@@ -867,12 +940,66 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
// If there are existing elements, this batch must come later.
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.");
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.");
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."
);
}
/**
* Checks that a given batch context has a time context which is below a given que element
* @param _context The batch context to validate has values lower.
* @param _queueIndex Index of the queue element we are validating came later than the context.
* @param _queueRef The storage container for the queue.
*/
function _validateContextBeforeEnqueue(
BatchContext memory _context,
uint40 _queueIndex,
iOVM_ChainStorageContainer _queueRef
)
internal
view
{
Lib_OVMCodec.QueueElement memory nextQueueElement = _getQueueElement(
_queueIndex,
_queueRef
);
// 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(
_context.timestamp <= nextQueueElement.timestamp,
"Sequencer transaction timestamp exceeds that of next queue element."
);
require(
_context.blockNumber <= nextQueueElement.blockNumber,
"Sequencer transaction blockNumber exceeds that of next queue element."
);
}
/**
......@@ -880,11 +1007,15 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
* @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.
* @param _queueLength The length of the queue at the start of the batchAppend call.
* @param _queueRef The storage container for the queue.
*/
function _validateNextBatchContext(
BatchContext memory _prevContext,
BatchContext memory _nextContext,
uint40 _nextQueueIndex
uint40 _nextQueueIndex,
uint40 _queueLength,
iOVM_ChainStorageContainer _queueRef
)
internal
view
......@@ -900,26 +1031,12 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
"Context blockNumber values must monotonically increase."
);
// If there are some queue elements pending:
if (getQueueLength() - _nextQueueIndex > 0) {
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."
// If there is going to be a queue element pulled in from this context:
if (_nextContext.numSubsequentQueueTransactions > 0) {
_validateContextBeforeEnqueue(
_nextContext,
_nextQueueIndex,
_queueRef
);
}
}
......@@ -927,13 +1044,27 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
/**
* Checks that the final batch context in a sequencer submission is valid.
* @param _finalContext The batch context to validate.
* @param _queueLength The length of the queue at the start of the batchAppend call.
* @param _nextQueueIndex The next element in the queue that will be pulled into the CTC.
* @param _queueRef The storage container for the queue.
*/
function _validateFinalBatchContext(
BatchContext memory _finalContext
BatchContext memory _finalContext,
uint40 _nextQueueIndex,
uint40 _queueLength,
iOVM_ChainStorageContainer _queueRef
)
internal
view
{
// If the queue is not now empty, check the mononoticity of whatever the next batch that will come in is.
if (_queueLength - _nextQueueIndex > 0 && _finalContext.numSubsequentQueueTransactions == 0) {
_validateContextBeforeEnqueue(
_finalContext,
_nextQueueIndex,
_queueRef
);
}
// 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.");
......@@ -1053,7 +1184,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad
Lib_OVMCodec.QueueElement memory el = getQueueElement(_queueIndex);
require(
el.queueRoot == transactionHash
el.transactionHash == transactionHash
&& el.timestamp == _transaction.timestamp
&& el.blockNumber == _transaction.blockNumber,
"Invalid Queue transaction."
......
......@@ -187,6 +187,23 @@ contract OVM_ChainStorageContainer is iOVM_ChainStorageContainer, Lib_AddressRes
return buffer.get(uint40(_index));
}
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
function get2(
uint256 _index
)
override
public
view
returns (
bytes32,
bytes32
)
{
return buffer.get2(uint40(_index));
}
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
......
......@@ -104,6 +104,22 @@ interface iOVM_ChainStorageContainer {
bytes32
);
/**
* Retrieves two consecutive objects from the container.
* @param _index Index of the particular objects to access.
* @return 32 byte object value at index `_index`.
* @return 32 byte object value at index `_index + 1`.
*/
function get2(
uint256 _index
)
external
view
returns (
bytes32,
bytes32
);
/**
* Removes all objects after and including a given index.
* @param _index Object index to delete from.
......
......@@ -81,7 +81,7 @@ library Lib_OVMCodec {
}
struct QueueElement {
bytes32 queueRoot;
bytes32 transactionHash;
uint40 timestamp;
uint40 blockNumber;
}
......
......@@ -211,6 +211,30 @@ library Lib_RingBuffer {
}
}
/**
* Retrieves two consecutive elements from the buffer.
* @param _self Buffer to access.
* @param _index Element index to retrieve.
* @return Value of the element at index `_index`.
* @return Value of the element at index `_index + 1`.
*/
function get2(
RingBuffer storage _self,
uint256 _index
)
internal
view
returns (
bytes32,
bytes32
)
{
return (
_self.get(_index),
_self.get(_index + 1)
);
}
/**
* Deletes all elements after (and including) a given index.
* @param _self Buffer to access.
......
......@@ -36,6 +36,8 @@
"dependencies": {
"@eth-optimism/core-utils": "^0.1.8",
"@eth-optimism/dev": "^1.1.1",
"@eth-optimism/core-utils": "^0.1.5",
"@eth-optimism/solc": "^0.6.12-alpha.1",
"@ethersproject/abstract-provider": "^5.0.8",
"@ethersproject/contracts": "^5.0.5",
"@ethersproject/hardware-wallets": "^5.0.8",
......
/* External Imports */
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers'
import { smockit, MockContract } from '@eth-optimism/smock'
import {
AppendSequencerBatchParams,
encodeAppendSequencerBatch,
} from '@eth-optimism/core-utils'
import { TransactionResponse } from '@ethersproject/abstract-provider'
import { keccak256 } from 'ethers/lib/utils'
import _ from 'lodash'
/* Internal Imports */
import {
makeAddressManager,
setProxyTarget,
FORCE_INCLUSION_PERIOD_SECONDS,
FORCE_INCLUSION_PERIOD_BLOCKS,
getEthTime,
getNextBlockNumber,
} from '../../../helpers'
// Still have some duplication from OVM_CanonicalTransactionChain.spec.ts, but it's so minimal that
// this is probably cleaner for now. Particularly since we're planning to move all of this out into
// core-utils soon anyway.
const DECOMPRESSION_ADDRESS = '0x4200000000000000000000000000000000000008'
const MAX_GAS_LIMIT = 8_000_000
const appendSequencerBatch = async (
OVM_CanonicalTransactionChain: Contract,
batch: AppendSequencerBatchParams
): Promise<TransactionResponse> => {
const methodId = keccak256(Buffer.from('appendSequencerBatch()')).slice(2, 10)
const calldata = encodeAppendSequencerBatch(batch)
return OVM_CanonicalTransactionChain.signer.sendTransaction({
to: OVM_CanonicalTransactionChain.address,
data: '0x' + methodId + calldata,
})
}
describe('[GAS BENCHMARK] OVM_CanonicalTransactionChain', () => {
let sequencer: Signer
before(async () => {
;[sequencer] = await ethers.getSigners()
})
let AddressManager: Contract
let Mock__OVM_ExecutionManager: MockContract
let Mock__OVM_StateCommitmentChain: MockContract
before(async () => {
AddressManager = await makeAddressManager()
await AddressManager.setAddress(
'OVM_Sequencer',
await sequencer.getAddress()
)
await AddressManager.setAddress(
'OVM_DecompressionPrecompileAddress',
DECOMPRESSION_ADDRESS
)
Mock__OVM_ExecutionManager = await smockit(
await ethers.getContractFactory('OVM_ExecutionManager')
)
Mock__OVM_StateCommitmentChain = await smockit(
await ethers.getContractFactory('OVM_StateCommitmentChain')
)
await setProxyTarget(
AddressManager,
'OVM_ExecutionManager',
Mock__OVM_ExecutionManager
)
await setProxyTarget(
AddressManager,
'OVM_StateCommitmentChain',
Mock__OVM_StateCommitmentChain
)
Mock__OVM_ExecutionManager.smocked.getMaxTransactionGasLimit.will.return.with(
MAX_GAS_LIMIT
)
})
let Factory__OVM_CanonicalTransactionChain: ContractFactory
let Factory__OVM_ChainStorageContainer: ContractFactory
before(async () => {
Factory__OVM_CanonicalTransactionChain = await ethers.getContractFactory(
'OVM_CanonicalTransactionChain'
)
Factory__OVM_ChainStorageContainer = await ethers.getContractFactory(
'OVM_ChainStorageContainer'
)
})
let OVM_CanonicalTransactionChain: Contract
beforeEach(async () => {
OVM_CanonicalTransactionChain = await Factory__OVM_CanonicalTransactionChain.deploy(
AddressManager.address,
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
)
})
describe('appendSequencerBatch', () => {
beforeEach(() => {
OVM_CanonicalTransactionChain = OVM_CanonicalTransactionChain.connect(
sequencer
)
})
it('200 transactions in a single context', async () => {
console.log(`Benchmark: 200 transactions in a single context.`)
const timestamp = (await getEthTime(ethers.provider)) - 100
const blockNumber = await getNextBlockNumber(ethers.provider)
const transactionTemplate = '0x' + '11'.repeat(400)
const transactions = []
const numTxs = 200
for (let i = 0; i < numTxs; i++) {
transactions.push(transactionTemplate)
}
const fixedCalldataCost =
(transactionTemplate.slice(2).length / 2) * 16 * numTxs
const res = await appendSequencerBatch(OVM_CanonicalTransactionChain, {
shouldStartAtBatch: 0,
totalElementsToAppend: numTxs,
contexts: [
{
numSequencedTransactions: numTxs,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber,
},
],
transactions,
})
const receipt = await res.wait()
console.log('Benchmark complete.')
console.log('Gas used:', receipt.gasUsed.toNumber())
console.log('Fixed calldata cost:', fixedCalldataCost)
console.log(
'Non-calldata overhead gas cost per transaction:',
(receipt.gasUsed.toNumber() - fixedCalldataCost) / numTxs
)
}).timeout(100000000)
it('200 transactions in 200 contexts', async () => {
console.log(`Benchmark: 200 transactions in 200 contexts.`)
const timestamp = (await getEthTime(ethers.provider)) - 100
const blockNumber = await getNextBlockNumber(ethers.provider)
const transactionTemplate = '0x' + '11'.repeat(400)
const transactions = []
const numTxs = 200
for (let i = 0; i < numTxs; i++) {
transactions.push(transactionTemplate)
}
const fixedCalldataCost =
(transactionTemplate.slice(2).length / 2) * 16 * numTxs
const res = await appendSequencerBatch(OVM_CanonicalTransactionChain, {
shouldStartAtBatch: 0,
totalElementsToAppend: numTxs,
contexts: [...Array(numTxs)].map(() => {
return {
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber,
}
}),
transactions,
})
const receipt = await res.wait()
console.log('Benchmark complete.')
console.log('Gas used:', receipt.gasUsed.toNumber())
console.log('Fixed calldata cost:', fixedCalldataCost)
console.log(
'Non-calldata overhead gas cost per transaction:',
(receipt.gasUsed.toNumber() - fixedCalldataCost) / numTxs
)
}).timeout(100000000)
})
})
......@@ -3,9 +3,14 @@ import { expect } from '../../../setup'
/* External Imports */
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract, BigNumber } from 'ethers'
import { TransactionResponse } from '@ethersproject/abstract-provider'
import { smockit, MockContract } from '@eth-optimism/smock'
import { remove0x } from '@eth-optimism/core-utils'
import {
AppendSequencerBatchParams,
encodeAppendSequencerBatch,
remove0x,
} from '@eth-optimism/core-utils'
import { TransactionResponse } from '@ethersproject/abstract-provider'
import { keccak256 } from 'ethers/lib/utils'
import _ from 'lodash'
/* Internal Imports */
......@@ -23,7 +28,6 @@ import {
ZERO_ADDRESS,
mineBlock,
} from '../../../helpers'
import { defaultAbiCoder, keccak256 } from 'ethers/lib/utils'
const ELEMENT_TEST_SIZES = [1, 2, 4, 8, 16]
const DECOMPRESSION_ADDRESS = '0x4200000000000000000000000000000000000008'
......@@ -31,7 +35,7 @@ const MAX_GAS_LIMIT = 8_000_000
const getQueueLeafHash = (index: number): string => {
return keccak256(
defaultAbiCoder.encode(
ethers.utils.defaultAbiCoder.encode(
['bool', 'uint256', 'uint256', 'uint256', 'bytes'],
[false, index, 0, 0, '0x']
)
......@@ -66,60 +70,12 @@ const encodeQueueTransaction = (
gasLimit: number,
data: string
): string => {
return defaultAbiCoder.encode(
return ethers.utils.defaultAbiCoder.encode(
['address', 'address', 'uint256', 'bytes'],
[sender, target, gasLimit, data]
)
}
interface BatchContext {
numSequencedTransactions: number
numSubsequentQueueTransactions: number
timestamp: number
blockNumber: number
}
interface AppendSequencerBatchParams {
shouldStartAtElement: number // 5 bytes -- starts at batch
totalElementsToAppend: number // 3 bytes -- total_elements_to_append
contexts: BatchContext[] // total_elements[fixed_size[]]
transactions: string[] // total_size_bytes[],total_size_bytes[]
}
const encodeAppendSequencerBatch = (b: AppendSequencerBatchParams): string => {
const encodedShouldStartAtElement = remove0x(
BigNumber.from(b.shouldStartAtElement).toHexString()
).padStart(10, '0')
const encodedTotalElementsToAppend = remove0x(
BigNumber.from(b.totalElementsToAppend).toHexString()
).padStart(6, '0')
const encodedContextsHeader = remove0x(
BigNumber.from(b.contexts.length).toHexString()
).padStart(6, '0')
const encodedContexts =
encodedContextsHeader +
b.contexts.reduce((acc, cur) => acc + encodeBatchContext(cur), '')
const encodedTransactionData = b.transactions.reduce((acc, cur) => {
if (cur.length % 2 !== 0)
throw new Error('Unexpected uneven hex string value!')
const encodedTxDataHeader = remove0x(
BigNumber.from(remove0x(cur).length / 2).toHexString()
).padStart(6, '0')
return acc + encodedTxDataHeader + remove0x(cur)
}, '')
return (
encodedShouldStartAtElement +
encodedTotalElementsToAppend +
encodedContexts +
encodedTransactionData
)
}
const appendSequencerBatch = async (
OVM_CanonicalTransactionChain: Contract,
batch: AppendSequencerBatchParams
......@@ -132,25 +88,6 @@ const appendSequencerBatch = async (
})
}
const encodeBatchContext = (context: BatchContext): string => {
return (
remove0x(
BigNumber.from(context.numSequencedTransactions).toHexString()
).padStart(6, '0') +
remove0x(
BigNumber.from(context.numSubsequentQueueTransactions).toHexString()
).padStart(6, '0') +
remove0x(BigNumber.from(context.timestamp).toHexString()).padStart(
10,
'0'
) +
remove0x(BigNumber.from(context.blockNumber).toHexString()).padStart(
10,
'0'
)
)
}
describe('OVM_CanonicalTransactionChain', () => {
let signer: Signer
let sequencer: Signer
......@@ -336,7 +273,7 @@ describe('OVM_CanonicalTransactionChain', () => {
const blockNumber = await getNextBlockNumber(ethers.provider)
await setEthTime(ethers.provider, timestamp)
const queueRoot = getTransactionHash(
const transactionHash = getTransactionHash(
await signer.getAddress(),
target,
gasLimit,
......@@ -358,7 +295,7 @@ describe('OVM_CanonicalTransactionChain', () => {
await OVM_CanonicalTransactionChain.getQueueElement(0)
)
).to.deep.include({
queueRoot,
transactionHash,
timestamp,
blockNumber,
})
......@@ -371,7 +308,7 @@ describe('OVM_CanonicalTransactionChain', () => {
it(`gets the element when ${size} elements exist`, async () => {
let timestamp: number
let blockNumber: number
let queueRoot: string
let transactionHash: string
const middleIndex = Math.floor(size / 2)
for (let i = 0; i < size; i++) {
......@@ -380,7 +317,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber = await getNextBlockNumber(ethers.provider)
await setEthTime(ethers.provider, timestamp)
queueRoot = getTransactionHash(
transactionHash = getTransactionHash(
await signer.getAddress(),
target,
gasLimit,
......@@ -406,7 +343,7 @@ describe('OVM_CanonicalTransactionChain', () => {
await OVM_CanonicalTransactionChain.getQueueElement(middleIndex)
)
).to.deep.include({
queueRoot,
transactionHash,
timestamp,
blockNumber,
})
......@@ -419,7 +356,7 @@ describe('OVM_CanonicalTransactionChain', () => {
it(`gets the element when ${size} elements exist`, async () => {
let timestamp: number
let blockNumber: number
let queueRoot: string
let transactionHash: string
for (let i = 0; i < size; i++) {
if (i === size - 1) {
......@@ -427,7 +364,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber = await getNextBlockNumber(ethers.provider)
await setEthTime(ethers.provider, timestamp)
queueRoot = getTransactionHash(
transactionHash = getTransactionHash(
await signer.getAddress(),
target,
gasLimit,
......@@ -453,7 +390,7 @@ describe('OVM_CanonicalTransactionChain', () => {
await OVM_CanonicalTransactionChain.getQueueElement(size - 1)
)
).to.deep.include({
queueRoot,
transactionHash,
timestamp,
blockNumber,
})
......@@ -571,7 +508,7 @@ describe('OVM_CanonicalTransactionChain', () => {
await appendSequencerBatch(
OVM_CanonicalTransactionChain.connect(sequencer),
{
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 1,
contexts: [
{
......@@ -675,7 +612,7 @@ describe('OVM_CanonicalTransactionChain', () => {
await appendSequencerBatch(
OVM_CanonicalTransactionChain.connect(sequencer),
{
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 1,
contexts: [
{
......@@ -730,63 +667,6 @@ describe('OVM_CanonicalTransactionChain', () => {
)
})
it('should allow for a lower bound per-tx gas usage of <400 gas [GAS BENCHMARK]', async () => {
const timestamp = (await getEthTime(ethers.provider)) - 100
const blockNumber = await getNextBlockNumber(ethers.provider)
// do two batch appends for no reason
await appendSequencerBatch(OVM_CanonicalTransactionChain, {
shouldStartAtElement: 0,
totalElementsToAppend: 1,
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber,
},
],
transactions: ['0x1234'],
})
await appendSequencerBatch(OVM_CanonicalTransactionChain, {
shouldStartAtElement: 1,
totalElementsToAppend: 1,
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber,
},
],
transactions: ['0x1234'],
})
console.log('\n~~~~ BEGINNGING TRASACTION IN QUESTION ~~~~')
const transactions = []
const numTxs = 200
for (let i = 0; i < numTxs; i++) {
transactions.push(
'0x' + '1080111111111111111111111111111111111111111111'.repeat(20)
)
}
const res = await appendSequencerBatch(OVM_CanonicalTransactionChain, {
shouldStartAtElement: 2,
totalElementsToAppend: numTxs,
contexts: [
{
numSequencedTransactions: numTxs,
numSubsequentQueueTransactions: 0,
timestamp,
blockNumber,
},
],
transactions,
})
const receipt = await res.wait()
console.log('Benchmark complete. Gas used:', receipt.gasUsed)
}).timeout(100000000)
it('should revert if expected start does not match current total batches', async () => {
await expect(
appendSequencerBatch(OVM_CanonicalTransactionChain, {
......@@ -799,7 +679,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber: 0,
},
],
shouldStartAtElement: 1234,
shouldStartAtBatch: 1234,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
......@@ -822,7 +702,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith('Not all sequencer transactions were processed.')
......@@ -840,7 +720,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber: 0,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith('Function can only be called by the Sequencer.')
......@@ -851,7 +731,7 @@ describe('OVM_CanonicalTransactionChain', () => {
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: ['0x1234'],
contexts: [],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith('Must provide at least one batch context.')
......@@ -869,7 +749,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber: 0,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 0,
})
).to.be.revertedWith('Must append at least one element.')
......@@ -896,10 +776,10 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith('Not enough queued transactions to append.')
).to.be.revertedWith('Index out of bounds.')
})
it('reverts when there are insufficient (but nonzero) transactions in the queue', async () => {
......@@ -922,7 +802,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: numEnqueues + 1,
})
).to.be.revertedWith('Not enough queued transactions to append.')
......@@ -952,7 +832,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 2,
})
).to.be.revertedWith(
......@@ -981,7 +861,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 2,
})
).to.be.revertedWith(
......@@ -1015,7 +895,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
......@@ -1039,7 +919,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
......@@ -1075,7 +955,7 @@ describe('OVM_CanonicalTransactionChain', () => {
await appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: new Array(numQueuedTransactions).fill('0x1234'),
contexts: validContexts,
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 2 * numQueuedTransactions,
})
})
......@@ -1090,7 +970,7 @@ describe('OVM_CanonicalTransactionChain', () => {
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: new Array(numQueuedTransactions).fill('0x1234'),
contexts: invalidTimestampContexts,
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 2 * numQueuedTransactions,
})
).to.be.revertedWith(
......@@ -1108,7 +988,7 @@ describe('OVM_CanonicalTransactionChain', () => {
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions: new Array(numQueuedTransactions).fill('0x1234'),
contexts: invalidBlockNumberContexts,
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 2 * numQueuedTransactions,
})
).to.be.revertedWith(
......@@ -1136,7 +1016,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith('Context timestamp is from the future.')
......@@ -1163,7 +1043,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 2,
})
).to.be.revertedWith('Context timestamp is from the future.')
......@@ -1184,7 +1064,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith('Context block number is from the future.')
......@@ -1212,7 +1092,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith('Context timestamp too far in the past.')
......@@ -1236,7 +1116,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber: 10,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith('Context block number too far in the past.')
......@@ -1260,7 +1140,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 1,
})
})
......@@ -1277,7 +1157,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber,
},
],
shouldStartAtElement: 1,
shouldStartAtBatch: 1,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
......@@ -1297,7 +1177,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber: blockNumber - 1,
},
],
shouldStartAtElement: 1,
shouldStartAtBatch: 1,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
......@@ -1322,7 +1202,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 2,
})
})
......@@ -1339,7 +1219,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber,
},
],
shouldStartAtElement: 2,
shouldStartAtBatch: 2,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
......@@ -1359,7 +1239,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber: blockNumber - 1,
},
],
shouldStartAtElement: 2,
shouldStartAtBatch: 2,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
......@@ -1393,7 +1273,7 @@ describe('OVM_CanonicalTransactionChain', () => {
blockNumber,
},
],
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: 1,
})
).to.be.revertedWith(
......@@ -1436,7 +1316,7 @@ describe('OVM_CanonicalTransactionChain', () => {
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions,
contexts,
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: size,
})
)
......@@ -1487,7 +1367,7 @@ describe('OVM_CanonicalTransactionChain', () => {
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions,
contexts,
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: size * 2,
})
)
......@@ -1527,7 +1407,7 @@ describe('OVM_CanonicalTransactionChain', () => {
appendSequencerBatch(OVM_CanonicalTransactionChain, {
transactions,
contexts,
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: size + spacing,
})
)
......@@ -1572,7 +1452,7 @@ describe('OVM_CanonicalTransactionChain', () => {
{
transactions,
contexts,
shouldStartAtElement: 0,
shouldStartAtBatch: 0,
totalElementsToAppend: size,
}
)
......
......@@ -51,15 +51,20 @@
ganache-core "^2.12.1"
glob "^7.1.6"
"@eth-optimism/core-utils@^0.1.8":
version "0.1.8"
resolved "https://registry.yarnpkg.com/@eth-optimism/core-utils/-/core-utils-0.1.8.tgz#bf9ac0e537e99a56b2f03af525d317aac5933c41"
integrity sha512-IdRDkyVWmrmbA4P0hvupS+uLSQ1qwG1bWtdvwqeG1+wfDMC7nfZyjdeRWrRZbFvVs/hKk20LERC7L5/lJEo1hA==
"@eth-optimism/core-utils@^0.1.5":
version "0.1.5"
resolved "https://registry.yarnpkg.com/@eth-optimism/core-utils/-/core-utils-0.1.5.tgz#90b3f3d14b25abd8a210d78678d36e54f0c841f4"
integrity sha512-DLcHKFXcfoVb0yizfHeDGvgfHYb5DRRMnZyXLXIN0HNrAeavQEjgRFLOo/qy+xNjGI99LKSvyJtHP/C9ImJDag==
dependencies:
"@ethersproject/abstract-provider" "^5.0.9"
colors "^1.4.0"
debug "^4.3.1"
ethers "^5.0.31"
axios "^0.21.1"
body-parser "^1.19.0"
debug "^4.1.1"
dotenv "^8.2.0"
ethereumjs-util "^6.2.0"
ethers "^5.0.24"
express "^4.17.1"
uuid "^3.3.3"
"@eth-optimism/dev@^1.1.1":
version "1.1.1"
......@@ -133,6 +138,21 @@
fs-extra "^9.0.1"
hardhat "^2.0.3"
"@eth-optimism/solc@^0.6.12-alpha.1":
version "0.6.12-alpha.1"
resolved "https://registry.yarnpkg.com/@eth-optimism/solc/-/solc-0.6.12-alpha.1.tgz#041876f83b34c6afe2f19dfe9626568df6ed8590"
integrity sha512-Ky73mo+2iNJs/VTaT751nMeZ7hXns0TBAlffTOxIOsScjAZ/zi/KWsDUo3r89aV2JKXcYAU/bLidxF40MVJeUw==
dependencies:
command-exists "^1.2.8"
commander "3.0.2"
follow-redirects "^1.12.1"
fs-extra "^0.30.0"
js-sha3 "0.8.0"
memorystream "^0.3.1"
require-from-string "^2.0.0"
semver "^5.5.0"
tmp "0.0.33"
"@ethereum-waffle/chai@^3.0.0", "@ethereum-waffle/chai@^3.2.2":
version "3.2.2"
resolved "https://registry.yarnpkg.com/@ethereum-waffle/chai/-/chai-3.2.2.tgz#33a349688386c9a8fdc4da5baea329036b9fe75e"
......@@ -1449,6 +1469,13 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
axios@^0.21.1:
version "0.21.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
dependencies:
follow-redirects "^1.10.0"
babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
......@@ -2094,7 +2121,7 @@ bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3:
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b"
integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==
body-parser@1.19.0, body-parser@^1.16.0:
body-parser@1.19.0, body-parser@^1.16.0, body-parser@^1.19.0:
version "1.19.0"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
......@@ -2633,11 +2660,6 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
colors@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
......@@ -3143,6 +3165,11 @@ domutils@^2.4.3, domutils@^2.4.4:
domelementtype "^2.0.1"
domhandler "^4.0.0"
dotenv@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
dotignore@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/dotignore/-/dotignore-0.1.2.tgz#f942f2200d28c3a76fbdd6f0ee9f3257c8a2e905"
......@@ -3821,7 +3848,7 @@ ethers@^4.0.0-beta.1, ethers@^4.0.32:
uuid "2.0.1"
xmlhttprequest "1.8.0"
ethers@^5, ethers@^5.0.0, ethers@^5.0.1, ethers@^5.0.14, ethers@^5.0.2, ethers@^5.0.25, ethers@^5.0.31:
ethers@^5, ethers@^5.0.0, ethers@^5.0.1, ethers@^5.0.14, ethers@^5.0.2, ethers@^5.0.24, ethers@^5.0.25, ethers@^5.0.31:
version "5.0.31"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.0.31.tgz#60e3b1425864fe5d2babc147ede01be8382a7d2a"
integrity sha512-zpq0YbNFLFn+t+ibS8UkVWFeK5w6rVMSvbSHrHAQslfazovLnQ/mc2gdN5+6P45/k8fPgHrfHrYvJ4XvyK/S1A==
......@@ -3924,7 +3951,7 @@ expand-template@^2.0.3:
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
express@^4.14.0:
express@^4.14.0, express@^4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
......@@ -4138,7 +4165,7 @@ flow-stoplight@^1.0.0:
resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b"
integrity sha1-SiksW8/4s5+mzAyxqFPYbyfu/3s=
follow-redirects@^1.12.1:
follow-redirects@^1.10.0, follow-redirects@^1.12.1:
version "1.13.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.2.tgz#dd73c8effc12728ba5cf4259d760ea5fb83e3147"
integrity sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA==
......@@ -8426,7 +8453,7 @@ uuid@3.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
uuid@^3.3.2:
uuid@^3.3.2, uuid@^3.3.3:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
......
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