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
));
}
}
...@@ -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 *
...@@ -26,10 +27,13 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, L ...@@ -26,10 +27,13 @@ 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;
/**
* 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;
function appendStateBatch(bytes32[] calldata _batch) external; /**
function deleteStateBatch(Lib_OVMCodec.ChainBatchHeader memory _batchHeader) external; * 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
);
/********************************** /**
* Public Functions: Batch Status * * 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
);
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
) )
......
// 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,23 +502,17 @@ describe.only('OVM_CanonicalTransactionChain', () => { ...@@ -473,23 +502,17 @@ 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, timestamp,
blockNumber, blockNumber,
...@@ -497,32 +520,56 @@ describe.only('OVM_CanonicalTransactionChain', () => { ...@@ -497,32 +520,56 @@ describe.only('OVM_CanonicalTransactionChain', () => {
l1TxOrigin: await OVM_CanonicalTransactionChain.signer.getAddress(), l1TxOrigin: await OVM_CanonicalTransactionChain.signer.getAddress(),
entrypoint, entrypoint,
gasLimit, gasLimit,
data data,
}, },
{ {
isSequenced: false, isSequenced: false,
queueIndex: 0, queueIndex: 0,
timestamp: 0, timestamp: 0,
blockNumber: 0, blockNumber: 0,
txData: '0x00' txData: '0x',
},
{
batchIndex: 0,
batchRoot: getQueueLeafHash(0),
batchSize: 1,
prevTotalElements: 0,
extraData: '0x',
}, },
{ {
index: 0, index: 0,
siblings: [] siblings: [],
} }
)).to.equal(true) )
).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),
{
shouldStartAtBatch: 0,
totalElementsToAppend: 1,
contexts: [
{
numSequencedTransactions: 1,
numSubsequentQueueTransactions: 0,
timestamp: timestamp,
blockNumber: blockNumber,
},
],
transactions: [data],
}
)
expect(await OVM_CanonicalTransactionChain.verifyTransaction( expect(
await OVM_CanonicalTransactionChain.verifyTransaction(
{ {
timestamp, timestamp,
blockNumber, blockNumber,
...@@ -530,20 +577,28 @@ describe.only('OVM_CanonicalTransactionChain', () => { ...@@ -530,20 +577,28 @@ describe.only('OVM_CanonicalTransactionChain', () => {
l1TxOrigin: ZERO_ADDRESS, l1TxOrigin: ZERO_ADDRESS,
entrypoint, entrypoint,
gasLimit, gasLimit,
data data,
}, },
{ {
isSequenced: true, isSequenced: true,
queueIndex: 0, queueIndex: 0,
timestamp, timestamp,
blockNumber, blockNumber,
txData: data txData: data,
},
{
batchIndex: 0,
batchRoot: getSequencerLeafHash(timestamp, blockNumber, data),
batchSize: 1,
prevTotalElements: 0,
extraData: '0x',
}, },
{ {
index: 0, index: 0,
siblings: [] siblings: [],
} }
)).to.equal(true) )
).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