Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
a1a499f1
Commit
a1a499f1
authored
Sep 17, 2020
by
Kelvin Fichter
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Staged more contracts
parent
4b241fdb
Changes
23
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
3067 additions
and
38 deletions
+3067
-38
OVM_BaseChain.sol
...contracts/optimistic-ethereum/OVM/chain/OVM_BaseChain.sol
+51
-13
OVM_CanonicalTransactionChain.sol
...stic-ethereum/OVM/chain/OVM_CanonicalTransactionChain.sol
+58
-17
OVM_StateCommitmentChain.sol
...ptimistic-ethereum/OVM/chain/OVM_StateCommitmentChain.sol
+33
-5
OVM_ExecutionManager.sol
...ptimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol
+1
-1
OVM_FraudVerifier.sol
...s/optimistic-ethereum/OVM/execution/OVM_FraudVerifier.sol
+246
-0
OVM_StateManagerFactory.sol
...mistic-ethereum/OVM/execution/OVM_StateManagerFactory.sol
+29
-0
OVM_StateTransitioner.sol
...timistic-ethereum/OVM/execution/OVM_StateTransitioner.sol
+354
-0
OVM_StateTransitionerFactory.sol
...c-ethereum/OVM/execution/OVM_StateTransitionerFactory.sol
+39
-0
OVM_BaseQueue.sol
...contracts/optimistic-ethereum/OVM/queue/OVM_BaseQueue.sol
+108
-0
OVM_L1ToL2TransactionQueue.sol
...imistic-ethereum/OVM/queue/OVM_L1ToL2TransactionQueue.sol
+76
-0
iOVM_BaseChain.sol
...ntracts/optimistic-ethereum/iOVM/chain/iOVM_BaseChain.sol
+1
-1
iOVM_CanonicalTransactionChain.sol
...ic-ethereum/iOVM/chain/iOVM_CanonicalTransactionChain.sol
+8
-0
iOVM_StateCommitmentChain.sol
...imistic-ethereum/iOVM/chain/iOVM_StateCommitmentChain.sol
+9
-1
iOVM_FraudVerifier.sol
...optimistic-ethereum/iOVM/execution/iOVM_FraudVerifier.sol
+34
-0
iOVM_StateManagerFactory.sol
...stic-ethereum/iOVM/execution/iOVM_StateManagerFactory.sol
+21
-0
iOVM_StateTransitioner.sol
...mistic-ethereum/iOVM/execution/iOVM_StateTransitioner.sol
+74
-0
iOVM_StateTransitionerFactory.sol
...ethereum/iOVM/execution/iOVM_StateTransitionerFactory.sol
+26
-0
iOVM_BaseQueue.sol
...ntracts/optimistic-ethereum/iOVM/queue/iOVM_BaseQueue.sol
+19
-0
iOVM_L1ToL2TransactionQueue.sol
...istic-ethereum/iOVM/queue/iOVM_L1ToL2TransactionQueue.sol
+22
-0
Lib_EthMerkleTrie.sol
...racts/optimistic-ethereum/iOVM/trie/Lib_EthMerkleTrie.sol
+778
-0
Lib_MerkleTrie.sol
...ontracts/optimistic-ethereum/iOVM/trie/Lib_MerkleTrie.sol
+931
-0
Lib_SecureMerkleTrie.sol
...ts/optimistic-ethereum/iOVM/trie/Lib_SecureMerkleTrie.sol
+142
-0
Lib_OVMCodec.sol
...acts/optimistic-ethereum/libraries/codec/Lib_OVMCodec.sol
+7
-0
No files found.
packages/contracts/contracts/optimistic-ethereum/OVM/chain/OVM_BaseChain.sol
View file @
a1a499f1
...
@@ -6,8 +6,8 @@ pragma experimental ABIEncoderV2;
...
@@ -6,8 +6,8 @@ pragma experimental ABIEncoderV2;
import { iOVM_BaseChain } from "../../iOVM/chain/iOVM_BaseChain.sol";
import { iOVM_BaseChain } from "../../iOVM/chain/iOVM_BaseChain.sol";
/* Library Imports */
/* Library Imports */
import { Lib_OVMCodec } from "../../
../
libraries/codec/Lib_OVMCodec.sol";
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_MerkleUtils } from "../../
../
libraries/utils/Lib_MerkleUtils.sol";
import { Lib_MerkleUtils } from "../../libraries/utils/Lib_MerkleUtils.sol";
/**
/**
* @title OVM_BaseChain
* @title OVM_BaseChain
...
@@ -27,6 +27,10 @@ contract OVM_BaseChain is iOVM_BaseChain {
...
@@ -27,6 +27,10 @@ contract OVM_BaseChain is iOVM_BaseChain {
* Public Functions: Batch Retrieval *
* Public Functions: Batch Retrieval *
*************************************/
*************************************/
/**
* Gets the total number of submitted elements.
* @return _totalElements Total submitted elements.
*/
function getTotalElements()
function getTotalElements()
override
override
public
public
...
@@ -38,6 +42,10 @@ contract OVM_BaseChain is iOVM_BaseChain {
...
@@ -38,6 +42,10 @@ contract OVM_BaseChain is iOVM_BaseChain {
return totalElements;
return totalElements;
}
}
/**
* Gets the total number of submitted batches.
* @return _totalBatches Total submitted batches.
*/
function getTotalBatches()
function getTotalBatches()
override
override
public
public
...
@@ -54,10 +62,17 @@ contract OVM_BaseChain is iOVM_BaseChain {
...
@@ -54,10 +62,17 @@ contract OVM_BaseChain is iOVM_BaseChain {
* Public Functions: Batch Verification *
* Public Functions: Batch Verification *
****************************************/
****************************************/
/**
* Verifies an inclusion proof for a given element.
* @param _element Element to verify.
* @param _batchHeader Header of the batch in which this element was included.
* @param _proof Inclusion proof for the element.
* @return _verified Whether or not the element was included in the batch.
*/
function verifyElement(
function verifyElement(
bytes calldata _element,
bytes calldata _element,
Lib_OVM
DataTypes.OVM
ChainBatchHeader memory _batchHeader,
Lib_OVM
Codec.
ChainBatchHeader memory _batchHeader,
Lib_OVM
DataTypes.OVM
ChainInclusionProof memory _proof
Lib_OVM
Codec.
ChainInclusionProof memory _proof
)
)
override
override
public
public
...
@@ -72,7 +87,7 @@ contract OVM_BaseChain is iOVM_BaseChain {
...
@@ -72,7 +87,7 @@ contract OVM_BaseChain is iOVM_BaseChain {
);
);
require(
require(
lib
MerkleUtils.verify(
Lib_
MerkleUtils.verify(
_batchHeader.batchRoot,
_batchHeader.batchRoot,
_element,
_element,
_proof.index,
_proof.index,
...
@@ -89,8 +104,12 @@ contract OVM_BaseChain is iOVM_BaseChain {
...
@@ -89,8 +104,12 @@ contract OVM_BaseChain is iOVM_BaseChain {
* Internal Functions: Batch Modification *
* Internal Functions: Batch Modification *
******************************************/
******************************************/
/**
* Appends a batch to the chain.
* @param _batchHeader Batch header to append.
*/
function _appendBatch(
function _appendBatch(
Lib_OVM
DataTypes.OVM
ChainBatchHeader memory _batchHeader
Lib_OVM
Codec.
ChainBatchHeader memory _batchHeader
)
)
internal
internal
{
{
...
@@ -100,36 +119,49 @@ contract OVM_BaseChain is iOVM_BaseChain {
...
@@ -100,36 +119,49 @@ contract OVM_BaseChain is iOVM_BaseChain {
totalElements += _batchHeader.batchSize;
totalElements += _batchHeader.batchSize;
}
}
/**
* Appends a batch to the chain.
* @param _elements Elements within the batch.
* @param _extraData Any extra data to append to the batch.
*/
function _appendBatch(
function _appendBatch(
bytes[] memory _elements,
bytes[] memory _elements,
bytes memory _extraData
bytes memory _extraData
)
)
internal
internal
{
{
Lib_OVM
DataTypes.OVMChainBatchHeader memory batchHeader = Lib_OVMDataTypes.OVM
ChainBatchHeader({
Lib_OVM
Codec.ChainBatchHeader memory batchHeader = Lib_OVMCodec.
ChainBatchHeader({
batchIndex: batches.length,
batchIndex: batches.length,
batchRoot:
lib
MerkleUtils.getMerkleRoot(_elements),
batchRoot:
Lib_
MerkleUtils.getMerkleRoot(_elements),
batchSize: _elements.length,
batchSize: _elements.length,
prevTotalElements: totalElements,
prevTotalElements: totalElements,
extraData: _extraData
extraData: _extraData
});
});
appendBatch(batchHeader);
_
appendBatch(batchHeader);
}
}
/**
* Appends a batch to the chain.
* @param _elements Elements within the batch.
*/
function _appendBatch(
function _appendBatch(
bytes[] memory _elements
bytes[] memory _elements
)
)
internal
internal
{
{
appendBatch(
_
appendBatch(
_elements,
_elements,
bytes('')
bytes('')
);
);
}
}
/**
* Removes a batch from the chain.
* @param _batchHeader Header of the batch to remove.
*/
function _deleteBatch(
function _deleteBatch(
Lib_OVM
DataTypes.OVM
ChainBatchHeader memory _batchHeader
Lib_OVM
Codec.
ChainBatchHeader memory _batchHeader
)
)
internal
internal
{
{
...
@@ -152,10 +184,16 @@ contract OVM_BaseChain is iOVM_BaseChain {
...
@@ -152,10 +184,16 @@ contract OVM_BaseChain is iOVM_BaseChain {
* Private Functions *
* Private Functions *
*********************/
*********************/
/**
* Calculates a hash for a given batch header.
* @param _batchHeader Header to hash.
* @return _hash Hash of the header.
*/
function _hashBatchHeader(
function _hashBatchHeader(
Lib_OVM
DataTypes.OVM
ChainBatchHeader memory _batchHeader
Lib_OVM
Codec.
ChainBatchHeader memory _batchHeader
)
)
internal
private
pure
returns (
returns (
bytes32 _hash
bytes32 _hash
)
)
...
...
packages/contracts/contracts/optimistic-ethereum/OVM/chain/OVM_CanonicalTransactionChain.sol
View file @
a1a499f1
...
@@ -3,42 +3,64 @@ pragma solidity ^0.7.0;
...
@@ -3,42 +3,64 @@ pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
pragma experimental ABIEncoderV2;
/* Library Imports */
/* Library Imports */
import { Lib_OVMCodec } from "../../
../
libraries/codec/Lib_OVMCodec.sol";
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_MerkleUtils } from "../../
../
libraries/utils/Lib_MerkleUtils.sol";
import { Lib_MerkleUtils } from "../../libraries/utils/Lib_MerkleUtils.sol";
/* Interface Imports */
/* Interface Imports */
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
import { iOVM_
BaseQueue } from "../../iOVM/queue/iOVM_Base
Queue.sol";
import { iOVM_
L1ToL2TransactionQueue } from "../../iOVM/queue/iOVM_L1ToL2Transaction
Queue.sol";
/* Contract Imports */
/* Contract Imports */
import { OVM_BaseChain } from "./OVM_BaseChain.sol";
import { OVM_BaseChain } from "./OVM_BaseChain.sol";
/**
* @title OVM_CanonicalTransactionChain
*/
contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_BaseChain {
contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_BaseChain {
iOVM_BaseQueue internal ovmL1ToL2TransactionQueue;
/*******************************************
* Contract Variables: Contract References *
*******************************************/
iOVM_L1ToL2TransactionQueue internal ovmL1ToL2TransactionQueue;
address internal sequencer;
address internal sequencer;
/*******************************************
* Contract Variables: Internal Accounting *
*******************************************/
uint256 internal forceInclusionPeriodSeconds;
uint256 internal forceInclusionPeriodSeconds;
uint256 internal lastOVMTimestamp;
uint256 internal lastOVMTimestamp;
/***************
* Constructor *
***************/
/**
* @param _ovmL1ToL2TransactionQueue Address of the OVM_L1ToL2TransactionQueue.
* @param _sequencer Address of the current sequencer.
* @param _forceInclusionPeriodSeconds Period during which only the sequencer can submit.
*/
constructor(
constructor(
address _ovmL1ToL2TransactionQueue,
address _ovmL1ToL2TransactionQueue,
address _sequencer,
address _sequencer,
uint256 _forceInclusionPeriodSeconds,
uint256 _forceInclusionPeriodSeconds
)
) {
Lib_ContractProxyResolver(_libContractProxyManager)
ovmL1ToL2TransactionQueue = iOVM_L1ToL2TransactionQueue(_ovmL1ToL2TransactionQueue);
{
ovmL1ToL2TransactionQueue = iOVM_BaseQueue(_ovmL1ToL2TransactionQueue);
sequencer = _sequencer;
sequencer = _sequencer;
forceInclusionPeriodSeconds = _forceInclusionPeriodSeconds;
forceInclusionPeriodSeconds = _forceInclusionPeriodSeconds;
}
}
modifier onlySequencer() {
require(
msg.sender == sequencer,
"Function can only be called by the sequencer."
);
_;
}
/****************************************
* Public Functions: Batch Manipulation *
****************************************/
/**
* Appends a batch from the L1ToL2TransactionQueue.
*/
function appendQueueBatch()
function appendQueueBatch()
override
override
public
public
...
@@ -59,14 +81,23 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
...
@@ -59,14 +81,23 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
ovmL1ToL2TransactionQueue.dequeue();
ovmL1ToL2TransactionQueue.dequeue();
}
}
/**
* Appends a sequencer batch.
* @param _batch Batch of transactions to append.
* @param _timestamp Timestamp for the provided batch.
*/
function appendSequencerBatch(
function appendSequencerBatch(
bytes[] memory _batch,
bytes[] memory _batch,
uint256 _timestamp
uint256 _timestamp
)
)
override
override
public
public
onlySequencer
{
{
require(
msg.sender == sequencer,
"Function can only be called by the sequencer."
);
require(
require(
_timestamp >= lastOVMTimestamp,
_timestamp >= lastOVMTimestamp,
"Batch timestamp must be later than the last OVM timestamp."
"Batch timestamp must be later than the last OVM timestamp."
...
@@ -87,6 +118,16 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
...
@@ -87,6 +118,16 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
_appendQueueBatch(queueElement, _batch.length);
_appendQueueBatch(queueElement, _batch.length);
}
}
/******************************************
* Internal Functions: Batch Manipulation *
******************************************/
/**
* Appends a queue batch to the chain.
* @param _queueElement Queue element to append.
* @param _batchSize Number of elements in the batch.
*/
function _appendQueueBatch(
function _appendQueueBatch(
Lib_OVMCodec.QueueElement memory _queueElement,
Lib_OVMCodec.QueueElement memory _queueElement,
uint256 _batchSize
uint256 _batchSize
...
...
packages/contracts/contracts/optimistic-ethereum/OVM/chain/OVM_StateCommitmentChain.sol
View file @
a1a499f1
...
@@ -3,7 +3,7 @@ pragma solidity ^0.7.0;
...
@@ -3,7 +3,7 @@ pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
pragma experimental ABIEncoderV2;
/* Library Imports */
/* Library Imports */
import { Lib_OVMCodec } from "../../
../
libraries/codec/Lib_OVMCodec.sol";
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/* Interface Imports */
/* Interface Imports */
import { iOVM_StateCommitmentChain } from "../../iOVM/chain/iOVM_StateCommitmentChain.sol";
import { iOVM_StateCommitmentChain } from "../../iOVM/chain/iOVM_StateCommitmentChain.sol";
...
@@ -13,20 +13,44 @@ import { iOVM_FraudVerifier } from "../../iOVM/execution/iOVM_FraudVerifier.sol"
...
@@ -13,20 +13,44 @@ import { iOVM_FraudVerifier } from "../../iOVM/execution/iOVM_FraudVerifier.sol"
/* Contract Imports */
/* Contract Imports */
import { OVM_BaseChain } from "./OVM_BaseChain.sol";
import { OVM_BaseChain } from "./OVM_BaseChain.sol";
/**
* @title OVM_StateCommitmentChain
*/
contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain {
contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain {
/*******************************************
* Contract Variables: Contract References *
*******************************************/
iOVM_CanonicalTransactionChain internal ovmCanonicalTransactionChain;
iOVM_CanonicalTransactionChain internal ovmCanonicalTransactionChain;
iOVM_FraudVerifier internal ovmFraudVerifier;
iOVM_FraudVerifier internal ovmFraudVerifier;
/***************
* Constructor *
***************/
/**
* @param _ovmCanonicalTransactionChain Address of the OVM_CanonicalTransactionChain.
* @param _ovmFraudVerifier Address of the OVM_FraudVerifier.
*/
constructor(
constructor(
address _ovmCanonicalTransactionChain,
address _ovmCanonicalTransactionChain,
address _ovmFraudVerifier
address _ovmFraudVerifier
)
) {
Lib_ContractProxyResolver(_libContractProxyManager)
{
ovmCanonicalTransactionChain = iOVM_CanonicalTransactionChain(_ovmCanonicalTransactionChain);
ovmCanonicalTransactionChain = iOVM_CanonicalTransactionChain(_ovmCanonicalTransactionChain);
ovmFraudVerifier = iOVM_FraudVerifier(_ovmFraudVerifier);
ovmFraudVerifier = iOVM_FraudVerifier(_ovmFraudVerifier);
}
}
/****************************************
* Public Functions: Batch Manipulation *
****************************************/
/**
* Appends a batch of state roots to the chain.
* @param _batch Batch of state roots.
*/
function appendStateBatch(
function appendStateBatch(
bytes32[] memory _batch
bytes32[] memory _batch
)
)
...
@@ -51,8 +75,12 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain {
...
@@ -51,8 +75,12 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain {
_appendBatch(elements);
_appendBatch(elements);
}
}
/**
* Deletes all state roots after (and including) a given batch.
* @param _batchHeader Header of the batch to start deleting from.
*/
function deleteStateBatch(
function deleteStateBatch(
Lib_OVM
DataTypes.OVM
ChainBatchHeader memory _batchHeader
Lib_OVM
Codec.
ChainBatchHeader memory _batchHeader
)
)
override
override
public
public
...
...
packages/contracts/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol
View file @
a1a499f1
...
@@ -909,7 +909,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
...
@@ -909,7 +909,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
)
)
{
{
_checkAccountLoad(_address);
_checkAccountLoad(_address);
ovmStateManager.getAccountEthAddress(_address);
return
ovmStateManager.getAccountEthAddress(_address);
}
}
/**
/**
...
...
packages/contracts/contracts/optimistic-ethereum/OVM/execution/OVM_FraudVerifier.sol
0 → 100644
View file @
a1a499f1
// 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_FraudVerifier } from "../../iOVM/execution/iOVM_FraudVerifier.sol";
import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol";
import { iOVM_StateTransitioner } from "../../iOVM/execution/iOVM_StateTransitioner.sol";
import { iOVM_StateTransitionerFactory } from "../../iOVM/execution/iOVM_StateTransitionerFactory.sol";
import { iOVM_StateManagerFactory } from "../../iOVM/execution/iOVM_StateManagerFactory.sol";
import { iOVM_StateCommitmentChain } from "../../iOVM/chain/iOVM_StateCommitmentChain.sol";
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
contract OVM_FraudVerifier is iOVM_FraudVerifier {
/*******************************************
* Contract Variables: Contract References *
*******************************************/
iOVM_StateCommitmentChain internal ovmStateCommitmentChain;
iOVM_CanonicalTransactionChain internal ovmCanonicalTransactionChain;
iOVM_ExecutionManager internal ovmExecutionManager;
iOVM_StateManagerFactory internal ovmStateManagerFactory;
/*******************************************
* Contract Variables: Internal Accounting *
*******************************************/
mapping (bytes32 => iOVM_StateTransitioner) internal transitioners;
/***************
* Constructor *
***************/
/**
* @param _ovmStateCommitmentChain Address of the OVM_StateCommitmentChain.
* @param _ovmCanonicalTransactionChain Address of the OVM_CanonicalTransactionChain.
* @param _ovmExecutionManager Address of the OVM_ExecutionManager.
* @param _ovmStateManagerFactory Address of the OVM_StateManagerFactory.
*/
constructor(
address _ovmStateCommitmentChain,
address _ovmCanonicalTransactionChain,
address _ovmExecutionManager,
address _ovmStateManagerFactory,
) {
ovmStateCommitmentChain = iOVM_StateCommitmentChain(_ovmStateCommitmentChain);
ovmCanonicalTransactionChain = iOVM_CanonicalTransactionChain(_ovmCanonicalTransactionChain);
ovmExecutionManager = iOVM_ExecutionManager(_ovmExecutionManager);
ovmStateManagerFactory = iOVM_StateManagerFactory(_ovmStateManagerFactory);
}
/****************************************
* Public Functions: Fraud Verification *
****************************************/
/**
* Begins the fraud verification process.
* @param _preStateRoot State root before the fraudulent transaction.
* @param _preStateRootBatchHeader Batch header for the provided pre-state root.
* @param _preStateRootProof Inclusion proof for the provided pre-state root.
* @param _transaction OVM transaction claimed to be fraudulent.
* @param _transactionBatchHeader Batch header for the provided transaction.
* @param _transactionProof Inclusion proof for the provided transaction.
*/
function initializeFraudVerification(
bytes32 _preStateRoot,
Lib_OVMCodec.ChainBatchHeader memory _preStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _preStateRootProof,
Lib_OVMCodec.TransactionData memory _transaction,
Lib_OVMCodec.ChainBatchHeader memory _transactionBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _transactionProof
)
override
public
{
if (_hasStateTransitioner(_preStateRoot)) {
return;
}
require(
_verifyStateRoot(
_preStateRoot,
_preStateRootBatchHeader,
_preStateRootProof
),
"Invalid pre-state root inclusion proof."
);
require(
_verifyTransaction(
_transaction,
_transactionBatchHeader,
_transactionProof
),
"Invalid transaction inclusion proof."
);
transitioners[_preStateRoot] = iOVM_StateTransitionerFactory(
resolve("OVM_StateTransitionerFactory")
).create(
address(libContractProxyManager),
_preStateRootProof.index,
_preStateRoot,
Lib_OVMCodec.hash(_transaction)
);
}
/**
* Finalizes the fraud verification process.
* @param _preStateRoot State root before the fraudulent transaction.
* @param _preStateRootBatchHeader Batch header for the provided pre-state root.
* @param _preStateRootProof Inclusion proof for the provided pre-state root.
* @param _postStateRoot State root after the fraudulent transaction.
* @param _postStateRootBatchHeader Batch header for the provided post-state root.
* @param _postStateRootProof Inclusion proof for the provided post-state root.
*/
function finalizeFraudVerification(
bytes32 _preStateRoot,
Lib_OVMCodec.ChainBatchHeader memory _preStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _preStateRootProof,
bytes32 _postStateRoot,
Lib_OVMCodec.ChainBatchHeader memory _postStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _postStateRootProof
)
override
public
{
iOVM_StateTransitioner transitioner = transitioners[_preStateRoot];
require(
transitioner.isComplete() == true,
"State transition process must be completed prior to finalization."
);
require(
_postStateRootProof.index == _preStateRootProof.index + 1,
"Invalid post-state root index."
);
require(
_verifyStateRoot(
_preStateRoot,
_preStateRootBatchHeader,
_preStateRootProof
),
"Invalid pre-state root inclusion proof."
);
require(
_verifyStateRoot(
_postStateRoot,
_postStateRootBatchHeader,
_postStateRootProof
),
"Invalid post-state root inclusion proof."
);
require(
_postStateRoot != transitioner.postStateRoot(),
"State transition has not been proven fraudulent."
);
ovmStateCommitmentChain.deleteStateBatch(
_postStateRootBatchHeader
);
}
/************************************
* Internal Functions: Verification *
************************************/
/**
* Checks whether a transitioner already exists for a given pre-state root.
* @param _preStateRoot Pre-state root to check.
* @return _exists Whether or not we already have a transitioner for the root.
*/
function _hasStateTransitioner(
bytes32 _preStateRoot
)
internal
view
returns (
bool _exists
)
{
return address(transitioners[_preStateRoot]) != address(0);
}
/**
* Verifies inclusion of a state root.
* @param _stateRoot State root to verify
* @param _stateRootBatchHeader Batch header for the provided state root.
* @param _stateRootProof Inclusion proof for the provided state root.
* @return _verified Whether or not the root was included.
*/
function _verifyStateRoot(
bytes32 _stateRoot,
Lib_OVMCodec.ChainBatchHeader memory _stateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _stateRootProof
)
internal
view
returns (
bool _verified
)
{
return ovmStateCommitmentChain.ovmBaseChain().verifyElement(
abi.encodePacked(_stateRoot),
_stateRootBatchHeader,
_stateRootProof
);
}
/**
* Verifies inclusion of a given transaction.
* @param _transaction OVM transaction to verify.
* @param _transactionBatchHeader Batch header for the provided transaction.
* @param _transactionProof Inclusion proof for the provided transaction.
* @return _verified Whether or not the transaction was included.
*/
function _verifyTransaction(
Lib_OVMCodec.TransactionData memory _transaction,
Lib_OVMCodec.ChainBatchHeader memory _transactionBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _transactionProof
)
internal
view
returns (
bool _verified
)
{
return ovmCanonicalTransactionChain.ovmBaseChain().verifyElement(
Lib_OVMCodec.encode(_transaction),
_transactionBatchHeader,
_transactionProof
);
}
}
packages/contracts/contracts/optimistic-ethereum/OVM/execution/OVM_StateManagerFactory.sol
0 → 100644
View file @
a1a499f1
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/* Interface Imports */
import { iOVM_StateManager } from "../../iOVM/execution/iOVM_StateManager.sol";
import { iOVM_StateManagerFactory } from "../../iOVM/execution/iOVM_StateManagerFactory.sol";
/* Contract Imports */
import { OVM_StateManager } from "./OVM_StateManager.sol";
/**
* @title OVM_StateManagerFactory
*/
contract OVM_StateManagerFactory is iOVM_StateManagerFactory {
/***************************************
* Public Functions: Contract Creation *
***************************************/
function create()
override
public
returns (
iOVM_StateManager _ovmStateManager
)
{
return new OVM_StateManager();
}
}
packages/contracts/contracts/optimistic-ethereum/OVM/execution/OVM_StateTransitioner.sol
0 → 100644
View file @
a1a499f1
// 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_EthUtils } from "../../../libraries/utils/Lib_EthUtils.sol";
import { Lib_EthMerkleTrie } from "../../../libraries/trie/Lib_EthMerkleTrie.sol";
/* Interface Imports */
import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol";
import { iOVM_StateManager } from "../../iOVM/execution/iOVM_StateManager.sol";
import { iOVM_StateManagerFactory } from "../../iOVM/execution/iOVM_StateManagerFactory.sol";
import { iOVM_StateTransitioner } from "../../iOVM/execution/iOVM_StateTransitioner.sol";
/**
* @title OVM_StateTransitioner
*/
contract OVM_StateTransitioner is iOVM_StateTransitioner {
/*******************
* Data Structures *
*******************/
enum TransitionPhase {
PRE_EXECUTION,
POST_EXECUTION,
COMPLETE
}
/*******************************************
* Contract Variables: Contract References *
*******************************************/
iOVM_ExecutionManager internal ovmExecutionManager;
iOVM_StateManager internal ovmStateManager;
/*******************************************
* Contract Variables: Internal Accounting *
*******************************************/
bytes32 internal preStateRoot;
bytes32 internal postStateRoot;
TransitionPhase internal phase;
uint256 public stateTransitionIndex;
bytes32 public transactionHash;
/***************
* Constructor *
***************/
/**
* @param _ovmExecutionManager Address of the OVM_ExecutionManager.
* @param _ovmStateManagerFactory Address of the OVM_StateManagerFactory.
* @param _stateTransitionIndex Index of the state transition being verified.
* @param _preStateRoot State root before the transition was executed.
* @param _transactionHash Hash of the executed transaction.
*/
constructor(
address _ovmExecutionManager,
address _ovmStateManagerFactory,
uint256 _stateTransitionIndex,
bytes32 _preStateRoot,
bytes32 _transactionHash
) {
stateTransitionIndex = _stateTransitionIndex;
preStateRoot = _preStateRoot;
postStateRoot = _preStateRoot;
transactionHash = _transactionHash;
ovmExecutionManager = iOVM_ExecutionManager(_ovmExecutionManager);
ovmStateManager = iOVM_StateManagerFactory(_ovmStateManagerFactory).create();
}
/**********************
* Function Modifiers *
**********************/
/**
* Checks that a function is only run during a specific phase.
* @param _phase Phase the function must run within.
*/
modifier onlyDuringPhase(
TransitionPhase _phase
) {
require(
phase == _phase,
"Function must be called during the correct phase."
);
_;
}
/**********************************
* Public Functions: State Access *
**********************************/
/**
* Retrieves the state root before execution.
* @return _preStateRoot State root before execution.
*/
function getPreStateRoot()
override
public
view
returns (
bytes32 _preStateRoot
)
{
return preStateRoot;
}
/**
* Retrieves the state root after execution.
* @return _preStateRoot State root after execution.
*/
function getPostStateRoot()
override
public
view
returns (
bytes32 _postStateRoot
)
{
return postStateRoot;
}
/**
* Checks whether the transitioner is complete.
* @return _complete Whether or not the transition process is finished.
*/
function isComplete()
override
public
view
returns (
bool _complete
)
{
return phase == TransitionPhase.COMPLETE;
}
/***********************************
* Public Functions: Pre-Execution *
***********************************/
/**
* Allows a user to prove the initial state of a contract.
* @param _ovmContractAddress Address of the contract on the OVM.
* @param _account Claimed account state.
* @param _stateTrieWitness Proof of the account state.
*/
function proveContractState(
address _ovmContractAddress,
Lib_OVMCodec.Account memory _account,
bytes memory _stateTrieWitness
)
override
public
onlyDuringPhase(TransitionPhase.PRE_EXECUTION)
{
require(
_account.codeHash == Lib_EthUtils.getCodeHash(_account.ethAddress),
"Invalid code hash provided."
);
require(
Lib_EthMerkleTrie.proveAccountState(
_ovmContractAddress,
_account,
_stateTrieWitness,
preStateRoot
),
"Invalid account state provided."
);
ovmStateManager.putAccount(
_ovmContractAddress,
_account
);
}
/**
* Allows a user to prove the initial state of a contract storage slot.
* @param _ovmContractAddress Address of the contract on the OVM.
* @param _key Claimed account slot key.
* @param _key Claimed account slot value.
* @param _stateTrieWitness Proof of the account state.
* @param _stateTrieWitness Proof of the storage slot.
*/
function proveStorageSlot(
address _ovmContractAddress,
bytes32 _key,
bytes32 _value,
bytes memory _stateTrieWitness,
bytes memory _storageTrieWitness
)
override
public
onlyDuringPhase(TransitionPhase.PRE_EXECUTION)
{
require(
ovmStateManager.hasAccount(_ovmContractAddress) == true,
"Contract must be verified before proving a storage slot."
);
require(
Lib_EthMerkleTrie.proveAccountStorageSlotValue(
_ovmContractAddress,
_key,
_value,
_stateTrieWitness,
_storageTrieWitness,
preStateRoot
),
"Invalid account state provided."
);
ovmStateManager.putContractStorage(
_ovmContractAddress,
_key,
_value
);
}
/*******************************
* Public Functions: Execution *
*******************************/
/**
* Executes the state transition.
* @param _transaction OVM transaction to execute.
*/
function applyTransaction(
Lib_OVMCodec.Transaction memory _transaction
)
override
public
{
require(
libOVMCodec.hash(_transaction) == transactionHash,
"Invalid transaction provided."
);
// TODO: Set state manager for EM here.
ovmExecutionManager.run(_transaction);
phase = TransitionPhase.POST_EXECUTION;
}
/************************************
* Public Functions: Post-Execution *
************************************/
/**
* Allows a user to commit the final state of a contract.
* @param _ovmContractAddress Address of the contract on the OVM.
* @param _account Claimed account state.
* @param _stateTrieWitness Proof of the account state.
*/
function commitContractState(
address _ovmContractAddress,
Lib_OVMCodec.Account memory _account,
bytes memory _stateTrieWitness
)
override
public
onlyDuringPhase(TransitionPhase.POST_EXECUTION)
{
require(
ovmStateManager.isUncommittedAccount(_ovmContractAddress) == true,
"Provided account is not uncommitted."
);
postStateRoot = Lib_EthMerkleTrie.updateAccountState(
_ovmContractAddress,
_account,
_stateTrieWitness,
postStateRoot
);
ovmStateManager.commitAccount(_ovmContractAddress);
}
/**
* Allows a user to commit the final state of a contract storage slot.
* @param _ovmContractAddress Address of the contract on the OVM.
* @param _key Claimed account slot key.
* @param _key Claimed account slot value.
* @param _stateTrieWitness Proof of the account state.
* @param _stateTrieWitness Proof of the storage slot.
*/
function commitStorageSlot(
address _ovmContractAddress,
bytes32 _key,
bytes32 _value,
bytes memory _stateTrieWitness,
bytes memory _storageTrieWitness
)
override
public
onlyDuringPhase(TransitionPhase.POST_EXECUTION)
{
require(
ovmStateManager.isUncommittedStorage(_ovmContractAddress, _key, _value) == true,
"Provided storage slot is not uncommitted."
);
postStateRoot = Lib_EthMerkleTrie.updateAccountStorageSlotValue(
_ovmContractAddress,
_key,
_value,
_stateTrieWitness,
_storageTrieWitness,
postStateRoot
);
ovmStateManager.commitStorage(_ovmContractAddress, _key, _value);
}
/**********************************
* Public Functions: Finalization *
**********************************/
/**
* Finalizes the transition process.
*/
function completeTransition()
override
public
onlyDuringPhase(TransitionPhase.POST_EXECUTION)
{
require(
ovmStateManager.totalUncommittedAccounts() == 0,
"All accounts must be committed before completing a transition."
);
require(
ovmStateManager.totalUncommittedStorage() == 0,
"All storage must be committed before completing a transition."
);
phase = TransitionPhase.COMPLETE;
}
}
packages/contracts/contracts/optimistic-ethereum/OVM/execution/OVM_StateTransitionerFactory.sol
0 → 100644
View file @
a1a499f1
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/* Interface Imports */
import { iOVM_StateTransitioner } from "../../iOVM/execution/iOVM_StateTransitioner.sol";
import { iOVM_StateTransitionerFactory } from "../../iOVM/execution/iOVM_StateTransitionerFactory.sol";
/* Contract Imports */
import { OVM_StateTransitioner } from "./OVM_StateTransitioner.sol";
/**
* @title OVM_StateTransitionerFactory
*/
contract OVM_StateTransitionerFactory is iOVM_StateTransitionerFactory {
/***************************************
* Public Functions: Contract Creation *
***************************************/
function create(
address _libContractProxyManager,
uint256 _stateTransitionIndex,
bytes32 _preStateRoot,
bytes32 _transactionHash
)
override
public
returns (
iOVM_StateTransitioner _ovmStateTransitioner
)
{
return new OVM_StateTransitioner(
_libContractProxyManager,
_stateTransitionIndex,
_preStateRoot,
_transactionHash
);
}
}
packages/contracts/contracts/optimistic-ethereum/OVM/queue/OVM_BaseQueue.sol
0 → 100644
View file @
a1a499f1
// 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_BaseQueue } from "../../iOVM/queue/iOVM_BaseQueue.sol";
/**
* @title OVM_BaseQueue
*/
contract OVM_BaseQueue is iOVM_BaseQueue {
/****************************************
* Contract Variables: Internal Storage *
****************************************/
Lib_OVMCodec.QueueElement[] internal queue;
uint256 internal front;
/**********************
* Function Modifiers *
**********************/
/**
* Asserts that the queue is not empty.
*/
modifier notEmpty() {
require(
size() > 0,
"Queue is empty."
);
_;
}
/**********************************
* Public Functions: Queue Access *
**********************************/
/**
* Gets the size of the queue.
* @return _size Number of elements in the queue.
*/
function size()
override
public
view
returns (
uint256 _size
)
{
return front >= queue.length ? 0 : queue.length - front;
}
/**
* Gets the top element of the queue.
* @return _element First element in the queue.
*/
function peek()
override
public
view
notEmpty
returns (
Lib_OVMCodec.QueueElement memory _element
)
{
return queue[front];
}
/******************************************
* Internal Functions: Queue Manipulation *
******************************************/
/**
* Adds an element to the queue.
* @param _element Queue element to add to the queue.
*/
function _enqueue(
Lib_OVMCodec.QueueElement memory _element
)
internal
{
queue.push(_element);
}
/**
* Pops an element from the queue.
* @return _element Queue element popped from the queue.
*/
function _dequeue()
internal
notEmpty
returns (
Lib_OVMCodec.QueueElement memory _element
)
{
_element = queue[front];
delete queue[front];
front += 1;
return _element;
}
}
packages/contracts/contracts/optimistic-ethereum/OVM/queue/OVM_L1ToL2TransactionQueue.sol
0 → 100644
View file @
a1a499f1
// 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_L1ToL2TransactionQueue } from "../../iOVM/queue/iOVM_L1ToL2TransactionQueue.sol";
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
/* Contract Imports */
import { OVM_BaseQueue } from "./OVM_BaseQueue.sol";
/**
* @title OVM_L1ToL2TransactionQueue
*/
contract OVM_L1ToL2TransactionQueue is iOVM_L1ToL2TransactionQueue, OVM_BaseQueue {
/*******************************************
* Contract Variables: Contract References *
*******************************************/
iOVM_CanonicalTransactionChain internal ovmCanonicalTransactionChain;
/***************
* Constructor *
***************/
/**
* @param _ovmCanonicalTransactionChain Address of the OVM_CanonicalTransactionChain.
*/
constructor(
address _ovmCanonicalTransactionChain
) {
ovmCanonicalTransactionChain = iOVM_CanonicalTransactionChain(_ovmCanonicalTransactionChain);
}
/****************************************
* Public Functions: Queue Manipulation *
****************************************/
/**
* Adds an element to the queue.
* @param _element Queue element to add to the queue.
*/
function enqueue(
Lib_OVMCodec.QueueElement memory _element
)
override
public
{
_enqueue(_element);
}
/**
* Pops an element from the queue.
* @return _element Queue element popped from the queue.
*/
function dequeue()
override
public
returns (
Lib_OVMCodec.QueueElement memory _element
)
{
require(
msg.sender == address(ovmCanonicalTransactionChain),
"Sender is not allowed to enqueue."
);
return _dequeue();
}
}
packages/contracts/contracts/optimistic-ethereum/iOVM/chain/iOVM_BaseChain.sol
View file @
a1a499f1
...
@@ -3,7 +3,7 @@ pragma solidity ^0.7.0;
...
@@ -3,7 +3,7 @@ pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
pragma experimental ABIEncoderV2;
/* Library Imports */
/* Library Imports */
import { Lib_OVMCodec } from "../../
../
libraries/codec/Lib_OVMCodec.sol";
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/**
/**
* @title iOVM_BaseChain
* @title iOVM_BaseChain
...
...
packages/contracts/contracts/optimistic-ethereum/iOVM/chain/iOVM_CanonicalTransactionChain.sol
View file @
a1a499f1
...
@@ -5,7 +5,15 @@ pragma experimental ABIEncoderV2;
...
@@ -5,7 +5,15 @@ pragma experimental ABIEncoderV2;
/* Interface Imports */
/* Interface Imports */
import { iOVM_BaseChain } from "./iOVM_BaseChain.sol";
import { iOVM_BaseChain } from "./iOVM_BaseChain.sol";
/**
* @title iOVM_CanonicalTransactionChain
*/
interface iOVM_CanonicalTransactionChain is iOVM_BaseChain {
interface iOVM_CanonicalTransactionChain is iOVM_BaseChain {
/****************************************
* Public Functions: Batch Manipulation *
****************************************/
function appendQueueBatch() external;
function appendQueueBatch() external;
function appendSequencerBatch(bytes[] calldata _batch, uint256 _timestamp) external;
function appendSequencerBatch(bytes[] calldata _batch, uint256 _timestamp) external;
}
}
packages/contracts/contracts/optimistic-ethereum/iOVM/chain/iOVM_StateCommitmentChain.sol
View file @
a1a499f1
...
@@ -6,9 +6,17 @@ pragma experimental ABIEncoderV2;
...
@@ -6,9 +6,17 @@ pragma experimental ABIEncoderV2;
import { iOVM_BaseChain } from "./iOVM_BaseChain.sol";
import { iOVM_BaseChain } from "./iOVM_BaseChain.sol";
/* Library Imports */
/* Library Imports */
import { Lib_OVMCodec } from "../../
../
libraries/codec/Lib_OVMCodec.sol";
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/**
* @title iOVM_StateCommitmentChain
*/
interface iOVM_StateCommitmentChain is iOVM_BaseChain {
interface iOVM_StateCommitmentChain is iOVM_BaseChain {
/****************************************
* Public Functions: Batch Manipulation *
****************************************/
function appendStateBatch(bytes32[] calldata _batch) external;
function appendStateBatch(bytes32[] calldata _batch) external;
function deleteStateBatch(Lib_OVMCodec.ChainBatchHeader memory _batchHeader) external;
function deleteStateBatch(Lib_OVMCodec.ChainBatchHeader memory _batchHeader) external;
}
}
packages/contracts/contracts/optimistic-ethereum/iOVM/execution/iOVM_FraudVerifier.sol
0 → 100644
View file @
a1a499f1
// 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_FraudVerifier
*/
interface iOVM_FraudVerifier {
/****************************************
* Public Functions: Fraud Verification *
****************************************/
function initializeFraudVerification(
bytes32 _preStateRoot,
Lib_OVMCodec.ChainBatchHeader calldata _preStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof calldata _preStateRootProof,
Lib_OVMCodec.Transaction calldata _transaction,
Lib_OVMCodec.ChainBatchHeader calldata _transactionBatchHeader,
Lib_OVMCodec.ChainInclusionProof calldata _transactionProof
) external;
function finalizeFraudVerification(
bytes32 _preStateRoot,
Lib_OVMCodec.ChainBatchHeader calldata _preStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof calldata _preStateRootProof,
bytes32 _postStateRoot,
Lib_OVMCodec.ChainBatchHeader calldata _postStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof calldata _postStateRootProof
) external;
}
packages/contracts/contracts/optimistic-ethereum/iOVM/execution/iOVM_StateManagerFactory.sol
0 → 100644
View file @
a1a499f1
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/* Contract Imports */
import { iOVM_StateManager } from "./iOVM_StateManager.sol";
/**
* @title iOVM_StateManagerFactory
*/
interface iOVM_StateManagerFactory {
/***************************************
* Public Functions: Contract Creation *
***************************************/
function create()
external
returns (
iOVM_StateManager _ovmStateManager
);
}
packages/contracts/contracts/optimistic-ethereum/iOVM/execution/iOVM_StateTransitioner.sol
0 → 100644
View file @
a1a499f1
// 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_StateTransitioner
*/
interface iOVM_StateTransitioner {
/**********************************
* Public Functions: State Access *
**********************************/
function getPreStateRoot() external view returns (bytes32 _preStateRoot);
function getPostStateRoot() external view returns (bytes32 _postStateRoot);
function isComplete() external view returns (bool _complete);
/***********************************
* Public Functions: Pre-Execution *
***********************************/
function proveContractState(
address _ovmContractAddress,
Lib_OVMCodec.Account calldata _account,
bytes calldata _stateTrieWitness
) external;
function proveStorageSlot(
address _ovmContractAddress,
bytes32 _key,
bytes32 _value,
bytes calldata _stateTrieWitness,
bytes calldata _storageTrieWitness
) external;
/*******************************
* Public Functions: Execution *
*******************************/
function applyTransaction(
Lib_OVMCodec.TransactionData calldata _transaction
) external;
/************************************
* Public Functions: Post-Execution *
************************************/
function commitContractState(
address _ovmContractAddress,
Lib_OVMCodec.Account calldata _account,
bytes calldata _stateTrieWitness
) external;
function commitStorageSlot(
address _ovmContractAddress,
bytes32 _key,
bytes32 _value,
bytes calldata _stateTrieWitness,
bytes calldata _storageTrieWitness
) external;
/**********************************
* Public Functions: Finalization *
**********************************/
function completeTransition() external;
}
packages/contracts/contracts/optimistic-ethereum/iOVM/execution/iOVM_StateTransitionerFactory.sol
0 → 100644
View file @
a1a499f1
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/* Contract Imports */
import { iOVM_StateTransitioner } from "./iOVM_StateTransitioner.sol";
/**
* @title iOVM_StateTransitionerFactory
*/
interface iOVM_StateTransitionerFactory {
/***************************************
* Public Functions: Contract Creation *
***************************************/
function create(
address _libContractProxyManager,
uint256 _stateTransitionIndex,
bytes32 _preStateRoot,
bytes32 _transactionHash
)
external
returns (
iOVM_StateTransitioner _ovmStateTransitioner
);
}
packages/contracts/contracts/optimistic-ethereum/iOVM/queue/iOVM_BaseQueue.sol
0 → 100644
View file @
a1a499f1
// 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_BaseQueue
*/
interface iOVM_BaseQueue {
/**********************************
* Public Functions: Queue Access *
**********************************/
function size() external view returns (uint256 _size);
function peek() external view returns (Lib_OVMCodec.QueueElement memory _element);
}
packages/contracts/contracts/optimistic-ethereum/iOVM/queue/iOVM_L1ToL2TransactionQueue.sol
0 → 100644
View file @
a1a499f1
// 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_BaseQueue } from "./iOVM_BaseQueue.sol";
/**
* @title iOVM_L1ToL2TransactionQueue
*/
interface iOVM_L1ToL2TransactionQueue is iOVM_BaseQueue {
/****************************************
* Public Functions: Queue Manipulation *
****************************************/
function enqueue(Lib_OVMCodec.QueueElement memory _element) external;
function dequeue() external returns (Lib_OVMCodec.QueueElement memory _element);
}
packages/contracts/contracts/optimistic-ethereum/iOVM/trie/Lib_EthMerkleTrie.sol
0 → 100644
View file @
a1a499f1
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_SecureMerkleTrie } from "./Lib_SecureMerkleTrie.sol";
import { Lib_RLPReader } from "../rlp/Lib_RLPReader.sol";
import { Lib_OVMCodec } from "../codec/Lib_OVMCodec.sol";
/**
* @title Lib_EthMerkleTrie
*/
library Lib_EthMerkleTrie {
/**********************
* Contract Constants *
**********************/
bytes32 constant private BYTES32_NULL = bytes32('');
uint256 constant private UINT256_NULL = uint256(0);
/*************************************
* Internal Functions: Storage Slots *
*************************************/
/**
* @notice Verifies a proof for the value of an account storage slot.
* @param _address Address of the contract account.
* @param _key Key for the storage slot.
* @param _value Value for the storage slot.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _storageTrieWitness Inclusion proof for the specific storage
* slot associated with the given key.
* @param _stateTrieRoot Known root of the state trie.
* @return `true` if the k/v pair is included, `false` otherwise.
*/
function proveAccountStorageSlotValue(
address _address,
bytes32 _key,
bytes32 _value,
bytes memory _stateTrieWitness,
bytes memory _storageTrieWitness,
bytes32 _stateTrieRoot
)
internal
view
returns (bool)
{
// Retrieve the current storage root.
Lib_OVMCodec.EVMAccount memory accountState = getAccountState(
_address,
_stateTrieWitness,
_stateTrieRoot
);
// Verify inclusion of the given k/v pair in the storage trie.
return verifyInclusionProof(
abi.encodePacked(_key),
abi.encodePacked(_value),
_storageTrieWitness,
accountState.storageRoot
);
}
/**
* @notice Updates the value for a given account storage slot.
* @param _address Address of the contract account.
* @param _key Key for the storage slot.
* @param _value New value for the storage slot.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _storageTrieWitness Inclusion proof for the specific storage
* slot associated with the given key.
* @param _stateTrieRoot Known root of the state trie.
* @return Root hash of the updated state trie.
*/
function updateAccountStorageSlotValue(
address _address,
bytes32 _key,
bytes32 _value,
bytes memory _stateTrieWitness,
bytes memory _storageTrieWitness,
bytes32 _stateTrieRoot
)
internal
view
returns (bytes32)
{
// Retreive the old storage root.
Lib_OVMCodec.EVMAccount memory accountState = getAccountState(
_address,
_stateTrieWitness,
_stateTrieRoot
);
// Generate a new storage root.
accountState.storageRoot = update(
abi.encodePacked(_key),
abi.encodePacked(_value),
_storageTrieWitness,
accountState.storageRoot
);
// Update the state trie with the new storage root.
return setAccountState(
accountState,
_address,
_stateTrieWitness,
_stateTrieRoot
);
}
/**************************************
* Internal Functions: Account Proofs *
*************************************/
/**
* @notice Verifies a proof of the current state for a given account.
* @param _address Address of the target account.
* @param _accountState Account state object to verify.
* @param _proofMatrix Matrix of fields to verify or ignore.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _stateTrieRoot Known root of the state trie.
* @return `true` if the given account state is valid, `false` otherwise.
*/
function proveAccountState(
address _address,
Lib_OVMCodec.EVMAccount memory _accountState,
Lib_OVMCodec.ProofMatrix memory _proofMatrix,
bytes memory _stateTrieWitness,
bytes32 _stateTrieRoot
)
internal
view
returns (bool)
{
// Pull the current account state.
Lib_OVMCodec.EVMAccount memory accountState = getAccountState(
_address,
_stateTrieWitness,
_stateTrieRoot
);
// Check each provided component conditionally.
return (
(!_proofMatrix.checkNonce || accountState.nonce == _accountState.nonce) &&
(!_proofMatrix.checkBalance || accountState.balance == _accountState.balance) &&
(!_proofMatrix.checkStorageRoot || accountState.storageRoot == _accountState.storageRoot) &&
(!_proofMatrix.checkCodeHash || accountState.codeHash == _accountState.codeHash)
);
}
/**
* @notice Verifies a proof of the current state for a given account.
* @param _address Address of the target account.
* @param _accountState Account state object to verify.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _stateTrieRoot Known root of the state trie.
* @return `true` if the given account state is valid, `false` otherwise.
*/
function proveAccountState(
address _address,
Lib_OVMCodec.EVMAccount memory _accountState,
bytes memory _stateTrieWitness,
bytes32 _stateTrieRoot
)
internal
view
returns (bool)
{
return proveAccountState(
_address,
_accountState,
Lib_OVMCodec.ProofMatrix({
checkNonce: true,
checkBalance: true,
checkStorageRoot: true,
checkCodeHash: true
}),
_stateTrieWitness,
_stateTrieRoot
);
}
/**
* @notice Verifies a proof of the current state for a given account.
* @param _address Address of the target account.
* @param _accountState Account state object to verify.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _stateTrieRoot Known root of the state trie.
* @return `true` if the given account state is valid, `false` otherwise.
*/
function proveAccountState(
address _address,
Lib_OVMCodec.Account memory _accountState,
bytes memory _stateTrieWitness,
bytes32 _stateTrieRoot
)
internal
view
returns (bool)
{
return proveAccountState(
_address,
Lib_OVMCodec.EVMAccount({
nonce: _accountState.nonce,
balance: _accountState.balance,
storageRoot: _accountState.storageRoot,
codeHash: _accountState.codeHash
}),
_stateTrieWitness,
_stateTrieRoot
);
}
/**
* @notice Verifies a proof of the account nonce.
* @param _address Address of the target account.
* @param _nonce Account transaction nonce.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _stateTrieRoot Known root of the state trie.
* @return `true` if the given nonce is valid, `false` otherwise.
*/
function proveAccountNonce(
address _address,
uint256 _nonce,
bytes memory _stateTrieWitness,
bytes32 _stateTrieRoot
)
internal
view
returns (bool)
{
return proveAccountState(
_address,
Lib_OVMCodec.EVMAccount({
nonce: _nonce,
balance: UINT256_NULL,
storageRoot: BYTES32_NULL,
codeHash: BYTES32_NULL
}),
Lib_OVMCodec.ProofMatrix({
checkNonce: true,
checkBalance: false,
checkStorageRoot: false,
checkCodeHash: false
}),
_stateTrieWitness,
_stateTrieRoot
);
}
/**
* @notice Verifies a proof of the account balance.
* @param _address Address of the target account.
* @param _balance Account balance in wei.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _stateTrieRoot Known root of the state trie.
* @return `true` if the given balance is valid, `false` otherwise.
*/
function proveAccountBalance(
address _address,
uint256 _balance,
bytes memory _stateTrieWitness,
bytes32 _stateTrieRoot
)
internal
view
returns (bool)
{
return proveAccountState(
_address,
Lib_OVMCodec.EVMAccount({
nonce: UINT256_NULL,
balance: _balance,
storageRoot: BYTES32_NULL,
codeHash: BYTES32_NULL
}),
Lib_OVMCodec.ProofMatrix({
checkNonce: false,
checkBalance: true,
checkStorageRoot: false,
checkCodeHash: false
}),
_stateTrieWitness,
_stateTrieRoot
);
}
/**
* @notice Verifies a proof of the account storage root.
* @param _address Address of the target account.
* @param _storageRoot Account storage root, empty if EOA.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _stateTrieRoot Known root of the state trie.
* @return `true` if the given storage root is valid, `false` otherwise.
*/
function proveAccountStorageRoot(
address _address,
bytes32 _storageRoot,
bytes memory _stateTrieWitness,
bytes32 _stateTrieRoot
)
internal
view
returns (bool)
{
return proveAccountState(
_address,
Lib_OVMCodec.EVMAccount({
nonce: UINT256_NULL,
balance: UINT256_NULL,
storageRoot: _storageRoot,
codeHash: BYTES32_NULL
}),
Lib_OVMCodec.ProofMatrix({
checkNonce: false,
checkBalance: false,
checkStorageRoot: true,
checkCodeHash: false
}),
_stateTrieWitness,
_stateTrieRoot
);
}
/**
* @notice Verifies a proof of the account code hash.
* @param _address Address of the target account.
* @param _codeHash Account code hash, empty if EOA.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _stateTrieRoot Known root of the state trie.
* @return `true` if the given code hash is valid, `false` otherwise.
*/
function proveAccountCodeHash(
address _address,
bytes32 _codeHash,
bytes memory _stateTrieWitness,
bytes32 _stateTrieRoot
)
internal
view
returns (bool)
{
return proveAccountState(
_address,
Lib_OVMCodec.EVMAccount({
nonce: UINT256_NULL,
balance: UINT256_NULL,
storageRoot: BYTES32_NULL,
codeHash: _codeHash
}),
Lib_OVMCodec.ProofMatrix({
checkNonce: false,
checkBalance: false,
checkStorageRoot: false,
checkCodeHash: true
}),
_stateTrieWitness,
_stateTrieRoot
);
}
/***************************************
* Internal Functions: Account Updates *
***************************************/
/**
* @notice Updates the current state for a given account.
* @param _address Address of the target account.
* @param _accountState Account state to insert.
* @param _proofMatrix Matrix of fields to update or ignore.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _stateTrieRoot Known root of the state trie.
* @return Root hash of the updated state trie.
*/
function updateAccountState(
address _address,
Lib_OVMCodec.EVMAccount memory _accountState,
Lib_OVMCodec.ProofMatrix memory _proofMatrix,
bytes memory _stateTrieWitness,
bytes32 _stateTrieRoot
)
internal
view
returns (bytes32)
{
Lib_OVMCodec.EVMAccount memory newAccountState = _accountState;
// If the user has provided everything, don't bother pulling the
// current account state.
if (
!_proofMatrix.checkNonce ||
!_proofMatrix.checkBalance ||
!_proofMatrix.checkStorageRoot ||
!_proofMatrix.checkCodeHash
) {
// Pull the old account state.
Lib_OVMCodec.EVMAccount memory oldAccountState = getAccountState(
_address,
_stateTrieWitness,
_stateTrieRoot
);
// Conditionally update elements that haven't been provided with
// elements from the old account state.
if (!_proofMatrix.checkNonce) {
newAccountState.nonce = oldAccountState.nonce;
}
if (!_proofMatrix.checkBalance) {
newAccountState.balance = oldAccountState.balance;
}
if (!_proofMatrix.checkStorageRoot) {
newAccountState.storageRoot = oldAccountState.storageRoot;
}
if (!_proofMatrix.checkCodeHash) {
newAccountState.codeHash = oldAccountState.codeHash;
}
}
// Update the account state.
return setAccountState(
newAccountState,
_address,
_stateTrieWitness,
_stateTrieRoot
);
}
/**
* @notice Updates the current state for a given account.
* @param _address Address of the target account.
* @param _accountState Account state to insert.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _stateTrieRoot Known root of the state trie.
* @return Root hash of the updated state trie.
*/
function updateAccountState(
address _address,
Lib_OVMCodec.EVMAccount memory _accountState,
bytes memory _stateTrieWitness,
bytes32 _stateTrieRoot
)
internal
view
returns (bytes32)
{
return updateAccountState(
_address,
_accountState,
Lib_OVMCodec.ProofMatrix({
checkNonce: true,
checkBalance: true,
checkStorageRoot: true,
checkCodeHash: true
}),
_stateTrieWitness,
_stateTrieRoot
);
}
/**
* @notice Updates the current state for a given account.
* @param _address Address of the target account.
* @param _accountState Account state to insert.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _stateTrieRoot Known root of the state trie.
* @return Root hash of the updated state trie.
*/
function updateAccountState(
address _address,
Lib_OVMCodec.Account memory _accountState,
bytes memory _stateTrieWitness,
bytes32 _stateTrieRoot
)
internal
view
returns (bytes32)
{
return updateAccountState(
_address,
Lib_OVMCodec.EVMAccount({
nonce: _accountState.nonce,
balance: _accountState.balance,
storageRoot: _accountState.storageRoot,
codeHash: _accountState.codeHash
}),
_stateTrieWitness,
_stateTrieRoot
);
}
/**
* @notice Updates an account nonce.
* @param _address Address of the target account.
* @param _nonce New account transaction nonce.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _stateTrieRoot Known root of the state trie.
* @return Root hash of the updated state trie.
*/
function updateAccountNonce(
address _address,
uint256 _nonce,
bytes memory _stateTrieWitness,
bytes32 _stateTrieRoot
)
internal
view
returns (bytes32)
{
return updateAccountState(
_address,
Lib_OVMCodec.EVMAccount({
nonce: _nonce,
balance: UINT256_NULL,
storageRoot: BYTES32_NULL,
codeHash: BYTES32_NULL
}),
Lib_OVMCodec.ProofMatrix({
checkNonce: true,
checkBalance: false,
checkStorageRoot: false,
checkCodeHash: false
}),
_stateTrieWitness,
_stateTrieRoot
);
}
/**
* @notice Updates an account balance.
* @param _address Address of the target account.
* @param _balance New account balance in wei.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _stateTrieRoot Known root of the state trie.
* @return Root hash of the updated state trie.
*/
function updateAccountBalance(
address _address,
uint256 _balance,
bytes memory _stateTrieWitness,
bytes32 _stateTrieRoot
)
internal
view
returns (bytes32)
{
return updateAccountState(
_address,
Lib_OVMCodec.EVMAccount({
nonce: UINT256_NULL,
balance: _balance,
storageRoot: BYTES32_NULL,
codeHash: BYTES32_NULL
}),
Lib_OVMCodec.ProofMatrix({
checkNonce: false,
checkBalance: true,
checkStorageRoot: false,
checkCodeHash: false
}),
_stateTrieWitness,
_stateTrieRoot
);
}
/**
* @notice Updates an account storage root.
* @param _address Address of the target account.
* @param _storageRoot New account storage root, empty if EOA.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _stateTrieRoot Known root of the state trie.
* @return Root hash of the updated state trie.
*/
function updateAccountStorageRoot(
address _address,
bytes32 _storageRoot,
bytes memory _stateTrieWitness,
bytes32 _stateTrieRoot
)
internal
view
returns (bytes32)
{
return updateAccountState(
_address,
Lib_OVMCodec.EVMAccount({
nonce: UINT256_NULL,
balance: UINT256_NULL,
storageRoot: _storageRoot,
codeHash: BYTES32_NULL
}),
Lib_OVMCodec.ProofMatrix({
checkNonce: false,
checkBalance: false,
checkStorageRoot: true,
checkCodeHash: false
}),
_stateTrieWitness,
_stateTrieRoot
);
}
/**
* @notice Updates an account code hash.
* @param _address Address of the target account.
* @param _codeHash New account code hash, empty if EOA.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _stateTrieRoot Known root of the state trie.
* @return Root hash of the updated state trie.
*/
function updateAccountCodeHash(
address _address,
bytes32 _codeHash,
bytes memory _stateTrieWitness,
bytes32 _stateTrieRoot
)
internal
view
returns (bytes32)
{
return updateAccountState(
_address,
Lib_OVMCodec.EVMAccount({
nonce: UINT256_NULL,
balance: UINT256_NULL,
storageRoot: BYTES32_NULL,
codeHash: _codeHash
}),
Lib_OVMCodec.ProofMatrix({
checkNonce: false,
checkBalance: false,
checkStorageRoot: false,
checkCodeHash: true
}),
_stateTrieWitness,
_stateTrieRoot
);
}
/*********************
* Private Functions *
*********************/
/**
* @notice Decodes an RLP-encoded account state into a useful struct.
* @param _encodedAccountState RLP-encoded account state.
* @return Account state struct.
*/
function decodeAccountState(
bytes memory _encodedAccountState
)
private
view
returns (Lib_OVMCodec.EVMAccount memory)
{
Lib_RLPReader.RLPItem[] memory accountState = libRLPReader.toList(libRLPReader.toRlpItem(_encodedAccountState));
return Lib_OVMCodec.EVMAccount({
nonce: libRLPReader.toUint(accountState[0]),
balance: libRLPReader.toUint(accountState[1]),
storageRoot: libByteUtils.toBytes32(libRLPReader.toBytes(accountState[2])),
codeHash: libByteUtils.toBytes32(libRLPReader.toBytes(accountState[3]))
});
}
/**
* @notice RLP-encodes an account state struct.
* @param _accountState Account state struct.
* @return RLP-encoded account state.
*/
function encodeAccountState(
Lib_OVMCodec.EVMAccount memory _accountState
)
private
view
returns (bytes memory)
{
bytes[] memory raw = new bytes[](4);
// Unfortunately we can't create this array outright because
// RLPWriter.encodeList will reject fixed-size arrays. Assigning
// index-by-index circumvents this issue.
raw[0] = libRLPWriter.encodeUint(_accountState.nonce);
raw[1] = libRLPWriter.encodeUint(_accountState.balance);
raw[2] = _accountState.storageRoot == 0 ? RLP_NULL_BYTES : libRLPWriter.encodeBytes(abi.encodePacked(_accountState.storageRoot));
raw[3] = _accountState.codeHash == 0 ? RLP_NULL_BYTES : libRLPWriter.encodeBytes(abi.encodePacked(_accountState.codeHash));
return libRLPWriter.encodeList(raw);
}
/**
* @notice Retrieves the current account state and converts into a struct.
* @param _address Account address.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _stateTrieRoot Known root of the state trie.
*/
function getAccountState(
address _address,
bytes memory _stateTrieWitness,
bytes32 _stateTrieRoot
)
private
view
returns (Lib_OVMCodec.EVMAccount memory)
{
Lib_OVMCodec.EVMAccount memory DEFAULT_ACCOUNT_STATE = Lib_OVMCodec.EVMAccount({
nonce: UINT256_NULL,
balance: UINT256_NULL,
storageRoot: keccak256(hex'80'),
codeHash: keccak256(hex'')
});
(
bool exists,
bytes memory encodedAccountState
) = get(
abi.encodePacked(_address),
_stateTrieWitness,
_stateTrieRoot
);
return exists ? decodeAccountState(encodedAccountState) : DEFAULT_ACCOUNT_STATE;
}
/**
* @notice Updates the current account state for a given address.
* @param _accountState New account state, as a struct.
* @param _address Account address.
* @param _stateTrieWitness Inclusion proof for the account state within
* the state trie.
* @param _stateTrieRoot Known root of the state trie.
* @return Root hash of the updated state trie.
*/
function setAccountState(
Lib_OVMCodec.EVMAccount memory _accountState,
address _address,
bytes memory _stateTrieWitness,
bytes32 _stateTrieRoot
)
private
view
returns (bytes32)
{
bytes memory encodedAccountState = encodeAccountState(_accountState);
return update(
abi.encodePacked(_address),
encodedAccountState,
_stateTrieWitness,
_stateTrieRoot
);
}
}
\ No newline at end of file
packages/contracts/contracts/optimistic-ethereum/iOVM/trie/Lib_MerkleTrie.sol
0 → 100644
View file @
a1a499f1
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/* Library Imports */
import { Lib_ByteUtils } from "../utils/Lib_ByteUtils.sol";
import { Lib_RLPReader } from "../rlp/Lib_RLPReader.sol";
import { Lib_RLPWriter } from "../rlp/Lib_RLPWriter.sol";
/**
* @title Lib_MerkleTrie
*/
library Lib_MerkleTrie {
/*******************
* Data Structures *
*******************/
enum NodeType {
BranchNode,
ExtensionNode,
LeafNode
}
struct TrieNode {
bytes encoded;
Lib_RLPReader.RLPItem[] decoded;
}
/**********************
* Contract Constants *
**********************/
// TREE_RADIX determines the number of elements per branch node.
uint256 constant TREE_RADIX = 16;
// Branch nodes have TREE_RADIX elements plus an additional `value` slot.
uint256 constant BRANCH_NODE_LENGTH = TREE_RADIX + 1;
// Leaf nodes and extension nodes always have two elements, a `path` and a `value`.
uint256 constant LEAF_OR_EXTENSION_NODE_LENGTH = 2;
// Prefixes are prepended to the `path` within a leaf or extension node and
// allow us to differentiate between the two node types. `ODD` or `EVEN` is
// determined by the number of nibbles within the unprefixed `path`. If the
// number of nibbles if even, we need to insert an extra padding nibble so
// the resulting prefixed `path` has an even number of nibbles.
uint8 constant PREFIX_EXTENSION_EVEN = 0;
uint8 constant PREFIX_EXTENSION_ODD = 1;
uint8 constant PREFIX_LEAF_EVEN = 2;
uint8 constant PREFIX_LEAF_ODD = 3;
// Just a utility constant. RLP represents `NULL` as 0x80.
bytes1 constant RLP_NULL = bytes1(0x80);
bytes constant RLP_NULL_BYTES = hex'80';
/**********************
* Internal Functions *
**********************/
/**
* @notice Verifies a proof that a given key/value pair is present in the
* Merkle trie.
* @param _key Key of the node to search for, as a hex string.
* @param _value Value of the node to search for, as a hex string.
* @param _proof Merkle trie inclusion proof for the desired node. Unlike
* traditional Merkle trees, this proof is executed top-down and consists
* of a list of RLP-encoded nodes that make a path down to the target node.
* @param _root Known root of the Merkle trie. Used to verify that the
* included proof is correctly constructed.
* @return `true` if the k/v pair exists in the trie, `false` otherwise.
*/
function verifyInclusionProof(
bytes memory _key,
bytes memory _value,
bytes memory _proof,
bytes32 _root
)
internal
view
returns (bool)
{
return verifyProof(_key, _value, _proof, _root, true);
}
/**
* @notice Verifies a proof that a given key/value pair is *not* present in
* the Merkle trie.
* @param _key Key of the node to search for, as a hex string.
* @param _value Value of the node to search for, as a hex string.
* @param _proof Merkle trie inclusion proof for the node *nearest* the
* target node. We effectively need to show that either the key exists and
* its value differs, or the key does not exist at all.
* @param _root Known root of the Merkle trie. Used to verify that the
* included proof is correctly constructed.
* @return `true` if the k/v pair is absent in the trie, `false` otherwise.
*/
function verifyExclusionProof(
bytes memory _key,
bytes memory _value,
bytes memory _proof,
bytes32 _root
)
internal
view
returns (bool)
{
return verifyProof(_key, _value, _proof, _root, false);
}
/**
* @notice Updates a Merkle trie and returns a new root hash.
* @param _key Key of the node to update, as a hex string.
* @param _value Value of the node to update, as a hex string.
* @param _proof Merkle trie inclusion proof for the node *nearest* the
* target node. If the key exists, we can simply update the value.
* Otherwise, we need to modify the trie to handle the new k/v pair.
* @param _root Known root of the Merkle trie. Used to verify that the
* included proof is correctly constructed.
* @return Root hash of the newly constructed trie.
*/
function update(
bytes memory _key,
bytes memory _value,
bytes memory _proof,
bytes32 _root
)
internal
view
returns (bytes32)
{
TrieNode[] memory proof = parseProof(_proof);
(uint256 pathLength, bytes memory keyRemainder, ) = walkNodePath(proof, _key, _root);
TrieNode[] memory newPath = getNewPath(proof, pathLength, keyRemainder, _value);
return getUpdatedTrieRoot(newPath, _key);
}
/**
* @notice Retrieves the value associated with a given key.
* @param _key Key to search for, as hex bytes.
* @param _proof Merkle trie inclusion proof for the key.
* @param _root Known root of the Merkle trie.
* @return Whether the node exists, value associated with the key if so.
*/
function get(
bytes memory _key,
bytes memory _proof,
bytes32 _root
)
internal
view
returns (bool, bytes memory)
{
TrieNode[] memory proof = parseProof(_proof);
(uint256 pathLength, bytes memory keyRemainder, ) = walkNodePath(proof, _key, _root);
bool exists = keyRemainder.length == 0;
bytes memory value = exists ? getNodeValue(proof[pathLength - 1]) : bytes('');
return (
exists,
value
);
}
/**
* Computes the root hash for a trie with a single node.
* @param _key Key for the single node.
* @param _value Value for the single node.
* @return Hash of the trie.
*/
function getSingleNodeRootHash(
bytes memory _key,
bytes memory _value
)
internal
view
returns (bytes32)
{
return keccak256(makeLeafNode(
_key,
_value
).encoded);
}
/*********************
* Private Functions *
*********************/
/**
* @notice Utility function that handles verification of inclusion or
* exclusion proofs. Since the verification methods are almost identical,
* it's easier to shove this into a single function.
* @param _key Key of the node to search for, as a hex string.
* @param _value Value of the node to search for, as a hex string.
* @param _proof Merkle trie inclusion proof for the node *nearest* the
* target node. If we're proving explicit inclusion, the nearest node
* should be the target node.
* @param _root Known root of the Merkle trie. Used to verify that the
* included proof is correctly constructed.
* @param _inclusion Whether to check for inclusion or exclusion.
* @return `true` if the k/v pair is (in/not in) the trie, `false` otherwise.
*/
function verifyProof(
bytes memory _key,
bytes memory _value,
bytes memory _proof,
bytes32 _root,
bool _inclusion
)
private
view
returns (bool)
{
TrieNode[] memory proof = parseProof(_proof);
(uint256 pathLength, bytes memory keyRemainder, bool isFinalNode) = walkNodePath(proof, _key, _root);
if (_inclusion) {
// Included leaf nodes should have no key remainder, values should match.
return (
keyRemainder.length == 0 &&
libByteUtils.equal(getNodeValue(proof[pathLength - 1]), _value)
);
} else {
// If there's no key remainder then a leaf with the given key exists and the value should differ.
// Otherwise, we need to make sure that we've hit a dead end.
return (
(keyRemainder.length == 0 && !libByteUtils.equal(getNodeValue(proof[pathLength - 1]), _value)) ||
(keyRemainder.length != 0 && isFinalNode)
);
}
}
/**
* @notice Walks through a proof using a provided key.
* @param _proof Inclusion proof to walk through.
* @param _key Key to use for the walk.
* @param _root Known root of the trie.
* @return (
* Length of the final path;
* Portion of the key remaining after the walk;
* Whether or not we've hit a dead end;
* )
*/
function walkNodePath(
TrieNode[] memory _proof,
bytes memory _key,
bytes32 _root
)
private
view
returns (
uint256,
bytes memory,
bool
)
{
uint256 pathLength = 0;
bytes memory key = libByteUtils.toNibbles(_key);
bytes32 currentNodeID = _root;
uint256 currentKeyIndex = 0;
uint256 currentKeyIncrement = 0;
TrieNode memory currentNode;
// Proof is top-down, so we start at the first element (root).
for (uint256 i = 0; i < _proof.length; i++) {
currentNode = _proof[i];
currentKeyIndex += currentKeyIncrement;
// Keep track of the proof elements we actually need.
// It's expensive to resize arrays, so this simply reduces gas costs.
pathLength += 1;
if (currentKeyIndex == 0) {
// First proof element is always the root node.
require(
keccak256(currentNode.encoded) == currentNodeID,
"Invalid root hash"
);
} else if (currentNode.encoded.length >= 32) {
// Nodes 32 bytes or larger are hashed inside branch nodes.
require(
keccak256(currentNode.encoded) == currentNodeID,
"Invalid large internal hash"
);
} else {
// Nodes smaller than 31 bytes aren't hashed.
require(
libByteUtils.toBytes32(currentNode.encoded) == currentNodeID,
"Invalid internal node hash"
);
}
if (currentNode.decoded.length == BRANCH_NODE_LENGTH) {
if (currentKeyIndex == key.length) {
// We've hit the end of the key, meaning the value should be within this branch node.
break;
} else {
// We're not at the end of the key yet.
// Figure out what the next node ID should be and continue.
uint8 branchKey = uint8(key[currentKeyIndex]);
Lib_RLPReader.RLPItem memory nextNode = currentNode.decoded[branchKey];
currentNodeID = getNodeID(nextNode);
currentKeyIncrement = 1;
continue;
}
} else if (currentNode.decoded.length == LEAF_OR_EXTENSION_NODE_LENGTH) {
bytes memory path = getNodePath(currentNode);
uint8 prefix = uint8(path[0]);
uint8 offset = 2 - prefix % 2;
bytes memory pathRemainder = libByteUtils.slice(path, offset);
bytes memory keyRemainder = libByteUtils.slice(key, currentKeyIndex);
uint256 sharedNibbleLength = getSharedNibbleLength(pathRemainder, keyRemainder);
if (prefix == PREFIX_LEAF_EVEN || prefix == PREFIX_LEAF_ODD) {
if (
pathRemainder.length == sharedNibbleLength &&
keyRemainder.length == sharedNibbleLength
) {
// The key within this leaf matches our key exactly.
// Increment the key index to reflect that we have no remainder.
currentKeyIndex += sharedNibbleLength;
}
// We've hit a leaf node, so our next node should be NULL.
currentNodeID = bytes32(RLP_NULL);
break;
} else if (prefix == PREFIX_EXTENSION_EVEN || prefix == PREFIX_EXTENSION_ODD) {
if (sharedNibbleLength == 0) {
// Our extension node doesn't share any part of our key.
// We've hit the end of this path, updates will need to modify this extension.
currentNodeID = bytes32(RLP_NULL);
break;
} else {
// Our extension shares some nibbles.
// Carry on to the next node.
currentNodeID = getNodeID(currentNode.decoded[1]);
currentKeyIncrement = sharedNibbleLength;
continue;
}
}
}
}
// If our node ID is NULL, then we're at a dead end.
bool isFinalNode = currentNodeID == bytes32(RLP_NULL);
return (pathLength, libByteUtils.slice(key, currentKeyIndex), isFinalNode);
}
/**
* @notice Creates new nodes to support a k/v pair insertion into a given
* Merkle trie path.
* @param _path Path to the node nearest the k/v pair.
* @param _pathLength Length of the path. Necessary because the provided
* path may include additional nodes (e.g., it comes directly from a proof)
* and we can't resize in-memory arrays without costly duplication.
* @param _keyRemainder Portion of the initial key that must be inserted
* into the trie.
* @param _value Value to insert at the given key.
* @return A new path with the inserted k/v pair and extra supporting nodes.
*/
function getNewPath(
TrieNode[] memory _path,
uint256 _pathLength,
bytes memory _keyRemainder,
bytes memory _value
)
private
view
returns (TrieNode[] memory)
{
bytes memory keyRemainder = _keyRemainder;
// Most of our logic depends on the status of the last node in the path.
TrieNode memory lastNode = _path[_pathLength - 1];
NodeType lastNodeType = getNodeType(lastNode);
// Create an array for newly created nodes.
// We need up to three new nodes, depending on the contents of the last node.
// Since array resizing is expensive, we'll keep track of the size manually.
// We're using an explicit `totalNewNodes += 1` after insertions for clarity.
TrieNode[] memory newNodes = new TrieNode[](3);
uint256 totalNewNodes = 0;
if (keyRemainder.length == 0 && lastNodeType == NodeType.LeafNode) {
// We've found a leaf node with the given key.
// Simply need to update the value of the node to match.
newNodes[totalNewNodes] = makeLeafNode(getNodeKey(lastNode), _value);
totalNewNodes += 1;
} else if (lastNodeType == NodeType.BranchNode) {
if (keyRemainder.length == 0) {
// We've found a branch node with the given key.
// Simply need to update the value of the node to match.
newNodes[totalNewNodes] = editBranchValue(lastNode, _value);
totalNewNodes += 1;
} else {
// We've found a branch node, but it doesn't contain our key.
// Reinsert the old branch for now.
newNodes[totalNewNodes] = lastNode;
totalNewNodes += 1;
// Create a new leaf node, slicing our remainder since the first byte points
// to our branch node.
newNodes[totalNewNodes] = makeLeafNode(libByteUtils.slice(keyRemainder, 1), _value);
totalNewNodes += 1;
}
} else {
// Our last node is either an extension node or a leaf node with a different key.
bytes memory lastNodeKey = getNodeKey(lastNode);
uint256 sharedNibbleLength = getSharedNibbleLength(lastNodeKey, keyRemainder);
if (sharedNibbleLength != 0) {
// We've got some shared nibbles between the last node and our key remainder.
// We'll need to insert an extension node that covers these shared nibbles.
bytes memory nextNodeKey = libByteUtils.slice(lastNodeKey, 0, sharedNibbleLength);
newNodes[totalNewNodes] = makeExtensionNode(nextNodeKey, getNodeHash(_value));
totalNewNodes += 1;
// Cut down the keys since we've just covered these shared nibbles.
lastNodeKey = libByteUtils.slice(lastNodeKey, sharedNibbleLength);
keyRemainder = libByteUtils.slice(keyRemainder, sharedNibbleLength);
}
// Create an empty branch to fill in.
TrieNode memory newBranch = makeEmptyBranchNode();
if (lastNodeKey.length == 0) {
// Key remainder was larger than the key for our last node.
// The value within our last node is therefore going to be shifted into
// a branch value slot.
newBranch = editBranchValue(newBranch, getNodeValue(lastNode));
} else {
// Last node key was larger than the key remainder.
// We're going to modify some index of our branch.
uint8 branchKey = uint8(lastNodeKey[0]);
// Move on to the next nibble.
lastNodeKey = libByteUtils.slice(lastNodeKey, 1);
if (lastNodeType == NodeType.LeafNode) {
// We're dealing with a leaf node.
// We'll modify the key and insert the old leaf node into the branch index.
TrieNode memory modifiedLastNode = makeLeafNode(lastNodeKey, getNodeValue(lastNode));
newBranch = editBranchIndex(newBranch, branchKey, getNodeHash(modifiedLastNode.encoded));
} else if (lastNodeKey.length != 0) {
// We're dealing with a shrinking extension node.
// We need to modify the node to decrease the size of the key.
TrieNode memory modifiedLastNode = makeExtensionNode(lastNodeKey, getNodeValue(lastNode));
newBranch = editBranchIndex(newBranch, branchKey, getNodeHash(modifiedLastNode.encoded));
} else {
// We're dealing with an unnecessary extension node.
// We're going to delete the node entirely.
// Simply insert its current value into the branch index.
newBranch = editBranchIndex(newBranch, branchKey, getNodeValue(lastNode));
}
}
if (keyRemainder.length == 0) {
// We've got nothing left in the key remainder.
// Simply insert the value into the branch value slot.
newBranch = editBranchValue(newBranch, _value);
// Push the branch into the list of new nodes.
newNodes[totalNewNodes] = newBranch;
totalNewNodes += 1;
} else {
// We've got some key remainder to work with.
// We'll be inserting a leaf node into the trie.
// First, move on to the next nibble.
keyRemainder = libByteUtils.slice(keyRemainder, 1);
// Push the branch into the list of new nodes.
newNodes[totalNewNodes] = newBranch;
totalNewNodes += 1;
// Push a new leaf node for our k/v pair.
newNodes[totalNewNodes] = makeLeafNode(keyRemainder, _value);
totalNewNodes += 1;
}
}
// Finally, join the old path with our newly created nodes.
// Since we're overwriting the last node in the path, we use `_pathLength - 1`.
return joinNodeArrays(_path, _pathLength - 1, newNodes, totalNewNodes);
}
/**
* @notice Computes the trie root from a given path.
* @param _nodes Path to some k/v pair.
* @param _key Key for the k/v pair.
* @return Root hash for the updated trie.
*/
function getUpdatedTrieRoot(
TrieNode[] memory _nodes,
bytes memory _key
)
private
view
returns (bytes32)
{
bytes memory key = libByteUtils.toNibbles(_key);
// Some variables to keep track of during iteration.
TrieNode memory currentNode;
NodeType currentNodeType;
bytes memory previousNodeHash;
// Run through the path backwards to rebuild our root hash.
for (uint256 i = _nodes.length; i > 0; i--) {
// Pick out the current node.
currentNode = _nodes[i - 1];
currentNodeType = getNodeType(currentNode);
if (currentNodeType == NodeType.LeafNode) {
// Leaf nodes are already correctly encoded.
// Shift the key over to account for the nodes key.
bytes memory nodeKey = getNodeKey(currentNode);
key = libByteUtils.slice(key, 0, key.length - nodeKey.length);
} else if (currentNodeType == NodeType.ExtensionNode) {
// Shift the key over to account for the nodes key.
bytes memory nodeKey = getNodeKey(currentNode);
key = libByteUtils.slice(key, 0, key.length - nodeKey.length);
// If this node is the last element in the path, it'll be correctly encoded
// and we can skip this part.
if (previousNodeHash.length > 0) {
// Re-encode the node based on the previous node.
currentNode = makeExtensionNode(nodeKey, previousNodeHash);
}
} else if (currentNodeType == NodeType.BranchNode) {
// If this node is the last element in the path, it'll be correctly encoded
// and we can skip this part.
if (previousNodeHash.length > 0) {
// Re-encode the node based on the previous node.
uint8 branchKey = uint8(key[key.length - 1]);
key = libByteUtils.slice(key, 0, key.length - 1);
currentNode = editBranchIndex(currentNode, branchKey, previousNodeHash);
}
}
// Compute the node hash for the next iteration.
previousNodeHash = getNodeHash(currentNode.encoded);
}
// Current node should be the root at this point.
// Simply return the hash of its encoding.
return keccak256(currentNode.encoded);
}
/**
* @notice Parses an RLP-encoded proof into something more useful.
* @param _proof RLP-encoded proof to parse.
* @return Proof parsed into easily accessible structs.
*/
function parseProof(
bytes memory _proof
)
private
view
returns (TrieNode[] memory)
{
Lib_RLPReader.RLPItem[] memory nodes = libRLPReader.toList(libRLPReader.toRlpItem(_proof));
TrieNode[] memory proof = new TrieNode[](nodes.length);
for (uint256 i = 0; i < nodes.length; i++) {
bytes memory encoded = libRLPReader.toBytes(nodes[i]);
proof[i] = TrieNode({
encoded: encoded,
decoded: libRLPReader.toList(libRLPReader.toRlpItem(encoded))
});
}
return proof;
}
/**
* @notice Picks out the ID for a node. Node ID is referred to as the
* "hash" within the specification, but nodes < 32 bytes are not actually
* hashed.
* @param _node Node to pull an ID for.
* @return ID for the node, depending on the size of its contents.
*/
function getNodeID(
Lib_RLPReader.RLPItem memory _node
)
private
view
returns (bytes32)
{
bytes memory nodeID;
if (_node.len < 32) {
// Nodes smaller than 32 bytes are RLP encoded.
nodeID = libRLPReader.toRlpBytes(_node);
} else {
// Nodes 32 bytes or larger are hashed.
nodeID = libRLPReader.toBytes(_node);
}
return libByteUtils.toBytes32(nodeID);
}
/**
* @notice Gets the path for a leaf or extension node.
* @param _node Node to get a path for.
* @return Node path, converted to an array of nibbles.
*/
function getNodePath(
TrieNode memory _node
)
private
view
returns (bytes memory)
{
return libByteUtils.toNibbles(libRLPReader.toBytes(_node.decoded[0]));
}
/**
* @notice Gets the key for a leaf or extension node. Keys are essentially
* just paths without any prefix.
* @param _node Node to get a key for.
* @return Node key, converted to an array of nibbles.
*/
function getNodeKey(
TrieNode memory _node
)
private
view
returns (bytes memory)
{
return removeHexPrefix(getNodePath(_node));
}
/**
* @notice Gets the path for a node.
* @param _node Node to get a value for.
* @return Node value, as hex bytes.
*/
function getNodeValue(
TrieNode memory _node
)
private
view
returns (bytes memory)
{
return libRLPReader.toBytes(_node.decoded[_node.decoded.length - 1]);
}
/**
* @notice Computes the node hash for an encoded node. Nodes < 32 bytes
* are not hashed, all others are keccak256 hashed.
* @param _encoded Encoded node to hash.
* @return Hash of the encoded node. Simply the input if < 32 bytes.
*/
function getNodeHash(
bytes memory _encoded
)
private
view
returns (bytes memory)
{
if (_encoded.length < 32) {
return _encoded;
} else {
return abi.encodePacked(keccak256(_encoded));
}
}
/**
* @notice Determines the type for a given node.
* @param _node Node to determine a type for.
* @return Type of the node; BranchNode/ExtensionNode/LeafNode.
*/
function getNodeType(
TrieNode memory _node
)
private
view
returns (NodeType)
{
if (_node.decoded.length == BRANCH_NODE_LENGTH) {
return NodeType.BranchNode;
} else if (_node.decoded.length == LEAF_OR_EXTENSION_NODE_LENGTH) {
bytes memory path = getNodePath(_node);
uint8 prefix = uint8(path[0]);
if (prefix == PREFIX_LEAF_EVEN || prefix == PREFIX_LEAF_ODD) {
return NodeType.LeafNode;
} else if (prefix == PREFIX_EXTENSION_EVEN || prefix == PREFIX_EXTENSION_ODD) {
return NodeType.ExtensionNode;
}
}
revert("Invalid node type");
}
/**
* @notice Utility; determines the number of nibbles shared between two
* nibble arrays.
* @param _a First nibble array.
* @param _b Second nibble array.
* @return Number of shared nibbles.
*/
function getSharedNibbleLength(
bytes memory _a,
bytes memory _b
)
private
view
returns (uint256)
{
uint256 i = 0;
while (_a.length > i && _b.length > i && _a[i] == _b[i]) {
i++;
}
return i;
}
/**
* @notice Utility; converts an RLP-encoded node into our nice struct.
* @param _raw RLP-encoded node to convert.
* @return Node as a TrieNode struct.
*/
function makeNode(
bytes[] memory _raw
)
private
view
returns (TrieNode memory)
{
bytes memory encoded = libRLPWriter.encodeList(_raw);
return TrieNode({
encoded: encoded,
decoded: libRLPReader.toList(libRLPReader.toRlpItem(encoded))
});
}
/**
* @notice Utility; converts an RLP-decoded node into our nice struct.
* @param _items RLP-decoded node to convert.
* @return Node as a TrieNode struct.
*/
function makeNode(
Lib_RLPReader.RLPItem[] memory _items
)
private
view
returns (TrieNode memory)
{
bytes[] memory raw = new bytes[](_items.length);
for (uint256 i = 0; i < _items.length; i++) {
raw[i] = libRLPReader.toRlpBytes(_items[i]);
}
return makeNode(raw);
}
/**
* @notice Creates a new extension node.
* @param _key Key for the extension node, unprefixed.
* @param _value Value for the extension node.
* @return New extension node with the given k/v pair.
*/
function makeExtensionNode(
bytes memory _key,
bytes memory _value
)
private
view
returns (TrieNode memory)
{
bytes[] memory raw = new bytes[](2);
bytes memory key = addHexPrefix(_key, false);
raw[0] = libRLPWriter.encodeBytes(libByteUtils.fromNibbles(key));
raw[1] = libRLPWriter.encodeBytes(_value);
return makeNode(raw);
}
/**
* @notice Creates a new leaf node.
* @dev This function is essentially identical to `makeExtensionNode`.
* Although we could route both to a single method with a flag, it's
* more gas efficient to keep them separate and duplicate the logic.
* @param _key Key for the leaf node, unprefixed.
* @param _value Value for the leaf node.
* @return New leaf node with the given k/v pair.
*/
function makeLeafNode(
bytes memory _key,
bytes memory _value
)
private
view
returns (TrieNode memory)
{
bytes[] memory raw = new bytes[](2);
bytes memory key = addHexPrefix(_key, true);
raw[0] = libRLPWriter.encodeBytes(libByteUtils.fromNibbles(key));
raw[1] = libRLPWriter.encodeBytes(_value);
return makeNode(raw);
}
/**
* @notice Creates an empty branch node.
* @return Empty branch node as a TrieNode stuct.
*/
function makeEmptyBranchNode()
private
view
returns (TrieNode memory)
{
bytes[] memory raw = new bytes[](BRANCH_NODE_LENGTH);
for (uint256 i = 0; i < raw.length; i++) {
raw[i] = RLP_NULL_BYTES;
}
return makeNode(raw);
}
/**
* @notice Modifies the value slot for a given branch.
* @param _branch Branch node to modify.
* @param _value Value to insert into the branch.
* @return Modified branch node.
*/
function editBranchValue(
TrieNode memory _branch,
bytes memory _value
)
private
view
returns (TrieNode memory)
{
bytes memory encoded = libRLPWriter.encodeBytes(_value);
_branch.decoded[_branch.decoded.length - 1] = libRLPReader.toRlpItem(encoded);
return makeNode(_branch.decoded);
}
/**
* @notice Modifies a slot at an index for a given branch.
* @param _branch Branch node to modify.
* @param _index Slot index to modify.
* @param _value Value to insert into the slot.
* @return Modified branch node.
*/
function editBranchIndex(
TrieNode memory _branch,
uint8 _index,
bytes memory _value
)
private
view
returns (TrieNode memory)
{
bytes memory encoded = _value.length < 32 ? _value : libRLPWriter.encodeBytes(_value);
_branch.decoded[_index] = libRLPReader.toRlpItem(encoded);
return makeNode(_branch.decoded);
}
/**
* @notice Utility; adds a prefix to a key.
* @param _key Key to prefix.
* @param _isLeaf Whether or not the key belongs to a leaf.
* @return Prefixed key.
*/
function addHexPrefix(
bytes memory _key,
bool _isLeaf
)
private
view
returns (bytes memory)
{
uint8 prefix = _isLeaf ? uint8(0x02) : uint8(0x00);
uint8 offset = uint8(_key.length % 2);
bytes memory prefixed = new bytes(2 - offset);
prefixed[0] = bytes1(prefix + offset);
return libByteUtils.concat(prefixed, _key);
}
/**
* @notice Utility; removes a prefix from a path.
* @param _path Path to remove the prefix from.
* @return Unprefixed key.
*/
function removeHexPrefix(
bytes memory _path
)
private
view
returns (bytes memory)
{
if (uint8(_path[0]) % 2 == 0) {
return libByteUtils.slice(_path, 2);
} else {
return libByteUtils.slice(_path, 1);
}
}
/**
* @notice Utility; combines two node arrays. Array lengths are required
* because the actual lengths may be longer than the filled lengths.
* Array resizing is extremely costly and should be avoided.
* @param _a First array to join.
* @param _aLength Length of the first array.
* @param _b Second array to join.
* @param _bLength Length of the second array.
* @return Combined node array.
*/
function joinNodeArrays(
TrieNode[] memory _a,
uint256 _aLength,
TrieNode[] memory _b,
uint256 _bLength
)
private
view
returns (TrieNode[] memory)
{
TrieNode[] memory ret = new TrieNode[](_aLength + _bLength);
// Copy elements from the first array.
for (uint256 i = 0; i < _aLength; i++) {
ret[i] = _a[i];
}
// Copy elements from the second array.
for (uint256 i = 0; i < _bLength; i++) {
ret[i + _aLength] = _b[i];
}
return ret;
}
}
\ No newline at end of file
packages/contracts/contracts/optimistic-ethereum/iOVM/trie/Lib_SecureMerkleTrie.sol
0 → 100644
View file @
a1a499f1
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_MerkleTrie } from "./Lib_MerkleTrie.sol";
/**
* @title Lib_SecureMerkleTrie
*/
library Lib_SecureMerkleTrie {
/**********************
* Internal Functions *
**********************/
/**
* @notice Verifies a proof that a given key/value pair is present in the
* Merkle trie.
* @param _key Key of the node to search for, as a hex string.
* @param _value Value of the node to search for, as a hex string.
* @param _proof Merkle trie inclusion proof for the desired node. Unlike
* traditional Merkle trees, this proof is executed top-down and consists
* of a list of RLP-encoded nodes that make a path down to the target node.
* @param _root Known root of the Merkle trie. Used to verify that the
* included proof is correctly constructed.
* @return `true` if the k/v pair exists in the trie, `false` otherwise.
*/
function verifyInclusionProof(
bytes memory _key,
bytes memory _value,
bytes memory _proof,
bytes32 _root
)
public
view
returns (bool)
{
bytes memory key = _getSecureKey(_key);
return Lib_MerkleTrie.verifyInclusionProof(key, _value, _proof, _root);
}
/**
* @notice Verifies a proof that a given key/value pair is *not* present in
* the Merkle trie.
* @param _key Key of the node to search for, as a hex string.
* @param _value Value of the node to search for, as a hex string.
* @param _proof Merkle trie inclusion proof for the node *nearest* the
* target node. We effectively need to show that either the key exists and
* its value differs, or the key does not exist at all.
* @param _root Known root of the Merkle trie. Used to verify that the
* included proof is correctly constructed.
* @return `true` if the k/v pair is absent in the trie, `false` otherwise.
*/
function verifyExclusionProof(
bytes memory _key,
bytes memory _value,
bytes memory _proof,
bytes32 _root
)
public
view
returns (bool)
{
bytes memory key = _getSecureKey(_key);
return Lib_MerkleTrie.verifyExclusionProof(key, _value, _proof, _root);
}
/**
* @notice Updates a Merkle trie and returns a new root hash.
* @param _key Key of the node to update, as a hex string.
* @param _value Value of the node to update, as a hex string.
* @param _proof Merkle trie inclusion proof for the node *nearest* the
* target node. If the key exists, we can simply update the value.
* Otherwise, we need to modify the trie to handle the new k/v pair.
* @param _root Known root of the Merkle trie. Used to verify that the
* included proof is correctly constructed.
* @return Root hash of the newly constructed trie.
*/
function update(
bytes memory _key,
bytes memory _value,
bytes memory _proof,
bytes32 _root
)
public
view
returns (bytes32)
{
bytes memory key = _getSecureKey(_key);
return Lib_MerkleTrie.update(key, _value, _proof, _root);
}
/**
* @notice Retrieves the value associated with a given key.
* @param _key Key to search for, as hex bytes.
* @param _proof Merkle trie inclusion proof for the key.
* @param _root Known root of the Merkle trie.
* @return Whether the node exists, value associated with the key if so.
*/
function get(
bytes memory _key,
bytes memory _proof,
bytes32 _root
)
public
view
returns (bool, bytes memory)
{
bytes memory key = _getSecureKey(_key);
return Lib_MerkleTrie.get(key, _proof, _root);
}
/**
* Computes the root hash for a trie with a single node.
* @param _key Key for the single node.
* @param _value Value for the single node.
* @return Hash of the trie.
*/
function getSingleNodeRootHash(
bytes memory _key,
bytes memory _value
)
public
view
returns (bytes32)
{
bytes memory key = _getSecureKey(_key);
return Lib_MerkleTrie.getSingleNodeRootHash(key, _value);
}
/*********************
* Private Functions *
*********************/
function _getSecureKey(
bytes memory _key
) private pure returns (bytes memory) {
return abi.encodePacked(keccak256(_key));
}
}
\ No newline at end of file
packages/contracts/contracts/optimistic-ethereum/libraries/codec/Lib_OVMCodec.sol
View file @
a1a499f1
...
@@ -22,6 +22,13 @@ library Lib_OVMCodec {
...
@@ -22,6 +22,13 @@ library Lib_OVMCodec {
address ethAddress;
address ethAddress;
}
}
struct EVMAccount {
uint256 nonce;
uint256 balance;
bytes32 storageRoot;
bytes32 codeHash;
}
struct ChainBatchHeader {
struct ChainBatchHeader {
uint256 batchIndex;
uint256 batchIndex;
bytes32 batchRoot;
bytes32 batchRoot;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment