Commit 4b241fdb authored by Kelvin Fichter's avatar Kelvin Fichter

Started adding chain contracts

parent 9b464f1d
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_BaseChain } from "../../iOVM/chain/iOVM_BaseChain.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_MerkleUtils } from "../../../libraries/utils/Lib_MerkleUtils.sol";
/**
* @title OVM_BaseChain
*/
contract OVM_BaseChain is iOVM_BaseChain {
/*******************************
* Contract Variables: Batches *
*******************************/
bytes32[] internal batches;
uint256 internal totalBatches;
uint256 internal totalElements;
/*************************************
* Public Functions: Batch Retrieval *
*************************************/
function getTotalElements()
override
public
view
returns (
uint256 _totalElements
)
{
return totalElements;
}
function getTotalBatches()
override
public
view
returns (
uint256 _totalBatches
)
{
return totalBatches;
}
/****************************************
* Public Functions: Batch Verification *
****************************************/
function verifyElement(
bytes calldata _element,
Lib_OVMDataTypes.OVMChainBatchHeader memory _batchHeader,
Lib_OVMDataTypes.OVMChainInclusionProof memory _proof
)
override
public
view
returns (
bool _verified
)
{
require(
_hashBatchHeader(_batchHeader) == batches[_batchHeader.batchIndex],
"Invalid batch header."
);
require(
libMerkleUtils.verify(
_batchHeader.batchRoot,
_element,
_proof.index,
_proof.siblings
),
"Invalid inclusion proof."
);
return true;
}
/******************************************
* Internal Functions: Batch Modification *
******************************************/
function _appendBatch(
Lib_OVMDataTypes.OVMChainBatchHeader memory _batchHeader
)
internal
{
bytes32 batchHeaderHash = _hashBatchHeader(_batchHeader);
batches.push(batchHeaderHash);
totalBatches += 1;
totalElements += _batchHeader.batchSize;
}
function _appendBatch(
bytes[] memory _elements,
bytes memory _extraData
)
internal
{
Lib_OVMDataTypes.OVMChainBatchHeader memory batchHeader = Lib_OVMDataTypes.OVMChainBatchHeader({
batchIndex: batches.length,
batchRoot: libMerkleUtils.getMerkleRoot(_elements),
batchSize: _elements.length,
prevTotalElements: totalElements,
extraData: _extraData
});
appendBatch(batchHeader);
}
function _appendBatch(
bytes[] memory _elements
)
internal
{
appendBatch(
_elements,
bytes('')
);
}
function _deleteBatch(
Lib_OVMDataTypes.OVMChainBatchHeader memory _batchHeader
)
internal
{
require(
_batchHeader.batchIndex < batches.length,
"Invalid batch index."
);
require(
_hashBatchHeader(_batchHeader) == batches[_batchHeader.batchIndex],
"Invalid batch header."
);
totalBatches = _batchHeader.batchIndex;
totalElements = _batchHeader.prevTotalElements;
}
/*********************
* Private Functions *
*********************/
function _hashBatchHeader(
Lib_OVMDataTypes.OVMChainBatchHeader memory _batchHeader
)
internal
returns (
bytes32 _hash
)
{
return keccak256(abi.encodePacked(
_batchHeader.batchRoot,
_batchHeader.batchSize,
_batchHeader.prevTotalElements,
_batchHeader.extraData
));
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_MerkleUtils } from "../../../libraries/utils/Lib_MerkleUtils.sol";
/* Interface Imports */
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
import { iOVM_BaseQueue } from "../../iOVM/queue/iOVM_BaseQueue.sol";
/* Contract Imports */
import { OVM_BaseChain } from "./OVM_BaseChain.sol";
contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_BaseChain {
iOVM_BaseQueue internal ovmL1ToL2TransactionQueue;
address internal sequencer;
uint256 internal forceInclusionPeriodSeconds;
uint256 internal lastOVMTimestamp;
constructor(
address _ovmL1ToL2TransactionQueue,
address _sequencer,
uint256 _forceInclusionPeriodSeconds,
)
Lib_ContractProxyResolver(_libContractProxyManager)
{
ovmL1ToL2TransactionQueue = iOVM_BaseQueue(_ovmL1ToL2TransactionQueue);
sequencer = _sequencer;
forceInclusionPeriodSeconds = _forceInclusionPeriodSeconds;
}
modifier onlySequencer() {
require(
msg.sender == sequencer,
"Function can only be called by the sequencer."
);
_;
}
function appendQueueBatch()
override
public
{
require(
ovmL1ToL2TransactionQueue.size() > 0 == true,
"No batches are currently queued to be appended."
);
Lib_OVMCodec.QueueElement memory queueElement = ovmL1ToL2TransactionQueue.peek();
require(
queueElement.timestamp + forceInclusionPeriodSeconds <= block.timestamp,
"Cannot append until the inclusion delay period has elapsed."
);
_appendQueueBatch(queueElement, 1);
ovmL1ToL2TransactionQueue.dequeue();
}
function appendSequencerBatch(
bytes[] memory _batch,
uint256 _timestamp
)
override
public
onlySequencer
{
require(
_timestamp >= lastOVMTimestamp,
"Batch timestamp must be later than the last OVM timestamp."
);
if (ovmL1ToL2TransactionQueue.size() > 0) {
require(
_timestamp <= ovmL1ToL2TransactionQueue.peek().timestamp,
"Older queue batches must be processed before a newer sequencer batch."
);
}
Lib_OVMCodec.QueueElement memory queueElement = Lib_OVMCodec.QueueElement({
timestamp: _timestamp,
batchRoot: Lib_MerkleUtils.getMerkleRoot(_batch),
isL1ToL2Batch: false
});
_appendQueueBatch(queueElement, _batch.length);
}
function _appendQueueBatch(
Lib_OVMCodec.QueueElement memory _queueElement,
uint256 _batchSize
)
internal
{
Lib_OVMCodec.ChainBatchHeader memory batchHeader = Lib_OVMCodec.ChainBatchHeader({
batchIndex: getTotalBatches(),
batchRoot: _queueElement.batchRoot,
batchSize: _batchSize,
prevTotalElements: getTotalElements(),
extraData: abi.encodePacked(
_queueElement.timestamp,
_queueElement.isL1ToL2Batch
)
});
_appendBatch(batchHeader);
lastOVMTimestamp = _queueElement.timestamp;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../../libraries/codec/Lib_OVMCodec.sol";
/* Interface Imports */
import { iOVM_StateCommitmentChain } from "../../iOVM/chain/iOVM_StateCommitmentChain.sol";
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
import { iOVM_FraudVerifier } from "../../iOVM/execution/iOVM_FraudVerifier.sol";
/* Contract Imports */
import { OVM_BaseChain } from "./OVM_BaseChain.sol";
contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain {
iOVM_CanonicalTransactionChain internal ovmCanonicalTransactionChain;
iOVM_FraudVerifier internal ovmFraudVerifier;
constructor(
address _ovmCanonicalTransactionChain,
address _ovmFraudVerifier
)
Lib_ContractProxyResolver(_libContractProxyManager)
{
ovmCanonicalTransactionChain = iOVM_CanonicalTransactionChain(_ovmCanonicalTransactionChain);
ovmFraudVerifier = iOVM_FraudVerifier(_ovmFraudVerifier);
}
function appendStateBatch(
bytes32[] memory _batch
)
override
public
{
require(
_batch.length > 0,
"Cannot submit an empty state batch."
);
require(
getTotalElements() + _batch.length <= ovmCanonicalTransactionChain.getTotalElements(),
"Number of state roots cannot exceed the number of canonical transactions."
);
bytes[] memory elements = new bytes[](_batch.length);
for (uint256 i = 0; i < _batch.length; i++) {
elements[i] = abi.encodePacked(_batch[i]);
}
_appendBatch(elements);
}
function deleteStateBatch(
Lib_OVMDataTypes.OVMChainBatchHeader memory _batchHeader
)
override
public
{
require(
msg.sender == address(ovmFraudVerifier),
"State batches can only be deleted by the fraud verifier"
);
_deleteBatch(_batchHeader);
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../../libraries/codec/Lib_OVMCodec.sol";
/**
* @title iOVM_BaseChain
*/
interface iOVM_BaseChain {
/*************************************
* Public Functions: Batch Retrieval *
*************************************/
function getTotalElements() external view returns (uint256 _totalElements);
function getTotalBatches() external view returns (uint256 _totalBatches);
/****************************************
* Public Functions: Batch Verification *
****************************************/
function verifyElement(
bytes calldata _element,
Lib_OVMCodec.ChainBatchHeader calldata _batchHeader,
Lib_OVMCodec.ChainInclusionProof calldata _proof
) external view returns (bool _verified);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_BaseChain } from "./iOVM_BaseChain.sol";
interface iOVM_CanonicalTransactionChain is iOVM_BaseChain {
function appendQueueBatch() external;
function appendSequencerBatch(bytes[] calldata _batch, uint256 _timestamp) external;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_BaseChain } from "./iOVM_BaseChain.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../../libraries/codec/Lib_OVMCodec.sol";
interface iOVM_StateCommitmentChain is iOVM_BaseChain {
function appendStateBatch(bytes32[] calldata _batch) external;
function deleteStateBatch(Lib_OVMCodec.ChainBatchHeader memory _batchHeader) external;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/**
* @title Lib_MerkleUtils
*/
library Lib_MerkleUtils {
function getMerkleRoot(
bytes32[] memory _hashes
)
public
view
returns (
bytes32 _root
)
{
if (_hashes.length == 1) {
return _hashes[0];
}
bytes32[] memory defaultHashes = _getDefaultHashes(_hashes.length);
bytes32[] memory nodes = _hashes;
if (_hashes.length % 2 == 1) {
nodes = new bytes32[](_hashes.length + 1);
for (uint256 i = 0; i < _hashes.length; i++) {
nodes[i] = _hashes[i];
}
}
uint256 currentLevel = 0;
uint256 nextLevelSize = _hashes.length;
if (nextLevelSize % 2 == 1) {
nodes[nextLevelSize] = defaultHashes[currentLevel];
nextLevelSize += 1;
}
while (nextLevelSize > 1) {
currentLevel += 1;
for (uint256 i = 0; i < nextLevelSize / 2; i++) {
nodes[i] = _getParentHash(
nodes[i*2],
nodes[i*2 + 1]
);
}
nextLevelSize = nextLevelSize / 2;
if (nextLevelSize % 2 == 1 && nextLevelSize != 1) {
nodes[nextLevelSize] = defaultHashes[currentLevel];
nextLevelSize += 1;
}
}
return nodes[0];
}
function getMerkleRoot(
bytes[] memory _elements
)
public
view
returns (
bytes32 _root
)
{
bytes32[] memory hashes = new bytes32[](_elements.length);
for (uint256 i = 0; i < _elements.length; i++) {
hashes[i] = keccak256(_elements[i]);
}
return getMerkleRoot(hashes);
}
function verify(
bytes32 _root,
bytes memory _leaf,
uint256 _path,
bytes32[] memory _siblings
)
public
pure
returns (
bool _verified
)
{
bytes32 computedRoot = keccak256(_leaf);
for (uint256 i = 0; i < _siblings.length; i++) {
bytes32 sibling = _siblings[i];
bool isRightSibling = uint8(_path >> i & 1) == 1;
if (isRightSibling) {
computedRoot = _getParentHash(computedRoot, sibling);
} else {
computedRoot = _getParentHash(sibling, computedRoot);
}
}
return computedRoot == _root;
}
function _getDefaultHashes(
uint256 _length
)
internal
pure
returns (
bytes32[] memory _defaultHashes
)
{
bytes32[] memory defaultHashes = new bytes32[](_length);
defaultHashes[0] = keccak256(abi.encodePacked(uint256(0)));
for (uint256 i = 1; i < defaultHashes.length; i++) {
defaultHashes[i] = keccak256(abi.encodePacked(defaultHashes[i-1]));
}
return defaultHashes;
}
function _getParentHash(
bytes32 _leftChildHash,
bytes32 _rightChildHash
)
internal
pure
returns (
bytes32 _hash
)
{
return keccak256(abi.encodePacked(_leftChildHash, _rightChildHash));
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment