Transaction Hash:
Block:
15794454 at Oct-21-2022 05:40:59 AM +UTC
Transaction Fee:
0.003136081846336257 ETH
$5.94
Gas Used:
162,141 Gas / 19.341695477 Gwei
Emitted Events:
275 |
StateCommitmentChain.StateBatchAppended( _batchIndex=10886, _batchRoot=C90F190FCFB10C92AA1F71CE69564C3F85AC6D7C3A8ADFE1367B3A0DD21D7480, _batchSize=28, _prevTotalElements=845130, _extraData=0x00000000000000000000000000000000000000000000000000000000635230EB0000000000000000000000005558C63D5BF229450995ADC160C023C9F4D4BE80 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x13992B9f...747e87d93 | (Boba Network: Chain Storage Container SCC) | ||||
0x388C818C...7ccB19297
Miner
| (Lido: Execution Layer Rewards Vault) | 202.760786641458334523 Eth | 202.760814556738266377 Eth | 0.000027915279931854 | |
0x5558c63d...9F4d4bE80 |
14.515690441289940532 Eth
Nonce: 3885
|
14.512554359443604275 Eth
Nonce: 3886
| 0.003136081846336257 |
Execution Trace
StateCommitmentChain.appendStateBatch( )
-
Lib_AddressManager.getAddress( _name=ChainStorageContainer-SCC-batches ) => ( 0x13992B9f327faCA11568BE18a8ad3E9747e87d93 )
-
ChainStorageContainer.STATICCALL( )
-
Lib_AddressManager.getAddress( _name=BondManager ) => ( 0x60660e6CDEb423cf847dD11De4C473130D65b627 )
BondManager.isCollateralized( _who=0x5558c63d5bf229450995adc160c023C9F4d4bE80 ) => ( True )
-
Lib_AddressManager.getAddress( _name=OVM_Proposer ) => ( 0x5558c63d5bf229450995adc160c023C9F4d4bE80 )
-
-
Lib_AddressManager.getAddress( _name=CanonicalTransactionChain ) => ( 0xfBd2541e316948B259264c02f370eD088E04c3Db )
CanonicalTransactionChain.STATICCALL( )
-
Lib_AddressManager.getAddress( _name=ChainStorageContainer-CTC-batches ) => ( 0x17148284d2da2f38c96346f1776C1BF7D7691231 )
-
ChainStorageContainer.STATICCALL( )
-
-
Lib_AddressManager.getAddress( _name=ChainStorageContainer-SCC-batches ) => ( 0x13992B9f327faCA11568BE18a8ad3E9747e87d93 )
-
ChainStorageContainer.STATICCALL( )
-
Lib_AddressManager.getAddress( _name=OVM_Proposer ) => ( 0x5558c63d5bf229450995adc160c023C9F4d4bE80 )
-
Lib_AddressManager.getAddress( _name=ChainStorageContainer-SCC-batches ) => ( 0x13992B9f327faCA11568BE18a8ad3E9747e87d93 )
-
ChainStorageContainer.STATICCALL( )
-
Lib_AddressManager.getAddress( _name=ChainStorageContainer-SCC-batches ) => ( 0x13992B9f327faCA11568BE18a8ad3E9747e87d93 )
-
ChainStorageContainer.STATICCALL( )
-
Lib_AddressManager.getAddress( _name=ChainStorageContainer-SCC-batches ) => ( 0x13992B9f327faCA11568BE18a8ad3E9747e87d93 )
ChainStorageContainer.push( _object=39D282F446827D2725FCBFF24C4AE48098D205B157233CBB242227041045857E, _globalMetadata=System.Byte[] )
-
Lib_AddressManager.getAddress( _name=StateCommitmentChain ) => ( 0xdE7355C971A5B733fe2133753Abd7e5441d441Ec )
-
appendStateBatch[StateCommitmentChain (ln:78)]
getTotalElements[StateCommitmentChain (ln:85)]
_getBatchExtraData[StateCommitmentChain (ln:55)]
getGlobalMetadata[StateCommitmentChain (ln:164)]
isCollateralized[StateCommitmentChain (ln:90)]
resolve[StateCommitmentChain (ln:90)]
getTotalElements[StateCommitmentChain (ln:95)]
_getBatchExtraData[StateCommitmentChain (ln:55)]
getGlobalMetadata[StateCommitmentChain (ln:164)]
getTotalElements[StateCommitmentChain (ln:96)]
resolve[StateCommitmentChain (ln:96)]
_appendBatch[StateCommitmentChain (ln:102)]
resolve[StateCommitmentChain (ln:211)]
_getBatchExtraData[StateCommitmentChain (ln:215)]
getGlobalMetadata[StateCommitmentChain (ln:164)]
ChainBatchHeader[StateCommitmentChain (ln:231)]
getTotalBatches[StateCommitmentChain (ln:233)]
getMerkleRoot[StateCommitmentChain (ln:234)]
StateBatchAppended[StateCommitmentChain (ln:239)]
push[StateCommitmentChain (ln:246)]
hashBatchHeader[StateCommitmentChain (ln:247)]
encode[Lib_OVMCodec (ln:413)]
_makeBatchExtraData[StateCommitmentChain (ln:248)]
encode[StateCommitmentChain (ln:102)]
File 1 of 6: StateCommitmentChain
File 2 of 6: Lib_AddressManager
File 3 of 6: ChainStorageContainer
File 4 of 6: BondManager
File 5 of 6: CanonicalTransactionChain
File 6 of 6: ChainStorageContainer
// SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* Library Imports */ import {Lib_OVMCodec} from '../../libraries/codec/Lib_OVMCodec.sol'; import {Lib_AddressResolver} from '../../libraries/resolver/Lib_AddressResolver.sol'; import {Lib_MerkleTree} from '../../libraries/utils/Lib_MerkleTree.sol'; /* Interface Imports */ import {IStateCommitmentChain} from './IStateCommitmentChain.sol'; import {ICanonicalTransactionChain} from './ICanonicalTransactionChain.sol'; import {IBondManager} from '../verification/IBondManager.sol'; import {IChainStorageContainer} from './IChainStorageContainer.sol'; /** * @title StateCommitmentChain * @dev The State Commitment Chain (SCC) contract contains a list of proposed state roots which * Proposers assert to be a result of each transaction in the Canonical Transaction Chain (CTC). * Elements here have a 1:1 correspondence with transactions in the CTC, and should be the unique * state root calculated off-chain by applying the canonical transactions one by one. * * Runtime target: EVM */ contract StateCommitmentChain is IStateCommitmentChain, Lib_AddressResolver { /************* * Constants * *************/ uint256 public FRAUD_PROOF_WINDOW; uint256 public SEQUENCER_PUBLISH_WINDOW; /*************** * Constructor * ***************/ /** * @param _libAddressManager Address of the Address Manager. */ constructor( address _libAddressManager, uint256 _fraudProofWindow, uint256 _sequencerPublishWindow ) Lib_AddressResolver(_libAddressManager) { FRAUD_PROOF_WINDOW = _fraudProofWindow; SEQUENCER_PUBLISH_WINDOW = _sequencerPublishWindow; } /******************** * Public Functions * ********************/ /** * Accesses the batch storage container. * @return Reference to the batch storage container. */ function batches() public view returns (IChainStorageContainer) { return IChainStorageContainer(resolve('ChainStorageContainer-SCC-batches')); } /** * @inheritdoc IStateCommitmentChain */ function getTotalElements() public view returns (uint256 _totalElements) { (uint40 totalElements, ) = _getBatchExtraData(); return uint256(totalElements); } /** * @inheritdoc IStateCommitmentChain */ function getTotalBatches() public view returns (uint256 _totalBatches) { return batches().length(); } /** * @inheritdoc IStateCommitmentChain */ function getLastSequencerTimestamp() public view returns (uint256 _lastSequencerTimestamp) { (, uint40 lastSequencerTimestamp) = _getBatchExtraData(); return uint256(lastSequencerTimestamp); } /** * @inheritdoc IStateCommitmentChain */ function appendStateBatch( bytes32[] memory _batch, uint256 _shouldStartAtElement ) public { // Fail fast in to make sure our batch roots aren't accidentally made fraudulent by the // publication of batches by some other user. require( _shouldStartAtElement == getTotalElements(), 'Actual batch start index does not match expected start index.' ); // Proposers must have previously staked at the BondManager require( IBondManager(resolve('BondManager')).isCollateralized(msg.sender), 'Proposer does not have enough collateral posted' ); require(_batch.length > 0, 'Cannot submit an empty state batch.'); require( getTotalElements() + _batch.length <= ICanonicalTransactionChain(resolve('CanonicalTransactionChain')) .getTotalElements(), 'Number of state roots cannot exceed the number of canonical transactions.' ); // Pass the block's timestamp and the publisher of the data // to be used in the fraud proofs _appendBatch(_batch, abi.encode(block.timestamp, msg.sender)); } /** * @inheritdoc IStateCommitmentChain */ function deleteStateBatch(Lib_OVMCodec.ChainBatchHeader memory _batchHeader) public { require( msg.sender == resolve('OVM_FraudVerifier'), 'State batches can only be deleted by the OVM_FraudVerifier.' ); require(_isValidBatchHeader(_batchHeader), 'Invalid batch header.'); require( insideFraudProofWindow(_batchHeader), 'State batches can only be deleted within the fraud proof window.' ); _deleteBatch(_batchHeader); } /** * @inheritdoc IStateCommitmentChain */ function verifyStateCommitment( bytes32 _element, Lib_OVMCodec.ChainBatchHeader memory _batchHeader, Lib_OVMCodec.ChainInclusionProof memory _proof ) public view returns (bool) { require(_isValidBatchHeader(_batchHeader), 'Invalid batch header.'); require( Lib_MerkleTree.verify( _batchHeader.batchRoot, _element, _proof.index, _proof.siblings, _batchHeader.batchSize ), 'Invalid inclusion proof.' ); return true; } /** * @inheritdoc IStateCommitmentChain */ function insideFraudProofWindow( Lib_OVMCodec.ChainBatchHeader memory _batchHeader ) public view returns (bool _inside) { (uint256 timestamp, ) = abi.decode( _batchHeader.extraData, (uint256, address) ); require(timestamp != 0, 'Batch header timestamp cannot be zero'); return (timestamp + FRAUD_PROOF_WINDOW) > block.timestamp; } /********************** * Internal Functions * **********************/ /** * Parses the batch context from the extra data. * @return Total number of elements submitted. * @return Timestamp of the last batch submitted by the sequencer. */ function _getBatchExtraData() internal view returns (uint40, uint40) { bytes27 extraData = batches().getGlobalMetadata(); // solhint-disable max-line-length uint40 totalElements; uint40 lastSequencerTimestamp; assembly { extraData := shr(40, extraData) totalElements := and( extraData, 0x000000000000000000000000000000000000000000000000000000FFFFFFFFFF ) lastSequencerTimestamp := shr( 40, and( extraData, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 ) ) } // solhint-enable max-line-length return (totalElements, lastSequencerTimestamp); } /** * Encodes the batch context for the extra data. * @param _totalElements Total number of elements submitted. * @param _lastSequencerTimestamp Timestamp of the last batch submitted by the sequencer. * @return Encoded batch context. */ function _makeBatchExtraData( uint40 _totalElements, uint40 _lastSequencerTimestamp ) internal pure returns (bytes27) { bytes27 extraData; assembly { extraData := _totalElements extraData := or(extraData, shl(40, _lastSequencerTimestamp)) extraData := shl(40, extraData) } return extraData; } /** * Appends a batch to the chain. * @param _batch Elements within the batch. * @param _extraData Any extra data to append to the batch. */ function _appendBatch(bytes32[] memory _batch, bytes memory _extraData) internal { address sequencer = resolve('OVM_Proposer'); ( uint40 totalElements, uint40 lastSequencerTimestamp ) = _getBatchExtraData(); if (msg.sender == sequencer) { lastSequencerTimestamp = uint40(block.timestamp); } else { // We keep track of the last batch submitted by the sequencer so there's a window in // which only the sequencer can publish state roots. A window like this just reduces // the chance of "system breaking" state roots being published while we're still in // testing mode. This window should be removed or significantly reduced in the future. require( lastSequencerTimestamp + SEQUENCER_PUBLISH_WINDOW < block.timestamp, 'Cannot publish state roots within the sequencer publication window.' ); } // For efficiency reasons getMerkleRoot modifies the `_batch` argument in place // while calculating the root hash therefore any arguments passed to it must not // be used again afterwards Lib_OVMCodec.ChainBatchHeader memory batchHeader = Lib_OVMCodec .ChainBatchHeader({ batchIndex: getTotalBatches(), batchRoot: Lib_MerkleTree.getMerkleRoot(_batch), batchSize: _batch.length, prevTotalElements: totalElements, extraData: _extraData }); emit StateBatchAppended( batchHeader.batchIndex, batchHeader.batchRoot, batchHeader.batchSize, batchHeader.prevTotalElements, batchHeader.extraData ); batches().push( Lib_OVMCodec.hashBatchHeader(batchHeader), _makeBatchExtraData( uint40(batchHeader.prevTotalElements + batchHeader.batchSize), lastSequencerTimestamp ) ); } /** * Removes a batch and all subsequent batches from the chain. * @param _batchHeader Header of the batch to remove. */ function _deleteBatch(Lib_OVMCodec.ChainBatchHeader memory _batchHeader) internal { require( _batchHeader.batchIndex < batches().length(), 'Invalid batch index.' ); require(_isValidBatchHeader(_batchHeader), 'Invalid batch header.'); batches().deleteElementsAfterInclusive( _batchHeader.batchIndex, _makeBatchExtraData(uint40(_batchHeader.prevTotalElements), 0) ); emit StateBatchDeleted(_batchHeader.batchIndex, _batchHeader.batchRoot); } /** * Checks that a batch header matches the stored hash for the given index. * @param _batchHeader Batch header to validate. * @return Whether or not the header matches the stored one. */ function _isValidBatchHeader( Lib_OVMCodec.ChainBatchHeader memory _batchHeader ) internal view returns (bool) { return Lib_OVMCodec.hashBatchHeader(_batchHeader) == batches().get(_batchHeader.batchIndex); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* Library Imports */ import {Lib_RLPReader} from '../rlp/Lib_RLPReader.sol'; import {Lib_RLPWriter} from '../rlp/Lib_RLPWriter.sol'; import {Lib_BytesUtils} from '../utils/Lib_BytesUtils.sol'; import {Lib_Bytes32Utils} from '../utils/Lib_Bytes32Utils.sol'; /** * @title Lib_OVMCodec */ library Lib_OVMCodec { /********* * Enums * *********/ enum QueueOrigin { SEQUENCER_QUEUE, L1TOL2_QUEUE } /*********** * Structs * ***********/ struct EVMAccount { uint256 nonce; uint256 balance; bytes32 storageRoot; bytes32 codeHash; } struct ChainBatchHeader { uint256 batchIndex; bytes32 batchRoot; uint256 batchSize; uint256 prevTotalElements; bytes extraData; } struct ChainInclusionProof { uint256 index; bytes32[] siblings; } struct Transaction { uint256 timestamp; uint256 blockNumber; QueueOrigin l1QueueOrigin; address l1TxOrigin; address entrypoint; uint256 gasLimit; 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 { bytes32 transactionHash; uint40 timestamp; uint40 blockNumber; } /********************** * Internal Functions * **********************/ /** * Encodes a standard OVM transaction. * @param _transaction OVM transaction to encode. * @return Encoded transaction bytes. */ function encodeTransaction(Transaction memory _transaction) internal pure returns (bytes memory) { return abi.encodePacked( _transaction.timestamp, _transaction.blockNumber, _transaction.l1QueueOrigin, _transaction.l1TxOrigin, _transaction.entrypoint, _transaction.gasLimit, _transaction.data ); } /** * Hashes a standard OVM transaction. * @param _transaction OVM transaction to encode. * @return Hashed transaction */ function hashTransaction(Transaction memory _transaction) internal pure returns (bytes32) { return keccak256(encodeTransaction(_transaction)); } /** * @notice Decodes an RLP-encoded account state into a useful struct. * @param _encoded RLP-encoded account state. * @return Account state struct. */ function decodeEVMAccount(bytes memory _encoded) internal pure returns (EVMAccount memory) { Lib_RLPReader.RLPItem[] memory accountState = Lib_RLPReader.readList( _encoded ); return EVMAccount({ nonce: Lib_RLPReader.readUint256(accountState[0]), balance: Lib_RLPReader.readUint256(accountState[1]), storageRoot: Lib_RLPReader.readBytes32(accountState[2]), codeHash: Lib_RLPReader.readBytes32(accountState[3]) }); } /** * Calculates a hash for a given batch header. * @param _batchHeader Header to hash. * @return Hash of the header. */ function hashBatchHeader(Lib_OVMCodec.ChainBatchHeader memory _batchHeader) internal pure returns (bytes32) { return keccak256( abi.encode( _batchHeader.batchRoot, _batchHeader.batchSize, _batchHeader.prevTotalElements, _batchHeader.extraData ) ); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* Library Imports */ import {Lib_AddressManager} from './Lib_AddressManager.sol'; /** * @title Lib_AddressResolver */ abstract contract Lib_AddressResolver { /************* * Variables * *************/ Lib_AddressManager public libAddressManager; /*************** * Constructor * ***************/ /** * @param _libAddressManager Address of the Lib_AddressManager. */ constructor(address _libAddressManager) { libAddressManager = Lib_AddressManager(_libAddressManager); } /******************** * Public Functions * ********************/ /** * Resolves the address associated with a given name. * @param _name Name to resolve an address for. * @return Address associated with the given name. */ function resolve(string memory _name) public view returns (address) { return libAddressManager.getAddress(_name); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /** * @title Lib_MerkleTree * @author River Keefer */ library Lib_MerkleTree { /********************** * Internal Functions * **********************/ /** * Calculates a merkle root for a list of 32-byte leaf hashes. WARNING: If the number * of leaves passed in is not a power of two, it pads out the tree with zero hashes. * If you do not know the original length of elements for the tree you are verifying, then * this may allow empty leaves past _elements.length to pass a verification check down the line. * Note that the _elements argument is modified, therefore it must not be used again afterwards * @param _elements Array of hashes from which to generate a merkle root. * @return Merkle root of the leaves, with zero hashes for non-powers-of-two (see above). */ function getMerkleRoot(bytes32[] memory _elements) internal pure returns (bytes32) { require( _elements.length > 0, 'Lib_MerkleTree: Must provide at least one leaf hash.' ); if (_elements.length == 1) { return _elements[0]; } uint256[16] memory defaults = [ 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563, 0x633dc4d7da7256660a892f8f1604a44b5432649cc8ec5cb3ced4c4e6ac94dd1d, 0x890740a8eb06ce9be422cb8da5cdafc2b58c0a5e24036c578de2a433c828ff7d, 0x3b8ec09e026fdc305365dfc94e189a81b38c7597b3d941c279f042e8206e0bd8, 0xecd50eee38e386bd62be9bedb990706951b65fe053bd9d8a521af753d139e2da, 0xdefff6d330bb5403f63b14f33b578274160de3a50df4efecf0e0db73bcdd3da5, 0x617bdd11f7c0a11f49db22f629387a12da7596f9d1704d7465177c63d88ec7d7, 0x292c23a9aa1d8bea7e2435e555a4a60e379a5a35f3f452bae60121073fb6eead, 0xe1cea92ed99acdcb045a6726b2f87107e8a61620a232cf4d7d5b5766b3952e10, 0x7ad66c0a68c72cb89e4fb4303841966e4062a76ab97451e3b9fb526a5ceb7f82, 0xe026cc5a4aed3c22a58cbd3d2ac754c9352c5436f638042dca99034e83636516, 0x3d04cffd8b46a874edf5cfae63077de85f849a660426697b06a829c70dd1409c, 0xad676aa337a485e4728a0b240d92b3ef7b3c372d06d189322bfd5f61f1e7203e, 0xa2fca4a49658f9fab7aa63289c91b7c7b6c832a6d0e69334ff5b0a3483d09dab, 0x4ebfd9cd7bca2505f7bef59cc1c12ecc708fff26ae4af19abe852afe9e20c862, 0x2def10d13dd169f550f578bda343d9717a138562e0093b380a1120789d53cf10 ]; // Reserve memory space for our hashes. bytes memory buf = new bytes(64); // We'll need to keep track of left and right siblings. bytes32 leftSibling; bytes32 rightSibling; // Number of non-empty nodes at the current depth. uint256 rowSize = _elements.length; // Current depth, counting from 0 at the leaves uint256 depth = 0; // Common sub-expressions uint256 halfRowSize; // rowSize / 2 bool rowSizeIsOdd; // rowSize % 2 == 1 while (rowSize > 1) { halfRowSize = rowSize / 2; rowSizeIsOdd = rowSize % 2 == 1; for (uint256 i = 0; i < halfRowSize; i++) { leftSibling = _elements[(2 * i)]; rightSibling = _elements[(2 * i) + 1]; assembly { mstore(add(buf, 32), leftSibling) mstore(add(buf, 64), rightSibling) } _elements[i] = keccak256(buf); } if (rowSizeIsOdd) { leftSibling = _elements[rowSize - 1]; rightSibling = bytes32(defaults[depth]); assembly { mstore(add(buf, 32), leftSibling) mstore(add(buf, 64), rightSibling) } _elements[halfRowSize] = keccak256(buf); } rowSize = halfRowSize + (rowSizeIsOdd ? 1 : 0); depth++; } return _elements[0]; } /** * Verifies a merkle branch for the given leaf hash. Assumes the original length * of leaves generated is a known, correct input, and does not return true for indices * extending past that index (even if _siblings would be otherwise valid.) * @param _root The Merkle root to verify against. * @param _leaf The leaf hash to verify inclusion of. * @param _index The index in the tree of this leaf. * @param _siblings Array of sibline nodes in the inclusion proof, starting from depth 0 * (bottom of the tree). * @param _totalLeaves The total number of leaves originally passed into. * @return Whether or not the merkle branch and leaf passes verification. */ function verify( bytes32 _root, bytes32 _leaf, uint256 _index, bytes32[] memory _siblings, uint256 _totalLeaves ) internal pure returns (bool) { require( _totalLeaves > 0, 'Lib_MerkleTree: Total leaves must be greater than zero.' ); require(_index < _totalLeaves, 'Lib_MerkleTree: Index out of bounds.'); require( _siblings.length == _ceilLog2(_totalLeaves), 'Lib_MerkleTree: Total siblings does not correctly correspond to total leaves.' ); bytes32 computedRoot = _leaf; for (uint256 i = 0; i < _siblings.length; i++) { if ((_index & 1) == 1) { computedRoot = keccak256(abi.encodePacked(_siblings[i], computedRoot)); } else { computedRoot = keccak256(abi.encodePacked(computedRoot, _siblings[i])); } _index >>= 1; } return _root == computedRoot; } /********************* * Private Functions * *********************/ /** * Calculates the integer ceiling of the log base 2 of an input. * @param _in Unsigned input to calculate the log. * @return ceil(log_base_2(_in)) */ function _ceilLog2(uint256 _in) private pure returns (uint256) { require(_in > 0, 'Lib_MerkleTree: Cannot compute ceil(log_2) of 0.'); if (_in == 1) { return 0; } // Find the highest set bit (will be floor(log_2)). // Borrowed with <3 from https://github.com/ethereum/solidity-examples uint256 val = _in; uint256 highest = 0; for (uint256 i = 128; i >= 1; i >>= 1) { if (val & (((uint256(1) << i) - 1) << i) != 0) { highest += i; val >>= i; } } // Increment by one if this is not a perfect logarithm. if ((uint256(1) << highest) != _in) { highest += 1; } return highest; } } // SPDX-License-Identifier: MIT pragma solidity >0.5.0 <0.9.0; /* Library Imports */ import {Lib_OVMCodec} from '../../libraries/codec/Lib_OVMCodec.sol'; /** * @title IStateCommitmentChain */ interface IStateCommitmentChain { /********** * Events * **********/ event StateBatchAppended( uint256 indexed _batchIndex, bytes32 _batchRoot, uint256 _batchSize, uint256 _prevTotalElements, bytes _extraData ); event StateBatchDeleted(uint256 indexed _batchIndex, bytes32 _batchRoot); /******************** * 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); /** * Retrieves the timestamp of the last batch submitted by the sequencer. * @return _lastSequencerTimestamp Last sequencer batch timestamp. */ function getLastSequencerTimestamp() external view returns (uint256 _lastSequencerTimestamp); /** * Appends a batch of state roots to the chain. * @param _batch Batch of state roots. * @param _shouldStartAtElement Index of the element at which this batch should start. */ function appendStateBatch( bytes32[] calldata _batch, uint256 _shouldStartAtElement ) 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; /** * 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); /** * 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); } // SPDX-License-Identifier: MIT pragma solidity >0.5.0 <0.9.0; /* Library Imports */ import {Lib_OVMCodec} from '../../libraries/codec/Lib_OVMCodec.sol'; /* Interface Imports */ import {IChainStorageContainer} from './IChainStorageContainer.sol'; /** * @title ICanonicalTransactionChain */ interface ICanonicalTransactionChain { /********** * Events * **********/ event L2GasParamsUpdated( uint256 l2GasDiscountDivisor, uint256 enqueueGasCost, uint256 enqueueL2GasPrepaid ); event TransactionEnqueued( address indexed _l1TxOrigin, address indexed _target, uint256 _gasLimit, bytes _data, uint256 indexed _queueIndex, uint256 _timestamp ); event QueueBatchAppended( uint256 _startingQueueIndex, uint256 _numQueueElements, uint256 _totalElements ); event SequencerBatchAppended( uint256 _startingQueueIndex, uint256 _numQueueElements, uint256 _totalElements ); event TransactionBatchAppended( uint256 indexed _batchIndex, bytes32 _batchRoot, uint256 _batchSize, uint256 _prevTotalElements, bytes _extraData ); /*********** * Structs * ***********/ struct BatchContext { uint256 numSequencedTransactions; uint256 numSubsequentQueueTransactions; uint256 timestamp; uint256 blockNumber; } /******************************* * Authorized Setter Functions * *******************************/ /** * Allows the Burn Admin to update the parameters which determine the amount of gas to burn. * The value of enqueueL2GasPrepaid is immediately updated as well. */ function setGasParams(uint256 _l2GasDiscountDivisor, uint256 _enqueueGasCost) external; /******************** * Public Functions * ********************/ /** * Accesses the batch storage container. * @return Reference to the batch storage container. */ function batches() external view returns (IChainStorageContainer); /** * Accesses the queue storage container. * @return Reference to the queue storage container. */ function queue() external view returns (IChainStorageContainer); /** * 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); /** * Returns the index of the next element to be enqueued. * @return Index for the next queue element. */ function getNextQueueIndex() external view returns (uint40); /** * Gets the queue element at a particular index. * @param _index Index of the queue element to access. * @return _element Queue element at the given index. */ function getQueueElement(uint256 _index) external view returns (Lib_OVMCodec.QueueElement memory _element); /** * Returns the timestamp of the last transaction. * @return Timestamp for the last transaction. */ function getLastTimestamp() external view returns (uint40); /** * Returns the blocknumber of the last transaction. * @return Blocknumber for the last transaction. */ function getLastBlockNumber() external view returns (uint40); /** * Get the number of queue elements which have not yet been included. * @return Number of pending queue elements. */ function getNumPendingQueueElements() external view returns (uint40); /** * Retrieves the length of the queue, including * both pending and canonical transactions. * @return Length of the queue. */ function getQueueLength() external view returns (uint40); /** * Adds a transaction to the queue. * @param _target Target contract to send the transaction to. * @param _gasLimit Gas limit for the given transaction. * @param _data Transaction data. */ function enqueue( address _target, uint256 _gasLimit, bytes memory _data ) external; /** * Allows the sequencer to append a batch of transactions. * @dev This function uses a custom encoding scheme for efficiency reasons. * .param _shouldStartAtElement Specific batch we expect to start appending to. * .param _totalElementsToAppend Total number of batch elements we expect to append. * .param _contexts Array of batch contexts. * .param _transactionDataFields Array of raw transaction data. */ function appendSequencerBatch( // uint40 _shouldStartAtElement, // uint24 _totalElementsToAppend, // BatchContext[] _contexts, // bytes[] _transactionDataFields ) external; } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /** * @title IBondManager */ interface IBondManager { /******************** * Public Functions * ********************/ function isCollateralized(address _who) external view returns (bool); } // SPDX-License-Identifier: MIT pragma solidity >0.5.0 <0.9.0; /** * @title IChainStorageContainer */ interface IChainStorageContainer { /******************** * Public Functions * ********************/ /** * Sets the container's global metadata field. We're using `bytes27` here because we use five * bytes to maintain the length of the underlying data structure, meaning we have an extra * 27 bytes to store arbitrary data. * @param _globalMetadata New global metadata to set. */ function setGlobalMetadata(bytes27 _globalMetadata) external; /** * Retrieves the container's global metadata field. * @return Container global metadata field. */ function getGlobalMetadata() external view returns (bytes27); /** * Retrieves the number of objects stored in the container. * @return Number of objects in the container. */ function length() external view returns (uint256); /** * Pushes an object into the container. * @param _object A 32 byte value to insert into the container. */ function push(bytes32 _object) external; /** * Pushes an object into the container. Function allows setting the global metadata since * we'll need to touch the "length" storage slot anyway, which also contains the global * metadata (it's an optimization). * @param _object A 32 byte value to insert into the container. * @param _globalMetadata New global metadata for the container. */ function push(bytes32 _object, bytes27 _globalMetadata) external; /** * Retrieves an object from the container. * @param _index Index of the particular object to access. * @return 32 byte object value. */ function get(uint256 _index) external view returns (bytes32); /** * Removes all objects after and including a given index. * @param _index Object index to delete from. */ function deleteElementsAfterInclusive(uint256 _index) external; /** * Removes all objects after and including a given index. Also allows setting the global * metadata field. * @param _index Object index to delete from. * @param _globalMetadata New global metadata for the container. */ function deleteElementsAfterInclusive(uint256 _index, bytes27 _globalMetadata) external; } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /** * @title Lib_RLPReader * @dev Adapted from "RLPReader" by Hamdi Allam ([email protected]). */ library Lib_RLPReader { /************* * Constants * *************/ uint256 internal constant MAX_LIST_LENGTH = 32; /********* * Enums * *********/ enum RLPItemType { DATA_ITEM, LIST_ITEM } /*********** * Structs * ***********/ struct RLPItem { uint256 length; uint256 ptr; } /********************** * Internal Functions * **********************/ /** * Converts bytes to a reference to memory position and length. * @param _in Input bytes to convert. * @return Output memory reference. */ function toRLPItem(bytes memory _in) internal pure returns (RLPItem memory) { uint256 ptr; assembly { ptr := add(_in, 32) } return RLPItem({length: _in.length, ptr: ptr}); } /** * Reads an RLP list value into a list of RLP items. * @param _in RLP list value. * @return Decoded RLP list items. */ function readList(RLPItem memory _in) internal pure returns (RLPItem[] memory) { (uint256 listOffset, , RLPItemType itemType) = _decodeLength(_in); require(itemType == RLPItemType.LIST_ITEM, 'Invalid RLP list value.'); // Solidity in-memory arrays can't be increased in size, but *can* be decreased in size by // writing to the length. Since we can't know the number of RLP items without looping over // the entire input, we'd have to loop twice to accurately size this array. It's easier to // simply set a reasonable maximum list length and decrease the size before we finish. RLPItem[] memory out = new RLPItem[](MAX_LIST_LENGTH); uint256 itemCount = 0; uint256 offset = listOffset; while (offset < _in.length) { require( itemCount < MAX_LIST_LENGTH, 'Provided RLP list exceeds max list length.' ); (uint256 itemOffset, uint256 itemLength, ) = _decodeLength( RLPItem({length: _in.length - offset, ptr: _in.ptr + offset}) ); out[itemCount] = RLPItem({ length: itemLength + itemOffset, ptr: _in.ptr + offset }); itemCount += 1; offset += itemOffset + itemLength; } // Decrease the array size to match the actual item count. assembly { mstore(out, itemCount) } return out; } /** * Reads an RLP list value into a list of RLP items. * @param _in RLP list value. * @return Decoded RLP list items. */ function readList(bytes memory _in) internal pure returns (RLPItem[] memory) { return readList(toRLPItem(_in)); } /** * Reads an RLP bytes value into bytes. * @param _in RLP bytes value. * @return Decoded bytes. */ function readBytes(RLPItem memory _in) internal pure returns (bytes memory) { ( uint256 itemOffset, uint256 itemLength, RLPItemType itemType ) = _decodeLength(_in); require(itemType == RLPItemType.DATA_ITEM, 'Invalid RLP bytes value.'); return _copy(_in.ptr, itemOffset, itemLength); } /** * Reads an RLP bytes value into bytes. * @param _in RLP bytes value. * @return Decoded bytes. */ function readBytes(bytes memory _in) internal pure returns (bytes memory) { return readBytes(toRLPItem(_in)); } /** * Reads an RLP string value into a string. * @param _in RLP string value. * @return Decoded string. */ function readString(RLPItem memory _in) internal pure returns (string memory) { return string(readBytes(_in)); } /** * Reads an RLP string value into a string. * @param _in RLP string value. * @return Decoded string. */ function readString(bytes memory _in) internal pure returns (string memory) { return readString(toRLPItem(_in)); } /** * Reads an RLP bytes32 value into a bytes32. * @param _in RLP bytes32 value. * @return Decoded bytes32. */ function readBytes32(RLPItem memory _in) internal pure returns (bytes32) { require(_in.length <= 33, 'Invalid RLP bytes32 value.'); ( uint256 itemOffset, uint256 itemLength, RLPItemType itemType ) = _decodeLength(_in); require(itemType == RLPItemType.DATA_ITEM, 'Invalid RLP bytes32 value.'); uint256 ptr = _in.ptr + itemOffset; bytes32 out; assembly { out := mload(ptr) // Shift the bytes over to match the item size. if lt(itemLength, 32) { out := div(out, exp(256, sub(32, itemLength))) } } return out; } /** * Reads an RLP bytes32 value into a bytes32. * @param _in RLP bytes32 value. * @return Decoded bytes32. */ function readBytes32(bytes memory _in) internal pure returns (bytes32) { return readBytes32(toRLPItem(_in)); } /** * Reads an RLP uint256 value into a uint256. * @param _in RLP uint256 value. * @return Decoded uint256. */ function readUint256(RLPItem memory _in) internal pure returns (uint256) { return uint256(readBytes32(_in)); } /** * Reads an RLP uint256 value into a uint256. * @param _in RLP uint256 value. * @return Decoded uint256. */ function readUint256(bytes memory _in) internal pure returns (uint256) { return readUint256(toRLPItem(_in)); } /** * Reads an RLP bool value into a bool. * @param _in RLP bool value. * @return Decoded bool. */ function readBool(RLPItem memory _in) internal pure returns (bool) { require(_in.length == 1, 'Invalid RLP boolean value.'); uint256 ptr = _in.ptr; uint256 out; assembly { out := byte(0, mload(ptr)) } require( out == 0 || out == 1, 'Lib_RLPReader: Invalid RLP boolean value, must be 0 or 1' ); return out != 0; } /** * Reads an RLP bool value into a bool. * @param _in RLP bool value. * @return Decoded bool. */ function readBool(bytes memory _in) internal pure returns (bool) { return readBool(toRLPItem(_in)); } /** * Reads an RLP address value into a address. * @param _in RLP address value. * @return Decoded address. */ function readAddress(RLPItem memory _in) internal pure returns (address) { if (_in.length == 1) { return address(0); } require(_in.length == 21, 'Invalid RLP address value.'); return address(uint160(readUint256(_in))); } /** * Reads an RLP address value into a address. * @param _in RLP address value. * @return Decoded address. */ function readAddress(bytes memory _in) internal pure returns (address) { return readAddress(toRLPItem(_in)); } /** * Reads the raw bytes of an RLP item. * @param _in RLP item to read. * @return Raw RLP bytes. */ function readRawBytes(RLPItem memory _in) internal pure returns (bytes memory) { return _copy(_in); } /********************* * Private Functions * *********************/ /** * Decodes the length of an RLP item. * @param _in RLP item to decode. * @return Offset of the encoded data. * @return Length of the encoded data. * @return RLP item type (LIST_ITEM or DATA_ITEM). */ function _decodeLength(RLPItem memory _in) private pure returns ( uint256, uint256, RLPItemType ) { require(_in.length > 0, 'RLP item cannot be null.'); uint256 ptr = _in.ptr; uint256 prefix; assembly { prefix := byte(0, mload(ptr)) } if (prefix <= 0x7f) { // Single byte. return (0, 1, RLPItemType.DATA_ITEM); } else if (prefix <= 0xb7) { // Short string. uint256 strLen = prefix - 0x80; require(_in.length > strLen, 'Invalid RLP short string.'); return (1, strLen, RLPItemType.DATA_ITEM); } else if (prefix <= 0xbf) { // Long string. uint256 lenOfStrLen = prefix - 0xb7; require(_in.length > lenOfStrLen, 'Invalid RLP long string length.'); uint256 strLen; assembly { // Pick out the string length. strLen := div(mload(add(ptr, 1)), exp(256, sub(32, lenOfStrLen))) } require(_in.length > lenOfStrLen + strLen, 'Invalid RLP long string.'); return (1 + lenOfStrLen, strLen, RLPItemType.DATA_ITEM); } else if (prefix <= 0xf7) { // Short list. uint256 listLen = prefix - 0xc0; require(_in.length > listLen, 'Invalid RLP short list.'); return (1, listLen, RLPItemType.LIST_ITEM); } else { // Long list. uint256 lenOfListLen = prefix - 0xf7; require(_in.length > lenOfListLen, 'Invalid RLP long list length.'); uint256 listLen; assembly { // Pick out the list length. listLen := div(mload(add(ptr, 1)), exp(256, sub(32, lenOfListLen))) } require(_in.length > lenOfListLen + listLen, 'Invalid RLP long list.'); return (1 + lenOfListLen, listLen, RLPItemType.LIST_ITEM); } } /** * Copies the bytes from a memory location. * @param _src Pointer to the location to read from. * @param _offset Offset to start reading from. * @param _length Number of bytes to read. * @return Copied bytes. */ function _copy( uint256 _src, uint256 _offset, uint256 _length ) private pure returns (bytes memory) { bytes memory out = new bytes(_length); if (out.length == 0) { return out; } uint256 src = _src + _offset; uint256 dest; assembly { dest := add(out, 32) } // Copy over as many complete words as we can. for (uint256 i = 0; i < _length / 32; i++) { assembly { mstore(dest, mload(src)) } src += 32; dest += 32; } // Pick out the remaining bytes. uint256 mask; unchecked { mask = 256**(32 - (_length % 32)) - 1; } assembly { mstore(dest, or(and(mload(src), not(mask)), and(mload(dest), mask))) } return out; } /** * Copies an RLP item into bytes. * @param _in RLP item to copy. * @return Copied bytes. */ function _copy(RLPItem memory _in) private pure returns (bytes memory) { return _copy(_in.ptr, 0, _in.length); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /** * @title Lib_RLPWriter * @author Bakaoh (with modifications) */ library Lib_RLPWriter { /********************** * Internal Functions * **********************/ /** * RLP encodes a byte string. * @param _in The byte string to encode. * @return The RLP encoded string in bytes. */ function writeBytes(bytes memory _in) internal pure returns (bytes memory) { bytes memory encoded; if (_in.length == 1 && uint8(_in[0]) < 128) { encoded = _in; } else { encoded = abi.encodePacked(_writeLength(_in.length, 128), _in); } return encoded; } /** * RLP encodes a list of RLP encoded byte byte strings. * @param _in The list of RLP encoded byte strings. * @return The RLP encoded list of items in bytes. */ function writeList(bytes[] memory _in) internal pure returns (bytes memory) { bytes memory list = _flatten(_in); return abi.encodePacked(_writeLength(list.length, 192), list); } /** * RLP encodes a string. * @param _in The string to encode. * @return The RLP encoded string in bytes. */ function writeString(string memory _in) internal pure returns (bytes memory) { return writeBytes(bytes(_in)); } /** * RLP encodes an address. * @param _in The address to encode. * @return The RLP encoded address in bytes. */ function writeAddress(address _in) internal pure returns (bytes memory) { return writeBytes(abi.encodePacked(_in)); } /** * RLP encodes a uint. * @param _in The uint256 to encode. * @return The RLP encoded uint256 in bytes. */ function writeUint(uint256 _in) internal pure returns (bytes memory) { return writeBytes(_toBinary(_in)); } /** * RLP encodes a bool. * @param _in The bool to encode. * @return The RLP encoded bool in bytes. */ function writeBool(bool _in) internal pure returns (bytes memory) { bytes memory encoded = new bytes(1); encoded[0] = (_in ? bytes1(0x01) : bytes1(0x80)); return encoded; } /********************* * Private Functions * *********************/ /** * Encode the first byte, followed by the `len` in binary form if `length` is more than 55. * @param _len The length of the string or the payload. * @param _offset 128 if item is string, 192 if item is list. * @return RLP encoded bytes. */ function _writeLength(uint256 _len, uint256 _offset) private pure returns (bytes memory) { bytes memory encoded; if (_len < 56) { encoded = new bytes(1); encoded[0] = bytes1(uint8(_len) + uint8(_offset)); } else { uint256 lenLen; uint256 i = 1; while (_len / i != 0) { lenLen++; i *= 256; } encoded = new bytes(lenLen + 1); encoded[0] = bytes1(uint8(lenLen) + uint8(_offset) + 55); for (i = 1; i <= lenLen; i++) { encoded[i] = bytes1(uint8((_len / (256**(lenLen - i))) % 256)); } } return encoded; } /** * Encode integer in big endian binary form with no leading zeroes. * @notice TODO: This should be optimized with assembly to save gas costs. * @param _x The integer to encode. * @return RLP encoded bytes. */ function _toBinary(uint256 _x) private pure returns (bytes memory) { bytes memory b = abi.encodePacked(_x); uint256 i = 0; for (; i < 32; i++) { if (b[i] != 0) { break; } } bytes memory res = new bytes(32 - i); for (uint256 j = 0; j < res.length; j++) { res[j] = b[i++]; } return res; } /** * Copies a piece of memory to another location. * @notice From: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol. * @param _dest Destination location. * @param _src Source location. * @param _len Length of memory to copy. */ function _memcpy( uint256 _dest, uint256 _src, uint256 _len ) private pure { uint256 dest = _dest; uint256 src = _src; uint256 len = _len; for (; len >= 32; len -= 32) { assembly { mstore(dest, mload(src)) } dest += 32; src += 32; } uint256 mask; unchecked { mask = 256**(32 - len) - 1; } assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) mstore(dest, or(destpart, srcpart)) } } /** * Flattens a list of byte strings into one byte string. * @notice From: https://github.com/sammayo/solidity-rlp-encoder/blob/master/RLPEncode.sol. * @param _list List of byte strings to flatten. * @return The flattened byte string. */ function _flatten(bytes[] memory _list) private pure returns (bytes memory) { if (_list.length == 0) { return new bytes(0); } uint256 len; uint256 i = 0; for (; i < _list.length; i++) { len += _list[i].length; } bytes memory flattened = new bytes(len); uint256 flattenedPtr; assembly { flattenedPtr := add(flattened, 0x20) } for (i = 0; i < _list.length; i++) { bytes memory item = _list[i]; uint256 listPtr; assembly { listPtr := add(item, 0x20) } _memcpy(flattenedPtr, listPtr, item.length); flattenedPtr += _list[i].length; } return flattened; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /** * @title Lib_BytesUtils */ library Lib_BytesUtils { /********************** * Internal Functions * **********************/ function slice( bytes memory _bytes, uint256 _start, uint256 _length ) internal pure returns (bytes memory) { require(_length + 31 >= _length, 'slice_overflow'); require(_start + _length >= _start, 'slice_overflow'); require(_bytes.length >= _start + _length, 'slice_outOfBounds'); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add( add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start ) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) //zero out the 32 bytes slice we are about to return //we need to do it because Solidity does not garbage collect mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function slice(bytes memory _bytes, uint256 _start) internal pure returns (bytes memory) { if (_start >= _bytes.length) { return bytes(''); } return slice(_bytes, _start, _bytes.length - _start); } function toBytes32(bytes memory _bytes) internal pure returns (bytes32) { if (_bytes.length < 32) { bytes32 ret; assembly { ret := mload(add(_bytes, 32)) } return ret; } return abi.decode(_bytes, (bytes32)); // will truncate if input length > 32 bytes } function toUint256(bytes memory _bytes) internal pure returns (uint256) { return uint256(toBytes32(_bytes)); } function toNibbles(bytes memory _bytes) internal pure returns (bytes memory) { bytes memory nibbles = new bytes(_bytes.length * 2); for (uint256 i = 0; i < _bytes.length; i++) { nibbles[i * 2] = _bytes[i] >> 4; nibbles[i * 2 + 1] = bytes1(uint8(_bytes[i]) % 16); } return nibbles; } function fromNibbles(bytes memory _bytes) internal pure returns (bytes memory) { bytes memory ret = new bytes(_bytes.length / 2); for (uint256 i = 0; i < ret.length; i++) { ret[i] = (_bytes[i * 2] << 4) | (_bytes[i * 2 + 1]); } return ret; } function equal(bytes memory _bytes, bytes memory _other) internal pure returns (bool) { return keccak256(_bytes) == keccak256(_other); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /** * @title Lib_Byte32Utils */ library Lib_Bytes32Utils { /********************** * Internal Functions * **********************/ /** * Converts a bytes32 value to a boolean. Anything non-zero will be converted to "true." * @param _in Input bytes32 value. * @return Bytes32 as a boolean. */ function toBool(bytes32 _in) internal pure returns (bool) { return _in != 0; } /** * Converts a boolean to a bytes32 value. * @param _in Input boolean value. * @return Boolean as a bytes32. */ function fromBool(bool _in) internal pure returns (bytes32) { return bytes32(uint256(_in ? 1 : 0)); } /** * Converts a bytes32 value to an address. Takes the *last* 20 bytes. * @param _in Input bytes32 value. * @return Bytes32 as an address. */ function toAddress(bytes32 _in) internal pure returns (address) { return address(uint160(uint256(_in))); } /** * Converts an address to a bytes32. * @param _in Input address value. * @return Address as a bytes32. */ function fromAddress(address _in) internal pure returns (bytes32) { return bytes32(uint256(uint160(_in))); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* External Imports */ import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; /** * @title Lib_AddressManager */ contract Lib_AddressManager is Ownable { /********** * Events * **********/ event AddressSet( string indexed _name, address _newAddress, address _oldAddress ); /************* * Variables * *************/ mapping(bytes32 => address) private addresses; /******************** * Public Functions * ********************/ /** * Changes the address associated with a particular name. * @param _name String name to associate an address with. * @param _address Address to associate with the name. */ function setAddress(string memory _name, address _address) external onlyOwner { bytes32 nameHash = _getNameHash(_name); address oldAddress = addresses[nameHash]; addresses[nameHash] = _address; emit AddressSet(_name, _address, oldAddress); } /** * Retrieves the address associated with a given name. * @param _name Name to retrieve an address for. * @return Address associated with the given name. */ function getAddress(string memory _name) external view returns (address) { return addresses[_getNameHash(_name)]; } /********************** * Internal Functions * **********************/ /** * Computes the hash of a name. * @param _name Name to compute a hash for. * @return Hash of the given name. */ function _getNameHash(string memory _name) internal pure returns (bytes32) { return keccak256(abi.encodePacked(_name)); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _setOwner(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _setOwner(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _setOwner(newOwner); } function _setOwner(address newOwner) private { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
File 2 of 6: Lib_AddressManager
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // SPDX-License-Identifier: MIT pragma solidity >0.5.0 <0.8.0; /* External Imports */ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; /** * @title Lib_AddressManager */ contract Lib_AddressManager is Ownable { /********** * Events * **********/ event AddressSet( string indexed _name, address _newAddress, address _oldAddress ); /************* * Variables * *************/ mapping (bytes32 => address) private addresses; /******************** * Public Functions * ********************/ /** * Changes the address associated with a particular name. * @param _name String name to associate an address with. * @param _address Address to associate with the name. */ function setAddress( string memory _name, address _address ) external onlyOwner { bytes32 nameHash = _getNameHash(_name); address oldAddress = addresses[nameHash]; addresses[nameHash] = _address; emit AddressSet( _name, _address, oldAddress ); } /** * Retrieves the address associated with a given name. * @param _name Name to retrieve an address for. * @return Address associated with the given name. */ function getAddress( string memory _name ) external view returns ( address ) { return addresses[_getNameHash(_name)]; } /********************** * Internal Functions * **********************/ /** * Computes the hash of a name. * @param _name Name to compute a hash for. * @return Hash of the given name. */ function _getNameHash( string memory _name ) internal pure returns ( bytes32 ) { return keccak256(abi.encodePacked(_name)); } }
File 3 of 6: ChainStorageContainer
// SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* Library Imports */ import {Lib_Buffer} from '../../libraries/utils/Lib_Buffer.sol'; import {Lib_AddressResolver} from '../../libraries/resolver/Lib_AddressResolver.sol'; /* Interface Imports */ import {IChainStorageContainer} from './IChainStorageContainer.sol'; /** * @title ChainStorageContainer * @dev The Chain Storage Container provides its owner contract with read, write and delete * functionality. This provides gas efficiency gains by enabling it to overwrite storage slots which * can no longer be used in a fraud proof due to the fraud window having passed, and the associated * chain state or transactions being finalized. * Three distinct Chain Storage Containers will be deployed on Layer 1: * 1. Stores transaction batches for the Canonical Transaction Chain * 2. Stores queued transactions for the Canonical Transaction Chain * 3. Stores chain state batches for the State Commitment Chain * * Runtime target: EVM */ contract ChainStorageContainer is IChainStorageContainer, Lib_AddressResolver { /************* * Libraries * *************/ using Lib_Buffer for Lib_Buffer.Buffer; /************* * Variables * *************/ string public owner; Lib_Buffer.Buffer internal buffer; /*************** * Constructor * ***************/ /** * @param _libAddressManager Address of the Address Manager. * @param _owner Name of the contract that owns this container (will be resolved later). */ constructor(address _libAddressManager, string memory _owner) Lib_AddressResolver(_libAddressManager) { owner = _owner; } /********************** * Function Modifiers * **********************/ modifier onlyOwner() { require( msg.sender == resolve(owner), 'ChainStorageContainer: Function can only be called by the owner.' ); _; } /******************** * Public Functions * ********************/ /** * @inheritdoc IChainStorageContainer */ function setGlobalMetadata(bytes27 _globalMetadata) public onlyOwner { return buffer.setExtraData(_globalMetadata); } /** * @inheritdoc IChainStorageContainer */ function getGlobalMetadata() public view returns (bytes27) { return buffer.getExtraData(); } /** * @inheritdoc IChainStorageContainer */ function length() public view returns (uint256) { return uint256(buffer.getLength()); } /** * @inheritdoc IChainStorageContainer */ function push(bytes32 _object) public onlyOwner { buffer.push(_object); } /** * @inheritdoc IChainStorageContainer */ function push(bytes32 _object, bytes27 _globalMetadata) public onlyOwner { buffer.push(_object, _globalMetadata); } /** * @inheritdoc IChainStorageContainer */ function get(uint256 _index) public view returns (bytes32) { return buffer.get(uint40(_index)); } /** * @inheritdoc IChainStorageContainer */ function deleteElementsAfterInclusive(uint256 _index) public onlyOwner { buffer.deleteElementsAfterInclusive(uint40(_index)); } /** * @inheritdoc IChainStorageContainer */ function deleteElementsAfterInclusive(uint256 _index, bytes27 _globalMetadata) public onlyOwner { buffer.deleteElementsAfterInclusive(uint40(_index), _globalMetadata); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /** * @title Lib_Buffer * @dev This library implements a bytes32 storage array with some additional gas-optimized * functionality. In particular, it encodes its length as a uint40, and tightly packs this with an * overwritable "extra data" field so we can store more information with a single SSTORE. */ library Lib_Buffer { /************* * Libraries * *************/ using Lib_Buffer for Buffer; /*********** * Structs * ***********/ struct Buffer { bytes32 context; mapping(uint256 => bytes32) buf; } struct BufferContext { // Stores the length of the array. Uint40 is way more elements than we'll ever reasonably // need in an array and we get an extra 27 bytes of extra data to play with. uint40 length; // Arbitrary extra data that can be modified whenever the length is updated. Useful for // squeezing out some gas optimizations. bytes27 extraData; } /********************** * Internal Functions * **********************/ /** * Pushes a single element to the buffer. * @param _self Buffer to access. * @param _value Value to push to the buffer. * @param _extraData Global extra data. */ function push( Buffer storage _self, bytes32 _value, bytes27 _extraData ) internal { BufferContext memory ctx = _self.getContext(); _self.buf[ctx.length] = _value; // Bump the global index and insert our extra data, then save the context. ctx.length++; 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(Buffer storage _self, bytes32 _value) internal { BufferContext memory ctx = _self.getContext(); _self.push(_value, 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(Buffer storage _self, uint256 _index) internal view returns (bytes32) { BufferContext memory ctx = _self.getContext(); require(_index < ctx.length, 'Index out of bounds.'); return _self.buf[_index]; } /** * 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( Buffer storage _self, uint40 _index, bytes27 _extraData ) internal { BufferContext memory ctx = _self.getContext(); require(_index < ctx.length, 'Index out of bounds.'); // Set our length and extra data, save the context. ctx.length = _index; ctx.extraData = _extraData; _self.setContext(ctx); } /** * Deletes all elements after (and including) a given index. * @param _self Buffer to access. * @param _index Index of the element to delete from (inclusive). */ function deleteElementsAfterInclusive(Buffer storage _self, uint40 _index) internal { BufferContext memory ctx = _self.getContext(); _self.deleteElementsAfterInclusive(_index, ctx.extraData); } /** * Retrieves the current global index. * @param _self Buffer to access. * @return Current global index. */ function getLength(Buffer storage _self) internal view returns (uint40) { BufferContext memory ctx = _self.getContext(); return ctx.length; } /** * Changes current global extra data. * @param _self Buffer to access. * @param _extraData New global extra data. */ function setExtraData(Buffer storage _self, bytes27 _extraData) internal { BufferContext memory ctx = _self.getContext(); ctx.extraData = _extraData; _self.setContext(ctx); } /** * Retrieves the current global extra data. * @param _self Buffer to access. * @return Current global extra data. */ function getExtraData(Buffer storage _self) internal view returns (bytes27) { BufferContext memory ctx = _self.getContext(); return ctx.extraData; } /** * Sets the current buffer context. * @param _self Buffer to access. * @param _ctx Current buffer context. */ function setContext(Buffer storage _self, BufferContext memory _ctx) internal { bytes32 context; uint40 length = _ctx.length; bytes27 extraData = _ctx.extraData; assembly { context := length context := or(context, extraData) } if (_self.context != context) { _self.context = context; } } /** * Retrieves the current buffer context. * @param _self Buffer to access. * @return Current buffer context. */ function getContext(Buffer storage _self) internal view returns (BufferContext memory) { bytes32 context = _self.context; uint40 length; bytes27 extraData; assembly { // solhint-disable-next-line max-line-length length := and( context, 0x000000000000000000000000000000000000000000000000000000FFFFFFFFFF ) // solhint-disable-next-line max-line-length extraData := and( context, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 ) } return BufferContext({length: length, extraData: extraData}); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* Library Imports */ import {Lib_AddressManager} from './Lib_AddressManager.sol'; /** * @title Lib_AddressResolver */ abstract contract Lib_AddressResolver { /************* * Variables * *************/ Lib_AddressManager public libAddressManager; /*************** * Constructor * ***************/ /** * @param _libAddressManager Address of the Lib_AddressManager. */ constructor(address _libAddressManager) { libAddressManager = Lib_AddressManager(_libAddressManager); } /******************** * Public Functions * ********************/ /** * Resolves the address associated with a given name. * @param _name Name to resolve an address for. * @return Address associated with the given name. */ function resolve(string memory _name) public view returns (address) { return libAddressManager.getAddress(_name); } } // SPDX-License-Identifier: MIT pragma solidity >0.5.0 <0.9.0; /** * @title IChainStorageContainer */ interface IChainStorageContainer { /******************** * Public Functions * ********************/ /** * Sets the container's global metadata field. We're using `bytes27` here because we use five * bytes to maintain the length of the underlying data structure, meaning we have an extra * 27 bytes to store arbitrary data. * @param _globalMetadata New global metadata to set. */ function setGlobalMetadata(bytes27 _globalMetadata) external; /** * Retrieves the container's global metadata field. * @return Container global metadata field. */ function getGlobalMetadata() external view returns (bytes27); /** * Retrieves the number of objects stored in the container. * @return Number of objects in the container. */ function length() external view returns (uint256); /** * Pushes an object into the container. * @param _object A 32 byte value to insert into the container. */ function push(bytes32 _object) external; /** * Pushes an object into the container. Function allows setting the global metadata since * we'll need to touch the "length" storage slot anyway, which also contains the global * metadata (it's an optimization). * @param _object A 32 byte value to insert into the container. * @param _globalMetadata New global metadata for the container. */ function push(bytes32 _object, bytes27 _globalMetadata) external; /** * Retrieves an object from the container. * @param _index Index of the particular object to access. * @return 32 byte object value. */ function get(uint256 _index) external view returns (bytes32); /** * Removes all objects after and including a given index. * @param _index Object index to delete from. */ function deleteElementsAfterInclusive(uint256 _index) external; /** * Removes all objects after and including a given index. Also allows setting the global * metadata field. * @param _index Object index to delete from. * @param _globalMetadata New global metadata for the container. */ function deleteElementsAfterInclusive(uint256 _index, bytes27 _globalMetadata) external; } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* External Imports */ import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; /** * @title Lib_AddressManager */ contract Lib_AddressManager is Ownable { /********** * Events * **********/ event AddressSet( string indexed _name, address _newAddress, address _oldAddress ); /************* * Variables * *************/ mapping(bytes32 => address) private addresses; /******************** * Public Functions * ********************/ /** * Changes the address associated with a particular name. * @param _name String name to associate an address with. * @param _address Address to associate with the name. */ function setAddress(string memory _name, address _address) external onlyOwner { bytes32 nameHash = _getNameHash(_name); address oldAddress = addresses[nameHash]; addresses[nameHash] = _address; emit AddressSet(_name, _address, oldAddress); } /** * Retrieves the address associated with a given name. * @param _name Name to retrieve an address for. * @return Address associated with the given name. */ function getAddress(string memory _name) external view returns (address) { return addresses[_getNameHash(_name)]; } /********************** * Internal Functions * **********************/ /** * Computes the hash of a name. * @param _name Name to compute a hash for. * @return Hash of the given name. */ function _getNameHash(string memory _name) internal pure returns (bytes32) { return keccak256(abi.encodePacked(_name)); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _setOwner(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _setOwner(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _setOwner(newOwner); } function _setOwner(address newOwner) private { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
File 4 of 6: BondManager
// SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* Interface Imports */ import {IBondManager} from './IBondManager.sol'; /* Contract Imports */ import {Lib_AddressResolver} from '../../libraries/resolver/Lib_AddressResolver.sol'; /** * @title BondManager * @dev This contract is, for now, a stub of the "real" BondManager that does nothing but * allow the "OVM_Proposer" to submit state root batches. * * Runtime target: EVM */ contract BondManager is IBondManager, Lib_AddressResolver { /** * @param _libAddressManager Address of the Address Manager. */ constructor(address _libAddressManager) Lib_AddressResolver(_libAddressManager) {} /** * Checks whether a given address is properly collateralized and can perform actions within * the system. * @param _who Address to check. * @return true if the address is properly collateralized, false otherwise. */ function isCollateralized(address _who) public view returns (bool) { // Only authenticate sequencer to submit state root batches. return _who == resolve('OVM_Proposer'); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /** * @title IBondManager */ interface IBondManager { /******************** * Public Functions * ********************/ function isCollateralized(address _who) external view returns (bool); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* Library Imports */ import {Lib_AddressManager} from './Lib_AddressManager.sol'; /** * @title Lib_AddressResolver */ abstract contract Lib_AddressResolver { /************* * Variables * *************/ Lib_AddressManager public libAddressManager; /*************** * Constructor * ***************/ /** * @param _libAddressManager Address of the Lib_AddressManager. */ constructor(address _libAddressManager) { libAddressManager = Lib_AddressManager(_libAddressManager); } /******************** * Public Functions * ********************/ /** * Resolves the address associated with a given name. * @param _name Name to resolve an address for. * @return Address associated with the given name. */ function resolve(string memory _name) public view returns (address) { return libAddressManager.getAddress(_name); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* External Imports */ import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; /** * @title Lib_AddressManager */ contract Lib_AddressManager is Ownable { /********** * Events * **********/ event AddressSet( string indexed _name, address _newAddress, address _oldAddress ); /************* * Variables * *************/ mapping(bytes32 => address) private addresses; /******************** * Public Functions * ********************/ /** * Changes the address associated with a particular name. * @param _name String name to associate an address with. * @param _address Address to associate with the name. */ function setAddress(string memory _name, address _address) external onlyOwner { bytes32 nameHash = _getNameHash(_name); address oldAddress = addresses[nameHash]; addresses[nameHash] = _address; emit AddressSet(_name, _address, oldAddress); } /** * Retrieves the address associated with a given name. * @param _name Name to retrieve an address for. * @return Address associated with the given name. */ function getAddress(string memory _name) external view returns (address) { return addresses[_getNameHash(_name)]; } /********************** * Internal Functions * **********************/ /** * Computes the hash of a name. * @param _name Name to compute a hash for. * @return Hash of the given name. */ function _getNameHash(string memory _name) internal pure returns (bytes32) { return keccak256(abi.encodePacked(_name)); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _setOwner(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _setOwner(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _setOwner(newOwner); } function _setOwner(address newOwner) private { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
File 5 of 6: CanonicalTransactionChain
// SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* Library Imports */ import {AddressAliasHelper} from '../../standards/AddressAliasHelper.sol'; import {Lib_OVMCodec} from '../../libraries/codec/Lib_OVMCodec.sol'; import {Lib_AddressResolver} from '../../libraries/resolver/Lib_AddressResolver.sol'; /* Interface Imports */ import {ICanonicalTransactionChain} from './ICanonicalTransactionChain.sol'; import {IChainStorageContainer} from './IChainStorageContainer.sol'; /** * @title CanonicalTransactionChain * @dev The Canonical Transaction Chain (CTC) contract is an append-only log of transactions * which must be applied to the rollup state. It defines the ordering of rollup transactions by * writing them to the 'CTC:batches' instance of the Chain Storage Container. * The CTC also allows any account to 'enqueue' an L2 transaction, which will require that the * Sequencer will eventually append it to the rollup state. * * Runtime target: EVM */ contract CanonicalTransactionChain is ICanonicalTransactionChain, Lib_AddressResolver { /************* * Constants * *************/ // L2 tx gas-related uint256 public constant MIN_ROLLUP_TX_GAS = 100000; uint256 public constant MAX_ROLLUP_TX_SIZE = 50000; // The approximate cost of calling the enqueue function uint256 public enqueueGasCost; // The ratio of the cost of L1 gas to the cost of L2 gas uint256 public l2GasDiscountDivisor; // The amount of L2 gas which can be forwarded to L2 without spam prevention via 'gas burn'. // Calculated as the product of l2GasDiscountDivisor * enqueueGasCost. // See comments in enqueue() for further detail. uint256 public enqueueL2GasPrepaid; // Encoding-related (all in bytes) uint256 internal constant BATCH_CONTEXT_SIZE = 16; uint256 internal constant BATCH_CONTEXT_LENGTH_POS = 12; uint256 internal constant BATCH_CONTEXT_START_POS = 15; uint256 internal constant TX_DATA_HEADER_SIZE = 3; uint256 internal constant BYTES_TILL_TX_DATA = 65; /************* * Variables * *************/ uint256 public maxTransactionGasLimit; /*************** * Queue State * ***************/ uint40 private _nextQueueIndex; // index of the first queue element not yet included Lib_OVMCodec.QueueElement[] queueElements; /*************** * Constructor * ***************/ constructor( address _libAddressManager, uint256 _maxTransactionGasLimit, uint256 _l2GasDiscountDivisor, uint256 _enqueueGasCost ) Lib_AddressResolver(_libAddressManager) { maxTransactionGasLimit = _maxTransactionGasLimit; l2GasDiscountDivisor = _l2GasDiscountDivisor; enqueueGasCost = _enqueueGasCost; enqueueL2GasPrepaid = _l2GasDiscountDivisor * _enqueueGasCost; } /********************** * Function Modifiers * **********************/ /** * Modifier to enforce that, if configured, only the Burn Admin may * successfully call a method. */ modifier onlyBurnAdmin() { require( msg.sender == libAddressManager.owner(), 'Only callable by the Burn Admin.' ); _; } /******************************* * Authorized Setter Functions * *******************************/ /** * Allows the Burn Admin to update the parameters which determine the amount of gas to burn. * The value of enqueueL2GasPrepaid is immediately updated as well. */ function setGasParams(uint256 _l2GasDiscountDivisor, uint256 _enqueueGasCost) external onlyBurnAdmin { enqueueGasCost = _enqueueGasCost; l2GasDiscountDivisor = _l2GasDiscountDivisor; // See the comment in enqueue() for the rationale behind this formula. enqueueL2GasPrepaid = _l2GasDiscountDivisor * _enqueueGasCost; emit L2GasParamsUpdated( l2GasDiscountDivisor, enqueueGasCost, enqueueL2GasPrepaid ); } /******************** * Public Functions * ********************/ /** * Accesses the batch storage container. * @return Reference to the batch storage container. */ function batches() public view returns (IChainStorageContainer) { return IChainStorageContainer(resolve('ChainStorageContainer-CTC-batches')); } /** * Accesses the queue storage container. * @return Reference to the queue storage container. */ function queue() public view returns (IChainStorageContainer) { return IChainStorageContainer(resolve('ChainStorageContainer-CTC-queue')); } /** * Retrieves the total number of elements submitted. * @return _totalElements Total submitted elements. */ function getTotalElements() public view returns (uint256 _totalElements) { (uint40 totalElements, , , ) = _getBatchExtraData(); return uint256(totalElements); } /** * Retrieves the total number of batches submitted. * @return _totalBatches Total submitted batches. */ function getTotalBatches() public view returns (uint256 _totalBatches) { return batches().length(); } /** * Returns the index of the next element to be enqueued. * @return Index for the next queue element. */ function getNextQueueIndex() public view returns (uint40) { return _nextQueueIndex; } /** * Returns the timestamp of the last transaction. * @return Timestamp for the last transaction. */ function getLastTimestamp() public view returns (uint40) { (, , uint40 lastTimestamp, ) = _getBatchExtraData(); return lastTimestamp; } /** * Returns the blocknumber of the last transaction. * @return Blocknumber for the last transaction. */ function getLastBlockNumber() public view returns (uint40) { (, , , uint40 lastBlockNumber) = _getBatchExtraData(); return lastBlockNumber; } /** * Gets the queue element at a particular index. * @param _index Index of the queue element to access. * @return _element Queue element at the given index. */ function getQueueElement(uint256 _index) public view returns (Lib_OVMCodec.QueueElement memory _element) { return queueElements[_index]; } /** * Get the number of queue elements which have not yet been included. * @return Number of pending queue elements. */ function getNumPendingQueueElements() public view returns (uint40) { return uint40(queueElements.length) - _nextQueueIndex; } /** * Retrieves the length of the queue, including * both pending and canonical transactions. * @return Length of the queue. */ function getQueueLength() public view returns (uint40) { return uint40(queueElements.length); } /** * Adds a transaction to the queue. * @param _target Target L2 contract to send the transaction to. * @param _gasLimit Gas limit for the enqueued L2 transaction. * @param _data Transaction data. */ function enqueue( address _target, uint256 _gasLimit, bytes memory _data ) external { require( _data.length <= MAX_ROLLUP_TX_SIZE, 'Transaction data size exceeds maximum for rollup transaction.' ); require( _gasLimit <= maxTransactionGasLimit, 'Transaction gas limit exceeds maximum for rollup transaction.' ); require( _gasLimit >= MIN_ROLLUP_TX_GAS, 'Transaction gas limit too low to enqueue.' ); // Transactions submitted to the queue lack a method for paying gas fees to the Sequencer. // So we need to prevent spam attacks by ensuring that the cost of enqueueing a transaction // from L1 to L2 is not underpriced. For transaction with a high L2 gas limit, we do this by // burning some extra gas on L1. Of course there is also some intrinsic cost to enqueueing a // transaction, so we want to make sure not to over-charge (by burning too much L1 gas). // Therefore, we define 'enqueueL2GasPrepaid' as the L2 gas limit above which we must burn // additional gas on L1. This threshold is the product of two inputs: // 1. enqueueGasCost: the base cost of calling this function. // 2. l2GasDiscountDivisor: the ratio between the cost of gas on L1 and L2. This is a // positive integer, meaning we assume L2 gas is always less costly. // The calculation below for gasToConsume can be seen as converting the difference (between // the specified L2 gas limit and the prepaid L2 gas limit) to an L1 gas amount. if (_gasLimit > enqueueL2GasPrepaid) { uint256 gasToConsume = (_gasLimit - enqueueL2GasPrepaid) / l2GasDiscountDivisor; uint256 startingGas = gasleft(); // Although this check is not necessary (burn below will run out of gas if not true), it // gives the user an explicit reason as to why the enqueue attempt failed. require( startingGas > gasToConsume, 'Insufficient gas for L2 rate limiting burn.' ); uint256 i; while (startingGas - gasleft() < gasToConsume) { i++; } } // Apply an aliasing unless msg.sender == tx.origin. This prevents an attack in which a // contract on L1 has the same address as a contract on L2 but doesn't have the same code. // We can safely ignore this for EOAs because they're guaranteed to have the same "code" // (i.e. no code at all). This also makes it possible for users to interact with contracts // on L2 even when the Sequencer is down. address sender; if (msg.sender == tx.origin) { sender = msg.sender; } else { sender = AddressAliasHelper.applyL1ToL2Alias(msg.sender); } bytes32 transactionHash = keccak256( abi.encode(sender, _target, _gasLimit, _data) ); queueElements.push( Lib_OVMCodec.QueueElement({ transactionHash: transactionHash, timestamp: uint40(block.timestamp), blockNumber: uint40(block.number) }) ); uint256 queueIndex = queueElements.length - 1; emit TransactionEnqueued( sender, _target, _gasLimit, _data, queueIndex, block.timestamp ); } /** * Allows the sequencer to append a batch of transactions. * @dev This function uses a custom encoding scheme for efficiency reasons. * .param _shouldStartAtElement Specific batch we expect to start appending to. * .param _totalElementsToAppend Total number of batch elements we expect to append. * .param _contexts Array of batch contexts. * .param _transactionDataFields Array of raw transaction data. */ function appendSequencerBatch() external { uint40 shouldStartAtElement; uint24 totalElementsToAppend; uint24 numContexts; assembly { shouldStartAtElement := shr(216, calldataload(4)) totalElementsToAppend := shr(232, calldataload(9)) numContexts := shr(232, calldataload(12)) } require( shouldStartAtElement == getTotalElements(), 'Actual batch start index does not match expected start index.' ); require( msg.sender == resolve('OVM_Sequencer'), 'Function can only be called by the Sequencer.' ); uint40 nextTransactionPtr = uint40( BATCH_CONTEXT_START_POS + BATCH_CONTEXT_SIZE * numContexts ); require( msg.data.length >= nextTransactionPtr, 'Not enough BatchContexts provided.' ); // Counter for number of sequencer transactions appended so far. uint32 numSequencerTransactions = 0; // Cache the _nextQueueIndex storage variable to a temporary stack variable. // This is safe as long as nothing reads or writes to the storage variable // until it is updated by the temp variable. uint40 nextQueueIndex = _nextQueueIndex; BatchContext memory curContext; for (uint32 i = 0; i < numContexts; i++) { BatchContext memory nextContext = _getBatchContext(i); // Now we can update our current context. curContext = nextContext; // Process sequencer transactions first. numSequencerTransactions += uint32(curContext.numSequencedTransactions); // Now process any subsequent queue transactions. nextQueueIndex += uint40(curContext.numSubsequentQueueTransactions); } require( nextQueueIndex <= queueElements.length, 'Attempted to append more elements than are available in the queue.' ); // Generate the required metadata that we need to append this batch uint40 numQueuedTransactions = totalElementsToAppend - numSequencerTransactions; uint40 blockTimestamp; uint40 blockNumber; if (curContext.numSubsequentQueueTransactions == 0) { // The last element is a sequencer tx, therefore pull timestamp and block number from // the last context. blockTimestamp = uint40(curContext.timestamp); blockNumber = uint40(curContext.blockNumber); } else { // The last element is a queue tx, therefore pull timestamp and block number from the // queue element. // curContext.numSubsequentQueueTransactions > 0 which means that we've processed at // least one queue element. We increment nextQueueIndex after processing each queue // element, so the index of the last element we processed is nextQueueIndex - 1. Lib_OVMCodec.QueueElement memory lastElement = queueElements[ nextQueueIndex - 1 ]; blockTimestamp = lastElement.timestamp; blockNumber = lastElement.blockNumber; } // Cache the previous blockhash to ensure all transaction data can be retrieved efficiently. _appendBatch( blockhash(block.number - 1), totalElementsToAppend, numQueuedTransactions, blockTimestamp, blockNumber ); emit SequencerBatchAppended( nextQueueIndex - numQueuedTransactions, numQueuedTransactions, getTotalElements() ); // Update the _nextQueueIndex storage variable. _nextQueueIndex = nextQueueIndex; } /********************** * Internal Functions * **********************/ /** * Returns the BatchContext located at a particular index. * @param _index The index of the BatchContext * @return The BatchContext at the specified index. */ function _getBatchContext(uint256 _index) internal pure returns (BatchContext memory) { uint256 contextPtr = 15 + _index * BATCH_CONTEXT_SIZE; uint256 numSequencedTransactions; uint256 numSubsequentQueueTransactions; uint256 ctxTimestamp; uint256 ctxBlockNumber; assembly { numSequencedTransactions := shr(232, calldataload(contextPtr)) numSubsequentQueueTransactions := shr( 232, calldataload(add(contextPtr, 3)) ) ctxTimestamp := shr(216, calldataload(add(contextPtr, 6))) ctxBlockNumber := shr(216, calldataload(add(contextPtr, 11))) } return BatchContext({ numSequencedTransactions: numSequencedTransactions, numSubsequentQueueTransactions: numSubsequentQueueTransactions, timestamp: ctxTimestamp, blockNumber: ctxBlockNumber }); } /** * Parses the batch context from the extra data. * @return Total number of elements submitted. * @return Index of the next queue element. */ function _getBatchExtraData() internal view returns ( uint40, uint40, uint40, uint40 ) { bytes27 extraData = batches().getGlobalMetadata(); uint40 totalElements; uint40 nextQueueIndex; uint40 lastTimestamp; uint40 lastBlockNumber; // solhint-disable max-line-length assembly { extraData := shr(40, extraData) totalElements := and( extraData, 0x000000000000000000000000000000000000000000000000000000FFFFFFFFFF ) nextQueueIndex := shr( 40, and( extraData, 0x00000000000000000000000000000000000000000000FFFFFFFFFF0000000000 ) ) lastTimestamp := shr( 80, and( extraData, 0x0000000000000000000000000000000000FFFFFFFFFF00000000000000000000 ) ) lastBlockNumber := shr( 120, and( extraData, 0x000000000000000000000000FFFFFFFFFF000000000000000000000000000000 ) ) } // solhint-enable max-line-length return (totalElements, nextQueueIndex, lastTimestamp, lastBlockNumber); } /** * Encodes the batch context for the extra data. * @param _totalElements Total number of elements submitted. * @param _nextQueueIdx Index of the next queue element. * @param _timestamp Timestamp for the last batch. * @param _blockNumber Block number of the last batch. * @return Encoded batch context. */ function _makeBatchExtraData( uint40 _totalElements, uint40 _nextQueueIdx, uint40 _timestamp, uint40 _blockNumber ) internal pure returns (bytes27) { bytes27 extraData; assembly { extraData := _totalElements extraData := or(extraData, shl(40, _nextQueueIdx)) extraData := or(extraData, shl(80, _timestamp)) extraData := or(extraData, shl(120, _blockNumber)) extraData := shl(40, extraData) } return extraData; } /** * Inserts a batch into the chain of batches. * @param _transactionRoot Root of the transaction tree for this batch. * @param _batchSize Number of elements in the batch. * @param _numQueuedTransactions Number of queue transactions in the batch. * @param _timestamp The latest batch timestamp. * @param _blockNumber The latest batch blockNumber. */ function _appendBatch( bytes32 _transactionRoot, uint256 _batchSize, uint256 _numQueuedTransactions, uint40 _timestamp, uint40 _blockNumber ) internal { IChainStorageContainer batchesRef = batches(); (uint40 totalElements, uint40 nextQueueIndex, , ) = _getBatchExtraData(); Lib_OVMCodec.ChainBatchHeader memory header = Lib_OVMCodec .ChainBatchHeader({ batchIndex: batchesRef.length(), batchRoot: _transactionRoot, batchSize: _batchSize, prevTotalElements: totalElements, extraData: hex'' }); emit TransactionBatchAppended( header.batchIndex, header.batchRoot, header.batchSize, header.prevTotalElements, header.extraData ); bytes32 batchHeaderHash = Lib_OVMCodec.hashBatchHeader(header); bytes27 latestBatchContext = _makeBatchExtraData( totalElements + uint40(header.batchSize), nextQueueIndex + uint40(_numQueuedTransactions), _timestamp, _blockNumber ); batchesRef.push(batchHeaderHash, latestBatchContext); } } // SPDX-License-Identifier: Apache-2.0 /* * Copyright 2019-2021, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pragma solidity ^0.8.7; library AddressAliasHelper { uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); /// @notice Utility function that converts the address in the L1 that submitted a tx to /// the inbox to the msg.sender viewed in the L2 /// @param l1Address the address in the L1 that triggered the tx to L2 /// @return l2Address L2 address as viewed in msg.sender function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { unchecked { l2Address = address(uint160(l1Address) + offset); } } /// @notice Utility function that converts the msg.sender viewed in the L2 to the /// address in the L1 that submitted a tx to the inbox /// @param l2Address L2 address as viewed in msg.sender /// @return l1Address the address in the L1 that triggered the tx to L2 function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { unchecked { l1Address = address(uint160(l2Address) - offset); } } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* Library Imports */ import {Lib_RLPReader} from '../rlp/Lib_RLPReader.sol'; import {Lib_RLPWriter} from '../rlp/Lib_RLPWriter.sol'; import {Lib_BytesUtils} from '../utils/Lib_BytesUtils.sol'; import {Lib_Bytes32Utils} from '../utils/Lib_Bytes32Utils.sol'; /** * @title Lib_OVMCodec */ library Lib_OVMCodec { /********* * Enums * *********/ enum QueueOrigin { SEQUENCER_QUEUE, L1TOL2_QUEUE } /*********** * Structs * ***********/ struct EVMAccount { uint256 nonce; uint256 balance; bytes32 storageRoot; bytes32 codeHash; } struct ChainBatchHeader { uint256 batchIndex; bytes32 batchRoot; uint256 batchSize; uint256 prevTotalElements; bytes extraData; } struct ChainInclusionProof { uint256 index; bytes32[] siblings; } struct Transaction { uint256 timestamp; uint256 blockNumber; QueueOrigin l1QueueOrigin; address l1TxOrigin; address entrypoint; uint256 gasLimit; 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 { bytes32 transactionHash; uint40 timestamp; uint40 blockNumber; } /********************** * Internal Functions * **********************/ /** * Encodes a standard OVM transaction. * @param _transaction OVM transaction to encode. * @return Encoded transaction bytes. */ function encodeTransaction(Transaction memory _transaction) internal pure returns (bytes memory) { return abi.encodePacked( _transaction.timestamp, _transaction.blockNumber, _transaction.l1QueueOrigin, _transaction.l1TxOrigin, _transaction.entrypoint, _transaction.gasLimit, _transaction.data ); } /** * Hashes a standard OVM transaction. * @param _transaction OVM transaction to encode. * @return Hashed transaction */ function hashTransaction(Transaction memory _transaction) internal pure returns (bytes32) { return keccak256(encodeTransaction(_transaction)); } /** * @notice Decodes an RLP-encoded account state into a useful struct. * @param _encoded RLP-encoded account state. * @return Account state struct. */ function decodeEVMAccount(bytes memory _encoded) internal pure returns (EVMAccount memory) { Lib_RLPReader.RLPItem[] memory accountState = Lib_RLPReader.readList( _encoded ); return EVMAccount({ nonce: Lib_RLPReader.readUint256(accountState[0]), balance: Lib_RLPReader.readUint256(accountState[1]), storageRoot: Lib_RLPReader.readBytes32(accountState[2]), codeHash: Lib_RLPReader.readBytes32(accountState[3]) }); } /** * Calculates a hash for a given batch header. * @param _batchHeader Header to hash. * @return Hash of the header. */ function hashBatchHeader(Lib_OVMCodec.ChainBatchHeader memory _batchHeader) internal pure returns (bytes32) { return keccak256( abi.encode( _batchHeader.batchRoot, _batchHeader.batchSize, _batchHeader.prevTotalElements, _batchHeader.extraData ) ); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* Library Imports */ import {Lib_AddressManager} from './Lib_AddressManager.sol'; /** * @title Lib_AddressResolver */ abstract contract Lib_AddressResolver { /************* * Variables * *************/ Lib_AddressManager public libAddressManager; /*************** * Constructor * ***************/ /** * @param _libAddressManager Address of the Lib_AddressManager. */ constructor(address _libAddressManager) { libAddressManager = Lib_AddressManager(_libAddressManager); } /******************** * Public Functions * ********************/ /** * Resolves the address associated with a given name. * @param _name Name to resolve an address for. * @return Address associated with the given name. */ function resolve(string memory _name) public view returns (address) { return libAddressManager.getAddress(_name); } } // SPDX-License-Identifier: MIT pragma solidity >0.5.0 <0.9.0; /* Library Imports */ import {Lib_OVMCodec} from '../../libraries/codec/Lib_OVMCodec.sol'; /* Interface Imports */ import {IChainStorageContainer} from './IChainStorageContainer.sol'; /** * @title ICanonicalTransactionChain */ interface ICanonicalTransactionChain { /********** * Events * **********/ event L2GasParamsUpdated( uint256 l2GasDiscountDivisor, uint256 enqueueGasCost, uint256 enqueueL2GasPrepaid ); event TransactionEnqueued( address indexed _l1TxOrigin, address indexed _target, uint256 _gasLimit, bytes _data, uint256 indexed _queueIndex, uint256 _timestamp ); event QueueBatchAppended( uint256 _startingQueueIndex, uint256 _numQueueElements, uint256 _totalElements ); event SequencerBatchAppended( uint256 _startingQueueIndex, uint256 _numQueueElements, uint256 _totalElements ); event TransactionBatchAppended( uint256 indexed _batchIndex, bytes32 _batchRoot, uint256 _batchSize, uint256 _prevTotalElements, bytes _extraData ); /*********** * Structs * ***********/ struct BatchContext { uint256 numSequencedTransactions; uint256 numSubsequentQueueTransactions; uint256 timestamp; uint256 blockNumber; } /******************************* * Authorized Setter Functions * *******************************/ /** * Allows the Burn Admin to update the parameters which determine the amount of gas to burn. * The value of enqueueL2GasPrepaid is immediately updated as well. */ function setGasParams(uint256 _l2GasDiscountDivisor, uint256 _enqueueGasCost) external; /******************** * Public Functions * ********************/ /** * Accesses the batch storage container. * @return Reference to the batch storage container. */ function batches() external view returns (IChainStorageContainer); /** * Accesses the queue storage container. * @return Reference to the queue storage container. */ function queue() external view returns (IChainStorageContainer); /** * 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); /** * Returns the index of the next element to be enqueued. * @return Index for the next queue element. */ function getNextQueueIndex() external view returns (uint40); /** * Gets the queue element at a particular index. * @param _index Index of the queue element to access. * @return _element Queue element at the given index. */ function getQueueElement(uint256 _index) external view returns (Lib_OVMCodec.QueueElement memory _element); /** * Returns the timestamp of the last transaction. * @return Timestamp for the last transaction. */ function getLastTimestamp() external view returns (uint40); /** * Returns the blocknumber of the last transaction. * @return Blocknumber for the last transaction. */ function getLastBlockNumber() external view returns (uint40); /** * Get the number of queue elements which have not yet been included. * @return Number of pending queue elements. */ function getNumPendingQueueElements() external view returns (uint40); /** * Retrieves the length of the queue, including * both pending and canonical transactions. * @return Length of the queue. */ function getQueueLength() external view returns (uint40); /** * Adds a transaction to the queue. * @param _target Target contract to send the transaction to. * @param _gasLimit Gas limit for the given transaction. * @param _data Transaction data. */ function enqueue( address _target, uint256 _gasLimit, bytes memory _data ) external; /** * Allows the sequencer to append a batch of transactions. * @dev This function uses a custom encoding scheme for efficiency reasons. * .param _shouldStartAtElement Specific batch we expect to start appending to. * .param _totalElementsToAppend Total number of batch elements we expect to append. * .param _contexts Array of batch contexts. * .param _transactionDataFields Array of raw transaction data. */ function appendSequencerBatch( // uint40 _shouldStartAtElement, // uint24 _totalElementsToAppend, // BatchContext[] _contexts, // bytes[] _transactionDataFields ) external; } // SPDX-License-Identifier: MIT pragma solidity >0.5.0 <0.9.0; /** * @title IChainStorageContainer */ interface IChainStorageContainer { /******************** * Public Functions * ********************/ /** * Sets the container's global metadata field. We're using `bytes27` here because we use five * bytes to maintain the length of the underlying data structure, meaning we have an extra * 27 bytes to store arbitrary data. * @param _globalMetadata New global metadata to set. */ function setGlobalMetadata(bytes27 _globalMetadata) external; /** * Retrieves the container's global metadata field. * @return Container global metadata field. */ function getGlobalMetadata() external view returns (bytes27); /** * Retrieves the number of objects stored in the container. * @return Number of objects in the container. */ function length() external view returns (uint256); /** * Pushes an object into the container. * @param _object A 32 byte value to insert into the container. */ function push(bytes32 _object) external; /** * Pushes an object into the container. Function allows setting the global metadata since * we'll need to touch the "length" storage slot anyway, which also contains the global * metadata (it's an optimization). * @param _object A 32 byte value to insert into the container. * @param _globalMetadata New global metadata for the container. */ function push(bytes32 _object, bytes27 _globalMetadata) external; /** * Retrieves an object from the container. * @param _index Index of the particular object to access. * @return 32 byte object value. */ function get(uint256 _index) external view returns (bytes32); /** * Removes all objects after and including a given index. * @param _index Object index to delete from. */ function deleteElementsAfterInclusive(uint256 _index) external; /** * Removes all objects after and including a given index. Also allows setting the global * metadata field. * @param _index Object index to delete from. * @param _globalMetadata New global metadata for the container. */ function deleteElementsAfterInclusive(uint256 _index, bytes27 _globalMetadata) external; } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /** * @title Lib_RLPReader * @dev Adapted from "RLPReader" by Hamdi Allam ([email protected]). */ library Lib_RLPReader { /************* * Constants * *************/ uint256 internal constant MAX_LIST_LENGTH = 32; /********* * Enums * *********/ enum RLPItemType { DATA_ITEM, LIST_ITEM } /*********** * Structs * ***********/ struct RLPItem { uint256 length; uint256 ptr; } /********************** * Internal Functions * **********************/ /** * Converts bytes to a reference to memory position and length. * @param _in Input bytes to convert. * @return Output memory reference. */ function toRLPItem(bytes memory _in) internal pure returns (RLPItem memory) { uint256 ptr; assembly { ptr := add(_in, 32) } return RLPItem({length: _in.length, ptr: ptr}); } /** * Reads an RLP list value into a list of RLP items. * @param _in RLP list value. * @return Decoded RLP list items. */ function readList(RLPItem memory _in) internal pure returns (RLPItem[] memory) { (uint256 listOffset, , RLPItemType itemType) = _decodeLength(_in); require(itemType == RLPItemType.LIST_ITEM, 'Invalid RLP list value.'); // Solidity in-memory arrays can't be increased in size, but *can* be decreased in size by // writing to the length. Since we can't know the number of RLP items without looping over // the entire input, we'd have to loop twice to accurately size this array. It's easier to // simply set a reasonable maximum list length and decrease the size before we finish. RLPItem[] memory out = new RLPItem[](MAX_LIST_LENGTH); uint256 itemCount = 0; uint256 offset = listOffset; while (offset < _in.length) { require( itemCount < MAX_LIST_LENGTH, 'Provided RLP list exceeds max list length.' ); (uint256 itemOffset, uint256 itemLength, ) = _decodeLength( RLPItem({length: _in.length - offset, ptr: _in.ptr + offset}) ); out[itemCount] = RLPItem({ length: itemLength + itemOffset, ptr: _in.ptr + offset }); itemCount += 1; offset += itemOffset + itemLength; } // Decrease the array size to match the actual item count. assembly { mstore(out, itemCount) } return out; } /** * Reads an RLP list value into a list of RLP items. * @param _in RLP list value. * @return Decoded RLP list items. */ function readList(bytes memory _in) internal pure returns (RLPItem[] memory) { return readList(toRLPItem(_in)); } /** * Reads an RLP bytes value into bytes. * @param _in RLP bytes value. * @return Decoded bytes. */ function readBytes(RLPItem memory _in) internal pure returns (bytes memory) { ( uint256 itemOffset, uint256 itemLength, RLPItemType itemType ) = _decodeLength(_in); require(itemType == RLPItemType.DATA_ITEM, 'Invalid RLP bytes value.'); return _copy(_in.ptr, itemOffset, itemLength); } /** * Reads an RLP bytes value into bytes. * @param _in RLP bytes value. * @return Decoded bytes. */ function readBytes(bytes memory _in) internal pure returns (bytes memory) { return readBytes(toRLPItem(_in)); } /** * Reads an RLP string value into a string. * @param _in RLP string value. * @return Decoded string. */ function readString(RLPItem memory _in) internal pure returns (string memory) { return string(readBytes(_in)); } /** * Reads an RLP string value into a string. * @param _in RLP string value. * @return Decoded string. */ function readString(bytes memory _in) internal pure returns (string memory) { return readString(toRLPItem(_in)); } /** * Reads an RLP bytes32 value into a bytes32. * @param _in RLP bytes32 value. * @return Decoded bytes32. */ function readBytes32(RLPItem memory _in) internal pure returns (bytes32) { require(_in.length <= 33, 'Invalid RLP bytes32 value.'); ( uint256 itemOffset, uint256 itemLength, RLPItemType itemType ) = _decodeLength(_in); require(itemType == RLPItemType.DATA_ITEM, 'Invalid RLP bytes32 value.'); uint256 ptr = _in.ptr + itemOffset; bytes32 out; assembly { out := mload(ptr) // Shift the bytes over to match the item size. if lt(itemLength, 32) { out := div(out, exp(256, sub(32, itemLength))) } } return out; } /** * Reads an RLP bytes32 value into a bytes32. * @param _in RLP bytes32 value. * @return Decoded bytes32. */ function readBytes32(bytes memory _in) internal pure returns (bytes32) { return readBytes32(toRLPItem(_in)); } /** * Reads an RLP uint256 value into a uint256. * @param _in RLP uint256 value. * @return Decoded uint256. */ function readUint256(RLPItem memory _in) internal pure returns (uint256) { return uint256(readBytes32(_in)); } /** * Reads an RLP uint256 value into a uint256. * @param _in RLP uint256 value. * @return Decoded uint256. */ function readUint256(bytes memory _in) internal pure returns (uint256) { return readUint256(toRLPItem(_in)); } /** * Reads an RLP bool value into a bool. * @param _in RLP bool value. * @return Decoded bool. */ function readBool(RLPItem memory _in) internal pure returns (bool) { require(_in.length == 1, 'Invalid RLP boolean value.'); uint256 ptr = _in.ptr; uint256 out; assembly { out := byte(0, mload(ptr)) } require( out == 0 || out == 1, 'Lib_RLPReader: Invalid RLP boolean value, must be 0 or 1' ); return out != 0; } /** * Reads an RLP bool value into a bool. * @param _in RLP bool value. * @return Decoded bool. */ function readBool(bytes memory _in) internal pure returns (bool) { return readBool(toRLPItem(_in)); } /** * Reads an RLP address value into a address. * @param _in RLP address value. * @return Decoded address. */ function readAddress(RLPItem memory _in) internal pure returns (address) { if (_in.length == 1) { return address(0); } require(_in.length == 21, 'Invalid RLP address value.'); return address(uint160(readUint256(_in))); } /** * Reads an RLP address value into a address. * @param _in RLP address value. * @return Decoded address. */ function readAddress(bytes memory _in) internal pure returns (address) { return readAddress(toRLPItem(_in)); } /** * Reads the raw bytes of an RLP item. * @param _in RLP item to read. * @return Raw RLP bytes. */ function readRawBytes(RLPItem memory _in) internal pure returns (bytes memory) { return _copy(_in); } /********************* * Private Functions * *********************/ /** * Decodes the length of an RLP item. * @param _in RLP item to decode. * @return Offset of the encoded data. * @return Length of the encoded data. * @return RLP item type (LIST_ITEM or DATA_ITEM). */ function _decodeLength(RLPItem memory _in) private pure returns ( uint256, uint256, RLPItemType ) { require(_in.length > 0, 'RLP item cannot be null.'); uint256 ptr = _in.ptr; uint256 prefix; assembly { prefix := byte(0, mload(ptr)) } if (prefix <= 0x7f) { // Single byte. return (0, 1, RLPItemType.DATA_ITEM); } else if (prefix <= 0xb7) { // Short string. uint256 strLen = prefix - 0x80; require(_in.length > strLen, 'Invalid RLP short string.'); return (1, strLen, RLPItemType.DATA_ITEM); } else if (prefix <= 0xbf) { // Long string. uint256 lenOfStrLen = prefix - 0xb7; require(_in.length > lenOfStrLen, 'Invalid RLP long string length.'); uint256 strLen; assembly { // Pick out the string length. strLen := div(mload(add(ptr, 1)), exp(256, sub(32, lenOfStrLen))) } require(_in.length > lenOfStrLen + strLen, 'Invalid RLP long string.'); return (1 + lenOfStrLen, strLen, RLPItemType.DATA_ITEM); } else if (prefix <= 0xf7) { // Short list. uint256 listLen = prefix - 0xc0; require(_in.length > listLen, 'Invalid RLP short list.'); return (1, listLen, RLPItemType.LIST_ITEM); } else { // Long list. uint256 lenOfListLen = prefix - 0xf7; require(_in.length > lenOfListLen, 'Invalid RLP long list length.'); uint256 listLen; assembly { // Pick out the list length. listLen := div(mload(add(ptr, 1)), exp(256, sub(32, lenOfListLen))) } require(_in.length > lenOfListLen + listLen, 'Invalid RLP long list.'); return (1 + lenOfListLen, listLen, RLPItemType.LIST_ITEM); } } /** * Copies the bytes from a memory location. * @param _src Pointer to the location to read from. * @param _offset Offset to start reading from. * @param _length Number of bytes to read. * @return Copied bytes. */ function _copy( uint256 _src, uint256 _offset, uint256 _length ) private pure returns (bytes memory) { bytes memory out = new bytes(_length); if (out.length == 0) { return out; } uint256 src = _src + _offset; uint256 dest; assembly { dest := add(out, 32) } // Copy over as many complete words as we can. for (uint256 i = 0; i < _length / 32; i++) { assembly { mstore(dest, mload(src)) } src += 32; dest += 32; } // Pick out the remaining bytes. uint256 mask; unchecked { mask = 256**(32 - (_length % 32)) - 1; } assembly { mstore(dest, or(and(mload(src), not(mask)), and(mload(dest), mask))) } return out; } /** * Copies an RLP item into bytes. * @param _in RLP item to copy. * @return Copied bytes. */ function _copy(RLPItem memory _in) private pure returns (bytes memory) { return _copy(_in.ptr, 0, _in.length); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /** * @title Lib_RLPWriter * @author Bakaoh (with modifications) */ library Lib_RLPWriter { /********************** * Internal Functions * **********************/ /** * RLP encodes a byte string. * @param _in The byte string to encode. * @return The RLP encoded string in bytes. */ function writeBytes(bytes memory _in) internal pure returns (bytes memory) { bytes memory encoded; if (_in.length == 1 && uint8(_in[0]) < 128) { encoded = _in; } else { encoded = abi.encodePacked(_writeLength(_in.length, 128), _in); } return encoded; } /** * RLP encodes a list of RLP encoded byte byte strings. * @param _in The list of RLP encoded byte strings. * @return The RLP encoded list of items in bytes. */ function writeList(bytes[] memory _in) internal pure returns (bytes memory) { bytes memory list = _flatten(_in); return abi.encodePacked(_writeLength(list.length, 192), list); } /** * RLP encodes a string. * @param _in The string to encode. * @return The RLP encoded string in bytes. */ function writeString(string memory _in) internal pure returns (bytes memory) { return writeBytes(bytes(_in)); } /** * RLP encodes an address. * @param _in The address to encode. * @return The RLP encoded address in bytes. */ function writeAddress(address _in) internal pure returns (bytes memory) { return writeBytes(abi.encodePacked(_in)); } /** * RLP encodes a uint. * @param _in The uint256 to encode. * @return The RLP encoded uint256 in bytes. */ function writeUint(uint256 _in) internal pure returns (bytes memory) { return writeBytes(_toBinary(_in)); } /** * RLP encodes a bool. * @param _in The bool to encode. * @return The RLP encoded bool in bytes. */ function writeBool(bool _in) internal pure returns (bytes memory) { bytes memory encoded = new bytes(1); encoded[0] = (_in ? bytes1(0x01) : bytes1(0x80)); return encoded; } /********************* * Private Functions * *********************/ /** * Encode the first byte, followed by the `len` in binary form if `length` is more than 55. * @param _len The length of the string or the payload. * @param _offset 128 if item is string, 192 if item is list. * @return RLP encoded bytes. */ function _writeLength(uint256 _len, uint256 _offset) private pure returns (bytes memory) { bytes memory encoded; if (_len < 56) { encoded = new bytes(1); encoded[0] = bytes1(uint8(_len) + uint8(_offset)); } else { uint256 lenLen; uint256 i = 1; while (_len / i != 0) { lenLen++; i *= 256; } encoded = new bytes(lenLen + 1); encoded[0] = bytes1(uint8(lenLen) + uint8(_offset) + 55); for (i = 1; i <= lenLen; i++) { encoded[i] = bytes1(uint8((_len / (256**(lenLen - i))) % 256)); } } return encoded; } /** * Encode integer in big endian binary form with no leading zeroes. * @notice TODO: This should be optimized with assembly to save gas costs. * @param _x The integer to encode. * @return RLP encoded bytes. */ function _toBinary(uint256 _x) private pure returns (bytes memory) { bytes memory b = abi.encodePacked(_x); uint256 i = 0; for (; i < 32; i++) { if (b[i] != 0) { break; } } bytes memory res = new bytes(32 - i); for (uint256 j = 0; j < res.length; j++) { res[j] = b[i++]; } return res; } /** * Copies a piece of memory to another location. * @notice From: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol. * @param _dest Destination location. * @param _src Source location. * @param _len Length of memory to copy. */ function _memcpy( uint256 _dest, uint256 _src, uint256 _len ) private pure { uint256 dest = _dest; uint256 src = _src; uint256 len = _len; for (; len >= 32; len -= 32) { assembly { mstore(dest, mload(src)) } dest += 32; src += 32; } uint256 mask; unchecked { mask = 256**(32 - len) - 1; } assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) mstore(dest, or(destpart, srcpart)) } } /** * Flattens a list of byte strings into one byte string. * @notice From: https://github.com/sammayo/solidity-rlp-encoder/blob/master/RLPEncode.sol. * @param _list List of byte strings to flatten. * @return The flattened byte string. */ function _flatten(bytes[] memory _list) private pure returns (bytes memory) { if (_list.length == 0) { return new bytes(0); } uint256 len; uint256 i = 0; for (; i < _list.length; i++) { len += _list[i].length; } bytes memory flattened = new bytes(len); uint256 flattenedPtr; assembly { flattenedPtr := add(flattened, 0x20) } for (i = 0; i < _list.length; i++) { bytes memory item = _list[i]; uint256 listPtr; assembly { listPtr := add(item, 0x20) } _memcpy(flattenedPtr, listPtr, item.length); flattenedPtr += _list[i].length; } return flattened; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /** * @title Lib_BytesUtils */ library Lib_BytesUtils { /********************** * Internal Functions * **********************/ function slice( bytes memory _bytes, uint256 _start, uint256 _length ) internal pure returns (bytes memory) { require(_length + 31 >= _length, 'slice_overflow'); require(_start + _length >= _start, 'slice_overflow'); require(_bytes.length >= _start + _length, 'slice_outOfBounds'); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add( add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start ) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) //zero out the 32 bytes slice we are about to return //we need to do it because Solidity does not garbage collect mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function slice(bytes memory _bytes, uint256 _start) internal pure returns (bytes memory) { if (_start >= _bytes.length) { return bytes(''); } return slice(_bytes, _start, _bytes.length - _start); } function toBytes32(bytes memory _bytes) internal pure returns (bytes32) { if (_bytes.length < 32) { bytes32 ret; assembly { ret := mload(add(_bytes, 32)) } return ret; } return abi.decode(_bytes, (bytes32)); // will truncate if input length > 32 bytes } function toUint256(bytes memory _bytes) internal pure returns (uint256) { return uint256(toBytes32(_bytes)); } function toNibbles(bytes memory _bytes) internal pure returns (bytes memory) { bytes memory nibbles = new bytes(_bytes.length * 2); for (uint256 i = 0; i < _bytes.length; i++) { nibbles[i * 2] = _bytes[i] >> 4; nibbles[i * 2 + 1] = bytes1(uint8(_bytes[i]) % 16); } return nibbles; } function fromNibbles(bytes memory _bytes) internal pure returns (bytes memory) { bytes memory ret = new bytes(_bytes.length / 2); for (uint256 i = 0; i < ret.length; i++) { ret[i] = (_bytes[i * 2] << 4) | (_bytes[i * 2 + 1]); } return ret; } function equal(bytes memory _bytes, bytes memory _other) internal pure returns (bool) { return keccak256(_bytes) == keccak256(_other); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /** * @title Lib_Byte32Utils */ library Lib_Bytes32Utils { /********************** * Internal Functions * **********************/ /** * Converts a bytes32 value to a boolean. Anything non-zero will be converted to "true." * @param _in Input bytes32 value. * @return Bytes32 as a boolean. */ function toBool(bytes32 _in) internal pure returns (bool) { return _in != 0; } /** * Converts a boolean to a bytes32 value. * @param _in Input boolean value. * @return Boolean as a bytes32. */ function fromBool(bool _in) internal pure returns (bytes32) { return bytes32(uint256(_in ? 1 : 0)); } /** * Converts a bytes32 value to an address. Takes the *last* 20 bytes. * @param _in Input bytes32 value. * @return Bytes32 as an address. */ function toAddress(bytes32 _in) internal pure returns (address) { return address(uint160(uint256(_in))); } /** * Converts an address to a bytes32. * @param _in Input address value. * @return Address as a bytes32. */ function fromAddress(address _in) internal pure returns (bytes32) { return bytes32(uint256(uint160(_in))); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* External Imports */ import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; /** * @title Lib_AddressManager */ contract Lib_AddressManager is Ownable { /********** * Events * **********/ event AddressSet( string indexed _name, address _newAddress, address _oldAddress ); /************* * Variables * *************/ mapping(bytes32 => address) private addresses; /******************** * Public Functions * ********************/ /** * Changes the address associated with a particular name. * @param _name String name to associate an address with. * @param _address Address to associate with the name. */ function setAddress(string memory _name, address _address) external onlyOwner { bytes32 nameHash = _getNameHash(_name); address oldAddress = addresses[nameHash]; addresses[nameHash] = _address; emit AddressSet(_name, _address, oldAddress); } /** * Retrieves the address associated with a given name. * @param _name Name to retrieve an address for. * @return Address associated with the given name. */ function getAddress(string memory _name) external view returns (address) { return addresses[_getNameHash(_name)]; } /********************** * Internal Functions * **********************/ /** * Computes the hash of a name. * @param _name Name to compute a hash for. * @return Hash of the given name. */ function _getNameHash(string memory _name) internal pure returns (bytes32) { return keccak256(abi.encodePacked(_name)); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _setOwner(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _setOwner(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _setOwner(newOwner); } function _setOwner(address newOwner) private { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
File 6 of 6: ChainStorageContainer
// SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* Library Imports */ import {Lib_Buffer} from '../../libraries/utils/Lib_Buffer.sol'; import {Lib_AddressResolver} from '../../libraries/resolver/Lib_AddressResolver.sol'; /* Interface Imports */ import {IChainStorageContainer} from './IChainStorageContainer.sol'; /** * @title ChainStorageContainer * @dev The Chain Storage Container provides its owner contract with read, write and delete * functionality. This provides gas efficiency gains by enabling it to overwrite storage slots which * can no longer be used in a fraud proof due to the fraud window having passed, and the associated * chain state or transactions being finalized. * Three distinct Chain Storage Containers will be deployed on Layer 1: * 1. Stores transaction batches for the Canonical Transaction Chain * 2. Stores queued transactions for the Canonical Transaction Chain * 3. Stores chain state batches for the State Commitment Chain * * Runtime target: EVM */ contract ChainStorageContainer is IChainStorageContainer, Lib_AddressResolver { /************* * Libraries * *************/ using Lib_Buffer for Lib_Buffer.Buffer; /************* * Variables * *************/ string public owner; Lib_Buffer.Buffer internal buffer; /*************** * Constructor * ***************/ /** * @param _libAddressManager Address of the Address Manager. * @param _owner Name of the contract that owns this container (will be resolved later). */ constructor(address _libAddressManager, string memory _owner) Lib_AddressResolver(_libAddressManager) { owner = _owner; } /********************** * Function Modifiers * **********************/ modifier onlyOwner() { require( msg.sender == resolve(owner), 'ChainStorageContainer: Function can only be called by the owner.' ); _; } /******************** * Public Functions * ********************/ /** * @inheritdoc IChainStorageContainer */ function setGlobalMetadata(bytes27 _globalMetadata) public onlyOwner { return buffer.setExtraData(_globalMetadata); } /** * @inheritdoc IChainStorageContainer */ function getGlobalMetadata() public view returns (bytes27) { return buffer.getExtraData(); } /** * @inheritdoc IChainStorageContainer */ function length() public view returns (uint256) { return uint256(buffer.getLength()); } /** * @inheritdoc IChainStorageContainer */ function push(bytes32 _object) public onlyOwner { buffer.push(_object); } /** * @inheritdoc IChainStorageContainer */ function push(bytes32 _object, bytes27 _globalMetadata) public onlyOwner { buffer.push(_object, _globalMetadata); } /** * @inheritdoc IChainStorageContainer */ function get(uint256 _index) public view returns (bytes32) { return buffer.get(uint40(_index)); } /** * @inheritdoc IChainStorageContainer */ function deleteElementsAfterInclusive(uint256 _index) public onlyOwner { buffer.deleteElementsAfterInclusive(uint40(_index)); } /** * @inheritdoc IChainStorageContainer */ function deleteElementsAfterInclusive(uint256 _index, bytes27 _globalMetadata) public onlyOwner { buffer.deleteElementsAfterInclusive(uint40(_index), _globalMetadata); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /** * @title Lib_Buffer * @dev This library implements a bytes32 storage array with some additional gas-optimized * functionality. In particular, it encodes its length as a uint40, and tightly packs this with an * overwritable "extra data" field so we can store more information with a single SSTORE. */ library Lib_Buffer { /************* * Libraries * *************/ using Lib_Buffer for Buffer; /*********** * Structs * ***********/ struct Buffer { bytes32 context; mapping(uint256 => bytes32) buf; } struct BufferContext { // Stores the length of the array. Uint40 is way more elements than we'll ever reasonably // need in an array and we get an extra 27 bytes of extra data to play with. uint40 length; // Arbitrary extra data that can be modified whenever the length is updated. Useful for // squeezing out some gas optimizations. bytes27 extraData; } /********************** * Internal Functions * **********************/ /** * Pushes a single element to the buffer. * @param _self Buffer to access. * @param _value Value to push to the buffer. * @param _extraData Global extra data. */ function push( Buffer storage _self, bytes32 _value, bytes27 _extraData ) internal { BufferContext memory ctx = _self.getContext(); _self.buf[ctx.length] = _value; // Bump the global index and insert our extra data, then save the context. ctx.length++; 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(Buffer storage _self, bytes32 _value) internal { BufferContext memory ctx = _self.getContext(); _self.push(_value, 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(Buffer storage _self, uint256 _index) internal view returns (bytes32) { BufferContext memory ctx = _self.getContext(); require(_index < ctx.length, 'Index out of bounds.'); return _self.buf[_index]; } /** * 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( Buffer storage _self, uint40 _index, bytes27 _extraData ) internal { BufferContext memory ctx = _self.getContext(); require(_index < ctx.length, 'Index out of bounds.'); // Set our length and extra data, save the context. ctx.length = _index; ctx.extraData = _extraData; _self.setContext(ctx); } /** * Deletes all elements after (and including) a given index. * @param _self Buffer to access. * @param _index Index of the element to delete from (inclusive). */ function deleteElementsAfterInclusive(Buffer storage _self, uint40 _index) internal { BufferContext memory ctx = _self.getContext(); _self.deleteElementsAfterInclusive(_index, ctx.extraData); } /** * Retrieves the current global index. * @param _self Buffer to access. * @return Current global index. */ function getLength(Buffer storage _self) internal view returns (uint40) { BufferContext memory ctx = _self.getContext(); return ctx.length; } /** * Changes current global extra data. * @param _self Buffer to access. * @param _extraData New global extra data. */ function setExtraData(Buffer storage _self, bytes27 _extraData) internal { BufferContext memory ctx = _self.getContext(); ctx.extraData = _extraData; _self.setContext(ctx); } /** * Retrieves the current global extra data. * @param _self Buffer to access. * @return Current global extra data. */ function getExtraData(Buffer storage _self) internal view returns (bytes27) { BufferContext memory ctx = _self.getContext(); return ctx.extraData; } /** * Sets the current buffer context. * @param _self Buffer to access. * @param _ctx Current buffer context. */ function setContext(Buffer storage _self, BufferContext memory _ctx) internal { bytes32 context; uint40 length = _ctx.length; bytes27 extraData = _ctx.extraData; assembly { context := length context := or(context, extraData) } if (_self.context != context) { _self.context = context; } } /** * Retrieves the current buffer context. * @param _self Buffer to access. * @return Current buffer context. */ function getContext(Buffer storage _self) internal view returns (BufferContext memory) { bytes32 context = _self.context; uint40 length; bytes27 extraData; assembly { // solhint-disable-next-line max-line-length length := and( context, 0x000000000000000000000000000000000000000000000000000000FFFFFFFFFF ) // solhint-disable-next-line max-line-length extraData := and( context, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000 ) } return BufferContext({length: length, extraData: extraData}); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* Library Imports */ import {Lib_AddressManager} from './Lib_AddressManager.sol'; /** * @title Lib_AddressResolver */ abstract contract Lib_AddressResolver { /************* * Variables * *************/ Lib_AddressManager public libAddressManager; /*************** * Constructor * ***************/ /** * @param _libAddressManager Address of the Lib_AddressManager. */ constructor(address _libAddressManager) { libAddressManager = Lib_AddressManager(_libAddressManager); } /******************** * Public Functions * ********************/ /** * Resolves the address associated with a given name. * @param _name Name to resolve an address for. * @return Address associated with the given name. */ function resolve(string memory _name) public view returns (address) { return libAddressManager.getAddress(_name); } } // SPDX-License-Identifier: MIT pragma solidity >0.5.0 <0.9.0; /** * @title IChainStorageContainer */ interface IChainStorageContainer { /******************** * Public Functions * ********************/ /** * Sets the container's global metadata field. We're using `bytes27` here because we use five * bytes to maintain the length of the underlying data structure, meaning we have an extra * 27 bytes to store arbitrary data. * @param _globalMetadata New global metadata to set. */ function setGlobalMetadata(bytes27 _globalMetadata) external; /** * Retrieves the container's global metadata field. * @return Container global metadata field. */ function getGlobalMetadata() external view returns (bytes27); /** * Retrieves the number of objects stored in the container. * @return Number of objects in the container. */ function length() external view returns (uint256); /** * Pushes an object into the container. * @param _object A 32 byte value to insert into the container. */ function push(bytes32 _object) external; /** * Pushes an object into the container. Function allows setting the global metadata since * we'll need to touch the "length" storage slot anyway, which also contains the global * metadata (it's an optimization). * @param _object A 32 byte value to insert into the container. * @param _globalMetadata New global metadata for the container. */ function push(bytes32 _object, bytes27 _globalMetadata) external; /** * Retrieves an object from the container. * @param _index Index of the particular object to access. * @return 32 byte object value. */ function get(uint256 _index) external view returns (bytes32); /** * Removes all objects after and including a given index. * @param _index Object index to delete from. */ function deleteElementsAfterInclusive(uint256 _index) external; /** * Removes all objects after and including a given index. Also allows setting the global * metadata field. * @param _index Object index to delete from. * @param _globalMetadata New global metadata for the container. */ function deleteElementsAfterInclusive(uint256 _index, bytes27 _globalMetadata) external; } // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; /* External Imports */ import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; /** * @title Lib_AddressManager */ contract Lib_AddressManager is Ownable { /********** * Events * **********/ event AddressSet( string indexed _name, address _newAddress, address _oldAddress ); /************* * Variables * *************/ mapping(bytes32 => address) private addresses; /******************** * Public Functions * ********************/ /** * Changes the address associated with a particular name. * @param _name String name to associate an address with. * @param _address Address to associate with the name. */ function setAddress(string memory _name, address _address) external onlyOwner { bytes32 nameHash = _getNameHash(_name); address oldAddress = addresses[nameHash]; addresses[nameHash] = _address; emit AddressSet(_name, _address, oldAddress); } /** * Retrieves the address associated with a given name. * @param _name Name to retrieve an address for. * @return Address associated with the given name. */ function getAddress(string memory _name) external view returns (address) { return addresses[_getNameHash(_name)]; } /********************** * Internal Functions * **********************/ /** * Computes the hash of a name. * @param _name Name to compute a hash for. * @return Hash of the given name. */ function _getNameHash(string memory _name) internal pure returns (bytes32) { return keccak256(abi.encodePacked(_name)); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _setOwner(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _setOwner(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _setOwner(newOwner); } function _setOwner(address newOwner) private { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }