Commit 6e1dbfe0 authored by Kelvin Fichter's avatar Kelvin Fichter

Transition to use address resolver pattern

parent fb3c1643
import { BuidlerConfig } from '@nomiclabs/buidler/config';
import './test/helpers/buidler/modify-compiler';
declare const config: BuidlerConfig;
export default config;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const config_1 = require("@nomiclabs/buidler/config");
const constants_1 = require("./test/helpers/constants");
config_1.usePlugin('@nomiclabs/buidler-ethers');
config_1.usePlugin('@nomiclabs/buidler-waffle');
require("./test/helpers/buidler/modify-compiler");
const config = {
networks: {
buidlerevm: {
accounts: constants_1.DEFAULT_ACCOUNTS_BUIDLER,
blockGasLimit: constants_1.RUN_OVM_TEST_GAS * 2,
},
},
mocha: {
timeout: 50000,
},
solc: {
version: '0.7.0',
optimizer: { enabled: true, runs: 200 },
},
};
exports.default = config;
//# sourceMappingURL=buidler.config.js.map
\ No newline at end of file
{"version":3,"file":"buidler.config.js","sourceRoot":"","sources":["../buidler.config.ts"],"names":[],"mappings":";;AAAA,sDAAoE;AAEpE,wDAGiC;AAEjC,kBAAS,CAAC,2BAA2B,CAAC,CAAA;AACtC,kBAAS,CAAC,2BAA2B,CAAC,CAAA;AAEtC,kDAA+C;AAE/C,MAAM,MAAM,GAAkB;IAC5B,QAAQ,EAAE;QACR,UAAU,EAAE;YACV,QAAQ,EAAE,oCAAwB;YAClC,aAAa,EAAE,4BAAgB,GAAG,CAAC;SACpC;KACF;IACD,KAAK,EAAE;QACL,OAAO,EAAE,KAAK;KACf;IACD,IAAI,EAAE;QACJ,OAAO,EAAE,OAAO;QAChB,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;KACxC;CACF,CAAA;AAED,kBAAe,MAAM,CAAA"}
\ No newline at end of file
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_ECDSAContractAccount } from "../../iOVM/accounts/iOVM_ECDSAContractAccount.sol";
import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_ECDSAUtils } from "../../libraries/utils/Lib_ECDSAUtils.sol";
/**
* @title OVM_ECDSAContractAccount
*/
contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
/********************
* Public Functions *
********************/
/**
* Executes a signed transaction.
* @param _transaction Signed EOA transaction.
* @param _signatureType Hashing scheme used for the transaction (e.g., ETH signed message).
* @param _v Signature `v` parameter.
* @param _r Signature `r` parameter.
* @param _s Signature `s` parameter.
* @return _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call.
*/
function execute(
bytes memory _transaction,
Lib_OVMCodec.EOASignatureType _signatureType,
uint8 _v,
bytes32 _r,
bytes32 _s
)
override
public
returns (
bool _success,
bytes memory _returndata
)
{
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
// Address of this contract within the ovm (ovmADDRESS) should be the same as the
// recovered address of the user who signed this message. This is how we manage to shim
// account abstraction even though the user isn't a contract.
require(
Lib_ECDSAUtils.recover(
_transaction,
_signatureType == Lib_OVMCodec.EOASignatureType.ETH_SIGNED_MESSAGE,
_v,
_r,
_s,
ovmExecutionManager.ovmCHAINID()
) == ovmExecutionManager.ovmADDRESS(),
"Signature provided for EOA transaction execution is invalid."
);
Lib_OVMCodec.EOATransaction memory decodedTx = Lib_OVMCodec.decodeEOATransaction(_transaction);
// Need to make sure that the transaction nonce is right and bump it if so.
require(
decodedTx.nonce == ovmExecutionManager.ovmGETNONCE() + 1,
"Transaction nonce does not match the expected nonce."
);
ovmExecutionManager.ovmSETNONCE(decodedTx.nonce);
// Contract creations are signalled by sending a transaction to the zero address.
if (decodedTx.target == address(0)) {
address created = ovmExecutionManager.ovmCREATE{gas: decodedTx.gasLimit}(
decodedTx.data
);
// EVM doesn't tell us whether a contract creation failed, even if it reverted during
// initialization. Always return `true` for our success value here.
return (true, abi.encode(created));
} else {
return ovmExecutionManager.ovmCALL(
decodedTx.gasLimit,
decodedTx.target,
decodedTx.data
);
}
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_BaseCrossDomainMessenger } from "../../iOVM/bridge/iOVM_BaseCrossDomainMessenger.sol";
/**
* @title OVM_BaseCrossDomainMessenger
*/
contract OVM_BaseCrossDomainMessenger is iOVM_BaseCrossDomainMessenger {
/**********************
* Contract Variables *
**********************/
mapping (bytes32 => bool) public receivedMessages;
mapping (bytes32 => bool) public sentMessages;
address public targetMessengerAddress;
uint256 public messageNonce;
address public xDomainMessageSender;
/********************
* Public Functions *
********************/
/**
* Sets the target messenger address.
* @dev Currently, this function is public and therefore allows anyone to modify the target
* messenger for a given xdomain messenger contract. Obviously this shouldn't be allowed,
* but we still need to determine an adequate mechanism for updating this address.
* @param _targetMessengerAddress New messenger address.
*/
function setTargetMessengerAddress(
address _targetMessengerAddress
)
override
public
{
targetMessengerAddress = _targetMessengerAddress;
}
/**********************
* Internal Functions *
**********************/
/**
* Generates the correct cross domain calldata for a message.
* @param _target Target contract address.
* @param _sender Message sender address.
* @param _message Message to send to the target.
* @param _messageNonce Nonce for the provided message.
* @return ABI encoded cross domain calldata.
*/
function _getXDomainCalldata(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce
)
internal
pure
returns (
bytes memory
)
{
return abi.encodeWithSelector(
bytes4(keccak256(bytes("relayMessage(address,address,bytes,uint256)"))),
_target,
_sender,
_message,
_messageNonce
);
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_EthMerkleTrie } from "../../libraries/trie/Lib_EthMerkleTrie.sol";
import { Lib_ByteUtils } from "../../libraries/utils/Lib_ByteUtils.sol";
/* Interface Imports */
import { iOVM_L1CrossDomainMessenger } from "../../iOVM/bridge/iOVM_L1CrossDomainMessenger.sol";
import { iOVM_L1ToL2TransactionQueue } from "../../iOVM/queue/iOVM_L1ToL2TransactionQueue.sol";
import { iOVM_StateCommitmentChain } from "../../iOVM/chain/iOVM_StateCommitmentChain.sol";
/* Contract Imports */
import { OVM_BaseCrossDomainMessenger } from "./OVM_BaseCrossDomainMessenger.sol";
/**
* @title OVM_L1CrossDomainMessenger
*/
contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, OVM_BaseCrossDomainMessenger, Lib_AddressResolver {
/*******************************************
* Contract Variables: Contract References *
*******************************************/
iOVM_L1ToL2TransactionQueue internal ovmL1ToL2TransactionQueue;
iOVM_StateCommitmentChain internal ovmStateCommitmentChain;
/***************
* Constructor *
***************/
/**
* @param _libAddressManager Address of the Address Manager.
*/
constructor(
address _libAddressManager
)
Lib_AddressResolver(_libAddressManager)
{
ovmL1ToL2TransactionQueue = iOVM_L1ToL2TransactionQueue(resolve("OVM_L1ToL2TransactionQueue"));
ovmStateCommitmentChain = iOVM_StateCommitmentChain(resolve("OVM_StateCommitmentChain"));
}
/********************
* Public Functions *
********************/
/**
* Relays a cross domain message to a contract.
* @inheritdoc iOVM_L1CrossDomainMessenger
*/
function relayMessage(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce,
L2MessageInclusionProof memory _proof
)
override
public
{
bytes memory xDomainCalldata = _getXDomainCalldata(
_target,
_sender,
_message,
_messageNonce
);
require(
_verifyXDomainMessage(
xDomainCalldata,
_proof
) == true,
"Provided message could not be verified."
);
require(
receivedMessages[keccak256(xDomainCalldata)] == false,
"Provided message has already been received."
);
xDomainMessageSender = _sender;
_target.call(_message);
// Messages are considered successfully executed if they complete
// without running out of gas (revert or not). As a result, we can
// ignore the result of the call and always mark the message as
// successfully executed because we won't get here unless we have
// enough gas left over.
receivedMessages[keccak256(xDomainCalldata)] = true;
}
/**
* Sends a cross domain message to the target messenger.
* @inheritdoc iOVM_L1CrossDomainMessenger
*/
function sendMessage(
address _target,
bytes memory _message,
uint32 _gasLimit
)
override
public
{
bytes memory xDomainCalldata = _getXDomainCalldata(
_target,
msg.sender,
_message,
messageNonce
);
_sendXDomainMessage(xDomainCalldata, _gasLimit);
messageNonce += 1;
sentMessages[keccak256(xDomainCalldata)] = true;
}
/**
* Replays a cross domain message to the target messenger.
* @inheritdoc iOVM_L1CrossDomainMessenger
*/
function replayMessage(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce,
uint32 _gasLimit
)
override
public
{
bytes memory xDomainCalldata = _getXDomainCalldata(
_target,
_sender,
_message,
_messageNonce
);
require(
sentMessages[keccak256(xDomainCalldata)] == true,
"Provided message has not already been sent."
);
_sendXDomainMessage(xDomainCalldata, _gasLimit);
}
/**********************
* Internal Functions *
**********************/
/**
* Verifies that the given message is valid.
* @param _xDomainCalldata Calldata to verify.
* @param _proof Inclusion proof for the message.
* @return Whether or not the provided message is valid.
*/
function _verifyXDomainMessage(
bytes memory _xDomainCalldata,
L2MessageInclusionProof memory _proof
)
internal
returns (
bool
)
{
return (
_verifyStateRootProof(_proof)
&& _verifyStorageProof(_xDomainCalldata, _proof)
);
}
/**
* Verifies that the state root within an inclusion proof is valid.
* @param _proof Message inclusion proof.
* @return Whether or not the provided proof is valid.
*/
function _verifyStateRootProof(
L2MessageInclusionProof memory _proof
)
internal
returns (
bool
)
{
// TODO: We *must* verify that the batch timestamp is sufficiently old.
// However, this requires that we first add timestamps to state batches
// and account for that change in various tests. Change of that size is
// out of scope for this ticket, so "TODO" for now.
return ovmStateCommitmentChain.verifyElement(
abi.encodePacked(_proof.stateRoot),
_proof.stateRootBatchHeader,
_proof.stateRootProof
);
}
/**
* Verifies that the storage proof within an inclusion proof is valid.
* @param _xDomainCalldata Encoded message calldata.
* @param _proof Message inclusion proof.
* @return Whether or not the provided proof is valid.
*/
function _verifyStorageProof(
bytes memory _xDomainCalldata,
L2MessageInclusionProof memory _proof
)
internal
returns (
bool
)
{
bytes32 storageKey = keccak256(
Lib_ByteUtils.concat(
abi.encodePacked(keccak256(_xDomainCalldata)),
abi.encodePacked(uint256(0))
)
);
return Lib_EthMerkleTrie.proveAccountStorageSlotValue(
0x4200000000000000000000000000000000000000,
storageKey,
bytes32(uint256(1)),
_proof.stateTrieWitness,
_proof.storageTrieWitness,
_proof.stateRoot
);
}
/**
* Sends a cross domain message.
* @param _message Message to send.
* @param _gasLimit OVM gas limit for the message.
*/
function _sendXDomainMessage(
bytes memory _message,
uint32 _gasLimit
)
internal
{
ovmL1ToL2TransactionQueue.enqueue(
targetMessengerAddress,
_gasLimit,
_message
);
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */
import { iOVM_L2CrossDomainMessenger } from "../../iOVM/bridge/iOVM_L2CrossDomainMessenger.sol";
import { iOVM_L1MessageSender } from "../../iOVM/precompiles/iOVM_L1MessageSender.sol";
import { iOVM_L2ToL1MessagePasser } from "../../iOVM/precompiles/iOVM_L2ToL1MessagePasser.sol";
/* Contract Imports */
import { OVM_BaseCrossDomainMessenger } from "./OVM_BaseCrossDomainMessenger.sol";
/**
* @title OVM_L2CrossDomainMessenger
*/
contract OVM_L2CrossDomainMessenger is iOVM_L2CrossDomainMessenger, OVM_BaseCrossDomainMessenger, Lib_AddressResolver {
/*******************************************
* Contract Variables: Contract References *
*******************************************/
iOVM_L1MessageSender internal ovmL1MessageSender;
iOVM_L2ToL1MessagePasser internal ovmL2ToL1MessagePasser;
/***************
* Constructor *
***************/
/**
* @param _libAddressManager Address of the Address Manager.
*/
constructor(
address _libAddressManager
)
Lib_AddressResolver(_libAddressManager)
{
ovmL1MessageSender = iOVM_L1MessageSender(resolve("OVM_L1MessageSender"));
ovmL2ToL1MessagePasser = iOVM_L2ToL1MessagePasser(resolve("OVM_L2ToL1MessagePasser"));
}
/********************
* Public Functions *
********************/
/**
* Relays a cross domain message to a contract.
* @inheritdoc iOVM_L2CrossDomainMessenger
*/
function relayMessage(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce
)
override
public
{
require(
_verifyXDomainMessage() == true,
"Provided message could not be verified."
);
bytes memory xDomainCalldata = _getXDomainCalldata(
_target,
_sender,
_message,
_messageNonce
);
require(
receivedMessages[keccak256(xDomainCalldata)] == false,
"Provided message has already been received."
);
xDomainMessageSender = _sender;
_target.call(_message);
// Messages are considered successfully executed if they complete
// without running out of gas (revert or not). As a result, we can
// ignore the result of the call and always mark the message as
// successfully executed because we won't get here unless we have
// enough gas left over.
receivedMessages[keccak256(xDomainCalldata)] = true;
}
/**
* Sends a cross domain message to the target messenger.
* @inheritdoc iOVM_L2CrossDomainMessenger
*/
function sendMessage(
address _target,
bytes memory _message,
uint256 _gasLimit
)
override
public
{
bytes memory xDomainCalldata = _getXDomainCalldata(
_target,
msg.sender,
_message,
messageNonce
);
_sendXDomainMessage(xDomainCalldata, _gasLimit);
messageNonce += 1;
sentMessages[keccak256(xDomainCalldata)] = true;
}
/**********************
* Internal Functions *
**********************/
/**
* Verifies that a received cross domain message is valid.
* @return _valid Whether or not the message is valid.
*/
function _verifyXDomainMessage()
internal
returns (
bool _valid
)
{
return (
ovmL1MessageSender.getL1MessageSender() == targetMessengerAddress
);
}
/**
* Sends a cross domain message.
* @param _message Message to send.
* @param _gasLimit Gas limit for the provided message.
*/
function _sendXDomainMessage(
bytes memory _message,
uint256 _gasLimit
)
internal
{
ovmL2ToL1MessagePasser.passMessageToL1(_message);
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_BaseChain } from "../../iOVM/chain/iOVM_BaseChain.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_MerkleUtils } from "../../libraries/utils/Lib_MerkleUtils.sol";
/**
* @title OVM_BaseChain
*/
contract OVM_BaseChain is iOVM_BaseChain {
/*******************************
* Contract Variables: Batches *
*******************************/
bytes32[] internal batches;
uint256 internal totalBatches;
uint256 internal totalElements;
/*************************************
* Public Functions: Batch Retrieval *
*************************************/
/**
* Gets the total number of submitted elements.
* @return _totalElements Total submitted elements.
*/
function getTotalElements()
override
public
view
returns (
uint256 _totalElements
)
{
return totalElements;
}
/**
* Gets the total number of submitted batches.
* @return _totalBatches Total submitted batches.
*/
function getTotalBatches()
override
public
view
returns (
uint256 _totalBatches
)
{
return totalBatches;
}
/****************************************
* Public Functions: Batch Verification *
****************************************/
/**
* Verifies an inclusion proof for a given element.
* @param _element Element to verify.
* @param _batchHeader Header of the batch in which this element was included.
* @param _proof Inclusion proof for the element.
* @return _verified Whether or not the element was included in the batch.
*/
function verifyElement(
bytes calldata _element,
Lib_OVMCodec.ChainBatchHeader memory _batchHeader,
Lib_OVMCodec.ChainInclusionProof memory _proof
)
override
public
view
returns (
bool _verified
)
{
require(
_hashBatchHeader(_batchHeader) == batches[_batchHeader.batchIndex],
"Invalid batch header."
);
require(
Lib_MerkleUtils.verify(
_batchHeader.batchRoot,
_element,
_proof.index,
_proof.siblings
),
"Invalid inclusion proof."
);
return true;
}
/******************************************
* Internal Functions: Batch Modification *
******************************************/
/**
* Appends a batch to the chain.
* @param _batchHeader Batch header to append.
*/
function _appendBatch(
Lib_OVMCodec.ChainBatchHeader memory _batchHeader
)
internal
{
bytes32 batchHeaderHash = _hashBatchHeader(_batchHeader);
batches.push(batchHeaderHash);
totalBatches += 1;
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(
bytes[] memory _elements,
bytes memory _extraData
)
internal
{
Lib_OVMCodec.ChainBatchHeader memory batchHeader = Lib_OVMCodec.ChainBatchHeader({
batchIndex: batches.length,
batchRoot: Lib_MerkleUtils.getMerkleRoot(_elements),
batchSize: _elements.length,
prevTotalElements: totalElements,
extraData: _extraData
});
_appendBatch(batchHeader);
}
/**
* Appends a batch to the chain.
* @param _elements Elements within the batch.
*/
function _appendBatch(
bytes[] memory _elements
)
internal
{
_appendBatch(
_elements,
bytes('')
);
}
/**
* Removes a batch from the chain.
* @param _batchHeader Header of the batch to remove.
*/
function _deleteBatch(
Lib_OVMCodec.ChainBatchHeader memory _batchHeader
)
internal
{
require(
_batchHeader.batchIndex < batches.length,
"Invalid batch index."
);
require(
_hashBatchHeader(_batchHeader) == batches[_batchHeader.batchIndex],
"Invalid batch header."
);
totalBatches = _batchHeader.batchIndex;
totalElements = _batchHeader.prevTotalElements;
}
/*********************
* Private Functions *
*********************/
/**
* Calculates a hash for a given batch header.
* @param _batchHeader Header to hash.
* @return _hash Hash of the header.
*/
function _hashBatchHeader(
Lib_OVMCodec.ChainBatchHeader memory _batchHeader
)
private
pure
returns (
bytes32 _hash
)
{
return keccak256(abi.encodePacked(
_batchHeader.batchRoot,
_batchHeader.batchSize,
_batchHeader.prevTotalElements,
_batchHeader.extraData
));
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_MerkleUtils } from "../../libraries/utils/Lib_MerkleUtils.sol";
/* Interface Imports */
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
import { iOVM_L1ToL2TransactionQueue } from "../../iOVM/queue/iOVM_L1ToL2TransactionQueue.sol";
/* Contract Imports */
import { OVM_BaseChain } from "./OVM_BaseChain.sol";
/**
* @title OVM_CanonicalTransactionChain
*/
contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_BaseChain, Lib_AddressResolver {
/*******************************************
* Contract Variables: Contract References *
*******************************************/
iOVM_L1ToL2TransactionQueue internal ovmL1ToL2TransactionQueue;
/*******************************************
* Contract Variables: Internal Accounting *
*******************************************/
uint256 internal forceInclusionPeriodSeconds;
uint256 internal lastOVMTimestamp;
/***************
* Constructor *
***************/
/**
* @param _libAddressManager Address of the Address Manager.
* @param _forceInclusionPeriodSeconds Period during which only the sequencer can submit.
*/
constructor(
address _libAddressManager,
uint256 _forceInclusionPeriodSeconds
)
Lib_AddressResolver(_libAddressManager)
{
ovmL1ToL2TransactionQueue = iOVM_L1ToL2TransactionQueue(resolve("OVM_L1ToL2TransactionQueue"));
forceInclusionPeriodSeconds = _forceInclusionPeriodSeconds;
}
/****************************************
* Public Functions: Batch Manipulation *
****************************************/
/**
* Appends a batch from the L1ToL2TransactionQueue.
*/
function appendQueueBatch()
override
public
{
require(
ovmL1ToL2TransactionQueue.size() > 0,
"No batches are currently queued to be appended."
);
Lib_OVMCodec.QueueElement memory queueElement = ovmL1ToL2TransactionQueue.peek();
require(
queueElement.timestamp + forceInclusionPeriodSeconds <= block.timestamp,
"Cannot append until the inclusion delay period has elapsed."
);
_appendQueueBatch(queueElement, 1);
ovmL1ToL2TransactionQueue.dequeue();
}
/**
* Appends a sequencer batch.
* @param _batch Batch of transactions to append.
* @param _timestamp Timestamp for the provided batch.
*/
function appendSequencerBatch(
bytes[] memory _batch,
uint256 _timestamp
)
override
public
{
require(
msg.sender == resolve("Sequencer"),
"Function can only be called by the Sequencer."
);
require(
_batch.length > 0,
"Cannot submit an empty batch."
);
require(
_timestamp > lastOVMTimestamp,
"Batch timestamp must be later than the last OVM timestamp."
);
if (ovmL1ToL2TransactionQueue.size() > 0) {
require(
_timestamp <= ovmL1ToL2TransactionQueue.peek().timestamp,
"Older queue batches must be processed before a newer sequencer batch."
);
}
Lib_OVMCodec.QueueElement memory queueElement = Lib_OVMCodec.QueueElement({
timestamp: _timestamp,
batchRoot: Lib_MerkleUtils.getMerkleRoot(_batch),
isL1ToL2Batch: false
});
_appendQueueBatch(queueElement, _batch.length);
}
/******************************************
* 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(
Lib_OVMCodec.QueueElement memory _queueElement,
uint256 _batchSize
)
internal
{
Lib_OVMCodec.ChainBatchHeader memory batchHeader = Lib_OVMCodec.ChainBatchHeader({
batchIndex: getTotalBatches(),
batchRoot: _queueElement.batchRoot,
batchSize: _batchSize,
prevTotalElements: getTotalElements(),
extraData: abi.encodePacked(
_queueElement.timestamp,
_queueElement.isL1ToL2Batch
)
});
_appendBatch(batchHeader);
lastOVMTimestamp = _queueElement.timestamp;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */
import { iOVM_FraudVerifier } from "../../iOVM/verification/iOVM_FraudVerifier.sol";
import { iOVM_StateCommitmentChain } from "../../iOVM/chain/iOVM_StateCommitmentChain.sol";
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
/* Contract Imports */
import { OVM_BaseChain } from "./OVM_BaseChain.sol";
/**
* @title OVM_StateCommitmentChain
*/
contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, Lib_AddressResolver {
/*******************************************
* Contract Variables: Contract References *
*******************************************/
iOVM_CanonicalTransactionChain internal ovmCanonicalTransactionChain;
iOVM_FraudVerifier internal ovmFraudVerifier;
/***************
* Constructor *
***************/
/**
* @param _libAddressManager Address of the Address Manager.
*/
constructor(
address _libAddressManager
)
Lib_AddressResolver(_libAddressManager)
{
ovmCanonicalTransactionChain = iOVM_CanonicalTransactionChain(resolve("OVM_CanonicalTransactionChain"));
ovmFraudVerifier = iOVM_FraudVerifier(resolve("OVM_FraudVerifier"));
}
/****************************************
* Public Functions: Batch Manipulation *
****************************************/
/**
* Appends a batch of state roots to the chain.
* @param _batch Batch of state roots.
*/
function appendStateBatch(
bytes32[] memory _batch
)
override
public
{
require(
_batch.length > 0,
"Cannot submit an empty state batch."
);
require(
getTotalElements() + _batch.length <= ovmCanonicalTransactionChain.getTotalElements(),
"Number of state roots cannot exceed the number of canonical transactions."
);
bytes[] memory elements = new bytes[](_batch.length);
for (uint256 i = 0; i < _batch.length; i++) {
elements[i] = abi.encodePacked(_batch[i]);
}
_appendBatch(elements);
}
/**
* Deletes all state roots after (and including) a given batch.
* @param _batchHeader Header of the batch to start deleting from.
*/
function deleteStateBatch(
Lib_OVMCodec.ChainBatchHeader memory _batchHeader
)
override
public
{
require(
msg.sender == address(ovmFraudVerifier),
"State batches can only be deleted by the OVM_FraudVerifier."
);
_deleteBatch(_batchHeader);
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_EthUtils } from "../../libraries/utils/Lib_EthUtils.sol";
/* Interface Imports */
import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol";
import { iOVM_StateManager } from "../../iOVM/execution/iOVM_StateManager.sol";
import { iOVM_SafetyChecker } from "../../iOVM/execution/iOVM_SafetyChecker.sol";
/* Contract Imports */
import { OVM_ECDSAContractAccount } from "../accounts/OVM_ECDSAContractAccount.sol";
/* Logging */
import { console } from "@nomiclabs/buidler/console.sol";
/**
* @title OVM_ExecutionManager
*/
contract OVM_ExecutionManager is iOVM_ExecutionManager {
/********************************
* External Contract References *
********************************/
iOVM_SafetyChecker public ovmSafetyChecker;
iOVM_StateManager public ovmStateManager;
/*******************************
* Execution Context Variables *
*******************************/
GasMeterConfig internal gasMeterConfig;
GlobalContext internal globalContext;
TransactionContext internal transactionContext;
MessageContext internal messageContext;
TransactionRecord internal transactionRecord;
MessageRecord internal messageRecord;
/**************************
* Gas Metering Constants *
**************************/
address constant GAS_METADATA_ADDRESS = 0x06a506A506a506A506a506a506A506A506A506A5;
uint256 constant NUISANCE_GAS_SLOAD = 20000;
uint256 constant NUISANCE_GAS_SSTORE = 20000;
uint256 constant MIN_NUISANCE_GAS_PER_CONTRACT = 30000;
uint256 constant NUISANCE_GAS_PER_CONTRACT_BYTE = 100;
uint256 constant MIN_GAS_FOR_INVALID_STATE_ACCESS = 30000;
/***************
* Constructor *
***************/
/**
* @param _ovmSafetyChecker Address of the iOVM_SafetyChecker implementation.
*/
constructor(
address _ovmSafetyChecker
) {
ovmSafetyChecker = iOVM_SafetyChecker(_ovmSafetyChecker);
}
/**********************
* Function Modifiers *
**********************/
/**
* Applies a net gas cost refund to a transaction to account for the difference in execution
* between L1 and L2.
* @param _cost Gas cost for the function after the refund.
*/
modifier netGasCost(
uint256 _cost
) {
uint256 gasProvided = gasleft();
_;
uint256 gasUsed = gasProvided - gasleft();
// We want to refund everything *except* the specified cost.
if (_cost < gasUsed) {
transactionRecord.ovmGasRefund += gasUsed - _cost;
}
}
/**
* Makes sure we're not inside a static context.
*/
modifier notStatic() {
if (messageContext.isStatic == true) {
_revertWithFlag(RevertFlag.STATIC_VIOLATION);
}
_;
}
/************************************
* Transaction Execution Entrypoint *
************************************/
/**
* Starts the execution of a transaction via the OVM_ExecutionManager.
* @param _transaction Transaction data to be executed.
* @param _ovmStateManager iOVM_StateManager implementation providing account state.
*/
function run(
Lib_OVMCodec.Transaction memory _transaction,
address _ovmStateManager
)
override
public
{
// Store our OVM_StateManager instance (significantly easier than attempting to pass the address
// around in calldata).
ovmStateManager = iOVM_StateManager(_ovmStateManager);
// Check whether we need to start a new epoch, do so if necessary.
_checkNeedsNewEpoch(_transaction.timestamp);
// Make sure the transaction's gas limit is valid. We don't revert here because we reserve
// reverts for INVALID_STATE_ACCESS.
if (_isValidGasLimit(_transaction.gasLimit, _transaction.l1QueueOrigin) == false) {
return;
}
// Initialize the execution context.
_initContext(_transaction);
// Run the transaction, make sure to meter the gas usage.
uint256 gasProvided = gasleft();
ovmCALL(
_transaction.gasLimit - gasMeterConfig.minTransactionGasLimit,
_transaction.entrypoint,
_transaction.data
);
uint256 gasUsed = gasProvided - gasleft();
// Update the cumulative gas based on the amount of gas used.
_updateCumulativeGas(gasUsed, _transaction.l1QueueOrigin);
// Wipe the execution context.
_resetContext();
}
/******************************
* Opcodes: Execution Context *
******************************/
/**
* @notice Overrides CALLER.
* @return _CALLER Address of the CALLER within the current message context.
*/
function ovmCALLER()
override
public
view
returns (
address _CALLER
)
{
return messageContext.ovmCALLER;
}
/**
* @notice Overrides ADDRESS.
* @return _ADDRESS Active ADDRESS within the current message context.
*/
function ovmADDRESS()
override
public
view
returns (
address _ADDRESS
)
{
return messageContext.ovmADDRESS;
}
/**
* @notice Overrides TIMESTAMP.
* @return _TIMESTAMP Value of the TIMESTAMP within the transaction context.
*/
function ovmTIMESTAMP()
override
public
view
returns (
uint256 _TIMESTAMP
)
{
return transactionContext.ovmTIMESTAMP;
}
/**
* @notice Overrides NUMBER.
* @return _NUMBER Value of the NUMBER within the transaction context.
*/
function ovmNUMBER()
override
public
view
returns (
uint256 _NUMBER
)
{
return transactionContext.ovmNUMBER;
}
/**
* @notice Overrides GASLIMIT.
* @return _GASLIMIT Value of the block's GASLIMIT within the transaction context.
*/
function ovmGASLIMIT()
override
public
view
returns (
uint256 _GASLIMIT
)
{
return transactionContext.ovmGASLIMIT;
}
/**
* @notice Overrides CHAINID.
* @return _CHAINID Value of the chain's CHAINID within the global context.
*/
function ovmCHAINID()
override
public
view
returns (
uint256 _CHAINID
)
{
return globalContext.ovmCHAINID;
}
/*********************************
* Opcodes: L2 Execution Context *
*********************************/
/**
* @notice Specifies from which L1 rollup queue this transaction originated from.
* @return _queueOrigin Address of the CALLER within the current message context.
*/
function ovmL1QUEUEORIGIN()
override
public
view
returns (
Lib_OVMCodec.QueueOrigin _queueOrigin
)
{
return transactionContext.ovmL1QUEUEORIGIN;
}
/**
* @notice Specifies what L1 EOA, if any, sent this transaction.
* @return _l1TxOrigin Address of the EOA which send the tx into L2 from L1.
*/
function ovmL1TXORIGIN()
override
public
view
returns (
address _l1TxOrigin
)
{
return transactionContext.ovmL1TXORIGIN;
}
/********************
* Opcodes: Halting *
********************/
/**
* @notice Overrides REVERT.
* @param _data Bytes data to pass along with the REVERT.
*/
function ovmREVERT(
bytes memory _data
)
override
public
{
_revertWithFlag(RevertFlag.INTENTIONAL_REVERT, _data);
}
/******************************
* Opcodes: Contract Creation *
******************************/
/**
* @notice Overrides CREATE.
* @param _bytecode Code to be used to CREATE a new contract.
* @return _contract Address of the created contract.
*/
function ovmCREATE(
bytes memory _bytecode
)
override
public
notStatic
netGasCost(40000 + _bytecode.length * 100)
returns (
address _contract
)
{
// Creator is always the current ADDRESS.
address creator = ovmADDRESS();
// Generate the correct CREATE address.
address contractAddress = Lib_EthUtils.getAddressForCREATE(
creator,
_getAccountNonce(creator)
);
return _createContract(
contractAddress,
_bytecode
);
}
/**
* @notice Overrides CREATE2.
* @param _bytecode Code to be used to CREATE2 a new contract.
* @param _salt Value used to determine the contract's address.
* @return _contract Address of the created contract.
*/
function ovmCREATE2(
bytes memory _bytecode,
bytes32 _salt
)
override
public
notStatic
netGasCost(40000 + _bytecode.length * 100)
returns (
address _contract
)
{
// Creator is always the current ADDRESS.
address creator = ovmADDRESS();
// Generate the correct CREATE2 address.
address contractAddress = Lib_EthUtils.getAddressForCREATE2(
creator,
_bytecode,
_salt
);
return _createContract(
contractAddress,
_bytecode
);
}
/*******************************
* Account Abstraction Opcodes *
******************************/
/**
* Retrieves the nonce of the current ovmADDRESS.
* @return _nonce Nonce of the current contract.
*/
function ovmGETNONCE()
override
public
returns (
uint256 _nonce
)
{
return _getAccountNonce(ovmADDRESS());
}
/**
* Sets the nonce of the current ovmADDRESS.
* @param _nonce New nonce for the current contract.
*/
function ovmSETNONCE(
uint256 _nonce
)
override
public
{
if (_nonce <= ovmGETNONCE()) {
return;
}
_setAccountNonce(ovmADDRESS(), _nonce);
}
/**
* Creates a new EOA contract account, for account abstraction.
* @dev Essentially functions like ovmCREATE or ovmCREATE2, but we can bypass a lot of checks
* because the contract we're creating is trusted (no need to do safety checking or to
* handle unexpected reverts). Doesn't need to return an address because the address is
* assumed to be the user's actual address.
* @param _messageHash Hash of a message signed by some user, for verification.
* @param _v Signature `v` parameter.
* @param _r Signature `r` parameter.
* @param _s Signature `s` parameter.
*/
function ovmCREATEEOA(
bytes32 _messageHash,
uint8 _v,
bytes32 _r,
bytes32 _s
)
override
public
notStatic
{
// Recover the EOA address from the message hash and signature parameters. Since we do the
// hashing in advance, we don't have handle different message hashing schemes. Even if this
// function were to return the wrong address (rather than explicitly returning the zero
// address), the rest of the transaction would simply fail (since there's no EOA account to
// actually execute the transaction).
address eoa = ecrecover(
_messageHash,
(_v - uint8(ovmCHAINID()) * 2) - 8,
_r,
_s
);
// Invalid signature is a case we proactively handle with a revert. We could alternatively
// have this function return a `success` boolean, but this is just easier.
if (eoa == address(0)) {
ovmREVERT(bytes("Signature provided for EOA contract creation is invalid."));
}
// If the user already has an EOA account, then there's no need to perform this operation.
if (_hasEmptyAccount(eoa) == false) {
return;
}
// We always need to initialize the contract with the default account values.
_initPendingAccount(eoa);
// Now actually create the account and get its bytecode. We're not worried about reverts
// (other than out of gas, which we can't capture anyway) because this contract is trusted.
OVM_ECDSAContractAccount eoaContractAccount = new OVM_ECDSAContractAccount();
bytes memory deployedCode = Lib_EthUtils.getCode(address(eoaContractAccount));
// Commit the account with its final values.
_commitPendingAccount(
eoa,
address(eoaContractAccount),
keccak256(deployedCode)
);
}
/*********************************
* Opcodes: Contract Interaction *
*********************************/
/**
* @notice Overrides CALL.
* @param _gasLimit Amount of gas to be passed into this call.
* @param _address Address of the contract to call.
* @param _calldata Data to send along with the call.
* @return _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call.
*/
function ovmCALL(
uint256 _gasLimit,
address _address,
bytes memory _calldata
)
override
public
netGasCost(100000)
returns (
bool _success,
bytes memory _returndata
)
{
// CALL updates the CALLER and ADDRESS.
MessageContext memory nextMessageContext = messageContext;
nextMessageContext.ovmCALLER = nextMessageContext.ovmADDRESS;
nextMessageContext.ovmADDRESS = _address;
nextMessageContext.isCreation = false;
bool isStaticEntrypoint = false;
return _callContract(
nextMessageContext,
_gasLimit,
_address,
_calldata,
isStaticEntrypoint
);
}
/**
* @notice Overrides STATICCALL.
* @param _gasLimit Amount of gas to be passed into this call.
* @param _address Address of the contract to call.
* @param _calldata Data to send along with the call.
* @return _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call.
*/
function ovmSTATICCALL(
uint256 _gasLimit,
address _address,
bytes memory _calldata
)
override
public
netGasCost(80000)
returns (
bool _success,
bytes memory _returndata
)
{
// STATICCALL updates the CALLER, updates the ADDRESS, and runs in a static context.
MessageContext memory nextMessageContext = messageContext;
nextMessageContext.ovmCALLER = nextMessageContext.ovmADDRESS;
nextMessageContext.ovmADDRESS = _address;
nextMessageContext.isStatic = true;
nextMessageContext.isCreation = false;
bool isStaticEntrypoint = true;
return _callContract(
nextMessageContext,
_gasLimit,
_address,
_calldata,
isStaticEntrypoint
);
}
/**
* @notice Overrides DELEGATECALL.
* @param _gasLimit Amount of gas to be passed into this call.
* @param _address Address of the contract to call.
* @param _calldata Data to send along with the call.
* @return _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call.
*/
function ovmDELEGATECALL(
uint256 _gasLimit,
address _address,
bytes memory _calldata
)
override
public
netGasCost(40000)
returns (
bool _success,
bytes memory _returndata
)
{
// DELEGATECALL does not change anything about the message context.
MessageContext memory nextMessageContext = messageContext;
nextMessageContext.isCreation = false;
bool isStaticEntrypoint = false;
return _callContract(
nextMessageContext,
_gasLimit,
_address,
_calldata,
isStaticEntrypoint
);
}
/************************************
* Opcodes: Contract Storage Access *
************************************/
/**
* @notice Overrides SLOAD.
* @param _key 32 byte key of the storage slot to load.
* @return _value 32 byte value of the requested storage slot.
*/
function ovmSLOAD(
bytes32 _key
)
override
public
netGasCost(40000)
returns (
bytes32 _value
)
{
// We always SLOAD from the storage of ADDRESS.
address contractAddress = ovmADDRESS();
return _getContractStorage(
contractAddress,
_key
);
}
/**
* @notice Overrides SSTORE.
* @param _key 32 byte key of the storage slot to set.
* @param _value 32 byte value for the storage slot.
*/
function ovmSSTORE(
bytes32 _key,
bytes32 _value
)
override
public
notStatic
netGasCost(60000)
{
// We always SSTORE to the storage of ADDRESS.
address contractAddress = ovmADDRESS();
_putContractStorage(
contractAddress,
_key,
_value
);
}
/*********************************
* Opcodes: Contract Code Access *
*********************************/
/**
* @notice Overrides EXTCODECOPY.
* @param _contract Address of the contract to copy code from.
* @param _offset Offset in bytes from the start of contract code to copy beyond.
* @param _length Total number of bytes to copy from the contract's code.
* @return _code Bytes of code copied from the requested contract.
*/
function ovmEXTCODECOPY(
address _contract,
uint256 _offset,
uint256 _length
)
override
public
returns (
bytes memory _code
)
{
// `ovmEXTCODECOPY` is the only overridden opcode capable of producing exactly one byte of
// return data. By blocking reads of one byte, we're able to use the condition that an
// OVM_ExecutionManager function return value having a length of exactly one byte indicates
// an error without an explicit revert. If users were able to read a single byte, they
// could forcibly trigger behavior that should only be available to this contract.
uint256 length = _length == 1 ? 2 : _length;
return Lib_EthUtils.getCode(
_getAccountEthAddress(_contract),
_offset,
length
);
}
/**
* @notice Overrides EXTCODESIZE.
* @param _contract Address of the contract to query the size of.
* @return _EXTCODESIZE Size of the requested contract in bytes.
*/
function ovmEXTCODESIZE(
address _contract
)
override
public
returns (
uint256 _EXTCODESIZE
)
{
return Lib_EthUtils.getCodeSize(
_getAccountEthAddress(_contract)
);
}
/**
* @notice Overrides EXTCODEHASH.
* @param _contract Address of the contract to query the hash of.
* @return _EXTCODEHASH Size of the requested contract in bytes.
*/
function ovmEXTCODEHASH(
address _contract
)
override
public
returns (
bytes32 _EXTCODEHASH
)
{
return Lib_EthUtils.getCodeHash(
_getAccountEthAddress(_contract)
);
}
/**************************************
* Public Functions: Execution Safety *
**************************************/
/**
* Performs the logic to create a contract and revert under various potential conditions.
* @dev This function is implemented as `public` because we need to be able to revert a
* contract creation without losing information about exactly *why* the contract reverted.
* In particular, we want to be sure that contracts cannot trigger an INVALID_STATE_ACCESS
* flag and then revert to reset the flag. We're able to do this by making an external
* call from `ovmCREATE` and `ovmCREATE2` to `safeCREATE`, which can capture and relay
* information before reverting.
* @param _address Address of the contract to associate with the one being created.
* @param _bytecode Code to be used to create the new contract.
*/
function safeCREATE(
address _address,
bytes memory _bytecode
)
override
public
{
// Since this function is public, anyone can attempt to directly call it. We need to make
// sure that the OVM_ExecutionManager itself is the only party that can actually try to
// call this function.
if (msg.sender != address(this)) {
return;
}
// We need to be sure that the user isn't trying to use a contract creation to overwrite
// some existing contract. On L1, users will prove that no contract exists at the address
// and the OVM_FraudVerifier will populate the code hash of this address with a special
// value that represents "known to be an empty account."
if (_hasEmptyAccount(_address) == false) {
_revertWithFlag(RevertFlag.CREATE_COLLISION);
}
// Check the creation bytecode against the OVM_SafetyChecker.
if (ovmSafetyChecker.isBytecodeSafe(_bytecode) == false) {
_revertWithFlag(RevertFlag.UNSAFE_BYTECODE);
}
// We always need to initialize the contract with the default account values.
_initPendingAccount(_address);
// We're going into a contract creation, so we need to set this flag to get the correct
// revert behavior.
messageContext.isCreation = true;
// Actually deploy the contract and retrieve its address. This step is hiding a lot of
// complexity because we need to ensure that contract creation *never* reverts by itself.
// We cover this partially by storing a revert flag and returning (instead of reverting)
// when we know that we're inside a contract's creation code.
address ethAddress = Lib_EthUtils.createContract(_bytecode);
// Now reset this flag so we go back to normal revert behavior.
messageContext.isCreation = false;
// Contract creation returns the zero address when it fails, which should only be possible
// if the user intentionally runs out of gas. However, we might still have a bit of gas
// left over since contract calls can only be passed 63/64ths of total gas, so we need to
// explicitly handle this case here.
if (ethAddress == address(0)) {
_revertWithFlag(RevertFlag.CREATE_EXCEPTION);
}
// Here we pull out the revert flag that would've been set during creation code. Now that
// we're out of creation code again, we can just revert normally while passing the flag
// through the revert data.
if (messageRecord.revertFlag != RevertFlag.DID_NOT_REVERT) {
_revertWithFlag(messageRecord.revertFlag);
}
// Again simply checking that the deployed code is safe too. Contracts can generate
// arbitrary deployment code, so there's no easy way to analyze this beforehand.
bytes memory deployedCode = Lib_EthUtils.getCode(ethAddress);
if (ovmSafetyChecker.isBytecodeSafe(deployedCode) == false) {
_revertWithFlag(RevertFlag.UNSAFE_BYTECODE);
}
// Contract creation didn't need to be reverted and the bytecode is safe. We finish up by
// associating the desired address with the newly created contract's code hash and address.
_commitPendingAccount(
_address,
ethAddress,
keccak256(deployedCode)
);
}
/********************************************
* Internal Functions: Contract Interaction *
********************************************/
/**
* Creates a new contract and associates it with some contract address.
* @param _contractAddress Address to associate the created contract with.
* @param _bytecode Bytecode to be used to create the contract.
* @return _created Final OVM contract address.
*/
function _createContract(
address _contractAddress,
bytes memory _bytecode
)
internal
returns (
address _created
)
{
// We always update the nonce of the creating account, even if the creation fails.
_setAccountNonce(ovmADDRESS(), 1);
// We're stepping into a CREATE or CREATE2, so we need to update ADDRESS to point
// to the contract's associated address and CALLER to point to the previous ADDRESS.
MessageContext memory nextMessageContext = messageContext;
nextMessageContext.ovmCALLER = messageContext.ovmADDRESS;
nextMessageContext.ovmADDRESS = _contractAddress;
// Run `safeCREATE` in a new EVM message so that our changes can be reflected even if
// `safeCREATE` reverts.
(bool _success, ) = _handleExternalInteraction(
nextMessageContext,
gasleft(),
address(this),
abi.encodeWithSignature(
"safeCREATE(address,bytes)",
_contractAddress,
_bytecode
),
false
);
// Need to make sure that this flag is reset so that it isn't propagated to creations in
// some parent EVM message.
messageRecord.revertFlag = RevertFlag.DID_NOT_REVERT;
// Yellow paper requires that address returned is zero if the contract deployment fails.
return _success ? _contractAddress : address(0);
}
/**
* Calls the deployed contract associated with a given address.
* @param _nextMessageContext Message context to be used for the call.
* @param _gasLimit Amount of gas to be passed into this call.
* @param _contract Address used to resolve the deployed contract.
* @param _calldata Data to send along with the call.
* @param _isStaticEntrypoint Whether or not this is coming from ovmSTATICCALL.
* @return _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call.
*/
function _callContract(
MessageContext memory _nextMessageContext,
uint256 _gasLimit,
address _contract,
bytes memory _calldata,
bool _isStaticEntrypoint
)
internal
returns (
bool _success,
bytes memory _returndata
)
{
return _handleExternalInteraction(
_nextMessageContext,
_gasLimit,
_getAccountEthAddress(_contract),
_calldata,
_isStaticEntrypoint
);
}
/**
* Handles the logic of making an external call and parsing revert information.
* @param _nextMessageContext Message context to be used for the call.
* @param _gasLimit Amount of gas to be passed into this call.
* @param _target Address of the contract to call.
* @param _data Data to send along with the call.
* @param _isStaticEntrypoint Whether or not this is coming from ovmSTATICCALL.
* @return _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call.
*/
function _handleExternalInteraction(
MessageContext memory _nextMessageContext,
uint256 _gasLimit,
address _target,
bytes memory _data,
bool _isStaticEntrypoint
)
internal
returns (
bool _success,
bytes memory _returndata
)
{
// We need to switch over to our next message context for the duration of this call.
MessageContext memory prevMessageContext = messageContext;
_switchMessageContext(prevMessageContext, _nextMessageContext);
// Nuisance gas is a system used to bound the ability for an attacker to make fraud proofs
// expensive by touching a lot of different accounts or storage slots. Since most contracts
// only use a few storage slots during any given transaction, this shouldn't be a limiting
// factor.
uint256 prevNuisanceGasLeft = messageRecord.nuisanceGasLeft;
uint256 nuisanceGasLimit = _getNuisanceGasLimit(_gasLimit);
messageRecord.nuisanceGasLeft = nuisanceGasLimit;
// Make the call and make sure to pass in the gas limit. Another instance of hidden
// complexity. `_target` is guaranteed to be a safe contract, meaning its return/revert
// behavior can be controlled. In particular, we enforce that flags are passed through
// revert data as to retrieve execution metadata that would normally be reverted out of
// existence.
(bool success, bytes memory returndata) = _target.call{gas: _gasLimit}(_data);
// Switch back to the original message context now that we're out of the call.
_switchMessageContext(_nextMessageContext, prevMessageContext);
// Assuming there were no reverts, the message record should be accurate here. We'll update
// this value in the case of a revert.
uint256 nuisanceGasLeft = messageRecord.nuisanceGasLeft;
// Reverts at this point are completely OK, but we need to make a few updates based on the
// information passed through the revert.
if (success == false) {
(
RevertFlag flag,
uint256 nuisanceGasLeftPostRevert,
uint256 ovmGasRefund,
bytes memory returndataFromFlag
) = _decodeRevertData(returndata);
// INVALID_STATE_ACCESS is the only flag that triggers an immediate abort of the
// parent EVM message. This behavior is necessary because INVALID_STATE_ACCESS must
// halt any further transaction execution that could impact the execution result.
if (flag == RevertFlag.INVALID_STATE_ACCESS) {
_revertWithFlag(flag);
}
// STATIC_VIOLATION should be passed all the way back up to the previous ovmSTATICCALL
// in the call stack.
if (
flag == RevertFlag.STATIC_VIOLATION
&& _isStaticEntrypoint == false
) {
_revertWithFlag(flag);
}
// INTENTIONAL_REVERT and UNSAFE_BYTECODE aren't dependent on the input state, so we
// can just handle them like standard reverts. Our only change here is to record the
// gas refund reported by the call (enforced by safety checking).
if (
flag == RevertFlag.INTENTIONAL_REVERT
|| flag == RevertFlag.UNSAFE_BYTECODE
) {
transactionRecord.ovmGasRefund = ovmGasRefund;
}
// INTENTIONAL_REVERT needs to pass up the user-provided return data encoded into the
// flag, *not* the full encoded flag. All other revert types return no data.
if (flag == RevertFlag.INTENTIONAL_REVERT) {
returndata = returndataFromFlag;
} else {
returndata = hex'';
}
// Reverts mean we need to use up whatever "nuisance gas" was used by the call.
// EXCEEDS_NUISANCE_GAS explicitly reduces the remaining nuisance gas for this message
// to zero. OUT_OF_GAS is a "pseudo" flag given that messages return no data when they
// run out of gas, so we have to treat this like EXCEEDS_NUISANCE_GAS. All other flags
// will simply pass up the remaining nuisance gas.
nuisanceGasLeft = nuisanceGasLeftPostRevert;
}
// We need to reset the nuisance gas back to its original value minus the amount used here.
messageRecord.nuisanceGasLeft = prevNuisanceGasLeft - (nuisanceGasLimit - nuisanceGasLeft);
return (
success,
returndata
);
}
/******************************************
* Internal Functions: State Manipulation *
******************************************/
/**
* Checks whether an account exists within the OVM_StateManager.
* @param _address Address of the account to check.
* @return _exists Whether or not the account exists.
*/
function _hasAccount(
address _address
)
internal
returns (
bool _exists
)
{
_checkAccountLoad(_address);
return ovmStateManager.hasAccount(_address);
}
/**
* Checks whether a known empty account exists within the OVM_StateManager.
* @param _address Address of the account to check.
* @return _exists Whether or not the account empty exists.
*/
function _hasEmptyAccount(
address _address
)
internal
returns (
bool _exists
)
{
_checkAccountLoad(_address);
return ovmStateManager.hasEmptyAccount(_address);
}
/**
* Sets the nonce of an account.
* @param _address Address of the account to modify.
* @param _nonce New account nonce.
*/
function _setAccountNonce(
address _address,
uint256 _nonce
)
internal
{
_checkAccountChange(_address);
ovmStateManager.setAccountNonce(_address, _nonce);
}
/**
* Gets the nonce of an account.
* @param _address Address of the account to access.
* @return _nonce Nonce of the account.
*/
function _getAccountNonce(
address _address
)
internal
returns (
uint256 _nonce
)
{
_checkAccountLoad(_address);
return ovmStateManager.getAccountNonce(_address);
}
/**
* Retrieves the Ethereum address of an account.
* @param _address Address of the account to access.
* @return _ethAddress Corresponding Ethereum address.
*/
function _getAccountEthAddress(
address _address
)
internal
returns (
address _ethAddress
)
{
_checkAccountLoad(_address);
return ovmStateManager.getAccountEthAddress(_address);
}
/**
* Creates the default account object for the given address.
* @param _address Address of the account create.
*/
function _initPendingAccount(
address _address
)
internal
{
// Although it seems like `_checkAccountChange` would be more appropriate here, we don't
// actually consider an account "changed" until it's inserted into the state (in this case
// by `_commitPendingAccount`).
_checkAccountLoad(_address);
ovmStateManager.initPendingAccount(_address);
}
/**
* Stores additional relevant data for a new account, thereby "committing" it to the state.
* This function is only called during `ovmCREATE` and `ovmCREATE2` after a successful contract
* creation.
* @param _address Address of the account to commit.
* @param _ethAddress Address of the associated deployed contract.
* @param _codeHash Hash of the code stored at the address.
*/
function _commitPendingAccount(
address _address,
address _ethAddress,
bytes32 _codeHash
)
internal
{
_checkAccountChange(_address);
ovmStateManager.commitPendingAccount(
_address,
_ethAddress,
_codeHash
);
}
/**
* Retrieves the value of a storage slot.
* @param _contract Address of the contract to query.
* @param _key 32 byte key of the storage slot.
* @return _value 32 byte storage slot value.
*/
function _getContractStorage(
address _contract,
bytes32 _key
)
internal
returns (
bytes32 _value
)
{
_checkContractStorageLoad(_contract, _key);
return ovmStateManager.getContractStorage(_contract, _key);
}
/**
* Sets the value of a storage slot.
* @param _contract Address of the contract to modify.
* @param _key 32 byte key of the storage slot.
* @param _value 32 byte storage slot value.
*/
function _putContractStorage(
address _contract,
bytes32 _key,
bytes32 _value
)
internal
{
_checkContractStorageChange(_contract, _key);
ovmStateManager.putContractStorage(_contract, _key, _value);
}
/**
* Validation whenever a contract needs to be loaded. Checks that the account exists, charges
* nuisance gas if the account hasn't been loaded before.
* @param _address Address of the account to load.
*/
function _checkAccountLoad(
address _address
)
internal
{
// See `_checkContractStorageLoad` for more information.
if (gasleft() < MIN_GAS_FOR_INVALID_STATE_ACCESS) {
_revertWithFlag(RevertFlag.OUT_OF_GAS);
}
// See `_checkContractStorageLoad` for more information.
if (ovmStateManager.hasAccount(_address) == false) {
_revertWithFlag(RevertFlag.INVALID_STATE_ACCESS);
}
// Check whether the account has been loaded before and mark it as loaded if not. We need
// this because "nuisance gas" only applies to the first time that an account is loaded.
(
bool _wasAccountAlreadyLoaded
) = ovmStateManager.testAndSetAccountLoaded(_address);
// If we hadn't already loaded the account, then we'll need to charge "nuisance gas" based
// on the size of the contract code.
if (_wasAccountAlreadyLoaded == false) {
_useNuisanceGas(
(Lib_EthUtils.getCodeSize(_getAccountEthAddress(_address)) * NUISANCE_GAS_PER_CONTRACT_BYTE) + MIN_NUISANCE_GAS_PER_CONTRACT
);
}
}
/**
* Validation whenever a contract needs to be changed. Checks that the account exists, charges
* nuisance gas if the account hasn't been changed before.
* @param _address Address of the account to change.
*/
function _checkAccountChange(
address _address
)
internal
{
// Check whether the account has been changed before and mark it as changed if not. We need
// this because "nuisance gas" only applies to the first time that an account is changed.
(
bool _wasAccountAlreadyChanged
) = ovmStateManager.testAndSetAccountChanged(_address);
// If we hadn't already changed the account, then we'll need to charge "nuisance gas" based
// on the size of the contract code.
if (_wasAccountAlreadyChanged == false) {
ovmStateManager.incrementTotalUncommittedAccounts();
_useNuisanceGas(
(Lib_EthUtils.getCodeSize(_getAccountEthAddress(_address)) * NUISANCE_GAS_PER_CONTRACT_BYTE) + MIN_NUISANCE_GAS_PER_CONTRACT
);
}
}
/**
* Validation whenever a slot needs to be loaded. Checks that the account exists, charges
* nuisance gas if the slot hasn't been loaded before.
* @param _contract Address of the account to load from.
* @param _key 32 byte key to load.
*/
function _checkContractStorageLoad(
address _contract,
bytes32 _key
)
internal
{
// Another case of hidden complexity. If we didn't enforce this requirement, then a
// contract could pass in just enough gas to cause the INVALID_STATE_ACCESS check to fail
// on L1 but not on L2. A contract could use this behavior to prevent the
// OVM_ExecutionManager from detecting an invalid state access. Reverting with OUT_OF_GAS
// allows us to also charge for the full message nuisance gas, because you deserve that for
// trying to break the contract in this way.
if (gasleft() < MIN_GAS_FOR_INVALID_STATE_ACCESS) {
_revertWithFlag(RevertFlag.OUT_OF_GAS);
}
// We need to make sure that the transaction isn't trying to access storage that hasn't
// been provided to the OVM_StateManager. We'll immediately abort if this is the case.
// We know that we have enough gas to do this check because of the above test.
if (ovmStateManager.hasContractStorage(_contract, _key) == false) {
_revertWithFlag(RevertFlag.INVALID_STATE_ACCESS);
}
// Check whether the slot has been loaded before and mark it as loaded if not. We need
// this because "nuisance gas" only applies to the first time that a slot is loaded.
(
bool _wasContractStorageAlreadyLoaded
) = ovmStateManager.testAndSetContractStorageLoaded(_contract, _key);
// If we hadn't already loaded the account, then we'll need to charge some fixed amount of
// "nuisance gas".
if (_wasContractStorageAlreadyLoaded == false) {
_useNuisanceGas(NUISANCE_GAS_SLOAD);
}
}
/**
* Validation whenever a slot needs to be changed. Checks that the account exists, charges
* nuisance gas if the slot hasn't been changed before.
* @param _contract Address of the account to change.
* @param _key 32 byte key to change.
*/
function _checkContractStorageChange(
address _contract,
bytes32 _key
)
internal
{
// See `_checkContractStorageLoad` for more information.
if (gasleft() < MIN_GAS_FOR_INVALID_STATE_ACCESS) {
_revertWithFlag(RevertFlag.OUT_OF_GAS);
}
// See `_checkContractStorageLoad` for more information.
if (ovmStateManager.hasContractStorage(_contract, _key) == false) {
_revertWithFlag(RevertFlag.INVALID_STATE_ACCESS);
}
// Check whether the slot has been changed before and mark it as changed if not. We need
// this because "nuisance gas" only applies to the first time that a slot is changed.
(
bool _wasContractStorageAlreadyChanged
) = ovmStateManager.testAndSetContractStorageChanged(_contract, _key);
// If we hadn't already changed the account, then we'll need to charge some fixed amount of
// "nuisance gas".
if (_wasContractStorageAlreadyChanged == false) {
ovmStateManager.incrementTotalUncommittedContractStorage();
_useNuisanceGas(NUISANCE_GAS_SSTORE);
}
}
/************************************
* Internal Functions: Revert Logic *
************************************/
/**
* Simple encoding for revert data.
* @param _flag Flag to revert with.
* @param _data Additional user-provided revert data.
* @return _revertdata Encoded revert data.
*/
function _encodeRevertData(
RevertFlag _flag,
bytes memory _data
)
internal
returns (
bytes memory _revertdata
)
{
// Out of gas and create exceptions will fundamentally return no data, so simulating it shouldn't either.
if (
_flag == RevertFlag.OUT_OF_GAS
|| _flag == RevertFlag.CREATE_EXCEPTION
) {
return bytes('');
}
// INVALID_STATE_ACCESS doesn't need to return any data other than the flag.
if (_flag == RevertFlag.INVALID_STATE_ACCESS) {
return abi.encode(
_flag,
0,
0,
bytes('')
);
}
// Just ABI encode the rest of the parameters.
return abi.encode(
_flag,
messageRecord.nuisanceGasLeft,
transactionRecord.ovmGasRefund,
_data
);
}
/**
* Simple decoding for revert data.
* @param _revertdata Revert data to decode.
* @return _flag Flag used to revert.
* @return _nuisanceGasLeft Amount of nuisance gas unused by the message.
* @return _ovmGasRefund Amount of gas refunded during the message.
* @return _data Additional user-provided revert data.
*/
function _decodeRevertData(
bytes memory _revertdata
)
internal
returns (
RevertFlag _flag,
uint256 _nuisanceGasLeft,
uint256 _ovmGasRefund,
bytes memory _data
)
{
// A length of zero means the call ran out of gas, just return empty data.
if (_revertdata.length == 0) {
return (
RevertFlag.OUT_OF_GAS,
0,
0,
bytes('')
);
}
// ABI decode the incoming data.
return abi.decode(_revertdata, (RevertFlag, uint256, uint256, bytes));
}
/**
* Causes a message to revert or abort.
* @param _flag Flag to revert with.
* @param _data Additional user-provided data.
*/
function _revertWithFlag(
RevertFlag _flag,
bytes memory _data
)
internal
{
// We don't want to revert when we're inside a CREATE or CREATE2, because those opcodes
// fail silently (we can't pass any data upwards). Instead, we set a flag and return a
// *single* byte, something the OVM_ExecutionManager will not return in any other case.
// We're thereby allowed to communicate failure without allowing contracts to trick us into
// thinking there was a failure.
if (messageContext.isCreation) {
messageRecord.revertFlag = _flag;
assembly {
return(0, 1)
}
}
// If we're not inside a CREATE or CREATE2, we can simply encode the necessary data and
// revert normally.
bytes memory revertdata = _encodeRevertData(
_flag,
_data
);
assembly {
revert(add(revertdata, 0x20), mload(revertdata))
}
}
/**
* Causes a message to revert or abort.
* @param _flag Flag to revert with.
*/
function _revertWithFlag(
RevertFlag _flag
)
internal
{
_revertWithFlag(_flag, bytes(''));
}
/******************************************
* Internal Functions: Nuisance Gas Logic *
******************************************/
/**
* Computes the nuisance gas limit from the gas limit.
* @dev This function is currently using a naive implementation whereby the nuisance gas limit
* is set to exactly equal the lesser of the gas limit or remaining gas. It's likely that
* this implementation is perfectly fine, but we may change this formula later.
* @param _gasLimit Gas limit to compute from.
* @return _nuisanceGasLimit Computed nuisance gas limit.
*/
function _getNuisanceGasLimit(
uint256 _gasLimit
)
internal
view
returns (
uint256 _nuisanceGasLimit
)
{
return _gasLimit < gasleft() ? _gasLimit : gasleft();
}
/**
* Uses a certain amount of nuisance gas.
* @param _amount Amount of nuisance gas to use.
*/
function _useNuisanceGas(
uint256 _amount
)
internal
{
// Essentially the same as a standard OUT_OF_GAS, except we also retain a record of the gas
// refund to be given at the end of the transaction.
if (messageRecord.nuisanceGasLeft < _amount) {
_revertWithFlag(RevertFlag.EXCEEDS_NUISANCE_GAS);
}
messageRecord.nuisanceGasLeft -= _amount;
}
/************************************
* Internal Functions: Gas Metering *
************************************/
/**
* Checks whether a transaction needs to start a new epoch and does so if necessary.
* @param _timestamp Transaction timestamp.
*/
function _checkNeedsNewEpoch(
uint256 _timestamp
)
internal
{
if (
_timestamp >= (
_getGasMetadata(GasMetadataKey.CURRENT_EPOCH_START_TIMESTAMP)
+ gasMeterConfig.secondsPerEpoch
)
) {
_putGasMetadata(
GasMetadataKey.CURRENT_EPOCH_START_TIMESTAMP,
_timestamp
);
_putGasMetadata(
GasMetadataKey.PREV_EPOCH_SEQUENCER_QUEUE_GAS,
_getGasMetadata(
GasMetadataKey.CUMULATIVE_SEQUENCER_QUEUE_GAS
)
);
_putGasMetadata(
GasMetadataKey.PREV_EPOCH_L1TOL2_QUEUE_GAS,
_getGasMetadata(
GasMetadataKey.CUMULATIVE_L1TOL2_QUEUE_GAS
)
);
}
}
/**
* Validates the gas limit for a given transaction.
* @param _gasLimit Gas limit provided by the transaction.
* @param _queueOrigin Queue from which the transaction orginated.
* @return _valid Whether or not the gas limit is valid.
*/
function _isValidGasLimit(
uint256 _gasLimit,
Lib_OVMCodec.QueueOrigin _queueOrigin
)
internal
returns (
bool _valid
)
{
if (_gasLimit > gasMeterConfig.maxTransactionGasLimit) {
return false;
}
if (_gasLimit < gasMeterConfig.minTransactionGasLimit) {
return false;
}
GasMetadataKey cumulativeGasKey;
GasMetadataKey prevEpochGasKey;
if (_queueOrigin == Lib_OVMCodec.QueueOrigin.SEQUENCER_QUEUE) {
cumulativeGasKey = GasMetadataKey.CUMULATIVE_SEQUENCER_QUEUE_GAS;
prevEpochGasKey = GasMetadataKey.PREV_EPOCH_SEQUENCER_QUEUE_GAS;
} else {
cumulativeGasKey = GasMetadataKey.CUMULATIVE_L1TOL2_QUEUE_GAS;
prevEpochGasKey = GasMetadataKey.PREV_EPOCH_L1TOL2_QUEUE_GAS;
}
return (
(
_getGasMetadata(cumulativeGasKey)
- _getGasMetadata(prevEpochGasKey)
+ _gasLimit
) > gasMeterConfig.maxGasPerQueuePerEpoch
);
}
/**
* Updates the cumulative gas after a transaction.
* @param _gasUsed Gas used by the transaction.
* @param _queueOrigin Queue from which the transaction orginated.
*/
function _updateCumulativeGas(
uint256 _gasUsed,
Lib_OVMCodec.QueueOrigin _queueOrigin
)
internal
{
GasMetadataKey cumulativeGasKey;
if (_queueOrigin == Lib_OVMCodec.QueueOrigin.SEQUENCER_QUEUE) {
cumulativeGasKey = GasMetadataKey.CUMULATIVE_SEQUENCER_QUEUE_GAS;
} else {
cumulativeGasKey = GasMetadataKey.CUMULATIVE_L1TOL2_QUEUE_GAS;
}
_putGasMetadata(
cumulativeGasKey,
(
_getGasMetadata(cumulativeGasKey)
+ gasMeterConfig.minTransactionGasLimit
+ _gasUsed
- transactionRecord.ovmGasRefund
)
);
}
/**
* Retrieves the value of a gas metadata key.
* @param _key Gas metadata key to retrieve.
* @return _value Value stored at the given key.
*/
function _getGasMetadata(
GasMetadataKey _key
)
internal
returns (
uint256 _value
)
{
return uint256(_getContractStorage(
GAS_METADATA_ADDRESS,
bytes32(uint256(_key))
));
}
/**
* Sets the value of a gas metadata key.
* @param _key Gas metadata key to set.
* @param _value Value to store at the given key.
*/
function _putGasMetadata(
GasMetadataKey _key,
uint256 _value
)
internal
{
_putContractStorage(
GAS_METADATA_ADDRESS,
bytes32(uint256(_key)),
bytes32(uint256(_value))
);
}
/*****************************************
* Internal Functions: Execution Context *
*****************************************/
/**
* Swaps over to a new message context.
* @param _prevMessageContext Context we're switching from.
* @param _nextMessageContext Context we're switching to.
*/
function _switchMessageContext(
MessageContext memory _prevMessageContext,
MessageContext memory _nextMessageContext
)
internal
{
// Avoid unnecessary the SSTORE.
if (_prevMessageContext.ovmCALLER != _nextMessageContext.ovmCALLER) {
messageContext.ovmCALLER = _nextMessageContext.ovmCALLER;
}
// Avoid unnecessary the SSTORE.
if (_prevMessageContext.ovmADDRESS != _nextMessageContext.ovmADDRESS) {
messageContext.ovmADDRESS = _nextMessageContext.ovmADDRESS;
}
// Avoid unnecessary the SSTORE.
if (_prevMessageContext.isStatic != _nextMessageContext.isStatic) {
messageContext.isStatic = _nextMessageContext.isStatic;
}
// Avoid unnecessary the SSTORE.
if (_prevMessageContext.isCreation != _nextMessageContext.isCreation) {
messageContext.isCreation = _nextMessageContext.isCreation;
}
}
/**
* Initializes the execution context.
* @param _transaction OVM transaction being executed.
*/
function _initContext(
Lib_OVMCodec.Transaction memory _transaction
)
internal
{
transactionContext.ovmTIMESTAMP = _transaction.timestamp;
transactionContext.ovmNUMBER = _transaction.number;
transactionContext.ovmTXGASLIMIT = _transaction.gasLimit;
transactionContext.ovmL1QUEUEORIGIN = _transaction.l1QueueOrigin;
transactionContext.ovmL1TXORIGIN = _transaction.l1Txorigin;
transactionContext.ovmGASLIMIT = gasMeterConfig.maxGasPerQueuePerEpoch;
}
/**
* Resets the transaction and message context.
*/
function _resetContext()
internal
{
transactionContext.ovmL1TXORIGIN = address(0);
transactionContext.ovmTIMESTAMP = 0;
transactionContext.ovmNUMBER = 0;
transactionContext.ovmGASLIMIT = 0;
transactionContext.ovmTXGASLIMIT = 0;
transactionContext.ovmL1QUEUEORIGIN = Lib_OVMCodec.QueueOrigin.SEQUENCER_QUEUE;
transactionRecord.ovmGasRefund = 0;
messageContext.ovmCALLER = address(0);
messageContext.ovmADDRESS = address(0);
messageContext.isStatic = false;
messageRecord.nuisanceGasLeft = 0;
messageRecord.revertFlag = RevertFlag.DID_NOT_REVERT;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/* Interface Imports */
import { iOVM_SafetyChecker } from "../../iOVM/execution/iOVM_SafetyChecker.sol";
/**
* @title OVM_SafetyChecker
*/
contract OVM_SafetyChecker is iOVM_SafetyChecker {
/********************
* Public Functions *
********************/
/**
* Checks that a given bytecode string is considered safe.
* @param _bytecode Bytecode string to check.
* @return _safe Whether or not the bytecode is safe.
*/
function isBytecodeSafe(
bytes memory _bytecode
)
override
public
view
returns (
bool _safe
)
{
return true;
}
}
// 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_StateManager } from "../../iOVM/execution/iOVM_StateManager.sol";
/**
* @title OVM_StateManager
*/
contract OVM_StateManager is iOVM_StateManager {
/**********************
* Contract Constants *
**********************/
bytes32 constant internal EMPTY_ACCOUNT_CODE_HASH = 0x00004B1DC0DE000000004B1DC0DE000000004B1DC0DE000000004B1DC0DE0000;
bytes32 constant internal STORAGE_XOR_VALUE = 0xFEEDFACECAFEBEEFFEEDFACECAFEBEEFFEEDFACECAFEBEEFFEEDFACECAFEBEEF;
/*******************************************
* Contract Variables: Contract References *
*******************************************/
address internal owner;
address internal ovmExecutionManager;
/****************************************
* Contract Variables: Internal Storage *
****************************************/
mapping (address => Lib_OVMCodec.Account) internal accounts;
mapping (address => mapping (bytes32 => bytes32)) internal contractStorage;
mapping (address => mapping (bytes32 => bool)) internal verifiedContractStorage;
mapping (bytes32 => ItemState) internal itemStates;
uint256 internal totalUncommittedAccounts;
uint256 internal totalUncommittedContractStorage;
/***************
* Constructor *
***************/
/**
* @param _owner Address of the owner of this contract.
*/
constructor(
address _owner
) {
owner = _owner;
}
/**********************
* Function Modifiers *
**********************/
/**
* Simple authentication, this contract should only be accessible to the owner or to the
* OVM_ExecutionManager during the transaction execution process.
*/
modifier authenticated() {
require(
msg.sender == owner || msg.sender == ovmExecutionManager,
"Function can only be called by authenticated addresses"
);
_;
}
/***************************
* Public Functions: Setup *
***************************/
/**
* Sets the address of the OVM_ExecutionManager.
* @param _ovmExecutionManager Address of the OVM_ExecutionManager.
*/
function setExecutionManager(
address _ovmExecutionManager
)
override
public
authenticated
{
ovmExecutionManager = _ovmExecutionManager;
}
/************************************
* Public Functions: Account Access *
************************************/
/**
* Inserts an account into the state.
* @param _address Address of the account to insert.
* @param _account Account to insert for the given address.
*/
function putAccount(
address _address,
Lib_OVMCodec.Account memory _account
)
override
public
authenticated
{
accounts[_address] = _account;
}
/**
* Retrieves an account from the state.
* @param _address Address of the account to retrieve.
* @return _account Account for the given address.
*/
function getAccount(address _address)
override
public
view
returns (
Lib_OVMCodec.Account memory _account
)
{
return accounts[_address];
}
/**
* Checks whether the state has a given account.
* @param _address Address of the account to check.
* @return _exists Whether or not the state has the account.
*/
function hasAccount(
address _address
)
override
public
view
returns (
bool _exists
)
{
return accounts[_address].codeHash != bytes32(0);
}
/**
* Checks whether the state has a given known empty account.
* @param _address Address of the account to check.
* @return _exists Whether or not the state has the empty account.
*/
function hasEmptyAccount(
address _address
)
override
public
view
returns (
bool _exists
)
{
return accounts[_address].codeHash == EMPTY_ACCOUNT_CODE_HASH;
}
/**
* Sets the nonce of an account.
* @param _address Address of the account to modify.
* @param _nonce New account nonce.
*/
function setAccountNonce(
address _address,
uint256 _nonce
)
override
public
authenticated
{
accounts[_address].nonce = _nonce;
}
/**
* Gets the nonce of an account.
* @param _address Address of the account to access.
* @return _nonce Nonce of the account.
*/
function getAccountNonce(
address _address
)
override
public
view
returns (
uint256 _nonce
)
{
return accounts[_address].nonce;
}
/**
* Retrieves the Ethereum address of an account.
* @param _address Address of the account to access.
* @return _ethAddress Corresponding Ethereum address.
*/
function getAccountEthAddress(
address _address
)
override
public
view
returns (
address _ethAddress
)
{
return accounts[_address].ethAddress;
}
/**
* Initializes a pending account (during CREATE or CREATE2) with the default values.
* @param _address Address of the account to initialize.
*/
function initPendingAccount(
address _address
)
override
public
authenticated
{
Lib_OVMCodec.Account storage account = accounts[_address];
account.nonce = 1;
account.codeHash = keccak256(hex'80');
account.isFresh = true;
}
/**
* Finalizes the creation of a pending account (during CREATE or CREATE2).
* @param _address Address of the account to finalize.
* @param _ethAddress Address of the account's associated contract on Ethereum.
* @param _codeHash Hash of the account's code.
*/
function commitPendingAccount(
address _address,
address _ethAddress,
bytes32 _codeHash
)
override
public
authenticated
{
Lib_OVMCodec.Account storage account = accounts[_address];
account.ethAddress = _ethAddress;
account.codeHash = _codeHash;
}
/**
* Checks whether an account has already been retrieved, and marks it as retrieved if not.
* @param _address Address of the account to check.
* @return _wasAccountAlreadyLoaded Whether or not the account was already loaded.
*/
function testAndSetAccountLoaded(
address _address
)
override
public
authenticated
returns (
bool _wasAccountAlreadyLoaded
)
{
return _testAndSetItemState(
keccak256(abi.encodePacked(_address)),
ItemState.ITEM_LOADED
);
}
/**
* Checks whether an account has already been modified, and marks it as modified if not.
* @param _address Address of the account to check.
* @return _wasAccountAlreadyChanged Whether or not the account was already modified.
*/
function testAndSetAccountChanged(
address _address
)
override
public
authenticated
returns (
bool _wasAccountAlreadyChanged
)
{
return _testAndSetItemState(
keccak256(abi.encodePacked(_address)),
ItemState.ITEM_CHANGED
);
}
/**
* Attempts to mark an account as committed.
* @param _address Address of the account to commit.
* @return _wasAccountCommitted Whether or not the account was committed.
*/
function commitAccount(
address _address
)
override
public
authenticated
returns (
bool _wasAccountCommitted
)
{
bytes32 item = keccak256(abi.encodePacked(_address));
if (itemStates[item] != ItemState.ITEM_CHANGED) {
return false;
}
itemStates[item] = ItemState.ITEM_COMMITTED;
totalUncommittedAccounts -= 1;
return true;
}
/**
* Increments the total number of uncommitted accounts.
*/
function incrementTotalUncommittedAccounts()
override
public
authenticated
{
totalUncommittedAccounts += 1;
}
/**
* Gets the total number of uncommitted accounts.
* @return _total Total uncommitted accounts.
*/
function getTotalUncommittedAccounts()
override
public
view
returns (
uint256 _total
)
{
return totalUncommittedAccounts;
}
/************************************
* Public Functions: Storage Access *
************************************/
/**
* Changes a contract storage slot value.
* @param _contract Address of the contract to modify.
* @param _key 32 byte storage slot key.
* @param _value 32 byte storage slot value.
*/
function putContractStorage(
address _contract,
bytes32 _key,
bytes32 _value
)
override
public
authenticated
{
// A hilarious optimization. `SSTORE`ing a value of `bytes32(0)` is common enough that it's
// worth populating this with a non-zero value in advance (during the fraud proof
// initialization phase) to cut the execution-time cost down to 5000 gas.
contractStorage[_contract][_key] = _value ^ STORAGE_XOR_VALUE;
// Only used when initially populating the contract storage. OVM_ExecutionManager will
// perform a `hasContractStorage` INVALID_STATE_ACCESS check before putting any contract
// storage because writing to zero when the actual value is nonzero causes a gas
// discrepancy. Could be moved into a new `putVerifiedContractStorage` function, or
// something along those lines.
if (verifiedContractStorage[_contract][_key] == false) {
verifiedContractStorage[_contract][_key] = true;
}
}
/**
* Retrieves a contract storage slot value.
* @param _contract Address of the contract to access.
* @param _key 32 byte storage slot key.
* @return _value 32 byte storage slot value.
*/
function getContractStorage(
address _contract,
bytes32 _key
)
override
public
view
returns (
bytes32 _value
)
{
// Storage XOR system doesn't work for newly created contracts that haven't set this
// storage slot value yet.
if (
verifiedContractStorage[_contract][_key] == false
&& accounts[_contract].isFresh
) {
return bytes32(0);
}
// See `putContractStorage` for more information about the XOR here.
return contractStorage[_contract][_key] ^ STORAGE_XOR_VALUE;
}
/**
* Checks whether a contract storage slot exists in the state.
* @param _contract Address of the contract to access.
* @param _key 32 byte storage slot key.
* @return _exists Whether or not the key was set in the state.
*/
function hasContractStorage(
address _contract,
bytes32 _key
)
override
public
view
returns (
bool _exists
)
{
return verifiedContractStorage[_contract][_key] || accounts[_contract].isFresh;
}
/**
* Checks whether a storage slot has already been retrieved, and marks it as retrieved if not.
* @param _contract Address of the contract to check.
* @param _key 32 byte storage slot key.
* @return _wasContractStorageAlreadyLoaded Whether or not the slot was already loaded.
*/
function testAndSetContractStorageLoaded(
address _contract,
bytes32 _key
)
override
public
authenticated
returns (
bool _wasContractStorageAlreadyLoaded
)
{
return _testAndSetItemState(
keccak256(abi.encodePacked(_contract, _key)),
ItemState.ITEM_LOADED
);
}
/**
* Checks whether a storage slot has already been modified, and marks it as modified if not.
* @param _contract Address of the contract to check.
* @param _key 32 byte storage slot key.
* @return _wasContractStorageAlreadyChanged Whether or not the slot was already modified.
*/
function testAndSetContractStorageChanged(
address _contract,
bytes32 _key
)
override
public
authenticated
returns (
bool _wasContractStorageAlreadyChanged
)
{
return _testAndSetItemState(
keccak256(abi.encodePacked(_contract, _key)),
ItemState.ITEM_CHANGED
);
}
/**
* Attempts to mark a storage slot as committed.
* @param _contract Address of the account to commit.
* @param _key 32 byte slot key to commit.
* @return _wasContractStorageCommitted Whether or not the slot was committed.
*/
function commitContractStorage(
address _contract,
bytes32 _key
)
override
public
authenticated
returns (
bool _wasContractStorageCommitted
)
{
bytes32 item = keccak256(abi.encodePacked(_contract, _key));
if (itemStates[item] != ItemState.ITEM_CHANGED) {
return false;
}
itemStates[item] = ItemState.ITEM_COMMITTED;
totalUncommittedContractStorage -= 1;
return true;
}
/**
* Increments the total number of uncommitted storage slots.
*/
function incrementTotalUncommittedContractStorage()
override
public
authenticated
{
totalUncommittedContractStorage += 1;
}
/**
* Gets the total number of uncommitted storage slots.
* @return _total Total uncommitted storage slots.
*/
function getTotalUncommittedContractStorage()
override
public
view
returns (
uint256 _total
)
{
return totalUncommittedContractStorage;
}
/**********************
* Internal Functions *
**********************/
/**
* Checks whether an item is in a particular state (ITEM_LOADED or ITEM_CHANGED) and sets the
* item to the provided state if not.
* @param _item 32 byte item ID to check.
* @param _minItemState Minumum state that must be satisfied by the item.
* @return _wasItemState Whether or not the item was already in the state.
*/
function _testAndSetItemState(
bytes32 _item,
ItemState _minItemState
)
internal
returns (
bool _wasItemState
)
{
bool wasItemState = itemStates[_item] >= _minItemState;
if (wasItemState == false) {
itemStates[_item] = _minItemState;
}
return wasItemState;
}
}
// 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 *
***************************************/
/**
* Creates a new OVM_StateManager
* @param _owner Owner of the created contract.
* @return _ovmStateManager New OVM_StateManager instance.
*/
function create(
address _owner
)
override
public
returns (
iOVM_StateManager _ovmStateManager
)
{
return new OVM_StateManager(_owner);
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/* Library Imports */
import { Lib_Bytes32Utils } from "../../libraries/utils/Lib_Bytes32Utils.sol";
/* Interface Imports */
import { iOVM_DeployerWhitelist } from "../../iOVM/precompiles/iOVM_DeployerWhitelist.sol";
import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol";
/**
* @title OVM_DeployerWhitelist
*/
contract OVM_DeployerWhitelist is iOVM_DeployerWhitelist {
/**********************
* Contract Constants *
**********************/
bytes32 internal constant KEY_INITIALIZED = 0x0000000000000000000000000000000000000000000000000000000000000010;
bytes32 internal constant KEY_OWNER = 0x0000000000000000000000000000000000000000000000000000000000000011;
bytes32 internal constant KEY_ALLOW_ARBITRARY_DEPLOYMENT = 0x0000000000000000000000000000000000000000000000000000000000000012;
/**********************
* Function Modifiers *
**********************/
/**
* Blocks functions to anyone except the contract owner.
*/
modifier onlyOwner() {
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
address owner = Lib_Bytes32Utils.toAddress(
ovmExecutionManager.ovmSLOAD(
KEY_OWNER
)
);
require(
ovmExecutionManager.ovmCALLER() == owner,
"Function can only be called by the owner of this contract."
);
_;
}
/********************
* Public Functions *
********************/
/**
* Initializes the whitelist.
* @param _owner Address of the owner for this contract.
* @param _allowArbitraryDeployment Whether or not to allow arbitrary contract deployment.
*/
function initialize(
address _owner,
bool _allowArbitraryDeployment
)
override
public
{
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
bool initialized = Lib_Bytes32Utils.toBool(
ovmExecutionManager.ovmSLOAD(KEY_INITIALIZED)
);
if (initialized == true) {
return;
}
ovmExecutionManager.ovmSSTORE(
KEY_INITIALIZED,
Lib_Bytes32Utils.fromBool(true)
);
ovmExecutionManager.ovmSSTORE(
KEY_OWNER,
Lib_Bytes32Utils.fromAddress(_owner)
);
ovmExecutionManager.ovmSSTORE(
KEY_ALLOW_ARBITRARY_DEPLOYMENT,
Lib_Bytes32Utils.fromBool(_allowArbitraryDeployment)
);
}
/**
* Adds or removes an address from the deployment whitelist.
* @param _deployer Address to update permissions for.
* @param _isWhitelisted Whether or not the address is whitelisted.
*/
function setWhitelistedDeployer(
address _deployer,
bool _isWhitelisted
)
override
public
onlyOwner
{
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
ovmExecutionManager.ovmSSTORE(
Lib_Bytes32Utils.fromAddress(_deployer),
Lib_Bytes32Utils.fromBool(_isWhitelisted)
);
}
/**
* Updates the owner of this contract.
* @param _owner Address of the new owner.
*/
function setOwner(
address _owner
)
override
public
onlyOwner
{
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
ovmExecutionManager.ovmSSTORE(
KEY_OWNER,
Lib_Bytes32Utils.fromAddress(_owner)
);
}
/**
* Updates the arbitrary deployment flag.
* @param _allowArbitraryDeployment Whether or not to allow arbitrary contract deployment.
*/
function setAllowArbitraryDeployment(
bool _allowArbitraryDeployment
)
override
public
onlyOwner
{
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
ovmExecutionManager.ovmSSTORE(
KEY_ALLOW_ARBITRARY_DEPLOYMENT,
Lib_Bytes32Utils.fromBool(_allowArbitraryDeployment)
);
}
/**
* Permanently enables arbitrary contract deployment and deletes the owner.
*/
function enableArbitraryContractDeployment()
override
public
onlyOwner
{
setAllowArbitraryDeployment(true);
setOwner(address(0));
}
/**
* Checks whether an address is allowed to deploy contracts.
* @param _deployer Address to check.
* @return _allowed Whether or not the address can deploy contracts.
*/
function isDeployerAllowed(
address _deployer
)
override
public
returns (
bool _allowed
)
{
iOVM_ExecutionManager ovmExecutionManager = iOVM_ExecutionManager(msg.sender);
bool initialized = Lib_Bytes32Utils.toBool(
ovmExecutionManager.ovmSLOAD(KEY_INITIALIZED)
);
if (initialized == false) {
return true;
}
bool allowArbitraryDeployment = Lib_Bytes32Utils.toBool(
ovmExecutionManager.ovmSLOAD(KEY_ALLOW_ARBITRARY_DEPLOYMENT)
);
if (allowArbitraryDeployment == true) {
return true;
}
bool isWhitelisted = Lib_Bytes32Utils.toBool(
ovmExecutionManager.ovmSLOAD(
Lib_Bytes32Utils.fromAddress(_deployer)
)
);
return isWhitelisted;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/* Interface Imports */
import { iOVM_L1MessageSender } from "../../iOVM/precompiles/iOVM_L1MessageSender.sol";
import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol";
/**
* @title OVM_L1MessageSender
*/
contract OVM_L1MessageSender is iOVM_L1MessageSender {
/********************
* Public Functions *
********************/
/**
* @return _l1MessageSender L1 message sender address (msg.sender).
*/
function getL1MessageSender()
override
public
returns (
address _l1MessageSender
)
{
return iOVM_ExecutionManager(msg.sender).ovmL1TXORIGIN();
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/* Interface Imports */
import { iOVM_L2ToL1MessagePasser } from "../../iOVM/precompiles/iOVM_L2ToL1MessagePasser.sol";
import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol";
/**
* @title OVM_L2ToL1MessagePasser
*/
contract OVM_L2ToL1MessagePasser is iOVM_L2ToL1MessagePasser {
/**********************
* Contract Variables *
**********************/
uint256 internal nonce;
/********************
* Public Functions *
********************/
/**
* Passes a message to L1.
* @param _message Message to pass to L1.
*/
function passMessageToL1(
bytes memory _message
)
override
public
{
// For now, to be trustfully relayed by sequencer to L1, so just emit
// an event for the sequencer to pick up.
emit L2ToL1Message(
nonce,
iOVM_ExecutionManager(msg.sender).ovmCALLER(),
_message
);
nonce = nonce + 1;
}
}
// 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;
}
}
// 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_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */
import { iOVM_L1ToL2TransactionQueue } from "../../iOVM/queue/iOVM_L1ToL2TransactionQueue.sol";
/* Contract Imports */
import { OVM_BaseQueue } from "./OVM_BaseQueue.sol";
/**
* @title OVM_L1ToL2TransactionQueue
*/
contract OVM_L1ToL2TransactionQueue is iOVM_L1ToL2TransactionQueue, OVM_BaseQueue, Lib_AddressResolver {
/*******************************************
* Contract Variables: Contract References *
*******************************************/
address internal ovmCanonicalTransactionChain;
/***************
* Constructor *
***************/
/**
* @param _libAddressManager Address of the Address Manager.
*/
constructor(
address _libAddressManager
)
Lib_AddressResolver(_libAddressManager)
{
ovmCanonicalTransactionChain = resolve("OVM_CanonicalTransactionChain");
}
/****************************************
* Public Functions: Queue Manipulation *
****************************************/
/**
* Adds a transaction to the queue.
* @param _target Target contract to send the transaction to.
* @param _gasLimit Gas limit for the given transaction.
* @param _data Transaction data.
*/
function enqueue(
address _target,
uint256 _gasLimit,
bytes memory _data
)
override
public
{
Lib_OVMCodec.QueueElement memory element = Lib_OVMCodec.QueueElement({
timestamp: block.timestamp,
batchRoot: keccak256(abi.encodePacked(
_target,
_gasLimit,
_data
)),
isL1ToL2Batch: true
});
_enqueue(element);
}
/**
* Pops an element from the queue.
*/
function dequeue()
override
public
{
require(
msg.sender == ovmCanonicalTransactionChain,
"Sender is not allowed to enqueue."
);
_dequeue();
}
}
// 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_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */
import { iOVM_FraudVerifier } from "../../iOVM/verification/iOVM_FraudVerifier.sol";
import { iOVM_StateTransitioner } from "../../iOVM/verification/iOVM_StateTransitioner.sol";
import { iOVM_StateTransitionerFactory } from "../../iOVM/verification/iOVM_StateTransitionerFactory.sol";
import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.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, Lib_AddressResolver {
/*******************************************
* Contract Variables: Contract References *
*******************************************/
iOVM_StateCommitmentChain internal ovmStateCommitmentChain;
iOVM_CanonicalTransactionChain internal ovmCanonicalTransactionChain;
/*******************************************
* Contract Variables: Internal Accounting *
*******************************************/
mapping (bytes32 => iOVM_StateTransitioner) internal transitioners;
/***************
* Constructor *
***************/
/**
* @param _libAddressManager Address of the Address Manager.
*/
constructor(
address _libAddressManager
)
Lib_AddressResolver(_libAddressManager)
{
ovmStateCommitmentChain = iOVM_StateCommitmentChain(resolve("OVM_StateCommitmentChain"));
ovmCanonicalTransactionChain = iOVM_CanonicalTransactionChain(resolve("OVM_CanonicalTransactionChain"));
}
/***************************************
* Public Functions: Transition Status *
***************************************/
/**
* Retrieves the state transitioner for a given root.
* @param _preStateRoot State root to query a transitioner for.
* @return _transitioner Corresponding state transitioner contract.
*/
function getStateTransitioner(
bytes32 _preStateRoot
)
override
public
view
returns (
iOVM_StateTransitioner _transitioner
)
{
return transitioners[_preStateRoot];
}
/****************************************
* 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.Transaction 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(libAddressManager),
_preStateRootProof.index,
_preStateRoot,
Lib_OVMCodec.hashTransaction(_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.getPostStateRoot(),
"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.verifyElement(
abi.encodePacked(_stateRoot),
_stateRootBatchHeader,
_stateRootProof
);
}
/**
* Verifies inclusion of a given transaction.
* @param _transaction OVM transaction to verify.
* @param _transactionBatchHeader Batch header for the provided transaction.
* @param _transactionProof Inclusion proof for the provided transaction.
* @return _verified Whether or not the transaction was included.
*/
function _verifyTransaction(
Lib_OVMCodec.Transaction memory _transaction,
Lib_OVMCodec.ChainBatchHeader memory _transactionBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _transactionProof
)
internal
view
returns (
bool _verified
)
{
return ovmCanonicalTransactionChain.verifyElement(
Lib_OVMCodec.encodeTransaction(_transaction),
_transactionBatchHeader,
_transactionProof
);
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_EthUtils } from "../../libraries/utils/Lib_EthUtils.sol";
import { Lib_EthMerkleTrie } from "../../libraries/trie/Lib_EthMerkleTrie.sol";
/* Interface Imports */
import { iOVM_StateTransitioner } from "../../iOVM/verification/iOVM_StateTransitioner.sol";
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";
/**
* @title OVM_StateTransitioner
*/
contract OVM_StateTransitioner is iOVM_StateTransitioner, Lib_AddressResolver {
/*******************
* 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 internal stateTransitionIndex;
bytes32 internal transactionHash;
/***************
* Constructor *
***************/
/**
* @param _libAddressManager Address of the Address Manager.
* @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 _libAddressManager,
uint256 _stateTransitionIndex,
bytes32 _preStateRoot,
bytes32 _transactionHash
)
Lib_AddressResolver(_libAddressManager)
{
stateTransitionIndex = _stateTransitionIndex;
preStateRoot = _preStateRoot;
postStateRoot = _preStateRoot;
transactionHash = _transactionHash;
ovmExecutionManager = iOVM_ExecutionManager(resolve("OVM_ExecutionManager"));
ovmStateManager = iOVM_StateManagerFactory(resolve("OVM_StateManagerFactory")).create(address(this));
}
/**********************
* 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 _postStateRoot 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 _value Claimed account slot value.
* @param _stateTrieWitness Proof of the account state.
* @param _storageTrieWitness 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(
Lib_OVMCodec.hashTransaction(_transaction) == transactionHash,
"Invalid transaction provided."
);
// TODO: Set state manager for EM here.
ovmStateManager.setExecutionManager(resolve("OVM_ExecutionManager"));
ovmExecutionManager.run(_transaction, address(ovmStateManager));
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.commitAccount(_ovmContractAddress) == true,
"Cannot commit an account that has not been changed."
);
postStateRoot = Lib_EthMerkleTrie.updateAccountState(
_ovmContractAddress,
_account,
_stateTrieWitness,
postStateRoot
);
}
/**
* 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 _value Claimed account slot value.
* @param _stateTrieWitness Proof of the account state.
* @param _storageTrieWitness 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.commitContractStorage(_ovmContractAddress, _key) == true,
"Cannot commit a storage slot that has not been changed."
);
postStateRoot = Lib_EthMerkleTrie.updateAccountStorageSlotValue(
_ovmContractAddress,
_key,
_value,
_stateTrieWitness,
_storageTrieWitness,
postStateRoot
);
}
/**********************************
* Public Functions: Finalization *
**********************************/
/**
* Finalizes the transition process.
*/
function completeTransition()
override
public
onlyDuringPhase(TransitionPhase.POST_EXECUTION)
{
require(
ovmStateManager.getTotalUncommittedAccounts() == 0,
"All accounts must be committed before completing a transition."
);
require(
ovmStateManager.getTotalUncommittedContractStorage() == 0,
"All storage must be committed before completing a transition."
);
phase = TransitionPhase.COMPLETE;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/* Interface Imports */
import { iOVM_StateTransitioner } from "../../iOVM/verification/iOVM_StateTransitioner.sol";
import { iOVM_StateTransitionerFactory } from "../../iOVM/verification/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 *
***************************************/
/**
* Creates a new OVM_StateTransitioner
* @param _libAddressManager Address of the Address Manager.
* @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.
* @return _ovmStateTransitioner New OVM_StateTransitioner instance.
*/
function create(
address _libAddressManager,
uint256 _stateTransitionIndex,
bytes32 _preStateRoot,
bytes32 _transactionHash
)
override
public
returns (
iOVM_StateTransitioner _ovmStateTransitioner
)
{
return new OVM_StateTransitioner(
_libAddressManager,
_stateTransitionIndex,
_preStateRoot,
_transactionHash
);
}
}
// 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_ECDSAContractAccount
*/
interface iOVM_ECDSAContractAccount {
/********************
* Public Functions *
********************/
function execute(
bytes memory _transaction,
Lib_OVMCodec.EOASignatureType _signatureType,
uint8 _v,
bytes32 _r,
bytes32 _s
) external returns (bool _success, bytes memory _returndata);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/**
* @title iOVM_BaseCrossDomainMessenger
*/
interface iOVM_BaseCrossDomainMessenger {
/********************
* Public Functions *
********************/
/**
* Sets the target messenger address.
* @param _targetMessengerAddress New messenger address.
*/
function setTargetMessengerAddress(
address _targetMessengerAddress
) external;
}
// 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_BaseCrossDomainMessenger } from "./iOVM_BaseCrossDomainMessenger.sol";
/**
* @title iOVM_L1CrossDomainMessenger
*/
interface iOVM_L1CrossDomainMessenger is iOVM_BaseCrossDomainMessenger {
/*******************
* Data Structures *
*******************/
struct L2MessageInclusionProof {
bytes32 stateRoot;
Lib_OVMCodec.ChainBatchHeader stateRootBatchHeader;
Lib_OVMCodec.ChainInclusionProof stateRootProof;
bytes stateTrieWitness;
bytes storageTrieWitness;
}
/********************
* Public Functions *
********************/
/**
* Relays a cross domain message to a contract.
* @param _target Target contract address.
* @param _sender Message sender address.
* @param _message Message to send to the target.
* @param _messageNonce Nonce for the provided message.
* @param _proof Inclusion proof for the given message.
*/
function relayMessage(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce,
L2MessageInclusionProof memory _proof
) external;
/**
* Sends a cross domain message to the target messenger.
* @param _target Target contract address.
* @param _message Message to send to the target.
* @param _gasLimit Gas limit for the provided message.
*/
function sendMessage(
address _target,
bytes memory _message,
uint32 _gasLimit
) external;
/**
* Replays a cross domain message to the target messenger.
* @param _target Target contract address.
* @param _sender Original sender address.
* @param _message Message to send to the target.
* @param _messageNonce Nonce for the provided message.
* @param _gasLimit Gas limit for the provided message.
*/
function replayMessage(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce,
uint32 _gasLimit
) external;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_BaseCrossDomainMessenger } from "./iOVM_BaseCrossDomainMessenger.sol";
/**
* @title iOVM_L2CrossDomainMessenger
*/
interface iOVM_L2CrossDomainMessenger is iOVM_BaseCrossDomainMessenger {
/********************
* Public Functions *
********************/
/**
* Relays a cross domain message to a contract.
* @param _target Target contract address.
* @param _sender Message sender address.
* @param _message Message to send to the target.
* @param _messageNonce Nonce for the provided message.
*/
function relayMessage(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce
) external;
/**
* Sends a cross domain message to the target messenger.
* @param _target Target contract address.
* @param _message Message to send to the target.
* @param _gasLimit Gas limit for the provided message.
*/
function sendMessage(
address _target,
bytes memory _message,
uint256 _gasLimit
) external;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/**
* @title iOVM_BaseChain
*/
interface iOVM_BaseChain {
/*************************************
* Public Functions: Batch Retrieval *
*************************************/
function getTotalElements() external view returns (uint256 _totalElements);
function getTotalBatches() external view returns (uint256 _totalBatches);
/****************************************
* Public Functions: Batch Verification *
****************************************/
function verifyElement(
bytes calldata _element,
Lib_OVMCodec.ChainBatchHeader calldata _batchHeader,
Lib_OVMCodec.ChainInclusionProof calldata _proof
) external view returns (bool _verified);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_BaseChain } from "./iOVM_BaseChain.sol";
/**
* @title iOVM_CanonicalTransactionChain
*/
interface iOVM_CanonicalTransactionChain is iOVM_BaseChain {
/****************************************
* Public Functions: Batch Manipulation *
****************************************/
function appendQueueBatch() external;
function appendSequencerBatch(bytes[] calldata _batch, uint256 _timestamp) external;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_BaseChain } from "./iOVM_BaseChain.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/**
* @title iOVM_StateCommitmentChain
*/
interface iOVM_StateCommitmentChain is iOVM_BaseChain {
/****************************************
* Public Functions: Batch Manipulation *
****************************************/
function appendStateBatch(bytes32[] calldata _batch) external;
function deleteStateBatch(Lib_OVMCodec.ChainBatchHeader memory _batchHeader) external;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
interface iOVM_ExecutionManager {
/**********
* Enums *
*********/
enum RevertFlag {
DID_NOT_REVERT,
OUT_OF_GAS,
INTENTIONAL_REVERT,
EXCEEDS_NUISANCE_GAS,
INVALID_STATE_ACCESS,
UNSAFE_BYTECODE,
CREATE_COLLISION,
STATIC_VIOLATION,
CREATE_EXCEPTION
}
enum GasMetadataKey {
CURRENT_EPOCH_START_TIMESTAMP,
CUMULATIVE_SEQUENCER_QUEUE_GAS,
CUMULATIVE_L1TOL2_QUEUE_GAS,
PREV_EPOCH_SEQUENCER_QUEUE_GAS,
PREV_EPOCH_L1TOL2_QUEUE_GAS
}
/***********
* Structs *
***********/
struct GasMeterConfig {
uint256 minTransactionGasLimit;
uint256 maxTransactionGasLimit;
uint256 maxGasPerQueuePerEpoch;
uint256 secondsPerEpoch;
}
struct GlobalContext {
uint256 ovmCHAINID;
}
struct TransactionContext {
Lib_OVMCodec.QueueOrigin ovmL1QUEUEORIGIN;
uint256 ovmTIMESTAMP;
uint256 ovmNUMBER;
uint256 ovmGASLIMIT;
uint256 ovmTXGASLIMIT;
address ovmL1TXORIGIN;
}
struct TransactionRecord {
uint256 ovmGasRefund;
}
struct MessageContext {
address ovmCALLER;
address ovmADDRESS;
bool isStatic;
bool isCreation;
}
struct MessageRecord {
uint256 nuisanceGasLeft;
RevertFlag revertFlag;
}
/************************************
* Transaction Execution Entrypoint *
************************************/
function run(
Lib_OVMCodec.Transaction calldata _transaction,
address _txStateManager
) external;
/*******************
* Context Opcodes *
*******************/
function ovmCALLER() external view returns (address _caller);
function ovmADDRESS() external view returns (address _address);
function ovmTIMESTAMP() external view returns (uint256 _timestamp);
function ovmNUMBER() external view returns (uint256 _number);
function ovmGASLIMIT() external view returns (uint256 _gasLimit);
function ovmCHAINID() external view returns (uint256 _chainId);
/**********************
* L2 Context Opcodes *
**********************/
function ovmL1QUEUEORIGIN() external view returns (Lib_OVMCodec.QueueOrigin _queueOrigin);
function ovmL1TXORIGIN() external view returns (address _l1TxOrigin);
/*******************
* Halting Opcodes *
*******************/
function ovmREVERT(bytes memory _data) external;
/*****************************
* Contract Creation Opcodes *
*****************************/
function ovmCREATE(bytes memory _bytecode) external returns (address _contract);
function ovmCREATE2(bytes memory _bytecode, bytes32 _salt) external returns (address _contract);
/*******************************
* Account Abstraction Opcodes *
******************************/
function ovmGETNONCE() external returns (uint256 _nonce);
function ovmSETNONCE(uint256 _nonce) external;
function ovmCREATEEOA(bytes32 _messageHash, uint8 _v, bytes32 _r, bytes32 _s) external;
/****************************
* Contract Calling Opcodes *
****************************/
function ovmCALL(uint256 _gasLimit, address _address, bytes memory _calldata) external returns (bool _success, bytes memory _returndata);
function ovmSTATICCALL(uint256 _gasLimit, address _address, bytes memory _calldata) external returns (bool _success, bytes memory _returndata);
function ovmDELEGATECALL(uint256 _gasLimit, address _address, bytes memory _calldata) external returns (bool _success, bytes memory _returndata);
/****************************
* Contract Storage Opcodes *
****************************/
function ovmSLOAD(bytes32 _key) external returns (bytes32 _value);
function ovmSSTORE(bytes32 _key, bytes32 _value) external;
/*************************
* Contract Code Opcodes *
*************************/
function ovmEXTCODECOPY(address _contract, uint256 _offset, uint256 _length) external returns (bytes memory _code);
function ovmEXTCODESIZE(address _contract) external returns (uint256 _size);
function ovmEXTCODEHASH(address _contract) external returns (bytes32 _hash);
/**************************************
* Public Functions: Execution Safety *
**************************************/
function safeCREATE(address _address, bytes memory _bytecode) external;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/**
* @title iOVM_SafetyChecker
*/
interface iOVM_SafetyChecker {
/********************
* Public Functions *
********************/
function isBytecodeSafe(bytes memory _bytecode) external view returns (bool _safe);
}
// 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_StateManager
*/
interface iOVM_StateManager {
/*******************
* Data Structures *
*******************/
enum ItemState {
ITEM_UNTOUCHED,
ITEM_LOADED,
ITEM_CHANGED,
ITEM_COMMITTED
}
/***************************
* Public Functions: Setup *
***************************/
function setExecutionManager(address _ovmExecutionManager) external;
/************************************
* Public Functions: Account Access *
************************************/
function putAccount(address _address, Lib_OVMCodec.Account memory _account) external;
function getAccount(address _address) external view returns (Lib_OVMCodec.Account memory _account);
function hasAccount(address _address) external view returns (bool _exists);
function hasEmptyAccount(address _address) external view returns (bool _exists);
function setAccountNonce(address _address, uint256 _nonce) external;
function getAccountNonce(address _address) external view returns (uint256 _nonce);
function getAccountEthAddress(address _address) external view returns (address _ethAddress);
function initPendingAccount(address _address) external;
function commitPendingAccount(address _address, address _ethAddress, bytes32 _codeHash) external;
function testAndSetAccountLoaded(address _address) external returns (bool _wasAccountAlreadyLoaded);
function testAndSetAccountChanged(address _address) external returns (bool _wasAccountAlreadyChanged);
function commitAccount(address _address) external returns (bool _wasAccountCommitted);
function incrementTotalUncommittedAccounts() external;
function getTotalUncommittedAccounts() external view returns (uint256 _total);
/************************************
* Public Functions: Storage Access *
************************************/
function putContractStorage(address _contract, bytes32 _key, bytes32 _value) external;
function getContractStorage(address _contract, bytes32 _key) external view returns (bytes32 _value);
function hasContractStorage(address _contract, bytes32 _key) external returns (bool _exists);
function testAndSetContractStorageLoaded(address _contract, bytes32 _key) external returns (bool _wasContractStorageAlreadyLoaded);
function testAndSetContractStorageChanged(address _contract, bytes32 _key) external returns (bool _wasContractStorageAlreadyChanged);
function commitContractStorage(address _contract, bytes32 _key) external returns (bool _wasContractStorageCommitted);
function incrementTotalUncommittedContractStorage() external;
function getTotalUncommittedContractStorage() external view returns (uint256 _total);
}
// 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(
address _owner
)
external
returns (
iOVM_StateManager _ovmStateManager
);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/**
* @title iOVM_DeployerWhitelist
*/
interface iOVM_DeployerWhitelist {
/********************
* Public Functions *
********************/
function initialize(address _owner, bool _allowArbitraryDeployment) external;
function setWhitelistedDeployer(address _deployer, bool _isWhitelisted) external;
function setOwner(address _newOwner) external;
function setAllowArbitraryDeployment(bool _allowArbitraryDeployment) external;
function enableArbitraryContractDeployment() external;
function isDeployerAllowed(address _deployer) external returns (bool _allowed);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/**
* @title iOVM_L1MessageSender
*/
interface iOVM_L1MessageSender {
/********************
* Public Functions *
********************/
function getL1MessageSender() external returns (address _l1MessageSender);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/**
* @title iOVM_L2ToL1MessagePasser
*/
interface iOVM_L2ToL1MessagePasser {
/**********
* Events *
**********/
event L2ToL1Message(
uint _nonce,
address _sender,
bytes _data
);
/********************
* Public Functions *
********************/
function passMessageToL1(bytes memory _message) external;
}
// 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);
}
// 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(address _target, uint256 _gasLimit, bytes memory _data) external;
function dequeue() external;
}
// 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_StateTransitioner } from "./iOVM_StateTransitioner.sol";
/**
* @title iOVM_FraudVerifier
*/
interface iOVM_FraudVerifier {
/***************************************
* Public Functions: Transition Status *
***************************************/
function getStateTransitioner(bytes32 _preStateRoot) external view returns (iOVM_StateTransitioner _transitioner);
/****************************************
* 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;
}
// 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.Transaction 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;
}
// 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 _proxyManager,
uint256 _stateTransitionIndex,
bytes32 _preStateRoot,
bytes32 _transactionHash
)
external
returns (
iOVM_StateTransitioner _ovmStateTransitioner
);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_RLPReader } from "../rlp/Lib_RLPReader.sol";
/**
* @title Lib_OVMCodec
*/
library Lib_OVMCodec {
/*********
* Enums *
*********/
enum EOASignatureType {
ETH_SIGNED_MESSAGE,
NATIVE_TRANSACTON
}
enum QueueOrigin {
SEQUENCER_QUEUE,
L1TOL2_QUEUE
}
/***********
* Structs *
***********/
struct Account {
uint256 nonce;
uint256 balance;
bytes32 storageRoot;
bytes32 codeHash;
address ethAddress;
bool isFresh;
}
struct EVMAccount {
uint256 nonce;
uint256 balance;
bytes32 storageRoot;
bytes32 codeHash;
}
struct ChainBatchHeader {
uint256 batchIndex;
bytes32 batchRoot;
uint256 batchSize;
uint256 prevTotalElements;
bytes extraData;
}
struct ChainInclusionProof {
uint256 index;
bytes32[] siblings;
}
struct Transaction {
uint256 timestamp;
uint256 number;
QueueOrigin l1QueueOrigin;
address l1Txorigin;
address entrypoint;
uint256 gasLimit;
bytes data;
}
struct ProofMatrix {
bool checkNonce;
bool checkBalance;
bool checkStorageRoot;
bool checkCodeHash;
}
struct QueueElement {
uint256 timestamp;
bytes32 batchRoot;
bool isL1ToL2Batch;
}
struct EOATransaction {
address target;
uint256 nonce;
uint256 gasLimit;
bytes data;
}
/*********************************************
* Internal Functions: Encoding and Decoding *
*********************************************/
/**
* Decodes an EOA transaction (i.e., native Ethereum RLP encoding).
* @param _transaction Encoded EOA transaction.
* @return _decoded Transaction decoded into a struct.
*/
function decodeEOATransaction(
bytes memory _transaction
)
internal
pure
returns (
EOATransaction memory _decoded
)
{
Lib_RLPReader.RLPItem[] memory decoded = Lib_RLPReader.toList(Lib_RLPReader.toRlpItem(_transaction));
return EOATransaction({
nonce: Lib_RLPReader.toUint(decoded[0]),
gasLimit: Lib_RLPReader.toUint(decoded[2]),
target: Lib_RLPReader.toAddress(decoded[3]),
data: Lib_RLPReader.toBytes(decoded[5])
});
}
/**
* Encodes a standard OVM transaction.
* @param _transaction OVM transaction to encode.
* @return _encoded Encoded transaction bytes.
*/
function encodeTransaction(
Transaction memory _transaction
)
internal
pure
returns (
bytes memory _encoded
)
{
return abi.encodePacked(
_transaction.timestamp,
_transaction.number,
_transaction.l1QueueOrigin,
_transaction.l1Txorigin,
_transaction.entrypoint,
_transaction.gasLimit,
_transaction.data
);
}
/**
* Hashes a standard OVM transaction.
* @param _transaction OVM transaction to encode.
* @return _hash Hashed transaction
*/
function hashTransaction(
Transaction memory _transaction
)
internal
pure
returns (
bytes32 _hash
)
{
return keccak256(encodeTransaction(_transaction));
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/**
* @title Lib_AddressManager
*/
contract Lib_AddressManager {
/*******************************************
* Contract Variables: Internal Accounting *
*******************************************/
mapping (bytes32 => address) private addresses;
/********************
* Public Functions *
********************/
function setAddress(
string memory _name,
address _address
)
public
{
addresses[_getNameHash(_name)] = _address;
}
function getAddress(
string memory _name
)
public
view
returns (address)
{
return addresses[_getNameHash(_name)];
}
/**********************
* Internal Functions *
**********************/
function _getNameHash(
string memory _name
)
internal
pure
returns (
bytes32 _hash
)
{
return keccak256(abi.encodePacked(_name));
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/* Library Imports */
import { Lib_AddressManager } from "./Lib_AddressManager.sol";
/**
* @title Lib_AddressResolver
*/
contract Lib_AddressResolver {
/*******************************************
* Contract Variables: Contract References *
*******************************************/
Lib_AddressManager internal libAddressManager;
/***************
* Constructor *
***************/
/**
* @param _libAddressManager Address of the Lib_AddressManager.
*/
constructor(
address _libAddressManager
)
public
{
libAddressManager = Lib_AddressManager(_libAddressManager);
}
/********************
* Public Functions *
********************/
function resolve(
string memory _name
)
public
view
returns (
address _contract
)
{
return libAddressManager.getAddress(_name);
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/**
* @title RLPReader
* @author Hamdi Allam hamdi.allam97@gmail.com
*/
library Lib_RLPReader {
/*
* Data Structures
*/
struct RLPItem {
uint len;
uint memPtr;
}
/*
* Contract Constants
*/
uint8 constant private STRING_SHORT_START = 0x80;
uint8 constant private STRING_LONG_START = 0xb8;
uint8 constant private LIST_SHORT_START = 0xc0;
uint8 constant private LIST_LONG_START = 0xf8;
uint8 constant private WORD_SIZE = 32;
/*
* Public Functions
*/
/**
* @param item RLP encoded bytes
*/
function toRlpItem(
bytes memory item
)
internal
pure
returns (RLPItem memory)
{
uint memPtr;
assembly {
memPtr := add(item, 0x20)
}
return RLPItem(item.length, memPtr);
}
/**
* @param item RLP encoded bytes
*/
function rlpLen(
RLPItem memory item
)
internal
pure
returns (uint)
{
return item.len;
}
/**
* @param item RLP encoded bytes
*/
function payloadLen(
RLPItem memory item
)
internal
pure
returns (uint)
{
return item.len - _payloadOffset(item.memPtr);
}
/**
* @param item RLP encoded list in bytes
*/
function toList(
RLPItem memory item
)
internal
pure
returns (RLPItem[] memory result)
{
require(isList(item));
uint items = numItems(item);
result = new RLPItem[](items);
uint memPtr = item.memPtr + _payloadOffset(item.memPtr);
uint dataLen;
for (uint i = 0; i < items; i++) {
dataLen = _itemLength(memPtr);
result[i] = RLPItem(dataLen, memPtr);
memPtr = memPtr + dataLen;
}
}
// @return indicator whether encoded payload is a list. negate this function call for isData.
function isList(
RLPItem memory item
)
internal
pure
returns (bool)
{
if (item.len == 0) return false;
uint8 byte0;
uint memPtr = item.memPtr;
assembly {
byte0 := byte(0, mload(memPtr))
}
if (byte0 < LIST_SHORT_START)
return false;
return true;
}
/** RLPItem conversions into data types **/
// @returns raw rlp encoding in bytes
function toRlpBytes(
RLPItem memory item
)
internal
pure
returns (bytes memory)
{
bytes memory result = new bytes(item.len);
if (result.length == 0) return result;
uint ptr;
assembly {
ptr := add(0x20, result)
}
copy(item.memPtr, ptr, item.len);
return result;
}
// any non-zero byte is considered true
function toBoolean(
RLPItem memory item
)
internal
pure
returns (bool)
{
require(item.len == 1);
uint result;
uint memPtr = item.memPtr;
assembly {
result := byte(0, mload(memPtr))
}
return result == 0 ? false : true;
}
function toAddress(
RLPItem memory item
)
internal
pure
returns (address)
{
// 1 byte for the length prefix
require(item.len == 21);
return address(toUint(item));
}
function toUint(
RLPItem memory item
)
internal
pure
returns (uint)
{
require(item.len > 0 && item.len <= 33);
uint offset = _payloadOffset(item.memPtr);
uint len = item.len - offset;
uint result;
uint memPtr = item.memPtr + offset;
assembly {
result := mload(memPtr)
// shfit to the correct location if neccesary
if lt(len, 32) {
result := div(result, exp(256, sub(32, len)))
}
}
return result;
}
// enforces 32 byte length
function toUintStrict(
RLPItem memory item
)
internal
pure
returns (uint)
{
// one byte prefix
require(item.len == 33);
uint result;
uint memPtr = item.memPtr + 1;
assembly {
result := mload(memPtr)
}
return result;
}
function toBytes(
RLPItem memory item
)
internal
pure
returns (bytes memory)
{
require(item.len > 0);
uint offset = _payloadOffset(item.memPtr);
uint len = item.len - offset; // data length
bytes memory result = new bytes(len);
uint destPtr;
assembly {
destPtr := add(0x20, result)
}
copy(item.memPtr + offset, destPtr, len);
return result;
}
/*
* Private Functions
*/
// @return number of payload items inside an encoded list.
function numItems(
RLPItem memory item
)
private
pure
returns (uint)
{
if (item.len == 0) return 0;
uint count = 0;
uint currPtr = item.memPtr + _payloadOffset(item.memPtr);
uint endPtr = item.memPtr + item.len;
while (currPtr < endPtr) {
currPtr = currPtr + _itemLength(currPtr); // skip over an item
count++;
}
return count;
}
// @return entire rlp item byte length
function _itemLength(
uint memPtr
)
private
pure
returns (uint len)
{
uint byte0;
assembly {
byte0 := byte(0, mload(memPtr))
}
if (byte0 < STRING_SHORT_START)
return 1;
else if (byte0 < STRING_LONG_START)
return byte0 - STRING_SHORT_START + 1;
else if (byte0 < LIST_SHORT_START) {
assembly {
let byteLen := sub(byte0, 0xb7) // number of bytes the actual length is
memPtr := add(memPtr, 1) // skip over the first byte
/* 32 byte word size */
let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len
len := add(dataLen, add(byteLen, 1))
}
}
else if (byte0 < LIST_LONG_START) {
return byte0 - LIST_SHORT_START + 1;
}
else {
assembly {
let byteLen := sub(byte0, 0xf7)
memPtr := add(memPtr, 1)
let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length
len := add(dataLen, add(byteLen, 1))
}
}
}
// @return number of bytes until the data
function _payloadOffset(
uint memPtr
)
private
pure
returns (uint)
{
uint byte0;
assembly {
byte0 := byte(0, mload(memPtr))
}
if (byte0 < STRING_SHORT_START)
return 0;
else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START))
return 1;
else if (byte0 < LIST_SHORT_START) // being explicit
return byte0 - (STRING_LONG_START - 1) + 1;
else
return byte0 - (LIST_LONG_START - 1) + 1;
}
/*
* @param src Pointer to source
* @param dest Pointer to destination
* @param len Amount of memory to copy from the source
*/
function copy(
uint src,
uint dest,
uint len
)
private
pure
{
if (len == 0) return;
// copy as many word sizes as possible
for (; len >= WORD_SIZE; len -= WORD_SIZE) {
assembly {
mstore(dest, mload(src))
}
src += WORD_SIZE;
dest += WORD_SIZE;
}
// left over bytes. Mask is used to remove unwanted bytes from the word
uint mask = 256 ** (WORD_SIZE - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask)) // zero out src
let destpart := and(mload(dest), mask) // retrieve the bytes
mstore(dest, or(destpart, srcpart))
}
}
}
\ No newline at end of file
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/**
* Source: https://github.com/omisego/plasma-mvp/blob/master/plasma/root_chain/contracts/RLPEncode.sol
* @title RLPEncode
* @dev A simple RLP encoding library.
* @author Bakaoh
*/
library Lib_RLPWriter {
/*
* Public Functions
*/
/**
* RLP encodes a byte string.
* @param self The byte string to encode.
* @return The RLP encoded string in bytes.
*/
function encodeBytes(
bytes memory self
)
internal
pure
returns (bytes memory)
{
bytes memory encoded;
if (self.length == 1 && uint8(self[0]) < 128) {
encoded = self;
} else {
encoded = concat(encodeLength(self.length, 128), self);
}
return encoded;
}
/**
* RLP encodes a list of RLP encoded byte byte strings.
* @param self The list of RLP encoded byte strings.
* @return The RLP encoded list of items in bytes.
*/
function encodeList(
bytes[] memory self
)
internal
pure
returns (bytes memory)
{
bytes memory list = flatten(self);
return concat(encodeLength(list.length, 192), list);
}
/**
* RLP encodes a string.
* @param self The string to encode.
* @return The RLP encoded string in bytes.
*/
function encodeString(
string memory self
)
internal
pure
returns (bytes memory)
{
return encodeBytes(bytes(self));
}
/**
* RLP encodes an address.
* @param self The address to encode.
* @return The RLP encoded address in bytes.
*/
function encodeAddress(
address self
)
internal
pure
returns (bytes memory)
{
bytes memory inputBytes;
assembly {
let m := mload(0x40)
mstore(add(m, 20), xor(0x140000000000000000000000000000000000000000, self))
mstore(0x40, add(m, 52))
inputBytes := m
}
return encodeBytes(inputBytes);
}
/**
* RLP encodes a uint.
* @param self The uint to encode.
* @return The RLP encoded uint in bytes.
*/
function encodeUint(
uint self
)
internal
pure
returns (bytes memory)
{
return encodeBytes(toBinary(self));
}
/**
* RLP encodes an int.
* @param self The int to encode.
* @return The RLP encoded int in bytes.
*/
function encodeInt(
int self
)
internal
pure
returns (bytes memory)
{
return encodeUint(uint(self));
}
/**
* RLP encodes a bool.
* @param self The bool to encode.
* @return The RLP encoded bool in bytes.
*/
function encodeBool(
bool self
)
internal
pure
returns (bytes memory)
{
bytes memory encoded = new bytes(1);
encoded[0] = (self ? bytes1(0x01) : bytes1(0x80));
return encoded;
}
/*
* Private Functions
*/
/**
* Encode the first byte, followed by the `len` in binary form if `length` is more than 55.
* @param len The length of the string or the payload.
* @param offset 128 if item is string, 192 if item is list.
* @return RLP encoded bytes.
*/
function encodeLength(
uint len,
uint offset
)
private
pure
returns (bytes memory)
{
bytes memory encoded;
if (len < 56) {
encoded = new bytes(1);
encoded[0] = byte(uint8(len) + uint8(offset));
} else {
uint lenLen;
uint i = 1;
while (len / i != 0) {
lenLen++;
i *= 256;
}
encoded = new bytes(lenLen + 1);
encoded[0] = byte(uint8(lenLen) + uint8(offset) + 55);
for(i = 1; i <= lenLen; i++) {
encoded[i] = byte(uint8((len / (256**(lenLen-i))) % 256));
}
}
return encoded;
}
/**
* Encode integer in big endian binary form with no leading zeroes.
* @notice TODO: This should be optimized with assembly to save gas costs.
* @param _x The integer to encode.
* @return RLP encoded bytes.
*/
function toBinary(
uint _x
)
private
pure
returns (bytes memory)
{
bytes memory b = new bytes(32);
assembly {
mstore(add(b, 32), _x)
}
uint i = 0;
for (; i < 32; i++) {
if (b[i] != 0) {
break;
}
}
bytes memory res = new bytes(32 - i);
for (uint j = 0; j < res.length; j++) {
res[j] = b[i++];
}
return res;
}
/**
* Copies a piece of memory to another location.
* @notice From: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol.
* @param _dest Destination location.
* @param _src Source location.
* @param _len Length of memory to copy.
*/
function memcpy(
uint _dest,
uint _src,
uint _len
)
private
pure
{
uint dest = _dest;
uint src = _src;
uint len = _len;
for(; len >= 32; len -= 32) {
assembly {
mstore(dest, mload(src))
}
dest += 32;
src += 32;
}
uint mask = 256 ** (32 - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
}
/**
* Flattens a list of byte strings into one byte string.
* @notice From: https://github.com/sammayo/solidity-rlp-encoder/blob/master/RLPEncode.sol.
* @param _list List of byte strings to flatten.
* @return The flattened byte string.
*/
function flatten(
bytes[] memory _list
)
private
pure
returns (bytes memory)
{
if (_list.length == 0) {
return new bytes(0);
}
uint len;
uint i = 0;
for (; i < _list.length; i++) {
len += _list[i].length;
}
bytes memory flattened = new bytes(len);
uint flattenedPtr;
assembly { flattenedPtr := add(flattened, 0x20) }
for(i = 0; i < _list.length; i++) {
bytes memory item = _list[i];
uint listPtr;
assembly { listPtr := add(item, 0x20)}
memcpy(flattenedPtr, listPtr, item.length);
flattenedPtr += _list[i].length;
}
return flattened;
}
/**
* Concatenates two bytes.
* @notice From: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol.
* @param _preBytes First byte string.
* @param _postBytes Second byte string.
* @return Both byte string combined.
*/
function concat(
bytes memory _preBytes,
bytes memory _postBytes
)
private
pure
returns (bytes memory)
{
bytes memory tempBytes;
assembly {
tempBytes := mload(0x40)
let length := mload(_preBytes)
mstore(tempBytes, length)
let mc := add(tempBytes, 0x20)
let end := add(mc, length)
for {
let cc := add(_preBytes, 0x20)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
length := mload(_postBytes)
mstore(tempBytes, add(length, mload(tempBytes)))
mc := end
end := add(mc, length)
for {
let cc := add(_postBytes, 0x20)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(0x40, and(
add(add(end, iszero(add(length, mload(_preBytes)))), 31),
not(31)
))
}
return tempBytes;
}
}
\ No newline at end of file
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_SecureMerkleTrie } from "./Lib_SecureMerkleTrie.sol";
import { Lib_OVMCodec } from "../codec/Lib_OVMCodec.sol";
import { Lib_ByteUtils } from "../utils/Lib_ByteUtils.sol";
import { Lib_RLPWriter } from "../rlp/Lib_RLPWriter.sol";
import { Lib_RLPReader } from "../rlp/Lib_RLPReader.sol";
/**
* @title Lib_EthMerkleTrie
*/
library Lib_EthMerkleTrie {
/**********************
* Contract Constants *
**********************/
bytes constant private RLP_NULL_BYTES = hex'80';
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 Lib_SecureMerkleTrie.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 = Lib_SecureMerkleTrie.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 = Lib_RLPReader.toList(Lib_RLPReader.toRlpItem(_encodedAccountState));
return Lib_OVMCodec.EVMAccount({
nonce: Lib_RLPReader.toUint(accountState[0]),
balance: Lib_RLPReader.toUint(accountState[1]),
storageRoot: Lib_ByteUtils.toBytes32(Lib_RLPReader.toBytes(accountState[2])),
codeHash: Lib_ByteUtils.toBytes32(Lib_RLPReader.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] = Lib_RLPWriter.encodeUint(_accountState.nonce);
raw[1] = Lib_RLPWriter.encodeUint(_accountState.balance);
raw[2] = _accountState.storageRoot == 0 ? RLP_NULL_BYTES : Lib_RLPWriter.encodeBytes(abi.encodePacked(_accountState.storageRoot));
raw[3] = _accountState.codeHash == 0 ? RLP_NULL_BYTES : Lib_RLPWriter.encodeBytes(abi.encodePacked(_accountState.codeHash));
return Lib_RLPWriter.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
) = Lib_SecureMerkleTrie.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 Lib_SecureMerkleTrie.update(
abi.encodePacked(_address),
encodedAccountState,
_stateTrieWitness,
_stateTrieRoot
);
}
}
\ No newline at end of file
// 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 &&
Lib_ByteUtils.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 && !Lib_ByteUtils.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 = Lib_ByteUtils.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(
Lib_ByteUtils.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 = Lib_ByteUtils.slice(path, offset);
bytes memory keyRemainder = Lib_ByteUtils.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, Lib_ByteUtils.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(Lib_ByteUtils.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 = Lib_ByteUtils.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 = Lib_ByteUtils.slice(lastNodeKey, sharedNibbleLength);
keyRemainder = Lib_ByteUtils.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 = Lib_ByteUtils.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 = Lib_ByteUtils.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 = Lib_ByteUtils.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 = Lib_ByteUtils.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 = Lib_ByteUtils.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 = Lib_ByteUtils.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 = Lib_RLPReader.toList(Lib_RLPReader.toRlpItem(_proof));
TrieNode[] memory proof = new TrieNode[](nodes.length);
for (uint256 i = 0; i < nodes.length; i++) {
bytes memory encoded = Lib_RLPReader.toBytes(nodes[i]);
proof[i] = TrieNode({
encoded: encoded,
decoded: Lib_RLPReader.toList(Lib_RLPReader.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 = Lib_RLPReader.toRlpBytes(_node);
} else {
// Nodes 32 bytes or larger are hashed.
nodeID = Lib_RLPReader.toBytes(_node);
}
return Lib_ByteUtils.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 Lib_ByteUtils.toNibbles(Lib_RLPReader.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 Lib_RLPReader.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
pure
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 = Lib_RLPWriter.encodeList(_raw);
return TrieNode({
encoded: encoded,
decoded: Lib_RLPReader.toList(Lib_RLPReader.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] = Lib_RLPReader.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] = Lib_RLPWriter.encodeBytes(Lib_ByteUtils.fromNibbles(key));
raw[1] = Lib_RLPWriter.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] = Lib_RLPWriter.encodeBytes(Lib_ByteUtils.fromNibbles(key));
raw[1] = Lib_RLPWriter.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 = Lib_RLPWriter.encodeBytes(_value);
_branch.decoded[_branch.decoded.length - 1] = Lib_RLPReader.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 : Lib_RLPWriter.encodeBytes(_value);
_branch.decoded[_index] = Lib_RLPReader.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 Lib_ByteUtils.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 Lib_ByteUtils.slice(_path, 2);
} else {
return Lib_ByteUtils.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
pure
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;
}
}
// 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
)
internal
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
)
internal
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
)
internal
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
)
internal
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
)
internal
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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/**
* @title Lib_ByteUtils
*/
library Lib_ByteUtils {
/**********************
* Internal Functions *
**********************/
function concat(
bytes memory _preBytes,
bytes memory _postBytes
)
internal
pure
returns (bytes memory)
{
bytes memory tempBytes;
assembly {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// Store the length of the first bytes array at the beginning of
// the memory for tempBytes.
let length := mload(_preBytes)
mstore(tempBytes, length)
// Maintain a memory counter for the current write location in the
// temp bytes array by adding the 32 bytes for the array length to
// the starting location.
let mc := add(tempBytes, 0x20)
// Stop copying when the memory counter reaches the length of the
// first bytes array.
let end := add(mc, length)
for {
// Initialize a copy counter to the start of the _preBytes data,
// 32 bytes into its memory.
let cc := add(_preBytes, 0x20)
} lt(mc, end) {
// Increase both counters by 32 bytes each iteration.
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
// Write the _preBytes data into the tempBytes memory 32 bytes
// at a time.
mstore(mc, mload(cc))
}
// Add the length of _postBytes to the current length of tempBytes
// and store it as the new length in the first 32 bytes of the
// tempBytes memory.
length := mload(_postBytes)
mstore(tempBytes, add(length, mload(tempBytes)))
// Move the memory counter back from a multiple of 0x20 to the
// actual end of the _preBytes data.
mc := end
// Stop copying when the memory counter reaches the new combined
// length of the arrays.
end := add(mc, length)
for {
let cc := add(_postBytes, 0x20)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
// Update the free-memory pointer by padding our last write location
// to 32 bytes: add 31 bytes to the end of tempBytes to move to the
// next 32 byte block, then round down to the nearest multiple of
// 32. If the sum of the length of the two arrays is zero then add
// one before rounding down to leave a blank 32 bytes (the length block with 0).
mstore(0x40, and(
add(add(end, iszero(add(length, mload(_preBytes)))), 31),
not(31) // Round down to the nearest 32 bytes.
))
}
return tempBytes;
}
function slice(
bytes memory _bytes,
uint256 _start,
uint256 _length
)
internal
pure
returns (bytes memory)
{
require(_bytes.length >= (_start + _length), "Read out of bounds");
bytes memory tempBytes;
assembly {
switch iszero(_length)
case 0 {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// The first word of the slice result is potentially a partial
// word read from the original array. To read it, we calculate
// the length of that partial word and start copying that many
// bytes into the array. The first word we copy will start with
// data we don't care about, but the last `lengthmod` bytes will
// land at the beginning of the contents of the new array. When
// we're done copying, we overwrite the full first word with
// the actual length of the slice.
let lengthmod := and(_length, 31)
// The multiplication in the next line is necessary
// because when slicing multiples of 32 bytes (lengthmod == 0)
// the following copy loop was copying the origin's length
// and then ending prematurely not copying everything it should.
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, _length)
for {
// The multiplication in the next line has the same exact purpose
// as the one above.
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(tempBytes, _length)
//update free-memory pointer
//allocating the array padded to 32 bytes like the compiler does now
mstore(0x40, and(add(mc, 31), not(31)))
}
//if we want a zero-length slice let's just return a zero-length array
default {
tempBytes := mload(0x40)
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
function slice(
bytes memory _bytes,
uint256 _start
)
internal
pure
returns (bytes memory)
{
if (_bytes.length - _start == 0) {
return bytes('');
}
return slice(_bytes, _start, _bytes.length - _start);
}
function toBytes32(
bytes memory _bytes
)
internal
pure
returns (bytes32)
{
bytes32 ret;
assembly {
ret := mload(add(_bytes, 32))
}
return ret;
}
function toUint256(
bytes memory _bytes
)
internal
pure
returns (uint256)
{
return uint256(toBytes32(_bytes));
}
function toNibbles(
bytes memory _bytes
)
internal
pure
returns (bytes memory)
{
bytes memory nibbles = new bytes(_bytes.length * 2);
for (uint256 i = 0; i < _bytes.length; i++) {
nibbles[i * 2] = _bytes[i] >> 4;
nibbles[i * 2 + 1] = bytes1(uint8(_bytes[i]) % 16);
}
return nibbles;
}
function fromNibbles(
bytes memory _bytes
)
internal
pure
returns (bytes memory)
{
bytes memory ret = new bytes(_bytes.length / 2);
for (uint256 i = 0; i < ret.length; i++) {
ret[i] = (_bytes[i * 2] << 4) | (_bytes[i * 2 + 1]);
}
return ret;
}
function equal(
bytes memory _bytes,
bytes memory _other
)
internal
pure
returns (bool)
{
return keccak256(_bytes) == keccak256(_other);
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/**
* @title Lib_Byte32Utils
*/
library Lib_Bytes32Utils {
/**********************
* Internal Functions *
**********************/
function toBool(
bytes32 _in
)
internal
pure
returns (
bool _out
)
{
return _in != 0;
}
function fromBool(
bool _in
)
internal
pure
returns (
bytes32 _out
)
{
return bytes32(uint256(_in ? 1 : 0));
}
function toAddress(
bytes32 _in
)
internal
pure
returns (
address _out
)
{
return address(uint160(uint256(_in)));
}
function fromAddress(
address _in
)
internal
pure
returns (
bytes32 _out
)
{
return bytes32(bytes20(_in));
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/**
* @title Lib_ECDSAUtils
*/
library Lib_ECDSAUtils {
/**************************************
* Internal Functions: ECDSA Recovery *
**************************************/
/**
* Recovers a signed address given a message and signature.
* @param _message Message that was originally signed.
* @param _isEthSignedMessage Whether or not the user used the `Ethereum Signed Message` prefix.
* @param _v Signature `v` parameter.
* @param _r Signature `r` parameter.
* @param _s Signature `s` parameter.
* @param _chainId Chain ID parameter.
* @return _sender Signer address.
*/
function recover(
bytes memory _message,
bool _isEthSignedMessage,
uint8 _v,
bytes32 _r,
bytes32 _s,
uint256 _chainId
)
internal
pure
returns (
address _sender
)
{
bytes32 messageHash;
if (_isEthSignedMessage) {
messageHash = getEthSignedMessageHash(_message);
} else {
messageHash = getNativeMessageHash(_message);
}
return ecrecover(
messageHash,
(_v - uint8(_chainId) * 2) - 8,
_r,
_s
);
}
/*************************************
* Private Functions: ECDSA Recovery *
*************************************/
/**
* Gets the native message hash (simple keccak256) for a message.
* @param _message Message to hash.
* @return _messageHash Native message hash.
*/
function getNativeMessageHash(
bytes memory _message
)
private
pure
returns (
bytes32 _messageHash
)
{
return keccak256(_message);
}
/**
* Gets the hash of a message with the `Ethereum Signed Message` prefix.
* @param _message Message to hash.
* @return _messageHash Prefixed message hash.
*/
function getEthSignedMessageHash(
bytes memory _message
)
private
pure
returns (
bytes32 _messageHash
)
{
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 messageHash = keccak256(_message);
return keccak256(abi.encodePacked(prefix, messageHash));
}
}
\ No newline at end of file
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_RLPWriter } from "../rlp/Lib_RLPWriter.sol";
/**
* @title Lib_EthUtils
*/
library Lib_EthUtils {
/***********************************
* Internal Functions: Code Access *
***********************************/
/**
* Gets the code for a given address.
* @param _address Address to get code for.
* @param _offset Offset to start reading from.
* @param _length Number of bytes to read.
* @return _code Code read from the contract.
*/
function getCode(
address _address,
uint256 _offset,
uint256 _length
)
internal
view
returns (
bytes memory _code
)
{
assembly {
_code := mload(0x40)
mstore(0x40, add(_code, add(_length, 0x20)))
mstore(_code, _length)
extcodecopy(_address, add(_code, 0x20), _offset, _length)
}
return _code;
}
/**
* Gets the full code for a given address.
* @param _address Address to get code for.
* @return _code Full code of the contract.
*/
function getCode(
address _address
)
internal
view
returns (
bytes memory _code
)
{
return getCode(
_address,
0,
getCodeSize(_address)
);
}
/**
* Gets the size of a contract's code in bytes.
* @param _address Address to get code size for.
* @return _codeSize Size of the contract's code in bytes.
*/
function getCodeSize(
address _address
)
internal
view
returns (
uint256 _codeSize
)
{
assembly {
_codeSize := extcodesize(_address)
}
return _codeSize;
}
/**
* Gets the hash of a contract's code.
* @param _address Address to get a code hash for.
* @return _codeHash Hash of the contract's code.
*/
function getCodeHash(
address _address
)
internal
view
returns (
bytes32 _codeHash
)
{
assembly {
_codeHash := extcodehash(_address)
}
return _codeHash;
}
/*****************************************
* Internal Functions: Contract Creation *
*****************************************/
/**
* Creates a contract with some given initialization code.
* @param _code Contract initialization code.
* @return _created Address of the created contract.
*/
function createContract(
bytes memory _code
)
internal
returns (
address _created
)
{
assembly {
_created := create(
0,
add(_code, 0x20),
mload(_code)
)
}
return _created;
}
/**
* Computes the address that would be generated by CREATE.
* @param _creator Address creating the contract.
* @param _nonce Creator's nonce.
* @return _address Address to be generated by CREATE.
*/
function getAddressForCREATE(
address _creator,
uint256 _nonce
)
internal
view
returns (
address _address
)
{
bytes[] memory encoded = new bytes[](2);
encoded[0] = Lib_RLPWriter.encodeAddress(_creator);
encoded[1] = Lib_RLPWriter.encodeUint(_nonce);
bytes memory encodedList = Lib_RLPWriter.encodeList(encoded);
return getAddressFromHash(keccak256(encodedList));
}
/**
* Computes the address that would be generated by CREATE2.
* @param _creator Address creating the contract.
* @param _bytecode Bytecode of the contract to be created.
* @param _salt 32 byte salt value mixed into the hash.
* @return _address Address to be generated by CREATE2.
*/
function getAddressForCREATE2(
address _creator,
bytes memory _bytecode,
bytes32 _salt
)
internal
view
returns (address _address)
{
bytes32 hashedData = keccak256(abi.encodePacked(
byte(0xff),
_creator,
_salt,
keccak256(_bytecode)
));
return getAddressFromHash(hashedData);
}
/****************************************
* Private Functions: Contract Creation *
****************************************/
/**
* Determines an address from a 32 byte hash. Since addresses are only
* 20 bytes, we need to retrieve the last 20 bytes from the original
* hash. Converting to uint256 and then uint160 gives us these bytes.
* @param _hash Hash to convert to an address.
* @return _address Hash converted to an address.
*/
function getAddressFromHash(
bytes32 _hash
)
private
pure
returns (
address _address
)
{
return address(bytes20(uint160(uint256(_hash))));
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/**
* @title Lib_MerkleUtils
*/
library Lib_MerkleUtils {
function getMerkleRoot(
bytes32[] memory _hashes
)
internal
view
returns (
bytes32 _root
)
{
if (_hashes.length == 1) {
return _hashes[0];
}
bytes32[] memory defaultHashes = _getDefaultHashes(_hashes.length);
bytes32[] memory nodes = _hashes;
if (_hashes.length % 2 == 1) {
nodes = new bytes32[](_hashes.length + 1);
for (uint256 i = 0; i < _hashes.length; i++) {
nodes[i] = _hashes[i];
}
}
uint256 currentLevel = 0;
uint256 nextLevelSize = _hashes.length;
if (nextLevelSize % 2 == 1) {
nodes[nextLevelSize] = defaultHashes[currentLevel];
nextLevelSize += 1;
}
while (nextLevelSize > 1) {
currentLevel += 1;
for (uint256 i = 0; i < nextLevelSize / 2; i++) {
nodes[i] = _getParentHash(
nodes[i*2],
nodes[i*2 + 1]
);
}
nextLevelSize = nextLevelSize / 2;
if (nextLevelSize % 2 == 1 && nextLevelSize != 1) {
nodes[nextLevelSize] = defaultHashes[currentLevel];
nextLevelSize += 1;
}
}
return nodes[0];
}
function getMerkleRoot(
bytes[] memory _elements
)
internal
view
returns (
bytes32 _root
)
{
bytes32[] memory hashes = new bytes32[](_elements.length);
for (uint256 i = 0; i < _elements.length; i++) {
hashes[i] = keccak256(_elements[i]);
}
return getMerkleRoot(hashes);
}
function verify(
bytes32 _root,
bytes memory _leaf,
uint256 _path,
bytes32[] memory _siblings
)
internal
pure
returns (
bool _verified
)
{
bytes32 computedRoot = keccak256(_leaf);
for (uint256 i = 0; i < _siblings.length; i++) {
bytes32 sibling = _siblings[i];
bool isRightSibling = uint8(_path >> i & 1) == 1;
if (isRightSibling) {
computedRoot = _getParentHash(computedRoot, sibling);
} else {
computedRoot = _getParentHash(sibling, computedRoot);
}
}
return computedRoot == _root;
}
function _getDefaultHashes(
uint256 _length
)
private
pure
returns (
bytes32[] memory _defaultHashes
)
{
bytes32[] memory defaultHashes = new bytes32[](_length);
defaultHashes[0] = keccak256(abi.encodePacked(uint256(0)));
for (uint256 i = 1; i < defaultHashes.length; i++) {
defaultHashes[i] = keccak256(abi.encodePacked(defaultHashes[i-1]));
}
return defaultHashes;
}
function _getParentHash(
bytes32 _leftChildHash,
bytes32 _rightChildHash
)
private
pure
returns (
bytes32 _hash
)
{
return keccak256(abi.encodePacked(_leftChildHash, _rightChildHash));
}
}
\ No newline at end of file
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Contract Imports */
import { OVM_BaseCrossDomainMessenger } from "../../OVM/bridge/OVM_BaseCrossDomainMessenger.sol";
/**
* @title mockOVM_CrossDomainMessenger
*/
contract mockOVM_CrossDomainMessenger is OVM_BaseCrossDomainMessenger {
/***********
* Structs *
***********/
struct ReceivedMessage {
uint256 timestamp;
address target;
address sender;
bytes message;
uint256 messageNonce;
uint256 gasLimit;
}
/**********************
* Contract Variables *
**********************/
ReceivedMessage[] internal fullReceivedMessages;
uint256 internal lastRelayedMessage;
uint256 internal delay;
/***************
* Constructor *
***************/
/**
* @param _delay Time in seconds before a message can be relayed.
*/
constructor(
uint256 _delay
) {
delay = _delay;
}
/********************
* Public Functions *
********************/
/**
* Sends a message to another mock xdomain messenger.
* @param _target Target for the message.
* @param _message Message to send.
* @param _gasLimit Amount of gas to send with the call.
*/
function sendMessage(
address _target,
bytes memory _message,
uint32 _gasLimit
)
public
{
mockOVM_CrossDomainMessenger targetMessenger = mockOVM_CrossDomainMessenger(
targetMessengerAddress
);
// Just send it over!
targetMessenger.receiveMessage(ReceivedMessage({
timestamp: block.timestamp,
target: _target,
sender: msg.sender,
message: _message,
messageNonce: messageNonce,
gasLimit: _gasLimit
}));
messageNonce += 1;
}
/**
* Receives a message to be sent later.
* @param _message Message to send later.
*/
function receiveMessage(
ReceivedMessage memory _message
)
public
{
fullReceivedMessages.push(_message);
}
/**
* Checks whether we have messages to relay.
* @param _exists Whether or not we have more messages to relay.
*/
function hasNextMessage()
public
view
returns (
bool _exists
)
{
return fullReceivedMessages.length < lastRelayedMessage;
}
/**
* Relays the last received message not yet relayed.
*/
function relayNextMessage()
public
{
if (hasNextMessage() == false) {
return;
}
ReceivedMessage memory nextMessage = fullReceivedMessages[lastRelayedMessage];
if (nextMessage.timestamp + delay > block.timestamp) {
return;
}
xDomainMessageSender = nextMessage.sender;
nextMessage.target.call{gas: nextMessage.gasLimit}(nextMessage.message);
lastRelayedMessage += 1;
}
}
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_extra_1 = __importDefault(require("fs-extra"));
const config_1 = require("@nomiclabs/buidler/config");
const strings_1 = require("@nomiclabs/buidler/internal/util/strings");
const artifacts_1 = require("@nomiclabs/buidler/internal/artifacts");
const task_names_1 = require("@nomiclabs/buidler/builtin-tasks/task-names");
config_1.internalTask(task_names_1.TASK_COMPILE_GET_COMPILER_INPUT, async (_, { config, run }, runSuper) => {
const input = await runSuper();
input.settings.outputSelection['*']['*'].push('storageLayout');
return input;
});
config_1.internalTask(task_names_1.TASK_BUILD_ARTIFACTS).setAction(async ({ force }, { config, run }) => {
const sources = await run(task_names_1.TASK_COMPILE_GET_SOURCE_PATHS);
if (sources.length === 0) {
console.log('No Solidity source file available.');
return;
}
const isCached = await run(task_names_1.TASK_COMPILE_CHECK_CACHE, { force });
if (isCached) {
console.log('All contracts have already been compiled, skipping compilation.');
return;
}
const compilationOutput = await run(task_names_1.TASK_COMPILE_COMPILE);
if (compilationOutput === undefined) {
return;
}
await fs_extra_1.default.ensureDir(config.paths.artifacts);
let numberOfContracts = 0;
for (const file of Object.values(compilationOutput.contracts)) {
for (const [contractName, contractOutput] of Object.entries(file)) {
const artifact = artifacts_1.getArtifactFromContractOutput(contractName, contractOutput);
numberOfContracts += 1;
artifact.storageLayout = contractOutput.storageLayout;
await artifacts_1.saveArtifact(config.paths.artifacts, artifact);
}
}
console.log('Compiled', numberOfContracts, strings_1.pluralize(numberOfContracts, 'contract'), 'successfully');
});
//# sourceMappingURL=modify-compiler.js.map
\ No newline at end of file
{"version":3,"file":"modify-compiler.js","sourceRoot":"","sources":["../../../../test/helpers/buidler/modify-compiler.ts"],"names":[],"mappings":";;;;;AACA,wDAA8B;AAC9B,sDAAwD;AACxD,sEAAoE;AACpE,qEAG8C;AAC9C,4EAMoD;AAEpD,qBAAY,CACV,4CAA+B,EAC/B,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,QAAQ,EAAE,EAAE;IACrC,MAAM,KAAK,GAAG,MAAM,QAAQ,EAAE,CAAA;IAG9B,KAAK,CAAC,QAAQ,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;IAE9D,OAAO,KAAK,CAAA;AACd,CAAC,CACF,CAAA;AAED,qBAAY,CAAC,iCAAoB,CAAC,CAAC,SAAS,CAC1C,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE;IACnC,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,0CAA6B,CAAC,CAAA;IAExD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE;QACxB,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAA;QACjD,OAAM;KACP;IAED,MAAM,QAAQ,GAAY,MAAM,GAAG,CAAC,qCAAwB,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;IAExE,IAAI,QAAQ,EAAE;QACZ,OAAO,CAAC,GAAG,CACT,iEAAiE,CAClE,CAAA;QACD,OAAM;KACP;IAED,MAAM,iBAAiB,GAAG,MAAM,GAAG,CAAC,iCAAoB,CAAC,CAAA;IAEzD,IAAI,iBAAiB,KAAK,SAAS,EAAE;QACnC,OAAM;KACP;IAED,MAAM,kBAAO,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IAC/C,IAAI,iBAAiB,GAAG,CAAC,CAAA;IAEzB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAM,iBAAiB,CAAC,SAAS,CAAC,EAAE;QAClE,KAAK,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YACjE,MAAM,QAAQ,GAAQ,yCAA6B,CACjD,YAAY,EACZ,cAAc,CACf,CAAA;YACD,iBAAiB,IAAI,CAAC,CAAA;YAGtB,QAAQ,CAAC,aAAa,GAAI,cAAsB,CAAC,aAAa,CAAA;YAE9D,MAAM,wBAAY,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;SACrD;KACF;IAED,OAAO,CAAC,GAAG,CACT,UAAU,EACV,iBAAiB,EACjB,mBAAS,CAAC,iBAAiB,EAAE,UAAU,CAAC,EACxC,cAAc,CACf,CAAA;AACH,CAAC,CACF,CAAA"}
\ No newline at end of file
export declare const DEFAULT_ACCOUNTS: {
balance: string;
secretKey: string;
}[];
export declare const DEFAULT_ACCOUNTS_BUIDLER: {
balance: string;
privateKey: string;
}[];
export declare const OVM_TX_GAS_LIMIT = 10000000;
export declare const RUN_OVM_TEST_GAS = 20000000;
export declare const FORCE_INCLUSION_PERIOD_SECONDS = 600;
export declare const NULL_BYTES32: string;
export declare const NON_NULL_BYTES32: string;
export declare const ZERO_ADDRESS: string;
export declare const NON_ZERO_ADDRESS: string;
export declare const VERIFIED_EMPTY_CONTRACT_HASH = "0x00004B1DC0DE000000004B1DC0DE000000004B1DC0DE000000004B1DC0DE0000";
export declare const NUISANCE_GAS_COSTS: {
NUISANCE_GAS_SLOAD: number;
NUISANCE_GAS_SSTORE: number;
MIN_NUISANCE_GAS_PER_CONTRACT: number;
NUISANCE_GAS_PER_CONTRACT_BYTE: number;
MIN_GAS_FOR_INVALID_STATE_ACCESS: number;
};
export declare const Helper_TestRunner_BYTELEN = 3654;
export declare const STORAGE_XOR = "0xfeedfacecafebeeffeedfacecafebeeffeedfacecafebeeffeedfacecafebeef";
export declare const getStorageXOR: (key: string) => string;
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getStorageXOR = exports.STORAGE_XOR = exports.Helper_TestRunner_BYTELEN = exports.NUISANCE_GAS_COSTS = exports.VERIFIED_EMPTY_CONTRACT_HASH = exports.NON_ZERO_ADDRESS = exports.ZERO_ADDRESS = exports.NON_NULL_BYTES32 = exports.NULL_BYTES32 = exports.FORCE_INCLUSION_PERIOD_SECONDS = exports.RUN_OVM_TEST_GAS = exports.OVM_TX_GAS_LIMIT = exports.DEFAULT_ACCOUNTS_BUIDLER = exports.DEFAULT_ACCOUNTS = void 0;
const ethers_1 = require("ethers");
const ethereum_waffle_1 = require("ethereum-waffle");
const buffer_xor_1 = __importDefault(require("buffer-xor"));
const utils_1 = require("./utils");
exports.DEFAULT_ACCOUNTS = ethereum_waffle_1.defaultAccounts;
exports.DEFAULT_ACCOUNTS_BUIDLER = ethereum_waffle_1.defaultAccounts.map((account) => {
return {
balance: ethers_1.ethers.BigNumber.from(account.balance).toHexString(),
privateKey: account.secretKey,
};
});
exports.OVM_TX_GAS_LIMIT = 10000000;
exports.RUN_OVM_TEST_GAS = 20000000;
exports.FORCE_INCLUSION_PERIOD_SECONDS = 600;
exports.NULL_BYTES32 = utils_1.makeHexString('00', 32);
exports.NON_NULL_BYTES32 = utils_1.makeHexString('11', 32);
exports.ZERO_ADDRESS = utils_1.makeAddress('00');
exports.NON_ZERO_ADDRESS = utils_1.makeAddress('11');
exports.VERIFIED_EMPTY_CONTRACT_HASH = '0x00004B1DC0DE000000004B1DC0DE000000004B1DC0DE000000004B1DC0DE0000';
exports.NUISANCE_GAS_COSTS = {
NUISANCE_GAS_SLOAD: 20000,
NUISANCE_GAS_SSTORE: 20000,
MIN_NUISANCE_GAS_PER_CONTRACT: 30000,
NUISANCE_GAS_PER_CONTRACT_BYTE: 100,
MIN_GAS_FOR_INVALID_STATE_ACCESS: 30000,
};
exports.Helper_TestRunner_BYTELEN = 3654;
exports.STORAGE_XOR = '0xfeedfacecafebeeffeedfacecafebeeffeedfacecafebeeffeedfacecafebeef';
exports.getStorageXOR = (key) => {
return utils_1.toHexString(buffer_xor_1.default(utils_1.fromHexString(key), utils_1.fromHexString(exports.STORAGE_XOR)));
};
//# sourceMappingURL=constants.js.map
\ No newline at end of file
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../test/helpers/constants.ts"],"names":[],"mappings":";;;;;;AACA,mCAA+B;AAC/B,qDAAiD;AACjD,4DAA4B;AAG5B,mCAAgF;AAEnE,QAAA,gBAAgB,GAAG,iCAAe,CAAA;AAClC,QAAA,wBAAwB,GAAG,iCAAe,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;IACtE,OAAO;QACL,OAAO,EAAE,eAAM,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE;QAC7D,UAAU,EAAE,OAAO,CAAC,SAAS;KAC9B,CAAA;AACH,CAAC,CAAC,CAAA;AAEW,QAAA,gBAAgB,GAAG,QAAU,CAAA;AAC7B,QAAA,gBAAgB,GAAG,QAAU,CAAA;AAC7B,QAAA,8BAA8B,GAAG,GAAG,CAAA;AAEpC,QAAA,YAAY,GAAG,qBAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;AACtC,QAAA,gBAAgB,GAAG,qBAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;AAC1C,QAAA,YAAY,GAAG,mBAAW,CAAC,IAAI,CAAC,CAAA;AAChC,QAAA,gBAAgB,GAAG,mBAAW,CAAC,IAAI,CAAC,CAAA;AAEpC,QAAA,4BAA4B,GACvC,oEAAoE,CAAA;AAEzD,QAAA,kBAAkB,GAAG;IAChC,kBAAkB,EAAE,KAAK;IACzB,mBAAmB,EAAE,KAAK;IAC1B,6BAA6B,EAAE,KAAK;IACpC,8BAA8B,EAAE,GAAG;IACnC,gCAAgC,EAAE,KAAK;CACxC,CAAA;AAGY,QAAA,yBAAyB,GAAG,IAAI,CAAA;AAEhC,QAAA,WAAW,GAAG,oEAAoE,CAAA;AAClF,QAAA,aAAa,GAAG,CAAC,GAAW,EAAU,EAAE;IACnD,OAAO,mBAAW,CAAC,oBAAG,CAAC,qBAAa,CAAC,GAAG,CAAC,EAAE,qBAAa,CAAC,mBAAW,CAAC,CAAC,CAAC,CAAA;AACzE,CAAC,CAAA"}
\ No newline at end of file
/// <reference types="node" />
export declare const toHexString: (buf: Buffer | string) => string;
export declare const fromHexString: (str: string | Buffer) => Buffer;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.fromHexString = exports.toHexString = void 0;
exports.toHexString = (buf) => {
return '0x' + exports.fromHexString(buf).toString('hex');
};
exports.fromHexString = (str) => {
if (typeof str === 'string' && str.startsWith('0x')) {
return Buffer.from(str.slice(2), 'hex');
}
return Buffer.from(str);
};
//# sourceMappingURL=buffer-utils.js.map
\ No newline at end of file
{"version":3,"file":"buffer-utils.js","sourceRoot":"","sources":["../../../../test/helpers/utils/buffer-utils.ts"],"names":[],"mappings":";;;AAKa,QAAA,WAAW,GAAG,CAAC,GAAoB,EAAU,EAAE;IAC1D,OAAO,IAAI,GAAG,qBAAa,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;AAClD,CAAC,CAAA;AAOY,QAAA,aAAa,GAAG,CAAC,GAAoB,EAAU,EAAE;IAC5D,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE;QACnD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;KACxC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACzB,CAAC,CAAA"}
\ No newline at end of file
export declare const makeHexString: (byte: string, len: number) => string;
export declare const makeAddress: (byte: string) => string;
export declare const remove0x: (str: string) => string;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.remove0x = exports.makeAddress = exports.makeHexString = void 0;
exports.makeHexString = (byte, len) => {
return '0x' + byte.repeat(len);
};
exports.makeAddress = (byte) => {
return exports.makeHexString(byte, 20);
};
exports.remove0x = (str) => {
if (str.startsWith('0x')) {
return str.slice(2);
}
else {
return str;
}
};
//# sourceMappingURL=byte-utils.js.map
\ No newline at end of file
{"version":3,"file":"byte-utils.js","sourceRoot":"","sources":["../../../../test/helpers/utils/byte-utils.ts"],"names":[],"mappings":";;;AAMa,QAAA,aAAa,GAAG,CAAC,IAAY,EAAE,GAAW,EAAU,EAAE;IACjE,OAAO,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;AAChC,CAAC,CAAA;AAOY,QAAA,WAAW,GAAG,CAAC,IAAY,EAAU,EAAE;IAClD,OAAO,qBAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;AAChC,CAAC,CAAA;AAOY,QAAA,QAAQ,GAAG,CAAC,GAAW,EAAU,EAAE;IAC9C,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE;QACxB,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;KACpB;SAAM;QACL,OAAO,GAAG,CAAA;KACX;AACH,CAAC,CAAA"}
\ No newline at end of file
export * from './buffer-utils';
export * from './byte-utils';
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./buffer-utils"), exports);
__exportStar(require("./byte-utils"), exports);
//# sourceMappingURL=index.js.map
\ No newline at end of file
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../test/helpers/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,iDAA8B;AAC9B,+CAA4B"}
\ No newline at end of file
......@@ -2,10 +2,8 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Proxy Imports */
import { Proxy_Resolver } from "../../proxy/Proxy_Resolver.sol";
/* Library Imports */
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_EthMerkleTrie } from "../../libraries/trie/Lib_EthMerkleTrie.sol";
import { Lib_ByteUtils } from "../../libraries/utils/Lib_ByteUtils.sol";
......@@ -20,7 +18,7 @@ import { OVM_BaseCrossDomainMessenger } from "./OVM_BaseCrossDomainMessenger.sol
/**
* @title OVM_L1CrossDomainMessenger
*/
contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, OVM_BaseCrossDomainMessenger, Proxy_Resolver {
contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, OVM_BaseCrossDomainMessenger, Lib_AddressResolver {
/*******************************************
* Contract Variables: Contract References *
......@@ -35,12 +33,12 @@ contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, OVM_BaseCros
***************/
/**
* @param _proxyManager Address of the Proxy_Manager.
* @param _libAddressManager Address of the Address Manager.
*/
constructor(
address _proxyManager
address _libAddressManager
)
Proxy_Resolver(_proxyManager)
Lib_AddressResolver(_libAddressManager)
{
ovmL1ToL2TransactionQueue = iOVM_L1ToL2TransactionQueue(resolve("OVM_L1ToL2TransactionQueue"));
ovmStateCommitmentChain = iOVM_StateCommitmentChain(resolve("OVM_StateCommitmentChain"));
......
......@@ -2,8 +2,8 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Proxy Imports */
import { Proxy_Resolver } from "../../proxy/Proxy_Resolver.sol";
/* Library Imports */
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */
import { iOVM_L2CrossDomainMessenger } from "../../iOVM/bridge/iOVM_L2CrossDomainMessenger.sol";
......@@ -16,7 +16,7 @@ import { OVM_BaseCrossDomainMessenger } from "./OVM_BaseCrossDomainMessenger.sol
/**
* @title OVM_L2CrossDomainMessenger
*/
contract OVM_L2CrossDomainMessenger is iOVM_L2CrossDomainMessenger, OVM_BaseCrossDomainMessenger, Proxy_Resolver {
contract OVM_L2CrossDomainMessenger is iOVM_L2CrossDomainMessenger, OVM_BaseCrossDomainMessenger, Lib_AddressResolver {
/*******************************************
* Contract Variables: Contract References *
......@@ -31,12 +31,12 @@ contract OVM_L2CrossDomainMessenger is iOVM_L2CrossDomainMessenger, OVM_BaseCros
***************/
/**
* @param _proxyManager Address of the Proxy_Manager.
* @param _libAddressManager Address of the Address Manager.
*/
constructor(
address _proxyManager
address _libAddressManager
)
Proxy_Resolver(_proxyManager)
Lib_AddressResolver(_libAddressManager)
{
ovmL1MessageSender = iOVM_L1MessageSender(resolve("OVM_L1MessageSender"));
ovmL2ToL1MessagePasser = iOVM_L2ToL1MessagePasser(resolve("OVM_L2ToL1MessagePasser"));
......
......@@ -2,11 +2,9 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Proxy Imports */
import { Proxy_Resolver } from "../../proxy/Proxy_Resolver.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_MerkleUtils } from "../../libraries/utils/Lib_MerkleUtils.sol";
/* Interface Imports */
......@@ -19,7 +17,7 @@ import { OVM_BaseChain } from "./OVM_BaseChain.sol";
/**
* @title OVM_CanonicalTransactionChain
*/
contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_BaseChain, Proxy_Resolver {
contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_BaseChain, Lib_AddressResolver {
/*******************************************
* Contract Variables: Contract References *
......@@ -41,14 +39,14 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
***************/
/**
* @param _proxyManager Address of the Proxy_Manager.
* @param _libAddressManager Address of the Address Manager.
* @param _forceInclusionPeriodSeconds Period during which only the sequencer can submit.
*/
constructor(
address _proxyManager,
address _libAddressManager,
uint256 _forceInclusionPeriodSeconds
)
Proxy_Resolver(_proxyManager)
Lib_AddressResolver(_libAddressManager)
{
ovmL1ToL2TransactionQueue = iOVM_L1ToL2TransactionQueue(resolve("OVM_L1ToL2TransactionQueue"));
forceInclusionPeriodSeconds = _forceInclusionPeriodSeconds;
......
......@@ -2,11 +2,9 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Proxy Imports */
import { Proxy_Resolver } from "../../proxy/Proxy_Resolver.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */
import { iOVM_FraudVerifier } from "../../iOVM/verification/iOVM_FraudVerifier.sol";
......@@ -19,7 +17,7 @@ import { OVM_BaseChain } from "./OVM_BaseChain.sol";
/**
* @title OVM_StateCommitmentChain
*/
contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, Proxy_Resolver {
contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, Lib_AddressResolver {
/*******************************************
* Contract Variables: Contract References *
......@@ -34,12 +32,12 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, P
***************/
/**
* @param _proxyManager Address of the Proxy_Manager.
* @param _libAddressManager Address of the Address Manager.
*/
constructor(
address _proxyManager
address _libAddressManager
)
Proxy_Resolver(_proxyManager)
Lib_AddressResolver(_libAddressManager)
{
ovmCanonicalTransactionChain = iOVM_CanonicalTransactionChain(resolve("OVM_CanonicalTransactionChain"));
ovmFraudVerifier = iOVM_FraudVerifier(resolve("OVM_FraudVerifier"));
......
......@@ -2,11 +2,9 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Proxy Imports */
import { Proxy_Resolver } from "../../proxy/Proxy_Resolver.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */
import { iOVM_L1ToL2TransactionQueue } from "../../iOVM/queue/iOVM_L1ToL2TransactionQueue.sol";
......@@ -17,7 +15,7 @@ import { OVM_BaseQueue } from "./OVM_BaseQueue.sol";
/**
* @title OVM_L1ToL2TransactionQueue
*/
contract OVM_L1ToL2TransactionQueue is iOVM_L1ToL2TransactionQueue, OVM_BaseQueue, Proxy_Resolver {
contract OVM_L1ToL2TransactionQueue is iOVM_L1ToL2TransactionQueue, OVM_BaseQueue, Lib_AddressResolver {
/*******************************************
* Contract Variables: Contract References *
......@@ -31,12 +29,12 @@ contract OVM_L1ToL2TransactionQueue is iOVM_L1ToL2TransactionQueue, OVM_BaseQueu
***************/
/**
* @param _proxyManager Address of the Proxy_Manager.
* @param _libAddressManager Address of the Address Manager.
*/
constructor(
address _proxyManager
address _libAddressManager
)
Proxy_Resolver(_proxyManager)
Lib_AddressResolver(_libAddressManager)
{
ovmCanonicalTransactionChain = resolve("OVM_CanonicalTransactionChain");
}
......
......@@ -2,11 +2,9 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Proxy Imports */
import { Proxy_Resolver } from "../../proxy/Proxy_Resolver.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */
import { iOVM_FraudVerifier } from "../../iOVM/verification/iOVM_FraudVerifier.sol";
......@@ -17,7 +15,7 @@ import { iOVM_StateManagerFactory } from "../../iOVM/execution/iOVM_StateManager
import { iOVM_StateCommitmentChain } from "../../iOVM/chain/iOVM_StateCommitmentChain.sol";
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
contract OVM_FraudVerifier is iOVM_FraudVerifier, Proxy_Resolver {
contract OVM_FraudVerifier is iOVM_FraudVerifier, Lib_AddressResolver {
/*******************************************
* Contract Variables: Contract References *
......@@ -39,12 +37,12 @@ contract OVM_FraudVerifier is iOVM_FraudVerifier, Proxy_Resolver {
***************/
/**
* @param _proxyManager Address of the Proxy_Manager.
* @param _libAddressManager Address of the Address Manager.
*/
constructor(
address _proxyManager
address _libAddressManager
)
Proxy_Resolver(_proxyManager)
Lib_AddressResolver(_libAddressManager)
{
ovmStateCommitmentChain = iOVM_StateCommitmentChain(resolve("OVM_StateCommitmentChain"));
ovmCanonicalTransactionChain = iOVM_CanonicalTransactionChain(resolve("OVM_CanonicalTransactionChain"));
......@@ -123,7 +121,7 @@ contract OVM_FraudVerifier is iOVM_FraudVerifier, Proxy_Resolver {
transitioners[_preStateRoot] = iOVM_StateTransitionerFactory(
resolve("OVM_StateTransitionerFactory")
).create(
address(proxyManager),
address(libAddressManager),
_preStateRootProof.index,
_preStateRoot,
Lib_OVMCodec.hashTransaction(_transaction)
......
......@@ -2,11 +2,9 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Proxy Imports */
import { Proxy_Resolver } from "../../proxy/Proxy_Resolver.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_EthUtils } from "../../libraries/utils/Lib_EthUtils.sol";
import { Lib_EthMerkleTrie } from "../../libraries/trie/Lib_EthMerkleTrie.sol";
......@@ -19,7 +17,7 @@ import { iOVM_StateManagerFactory } from "../../iOVM/execution/iOVM_StateManager
/**
* @title OVM_StateTransitioner
*/
contract OVM_StateTransitioner is iOVM_StateTransitioner, Proxy_Resolver {
contract OVM_StateTransitioner is iOVM_StateTransitioner, Lib_AddressResolver {
/*******************
* Data Structures *
......@@ -56,18 +54,18 @@ contract OVM_StateTransitioner is iOVM_StateTransitioner, Proxy_Resolver {
***************/
/**
* @param _proxyManager Address of the Proxy_Manager.
* @param _libAddressManager Address of the Address Manager.
* @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 _proxyManager,
address _libAddressManager,
uint256 _stateTransitionIndex,
bytes32 _preStateRoot,
bytes32 _transactionHash
)
Proxy_Resolver(_proxyManager)
Lib_AddressResolver(_libAddressManager)
{
stateTransitionIndex = _stateTransitionIndex;
preStateRoot = _preStateRoot;
......@@ -253,7 +251,7 @@ contract OVM_StateTransitioner is iOVM_StateTransitioner, Proxy_Resolver {
// TODO: Set state manager for EM here.
ovmStateManager.setExecutionManager(resolveTarget("OVM_ExecutionManager"));
ovmStateManager.setExecutionManager(resolve("OVM_ExecutionManager"));
ovmExecutionManager.run(_transaction, address(ovmStateManager));
phase = TransitionPhase.POST_EXECUTION;
......
......@@ -19,14 +19,14 @@ contract OVM_StateTransitionerFactory is iOVM_StateTransitionerFactory {
/**
* Creates a new OVM_StateTransitioner
* @param _proxyManager Address of the Proxy_Manager.
* @param _libAddressManager Address of the Address Manager.
* @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.
* @return _ovmStateTransitioner New OVM_StateTransitioner instance.
*/
function create(
address _proxyManager,
address _libAddressManager,
uint256 _stateTransitionIndex,
bytes32 _preStateRoot,
bytes32 _transactionHash
......@@ -38,7 +38,7 @@ contract OVM_StateTransitionerFactory is iOVM_StateTransitionerFactory {
)
{
return new OVM_StateTransitioner(
_proxyManager,
_libAddressManager,
_stateTransitionIndex,
_preStateRoot,
_transactionHash
......
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/**
* @title Lib_AddressManager
*/
contract Lib_AddressManager {
/*******************************************
* Contract Variables: Internal Accounting *
*******************************************/
mapping (bytes32 => address) private addresses;
/********************
* Public Functions *
********************/
function setAddress(
string memory _name,
address _address
)
public
{
addresses[_getNameHash(_name)] = _address;
}
function getAddress(
string memory _name
)
public
view
returns (address)
{
return addresses[_getNameHash(_name)];
}
/**********************
* Internal Functions *
**********************/
function _getNameHash(
string memory _name
)
internal
pure
returns (
bytes32 _hash
)
{
return keccak256(abi.encodePacked(_name));
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/* Library Imports */
import { Lib_AddressManager } from "./Lib_AddressManager.sol";
/**
* @title Lib_AddressResolver
*/
contract Lib_AddressResolver {
/*******************************************
* Contract Variables: Contract References *
*******************************************/
Lib_AddressManager internal libAddressManager;
/***************
* Constructor *
***************/
/**
* @param _libAddressManager Address of the Lib_AddressManager.
*/
constructor(
address _libAddressManager
)
public
{
libAddressManager = Lib_AddressManager(_libAddressManager);
}
/********************
* Public Functions *
********************/
function resolve(
string memory _name
)
public
view
returns (
address _contract
)
{
return libAddressManager.getAddress(_name);
}
}
......@@ -4,12 +4,15 @@
"main": "index.js",
"license": "MIT",
"scripts": {
"build": "yarn run build:contracts",
"build": "yarn run build:contracts && yarn run build:typescript && yarn run build:copy",
"build:typescript": "tsc -p .",
"build:contracts": "buidler compile",
"build:copy": "copyfiles -u 2 \"contracts/optimistic-ethereum/**/*.sol\" \"build/contracts\"",
"test": "yarn run test:contracts",
"test:contracts": "buidler test",
"lint": "tslint --format stylish --project .",
"fix": "prettier --config prettier-config.json --write \"buidler.config.ts\" \"{src,test}/**/*.ts\""
"fix": "prettier --config prettier-config.json --write \"buidler.config.ts\" \"{src,test}/**/*.ts\"",
"clean": "rm -rf ./artifacts ./build ./cache"
},
"devDependencies": {
"@nomiclabs/buidler": "^1.4.4",
......@@ -22,6 +25,7 @@
"assert": "^2.0.0",
"buffer-xor": "^2.0.2",
"chai": "^4.2.0",
"copyfiles": "^2.3.0",
"ethereum-waffle": "3.0.0",
"ethers": "5.0.0",
"fs-extra": "^9.0.1",
......
......@@ -16,6 +16,7 @@ export const DEFAULT_ACCOUNTS_BUIDLER = defaultAccounts.map((account) => {
export const OVM_TX_GAS_LIMIT = 10_000_000
export const RUN_OVM_TEST_GAS = 20_000_000
export const FORCE_INCLUSION_PERIOD_SECONDS = 600
export const NULL_BYTES32 = makeHexString('00', 32)
export const NON_NULL_BYTES32 = makeHexString('11', 32)
......
......@@ -21,7 +21,7 @@
]
},
"include": ["*.ts", "**/*.ts", "artifacts/*.json"],
"exclude": ["./build", "node_modules"],
"exclude": ["./build", "node_modules", "test"],
"files": [
"./buidler.config.ts",
"./buidler-env.d.ts",
......
......@@ -887,6 +887,11 @@ ansi-regex@^4.1.0:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
ansi-regex@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
ansi-styles@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
......@@ -899,7 +904,7 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
ansi-styles@^4.1.0:
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
......@@ -2284,6 +2289,15 @@ cliui@^5.0.0:
strip-ansi "^5.2.0"
wrap-ansi "^5.1.0"
cliui@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
dependencies:
string-width "^4.2.0"
strip-ansi "^6.0.0"
wrap-ansi "^6.2.0"
clone-buffer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58"
......@@ -2468,6 +2482,18 @@ copy-props@^2.0.1:
each-props "^1.3.0"
is-plain-object "^2.0.1"
copyfiles@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-2.3.0.tgz#1c26ebbe3d46bba2d309a3fd8e3aaccf53af8c76"
integrity sha512-73v7KFuDFJ/ofkQjZBMjMBFWGgkS76DzXvBMUh7djsMOE5EELWtAO/hRB6Wr5Vj5Zg+YozvoHemv0vnXpqxmOQ==
dependencies:
glob "^7.0.5"
minimatch "^3.0.3"
mkdirp "^1.0.4"
noms "0.0.0"
through2 "^2.0.1"
yargs "^15.3.1"
core-js-pure@^3.0.1:
version "3.6.5"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
......@@ -2916,6 +2942,11 @@ emoji-regex@^7.0.1:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
......@@ -3934,6 +3965,14 @@ find-up@^2.1.0:
dependencies:
locate-path "^2.0.0"
find-up@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
dependencies:
locate-path "^5.0.0"
path-exists "^4.0.0"
find-yarn-workspace-root@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz#40eb8e6e7c2502ddfaa2577c176f221422f860db"
......@@ -4319,7 +4358,7 @@ glob@7.1.3:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@7.1.6, glob@^7.1.1, glob@^7.1.3, glob@~7.1.6:
glob@7.1.6, glob@^7.0.5, glob@^7.1.1, glob@^7.1.3, glob@~7.1.6:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
......@@ -4941,6 +4980,11 @@ is-fullwidth-code-point@^2.0.0:
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
is-function@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08"
......@@ -5639,6 +5683,13 @@ locate-path@^3.0.0:
p-locate "^3.0.0"
path-exists "^3.0.0"
locate-path@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
dependencies:
p-locate "^4.1.0"
locate-path@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
......@@ -5923,7 +5974,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
minimatch@3.0.4, minimatch@^3.0.4:
minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
......@@ -5965,7 +6016,7 @@ mkdirp-promise@^5.0.1:
dependencies:
mkdirp "*"
mkdirp@*:
mkdirp@*, mkdirp@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
......@@ -6141,6 +6192,14 @@ node-gyp-build@^4.2.0:
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739"
integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==
noms@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/noms/-/noms-0.0.0.tgz#da8ebd9f3af9d6760919b27d9cdc8092a7332859"
integrity sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=
dependencies:
inherits "^2.0.1"
readable-stream "~1.0.31"
normalize-package-data@^2.3.2:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
......@@ -6404,7 +6463,7 @@ p-limit@^1.1.0:
dependencies:
p-try "^1.0.0"
p-limit@^2.0.0:
p-limit@^2.0.0, p-limit@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
......@@ -6432,6 +6491,13 @@ p-locate@^3.0.0:
dependencies:
p-limit "^2.0.0"
p-locate@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
dependencies:
p-limit "^2.2.0"
p-locate@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
......@@ -6974,7 +7040,7 @@ readable-stream@^3.0.6, readable-stream@^3.6.0:
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
readable-stream@~1.0.15:
readable-stream@~1.0.15, readable-stream@~1.0.31:
version "1.0.34"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=
......@@ -7771,6 +7837,15 @@ string-width@^3.0.0, string-width@^3.1.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^5.1.0"
string-width@^4.1.0, string-width@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5"
integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
string.prototype.trim@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz#141233dff32c82bfad80684d7e5f0869ee0fb782"
......@@ -7836,6 +7911,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
dependencies:
ansi-regex "^4.1.0"
strip-ansi@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
dependencies:
ansi-regex "^5.0.0"
strip-bom@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
......@@ -7993,7 +8075,7 @@ through2-filter@^3.0.0:
through2 "~2.0.0"
xtend "~4.0.0"
through2@^2.0.0, through2@^2.0.3, through2@~2.0.0:
through2@^2.0.0, through2@^2.0.1, through2@^2.0.3, through2@~2.0.0:
version "2.0.5"
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
......@@ -8915,6 +8997,15 @@ wrap-ansi@^5.1.0:
string-width "^3.0.0"
strip-ansi "^5.0.0"
wrap-ansi@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
......@@ -9044,6 +9135,14 @@ yargs-parser@^15.0.1:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs-parser@^18.1.2:
version "18.1.3"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs-parser@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4"
......@@ -9105,6 +9204,23 @@ yargs@^14.2.3:
y18n "^4.0.0"
yargs-parser "^15.0.1"
yargs@^15.3.1:
version "15.4.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
dependencies:
cliui "^6.0.0"
decamelize "^1.2.0"
find-up "^4.1.0"
get-caller-file "^2.0.1"
require-directory "^2.1.1"
require-main-filename "^2.0.0"
set-blocking "^2.0.0"
string-width "^4.2.0"
which-module "^2.0.0"
y18n "^4.0.0"
yargs-parser "^18.1.2"
yargs@^4.7.1:
version "4.8.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment