Commit 6619834b authored by Kelvin Fichter's avatar Kelvin Fichter Committed by GitHub

Adds CTC verification functions, removes BaseChain (#24)

* Upgraded to new RingBuffer

* First pass at verification function

* Removed BaseChain as a contract

* Update OVM_CanonicalTransactionChain.spec.ts

* Removed unused chain variable

* Added overwriting logic to CTC

* Corrected CTC event emit

* Remove timebound ring buffer tests
parent 5026308f
...@@ -179,8 +179,8 @@ contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, OVM_BaseCros ...@@ -179,8 +179,8 @@ contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, OVM_BaseCros
{ {
return ( return (
ovmStateCommitmentChain.insideFraudProofWindow(_proof.stateRootBatchHeader) == false ovmStateCommitmentChain.insideFraudProofWindow(_proof.stateRootBatchHeader) == false
&& ovmStateCommitmentChain.verifyElement( && ovmStateCommitmentChain.verifyStateCommitment(
abi.encodePacked(_proof.stateRoot), _proof.stateRoot,
_proof.stateRootBatchHeader, _proof.stateRootBatchHeader,
_proof.stateRootProof _proof.stateRootProof
) )
......
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_BaseChain } from "../../iOVM/chain/iOVM_BaseChain.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_MerkleUtils } from "../../libraries/utils/Lib_MerkleUtils.sol";
import { TimeboundRingBuffer, Lib_TimeboundRingBuffer } from "../../libraries/utils/Lib_TimeboundRingBuffer.sol";
/**
* @title OVM_BaseChain
*/
contract OVM_BaseChain is iOVM_BaseChain {
/*******************************
* Contract Variables: Batches *
*******************************/
using Lib_TimeboundRingBuffer for TimeboundRingBuffer;
TimeboundRingBuffer internal batches;
uint256 internal totalBatches;
uint256 internal totalElements;
/***************
* Constructor *
***************/
constructor()
{
// TODO: Add propper customization
batches.init(4, 2, 100000);
}
/*************************************
* Public Functions: Batch Retrieval *
*************************************/
/**
* Gets the total number of submitted elements.
* @return _totalElements Total submitted elements.
*/
function getTotalElements()
virtual
override
public
view
returns (
uint256 _totalElements
)
{
return uint256(uint224(batches.getExtraData()));
}
/**
* Gets the total number of submitted batches.
* @return _totalBatches Total submitted batches.
*/
function getTotalBatches()
override
public
view
returns (
uint256 _totalBatches
)
{
return uint256(batches.getLength());
}
/****************************************
* Public Functions: Batch Verification *
****************************************/
/**
* Verifies an inclusion proof for a given element.
* @param _element Element to verify.
* @param _batchHeader Header of the batch in which this element was included.
* @param _proof Inclusion proof for the element.
* @return _verified Whether or not the element was included in the batch.
*/
function verifyElement(
bytes calldata _element,
Lib_OVMCodec.ChainBatchHeader memory _batchHeader,
Lib_OVMCodec.ChainInclusionProof memory _proof
)
override
public
view
returns (
bool _verified
)
{
require(
_hashBatchHeader(_batchHeader) == batches.get(uint32(_batchHeader.batchIndex)),
"Invalid batch header."
);
require(
Lib_MerkleUtils.verify(
_batchHeader.batchRoot,
_element,
_proof.index,
_proof.siblings
),
"Invalid inclusion proof."
);
return true;
}
/******************************************
* Internal Functions: Batch Modification *
******************************************/
/**
* Appends a batch to the chain.
* @param _batchHeader Batch header to append.
*/
function _appendBatch(
Lib_OVMCodec.ChainBatchHeader memory _batchHeader
)
internal
{
bytes32 batchHeaderHash = _hashBatchHeader(_batchHeader);
batches.push(batchHeaderHash, bytes28(uint224(getTotalElements() + _batchHeader.batchSize)));
}
/**
* Appends a batch to the chain.
* @param _elements Elements within the batch.
* @param _extraData Any extra data to append to the batch.
*/
function _appendBatch(
bytes[] memory _elements,
bytes memory _extraData
)
internal
{
Lib_OVMCodec.ChainBatchHeader memory batchHeader = Lib_OVMCodec.ChainBatchHeader({
batchIndex: uint(batches.getLength()),
batchRoot: Lib_MerkleUtils.getMerkleRoot(_elements),
batchSize: _elements.length,
prevTotalElements: totalElements,
extraData: _extraData
});
_appendBatch(batchHeader);
}
/**
* Appends a batch to the chain.
* @param _elements Elements within the batch.
*/
function _appendBatch(
bytes[] memory _elements
)
internal
{
_appendBatch(
_elements,
bytes('')
);
}
/**
* Removes a batch from the chain.
* @param _batchHeader Header of the batch to remove.
*/
function _deleteBatch(
Lib_OVMCodec.ChainBatchHeader memory _batchHeader
)
internal
{
require(
_batchHeader.batchIndex < batches.getLength(),
"Invalid batch index."
);
require(
_hashBatchHeader(_batchHeader) == batches.get(uint32(_batchHeader.batchIndex)),
"Invalid batch header."
);
totalElements = _batchHeader.prevTotalElements;
batches.deleteElementsAfter(uint32(_batchHeader.batchIndex - 1), bytes28(uint224(totalElements)));
}
/**
* Calculates a hash for a given batch header.
* @param _batchHeader Header to hash.
* @return _hash Hash of the header.
*/
function _hashBatchHeader(
Lib_OVMCodec.ChainBatchHeader memory _batchHeader
)
internal
pure
returns (
bytes32 _hash
)
{
return keccak256(abi.encodePacked(
_batchHeader.batchRoot,
_batchHeader.batchSize,
_batchHeader.prevTotalElements,
_batchHeader.extraData
));
}
}
...@@ -6,15 +6,12 @@ pragma experimental ABIEncoderV2; ...@@ -6,15 +6,12 @@ pragma experimental ABIEncoderV2;
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_MerkleUtils } from "../../libraries/utils/Lib_MerkleUtils.sol";
import { Lib_MerkleRoot } from "../../libraries/utils/Lib_MerkleRoot.sol"; import { Lib_RingBuffer, iRingBufferOverwriter } from "../../libraries/utils/Lib_RingBuffer.sol";
import { TimeboundRingBuffer, Lib_TimeboundRingBuffer } from "../../libraries/utils/Lib_TimeboundRingBuffer.sol";
/* Interface Imports */ /* Interface Imports */
import { iOVM_BaseChain } from "../../iOVM/chain/iOVM_BaseChain.sol";
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol"; import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
/* Contract Imports */ /* Contract Imports */
import { OVM_BaseChain } from "./OVM_BaseChain.sol";
import { OVM_ExecutionManager } from "../execution/OVM_ExecutionManager.sol"; import { OVM_ExecutionManager } from "../execution/OVM_ExecutionManager.sol";
/* Logging Imports */ /* Logging Imports */
...@@ -23,7 +20,9 @@ import { console } from "@nomiclabs/buidler/console.sol"; ...@@ -23,7 +20,9 @@ import { console } from "@nomiclabs/buidler/console.sol";
/** /**
* @title OVM_CanonicalTransactionChain * @title OVM_CanonicalTransactionChain
*/ */
contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_BaseChain, Lib_AddressResolver { contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_AddressResolver {
using Lib_RingBuffer for Lib_RingBuffer.RingBuffer;
/************* /*************
* Constants * * Constants *
...@@ -32,7 +31,8 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -32,7 +31,8 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
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
// Encoding constants (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;
...@@ -43,25 +43,19 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -43,25 +43,19 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
/************* /*************
* Variables * * Variables *
*************/ *************/
uint256 internal forceInclusionPeriodSeconds; uint256 internal forceInclusionPeriodSeconds;
uint256 internal lastOVMTimestamp; uint256 internal lastOVMTimestamp;
address internal sequencer; address internal sequencer;
address internal decompressionPrecompileAddress; address internal decompressionPrecompileAddress;
Lib_RingBuffer.RingBuffer internal batches;
using Lib_TimeboundRingBuffer for TimeboundRingBuffer; Lib_RingBuffer.RingBuffer internal queue;
TimeboundRingBuffer internal queue;
TimeboundRingBuffer internal chain;
/*************** /***************
* Constructor * * Constructor *
***************/ ***************/
/**
* @param _libAddressManager Address of the Address Manager.
* @param _forceInclusionPeriodSeconds Period during which only the sequencer can submit.
*/
constructor( constructor(
address _libAddressManager, address _libAddressManager,
uint256 _forceInclusionPeriodSeconds uint256 _forceInclusionPeriodSeconds
...@@ -72,8 +66,17 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -72,8 +66,17 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
decompressionPrecompileAddress = resolve("OVM_DecompressionPrecompileAddress"); decompressionPrecompileAddress = resolve("OVM_DecompressionPrecompileAddress");
forceInclusionPeriodSeconds = _forceInclusionPeriodSeconds; forceInclusionPeriodSeconds = _forceInclusionPeriodSeconds;
queue.init(100, 50, 10000000000); // TODO: Update once we have arbitrary condition batches.init(
batches.init(2, 50, 0); // TODO: Update once we have arbitrary condition 16,
Lib_OVMCodec.RING_BUFFER_CTC_BATCHES,
iRingBufferOverwriter(resolve("OVM_StateCommitmentChain"))
);
queue.init(
16,
Lib_OVMCodec.RING_BUFFER_CTC_QUEUE,
iRingBufferOverwriter(resolve("OVM_StateCommitmentChain"))
);
} }
...@@ -81,18 +84,35 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -81,18 +84,35 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
* Public Functions * * Public Functions *
********************/ ********************/
/**
* @inheritdoc iOVM_CanonicalTransactionChain
*/
function getTotalElements() function getTotalElements()
override(OVM_BaseChain, iOVM_BaseChain) override
public public
view view
returns ( returns (
uint256 _totalElements uint256 _totalElements
) )
{ {
(uint40 totalElements,) = _getLatestBatchContext(); (uint40 totalElements,) = _getBatchExtraData();
return uint256(totalElements); return uint256(totalElements);
} }
/**
* @inheritdoc iOVM_CanonicalTransactionChain
*/
function getTotalBatches()
override
public
view
returns (
uint256 _totalBatches
)
{
return uint256(batches.getLength());
}
/** /**
* @inheritdoc iOVM_CanonicalTransactionChain * @inheritdoc iOVM_CanonicalTransactionChain
*/ */
...@@ -106,15 +126,15 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -106,15 +126,15 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
Lib_OVMCodec.QueueElement memory _element Lib_OVMCodec.QueueElement memory _element
) )
{ {
uint32 trueIndex = uint32(_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;
uint32 elementBlockNumber; uint40 elementBlockNumber;
assembly { assembly {
elementTimestamp := and(timestampAndBlockNumber, 0x000000000000000000000000000000000000000000000000000000ffffffffff) elementTimestamp := and(timestampAndBlockNumber, 0x000000000000000000000000000000000000000000000000000000FFFFFFFFFF)
elementBlockNumber := shr(40, and(timestampAndBlockNumber, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000)) elementBlockNumber := shr(40, and(timestampAndBlockNumber, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000))
} }
return Lib_OVMCodec.QueueElement({ return Lib_OVMCodec.QueueElement({
...@@ -137,14 +157,17 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -137,14 +157,17 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
{ {
require( require(
_data.length <= MAX_ROLLUP_TX_SIZE, _data.length <= MAX_ROLLUP_TX_SIZE,
"Transaction exceeds maximum rollup data size." "Transaction exceeds maximum rollup transaction data size."
); );
require( require(
_gasLimit >= MIN_ROLLUP_TX_GAS, _gasLimit >= MIN_ROLLUP_TX_GAS,
"Layer 2 gas limit too low to enqueue." "Transaction gas limit too low to enqueue."
); );
// We need to consume some amount of L1 gas in order to rate limit transactions going into
// L2. However, L2 is cheaper than L1 so we only need to burn some small proportion of the
// provided L1 gas.
uint256 gasToConsume = _gasLimit/L2_GAS_DISCOUNT_DIVISOR; uint256 gasToConsume = _gasLimit/L2_GAS_DISCOUNT_DIVISOR;
uint256 startingGas = gasleft(); uint256 startingGas = gasleft();
...@@ -155,10 +178,6 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -155,10 +178,6 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
"Insufficient gas for L2 rate limiting burn." "Insufficient gas for L2 rate limiting burn."
); );
// We need to consume some amount of L1 gas in order to rate limit transactions going into
// L2. However, L2 is cheaper than L1 so we only need to burn some small proportion of the
// provided L1 gas.
//
// Here we do some "dumb" work in order to burn gas, although we should probably replace // Here we do some "dumb" work in order to burn gas, although we should probably replace
// this with something like minting gas token later on. // this with something like minting gas token later on.
uint256 i; uint256 i;
...@@ -166,29 +185,33 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -166,29 +185,33 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
i++; i++;
} }
bytes memory transaction = abi.encode( bytes32 transactionHash = keccak256(
msg.sender, abi.encode(
_target, msg.sender,
_gasLimit, _target,
_data _gasLimit,
_data
)
); );
bytes32 transactionHash = keccak256(transaction);
bytes32 timestampAndBlockNumber; bytes32 timestampAndBlockNumber;
assembly { assembly {
timestampAndBlockNumber := or(timestamp(), shl(40, number())) timestampAndBlockNumber := timestamp()
timestampAndBlockNumber := or(timestampAndBlockNumber, shl(40, number()))
} }
queue.push2(transactionHash, timestampAndBlockNumber, bytes28(0)); queue.push2(
transactionHash,
timestampAndBlockNumber
);
(, uint32 nextQueueIndex) = _getLatestBatchContext(); uint40 queueIndex = queue.getLength() / 2;
// TODO: Evaluate if we need timestamp
emit TransactionEnqueued( emit TransactionEnqueued(
msg.sender, msg.sender,
_target, _target,
_gasLimit, _gasLimit,
_data, _data,
nextQueueIndex - 1, queueIndex - 1,
block.timestamp block.timestamp
); );
} }
...@@ -197,7 +220,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -197,7 +220,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
* @inheritdoc iOVM_CanonicalTransactionChain * @inheritdoc iOVM_CanonicalTransactionChain
*/ */
function appendQueueBatch( function appendQueueBatch(
uint _numQueuedTransactions uint256 _numQueuedTransactions
) )
override override
public public
...@@ -207,16 +230,15 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -207,16 +230,15 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
"Must append more than zero transactions." "Must append more than zero transactions."
); );
(, uint32 nextQueueIndex) = _getLatestBatchContext(); uint40 nextQueueIndex = _getNextQueueIndex();
bytes32[] memory leaves = new bytes32[](_numQueuedTransactions); bytes32[] memory leaves = new bytes32[](_numQueuedTransactions);
for (uint i = 0; i < _numQueuedTransactions; i++) { for (uint256 i = 0; i < _numQueuedTransactions; i++) {
leaves[i] = _getQueueLeafHash(nextQueueIndex); leaves[i] = _getQueueLeafHash(nextQueueIndex);
nextQueueIndex++; nextQueueIndex++;
} }
_appendBatch( _appendBatch(
Lib_MerkleRoot.getMerkleRoot(leaves), Lib_MerkleUtils.getMerkleRoot(leaves),
_numQueuedTransactions, _numQueuedTransactions,
_numQueuedTransactions _numQueuedTransactions
); );
...@@ -231,29 +253,21 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -231,29 +253,21 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
/** /**
* @inheritdoc iOVM_CanonicalTransactionChain * @inheritdoc iOVM_CanonicalTransactionChain
*/ */
function appendSequencerBatch( // USES CUSTOM ENCODING FOR EFFICIENCY PURPOSES function appendSequencerBatch()
// uint40 _shouldStartAtBatch,
// uint24 _totalElementsToAppend,
// BatchContext[] _contexts,
// bytes[] _transactionDataFields
)
override override
public public
{ {
uint40 _shouldStartAtBatch; uint40 shouldStartAtBatch;
uint24 _totalElementsToAppend; uint24 totalElementsToAppend;
uint24 _contextsLength; uint24 numContexts;
assembly { assembly {
// First 5 bytes after MethodId is _shouldStartAtBatch shouldStartAtBatch := shr(216, calldataload(4))
_shouldStartAtBatch := shr(216, calldataload(4)) totalElementsToAppend := shr(232, calldataload(9))
// Next 3 bytes is _totalElementsToAppend numContexts := shr(232, calldataload(12))
_totalElementsToAppend := shr(232, calldataload(9))
// And the last 3 bytes is the _contextsLength
_contextsLength := shr(232, calldataload(12))
} }
require( require(
_shouldStartAtBatch == getTotalBatches(), shouldStartAtBatch == getTotalBatches(),
"Actual batch start index does not match expected start index." "Actual batch start index does not match expected start index."
); );
...@@ -263,58 +277,43 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -263,58 +277,43 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
); );
require( require(
_contextsLength > 0, numContexts > 0,
"Must provide at least one batch context." "Must provide at least one batch context."
); );
require( require(
_totalElementsToAppend > 0, totalElementsToAppend > 0,
"Must append at least one element." "Must append at least one element."
); );
bytes32[] memory leaves = new bytes32[](_totalElementsToAppend); uint40 nextTransactionPtr = uint40(BATCH_CONTEXT_START_POS + BATCH_CONTEXT_SIZE * numContexts);
uint32 transactionIndex = 0; uint256 calldataSize;
uint32 numSequencerTransactionsProcessed = 0;
uint32 nextSequencerTransactionPosition = uint32(BATCH_CONTEXT_START_POS + BATCH_CONTEXT_SIZE * _contextsLength);
uint theCalldataSize;
assembly { assembly {
theCalldataSize := calldatasize() calldataSize := calldatasize()
} }
require(theCalldataSize >= nextSequencerTransactionPosition, "Not enough BatchContexts provided.");
require(
calldataSize >= nextTransactionPtr,
"Not enough BatchContexts provided."
);
(, uint32 nextQueueIndex) = _getLatestBatchContext(); bytes32[] memory leaves = new bytes32[](totalElementsToAppend);
uint32 transactionIndex = 0;
uint32 numSequencerTransactionsProcessed = 0;
uint40 nextQueueIndex = _getNextQueueIndex();
for (uint32 i = 0; i < _contextsLength; i++) { for (uint32 i = 0; i < numContexts; i++) {
BatchContext memory context = _getBatchContext(i); BatchContext memory context = _getBatchContext(i);
_validateBatchContext(context, nextQueueIndex); _validateBatchContext(context, nextQueueIndex);
for (uint32 j = 0; j < context.numSequencedTransactions; j++) { for (uint32 j = 0; j < context.numSequencedTransactions; j++) {
uint256 txDataLength; uint256 txDataLength;
assembly { assembly {
// 3 byte txDataLength txDataLength := shr(232, calldataload(nextTransactionPtr))
txDataLength := shr(232, calldataload(nextSequencerTransactionPosition))
}
bytes memory _chainElement = new bytes(BYTES_TILL_TX_DATA + txDataLength);
bytes32 leafHash;
uint _timestamp = context.timestamp;
uint _blockNumber = context.blockNumber;
assembly {
let chainElementStart := add(_chainElement, 0x20)
mstore8(chainElementStart, 1)
mstore8(add(chainElementStart, 1), 0)
mstore(add(chainElementStart, 2), _timestamp)
mstore(add(chainElementStart, 34), _blockNumber)
// Store the rest of the transaction
calldatacopy(add(chainElementStart, BYTES_TILL_TX_DATA), add(nextSequencerTransactionPosition, 3), txDataLength)
// Calculate the hash
leafHash := keccak256(chainElementStart, add(BYTES_TILL_TX_DATA, txDataLength))
} }
leaves[transactionIndex] = leafHash; leaves[transactionIndex] = _getSequencerLeafHash(context, nextTransactionPtr, txDataLength);
nextSequencerTransactionPosition += uint32(TX_DATA_HEADER_SIZE + txDataLength); nextTransactionPtr += uint40(TX_DATA_HEADER_SIZE + txDataLength);
numSequencerTransactionsProcessed++; numSequencerTransactionsProcessed++;
transactionIndex++; transactionIndex++;
} }
...@@ -327,18 +326,19 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -327,18 +326,19 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
} }
require( require(
theCalldataSize == nextSequencerTransactionPosition, calldataSize == nextTransactionPtr,
"Not all sequencer transactions were processed." "Not all sequencer transactions were processed."
); );
require( require(
transactionIndex == _totalElementsToAppend, transactionIndex == totalElementsToAppend,
"Actual transaction index does not match expected total elements to append." "Actual transaction index does not match expected total elements to append."
); );
uint256 numQueuedTransactions = _totalElementsToAppend - numSequencerTransactionsProcessed; uint40 numQueuedTransactions = totalElementsToAppend - numSequencerTransactionsProcessed;
_appendBatch( _appendBatch(
Lib_MerkleRoot.getMerkleRoot(leaves), Lib_MerkleUtils.getMerkleRoot(leaves),
_totalElementsToAppend, totalElementsToAppend,
numQueuedTransactions numQueuedTransactions
); );
...@@ -350,30 +350,38 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -350,30 +350,38 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
} }
/** /**
* Verifies whether a transaction is included in the chain. * @inheritdoc iOVM_CanonicalTransactionChain
* @return _isVerified True if the transaction exists in the CTC, false if not.
*/ */
function verifyTransaction( function verifyTransaction(
Lib_OVMCodec.Transaction memory _transaction, Lib_OVMCodec.Transaction memory _transaction,
TransactionChainElement memory _txChainElement, Lib_OVMCodec.TransactionChainElement memory _txChainElement,
Lib_OVMCodec.ChainBatchHeader memory _batchHeader,
Lib_OVMCodec.ChainInclusionProof memory _inclusionProof Lib_OVMCodec.ChainInclusionProof memory _inclusionProof
) )
override
public public
view view
returns ( returns (
bool _isVerified bool
) )
{ {
if (_txChainElement.isSequenced == true) { if (_txChainElement.isSequenced == true) {
return _verifySequencerTransaction(_transaction, _txChainElement, _inclusionProof); return _verifySequencerTransaction(
_transaction,
_txChainElement,
_batchHeader,
_inclusionProof
);
} else { } else {
return _verifyQueueTransaction(_transaction, _txChainElement.queueIndex, _inclusionProof); return _verifyQueueTransaction(
_transaction,
_txChainElement.queueIndex,
_batchHeader,
_inclusionProof
);
} }
} }
/********************** /**********************
* Internal Functions * * Internal Functions *
**********************/ **********************/
...@@ -381,34 +389,30 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -381,34 +389,30 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
/** /**
* Returns the BatchContext located at a particular index. * Returns the BatchContext located at a particular index.
* @param _index The index of the BatchContext * @param _index The index of the BatchContext
* @return _context The BatchContext at the specified index. * @return The BatchContext at the specified index.
*/ */
function _getBatchContext( function _getBatchContext(
uint _index uint256 _index
) )
internal internal
view pure
returns ( returns (
BatchContext memory _context BatchContext memory
) )
{ {
// Batch contexts always start at byte 12: uint256 contextPtr = 15 + _index * BATCH_CONTEXT_SIZE;
// 4[method_id] + 5[_shouldStartAtBatch] + 3[_totalElementsToAppend] + 3[numBatchContexts] uint256 numSequencedTransactions;
uint contextPosition = 15 + _index * BATCH_CONTEXT_SIZE; uint256 numSubsequentQueueTransactions;
uint numSequencedTransactions; uint256 ctxTimestamp;
uint numSubsequentQueueTransactions; uint256 ctxBlockNumber;
uint ctxTimestamp;
uint ctxBlockNumber;
assembly { assembly {
// 3 byte numSequencedTransactions numSequencedTransactions := shr(232, calldataload(contextPtr))
numSequencedTransactions := shr(232, calldataload(contextPosition)) numSubsequentQueueTransactions := shr(232, calldataload(add(contextPtr, 3)))
// 3 byte numSubsequentQueueTransactions ctxTimestamp := shr(216, calldataload(add(contextPtr, 6)))
numSubsequentQueueTransactions := shr(232, calldataload(add(contextPosition, 3))) ctxBlockNumber := shr(216, calldataload(add(contextPtr, 11)))
// 5 byte timestamp
ctxTimestamp := shr(216, calldataload(add(contextPosition, 6)))
// 5 byte blockNumber
ctxBlockNumber := shr(216, calldataload(add(contextPosition, 11)))
} }
return BatchContext({ return BatchContext({
numSequencedTransactions: numSequencedTransactions, numSequencedTransactions: numSequencedTransactions,
numSubsequentQueueTransactions: numSubsequentQueueTransactions, numSubsequentQueueTransactions: numSubsequentQueueTransactions,
...@@ -417,67 +421,88 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -417,67 +421,88 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
}); });
} }
/**
* Returns the index of the next element to be enqueued.
* @return Index for the next queue element.
*/
function _getNextQueueIndex()
internal
view
returns (
uint40
)
{
(, uint40 nextQueueIndex) = _getBatchExtraData();
return nextQueueIndex;
}
/** /**
* Parses the batch context from the extra data. * Parses the batch context from the extra data.
* @return _totalElements Total number of elements submitted. * @return Total number of elements submitted.
* @return _nextQueueIndex Index of the next queue element. * @return Index of the next queue element.
*/ */
function _getLatestBatchContext() function _getBatchExtraData()
internal internal
view view
returns ( returns (
uint40 _totalElements, uint40,
uint32 _nextQueueIndex uint40
) )
{ {
bytes28 extraData = batches.getExtraData(); bytes27 extraData = batches.getExtraData();
uint40 totalElements; uint40 totalElements;
uint32 nextQueueIndex; uint40 nextQueueIndex;
assembly { assembly {
totalElements := and(shr(32, extraData), 0x000000000000000000000000000000000000000000000000000000ffffffffff) extraData := shr(40, extraData)
nextQueueIndex := shr(40, and(shr(32, extraData), 0xffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000)) totalElements := and(extraData, 0x000000000000000000000000000000000000000000000000000000FFFFFFFFFF)
nextQueueIndex := shr(40, and(extraData, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000))
} }
return (totalElements, nextQueueIndex); return (
totalElements,
nextQueueIndex
);
} }
/** /**
* 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.
* @return _context Encoded batch context. * @return Encoded batch context.
*/ */
function _makeLatestBatchContext( function _makeBatchExtraData(
uint40 _totalElements, uint40 _totalElements,
uint32 _nextQueueIndex uint40 _nextQueueIndex
) )
internal internal
pure pure
returns ( returns (
bytes28 _context bytes27
) )
{ {
bytes28 totalElementsAndNextQueueIndex; bytes27 extraData;
assembly { assembly {
totalElementsAndNextQueueIndex := shl(32, or(_totalElements, shl(40, _nextQueueIndex))) extraData := _totalElements
extraData := or(extraData, shl(40, _nextQueueIndex))
extraData := shl(40, extraData)
} }
return totalElementsAndNextQueueIndex; return extraData;
} }
/** /**
* Retrieves the hash of a queue element. * Retrieves the hash of a queue element.
* @param _index Index of the queue element to retrieve a hash for. * @param _index Index of the queue element to retrieve a hash for.
* @return _queueLeafHash Hash of the queue element. * @return Hash of the queue element.
*/ */
function _getQueueLeafHash( function _getQueueLeafHash(
uint _index uint256 _index
) )
internal internal
view view
returns ( returns (
bytes32 _queueLeafHash bytes32
) )
{ {
Lib_OVMCodec.QueueElement memory element = getQueueElement(_index); Lib_OVMCodec.QueueElement memory element = getQueueElement(_index);
...@@ -489,7 +514,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -489,7 +514,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
); );
return _hashTransactionChainElement( return _hashTransactionChainElement(
TransactionChainElement({ Lib_OVMCodec.TransactionChainElement({
isSequenced: false, isSequenced: false,
queueIndex: _index, queueIndex: _index,
timestamp: 0, timestamp: 0,
...@@ -499,6 +524,86 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -499,6 +524,86 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
); );
} }
/**
* Retrieves the hash of a sequencer element.
* @param _context Batch context for the given element.
* @param _nextTransactionPtr Pointer to the next transaction in the calldata.
* @param _txDataLength Length of the transaction item.
* @return Hash of the sequencer element.
*/
function _getSequencerLeafHash(
BatchContext memory _context,
uint256 _nextTransactionPtr,
uint256 _txDataLength
)
internal
pure
returns (
bytes32
)
{
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)
mstore8(chainElementStart, 1)
mstore8(add(chainElementStart, 1), 0)
mstore(add(chainElementStart, 2), ctxTimestamp)
mstore(add(chainElementStart, 34), ctxBlockNumber)
calldatacopy(add(chainElementStart, BYTES_TILL_TX_DATA), add(_nextTransactionPtr, 3), _txDataLength)
leafHash := keccak256(chainElementStart, add(BYTES_TILL_TX_DATA, _txDataLength))
}
return leafHash;
}
/**
* Retrieves the hash of a sequencer element.
* @param _txChainElement The chain element which is hashed to calculate the leaf.
* @return Hash of the sequencer element.
*/
function _getSequencerLeafHash(
Lib_OVMCodec.TransactionChainElement memory _txChainElement
)
internal
view
returns(
bytes32
)
{
bytes memory txData = _txChainElement.txData;
uint256 txDataLength = _txChainElement.txData.length;
bytes memory chainElement = new bytes(BYTES_TILL_TX_DATA + txDataLength);
uint256 ctxTimestamp = _txChainElement.timestamp;
uint256 ctxBlockNumber = _txChainElement.blockNumber;
bytes32 leafHash;
assembly {
let chainElementStart := add(chainElement, 0x20)
mstore8(chainElementStart, 1)
mstore8(add(chainElementStart, 1), 0)
mstore(add(chainElementStart, 2), ctxTimestamp)
mstore(add(chainElementStart, 34), ctxBlockNumber)
pop(staticcall(gas(), 0x04, add(txData, 0x20), txDataLength, add(chainElementStart, 66), txDataLength))
leafHash := keccak256(chainElementStart, add(BYTES_TILL_TX_DATA, txDataLength))
}
return leafHash;
}
/** /**
* Inserts a batch into the chain of batches. * Inserts a batch into the chain of batches.
* @param _transactionRoot Root of the transaction tree for this batch. * @param _transactionRoot Root of the transaction tree for this batch.
...@@ -507,12 +612,12 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -507,12 +612,12 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
*/ */
function _appendBatch( function _appendBatch(
bytes32 _transactionRoot, bytes32 _transactionRoot,
uint _batchSize, uint256 _batchSize,
uint _numQueuedTransactions uint256 _numQueuedTransactions
) )
internal internal
{ {
(uint40 totalElements, uint32 nextQueueIndex) = _getLatestBatchContext(); (uint40 totalElements, uint40 nextQueueIndex) = _getBatchExtraData();
Lib_OVMCodec.ChainBatchHeader memory header = Lib_OVMCodec.ChainBatchHeader({ Lib_OVMCodec.ChainBatchHeader memory header = Lib_OVMCodec.ChainBatchHeader({
batchIndex: batches.getLength(), batchIndex: batches.getLength(),
...@@ -522,10 +627,10 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -522,10 +627,10 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
extraData: hex"" extraData: hex""
}); });
bytes32 batchHeaderHash = _hashBatchHeader(header); bytes32 batchHeaderHash = Lib_OVMCodec.hashBatchHeader(header);
bytes28 latestBatchContext = _makeLatestBatchContext( bytes27 latestBatchContext = _makeBatchExtraData(
totalElements + uint40(header.batchSize), totalElements + uint40(header.batchSize),
nextQueueIndex + uint32(_numQueuedTransactions) nextQueueIndex + uint40(_numQueuedTransactions)
); );
batches.push(batchHeaderHash, latestBatchContext); batches.push(batchHeaderHash, latestBatchContext);
...@@ -538,7 +643,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -538,7 +643,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
*/ */
function _validateBatchContext( function _validateBatchContext(
BatchContext memory _context, BatchContext memory _context,
uint32 _nextQueueIndex uint40 _nextQueueIndex
) )
internal internal
view view
...@@ -568,15 +673,15 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -568,15 +673,15 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
/** /**
* Hashes a transaction chain element. * Hashes a transaction chain element.
* @param _element Chain element to hash. * @param _element Chain element to hash.
* @return _hash Hash of the chain element. * @return Hash of the chain element.
*/ */
function _hashTransactionChainElement( function _hashTransactionChainElement(
TransactionChainElement memory _element Lib_OVMCodec.TransactionChainElement memory _element
) )
internal internal
pure pure
returns ( returns (
bytes32 _hash bytes32
) )
{ {
return keccak256( return keccak256(
...@@ -590,117 +695,136 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba ...@@ -590,117 +695,136 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
); );
} }
/************************************************
* Internal Functions: Transaction Verification *
************************************************/
/** /**
* Verifies a sequencer transaction, returning true if it was indeed included in the CTC * Verifies a sequencer transaction, returning true if it was indeed included in the CTC
* @param _transaction The transaction we are verifying inclusion of. * @param _transaction The transaction we are verifying inclusion of.
* @param _txChainElement The chain element that the transaction is claimed to be a part of. * @param _txChainElement The chain element that the transaction is claimed to be a part of.
* @param _batchHeader Header of the batch the transaction was included in.
* @param _inclusionProof An inclusion proof into the CTC at a particular index. * @param _inclusionProof An inclusion proof into the CTC at a particular index.
* @return _isVerified True if the transaction was included in the specified location, else false. * @return True if the transaction was included in the specified location, else false.
*/ */
function _verifySequencerTransaction( function _verifySequencerTransaction(
Lib_OVMCodec.Transaction memory _transaction, Lib_OVMCodec.Transaction memory _transaction,
TransactionChainElement memory _txChainElement, Lib_OVMCodec.TransactionChainElement memory _txChainElement,
Lib_OVMCodec.ChainBatchHeader memory _batchHeader,
Lib_OVMCodec.ChainInclusionProof memory _inclusionProof Lib_OVMCodec.ChainInclusionProof memory _inclusionProof
) )
internal internal
view view
returns ( returns (
bool _isVerified bool
) )
{ {
OVM_ExecutionManager em = OVM_ExecutionManager(resolve("OVM_ExecutionManager")); OVM_ExecutionManager ovmExecutionManager = OVM_ExecutionManager(resolve("OVM_ExecutionManager"));
uint256 gasLimit = em.getMaxTransactionGasLimit(); uint256 gasLimit = ovmExecutionManager.getMaxTransactionGasLimit();
address decompressionAddress = decompressionPrecompileAddress; bytes32 leafHash = _getSequencerLeafHash(_txChainElement);
bytes32 leafHash = _getSequencerChainElementLeafHash(_txChainElement);
require(
// TODO: Verify inclusion proof points to the computed leafHash _verifyElement(
// require(verifyElement(_inclusionProof, leafHash), "Invalid sequencer tx: inclusion proof invalid.") leafHash,
_batchHeader,
require(_transaction.blockNumber == _txChainElement.blockNumber, "Invalid sequencer tx: blockNumber."); _inclusionProof
require(_transaction.timestamp == _txChainElement.timestamp, "Invalid sequencer tx: timestamp."); ),
require(_transaction.entrypoint == decompressionAddress, "Invalid sequencer tx: entrypoint."); "Invalid Sequencer transaction inclusion proof."
require(_transaction.gasLimit == gasLimit, "Invalid sequencer tx: gasLimit."); );
require(keccak256(_transaction.data) == keccak256(_txChainElement.txData), "Invalid sequencer tx: data.");
require(_transaction.l1TxOrigin == address(0), "Invalid sequencer tx: l1TxOrigin."); require(
require(_transaction.l1QueueOrigin == Lib_OVMCodec.QueueOrigin.SEQUENCER_QUEUE, "Invalid sequencer tx: l1QueueOrigin."); _transaction.blockNumber == _txChainElement.blockNumber
&& _transaction.timestamp == _txChainElement.timestamp
&& _transaction.entrypoint == decompressionPrecompileAddress
&& _transaction.gasLimit == gasLimit
&& _transaction.l1TxOrigin == address(0)
&& _transaction.l1QueueOrigin == Lib_OVMCodec.QueueOrigin.SEQUENCER_QUEUE
&& keccak256(_transaction.data) == keccak256(_txChainElement.txData),
"Invalid Sequencer transaction."
);
return true; return true;
} }
/** /**
* Computes the sequencerChainElement leaf hash * Verifies a queue transaction, returning true if it was indeed included in the CTC
* @param _txChainElement The chain element which is hashed to calculate the leaf. * @param _transaction The transaction we are verifying inclusion of.
* @return _leafHash The hash of the chain element (using the special chain element leaf encoding). * @param _queueIndex The queueIndex of the queued transaction.
* @param _batchHeader Header of the batch the transaction was included in.
* @param _inclusionProof An inclusion proof into the CTC at a particular index (should point to queue tx).
* @return True if the transaction was included in the specified location, else false.
*/ */
function _getSequencerChainElementLeafHash( function _verifyQueueTransaction(
TransactionChainElement memory _txChainElement Lib_OVMCodec.Transaction memory _transaction,
uint256 _queueIndex,
Lib_OVMCodec.ChainBatchHeader memory _batchHeader,
Lib_OVMCodec.ChainInclusionProof memory _inclusionProof
) )
internal internal
view view
returns( returns (
bytes32 _leafHash bool
) )
{ {
bytes memory txData = _txChainElement.txData; bytes32 leafHash = _getQueueLeafHash(_inclusionProof.index);
uint256 txDataLength = _txChainElement.txData.length;
bytes memory _chainElement = new bytes(BYTES_TILL_TX_DATA + txDataLength);
bytes32 leafHash;
uint _timestamp = _txChainElement.timestamp;
uint _blockNumber = _txChainElement.blockNumber;
assembly { require(
let chainElementStart := add(_chainElement, 0x20) _verifyElement(
mstore8(chainElementStart, 1) leafHash,
mstore8(add(chainElementStart, 1), 0) _batchHeader,
mstore(add(chainElementStart, 2), _timestamp) _inclusionProof
mstore(add(chainElementStart, 34), _blockNumber) ),
"Invalid Queue transaction inclusion proof."
);
// Copy the txData into memory using identity precompile bytes32 transactionHash = keccak256(
pop(staticcall(gas(), 0x04, add(txData, 0x20), txDataLength, add(chainElementStart, 66), txDataLength)) abi.encode(
_transaction.l1TxOrigin,
_transaction.entrypoint,
_transaction.gasLimit,
_transaction.data
)
);
// Calculate the hash Lib_OVMCodec.QueueElement memory el = getQueueElement(_queueIndex);
leafHash := keccak256(chainElementStart, add(BYTES_TILL_TX_DATA, txDataLength)) require(
} el.queueRoot == transactionHash
return _leafHash; && el.timestamp == _transaction.timestamp
&& el.blockNumber == _transaction.blockNumber,
"Invalid Queue transaction."
);
return true;
} }
/** /**
* Verifies a queue transaction, returning true if it was indeed included in the CTC * Verifies a batch inclusion proof.
* @param _transaction The transaction we are verifying inclusion of. * @param _element Hash of the element to verify a proof for.
* @param _queueIndex The queueIndex of the queued transaction. * @param _batchHeader Header of the batch in which the element was included.
* @param _inclusionProof An inclusion proof into the CTC at a particular index (should point to queue tx). * @param _proof Merkle inclusion proof for the element.
* @return _isVerified True if the transaction was included in the specified location, else false.
*/ */
function _verifyQueueTransaction( function _verifyElement(
Lib_OVMCodec.Transaction memory _transaction, bytes32 _element,
uint256 _queueIndex, Lib_OVMCodec.ChainBatchHeader memory _batchHeader,
Lib_OVMCodec.ChainInclusionProof memory _inclusionProof Lib_OVMCodec.ChainInclusionProof memory _proof
) )
internal internal
view view
returns ( returns (
bool _isVerified bool
) )
{ {
bytes32 leafHash = _getQueueLeafHash(_inclusionProof.index); require(
// TODO: Verify inclusion proof Lib_OVMCodec.hashBatchHeader(_batchHeader) == batches.get(uint32(_batchHeader.batchIndex)),
// require(verifyElement(_inclusionProof, leafHash), "Invalid queue tx: inclusion proof invalid.") "Invalid batch header."
);
Lib_OVMCodec.QueueElement memory ele = getQueueElement(_queueIndex);
bytes memory txEncoded = abi.encode( require(
_transaction.l1TxOrigin, Lib_MerkleUtils.verify(
_transaction.entrypoint, _batchHeader.batchRoot,
_transaction.gasLimit, _element,
_transaction.data _proof.index,
); _proof.siblings
bytes32 transactionHash = keccak256(txEncoded); ),
require(ele.queueRoot == transactionHash, "Invalid queue tx: transaction hash does not match."); "Invalid inclusion proof."
require(ele.timestamp == _transaction.timestamp, "Invalid queue tx: timestamp does not match."); );
require(ele.blockNumber == _transaction.blockNumber, "Invalid queue tx: blockNumber does not match.");
return true; return true;
} }
} }
...@@ -5,19 +5,20 @@ pragma experimental ABIEncoderV2; ...@@ -5,19 +5,20 @@ 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_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";
/* Contract Imports */
import { OVM_BaseChain } from "./OVM_BaseChain.sol";
/** /**
* @title OVM_StateCommitmentChain * @title OVM_StateCommitmentChain
*/ */
contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, Lib_AddressResolver { contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, iRingBufferOverwriter, Lib_AddressResolver {
using Lib_RingBuffer for Lib_RingBuffer.RingBuffer;
/************* /*************
* Constants * * Constants *
...@@ -25,11 +26,14 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, L ...@@ -25,11 +26,14 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, L
uint256 constant public FRAUD_PROOF_WINDOW = 7 days; uint256 constant public FRAUD_PROOF_WINDOW = 7 days;
/*******************************************
* Contract Variables: Contract References *
*******************************************/
/*************
* Variables *
*************/
uint256 internal lastDeletableIndex;
uint256 internal lastDeletableTimestamp;
Lib_RingBuffer.RingBuffer internal batches;
iOVM_CanonicalTransactionChain internal ovmCanonicalTransactionChain; iOVM_CanonicalTransactionChain internal ovmCanonicalTransactionChain;
iOVM_FraudVerifier internal ovmFraudVerifier; iOVM_FraudVerifier internal ovmFraudVerifier;
...@@ -48,16 +52,50 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, L ...@@ -48,16 +52,50 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, L
{ {
ovmCanonicalTransactionChain = iOVM_CanonicalTransactionChain(resolve("OVM_CanonicalTransactionChain")); ovmCanonicalTransactionChain = iOVM_CanonicalTransactionChain(resolve("OVM_CanonicalTransactionChain"));
ovmFraudVerifier = iOVM_FraudVerifier(resolve("OVM_FraudVerifier")); ovmFraudVerifier = iOVM_FraudVerifier(resolve("OVM_FraudVerifier"));
batches.init(
16,
Lib_OVMCodec.RING_BUFFER_SCC_BATCHES,
iRingBufferOverwriter(address(this))
);
} }
/**************************************** /********************
* Public Functions: Batch Manipulation * * Public Functions *
****************************************/ ********************/
/** /**
* Appends a batch of state roots to the chain. * @inheritdoc iOVM_StateCommitmentChain
* @param _batch Batch of state roots. */
function getTotalElements()
virtual
override
public
view
returns (
uint256 _totalElements
)
{
return uint256(uint216(batches.getExtraData()));
}
/**
* @inheritdoc iOVM_StateCommitmentChain
*/
function getTotalBatches()
override
public
view
returns (
uint256 _totalBatches
)
{
return uint256(batches.getLength());
}
/**
* @inheritdoc iOVM_StateCommitmentChain
*/ */
function appendStateBatch( function appendStateBatch(
bytes32[] memory _batch bytes32[] memory _batch
...@@ -89,8 +127,7 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, L ...@@ -89,8 +127,7 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, L
} }
/** /**
* Deletes all state roots after (and including) a given batch. * @inheritdoc iOVM_StateCommitmentChain
* @param _batchHeader Header of the batch to start deleting from.
*/ */
function deleteStateBatch( function deleteStateBatch(
Lib_OVMCodec.ChainBatchHeader memory _batchHeader Lib_OVMCodec.ChainBatchHeader memory _batchHeader
...@@ -111,11 +148,42 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, L ...@@ -111,11 +148,42 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, L
_deleteBatch(_batchHeader); _deleteBatch(_batchHeader);
} }
/**
* @inheritdoc iOVM_StateCommitmentChain
*/
function verifyStateCommitment(
bytes32 _element,
Lib_OVMCodec.ChainBatchHeader memory _batchHeader,
Lib_OVMCodec.ChainInclusionProof memory _proof
)
override
public
view
returns (
bool
)
{
require(
Lib_OVMCodec.hashBatchHeader(_batchHeader) == batches.get(uint32(_batchHeader.batchIndex)),
"Invalid batch header."
);
require(
Lib_MerkleUtils.verify(
_batchHeader.batchRoot,
_element,
_proof.index,
_proof.siblings
),
"Invalid inclusion proof."
);
/********************************** return true;
* Public Functions: Batch Status * }
**********************************/
/**
* @inheritdoc iOVM_StateCommitmentChain
*/
function insideFraudProofWindow( function insideFraudProofWindow(
Lib_OVMCodec.ChainBatchHeader memory _batchHeader Lib_OVMCodec.ChainBatchHeader memory _batchHeader
) )
...@@ -138,4 +206,134 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, L ...@@ -138,4 +206,134 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, L
return timestamp + FRAUD_PROOF_WINDOW > block.timestamp; return timestamp + FRAUD_PROOF_WINDOW > block.timestamp;
} }
/**
* @inheritdoc iOVM_StateCommitmentChain
*/
function setLastDeletableIndex(
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(
Lib_OVMCodec.hashBatchHeader(_stateBatchHeader) == batches.get(uint32(_stateBatchHeader.batchIndex)),
"Invalid batch header."
);
require(
insideFraudProofWindow(_stateBatchHeader) == false,
"Batch header must be outside of fraud proof window to be deletable."
);
require(
_stateBatchHeader.batchIndex > lastDeletableIndex,
"Batch index must be greater than last deletable index."
);
require(
ovmCanonicalTransactionChain.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 ovmCanonicalTransactionChain.getQueueElement(_index / 2).timestamp < lastDeletableTimestamp;
} else {
return _index < lastDeletableIndex;
}
}
/**********************
* Internal Functions *
**********************/
/**
* Appends a batch to the chain.
* @param _batchHeader Batch header to append.
*/
function _appendBatch(
Lib_OVMCodec.ChainBatchHeader memory _batchHeader
)
internal
{
batches.push(
Lib_OVMCodec.hashBatchHeader(_batchHeader),
bytes27(uint216(getTotalElements() + _batchHeader.batchSize))
);
}
/**
* Appends a batch to the chain.
* @param _elements Elements within the batch.
* @param _extraData Any extra data to append to the batch.
*/
function _appendBatch(
bytes[] memory _elements,
bytes memory _extraData
)
internal
{
Lib_OVMCodec.ChainBatchHeader memory batchHeader = Lib_OVMCodec.ChainBatchHeader({
batchIndex: uint256(batches.getLength()),
batchRoot: Lib_MerkleUtils.getMerkleRoot(_elements),
batchSize: _elements.length,
prevTotalElements: getTotalElements(),
extraData: _extraData
});
_appendBatch(batchHeader);
}
/**
* Removes a batch from the chain.
* @param _batchHeader Header of the batch to remove.
*/
function _deleteBatch(
Lib_OVMCodec.ChainBatchHeader memory _batchHeader
)
internal
{
require(
_batchHeader.batchIndex < batches.getLength(),
"Invalid batch index."
);
require(
Lib_OVMCodec.hashBatchHeader(_batchHeader) == batches.get(uint32(_batchHeader.batchIndex)),
"Invalid batch header."
);
batches.deleteElementsAfterInclusive(
uint40(_batchHeader.batchIndex),
bytes27(uint216(_batchHeader.prevTotalElements))
);
}
} }
...@@ -82,6 +82,7 @@ contract OVM_FraudVerifier is iOVM_FraudVerifier, Lib_AddressResolver { ...@@ -82,6 +82,7 @@ contract OVM_FraudVerifier is iOVM_FraudVerifier, Lib_AddressResolver {
* @param _preStateRootBatchHeader Batch header for the provided pre-state root. * @param _preStateRootBatchHeader Batch header for the provided pre-state root.
* @param _preStateRootProof Inclusion proof for the provided pre-state root. * @param _preStateRootProof Inclusion proof for the provided pre-state root.
* @param _transaction OVM transaction claimed to be fraudulent. * @param _transaction OVM transaction claimed to be fraudulent.
* @param _txChainElement OVM transaction chain element.
* @param _transactionBatchHeader Batch header for the provided transaction. * @param _transactionBatchHeader Batch header for the provided transaction.
* @param _transactionProof Inclusion proof for the provided transaction. * @param _transactionProof Inclusion proof for the provided transaction.
*/ */
...@@ -90,6 +91,7 @@ contract OVM_FraudVerifier is iOVM_FraudVerifier, Lib_AddressResolver { ...@@ -90,6 +91,7 @@ contract OVM_FraudVerifier is iOVM_FraudVerifier, Lib_AddressResolver {
Lib_OVMCodec.ChainBatchHeader memory _preStateRootBatchHeader, Lib_OVMCodec.ChainBatchHeader memory _preStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _preStateRootProof, Lib_OVMCodec.ChainInclusionProof memory _preStateRootProof,
Lib_OVMCodec.Transaction memory _transaction, Lib_OVMCodec.Transaction memory _transaction,
Lib_OVMCodec.TransactionChainElement memory _txChainElement,
Lib_OVMCodec.ChainBatchHeader memory _transactionBatchHeader, Lib_OVMCodec.ChainBatchHeader memory _transactionBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _transactionProof Lib_OVMCodec.ChainInclusionProof memory _transactionProof
) )
...@@ -101,7 +103,7 @@ contract OVM_FraudVerifier is iOVM_FraudVerifier, Lib_AddressResolver { ...@@ -101,7 +103,7 @@ contract OVM_FraudVerifier is iOVM_FraudVerifier, Lib_AddressResolver {
} }
require( require(
_verifyStateRoot( ovmStateCommitmentChain.verifyStateCommitment(
_preStateRoot, _preStateRoot,
_preStateRootBatchHeader, _preStateRootBatchHeader,
_preStateRootProof _preStateRootProof
...@@ -110,8 +112,9 @@ contract OVM_FraudVerifier is iOVM_FraudVerifier, Lib_AddressResolver { ...@@ -110,8 +112,9 @@ contract OVM_FraudVerifier is iOVM_FraudVerifier, Lib_AddressResolver {
); );
require( require(
_verifyTransaction( ovmCanonicalTransactionChain.verifyTransaction(
_transaction, _transaction,
_txChainElement,
_transactionBatchHeader, _transactionBatchHeader,
_transactionProof _transactionProof
), ),
...@@ -161,7 +164,7 @@ contract OVM_FraudVerifier is iOVM_FraudVerifier, Lib_AddressResolver { ...@@ -161,7 +164,7 @@ contract OVM_FraudVerifier is iOVM_FraudVerifier, Lib_AddressResolver {
); );
require( require(
_verifyStateRoot( ovmStateCommitmentChain.verifyStateCommitment(
_preStateRoot, _preStateRoot,
_preStateRootBatchHeader, _preStateRootBatchHeader,
_preStateRootProof _preStateRootProof
...@@ -170,7 +173,7 @@ contract OVM_FraudVerifier is iOVM_FraudVerifier, Lib_AddressResolver { ...@@ -170,7 +173,7 @@ contract OVM_FraudVerifier is iOVM_FraudVerifier, Lib_AddressResolver {
); );
require( require(
_verifyStateRoot( ovmStateCommitmentChain.verifyStateCommitment(
_postStateRoot, _postStateRoot,
_postStateRootBatchHeader, _postStateRootBatchHeader,
_postStateRootProof _postStateRootProof
...@@ -209,54 +212,4 @@ contract OVM_FraudVerifier is iOVM_FraudVerifier, Lib_AddressResolver { ...@@ -209,54 +212,4 @@ contract OVM_FraudVerifier is iOVM_FraudVerifier, Lib_AddressResolver {
{ {
return address(transitioners[_preStateRoot]) != address(0); return address(transitioners[_preStateRoot]) != address(0);
} }
/**
* Verifies inclusion of a state root.
* @param _stateRoot State root to verify
* @param _stateRootBatchHeader Batch header for the provided state root.
* @param _stateRootProof Inclusion proof for the provided state root.
* @return _verified Whether or not the root was included.
*/
function _verifyStateRoot(
bytes32 _stateRoot,
Lib_OVMCodec.ChainBatchHeader memory _stateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _stateRootProof
)
internal
view
returns (
bool _verified
)
{
return ovmStateCommitmentChain.verifyElement(
abi.encodePacked(_stateRoot),
_stateRootBatchHeader,
_stateRootProof
);
}
/**
* Verifies inclusion of a given transaction.
* @param _transaction OVM transaction to verify.
* @param _transactionBatchHeader Batch header for the provided transaction.
* @param _transactionProof Inclusion proof for the provided transaction.
* @return _verified Whether or not the transaction was included.
*/
function _verifyTransaction(
Lib_OVMCodec.Transaction memory _transaction,
Lib_OVMCodec.ChainBatchHeader memory _transactionBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _transactionProof
)
internal
view
returns (
bool _verified
)
{
return ovmCanonicalTransactionChain.verifyElement(
Lib_OVMCodec.encodeTransaction(_transaction),
_transactionBatchHeader,
_transactionProof
);
}
} }
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/**
* @title iOVM_BaseChain
*/
interface iOVM_BaseChain {
/*************************************
* Public Functions: Batch Retrieval *
*************************************/
function getTotalElements() external view returns (uint256 _totalElements);
function getTotalBatches() external view returns (uint256 _totalBatches);
/****************************************
* Public Functions: Batch Verification *
****************************************/
function verifyElement(
bytes calldata _element,
Lib_OVMCodec.ChainBatchHeader calldata _batchHeader,
Lib_OVMCodec.ChainInclusionProof calldata _proof
) external view returns (bool _verified);
}
...@@ -5,13 +5,10 @@ pragma experimental ABIEncoderV2; ...@@ -5,13 +5,10 @@ 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";
/* Interface Imports */
import { iOVM_BaseChain } from "./iOVM_BaseChain.sol";
/** /**
* @title iOVM_CanonicalTransactionChain * @title iOVM_CanonicalTransactionChain
*/ */
interface iOVM_CanonicalTransactionChain is iOVM_BaseChain { interface iOVM_CanonicalTransactionChain {
/********** /**********
* Events * * Events *
...@@ -50,19 +47,33 @@ interface iOVM_CanonicalTransactionChain is iOVM_BaseChain { ...@@ -50,19 +47,33 @@ interface iOVM_CanonicalTransactionChain is iOVM_BaseChain {
uint256 blockNumber; uint256 blockNumber;
} }
struct TransactionChainElement {
bool isSequenced;
uint256 queueIndex; // QUEUED TX ONLY
uint256 timestamp; // SEQUENCER TX ONLY
uint256 blockNumber; // SEQUENCER TX ONLY
bytes txData; // SEQUENCER TX ONLY
}
/******************** /********************
* Public Functions * * Public Functions *
********************/ ********************/
/**
* Retrieves the total number of elements submitted.
* @return _totalElements Total submitted elements.
*/
function getTotalElements()
external
view
returns (
uint256 _totalElements
);
/**
* Retrieves the total number of batches submitted.
* @return _totalBatches Total submitted batches.
*/
function getTotalBatches()
external
view
returns (
uint256 _totalBatches
);
/** /**
* 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.
...@@ -87,7 +98,8 @@ interface iOVM_CanonicalTransactionChain is iOVM_BaseChain { ...@@ -87,7 +98,8 @@ interface iOVM_CanonicalTransactionChain is iOVM_BaseChain {
address _target, address _target,
uint256 _gasLimit, uint256 _gasLimit,
bytes memory _data bytes memory _data
) external; )
external;
/** /**
* Appends a given number of queued transactions as a single batch. * Appends a given number of queued transactions as a single batch.
...@@ -95,19 +107,42 @@ interface iOVM_CanonicalTransactionChain is iOVM_BaseChain { ...@@ -95,19 +107,42 @@ interface iOVM_CanonicalTransactionChain is iOVM_BaseChain {
*/ */
function appendQueueBatch( function appendQueueBatch(
uint256 _numQueuedTransactions uint256 _numQueuedTransactions
) external; )
external;
/** /**
* Allows the sequencer to append a batch of transactions. * Allows the sequencer to append a batch of transactions.
* param _shouldStartAtBatch Specific batch we expect to start appending to. * @dev This function uses a custom encoding scheme for efficiency reasons.
* param _totalElementsToAppend Total number of batch elements we expect to append. * .param _shouldStartAtBatch Specific batch we expect to start appending to.
* param _contexts Array of batch contexts. * .param _totalElementsToAppend Total number of batch elements we expect to append.
* param _transactionDataFields Array of raw transaction data. * .param _contexts Array of batch contexts.
* .param _transactionDataFields Array of raw transaction data.
*/ */
function appendSequencerBatch( // USES CUSTOM ENCODING FOR EFFICIENCY PURPOSES function appendSequencerBatch(
// uint40 _shouldStartAtBatch, // uint40 _shouldStartAtBatch,
// uint24 _totalElementsToAppend, // uint24 _totalElementsToAppend,
// BatchContext[] _contexts, // BatchContext[] _contexts,
// bytes[] _transactionDataFields // bytes[] _transactionDataFields
) external; )
external;
/**
* Verifies whether a transaction is included in the chain.
* @param _transaction Transaction to verify.
* @param _txChainElement Transaction chain element corresponding to the transaction.
* @param _batchHeader Header of the batch the transaction was included in.
* @param _inclusionProof Inclusion proof for the provided transaction chain element.
* @return True if the transaction exists in the CTC, false if not.
*/
function verifyTransaction(
Lib_OVMCodec.Transaction memory _transaction,
Lib_OVMCodec.TransactionChainElement memory _txChainElement,
Lib_OVMCodec.ChainBatchHeader memory _batchHeader,
Lib_OVMCodec.ChainInclusionProof memory _inclusionProof
)
external
view
returns (
bool
);
} }
...@@ -2,27 +2,103 @@ ...@@ -2,27 +2,103 @@
pragma solidity ^0.7.0; pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_BaseChain } from "./iOVM_BaseChain.sol";
/* Library Imports */ /* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol"; import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/** /**
* @title iOVM_StateCommitmentChain * @title iOVM_StateCommitmentChain
*/ */
interface iOVM_StateCommitmentChain is iOVM_BaseChain { interface iOVM_StateCommitmentChain {
/********************
* Public Functions *
********************/
/**
* Retrieves the total number of elements submitted.
* @return _totalElements Total submitted elements.
*/
function getTotalElements()
external
view
returns (
uint256 _totalElements
);
/**
* Retrieves the total number of batches submitted.
* @return _totalBatches Total submitted batches.
*/
function getTotalBatches()
external
view
returns (
uint256 _totalBatches
);
/**************************************** /**
* Public Functions: Batch Manipulation * * Appends a batch of state roots to the chain.
****************************************/ * @param _batch Batch of state roots.
*/
function appendStateBatch(
bytes32[] calldata _batch
)
external;
function appendStateBatch(bytes32[] calldata _batch) external; /**
function deleteStateBatch(Lib_OVMCodec.ChainBatchHeader memory _batchHeader) external; * Deletes all state roots after (and including) a given batch.
* @param _batchHeader Header of the batch to start deleting from.
*/
function deleteStateBatch(
Lib_OVMCodec.ChainBatchHeader memory _batchHeader
)
external;
/********************************** /**
* Public Functions: Batch Status * * Verifies a batch inclusion proof.
**********************************/ * @param _element Hash of the element to verify a proof for.
* @param _batchHeader Header of the batch in which the element was included.
* @param _proof Merkle inclusion proof for the element.
*/
function verifyStateCommitment(
bytes32 _element,
Lib_OVMCodec.ChainBatchHeader memory _batchHeader,
Lib_OVMCodec.ChainInclusionProof memory _proof
)
external
view
returns (
bool _verified
);
function insideFraudProofWindow(Lib_OVMCodec.ChainBatchHeader memory _batchHeader) external view returns (bool _inside); /**
* Checks whether a given batch is still inside its fraud proof window.
* @param _batchHeader Header of the batch to check.
* @return _inside Whether or not the batch is inside the fraud proof window.
*/
function insideFraudProofWindow(
Lib_OVMCodec.ChainBatchHeader memory _batchHeader
)
external
view
returns (
bool _inside
);
/**
* Sets the last batch index that can be deleted.
* @param _stateBatchHeader Proposed batch header that can be deleted.
* @param _transaction Transaction to verify.
* @param _txChainElement Transaction chain element corresponding to the transaction.
* @param _txBatchHeader Header of the batch the transaction was included in.
* @param _txInclusionProof Inclusion proof for the provided transaction chain element.
*/
function setLastDeletableIndex(
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;
} }
...@@ -29,6 +29,7 @@ interface iOVM_FraudVerifier { ...@@ -29,6 +29,7 @@ interface iOVM_FraudVerifier {
Lib_OVMCodec.ChainBatchHeader calldata _preStateRootBatchHeader, Lib_OVMCodec.ChainBatchHeader calldata _preStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof calldata _preStateRootProof, Lib_OVMCodec.ChainInclusionProof calldata _preStateRootProof,
Lib_OVMCodec.Transaction calldata _transaction, Lib_OVMCodec.Transaction calldata _transaction,
Lib_OVMCodec.TransactionChainElement calldata _txChainElement,
Lib_OVMCodec.ChainBatchHeader calldata _transactionBatchHeader, Lib_OVMCodec.ChainBatchHeader calldata _transactionBatchHeader,
Lib_OVMCodec.ChainInclusionProof calldata _transactionProof Lib_OVMCodec.ChainInclusionProof calldata _transactionProof
) external; ) external;
......
...@@ -21,6 +21,11 @@ library Lib_OVMCodec { ...@@ -21,6 +21,11 @@ library Lib_OVMCodec {
bytes32 constant internal KECCAK256_RLP_NULL_BYTES = keccak256(RLP_NULL_BYTES); bytes32 constant internal KECCAK256_RLP_NULL_BYTES = keccak256(RLP_NULL_BYTES);
bytes32 constant internal KECCAK256_NULL_BYTES = keccak256(NULL_BYTES); bytes32 constant internal KECCAK256_NULL_BYTES = keccak256(NULL_BYTES);
// Ring buffer IDs
bytes32 constant internal RING_BUFFER_SCC_BATCHES = keccak256("RING_BUFFER_SCC_BATCHES");
bytes32 constant internal RING_BUFFER_CTC_BATCHES = keccak256("RING_BUFFER_CTC_BATCHES");
bytes32 constant internal RING_BUFFER_CTC_QUEUE = keccak256("RING_BUFFER_CTC_QUEUE");
/********* /*********
* Enums * * Enums *
...@@ -80,10 +85,18 @@ library Lib_OVMCodec { ...@@ -80,10 +85,18 @@ library Lib_OVMCodec {
bytes data; bytes data;
} }
struct TransactionChainElement {
bool isSequenced;
uint256 queueIndex; // QUEUED TX ONLY
uint256 timestamp; // SEQUENCER TX ONLY
uint256 blockNumber; // SEQUENCER TX ONLY
bytes txData; // SEQUENCER TX ONLY
}
struct QueueElement { struct QueueElement {
bytes32 queueRoot; bytes32 queueRoot;
uint40 timestamp; uint40 timestamp;
uint32 blockNumber; uint40 blockNumber;
} }
struct EOATransaction { struct EOATransaction {
...@@ -236,4 +249,28 @@ library Lib_OVMCodec { ...@@ -236,4 +249,28 @@ library Lib_OVMCodec {
codeHash: Lib_RLPReader.readBytes32(accountState[3]) codeHash: Lib_RLPReader.readBytes32(accountState[3])
}); });
} }
/**
* Calculates a hash for a given batch header.
* @param _batchHeader Header to hash.
* @return _hash Hash of the header.
*/
function hashBatchHeader(
Lib_OVMCodec.ChainBatchHeader memory _batchHeader
)
internal
pure
returns (
bytes32 _hash
)
{
return keccak256(
abi.encode(
_batchHeader.batchRoot,
_batchHeader.batchSize,
_batchHeader.prevTotalElements,
_batchHeader.extraData
)
);
}
} }
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
// author: cobaltedge
library Lib_MerkleRoot {
function getMerkleRoot(bytes32[] memory elements)
internal view returns (bytes32) {
// compute tree depth
uint pow2 = 1;
uint depth = 0;
while (pow2 < elements.length) {
pow2 <<= 1;
depth++;
}
bytes memory buf = new bytes(64);
bytes32 left; bytes32 right;
for (uint i = 0; i < elements.length / 2; i++) {
left = elements[2 * i ];
right = elements[2 * i + 1];
assembly {
mstore(add(buf, 32), left)
mstore(add(buf, 64), right)
}
elements[i] = keccak256(buf);
}
for (uint i = elements.length; i < pow2 >> 1; i++) {
elements[i] = 0x0000000000000000000000000000000000000000000000000000000000000000;
}
uint diff = (pow2 - elements.length) / 2;
uint pow2_ = pow2 >> 1;
for (uint d = 2; d <= depth; d++) {
pow2_ >>= 1;
diff /= 2;
uint midpoint = pow2_ - diff;
for (uint i = 0; i < midpoint; i++) {
left = elements[2 * i ];
right = elements[2 * i + 1];
assembly {
mstore(add(buf, 32), left)
mstore(add(buf, 64), right)
}
elements[i] = keccak256(buf);
}
for (uint i = midpoint; i < pow2_; i++) {
elements[i] = 0x0000000000000000000000000000000000000000000000000000000000000000;
}
}
return elements[0];
}
}
\ No newline at end of file
...@@ -82,7 +82,7 @@ library Lib_MerkleUtils { ...@@ -82,7 +82,7 @@ library Lib_MerkleUtils {
function verify( function verify(
bytes32 _root, bytes32 _root,
bytes memory _leaf, bytes32 _leaf,
uint256 _path, uint256 _path,
bytes32[] memory _siblings bytes32[] memory _siblings
) )
...@@ -92,7 +92,7 @@ library Lib_MerkleUtils { ...@@ -92,7 +92,7 @@ library Lib_MerkleUtils {
bool _verified bool _verified
) )
{ {
bytes32 computedRoot = keccak256(_leaf); bytes32 computedRoot = _leaf;
for (uint256 i = 0; i < _siblings.length; i++) { for (uint256 i = 0; i < _siblings.length; i++) {
bytes32 sibling = _siblings[i]; bytes32 sibling = _siblings[i];
...@@ -108,6 +108,26 @@ library Lib_MerkleUtils { ...@@ -108,6 +108,26 @@ library Lib_MerkleUtils {
return computedRoot == _root; 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( function _getDefaultHashes(
uint256 _length uint256 _length
) )
......
pragma solidity ^0.7.0;
/* Logging Imports */
import { console } from "@nomiclabs/buidler/console.sol";
interface iRingBufferOverwriter {
function canOverwrite(bytes32 _id, uint256 _index) external returns (bool);
}
library Lib_RingBuffer {
using Lib_RingBuffer for RingBuffer;
/***********
* Structs *
***********/
struct Buffer {
uint256 length;
mapping (uint256 => bytes32) buf;
}
struct RingBuffer {
bytes32 id;
iRingBufferOverwriter overwriter;
bytes32 contextA;
bytes32 contextB;
Buffer bufferA;
Buffer bufferB;
}
struct RingBufferContext {
// contextA
uint40 globalIndex;
bytes27 extraData;
// contextB
uint64 currBufferIndex;
uint40 prevResetIndex;
uint40 currResetIndex;
}
/*************
* Constants *
*************/
uint256 constant MIN_CAPACITY = 16;
/**********************
* Internal Functions *
**********************/
/**
* Utility for initializing a ring buffer object.
* @param _self Buffer to access.
* @param _initialBufferSize Initial length of both sub-buffers.
* @param _overwriter Address of the overwriter contract.
*/
function init(
RingBuffer storage _self,
uint256 _initialBufferSize,
bytes32 _id,
iRingBufferOverwriter _overwriter
)
internal
{
_self.bufferA.length = _initialBufferSize;
_self.bufferB.length = _initialBufferSize;
_self.id = _id;
_self.overwriter = _overwriter;
}
/**
* Pushes a single element to the buffer.
* @param _self Buffer to access.
* @param _value Value to push to the buffer.
* @param _extraData Optional global extra data.
*/
function push(
RingBuffer storage _self,
bytes32 _value,
bytes27 _extraData
)
internal
{
RingBufferContext memory ctx = _self.getContext();
Buffer storage currBuffer = _self.getBuffer(ctx.currBufferIndex);
// Set a minimum capacity.
if (currBuffer.length == 0) {
currBuffer.length = MIN_CAPACITY;
}
// Check if we need to expand the buffer.
if (ctx.globalIndex - ctx.currResetIndex >= currBuffer.length) {
bool canOverwrite;
try _self.overwriter.canOverwrite(_self.id, ctx.currResetIndex) returns (bool _canOverwrite) {
canOverwrite = _canOverwrite;
} catch {
// Just in case canOverwrite is broken.
canOverwrite = false;
}
if (canOverwrite) {
// We're going to overwrite the inactive buffer.
// Bump the buffer index, reset the delete offset, and set our reset indices.
ctx.currBufferIndex++;
ctx.prevResetIndex = ctx.currResetIndex;
ctx.currResetIndex = ctx.globalIndex;
// Swap over to the next buffer.
currBuffer = _self.getBuffer(ctx.currBufferIndex);
} else {
// We're not overwriting yet, double the length of the current buffer.
currBuffer.length *= 2;
}
}
// Index to write to is the difference of the global and reset indices.
uint256 writeHead = ctx.globalIndex - ctx.currResetIndex;
currBuffer.buf[writeHead] = _value;
// Bump the global index and insert our extra data, then save the context.
ctx.globalIndex++;
ctx.extraData = _extraData;
_self.setContext(ctx);
}
/**
* Pushes a single element to the buffer.
* @param _self Buffer to access.
* @param _value Value to push to the buffer.
*/
function push(
RingBuffer storage _self,
bytes32 _value
)
internal
{
RingBufferContext memory ctx = _self.getContext();
_self.push(
_value,
ctx.extraData
);
}
/**
* Pushes a two elements to the buffer.
* @param _self Buffer to access.
* @param _valueA First value to push to the buffer.
* @param _valueA Second value to push to the buffer.
* @param _extraData Optional global extra data.
*/
function push2(
RingBuffer storage _self,
bytes32 _valueA,
bytes32 _valueB,
bytes27 _extraData
)
internal
{
_self.push(_valueA, _extraData);
_self.push(_valueB, _extraData);
}
/**
* Pushes a two elements to the buffer.
* @param _self Buffer to access.
* @param _valueA First value to push to the buffer.
* @param _valueA Second value to push to the buffer.
*/
function push2(
RingBuffer storage _self,
bytes32 _valueA,
bytes32 _valueB
)
internal
{
RingBufferContext memory ctx = _self.getContext();
_self.push2(
_valueA,
_valueB,
ctx.extraData
);
}
/**
* Retrieves an element from the buffer.
* @param _self Buffer to access.
* @param _index Element index to retrieve.
* @return Value of the element at the given index.
*/
function get(
RingBuffer storage _self,
uint256 _index
)
internal
view
returns (
bytes32
)
{
RingBufferContext memory ctx = _self.getContext();
require(
_index < ctx.globalIndex,
"Index out of bounds."
);
Buffer storage currBuffer = _self.getBuffer(ctx.currBufferIndex);
Buffer storage prevBuffer = _self.getBuffer(ctx.currBufferIndex + 1);
if (_index >= ctx.currResetIndex) {
// We're trying to load an element from the current buffer.
// Relative index is just the difference from the reset index.
uint256 relativeIndex = _index - ctx.currResetIndex;
// Shouldn't happen but why not check.
require(
relativeIndex < currBuffer.length,
"Index out of bounds."
);
return currBuffer.buf[relativeIndex];
} else {
// We're trying to load an element from the previous buffer.
// Relative index is the difference from the reset index in the other direction.
uint256 relativeIndex = ctx.currResetIndex - _index;
// Condition only fails in the case that we deleted and flipped buffers.
require(
ctx.currResetIndex > ctx.prevResetIndex,
"Index out of bounds."
);
// Make sure we're not trying to read beyond the array.
require(
relativeIndex <= prevBuffer.length,
"Index out of bounds."
);
return prevBuffer.buf[prevBuffer.length - relativeIndex];
}
}
/**
* Deletes all elements after (and including) a given index.
* @param _self Buffer to access.
* @param _index Index of the element to delete from (inclusive).
* @param _extraData Optional global extra data.
*/
function deleteElementsAfterInclusive(
RingBuffer storage _self,
uint40 _index,
bytes27 _extraData
)
internal
{
RingBufferContext memory ctx = _self.getContext();
require(
_index < ctx.globalIndex && _index >= ctx.prevResetIndex,
"Index out of bounds."
);
Buffer storage currBuffer = _self.getBuffer(ctx.currBufferIndex);
Buffer storage prevBuffer = _self.getBuffer(ctx.currBufferIndex + 1);
if (_index < ctx.currResetIndex) {
// We're switching back to the previous buffer.
// Reduce the buffer index, set the current reset index back to match the previous one.
// We use the equality of these two values to prevent reading beyond this buffer.
ctx.currBufferIndex--;
ctx.currResetIndex = ctx.prevResetIndex;
}
// Set our global index and extra data, save the context.
ctx.globalIndex = _index;
ctx.extraData = _extraData;
_self.setContext(ctx);
}
/**
* Retrieves the current global index.
* @param _self Buffer to access.
* @return Current global index.
*/
function getLength(
RingBuffer storage _self
)
internal
view
returns (
uint40
)
{
RingBufferContext memory ctx = _self.getContext();
return ctx.globalIndex;
}
/**
* Retrieves the current global extra data.
* @param _self Buffer to access.
* @return Current global extra data.
*/
function getExtraData(
RingBuffer storage _self
)
internal
view
returns (
bytes27
)
{
RingBufferContext memory ctx = _self.getContext();
return ctx.extraData;
}
/**
* Sets the current ring buffer context.
* @param _self Buffer to access.
* @param _ctx Current ring buffer context.
*/
function setContext(
RingBuffer storage _self,
RingBufferContext memory _ctx
)
internal
returns (
bytes32
)
{
bytes32 contextA;
bytes32 contextB;
uint40 globalIndex = _ctx.globalIndex;
bytes27 extraData = _ctx.extraData;
assembly {
contextA := globalIndex
contextA := or(contextA, extraData)
}
uint64 currBufferIndex = _ctx.currBufferIndex;
uint40 prevResetIndex = _ctx.prevResetIndex;
uint40 currResetIndex = _ctx.currResetIndex;
assembly {
contextB := currBufferIndex
contextB := or(contextB, shl(64, prevResetIndex))
contextB := or(contextB, shl(104, currResetIndex))
}
if (_self.contextA != contextA) {
_self.contextA = contextA;
}
if (_self.contextB != contextB) {
_self.contextB = contextB;
}
}
/**
* Retrieves the current ring buffer context.
* @param _self Buffer to access.
* @return Current ring buffer context.
*/
function getContext(
RingBuffer storage _self
)
internal
view
returns (
RingBufferContext memory
)
{
bytes32 contextA = _self.contextA;
bytes32 contextB = _self.contextB;
uint40 globalIndex;
bytes27 extraData;
assembly {
globalIndex := and(contextA, 0x000000000000000000000000000000000000000000000000000000FFFFFFFFFF)
extraData := and(contextA, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000)
}
uint64 currBufferIndex;
uint40 prevResetIndex;
uint40 currResetIndex;
assembly {
currBufferIndex := and(contextB, 0x000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFF)
prevResetIndex := shr(64, and(contextB, 0x00000000000000000000000000000000000000FFFFFFFFFF0000000000000000))
currResetIndex := shr(104, and(contextB, 0x0000000000000000000000000000FFFFFFFFFF00000000000000000000000000))
}
return RingBufferContext({
globalIndex: globalIndex,
extraData: extraData,
currBufferIndex: currBufferIndex,
prevResetIndex: prevResetIndex,
currResetIndex: currResetIndex
});
}
/**
* Retrieves the a buffer from the ring buffer by index.
* @param _self Buffer to access.
* @param _which Index of the sub buffer to access.
* @return Sub buffer for the index.
*/
function getBuffer(
RingBuffer storage _self,
uint256 _which
)
internal
view
returns (
Buffer storage
)
{
return _which % 2 == 0 ? _self.bufferA : _self.bufferB;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/* Logging */
import { console } from "@nomiclabs/buidler/console.sol";
struct TimeboundRingBuffer {
mapping(uint=>bytes32) elements;
bytes32 context;
uint32 maxSize;
uint32 maxSizeIncrementAmount;
uint32 deletionOffset;
uint firstElementTimestamp;
uint timeout;
}
/**
* @title Lib_TimeboundRingBuffer
*/
library Lib_TimeboundRingBuffer {
function init(
TimeboundRingBuffer storage _self,
uint32 _startingSize,
uint32 _maxSizeIncrementAmount,
uint _timeout
)
internal
{
_self.maxSize = _startingSize;
_self.maxSizeIncrementAmount = _maxSizeIncrementAmount;
_self.timeout = _timeout;
_self.firstElementTimestamp = block.timestamp;
}
function pushAppendOnly(
TimeboundRingBuffer storage _self,
bytes32 _ele,
bytes28 _extraData
)
internal
{
uint length = _getLength(_self.context);
uint maxSize = _self.maxSize;
if (length == maxSize) {
if (block.timestamp < _self.firstElementTimestamp + _self.timeout) {
_self.maxSize += _self.maxSizeIncrementAmount;
maxSize = _self.maxSize;
}
}
_self.elements[length % maxSize] = _ele;
_self.context = makeContext(uint32(length+1), _extraData);
}
function push(
TimeboundRingBuffer storage _self,
bytes32 _ele,
bytes28 _extraData
)
internal
{
pushAppendOnly(_self, _ele, _extraData);
if (_self.deletionOffset != 0) {
_self.deletionOffset += 1;
}
}
function push2AppendOnly(
TimeboundRingBuffer storage _self,
bytes32 _ele1,
bytes32 _ele2,
bytes28 _extraData
)
internal
{
uint length = _getLength(_self.context);
uint maxSize = _self.maxSize;
if (length + 1 >= maxSize) {
if (block.timestamp < _self.firstElementTimestamp + _self.timeout) {
// Because this is a push2 we need to at least increment by 2
_self.maxSize += _self.maxSizeIncrementAmount > 1 ? _self.maxSizeIncrementAmount : 2;
maxSize = _self.maxSize;
}
}
_self.elements[length % maxSize] = _ele1;
_self.elements[(length + 1) % maxSize] = _ele2;
_self.context = makeContext(uint32(length+2), _extraData);
}
function push2(
TimeboundRingBuffer storage _self,
bytes32 _ele1,
bytes32 _ele2,
bytes28 _extraData
)
internal
{
push2AppendOnly(_self, _ele1, _ele2, _extraData);
if (_self.deletionOffset != 0) {
_self.deletionOffset = _self.deletionOffset == 1 ? 0 : _self.deletionOffset - 2;
}
}
function makeContext(
uint32 _length,
bytes28 _extraData
)
internal
pure
returns(
bytes32
)
{
return bytes32(_extraData) | bytes32(uint256(_length));
}
function getLength(
TimeboundRingBuffer storage _self
)
internal
view
returns(
uint32
)
{
return _getLength(_self.context);
}
function _getLength(
bytes32 context
)
internal
pure
returns(
uint32
)
{
// Length is the last 4 bytes
return uint32(uint256(context & 0x00000000000000000000000000000000000000000000000000000000ffffffff));
}
function getExtraData(
TimeboundRingBuffer storage _self
)
internal
view
returns(
bytes28
)
{
// Extra Data is the first 28 bytes
return bytes28(_self.context & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000);
}
function get(
TimeboundRingBuffer storage _self,
uint32 _index
)
internal
view
returns(
bytes32
)
{
uint length = _getLength(_self.context);
require(_index < length, "Index too large.");
require(length - _index <= _self.maxSize - _self.deletionOffset, "Index too old & has been overridden.");
return _self.elements[_index % _self.maxSize];
}
function deleteElementsAfter(
TimeboundRingBuffer storage _self,
uint32 _index,
bytes28 _extraData
)
internal
{
uint32 length = _getLength(_self.context);
uint32 deletionStartingIndex = _index + 1;
uint32 numDeletedElements = length - deletionStartingIndex;
uint32 newDeletionOffset = _self.deletionOffset + numDeletedElements;
require(deletionStartingIndex < length, "Index too large.");
require(newDeletionOffset <= _self.maxSize, "Attempting to delete too many elements.");
_self.deletionOffset = newDeletionOffset;
_self.context = makeContext(deletionStartingIndex, _extraData);
}
}
\ No newline at end of file
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_RingBuffer, iRingBufferOverwriter } from "../../optimistic-ethereum/libraries/utils/Lib_RingBuffer.sol";
/**
* @title TestLib_RingBuffer
*/
contract TestLib_RingBuffer {
using Lib_RingBuffer for Lib_RingBuffer.RingBuffer;
Lib_RingBuffer.RingBuffer internal buf;
function init(
uint256 _initialBufferSize,
bytes32 _id,
iRingBufferOverwriter _overwriter
)
public
{
buf.init(
_initialBufferSize,
_id,
_overwriter
);
}
function push(
bytes32 _value,
bytes27 _extraData
)
public
{
buf.push(
_value,
_extraData
);
}
function push2(
bytes32 _valueA,
bytes32 _valueB,
bytes27 _extraData
)
public
{
buf.push2(
_valueA,
_valueB,
_extraData
);
}
function get(
uint256 _index
)
public
view
returns (
bytes32
)
{
return buf.get(_index);
}
function deleteElementsAfterInclusive(
uint40 _index,
bytes27 _extraData
)
internal
{
return buf.deleteElementsAfterInclusive(
_index,
_extraData
);
}
function getLength()
internal
view
returns (
uint40
)
{
return buf.getLength();
}
function getExtraData()
internal
view
returns (
bytes27
)
{
return buf.getExtraData();
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { TimeboundRingBuffer, Lib_TimeboundRingBuffer } from "../../optimistic-ethereum/libraries/utils/Lib_TimeboundRingBuffer.sol";
/**
* @title TestLib_TimeboundRingBuffer
*/
contract TestLib_TimeboundRingBuffer {
using Lib_TimeboundRingBuffer for TimeboundRingBuffer;
TimeboundRingBuffer public list;
constructor (
uint32 _startingSize,
uint32 _maxSizeIncrementAmount,
uint _timeout
) {
list.init(_startingSize, _maxSizeIncrementAmount, _timeout);
}
function push(bytes32 _ele, bytes28 _extraData) public {
list.push(_ele, _extraData);
}
function push2(bytes32 _ele1, bytes32 _ele2, bytes28 _extraData) public {
list.push2(_ele1, _ele2, _extraData);
}
function get(uint32 _index) public view returns(bytes32) {
return list.get(_index);
}
function deleteElementsAfter(uint32 _index, bytes28 _extraData) public {
return list.deleteElementsAfter(_index, _extraData);
}
function getLength() public view returns(uint32) {
return list.getLength();
}
function getExtraData() public view returns(bytes28) {
return list.getExtraData();
}
function getMaxSize() public view returns(uint32) {
return list.maxSize;
}
function getMaxSizeIncrementAmount() public view returns(uint32) {
return list.maxSizeIncrementAmount;
}
function getFirstElementTimestamp() public view returns(uint) {
return list.firstElementTimestamp;
}
function getTimeout() public view returns(uint) {
return list.timeout;
}
}
\ No newline at end of file
...@@ -212,7 +212,7 @@ describe('OVM_L1CrossDomainMessenger', () => { ...@@ -212,7 +212,7 @@ describe('OVM_L1CrossDomainMessenger', () => {
}) })
beforeEach(async () => { beforeEach(async () => {
Mock__OVM_StateCommitmentChain.smocked.verifyElement.will.return.with( Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with(
true true
) )
Mock__OVM_StateCommitmentChain.smocked.insideFraudProofWindow.will.return.with( Mock__OVM_StateCommitmentChain.smocked.insideFraudProofWindow.will.return.with(
...@@ -245,7 +245,7 @@ describe('OVM_L1CrossDomainMessenger', () => { ...@@ -245,7 +245,7 @@ describe('OVM_L1CrossDomainMessenger', () => {
}) })
it('should revert if provided an invalid state root proof', async () => { it('should revert if provided an invalid state root proof', async () => {
Mock__OVM_StateCommitmentChain.smocked.verifyElement.will.return.with( Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with(
false false
) )
......
...@@ -5,7 +5,7 @@ import { ethers } from '@nomiclabs/buidler' ...@@ -5,7 +5,7 @@ import { ethers } from '@nomiclabs/buidler'
import { Signer, ContractFactory, Contract, BigNumber } from 'ethers' import { Signer, ContractFactory, Contract, BigNumber } 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 _ from 'lodash' import _, { times } from 'lodash'
/* Internal Imports */ /* Internal Imports */
import { import {
...@@ -18,21 +18,36 @@ import { ...@@ -18,21 +18,36 @@ import {
getEthTime, getEthTime,
getNextBlockNumber, getNextBlockNumber,
increaseEthTime, increaseEthTime,
ZERO_ADDRESS ZERO_ADDRESS,
} from '../../../helpers' } from '../../../helpers'
import { defaultAbiCoder, keccak256 } from 'ethers/lib/utils' import { defaultAbiCoder, keccak256 } from 'ethers/lib/utils'
interface sequencerBatchContext {
numSequencedTransactions: Number
numSubsequentQueueTransactions: Number
timestamp: Number
blockNumber: Number
}
const ELEMENT_TEST_SIZES = [1, 2, 4, 8, 16] const ELEMENT_TEST_SIZES = [1, 2, 4, 8, 16]
const DECOMPRESSION_ADDRESS = '0x4200000000000000000000000000000000000008' const DECOMPRESSION_ADDRESS = '0x4200000000000000000000000000000000000008'
const MAX_GAS_LIMIT = 8_000_000 const MAX_GAS_LIMIT = 8_000_000
const getQueueLeafHash = (index: number): string => {
return keccak256(
defaultAbiCoder.encode(
['bool', 'uint256', 'uint256', 'uint256', 'bytes'],
[false, index, 0, 0, '0x']
)
)
}
const getSequencerLeafHash = (
timestamp: number,
blockNumber: number,
data: string
): string => {
return keccak256(
'0x0100' +
remove0x(BigNumber.from(timestamp).toHexString()).padStart(64, '0') +
remove0x(BigNumber.from(blockNumber).toHexString()).padStart(64, '0') +
remove0x(data)
)
}
const getTransactionHash = ( const getTransactionHash = (
sender: string, sender: string,
target: string, target: string,
...@@ -131,7 +146,7 @@ const encodeBatchContext = (context: BatchContext): string => { ...@@ -131,7 +146,7 @@ const encodeBatchContext = (context: BatchContext): string => {
) )
} }
describe.only('OVM_CanonicalTransactionChain', () => { describe('OVM_CanonicalTransactionChain', () => {
let signer: Signer let signer: Signer
let sequencer: Signer let sequencer: Signer
before(async () => { before(async () => {
...@@ -140,6 +155,7 @@ describe.only('OVM_CanonicalTransactionChain', () => { ...@@ -140,6 +155,7 @@ describe.only('OVM_CanonicalTransactionChain', () => {
let AddressManager: Contract let AddressManager: Contract
let Mock__OVM_ExecutionManager: MockContract let Mock__OVM_ExecutionManager: MockContract
let Mock__OVM_StateCommitmentChain: MockContract
before(async () => { before(async () => {
AddressManager = await makeAddressManager() AddressManager = await makeAddressManager()
await AddressManager.setAddress( await AddressManager.setAddress(
...@@ -155,12 +171,23 @@ describe.only('OVM_CanonicalTransactionChain', () => { ...@@ -155,12 +171,23 @@ describe.only('OVM_CanonicalTransactionChain', () => {
await ethers.getContractFactory('OVM_ExecutionManager') await ethers.getContractFactory('OVM_ExecutionManager')
) )
Mock__OVM_StateCommitmentChain = smockit(
await ethers.getContractFactory('OVM_StateCommitmentChain')
)
await setProxyTarget( await setProxyTarget(
AddressManager, AddressManager,
'OVM_ExecutionManager', 'OVM_ExecutionManager',
Mock__OVM_ExecutionManager Mock__OVM_ExecutionManager
) )
await setProxyTarget(
AddressManager,
'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
) )
...@@ -192,7 +219,9 @@ describe.only('OVM_CanonicalTransactionChain', () => { ...@@ -192,7 +219,9 @@ describe.only('OVM_CanonicalTransactionChain', () => {
await expect( await expect(
OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data) OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data)
).to.be.revertedWith('Transaction exceeds maximum rollup data size.') ).to.be.revertedWith(
'Transaction exceeds maximum rollup transaction data size.'
)
}) })
it('should revert if gas limit parameter is not at least MIN_ROLLUP_TX_GAS', async () => { it('should revert if gas limit parameter is not at least MIN_ROLLUP_TX_GAS', async () => {
...@@ -201,7 +230,7 @@ describe.only('OVM_CanonicalTransactionChain', () => { ...@@ -201,7 +230,7 @@ describe.only('OVM_CanonicalTransactionChain', () => {
await expect( await expect(
OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data) OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data)
).to.be.revertedWith('Layer 2 gas limit too low to enqueue.') ).to.be.revertedWith('Transaction gas limit too low to enqueue.')
}) })
it('should revert if transaction gas limit does not cover rollup burn', async () => { it('should revert if transaction gas limit does not cover rollup burn', async () => {
...@@ -242,7 +271,7 @@ describe.only('OVM_CanonicalTransactionChain', () => { ...@@ -242,7 +271,7 @@ describe.only('OVM_CanonicalTransactionChain', () => {
it('should revert when accessing a non-existent element', async () => { it('should revert when accessing a non-existent element', async () => {
await expect( await expect(
OVM_CanonicalTransactionChain.getQueueElement(0) OVM_CanonicalTransactionChain.getQueueElement(0)
).to.be.revertedWith('Index too large') ).to.be.revertedWith('Index out of bounds.')
}) })
describe('when the requested element exists', () => { describe('when the requested element exists', () => {
...@@ -394,7 +423,7 @@ describe.only('OVM_CanonicalTransactionChain', () => { ...@@ -394,7 +423,7 @@ describe.only('OVM_CanonicalTransactionChain', () => {
it('should revert if the queue is empty', async () => { it('should revert if the queue is empty', async () => {
await expect( await expect(
OVM_CanonicalTransactionChain.appendQueueBatch(1) OVM_CanonicalTransactionChain.appendQueueBatch(1)
).to.be.revertedWith('Index too large.') ).to.be.revertedWith('Index out of bounds.')
}) })
describe('when the queue is not empty', () => { describe('when the queue is not empty', () => {
...@@ -459,7 +488,7 @@ describe.only('OVM_CanonicalTransactionChain', () => { ...@@ -459,7 +488,7 @@ describe.only('OVM_CanonicalTransactionChain', () => {
it(`should revert if appending ${size} + 1 elements`, async () => { it(`should revert if appending ${size} + 1 elements`, async () => {
await expect( await expect(
OVM_CanonicalTransactionChain.appendQueueBatch(size + 1) OVM_CanonicalTransactionChain.appendQueueBatch(size + 1)
).to.be.revertedWith('Index too large.') ).to.be.revertedWith('Index out of bounds.')
}) })
}) })
}) })
...@@ -473,77 +502,103 @@ describe.only('OVM_CanonicalTransactionChain', () => { ...@@ -473,77 +502,103 @@ describe.only('OVM_CanonicalTransactionChain', () => {
const gasLimit = 500_000 const gasLimit = 500_000
const data = '0x' + '12'.repeat(1234) const data = '0x' + '12'.repeat(1234)
const timestamp = (await getEthTime(ethers.provider) + 100) const timestamp = (await getEthTime(ethers.provider)) + 100
await setEthTime(ethers.provider, timestamp) await setEthTime(ethers.provider, timestamp)
await OVM_CanonicalTransactionChain.enqueue( await OVM_CanonicalTransactionChain.enqueue(entrypoint, gasLimit, data)
entrypoint,
gasLimit,
data
)
const blockNumber = await ethers.provider.getBlockNumber() const blockNumber = await ethers.provider.getBlockNumber()
await increaseEthTime( await increaseEthTime(ethers.provider, FORCE_INCLUSION_PERIOD_SECONDS * 2)
ethers.provider,
FORCE_INCLUSION_PERIOD_SECONDS * 2
)
await OVM_CanonicalTransactionChain.appendQueueBatch(1) await OVM_CanonicalTransactionChain.appendQueueBatch(1)
expect(await OVM_CanonicalTransactionChain.verifyTransaction( expect(
{ await OVM_CanonicalTransactionChain.verifyTransaction(
timestamp, {
blockNumber, timestamp,
l1QueueOrigin: 1, blockNumber,
l1TxOrigin: await OVM_CanonicalTransactionChain.signer.getAddress(), l1QueueOrigin: 1,
entrypoint, l1TxOrigin: await OVM_CanonicalTransactionChain.signer.getAddress(),
gasLimit, entrypoint,
data gasLimit,
}, data,
{ },
isSequenced: false, {
queueIndex: 0, isSequenced: false,
timestamp: 0, queueIndex: 0,
blockNumber: 0, timestamp: 0,
txData: '0x00' blockNumber: 0,
}, txData: '0x',
{ },
index: 0, {
siblings: [] batchIndex: 0,
} batchRoot: getQueueLeafHash(0),
)).to.equal(true) batchSize: 1,
prevTotalElements: 0,
extraData: '0x',
},
{
index: 0,
siblings: [],
}
)
).to.equal(true)
}) })
it('should successfully verify against a valid sequencer transaction', async () => { it('should successfully verify against a valid sequencer transaction', async () => {
const entrypoint = DECOMPRESSION_ADDRESS const entrypoint = DECOMPRESSION_ADDRESS
const gasLimit = MAX_GAS_LIMIT const gasLimit = MAX_GAS_LIMIT
const data = '0x' + '12'.repeat(1234) const data = '0x' + '12'.repeat(1234)
const timestamp = await getEthTime(ethers.provider) - 10 const timestamp = (await getEthTime(ethers.provider)) - 10
const blockNumber = await ethers.provider.getBlockNumber() - 1 const blockNumber = (await ethers.provider.getBlockNumber()) - 1
// TODO: await OVM_CanonicalTransactionChain.appendQueueBatch(1) await appendSequencerBatch(
OVM_CanonicalTransactionChain.connect(sequencer),
expect(await OVM_CanonicalTransactionChain.verifyTransaction(
{
timestamp,
blockNumber,
l1QueueOrigin: 0,
l1TxOrigin: ZERO_ADDRESS,
entrypoint,
gasLimit,
data
},
{
isSequenced: true,
queueIndex: 0,
timestamp,
blockNumber,
txData: data
},
{ {
index: 0, shouldStartAtBatch: 0,
siblings: [] totalElementsToAppend: 1,
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp: timestamp,
blockNumber: blockNumber,
},
],
transactions: [data],
} }
)).to.equal(true) )
expect(
await OVM_CanonicalTransactionChain.verifyTransaction(
{
timestamp,
blockNumber,
l1QueueOrigin: 0,
l1TxOrigin: ZERO_ADDRESS,
entrypoint,
gasLimit,
data,
},
{
isSequenced: true,
queueIndex: 0,
timestamp,
blockNumber,
txData: data,
},
{
batchIndex: 0,
batchRoot: getSequencerLeafHash(timestamp, blockNumber, data),
batchSize: 1,
prevTotalElements: 0,
extraData: '0x',
},
{
index: 0,
siblings: [],
}
)
).to.equal(true)
}) })
}) })
......
...@@ -16,6 +16,16 @@ import { ...@@ -16,6 +16,16 @@ import {
NULL_BYTES32, NULL_BYTES32,
} from '../../../helpers' } from '../../../helpers'
const DUMMY_TX_CHAIN_ELEMENTS = [...Array(10)].map(() => {
return {
isSequenced: false,
queueIndex: BigNumber.from(0),
timestamp: BigNumber.from(0),
blockNumber: BigNumber.from(0),
txData: NULL_BYTES32,
}
})
describe('OVM_FraudVerifier', () => { describe('OVM_FraudVerifier', () => {
let AddressManager: Contract let AddressManager: Contract
before(async () => { before(async () => {
...@@ -83,7 +93,7 @@ describe('OVM_FraudVerifier', () => { ...@@ -83,7 +93,7 @@ describe('OVM_FraudVerifier', () => {
describe('initializeFraudVerification', () => { describe('initializeFraudVerification', () => {
describe('when provided an invalid pre-state root inclusion proof', () => { describe('when provided an invalid pre-state root inclusion proof', () => {
before(() => { before(() => {
Mock__OVM_StateCommitmentChain.smocked.verifyElement.will.return.with( Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with(
false false
) )
}) })
...@@ -95,6 +105,7 @@ describe('OVM_FraudVerifier', () => { ...@@ -95,6 +105,7 @@ describe('OVM_FraudVerifier', () => {
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0], DUMMY_BATCH_PROOFS[0],
DUMMY_OVM_TRANSACTIONS[0], DUMMY_OVM_TRANSACTIONS[0],
DUMMY_TX_CHAIN_ELEMENTS[0],
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0] DUMMY_BATCH_PROOFS[0]
) )
...@@ -104,14 +115,14 @@ describe('OVM_FraudVerifier', () => { ...@@ -104,14 +115,14 @@ describe('OVM_FraudVerifier', () => {
describe('when provided a valid pre-state root inclusion proof', () => { describe('when provided a valid pre-state root inclusion proof', () => {
before(() => { before(() => {
Mock__OVM_StateCommitmentChain.smocked.verifyElement.will.return.with( Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with(
true true
) )
}) })
describe('when provided an invalid transaction inclusion proof', () => { describe('when provided an invalid transaction inclusion proof', () => {
before(() => { before(() => {
Mock__OVM_CanonicalTransactionChain.smocked.verifyElement.will.return.with( Mock__OVM_CanonicalTransactionChain.smocked.verifyTransaction.will.return.with(
false false
) )
}) })
...@@ -123,6 +134,7 @@ describe('OVM_FraudVerifier', () => { ...@@ -123,6 +134,7 @@ describe('OVM_FraudVerifier', () => {
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0], DUMMY_BATCH_PROOFS[0],
DUMMY_OVM_TRANSACTIONS[0], DUMMY_OVM_TRANSACTIONS[0],
DUMMY_TX_CHAIN_ELEMENTS[0],
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0] DUMMY_BATCH_PROOFS[0]
) )
...@@ -132,7 +144,7 @@ describe('OVM_FraudVerifier', () => { ...@@ -132,7 +144,7 @@ describe('OVM_FraudVerifier', () => {
describe('when provided a valid transaction inclusion proof', () => { describe('when provided a valid transaction inclusion proof', () => {
before(() => { before(() => {
Mock__OVM_CanonicalTransactionChain.smocked.verifyElement.will.return.with( Mock__OVM_CanonicalTransactionChain.smocked.verifyTransaction.will.return.with(
true true
) )
}) })
...@@ -144,6 +156,7 @@ describe('OVM_FraudVerifier', () => { ...@@ -144,6 +156,7 @@ describe('OVM_FraudVerifier', () => {
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0], DUMMY_BATCH_PROOFS[0],
DUMMY_OVM_TRANSACTIONS[0], DUMMY_OVM_TRANSACTIONS[0],
DUMMY_TX_CHAIN_ELEMENTS[0],
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0] DUMMY_BATCH_PROOFS[0]
) )
...@@ -159,10 +172,10 @@ describe('OVM_FraudVerifier', () => { ...@@ -159,10 +172,10 @@ describe('OVM_FraudVerifier', () => {
describe('finalizeFraudVerification', () => { describe('finalizeFraudVerification', () => {
beforeEach(async () => { beforeEach(async () => {
Mock__OVM_StateCommitmentChain.smocked.verifyElement.will.return.with( Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with(
true true
) )
Mock__OVM_CanonicalTransactionChain.smocked.verifyElement.will.return.with( Mock__OVM_CanonicalTransactionChain.smocked.verifyTransaction.will.return.with(
true true
) )
...@@ -171,6 +184,7 @@ describe('OVM_FraudVerifier', () => { ...@@ -171,6 +184,7 @@ describe('OVM_FraudVerifier', () => {
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0], DUMMY_BATCH_PROOFS[0],
DUMMY_OVM_TRANSACTIONS[0], DUMMY_OVM_TRANSACTIONS[0],
DUMMY_TX_CHAIN_ELEMENTS[0],
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0] DUMMY_BATCH_PROOFS[0]
) )
...@@ -230,7 +244,7 @@ describe('OVM_FraudVerifier', () => { ...@@ -230,7 +244,7 @@ describe('OVM_FraudVerifier', () => {
describe('when provided an invalid pre-state root inclusion proof', () => { describe('when provided an invalid pre-state root inclusion proof', () => {
beforeEach(() => { beforeEach(() => {
Mock__OVM_StateCommitmentChain.smocked.verifyElement.will.return.with( Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with(
false false
) )
}) })
...@@ -251,14 +265,14 @@ describe('OVM_FraudVerifier', () => { ...@@ -251,14 +265,14 @@ describe('OVM_FraudVerifier', () => {
describe('when provided a valid pre-state root inclusion proof', () => { describe('when provided a valid pre-state root inclusion proof', () => {
before(() => { before(() => {
Mock__OVM_StateCommitmentChain.smocked.verifyElement.will.return.with( Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with(
true true
) )
}) })
describe('when provided an invalid post-state root inclusion proof', () => { describe('when provided an invalid post-state root inclusion proof', () => {
beforeEach(() => { beforeEach(() => {
Mock__OVM_StateCommitmentChain.smocked.verifyElement.will.return.with( Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with(
(stateRoot: string, ...args: any) => { (stateRoot: string, ...args: any) => {
return stateRoot !== NON_NULL_BYTES32 return stateRoot !== NON_NULL_BYTES32
} }
...@@ -281,7 +295,7 @@ describe('OVM_FraudVerifier', () => { ...@@ -281,7 +295,7 @@ describe('OVM_FraudVerifier', () => {
describe('when provided a valid post-state root inclusion proof', () => { describe('when provided a valid post-state root inclusion proof', () => {
before(() => { before(() => {
Mock__OVM_StateCommitmentChain.smocked.verifyElement.will.return.with( Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with(
true true
) )
}) })
......
/* tslint:disable:no-empty */
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, Signer } from 'ethers'
/* Internal Imports */
import {
NON_NULL_BYTES32,
makeHexString,
increaseEthTime,
} from '../../../helpers'
const numToBytes32 = (num: Number): string => {
if (num < 0 || num > 255) {
throw new Error('Unsupported number.')
}
const strNum = num < 16 ? '0' + num.toString(16) : num.toString(16)
return '0x' + '00'.repeat(31) + strNum
}
describe('Lib_TimeboundRingBuffer', () => {
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let Lib_TimeboundRingBuffer: Contract
const NON_NULL_BYTES28 = makeHexString('01', 28)
const pushNum = (num: Number) =>
Lib_TimeboundRingBuffer.push(numToBytes32(num), NON_NULL_BYTES28)
const push2Nums = (num1: Number, num2: Number) =>
Lib_TimeboundRingBuffer.push2(
numToBytes32(num1),
numToBytes32(num2),
NON_NULL_BYTES28
)
describe('push with no timeout', () => {
beforeEach(async () => {
Lib_TimeboundRingBuffer = await (
await ethers.getContractFactory('TestLib_TimeboundRingBuffer')
).deploy(4, 1, 0)
for (let i = 0; i < 4; i++) {
await Lib_TimeboundRingBuffer.push(numToBytes32(i), NON_NULL_BYTES28)
}
})
it('should push a single value which increases the length', async () => {
expect(await Lib_TimeboundRingBuffer.getLength()).to.equal(4)
await Lib_TimeboundRingBuffer.push(NON_NULL_BYTES32, NON_NULL_BYTES28)
expect(await Lib_TimeboundRingBuffer.getLength()).to.equal(5)
})
it('should overwrite old values:[0,1,2,3] -> [4,5,2,3]', async () => {
expect(await Lib_TimeboundRingBuffer.get(0)).to.equal(numToBytes32(0))
await Lib_TimeboundRingBuffer.push(numToBytes32(4), NON_NULL_BYTES28)
expect(await Lib_TimeboundRingBuffer.get(4)).to.equal(numToBytes32(4))
await Lib_TimeboundRingBuffer.push(numToBytes32(5), NON_NULL_BYTES28)
expect(await Lib_TimeboundRingBuffer.get(5)).to.equal(numToBytes32(5))
})
})
describe('get()', () => {
before(async () => {
Lib_TimeboundRingBuffer = await (
await ethers.getContractFactory('TestLib_TimeboundRingBuffer')
).deploy(2, 1, 10_000)
await increaseEthTime(ethers.provider, 20_000)
for (let i = 0; i < 4; i++) {
await Lib_TimeboundRingBuffer.push(numToBytes32(i), NON_NULL_BYTES28)
}
})
it('should revert if index is too old', async () => {
await expect(Lib_TimeboundRingBuffer.get(0)).to.be.revertedWith(
'Index too old & has been overridden.'
)
})
it('should revert if index is greater than length', async () => {
await expect(Lib_TimeboundRingBuffer.get(5)).to.be.revertedWith(
'Index too large.'
)
})
})
describe('push with timeout', () => {
const startSize = 2
beforeEach(async () => {
Lib_TimeboundRingBuffer = await (
await ethers.getContractFactory('TestLib_TimeboundRingBuffer')
).deploy(startSize, 1, 10_000)
for (let i = 0; i < startSize; i++) {
await pushNum(i)
}
})
const pushJunk = () =>
Lib_TimeboundRingBuffer.push(NON_NULL_BYTES32, NON_NULL_BYTES28)
it('should push a single value which extends the array', async () => {
await pushNum(2)
const increasedSize = startSize + 1
expect(await Lib_TimeboundRingBuffer.getMaxSize()).to.equal(increasedSize)
await increaseEthTime(ethers.provider, 20_000)
await pushNum(3)
expect(await Lib_TimeboundRingBuffer.getMaxSize()).to.equal(increasedSize) // Shouldn't increase the size this time
expect(await Lib_TimeboundRingBuffer.get(2)).to.equal(numToBytes32(2))
expect(await Lib_TimeboundRingBuffer.get(3)).to.equal(numToBytes32(3))
})
it('should NOT extend the array if the time is not up and extend it when it is', async () => {
await pushJunk()
const increasedSize = startSize + 1
expect(await Lib_TimeboundRingBuffer.getMaxSize()).to.equal(increasedSize)
await increaseEthTime(ethers.provider, 20_000)
// Push the time forward and verify that the time doesn't increment
for (let i = 0; i < increasedSize + 1; i++) {
await pushJunk()
}
expect(await Lib_TimeboundRingBuffer.getMaxSize()).to.equal(increasedSize)
})
})
describe('push2 with timeout', () => {
const startSize = 2
beforeEach(async () => {
Lib_TimeboundRingBuffer = await (
await ethers.getContractFactory('TestLib_TimeboundRingBuffer')
).deploy(startSize, 1, 10_000)
})
it('should push a single value which extends the array', async () => {
await push2Nums(0, 1)
await push2Nums(2, 3)
const increasedSize = startSize + 2
expect(await Lib_TimeboundRingBuffer.getMaxSize()).to.equal(increasedSize)
await increaseEthTime(ethers.provider, 20_000)
await push2Nums(4, 5)
expect(await Lib_TimeboundRingBuffer.getMaxSize()).to.equal(increasedSize) // Shouldn't increase the size this time
for (let i = 2; i < 6; i++) {
expect(await Lib_TimeboundRingBuffer.get(i)).to.equal(numToBytes32(i))
}
})
})
describe('getExtraData', () => {
beforeEach(async () => {
Lib_TimeboundRingBuffer = await (
await ethers.getContractFactory('TestLib_TimeboundRingBuffer')
).deploy(2, 1, 10_000)
})
it('should return the expected extra data', async () => {
await Lib_TimeboundRingBuffer.push(NON_NULL_BYTES32, NON_NULL_BYTES28)
expect(await Lib_TimeboundRingBuffer.getExtraData()).to.equal(
NON_NULL_BYTES28
)
})
})
describe('deleteElementsAfter', () => {
// [0,1,2,3] -> [0,1,-,-]
beforeEach(async () => {
Lib_TimeboundRingBuffer = await (
await ethers.getContractFactory('TestLib_TimeboundRingBuffer')
).deploy(4, 1, 0)
for (let i = 0; i < 4; i++) {
pushNum(i)
}
})
it('should disallow deletions which are too old', async () => {
push2Nums(4, 5)
await expect(
Lib_TimeboundRingBuffer.deleteElementsAfter(0, NON_NULL_BYTES28)
).to.be.revertedWith('Attempting to delete too many elements.')
})
it('should not allow get to be called on an old value even after deletion', async () => {
pushNum(4)
expect(await Lib_TimeboundRingBuffer.getMaxSize()).to.equal(4)
await expect(Lib_TimeboundRingBuffer.get(0)).to.be.revertedWith(
'Index too old & has been overridden.'
)
Lib_TimeboundRingBuffer.deleteElementsAfter(3, NON_NULL_BYTES28)
await expect(Lib_TimeboundRingBuffer.get(0)).to.be.revertedWith(
'Index too old & has been overridden.'
)
await expect(Lib_TimeboundRingBuffer.get(4)).to.be.revertedWith(
'Index too large.'
)
expect(await Lib_TimeboundRingBuffer.get(1)).to.equal(numToBytes32(1))
expect(await Lib_TimeboundRingBuffer.get(3)).to.equal(numToBytes32(3))
})
it('should not reduce the overall size of the buffer', async () => {
pushNum(4)
expect(await Lib_TimeboundRingBuffer.get(1)).to.equal(numToBytes32(1))
// We expect that we can still access `1` because the deletionOffset
// will have reduced by 1 after we pushed.
Lib_TimeboundRingBuffer.deleteElementsAfter(3, NON_NULL_BYTES28)
expect(await Lib_TimeboundRingBuffer.get(1)).to.equal(numToBytes32(1))
})
})
})
...@@ -3,7 +3,7 @@ import { ZERO_ADDRESS, NULL_BYTES32 } from '../constants' ...@@ -3,7 +3,7 @@ import { ZERO_ADDRESS, NULL_BYTES32 } from '../constants'
export const DUMMY_OVM_TRANSACTIONS = [ export const DUMMY_OVM_TRANSACTIONS = [
{ {
timestamp: 0, timestamp: 0,
number: 0, blockNumber: 0,
l1QueueOrigin: 0, l1QueueOrigin: 0,
l1TxOrigin: ZERO_ADDRESS, l1TxOrigin: ZERO_ADDRESS,
entrypoint: ZERO_ADDRESS, entrypoint: ZERO_ADDRESS,
......
...@@ -263,7 +263,7 @@ export class ExecutionManagerTestRunner { ...@@ -263,7 +263,7 @@ export class ExecutionManagerTestRunner {
await this.contracts.OVM_ExecutionManager.run( await this.contracts.OVM_ExecutionManager.run(
{ {
timestamp: step.functionParams.timestamp, timestamp: step.functionParams.timestamp,
number: 0, blockNumber: 0,
l1QueueOrigin: step.functionParams.queueOrigin, l1QueueOrigin: step.functionParams.queueOrigin,
l1TxOrigin: step.functionParams.origin, l1TxOrigin: step.functionParams.origin,
entrypoint: step.functionParams.entrypoint, entrypoint: step.functionParams.entrypoint,
......
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