Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"release:alpha": "yarn clean && yarn build && lerna version prerelease --yes && lerna publish from-package --yes --force-publish --dist-tag=alpha -m \"chore(@ethereum-optimism) publish %s release\"",
"release:beta": "yarn clean && yarn build && lerna version prerelease --yes && lerna publish from-package --yes --force-publish --dist-tag=beta -m \"chore(@ethereum-optimism) publish %s release\"",
"patch": "yarn run patch:ganache && yarn run patch:waffle",
"patch:ganache": "sed -ie 's!import { Provider as Web3Provider } from \"web3/providers\";!import { provider as Web3Provider } from \"web3-core\";!' node_modules/ganache-core/typings/index.d.ts",
"patch:ganache": "sed -ie 's!import { Provider as Web3Provider } from \"web3/providers\";!import { AbstractProvider as Web3Provider } from \"web3-core\";!' node_modules/ganache-core/typings/index.d.ts",
"patch:waffle": "sed -ie 's!Ganache.GanacheOpts!any!g' node_modules/ethereum-waffle/dist/waffle.d.ts"
},
"repository": "git+https://github.com/ethereum-optimism/optimism-monorepo.git",
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
pragma solidity ^0.5.0;
pragma experimental ABIEncoderV2;

/* Internal Imports */
import {DataTypes as dt} from "./DataTypes.sol";

contract L1ToL2TransactionPasser {
uint nonce = 0;
/*
* Events
*/

event L1ToL2Transaction(
uint _nonce,
Expand All @@ -14,13 +13,30 @@ contract L1ToL2TransactionPasser {
bytes _callData
);

function passTransactionToL2(address ovmEntrypoint, bytes memory ovmCalldata) public {
// TODO: Actually create/enqueue a rollup block with this message. We are simply mocking this functionality for now.

/*
* Contract Variables
*/

uint nonce;


/*
* Public Functions
*/

function passTransactionToL2(
address _ovmEntrypoint,
bytes memory _ovmCalldata
) public {
// TODO: Actually create/enqueue a rollup block with this message.
// We are simply mocking this functionality for now.

emit L1ToL2Transaction(
nonce++,
msg.sender,
ovmEntrypoint,
ovmCalldata
_ovmEntrypoint,
_ovmCalldata
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,105 @@ pragma solidity ^0.5.0;
pragma experimental ABIEncoderV2;

/* Internal Imports */
import {DataTypes as dt} from "./DataTypes.sol";
import { DataTypes } from "../utils/DataTypes.sol";

contract L2ToL1MessageReceiver {
/*
* Events
*/

event L2ToL1MessageEnqueued(
address ovmSender,
bytes callData,
uint nonce
);


/*
* Structs
*/

struct EnqueuedL2ToL1Message {
dt.L2ToL1Message message;
DataTypes.L2ToL1Message message;
uint l1BlockEnqueued;
}


/*
* Contract Variables
*/

address public sequencer;
uint public blocksUntilFinal;
uint messageNonce = 0;
uint public messageNonce;
mapping (uint => EnqueuedL2ToL1Message) public messages;


/*
* Constructor
*/

constructor(address _sequencer, uint _blocksUntilFinal) public {
sequencer = _sequencer;
blocksUntilFinal = _blocksUntilFinal;
}

function enqueueL2ToL1Message(dt.L2ToL1Message memory _message) public {
require(msg.sender == sequencer, "For now, only our trusted sequencer can enqueue messages to be verified on L1");
uint blockNum = block.number;

/*
* Public Functions
*/

function enqueueL2ToL1Message(
DataTypes.L2ToL1Message memory _message
) public {
require(
msg.sender == sequencer,
"For now, only our trusted sequencer can enqueue messages."
);

// Enqueue the message.
messages[messageNonce] = EnqueuedL2ToL1Message({
message: _message,
l1BlockEnqueued: blockNum
l1BlockEnqueued: block.number
});

// Let the world know.
emit L2ToL1MessageEnqueued(
_message.ovmSender,
_message.callData,
messageNonce
);

// On to the next one.
messageNonce += 1;
}

function verifyL2ToL1Message(dt.L2ToL1Message memory _message, uint _nonce) public view returns (bool) {
// The enqueued message for the given nonce must match the _message being verified
function verifyL2ToL1Message(
DataTypes.L2ToL1Message memory _message,
uint _nonce
) public view returns (bool) {
// The enqueued message for the given nonce must match the _message
// being verified.
bytes32 givenMessageHash = getMessageHash(_message);
bytes32 storedMessageHash = getMessageHash(messages[_nonce].message);
bool messageWasEnqueued = (storedMessageHash == givenMessageHash);
// Message must be finalized on L1
bool messageIsFinalized = (block.number >= messages[_nonce].l1BlockEnqueued + blocksUntilFinal);

// Message must be finalized on L1.
bool messageIsFinalized = (
block.number >= messages[_nonce].l1BlockEnqueued + blocksUntilFinal
);

return messageWasEnqueued && messageIsFinalized;
}

function getMessageHash(dt.L2ToL1Message memory _message) internal pure returns(bytes32) {

/*
* Internal Functions
*/

function getMessageHash(
DataTypes.L2ToL1Message memory _message
) internal pure returns (bytes32) {
return keccak256(abi.encode(_message));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
pragma solidity ^0.5.0;
pragma experimental ABIEncoderV2;

/* Internal Imports */
import { DataTypes } from "../utils/DataTypes.sol";
import { RollupMerkleUtils } from "../utils/RollupMerkleUtils.sol";
import { L1ToL2TransactionQueue } from "../queue/L1ToL2TransactionQueue.sol";
import { SafetyTransactionQueue } from "../queue/SafetyTransactionQueue.sol";

contract CanonicalTransactionChain {
/*
* Contract Variables
*/

address public sequencer;
uint public forceInclusionPeriod;
RollupMerkleUtils public merkleUtils;
L1ToL2TransactionQueue public l1ToL2Queue;
SafetyTransactionQueue public safetyQueue;
uint public cumulativeNumElements;
bytes32[] public batches;
uint public lastOVMTimestamp;


/*
* Constructor
*/

constructor(
address _rollupMerkleUtilsAddress,
address _sequencer,
address _l1ToL2TransactionPasserAddress,
uint _forceInclusionPeriod
) public {
merkleUtils = RollupMerkleUtils(_rollupMerkleUtilsAddress);
sequencer = _sequencer;
forceInclusionPeriod = _forceInclusionPeriod;
lastOVMTimestamp = 0;

safetyQueue = new SafetyTransactionQueue(address(this));
l1ToL2Queue = new L1ToL2TransactionQueue(
_l1ToL2TransactionPasserAddress,
address(this)
);
}


/*
* Public Functions
*/

function getBatchesLength() public view returns (uint) {
return batches.length;
}

function hashBatchHeader(
DataTypes.TxChainBatchHeader memory _batchHeader
) public pure returns (bytes32) {
return keccak256(abi.encodePacked(
_batchHeader.timestamp,
_batchHeader.isL1ToL2Tx,
_batchHeader.elementsMerkleRoot,
_batchHeader.numElementsInBatch,
_batchHeader.cumulativePrevElements
));
}

function authenticateAppend(
address _sender
) public view returns (bool) {
return _sender == sequencer;
}

function appendL1ToL2Batch() public {
DataTypes.TimestampedHash memory l1ToL2Header = l1ToL2Queue.peek();

require(
safetyQueue.isEmpty() || l1ToL2Header.timestamp <= safetyQueue.peekTimestamp(),
"Must process older SafetyQueue batches first to enforce timestamp monotonicity"
);

_appendQueueBatch(l1ToL2Header, true);
l1ToL2Queue.dequeue();
}

function appendSafetyBatch() public {
DataTypes.TimestampedHash memory safetyHeader = safetyQueue.peek();

require(
l1ToL2Queue.isEmpty() || safetyHeader.timestamp <= l1ToL2Queue.peekTimestamp(),
"Must process older L1ToL2Queue batches first to enforce timestamp monotonicity"
);

_appendQueueBatch(safetyHeader, false);
safetyQueue.dequeue();
}

function _appendQueueBatch(
DataTypes.TimestampedHash memory timestampedHash,
bool isL1ToL2Tx
) internal {
uint timestamp = timestampedHash.timestamp;

require(
timestamp + forceInclusionPeriod <= now || authenticateAppend(msg.sender),
"Message sender does not have permission to append this batch"
);

lastOVMTimestamp = timestamp;
bytes32 elementsMerkleRoot = timestampedHash.txHash;
uint numElementsInBatch = 1;

bytes32 batchHeaderHash = keccak256(abi.encodePacked(
timestamp,
isL1ToL2Tx,
elementsMerkleRoot,
numElementsInBatch,
cumulativeNumElements // cumulativePrevElements
));

batches.push(batchHeaderHash);
cumulativeNumElements += numElementsInBatch;
}

function appendSequencerBatch(
bytes[] memory _txBatch,
uint _timestamp
) public {
require(
authenticateAppend(msg.sender),
"Message sender does not have permission to append a batch"
);

require(
_txBatch.length > 0,
"Cannot submit an empty batch"
);

require(
_timestamp + forceInclusionPeriod > now,
"Cannot submit a batch with a timestamp older than the sequencer inclusion period"
);

require(
_timestamp <= now,
"Cannot submit a batch with a timestamp in the future"
);

require(
l1ToL2Queue.isEmpty() || _timestamp <= l1ToL2Queue.peekTimestamp(),
"Must process older L1ToL2Queue batches first to enforce timestamp monotonicity"
);

require(
safetyQueue.isEmpty() || _timestamp <= safetyQueue.peekTimestamp(),
"Must process older SafetyQueue batches first to enforce timestamp monotonicity"
);

require(
_timestamp >= lastOVMTimestamp,
"Timestamps must monotonically increase"
);

lastOVMTimestamp = _timestamp;

bytes32 batchHeaderHash = keccak256(abi.encodePacked(
_timestamp,
false, // isL1ToL2Tx
merkleUtils.getMerkleRoot(_txBatch), // elementsMerkleRoot
_txBatch.length, // numElementsInBatch
cumulativeNumElements // cumulativeNumElements
));

batches.push(batchHeaderHash);
cumulativeNumElements += _txBatch.length;
}

// verifies an element is in the current list at the given position
function verifyElement(
bytes memory _element, // the element of the list being proven
uint _position, // the position in the list of the element being proven
DataTypes.TxElementInclusionProof memory _inclusionProof // inclusion proof in the rollup batch
) public view returns (bool) {
// For convenience, store the batchHeader
DataTypes.TxChainBatchHeader memory batchHeader = _inclusionProof.batchHeader;

// make sure absolute position equivalent to relative positions
if (_position != _inclusionProof.indexInBatch +
batchHeader.cumulativePrevElements) {
return false;
}

// verify elementsMerkleRoot
if (!merkleUtils.verify(
batchHeader.elementsMerkleRoot,
_element,
_inclusionProof.indexInBatch,
_inclusionProof.siblings
)) {
return false;
}

//compare computed batch header with the batch header in the list.
return hashBatchHeader(batchHeader) == batches[_inclusionProof.batchIndex];
}
}
Loading