Commit 7ce544e8 authored by Kelvin Fichter's avatar Kelvin Fichter

Merge branch 'dev-chain' of github.com:ethereum-optimism/contracts-v2 into master

parents 3a1be8ab 8a10f31b
import { usePlugin, BuidlerConfig } from '@nomiclabs/buidler/config' import { usePlugin, BuidlerConfig } from '@nomiclabs/buidler/config'
import { import { DEFAULT_ACCOUNTS_BUIDLER, GAS_LIMIT } from './test/helpers/constants'
DEFAULT_ACCOUNTS_BUIDLER,
GAS_LIMIT,
} from './test/helpers/constants'
usePlugin('@nomiclabs/buidler-ethers') usePlugin('@nomiclabs/buidler-ethers')
usePlugin('@nomiclabs/buidler-waffle') usePlugin('@nomiclabs/buidler-waffle')
...@@ -21,7 +18,7 @@ const config: BuidlerConfig = { ...@@ -21,7 +18,7 @@ const config: BuidlerConfig = {
timeout: 50000, timeout: 50000,
}, },
solc: { solc: {
version: "0.7.0", version: '0.7.0',
optimizer: { enabled: true, runs: 200 }, optimizer: { enabled: true, runs: 200 },
}, },
} }
......
// 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;
/* Proxy Imports */
import { Proxy_Resolver } from "../../proxy/Proxy_Resolver.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_MerkleUtils } from "../../libraries/utils/Lib_MerkleUtils.sol";
/* Interface Imports */
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
import { iOVM_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, Proxy_Resolver {
/*******************************************
* Contract Variables: Contract References *
*******************************************/
iOVM_L1ToL2TransactionQueue internal ovmL1ToL2TransactionQueue;
/*******************************************
* Contract Variables: Internal Accounting *
*******************************************/
uint256 internal forceInclusionPeriodSeconds;
uint256 internal lastOVMTimestamp;
/***************
* Constructor *
***************/
/**
* @param _proxyManager Address of the Proxy_Manager.
* @param _forceInclusionPeriodSeconds Period during which only the sequencer can submit.
*/
constructor(
address _proxyManager,
uint256 _forceInclusionPeriodSeconds
)
Proxy_Resolver(_proxyManager)
{
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;
/* Proxy Imports */
import { Proxy_Resolver } from "../../proxy/Proxy_Resolver.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.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, Proxy_Resolver {
/*******************************************
* Contract Variables: Contract References *
*******************************************/
iOVM_CanonicalTransactionChain internal ovmCanonicalTransactionChain;
iOVM_FraudVerifier internal ovmFraudVerifier;
/***************
* Constructor *
***************/
/**
* @param _proxyManager Address of the Proxy_Manager.
*/
constructor(
address _proxyManager
)
Proxy_Resolver(_proxyManager)
{
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);
}
}
...@@ -14,6 +14,9 @@ import { iOVM_SafetyChecker } from "../../iOVM/execution/iOVM_SafetyChecker.sol" ...@@ -14,6 +14,9 @@ import { iOVM_SafetyChecker } from "../../iOVM/execution/iOVM_SafetyChecker.sol"
/* Contract Imports */ /* Contract Imports */
import { OVM_ECDSAContractAccount } from "../accounts/OVM_ECDSAContractAccount.sol"; import { OVM_ECDSAContractAccount } from "../accounts/OVM_ECDSAContractAccount.sol";
/* Logging */
import { console } from "@nomiclabs/buidler/console.sol";
/** /**
* @title OVM_ExecutionManager * @title OVM_ExecutionManager
*/ */
...@@ -31,10 +34,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -31,10 +34,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
* Execution Context Variables * * Execution Context Variables *
*******************************/ *******************************/
GasMeterConfig internal gasMeterConfig;
GlobalContext internal globalContext; GlobalContext internal globalContext;
TransactionContext internal transactionContext; TransactionContext internal transactionContext;
MessageContext internal messageContext; MessageContext internal messageContext;
TransactionRecord internal transactionRecord; TransactionRecord internal transactionRecord;
MessageRecord internal messageRecord; MessageRecord internal messageRecord;
...@@ -43,6 +46,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -43,6 +46,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
* Gas Metering Constants * * Gas Metering Constants *
**************************/ **************************/
address constant GAS_METADATA_ADDRESS = 0x06a506A506a506A506a506a506A506A506A506A5;
uint256 constant NUISANCE_GAS_SLOAD = 20000; uint256 constant NUISANCE_GAS_SLOAD = 20000;
uint256 constant NUISANCE_GAS_SSTORE = 20000; uint256 constant NUISANCE_GAS_SSTORE = 20000;
uint256 constant NUISANCE_GAS_PER_CONTRACT_BYTE = 100; uint256 constant NUISANCE_GAS_PER_CONTRACT_BYTE = 100;
...@@ -75,14 +79,24 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -75,14 +79,24 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
modifier netGasCost( modifier netGasCost(
uint256 _cost uint256 _cost
) { ) {
uint256 preExecutionGas = gasleft(); uint256 gasProvided = gasleft();
_; _;
uint256 postExecutionGas = gasleft(); uint256 gasUsed = gasProvided - gasleft();
// We want to refund everything *except* the specified cost. // We want to refund everything *except* the specified cost.
transactionRecord.ovmGasRefund += ( if (_cost < gasUsed) {
(preExecutionGas - postExecutionGas) - _cost transactionRecord.ovmGasRefund += gasUsed - _cost;
); }
}
/**
* Makes sure we're not inside a static context.
*/
modifier notStatic() {
if (messageContext.isStatic == true) {
_revertWithFlag(RevertFlag.STATIC_VIOLATION);
}
_;
} }
...@@ -102,7 +116,36 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -102,7 +116,36 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
override override
public 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.queueOrigin) == 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.queueOrigin);
// Wipe the execution context.
_resetContext();
} }
...@@ -117,6 +160,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -117,6 +160,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
function ovmCALLER() function ovmCALLER()
override override
public public
view
returns ( returns (
address _CALLER address _CALLER
) )
...@@ -131,6 +175,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -131,6 +175,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
function ovmADDRESS() function ovmADDRESS()
override override
public public
view
returns ( returns (
address _ADDRESS address _ADDRESS
) )
...@@ -145,6 +190,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -145,6 +190,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
function ovmORIGIN() function ovmORIGIN()
override override
public public
view
returns ( returns (
address _ORIGIN address _ORIGIN
) )
...@@ -159,6 +205,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -159,6 +205,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
function ovmTIMESTAMP() function ovmTIMESTAMP()
override override
public public
view
returns ( returns (
uint256 _TIMESTAMP uint256 _TIMESTAMP
) )
...@@ -173,6 +220,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -173,6 +220,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
function ovmGASLIMIT() function ovmGASLIMIT()
override override
public public
view
returns ( returns (
uint256 _GASLIMIT uint256 _GASLIMIT
) )
...@@ -187,6 +235,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -187,6 +235,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
function ovmCHAINID() function ovmCHAINID()
override override
public public
view
returns ( returns (
uint256 _CHAINID uint256 _CHAINID
) )
...@@ -227,6 +276,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -227,6 +276,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
) )
override override
public public
notStatic
netGasCost(40000 + _bytecode.length * 100) netGasCost(40000 + _bytecode.length * 100)
returns ( returns (
address _contract address _contract
...@@ -241,12 +291,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -241,12 +291,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
_getAccountNonce(creator) _getAccountNonce(creator)
); );
_createContract( return _createContract(
contractAddress, contractAddress,
_bytecode _bytecode
); );
return contractAddress;
} }
/** /**
...@@ -261,6 +309,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -261,6 +309,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
) )
override override
public public
notStatic
netGasCost(40000 + _bytecode.length * 100) netGasCost(40000 + _bytecode.length * 100)
returns ( returns (
address _contract address _contract
...@@ -276,12 +325,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -276,12 +325,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
_salt _salt
); );
_createContract( return _createContract(
contractAddress, contractAddress,
_bytecode _bytecode
); );
return contractAddress;
} }
...@@ -339,6 +386,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -339,6 +386,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
) )
override override
public public
notStatic
{ {
// Recover the EOA address from the message hash and signature parameters. Since we do the // 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 // hashing in advance, we don't have handle different message hashing schemes. Even if this
...@@ -359,7 +407,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -359,7 +407,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
} }
// If the user already has an EOA account, then there's no need to perform this operation. // If the user already has an EOA account, then there's no need to perform this operation.
if (_hasAccount(eoa) == true) { if (_hasEmptyAccount(eoa) == false) {
return; return;
} }
...@@ -409,12 +457,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -409,12 +457,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
MessageContext memory nextMessageContext = messageContext; MessageContext memory nextMessageContext = messageContext;
nextMessageContext.ovmCALLER = nextMessageContext.ovmADDRESS; nextMessageContext.ovmCALLER = nextMessageContext.ovmADDRESS;
nextMessageContext.ovmADDRESS = _address; nextMessageContext.ovmADDRESS = _address;
nextMessageContext.isCreation = false;
bool isStaticEntrypoint = false;
return _callContract( return _callContract(
nextMessageContext, nextMessageContext,
_gasLimit, _gasLimit,
_address, _address,
_calldata _calldata,
isStaticEntrypoint
); );
} }
...@@ -444,12 +495,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -444,12 +495,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
nextMessageContext.ovmCALLER = nextMessageContext.ovmADDRESS; nextMessageContext.ovmCALLER = nextMessageContext.ovmADDRESS;
nextMessageContext.ovmADDRESS = _address; nextMessageContext.ovmADDRESS = _address;
nextMessageContext.isStatic = true; nextMessageContext.isStatic = true;
nextMessageContext.isCreation = false;
bool isStaticEntrypoint = true;
return _callContract( return _callContract(
nextMessageContext, nextMessageContext,
_gasLimit, _gasLimit,
_address, _address,
_calldata _calldata,
isStaticEntrypoint
); );
} }
...@@ -476,12 +530,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -476,12 +530,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
{ {
// DELEGATECALL does not change anything about the message context. // DELEGATECALL does not change anything about the message context.
MessageContext memory nextMessageContext = messageContext; MessageContext memory nextMessageContext = messageContext;
nextMessageContext.isCreation = false;
bool isStaticEntrypoint = false;
return _callContract( return _callContract(
nextMessageContext, nextMessageContext,
_gasLimit, _gasLimit,
_address, _address,
_calldata _calldata,
isStaticEntrypoint
); );
} }
...@@ -525,6 +582,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -525,6 +582,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
) )
override override
public public
notStatic
netGasCost(60000) netGasCost(60000)
{ {
// We always SSTORE to the storage of ADDRESS. // We always SSTORE to the storage of ADDRESS.
...@@ -570,7 +628,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -570,7 +628,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
return Lib_EthUtils.getCode( return Lib_EthUtils.getCode(
_getAccountEthAddress(_contract), _getAccountEthAddress(_contract),
_offset, _offset,
_length length
); );
} }
...@@ -617,7 +675,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -617,7 +675,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
* Public Functions: Execution Safety * * Public Functions: Execution Safety *
**************************************/ **************************************/
/** /**
* Performs the logic to create a contract and revert under various potential conditions. * 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 * @dev This function is implemented as `public` because we need to be able to revert a
...@@ -643,6 +700,14 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -643,6 +700,14 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
return; 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. // Check the creation bytecode against the OVM_SafetyChecker.
if (ovmSafetyChecker.isBytecodeSafe(_bytecode) == false) { if (ovmSafetyChecker.isBytecodeSafe(_bytecode) == false) {
_revertWithFlag(RevertFlag.UNSAFE_BYTECODE); _revertWithFlag(RevertFlag.UNSAFE_BYTECODE);
...@@ -651,12 +716,27 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -651,12 +716,27 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
// We always need to initialize the contract with the default account values. // We always need to initialize the contract with the default account values.
_initPendingAccount(_address); _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 // 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. // 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) // 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. // when we know that we're inside a contract's creation code.
address ethAddress = Lib_EthUtils.createContract(_bytecode); 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 // 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 // we're out of creation code again, we can just revert normally while passing the flag
// through the revert data. // through the revert data.
...@@ -689,24 +769,29 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -689,24 +769,29 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
* Creates a new contract and associates it with some contract address. * Creates a new contract and associates it with some contract address.
* @param _contractAddress Address to associate the created contract with. * @param _contractAddress Address to associate the created contract with.
* @param _bytecode Bytecode to be used to create the contract. * @param _bytecode Bytecode to be used to create the contract.
* @return _created Final OVM contract address.
*/ */
function _createContract( function _createContract(
address _contractAddress, address _contractAddress,
bytes memory _bytecode bytes memory _bytecode
) )
internal internal
returns (
address _created
)
{ {
// We always update the nonce of the creating account, even if the creation fails. // We always update the nonce of the creating account, even if the creation fails.
_setAccountNonce(ovmADDRESS(), 1); _setAccountNonce(ovmADDRESS(), 1);
// We're stepping into a CREATE or CREATE2, so we need to update ADDRESS to point // We're stepping into a CREATE or CREATE2, so we need to update ADDRESS to point
// to the contract's associated address. // to the contract's associated address and CALLER to point to the previous ADDRESS.
MessageContext memory nextMessageContext = messageContext; MessageContext memory nextMessageContext = messageContext;
nextMessageContext.ovmCALLER = messageContext.ovmADDRESS;
nextMessageContext.ovmADDRESS = _contractAddress; nextMessageContext.ovmADDRESS = _contractAddress;
// Run `safeCREATE` in a new EVM message so that our changes can be reflected even if // Run `safeCREATE` in a new EVM message so that our changes can be reflected even if
// `safeCREATE` reverts. // `safeCREATE` reverts.
_handleExternalInteraction( (bool _success, ) = _handleExternalInteraction(
nextMessageContext, nextMessageContext,
gasleft(), gasleft(),
address(this), address(this),
...@@ -714,12 +799,16 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -714,12 +799,16 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
"safeCREATE(address,bytes)", "safeCREATE(address,bytes)",
_contractAddress, _contractAddress,
_bytecode _bytecode
) ),
false
); );
// Need to make sure that this flag is reset so that it isn't propagated to creations in // Need to make sure that this flag is reset so that it isn't propagated to creations in
// some parent EVM message. // some parent EVM message.
messageRecord.revertFlag = RevertFlag.DID_NOT_REVERT; messageRecord.revertFlag = RevertFlag.DID_NOT_REVERT;
// Yellow paper requires that address returned is zero if the contract deployment fails.
return _success ? _contractAddress : address(0);
} }
/** /**
...@@ -728,6 +817,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -728,6 +817,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
* @param _gasLimit Amount of gas to be passed into this call. * @param _gasLimit Amount of gas to be passed into this call.
* @param _contract Address used to resolve the deployed contract. * @param _contract Address used to resolve the deployed contract.
* @param _calldata Data to send along with the call. * @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 _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call. * @return _returndata Data returned by the call.
*/ */
...@@ -735,7 +825,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -735,7 +825,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
MessageContext memory _nextMessageContext, MessageContext memory _nextMessageContext,
uint256 _gasLimit, uint256 _gasLimit,
address _contract, address _contract,
bytes memory _calldata bytes memory _calldata,
bool _isStaticEntrypoint
) )
internal internal
returns ( returns (
...@@ -747,7 +838,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -747,7 +838,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
_nextMessageContext, _nextMessageContext,
_gasLimit, _gasLimit,
_getAccountEthAddress(_contract), _getAccountEthAddress(_contract),
_calldata _calldata,
_isStaticEntrypoint
); );
} }
...@@ -757,6 +849,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -757,6 +849,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
* @param _gasLimit Amount of gas to be passed into this call. * @param _gasLimit Amount of gas to be passed into this call.
* @param _target Address of the contract to call. * @param _target Address of the contract to call.
* @param _data Data to send along with the 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 _success Whether or not the call returned (rather than reverted).
* @return _returndata Data returned by the call. * @return _returndata Data returned by the call.
*/ */
...@@ -764,7 +857,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -764,7 +857,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
MessageContext memory _nextMessageContext, MessageContext memory _nextMessageContext,
uint256 _gasLimit, uint256 _gasLimit,
address _target, address _target,
bytes memory _data bytes memory _data,
bool _isStaticEntrypoint
) )
internal internal
returns ( returns (
...@@ -791,6 +885,9 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -791,6 +885,9 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
// existence. // existence.
(bool success, bytes memory returndata) = _target.call{gas: _gasLimit}(_data); (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 // Assuming there were no reverts, the message record should be accurate here. We'll update
// this value in the case of a revert. // this value in the case of a revert.
uint256 nuisanceGasLeft = messageRecord.nuisanceGasLeft; uint256 nuisanceGasLeft = messageRecord.nuisanceGasLeft;
...@@ -802,6 +899,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -802,6 +899,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
RevertFlag flag, RevertFlag flag,
uint256 nuisanceGasLeftPostRevert, uint256 nuisanceGasLeftPostRevert,
uint256 ovmGasRefund, uint256 ovmGasRefund,
bytes memory returndataFromFlag
) = _decodeRevertData(returndata); ) = _decodeRevertData(returndata);
// INVALID_STATE_ACCESS is the only flag that triggers an immediate abort of the // INVALID_STATE_ACCESS is the only flag that triggers an immediate abort of the
...@@ -811,6 +909,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -811,6 +909,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
_revertWithFlag(flag); _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 // 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 // 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). // gas refund reported by the call (enforced by safety checking).
...@@ -821,6 +928,14 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -821,6 +928,14 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
transactionRecord.ovmGasRefund = ovmGasRefund; 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. // 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 // 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 // to zero. OUT_OF_GAS is a "pseudo" flag given that messages return no data when they
...@@ -832,9 +947,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -832,9 +947,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
// We need to reset the nuisance gas back to its original value minus the amount used here. // We need to reset the nuisance gas back to its original value minus the amount used here.
messageRecord.nuisanceGasLeft = prevNuisanceGasLeft - (nuisanceGasLimit - nuisanceGasLeft); messageRecord.nuisanceGasLeft = prevNuisanceGasLeft - (nuisanceGasLimit - nuisanceGasLeft);
// Switch back to the original message context now that we're out of the call.
_switchMessageContext(_nextMessageContext, prevMessageContext);
return ( return (
success, success,
returndata returndata
...@@ -863,6 +975,23 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -863,6 +975,23 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
return ovmStateManager.hasAccount(_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. * Sets the nonce of an account.
* @param _address Address of the account to modify. * @param _address Address of the account to modify.
...@@ -921,7 +1050,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -921,7 +1050,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
) )
internal internal
{ {
_checkAccountChange(_address); // 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); ovmStateManager.initPendingAccount(_address);
} }
...@@ -994,11 +1126,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -994,11 +1126,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
) )
internal internal
{ {
// We need to make sure that the transaction isn't trying to access an account that hasn't // See `_checkContractStorageLoad` for more information.
// been provided to the OVM_StateManager. We'll immediately abort if this is the case. if (gasleft() < MIN_GAS_FOR_INVALID_STATE_ACCESS) {
_checkInvalidStateAccess( _revertWithFlag(RevertFlag.OUT_OF_GAS);
ovmStateManager.hasAccount(_address) }
);
// 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 // 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. // this because "nuisance gas" only applies to the first time that an account is loaded.
...@@ -1034,6 +1170,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -1034,6 +1170,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
// If we hadn't already changed the account, then we'll need to charge "nuisance gas" based // If we hadn't already changed the account, then we'll need to charge "nuisance gas" based
// on the size of the contract code. // on the size of the contract code.
if (_wasAccountAlreadyChanged == false) { if (_wasAccountAlreadyChanged == false) {
ovmStateManager.incrementTotalUncommittedAccounts();
_useNuisanceGas( _useNuisanceGas(
Lib_EthUtils.getCodeSize(_getAccountEthAddress(_address)) * NUISANCE_GAS_PER_CONTRACT_BYTE Lib_EthUtils.getCodeSize(_getAccountEthAddress(_address)) * NUISANCE_GAS_PER_CONTRACT_BYTE
); );
...@@ -1052,11 +1189,22 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -1052,11 +1189,22 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
) )
internal 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 // 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. // been provided to the OVM_StateManager. We'll immediately abort if this is the case.
_checkInvalidStateAccess( // We know that we have enough gas to do this check because of the above test.
ovmStateManager.hasContractStorage(_contract, _key) 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 // 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. // this because "nuisance gas" only applies to the first time that a slot is loaded.
...@@ -1083,6 +1231,16 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -1083,6 +1231,16 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
) )
internal 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 // 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. // this because "nuisance gas" only applies to the first time that a slot is changed.
( (
...@@ -1092,6 +1250,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -1092,6 +1250,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
// If we hadn't already changed the account, then we'll need to charge some fixed amount of // If we hadn't already changed the account, then we'll need to charge some fixed amount of
// "nuisance gas". // "nuisance gas".
if (_wasContractStorageAlreadyChanged == false) { if (_wasContractStorageAlreadyChanged == false) {
ovmStateManager.incrementTotalUncommittedContractStorage();
_useNuisanceGas(NUISANCE_GAS_SSTORE); _useNuisanceGas(NUISANCE_GAS_SSTORE);
} }
} }
...@@ -1121,6 +1280,16 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -1121,6 +1280,16 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
return bytes(''); 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. // Just ABI encode the rest of the parameters.
return abi.encode( return abi.encode(
_flag, _flag,
...@@ -1179,7 +1348,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -1179,7 +1348,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
// *single* byte, something the OVM_ExecutionManager will not return in any other case. // *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 // We're thereby allowed to communicate failure without allowing contracts to trick us into
// thinking there was a failure. // thinking there was a failure.
if (_inCreationContext()) { if (messageContext.isCreation) {
messageRecord.revertFlag = _flag; messageRecord.revertFlag = _flag;
assembly { assembly {
...@@ -1211,30 +1380,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -1211,30 +1380,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
_revertWithFlag(_flag, bytes('')); _revertWithFlag(_flag, bytes(''));
} }
/**
* Checks for an attempt to access some inaccessible state.
* @param _condition Result of some function that checks for bad access.
*/
function _checkInvalidStateAccess(
bool _condition
)
internal
{
// Another case of hidden complexity. If we didn't enforce this requirement, then a
// contract could pass in just enough gas to cause this 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 as to generally disincentivize this attack.
if (gasleft() < MIN_GAS_FOR_INVALID_STATE_ACCESS) {
_revertWithFlag(RevertFlag.OUT_OF_GAS);
}
// We have enough gas to comfortably run this revert, so do it.
if (_condition == false) {
_revertWithFlag(RevertFlag.INVALID_STATE_ACCESS);
}
}
/****************************************** /******************************************
* Internal Functions: Nuisance Gas Logic * * Internal Functions: Nuisance Gas Logic *
...@@ -1279,6 +1424,155 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -1279,6 +1424,155 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
} }
/************************************
* 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,
uint256 _queueOrigin
)
internal
returns (
bool _valid
)
{
if (_gasLimit > gasMeterConfig.maxTransactionGasLimit) {
return false;
}
if (_gasLimit < gasMeterConfig.minTransactionGasLimit) {
return false;
}
GasMetadataKey cumulativeGasKey;
GasMetadataKey prevEpochGasKey;
if (_queueOrigin == uint256(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,
uint256 _queueOrigin
)
internal
{
GasMetadataKey cumulativeGasKey;
if (_queueOrigin == uint256(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 * * Internal Functions: Execution Context *
*****************************************/ *****************************************/
...@@ -1308,23 +1602,47 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager { ...@@ -1308,23 +1602,47 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager {
if (_prevMessageContext.isStatic != _nextMessageContext.isStatic) { if (_prevMessageContext.isStatic != _nextMessageContext.isStatic) {
messageContext.isStatic = _nextMessageContext.isStatic; messageContext.isStatic = _nextMessageContext.isStatic;
} }
// Avoid unnecessary the SSTORE.
if (_prevMessageContext.isCreation != _nextMessageContext.isCreation) {
messageContext.isCreation = _nextMessageContext.isCreation;
}
} }
/** /**
* Checks whether we're inside contract creation code. * Initializes the execution context.
* @return _inCreation Whether or not we're in a contract creation. * @param _transaction OVM transaction being executed.
*/ */
function _inCreationContext() function _initContext(
internal Lib_OVMCodec.Transaction memory _transaction
returns (
bool _inCreation
) )
internal
{ {
// An interesting "hack" of sorts. Since the contract doesn't exist yet, it won't have any transactionContext.ovmTIMESTAMP = _transaction.timestamp;
// stored contract code. A simple-but-elegant way to detect this condition. transactionContext.ovmTXGASLIMIT = _transaction.gasLimit;
return ( transactionContext.ovmQUEUEORIGIN = _transaction.queueOrigin;
ovmADDRESS() != address(0) transactionContext.ovmGASLIMIT = gasMeterConfig.maxGasPerQueuePerEpoch;
&& ovmEXTCODESIZE(ovmADDRESS()) == 0 }
);
/**
* Resets the transaction and message context.
*/
function _resetContext()
internal
{
transactionContext.ovmORIGIN = address(0);
transactionContext.ovmTIMESTAMP = 0;
transactionContext.ovmGASLIMIT = 0;
transactionContext.ovmTXGASLIMIT = 0;
transactionContext.ovmQUEUEORIGIN = 0;
transactionRecord.ovmGasRefund = 0;
messageContext.ovmCALLER = address(0);
messageContext.ovmADDRESS = address(0);
messageContext.isStatic = false;
messageRecord.nuisanceGasLeft = 0;
messageRecord.revertFlag = RevertFlag.DID_NOT_REVERT;
} }
} }
...@@ -13,6 +13,22 @@ import { iOVM_StateManager } from "../../iOVM/execution/iOVM_StateManager.sol"; ...@@ -13,6 +13,22 @@ import { iOVM_StateManager } from "../../iOVM/execution/iOVM_StateManager.sol";
*/ */
contract OVM_StateManager is iOVM_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 * * Contract Variables: Internal Storage *
****************************************/ ****************************************/
...@@ -21,6 +37,58 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -21,6 +37,58 @@ contract OVM_StateManager is iOVM_StateManager {
mapping (address => mapping (bytes32 => bytes32)) internal contractStorage; mapping (address => mapping (bytes32 => bytes32)) internal contractStorage;
mapping (address => mapping (bytes32 => bool)) internal verifiedContractStorage; mapping (address => mapping (bytes32 => bool)) internal verifiedContractStorage;
mapping (bytes32 => ItemState) internal itemStates; 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;
}
/************************************ /************************************
...@@ -38,6 +106,7 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -38,6 +106,7 @@ contract OVM_StateManager is iOVM_StateManager {
) )
override override
public public
authenticated
{ {
accounts[_address] = _account; accounts[_address] = _account;
} }
...@@ -50,6 +119,7 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -50,6 +119,7 @@ contract OVM_StateManager is iOVM_StateManager {
function getAccount(address _address) function getAccount(address _address)
override override
public public
view
returns ( returns (
Lib_OVMCodec.Account memory _account Lib_OVMCodec.Account memory _account
) )
...@@ -67,6 +137,7 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -67,6 +137,7 @@ contract OVM_StateManager is iOVM_StateManager {
) )
override override
public public
view
returns ( returns (
bool _exists bool _exists
) )
...@@ -74,6 +145,24 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -74,6 +145,24 @@ contract OVM_StateManager is iOVM_StateManager {
return accounts[_address].codeHash != bytes32(0); 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. * Sets the nonce of an account.
* @param _address Address of the account to modify. * @param _address Address of the account to modify.
...@@ -85,6 +174,7 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -85,6 +174,7 @@ contract OVM_StateManager is iOVM_StateManager {
) )
override override
public public
authenticated
{ {
accounts[_address].nonce = _nonce; accounts[_address].nonce = _nonce;
} }
...@@ -99,6 +189,7 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -99,6 +189,7 @@ contract OVM_StateManager is iOVM_StateManager {
) )
override override
public public
view
returns ( returns (
uint256 _nonce uint256 _nonce
) )
...@@ -116,6 +207,7 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -116,6 +207,7 @@ contract OVM_StateManager is iOVM_StateManager {
) )
override override
public public
view
returns ( returns (
address _ethAddress address _ethAddress
) )
...@@ -132,10 +224,12 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -132,10 +224,12 @@ contract OVM_StateManager is iOVM_StateManager {
) )
override override
public public
authenticated
{ {
Lib_OVMCodec.Account storage account = accounts[_address]; Lib_OVMCodec.Account storage account = accounts[_address];
account.nonce = 1; account.nonce = 1;
account.codeHash = keccak256(hex'80'); account.codeHash = keccak256(hex'80');
account.isFresh = true;
} }
/** /**
...@@ -151,6 +245,7 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -151,6 +245,7 @@ contract OVM_StateManager is iOVM_StateManager {
) )
override override
public public
authenticated
{ {
Lib_OVMCodec.Account storage account = accounts[_address]; Lib_OVMCodec.Account storage account = accounts[_address];
account.ethAddress = _ethAddress; account.ethAddress = _ethAddress;
...@@ -167,11 +262,12 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -167,11 +262,12 @@ contract OVM_StateManager is iOVM_StateManager {
) )
override override
public public
authenticated
returns ( returns (
bool _wasAccountAlreadyLoaded bool _wasAccountAlreadyLoaded
) )
{ {
return _testItemState( return _testAndSetItemState(
keccak256(abi.encodePacked(_address)), keccak256(abi.encodePacked(_address)),
ItemState.ITEM_LOADED ItemState.ITEM_LOADED
); );
...@@ -187,16 +283,69 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -187,16 +283,69 @@ contract OVM_StateManager is iOVM_StateManager {
) )
override override
public public
authenticated
returns ( returns (
bool _wasAccountAlreadyChanged bool _wasAccountAlreadyChanged
) )
{ {
return _testItemState( return _testAndSetItemState(
keccak256(abi.encodePacked(_address)), keccak256(abi.encodePacked(_address)),
ItemState.ITEM_CHANGED 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 * * Public Functions: Storage Access *
...@@ -215,10 +364,25 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -215,10 +364,25 @@ contract OVM_StateManager is iOVM_StateManager {
) )
override override
public public
authenticated
{ {
contractStorage[_contract][_key] = _value; // 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
&& accounts[_contract].isFresh == false
) {
verifiedContractStorage[_contract][_key] = true; verifiedContractStorage[_contract][_key] = true;
} }
}
/** /**
* Retrieves a contract storage slot value. * Retrieves a contract storage slot value.
...@@ -232,11 +396,13 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -232,11 +396,13 @@ contract OVM_StateManager is iOVM_StateManager {
) )
override override
public public
view
returns ( returns (
bytes32 _value bytes32 _value
) )
{ {
return contractStorage[_contract][_key]; // See `putContractStorage` for more information about the XOR here.
return contractStorage[_contract][_key] ^ STORAGE_XOR_VALUE;
} }
/** /**
...@@ -251,11 +417,12 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -251,11 +417,12 @@ contract OVM_StateManager is iOVM_StateManager {
) )
override override
public public
view
returns ( returns (
bool _exists bool _exists
) )
{ {
return verifiedContractStorage[_contract][_key]; return verifiedContractStorage[_contract][_key] || accounts[_contract].isFresh;
} }
/** /**
...@@ -270,11 +437,12 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -270,11 +437,12 @@ contract OVM_StateManager is iOVM_StateManager {
) )
override override
public public
authenticated
returns ( returns (
bool _wasContractStorageAlreadyLoaded bool _wasContractStorageAlreadyLoaded
) )
{ {
return _testItemState( return _testAndSetItemState(
keccak256(abi.encodePacked(_contract, _key)), keccak256(abi.encodePacked(_contract, _key)),
ItemState.ITEM_LOADED ItemState.ITEM_LOADED
); );
...@@ -292,16 +460,71 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -292,16 +460,71 @@ contract OVM_StateManager is iOVM_StateManager {
) )
override override
public public
authenticated
returns ( returns (
bool _wasContractStorageAlreadyChanged bool _wasContractStorageAlreadyChanged
) )
{ {
return _testItemState( return _testAndSetItemState(
keccak256(abi.encodePacked(_contract, _key)), keccak256(abi.encodePacked(_contract, _key)),
ItemState.ITEM_CHANGED 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 * * Internal Functions *
...@@ -314,7 +537,7 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -314,7 +537,7 @@ contract OVM_StateManager is iOVM_StateManager {
* @param _minItemState Minumum state that must be satisfied by the item. * @param _minItemState Minumum state that must be satisfied by the item.
* @return _wasItemState Whether or not the item was already in the state. * @return _wasItemState Whether or not the item was already in the state.
*/ */
function _testItemState( function _testAndSetItemState(
bytes32 _item, bytes32 _item,
ItemState _minItemState ItemState _minItemState
) )
...@@ -323,8 +546,7 @@ contract OVM_StateManager is iOVM_StateManager { ...@@ -323,8 +546,7 @@ contract OVM_StateManager is iOVM_StateManager {
bool _wasItemState bool _wasItemState
) )
{ {
ItemState itemState = itemStates[_item]; bool wasItemState = itemStates[_item] >= _minItemState;
bool wasItemState = itemState >= _minItemState;
if (wasItemState == false) { if (wasItemState == false) {
itemStates[_item] = _minItemState; itemStates[_item] = _minItemState;
......
// 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;
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;
/* Proxy Imports */
import { Proxy_Resolver } from "../../proxy/Proxy_Resolver.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.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, Proxy_Resolver {
/*******************************************
* Contract Variables: Contract References *
*******************************************/
address internal ovmCanonicalTransactionChain;
/***************
* Constructor *
***************/
/**
* @param _proxyManager Address of the Proxy_Manager.
*/
constructor(
address _proxyManager
)
Proxy_Resolver(_proxyManager)
{
ovmCanonicalTransactionChain = resolve("OVM_CanonicalTransactionChain");
}
/****************************************
* Public Functions: Queue Manipulation *
****************************************/
/**
* Adds an element to the queue.
* @param _element Queue element to add to the queue.
*/
function enqueue(
Lib_OVMCodec.QueueElement memory _element
)
override
public
{
_enqueue(_element);
}
/**
* Pops an element from the queue.
* @return _element Queue element popped from the queue.
*/
function dequeue()
override
public
returns (
Lib_OVMCodec.QueueElement memory _element
)
{
require(
msg.sender == ovmCanonicalTransactionChain,
"Sender is not allowed to enqueue."
);
return _dequeue();
}
}
// SPDX-License-Identifier: UNLICENSED
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";
/* 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, Proxy_Resolver {
/*******************************************
* Contract Variables: Contract References *
*******************************************/
iOVM_StateCommitmentChain internal ovmStateCommitmentChain;
iOVM_CanonicalTransactionChain internal ovmCanonicalTransactionChain;
/*******************************************
* Contract Variables: Internal Accounting *
*******************************************/
mapping (bytes32 => iOVM_StateTransitioner) internal transitioners;
/***************
* Constructor *
***************/
/**
* @param _proxyManager Address of the Proxy_Manager.
*/
constructor(
address _proxyManager
)
Proxy_Resolver(_proxyManager)
{
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(proxyManager),
_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;
/* Proxy Imports */
import { Proxy_Resolver } from "../../proxy/Proxy_Resolver.sol";
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_EthUtils } from "../../libraries/utils/Lib_EthUtils.sol";
import { Lib_EthMerkleTrie } from "../../libraries/trie/Lib_EthMerkleTrie.sol";
/* Interface Imports */
import { iOVM_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, Proxy_Resolver {
/*******************
* 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 _proxyManager Address of the Proxy_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,
uint256 _stateTransitionIndex,
bytes32 _preStateRoot,
bytes32 _transactionHash
)
Proxy_Resolver(_proxyManager)
{
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(resolveTarget("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 _proxyManager Address of the Proxy_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,
uint256 _stateTransitionIndex,
bytes32 _preStateRoot,
bytes32 _transactionHash
)
override
public
returns (
iOVM_StateTransitioner _ovmStateTransitioner
)
{
return new OVM_StateTransitioner(
_proxyManager,
_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_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;
}
...@@ -6,9 +6,9 @@ pragma experimental ABIEncoderV2; ...@@ -6,9 +6,9 @@ pragma experimental ABIEncoderV2;
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol"; import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
interface iOVM_ExecutionManager { interface iOVM_ExecutionManager {
/******************* /**********
* Data Structures * * Enums *
*******************/ *********/
enum RevertFlag { enum RevertFlag {
DID_NOT_REVERT, DID_NOT_REVERT,
...@@ -16,7 +16,35 @@ interface iOVM_ExecutionManager { ...@@ -16,7 +16,35 @@ interface iOVM_ExecutionManager {
INTENTIONAL_REVERT, INTENTIONAL_REVERT,
EXCEEDS_NUISANCE_GAS, EXCEEDS_NUISANCE_GAS,
INVALID_STATE_ACCESS, INVALID_STATE_ACCESS,
UNSAFE_BYTECODE 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
}
enum QueueOrigin {
SEQUENCER_QUEUE,
L1TOL2_QUEUE
}
/***********
* Structs *
***********/
struct GasMeterConfig {
uint256 minTransactionGasLimit;
uint256 maxTransactionGasLimit;
uint256 maxGasPerQueuePerEpoch;
uint256 secondsPerEpoch;
} }
struct GlobalContext { struct GlobalContext {
...@@ -39,6 +67,7 @@ interface iOVM_ExecutionManager { ...@@ -39,6 +67,7 @@ interface iOVM_ExecutionManager {
address ovmCALLER; address ovmCALLER;
address ovmADDRESS; address ovmADDRESS;
bool isStatic; bool isStatic;
bool isCreation;
} }
struct MessageRecord { struct MessageRecord {
...@@ -61,12 +90,12 @@ interface iOVM_ExecutionManager { ...@@ -61,12 +90,12 @@ interface iOVM_ExecutionManager {
* Context Opcodes * * Context Opcodes *
*******************/ *******************/
function ovmCALLER() external returns (address _caller); function ovmCALLER() external view returns (address _caller);
function ovmADDRESS() external returns (address _address); function ovmADDRESS() external view returns (address _address);
function ovmORIGIN() external returns (address _origin); function ovmORIGIN() external view returns (address _origin);
function ovmTIMESTAMP() external returns (uint256 _timestamp); function ovmTIMESTAMP() external view returns (uint256 _timestamp);
function ovmGASLIMIT() external returns (uint256 _gasLimit); function ovmGASLIMIT() external view returns (uint256 _gasLimit);
function ovmCHAINID() external returns (uint256 _chainId); function ovmCHAINID() external view returns (uint256 _chainId);
/******************* /*******************
......
...@@ -17,24 +17,36 @@ interface iOVM_StateManager { ...@@ -17,24 +17,36 @@ interface iOVM_StateManager {
enum ItemState { enum ItemState {
ITEM_UNTOUCHED, ITEM_UNTOUCHED,
ITEM_LOADED, ITEM_LOADED,
ITEM_CHANGED ITEM_CHANGED,
ITEM_COMMITTED
} }
/***************************
* Public Functions: Setup *
***************************/
function setExecutionManager(address _ovmExecutionManager) external;
/************************************ /************************************
* Public Functions: Account Access * * Public Functions: Account Access *
************************************/ ************************************/
function putAccount(address _address, Lib_OVMCodec.Account memory _account) external; function putAccount(address _address, Lib_OVMCodec.Account memory _account) external;
function getAccount(address _address) external returns (Lib_OVMCodec.Account memory _account); function getAccount(address _address) external view returns (Lib_OVMCodec.Account memory _account);
function hasAccount(address _address) external returns (bool _exists); 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 setAccountNonce(address _address, uint256 _nonce) external;
function getAccountNonce(address _address) external returns (uint256 _nonce); function getAccountNonce(address _address) external view returns (uint256 _nonce);
function getAccountEthAddress(address _address) external returns (address _ethAddress); function getAccountEthAddress(address _address) external view returns (address _ethAddress);
function initPendingAccount(address _address) external; function initPendingAccount(address _address) external;
function commitPendingAccount(address _address, address _ethAddress, bytes32 _codeHash) external; function commitPendingAccount(address _address, address _ethAddress, bytes32 _codeHash) external;
function testAndSetAccountLoaded(address _address) external returns (bool _wasAccountAlreadyLoaded); function testAndSetAccountLoaded(address _address) external returns (bool _wasAccountAlreadyLoaded);
function testAndSetAccountChanged(address _address) external returns (bool _wasAccountAlreadyChanged); 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);
/************************************ /************************************
...@@ -42,8 +54,11 @@ interface iOVM_StateManager { ...@@ -42,8 +54,11 @@ interface iOVM_StateManager {
************************************/ ************************************/
function putContractStorage(address _contract, bytes32 _key, bytes32 _value) external; function putContractStorage(address _contract, bytes32 _key, bytes32 _value) external;
function getContractStorage(address _contract, bytes32 _key) external returns (bytes32 _value); function getContractStorage(address _contract, bytes32 _key) external view returns (bytes32 _value);
function hasContractStorage(address _contract, bytes32 _key) external returns (bool _exists); function hasContractStorage(address _contract, bytes32 _key) external returns (bool _exists);
function testAndSetContractStorageLoaded(address _contract, bytes32 _key) external returns (bool _wasContractStorageAlreadyLoaded); function testAndSetContractStorageLoaded(address _contract, bytes32 _key) external returns (bool _wasContractStorageAlreadyLoaded);
function testAndSetContractStorageChanged(address _contract, bytes32 _key) external returns (bool _wasContractStorageAlreadyChanged); 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;
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(Lib_OVMCodec.QueueElement memory _element) external;
function dequeue() external 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_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
);
}
...@@ -20,6 +20,14 @@ library Lib_OVMCodec { ...@@ -20,6 +20,14 @@ library Lib_OVMCodec {
bytes32 storageRoot; bytes32 storageRoot;
bytes32 codeHash; bytes32 codeHash;
address ethAddress; address ethAddress;
bool isFresh;
}
struct EVMAccount {
uint256 nonce;
uint256 balance;
bytes32 storageRoot;
bytes32 codeHash;
} }
struct ChainBatchHeader { struct ChainBatchHeader {
...@@ -98,4 +106,46 @@ library Lib_OVMCodec { ...@@ -98,4 +106,46 @@ library Lib_OVMCodec {
data: Lib_RLPReader.toBytes(decoded[5]) 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.queueOrigin,
_transaction.entrypoint,
_transaction.origin,
_transaction.msgSender,
_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;
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;
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;
/* Proxy Imports */
import { Proxy_Manager } from "./Proxy_Manager.sol";
/**
* @title Proxy_Forwarder
*/
contract Proxy_Forwarder {
Proxy_Manager private proxyManager;
constructor(
address _proxyManager
) {
proxyManager = Proxy_Manager(_proxyManager);
}
fallback()
external
{
address target = _getTarget();
bytes memory data = msg.data;
require(
target != address(0),
"Proxy does not have a target."
);
assembly {
let success := call(
gas(),
target,
0,
add(data, 0x20),
mload(data),
0,
0
)
let size := returndatasize()
let returndata := mload(0x40)
mstore(0x40, add(returndata, add(size, 0x20)))
returndatacopy(add(returndata, 0x20), 0, size)
if iszero(success) {
revert(add(returndata, 0x20), size)
}
return(add(returndata, 0x20), size)
}
}
function _getTarget()
internal
view
returns (
address _target
)
{
address target;
if (proxyManager.isProxy(msg.sender)) {
target = proxyManager.getTarget(msg.sender);
} else if (proxyManager.hasProxy(msg.sender)) {
target = proxyManager.getProxy(msg.sender);
} else {
target = proxyManager.getTarget(address(this));
}
return target;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/**
* @title Proxy_Manager
*/
contract Proxy_Manager {
mapping (bytes32 => address) private proxyByName;
mapping (bytes32 => address) private targetByName;
mapping (address => bytes32) private nameByProxy;
mapping (address => bytes32) private nameByTarget;
function setProxy(
string memory _name,
address _proxy
)
public
{
proxyByName[_getNameHash(_name)] = _proxy;
nameByProxy[_proxy] = _getNameHash(_name);
}
function getProxy(
string memory _name
)
public
view
returns (
address _proxy
)
{
return proxyByName[_getNameHash(_name)];
}
function getProxy(
address _target
)
public
view
returns (
address _proxy
)
{
return proxyByName[nameByTarget[_target]];
}
function hasProxy(
string memory _name
)
public
view
returns (
bool _hasProxy
)
{
return getProxy(_name) != address(0);
}
function hasProxy(
address _target
)
public
view
returns (
bool _hasProxy
)
{
return getProxy(_target) != address(0);
}
function isProxy(
address _proxy
)
public
view
returns (
bool _isProxy
)
{
return nameByProxy[_proxy] != bytes32('');
}
function setTarget(
string memory _name,
address _target
)
public
{
targetByName[_getNameHash(_name)] = _target;
nameByTarget[_target] = _getNameHash(_name);
}
function getTarget(
string memory _name
)
public
view
returns (
address _target
)
{
return targetByName[_getNameHash(_name)];
}
function getTarget(
address _proxy
)
public
view
returns (
address _target
)
{
return targetByName[nameByProxy[_proxy]];
}
function hasTarget(
string memory _name
)
public
view
returns (
bool _hasTarget
)
{
return getTarget(_name) != address(0);
}
function hasTarget(
address _proxy
)
public
view
returns (
bool _hasTarget
)
{
return getTarget(_proxy) != address(0);
}
function isTarget(
address _target
)
public
view
returns (
bool _isTarget
)
{
return nameByTarget[_target] != bytes32('');
}
function _getNameHash(
string memory _name
)
internal
pure
returns (
bytes32 _hash
)
{
return keccak256(abi.encodePacked(_name));
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
/* Proxy Imports */
import { Proxy_Manager } from "./Proxy_Manager.sol";
/**
* @title Proxy_Resolver
*/
contract Proxy_Resolver {
Proxy_Manager internal proxyManager;
constructor(
address _proxyManager
) {
proxyManager = Proxy_Manager(_proxyManager);
}
function resolve(
string memory _name
)
public
view
returns (
address _proxy
)
{
return proxyManager.getProxy(_name);
}
function resolveTarget(
string memory _name
)
public
view
returns (
address _target
)
{
return proxyManager.getTarget(_name);
}
}
pragma solidity >=0.7.0;
pragma experimental ABIEncoderV2;
// note: this pattern breaks if an action leads to reversion, since the result will not be stored.
// Thus, only the final action in deployedActions can be an ovmREVERT, and the result must be checked via the callee.
contract MockOvmCodeContract {
bytes[] public EMReturnValuesInConstructor;
bytes[][] public EMReturnValuesInDeployed;
bytes[][] public callDatasForEMInDeployed;
bytes[] public returnDataForCodeContractInDeployed;
uint public callIndex = 0;
bytes public constrRet0;
bytes public constrRet1;
bytes public constCall0;
constructor(bytes[] memory _callsToEMInConstructor, bytes[][] memory _calldatasToEMInDeployed, bytes[] memory _returnDataForCodeContractInDeployed) {
require(_calldatasToEMInDeployed.length == _returnDataForCodeContractInDeployed.length, "Invalid behavior requested for mock code contract: mismatch between number of calldata batches and returndata for post-deployment behavior.");
callDatasForEMInDeployed = _calldatasToEMInDeployed;
returnDataForCodeContractInDeployed = _returnDataForCodeContractInDeployed;
bytes[] memory callsToDoNow = _callsToEMInConstructor;
bytes[] memory returnVals = doEMCalls(callsToDoNow);
constCall0 = callsToDoNow[0];
constrRet0 = returnVals[0];
constrRet1 = returnVals[1];
for (uint i = 0; i < returnVals.length; i++) {
EMReturnValuesInConstructor.push(returnVals[i]);
}
}
fallback() external {
bytes[] memory calldatas = callDatasForEMInDeployed[callIndex];
bytes[] memory returndatas = doEMCalls(calldatas);
EMReturnValuesInDeployed.push();
for (uint i = 0; i < returndatas.length; i++) {
EMReturnValuesInDeployed[callIndex].push(returndatas[i]);
}
bytes memory dataToReturn = returnDataForCodeContractInDeployed[callIndex];
callIndex++;
uint returnLength = dataToReturn.length;
assembly {
return(add(dataToReturn, 0x20), returnLength)
}
}
function doEMCalls(bytes[] memory _calldatas) internal returns(bytes[] memory) {
bytes[] memory calldatas = _calldatas;
bytes[] memory results = new bytes[](calldatas.length);
for (uint i = 0; i < calldatas.length; i++) {
bytes memory data = calldatas[i];
bytes memory result = callExecutionManager(data);
results[i] = result;
}
return results;
}
function callExecutionManager (bytes memory _data) internal returns (bytes memory actionResult) {
uint dataLength = _data.length;
uint returnedLength;
assembly {
function isContextCREATE() -> isCREATE {
isCREATE := iszero(extcodesize(address()))
}
// Note: this function is the only way that the opcodes REVERT, CALLER, EXTCODESIZE, ADDRESS can appear in a code contract which passes SafetyChecker.isBytecodeSafe().
// The static analysis enforces that the EXACT functionality below is implemented by comparing to a reference bytestring.
function doSafeExecutionManagerCall(argOff, argLen, retOffset, retLen) {
let success := call(gas(), caller(), 0, argOff, argLen, retOffset, retLen)
if iszero(success) {
mstore(0, 0x2a2a7adb00000000000000000000000000000000000000000000000000000000) // ovmREVERT(bytes) methodId
returndatacopy(4, 0, 32)
let secondsuccess := call(gas(), caller(), 0, 0, 36, 0, 0)
if iszero(secondsuccess) {
returndatacopy(0, 0, 32)
revert(0, 32)
}
// ovmREVERT will only succeed if we are in a CREATE context, in which case we abort by deploying a single byte contract.
mstore(0,0)
return(0, 1)
}
}
doSafeExecutionManagerCall(add(_data, 0x20), dataLength, 0, 0)
returnedLength := returndatasize()
}
bytes memory returned = new bytes(returnedLength);
assembly {
returndatacopy(add(returned, 0x20), 0, returndatasize())
}
return returned;
}
}
\ No newline at end of file
pragma solidity >=0.7.0;
pragma experimental ABIEncoderV2;
struct MessageSteps {
bytes[] callsToEM;
bool shouldRevert;
}
struct EMResponse {
bool success;
bytes data;
}
contract Helper_CodeContractForCalls {
function runSteps(
MessageSteps calldata _stepsToRun
) external returns(EMResponse[] memory) {
uint numSteps = _stepsToRun.callsToEM.length;
EMResponse[] memory EMResponses = new EMResponse[](numSteps);
for (uint i = 0; i < numSteps; i++) {
bytes memory dataToSend = _stepsToRun.callsToEM[i];
(bool success, bytes memory responseData) = address(msg.sender).call(dataToSend);
EMResponses[i].success = success;
EMResponses[i].data = responseData;
}
return EMResponses; // TODO: revert with this data in case of !shouldRevert
}
}
...@@ -2,18 +2,18 @@ ...@@ -2,18 +2,18 @@
pragma solidity ^0.7.0; pragma solidity ^0.7.0;
contract Helper_ModifiableStorage { contract Helper_ModifiableStorage {
address private target; mapping (address => address) private target;
constructor( constructor(
address _target address _target
) { ) {
target = _target; target[address(this)] = _target;
} }
fallback() fallback()
external external
{ {
(bool success, bytes memory returndata) = target.delegatecall(msg.data); (bool success, bytes memory returndata) = target[address(this)].delegatecall(msg.data);
if (success) { if (success) {
assembly { assembly {
......
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Logging */
import { console } from "@nomiclabs/buidler/console.sol";
/**
* @title Helper_TestRunner
*/
contract Helper_TestRunner {
struct TestStep {
string functionName;
bytes functionData;
bool expectedReturnStatus;
bytes expectedReturnData;
}
function runSingleTestStep(
TestStep memory _step
)
public
{
bytes32 namehash = keccak256(abi.encodePacked(_step.functionName));
if (namehash == keccak256("evmRETURN")) {
bytes memory returndata = _step.functionData;
assembly {
return(add(returndata, 0x20), mload(returndata))
}
}
if (namehash == keccak256("evmREVERT")) {
bytes memory returndata = _step.functionData;
assembly {
revert(add(returndata, 0x20), mload(returndata))
}
}
if (namehash == keccak256("evmINVALID")) {
assembly {
invalid()
}
}
(bool success, bytes memory returndata) = address(msg.sender).call(_step.functionData);
if (success != _step.expectedReturnStatus) {
if (success == true) {
console.log("ERROR: Expected function to revert, but function returned successfully");
console.log("Offending Step: %s", _step.functionName);
console.log("Return Data:");
console.logBytes(returndata);
console.log("");
} else {
(
uint256 _flag,
uint256 _nuisanceGasLeft,
uint256 _ovmGasRefund,
bytes memory _data
) = _decodeRevertData(returndata);
console.log("ERROR: Expected function to return successfully, but function reverted");
console.log("Offending Step: %s", _step.functionName);
console.log("Flag: %s", _flag);
console.log("Nuisance Gas Left: %s", _nuisanceGasLeft);
console.log("OVM Gas Refund: %s", _ovmGasRefund);
console.log("Extra Data:");
console.logBytes(_data);
console.log("");
}
revert("Test step failed.");
}
if (keccak256(returndata) != keccak256(_step.expectedReturnData)) {
if (success == true) {
console.log("ERROR: Actual return data does not match expected return data");
console.log("Offending Step: %s", _step.functionName);
console.log("Expected:");
console.logBytes(_step.expectedReturnData);
console.log("Actual:");
console.logBytes(returndata);
console.log("");
} else {
(
uint256 _expectedFlag,
uint256 _expectedNuisanceGasLeft,
uint256 _expectedOvmGasRefund,
bytes memory _expectedData
) = _decodeRevertData(_step.expectedReturnData);
(
uint256 _flag,
uint256 _nuisanceGasLeft,
uint256 _ovmGasRefund,
bytes memory _data
) = _decodeRevertData(returndata);
console.log("ERROR: Actual revert flag data does not match expected revert flag data");
console.log("Offending Step: %s", _step.functionName);
console.log("Expected Flag: %s", _expectedFlag);
console.log("Actual Flag: %s", _flag);
console.log("Expected Nuisance Gas Left: %s", _expectedNuisanceGasLeft);
console.log("Actual Nuisance Gas Left: %s", _nuisanceGasLeft);
console.log("Expected OVM Gas Refund: %s", _expectedOvmGasRefund);
console.log("Actual OVM Gas Refund: %s", _ovmGasRefund);
console.log("Expected Extra Data:");
console.logBytes(_expectedData);
console.log("Actual Extra Data:");
console.logBytes(_data);
console.log("");
}
revert("Test step failed.");
}
if (success == false || (success == true && returndata.length == 1)) {
assembly {
if eq(extcodesize(address()), 0) {
return(0, 1)
}
revert(add(returndata, 0x20), mload(returndata))
}
}
}
function runMultipleTestSteps(
TestStep[] memory _steps
)
public
{
for (uint256 i = 0; i < _steps.length; i++) {
runSingleTestStep(_steps[i]);
}
}
function _decodeRevertData(
bytes memory _revertdata
)
internal
returns (
uint256 _flag,
uint256 _nuisanceGasLeft,
uint256 _ovmGasRefund,
bytes memory _data
)
{
if (_revertdata.length == 0) {
return (
0,
0,
0,
bytes('')
);
}
return abi.decode(_revertdata, (uint256, uint256, uint256, bytes));
}
}
contract Helper_TestRunner_CREATE is Helper_TestRunner {
constructor(
bytes memory _bytecode,
TestStep[] memory _steps
) {
if (_steps.length > 0) {
runMultipleTestSteps(_steps);
} else {
assembly {
return(add(_bytecode, 0x20), mload(_bytecode))
}
}
}
}
...@@ -8,13 +8,13 @@ ...@@ -8,13 +8,13 @@
## Test Cases ## Test Cases
- CALL-types - CALL-types
- for all: call an undeployed contract and make sure it errors or whatevs (or maybe that's just a unit test) - for all: call an undeployed contract and make sure it errors or whatevs (or maybe that's just a unit test)
- ovmCALL ALL DONE - ovmCALL
- -> ovmCALLER - -> ovmCALLER
- -> ovmADDRESS - -> ovmADDRESS
- -> SLOAD - -> SLOAD
- -> SSTORE - -> SSTORE
- -> CREATE/2 - -> CREATE/2
- ovmSTATICCALL ALL DONE - ovmSTATICCALL
- -> ovmCALLER - -> ovmCALLER
- -> ovmADDRESS - -> ovmADDRESS
- -> SLOAD - -> SLOAD
...@@ -22,27 +22,31 @@ ...@@ -22,27 +22,31 @@
- -> CREATE/2 (fail) - -> CREATE/2 (fail)
- -> ovmCALL -> ovmSSTORE - -> ovmCALL -> ovmSSTORE
- -> ovmCALL -> ovmCREATE - -> ovmCALL -> ovmCREATE
- -> ovmSTATICCALL -> RETURN -> SLOAD (fails) - -> ovmSTATICCALL -> RETURN -> SSTORE (fails)
- ovmDELEGATECALL - ovmDELEGATECALL
- -> ovmCALLER DONE - -> ovmCALLER
- -> ovmADDRESS DONE - -> ovmADDRESS
- -> SLOAD DONE - -> SLOAD
- -> SSTORE DONE - -> SSTORE
- -> CREATE/2 DONE - -> CREATE/2
- -> ovmDELEGATECALL -> ovmCALLER DONE - -> ovmDELEGATECALL -> ovmCALLER
- -> ovmDELEGATECALL -> ovmADDRESS DONE - -> ovmDELEGATECALL -> ovmADDRESS
DONE - -> ovmDELEGATECALL -> ovmCREATE
- Code-related - Code-related
- TODO: fill this in
- CREATE-types - CREATE-types
- do we just duplicate these exactly for CREATE and CREATE2? Probably - do we just duplicate these exactly for CREATE and CREATE2? Probably
- ovmCREATE -> success -> ovmEXTCODE{SIZE,HASH,COPY} DONE - ovmCREATE -> success -> ovmEXTCODE{SIZE,HASH,COPY}
- ovmCREATE -> fail (ovmREVERT, NOT out of gas/invalid jump) -> ovmEXTCODE{SIZE,HASH,COPY} DONE - ovmCREATE -> fail (ovmREVERT, NOT out of gas/invalid jump) -> ovmEXTCODE{SIZE,HASH,COPY}
- ovmCREATE -> fail -> ovmCALL what was attempted to be created (fail) - ovmCREATE -> fail -> ovmCALL what was attempted to be created (fail)
- ovmCREATE -> ovmCREATE (during constructor) -> success -> success (check right address for inner deployment) - ovmCREATE -> ovmCREATE (during constructor) -> success -> success (check right address for inner deployment)
- ovmCREATE -> ovmCALL(in constructor) -> ovmSSTORE, return -> ovmREVERT (deployment fails, storage not modified, but state access gas correctly increased) DONE - ovmCREATE -> ovmCALL(in constructor) -> ovmSSTORE, return -> ovmREVERT (deployment fails, storage not modified, but state access gas correctly increased)
DONE - ovmCREATE -> INVALID -> returns 0 address
- ovmCREATE -> ovmCREATE (during constructor) -> success -> fail (outer contract) - ovmCREATE -> ovmCREATE (during constructor) -> success -> fail (outer contract)
- "creator" does ovmCREATE -> invalid jumpdest -> creator out-of-gasses (or at least, appears to--really it will revert with no data, so there will be some gas left) - "creator" does ovmCREATE -> invalid jumpdest -> creator out-of-gasses (or at least, appears to--really it will revert with no data, so there will be some gas left)
- "creator" does ovmCREATE -> initcode does ovmCREATE -> invalid jumpdest -> creator out-of-gasses (or at least, appears to--really it will revert with no data, so there will be some gas left) AKA same as above but nested CREATEs - "creator" does ovmCREATE -> initcode does ovmCREATE -> invalid jumpdest -> creator out-of-gasses (or at least, appears to--really it will revert with no data, so there will be some gas left) AKA same as above but nested CREATEs
- CREATE collisions: TODO fill in what this needs
- OVM gas metering - OVM gas metering
- do everything for both queue origins/flip flopped roles: - do everything for both queue origins/flip flopped roles:
...@@ -52,6 +56,7 @@ ...@@ -52,6 +56,7 @@
- out of gas - out of gas
- ovmCALL -> [ovmCALL(gas()/2) -> out of gas] -> SSTORE (does not out of gas parent) - ovmCALL -> [ovmCALL(gas()/2) -> out of gas] -> SSTORE (does not out of gas parent)
- ovmCREATE -> ovmCALL(in constructor) -> ovmSSTORE, return -> ovmREVERT (deployment fails, storage not modified, but state access gas correctly increased)
- State access limiting logic - State access limiting logic
...@@ -70,9 +75,9 @@ ...@@ -70,9 +75,9 @@
- Explicit invalid state access tests - Explicit invalid state access tests
- CALL -> CALL, ISA - CALL -> CALL, ISA
- CALL -> CALL, CALL, ISA - CALL -> CALL, CALL, ISA
- CREATE -> CREATE, ISA DONE - CREATE -> CREATE, ISA
- CREATE -> CREATE -> CREATE ISA - CREATE -> CREATE -> CREATE ISA
- CREATE -> CALL, ISA DONE - CREATE -> CALL, ISA
- CALL -> CREATE, ISA DONE - CALL -> CREATE, ISA
- CALL -> CREATE -> CALL, ISA - CALL -> CREATE -> CALL, ISA
- CREATE -> CALL -> CREATE, ISA - CREATE -> CALL -> CREATE, ISA
\ No newline at end of file
...@@ -7,7 +7,9 @@ ...@@ -7,7 +7,9 @@
"build": "yarn run build:contracts", "build": "yarn run build:contracts",
"build:contracts": "buidler compile", "build:contracts": "buidler compile",
"test": "yarn run test:contracts", "test": "yarn run test:contracts",
"test:contracts": "buidler test \"test/contracts/OVM/execution/OVM_test.spec.ts\"" "test:contracts": "buidler test \"test/contracts/OVM/execution/OVM_ExecutionManager/run.spec.ts\"",
"lint": "tslint --format stylish --project .",
"fix": "prettier --config prettier-config.json --write \"buidler.config.ts\" \"{src,test}/**/*.ts\""
}, },
"devDependencies": { "devDependencies": {
"@nomiclabs/buidler": "^1.4.4", "@nomiclabs/buidler": "^1.4.4",
...@@ -23,7 +25,12 @@ ...@@ -23,7 +25,12 @@
"fs-extra": "^9.0.1", "fs-extra": "^9.0.1",
"lodash": "^4.17.20", "lodash": "^4.17.20",
"mocha": "^8.1.1", "mocha": "^8.1.1",
"prettier": "^2.1.2",
"ts-node": "^9.0.0", "ts-node": "^9.0.0",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"tslint-no-focused-test": "^0.5.0",
"tslint-plugin-prettier": "^2.3.0",
"typescript": "^4.0.2" "typescript": "^4.0.2"
} }
} }
{
"$schema": "http://json.schemastore.org/prettierrc",
"trailingComma": "es5",
"tabWidth": 2,
"semi": false,
"singleQuote": true,
"arrowParens": "always"
}
/* tslint:disable:no-empty */
import { expect } from '../../../setup'
describe('OVM_ECDSAContractAccount', () => {
describe('execute', () => {
describe('when provided an invalid signature', () => {
it('should revert', async () => {})
})
describe('when provided a valid signature', () => {
describe('when provided an invalid nonce', () => {
it('should revert', async () => {})
})
describe('when provided a valid nonce', () => {
describe('when executing ovmCREATE', () => {
it('should return the address of the created contract', async () => {})
})
describe('when executing ovmCALL', () => {
it('should return the result of the call', async () => {})
})
})
})
})
})
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Signer, ContractFactory, Contract } from 'ethers'
/* Internal Imports */
import {
getProxyManager,
MockContract,
getMockContract,
setProxyTarget,
NON_NULL_BYTES32,
FORCE_INCLUSION_PERIOD_SECONDS,
ZERO_ADDRESS,
} from '../../../helpers'
const getEthTime = async (): Promise<number> => {
return (await ethers.provider.getBlock('latest')).timestamp
}
const setEthTime = async (time: number): Promise<void> => {
await ethers.provider.send('evm_setNextBlockTimestamp', [time])
}
describe('OVM_CanonicalTransactionChain', () => {
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let Proxy_Manager: Contract
before(async () => {
Proxy_Manager = await getProxyManager()
})
let Mock__OVM_L1ToL2TransactionQueue: MockContract
before(async () => {
Mock__OVM_L1ToL2TransactionQueue = await getMockContract(
await ethers.getContractFactory('OVM_L1ToL2TransactionQueue')
)
await setProxyTarget(
Proxy_Manager,
'OVM_L1ToL2TransactionQueue',
Mock__OVM_L1ToL2TransactionQueue
)
})
let Factory__OVM_CanonicalTransactionChain: ContractFactory
before(async () => {
Factory__OVM_CanonicalTransactionChain = await ethers.getContractFactory(
'OVM_CanonicalTransactionChain'
)
})
let OVM_CanonicalTransactionChain: Contract
beforeEach(async () => {
OVM_CanonicalTransactionChain = await Factory__OVM_CanonicalTransactionChain.deploy(
Proxy_Manager.address,
FORCE_INCLUSION_PERIOD_SECONDS
)
})
describe('appendQueueBatch()', () => {
describe('when the L1ToL2TransactionQueue queue is empty', () => {
before(() => {
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('size', [0])
})
it('should revert', async () => {
await expect(
OVM_CanonicalTransactionChain.appendQueueBatch()
).to.be.revertedWith('No batches are currently queued to be appended.')
})
})
describe('when the L1ToL2TransactionQueue queue is not empty', () => {
before(() => {
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('size', [1])
})
describe('when the inclusion delay period has not elapsed', () => {
beforeEach(async () => {
const timestamp = await getEthTime()
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('peek', [
{
timestamp,
batchRoot: NON_NULL_BYTES32,
isL1ToL2Batch: true,
},
])
await setEthTime(timestamp + FORCE_INCLUSION_PERIOD_SECONDS / 2)
})
it('should revert', async () => {
await expect(
OVM_CanonicalTransactionChain.appendQueueBatch()
).to.be.revertedWith(
'Cannot append until the inclusion delay period has elapsed.'
)
})
})
describe('when the inclusion delay period has elapsed', () => {
beforeEach(async () => {
const timestamp = await getEthTime()
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('peek', [
{
timestamp,
batchRoot: NON_NULL_BYTES32,
isL1ToL2Batch: true,
},
])
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('dequeue', [
{
timestamp,
batchRoot: NON_NULL_BYTES32,
isL1ToL2Batch: true,
},
])
await setEthTime(timestamp + FORCE_INCLUSION_PERIOD_SECONDS)
})
it('should append the top element of the queue and attempt to dequeue', async () => {
await expect(OVM_CanonicalTransactionChain.appendQueueBatch()).to.not
.be.reverted
// TODO: Check that the batch root was inserted.
expect(
Mock__OVM_L1ToL2TransactionQueue.getCallCount('dequeue')
).to.equal(1)
})
})
})
})
describe('appendSequencerBatch()', () => {
describe('when the sender is not the sequencer', () => {
before(async () => {
await Proxy_Manager.setProxy('Sequencer', ZERO_ADDRESS)
})
it('should revert', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch([], 0)
).to.be.revertedWith('Function can only be called by the Sequencer.')
})
})
describe('when the sender is the sequencer', () => {
before(async () => {
await Proxy_Manager.setProxy('Sequencer', await signer.getAddress())
})
describe('when the given batch is empty', () => {
const batch = []
it('should revert', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(batch, 0)
).to.be.revertedWith('Cannot submit an empty batch.')
})
})
describe('when the given batch is not empty', () => {
const batch = [NON_NULL_BYTES32]
describe('when the timestamp is not greater than the previous OVM timestamp', () => {
const timestamp = 0
it('should revert', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(
batch,
timestamp
)
).to.be.revertedWith(
'Batch timestamp must be later than the last OVM timestamp.'
)
})
})
describe('when the timestamp is greater than the previous OVM timestamp', () => {
const timestamp = 1000
describe('when the queue is not empty', () => {
before(() => {
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('size', [1])
})
describe('when the first element in the queue is older than the provided batch', () => {
before(() => {
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('peek', [
{
timestamp: timestamp / 2,
batchRoot: NON_NULL_BYTES32,
isL1ToL2Batch: true,
},
])
})
it('should revert', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(
batch,
timestamp
)
).to.be.revertedWith(
'Older queue batches must be processed before a newer sequencer batch.'
)
})
})
describe('when the first element in the queue is not older than the provided batch', () => {
before(() => {
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('peek', [
{
timestamp,
batchRoot: NON_NULL_BYTES32,
isL1ToL2Batch: true,
},
])
})
it('should insert the sequencer batch', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(
batch,
timestamp
)
).to.not.be.reverted
// TODO: Check that the batch was inserted correctly.
})
})
})
describe('when the queue is empty', async () => {
before(() => {
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('size', [0])
})
it('should insert the sequencer batch', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(
batch,
timestamp
)
).to.not.be.reverted
// TODO: Check that the batch was inserted correctly.
})
})
})
})
})
})
describe('getTotalElements()', () => {
describe('when no batch elements have been inserted', () => {
it('should return zero', async () => {
expect(await OVM_CanonicalTransactionChain.getTotalElements()).to.equal(
0
)
})
})
describe('when one batch element has been inserted', () => {
beforeEach(async () => {
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('size', [0])
await OVM_CanonicalTransactionChain.appendSequencerBatch(
[NON_NULL_BYTES32],
1000
)
})
it('should return the number of inserted batch elements', async () => {
expect(await OVM_CanonicalTransactionChain.getTotalElements()).to.equal(
1
)
})
})
describe('when 64 batch elements have been inserted in one batch', () => {
const batch = Array(64).fill(NON_NULL_BYTES32)
beforeEach(async () => {
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('size', [0])
await OVM_CanonicalTransactionChain.appendSequencerBatch(batch, 1000)
})
it('should return the number of inserted batch elements', async () => {
expect(await OVM_CanonicalTransactionChain.getTotalElements()).to.equal(
64
)
})
})
describe('when 32 batch elements have been inserted in each of two batches', () => {
const batch = Array(32).fill(NON_NULL_BYTES32)
beforeEach(async () => {
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('size', [0])
await OVM_CanonicalTransactionChain.appendSequencerBatch(batch, 1000)
await OVM_CanonicalTransactionChain.appendSequencerBatch(batch, 2000)
})
it('should return the number of inserted batch elements', async () => {
expect(await OVM_CanonicalTransactionChain.getTotalElements()).to.equal(
64
)
})
})
})
describe('getTotalBatches()', () => {
describe('when no batches have been inserted', () => {
it('should return zero', async () => {
expect(await OVM_CanonicalTransactionChain.getTotalBatches()).to.equal(
0
)
})
})
describe('when one batch has been inserted', () => {
beforeEach(async () => {
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('size', [0])
await OVM_CanonicalTransactionChain.appendSequencerBatch(
[NON_NULL_BYTES32],
1000
)
})
it('should return the number of inserted batch elements', async () => {
expect(await OVM_CanonicalTransactionChain.getTotalBatches()).to.equal(
1
)
})
})
describe('when 8 batches have been inserted', () => {
beforeEach(async () => {
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('size', [0])
for (let i = 0; i < 8; i++) {
await OVM_CanonicalTransactionChain.appendSequencerBatch(
[NON_NULL_BYTES32],
1000 * (i + 1)
)
}
})
it('should return the number of inserted batch elements', async () => {
expect(await OVM_CanonicalTransactionChain.getTotalBatches()).to.equal(
8
)
})
})
})
describe('verifyElement()', () => {
it('should revert when given an invalid batch header', async () => {
// TODO
})
it('should revert when given an invalid inclusion proof', async () => {
// TODO
})
it('should return true when given a valid proof', async () => {
// TODO
})
})
})
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Signer, ContractFactory, Contract } from 'ethers'
/* Internal Imports */
import {
getProxyManager,
MockContract,
getMockContract,
setProxyTarget,
NON_NULL_BYTES32,
ZERO_ADDRESS,
} from '../../../helpers'
describe('OVM_StateCommitmentChain', () => {
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let Proxy_Manager: Contract
before(async () => {
Proxy_Manager = await getProxyManager()
})
let Mock__OVM_CanonicalTransactionChain: MockContract
before(async () => {
Mock__OVM_CanonicalTransactionChain = await getMockContract(
await ethers.getContractFactory('OVM_CanonicalTransactionChain')
)
await setProxyTarget(
Proxy_Manager,
'OVM_CanonicalTransactionChain',
Mock__OVM_CanonicalTransactionChain
)
})
let Factory__OVM_StateCommitmentChain: ContractFactory
before(async () => {
Factory__OVM_StateCommitmentChain = await ethers.getContractFactory(
'OVM_StateCommitmentChain'
)
})
let OVM_StateCommitmentChain: Contract
beforeEach(async () => {
OVM_StateCommitmentChain = await Factory__OVM_StateCommitmentChain.deploy(
Proxy_Manager.address
)
})
describe('appendStateBatch', () => {
describe('when the provided batch is empty', () => {
const batch = []
it('should revert', async () => {
await expect(
OVM_StateCommitmentChain.appendStateBatch(batch)
).to.be.revertedWith('Cannot submit an empty state batch.')
})
})
describe('when the provided batch is not empty', () => {
const batch = [NON_NULL_BYTES32]
describe('when submitting more elements than present in the OVM_CanonicalTransactionChain', () => {
before(() => {
Mock__OVM_CanonicalTransactionChain.setReturnValues(
'getTotalElements',
[batch.length - 1]
)
})
it('should revert', async () => {
await expect(
OVM_StateCommitmentChain.appendStateBatch(batch)
).to.be.revertedWith(
'Number of state roots cannot exceed the number of canonical transactions.'
)
})
})
describe('when not submitting more elements than present in the OVM_CanonicalTransactionChain', () => {
before(() => {
Mock__OVM_CanonicalTransactionChain.setReturnValues(
'getTotalElements',
[batch.length]
)
})
it('should append the state batch', async () => {
await expect(OVM_StateCommitmentChain.appendStateBatch(batch)).to.not
.be.reverted
// TODO: Check for correct insertion.
})
})
})
})
describe('deleteStateBatch', () => {
// TODO: Calculate the right header.
const batch = [NON_NULL_BYTES32]
const batchHeader = {
batchIndex: 0,
batchRoot: NON_NULL_BYTES32,
batchSize: 0,
prevTotalElements: 0,
extraData: '0x',
}
beforeEach(async () => {
Mock__OVM_CanonicalTransactionChain.setReturnValues('getTotalElements', [
batch.length,
])
await OVM_StateCommitmentChain.appendStateBatch(batch)
})
describe('when the sender is not the OVM_FraudVerifier', () => {
before(async () => {
await Proxy_Manager.setProxy('OVM_FraudVerifier', ZERO_ADDRESS)
})
it('should revert', async () => {
await expect(
OVM_StateCommitmentChain.deleteStateBatch(batchHeader)
).to.be.revertedWith(
'State batches can only be deleted by the OVM_FraudVerifier.'
)
})
})
describe('when the sender is the OVM_FraudVerifier', () => {
before(async () => {
await Proxy_Manager.setProxy(
'OVM_FraudVerifier',
await signer.getAddress()
)
})
describe('when the provided batch index is greater than the total submitted', () => {
it('should revert', async () => {
await expect(
OVM_StateCommitmentChain.deleteStateBatch({
...batchHeader,
batchIndex: 1,
})
).to.be.revertedWith('Invalid batch index.')
})
})
describe('when the provided batch index is not greater than the total submitted', () => {
describe('when the provided batch header is invalid', () => {
it('should revert', async () => {
await expect(
OVM_StateCommitmentChain.deleteStateBatch({
...batchHeader,
extraData: '0x1234',
})
).to.be.revertedWith('Invalid batch header.')
})
})
describe('when the provided batch header is valid', () => {
it('should remove the batch and all following batches', async () => {
await expect(OVM_StateCommitmentChain.deleteStateBatch(batchHeader))
.to.not.be.reverted
// TODO: Check that it deleted the batches.
})
})
})
})
})
describe('getTotalElements', () => {
describe('when no batch elements have been inserted', () => {
it('should return zero', async () => {
expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(0)
})
})
describe('when one batch element has been inserted', () => {
beforeEach(async () => {
const batch = [NON_NULL_BYTES32]
Mock__OVM_CanonicalTransactionChain.setReturnValues(
'getTotalElements',
[batch.length]
)
await OVM_StateCommitmentChain.appendStateBatch(batch)
})
it('should return the number of inserted batch elements', async () => {
expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(1)
})
})
describe('when 64 batch elements have been inserted in one batch', () => {
beforeEach(async () => {
const batch = Array(64).fill(NON_NULL_BYTES32)
Mock__OVM_CanonicalTransactionChain.setReturnValues(
'getTotalElements',
[batch.length]
)
await OVM_StateCommitmentChain.appendStateBatch(batch)
})
it('should return the number of inserted batch elements', async () => {
expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(64)
})
})
describe('when 32 batch elements have been inserted in each of two batches', () => {
beforeEach(async () => {
const batch = Array(32).fill(NON_NULL_BYTES32)
Mock__OVM_CanonicalTransactionChain.setReturnValues(
'getTotalElements',
[batch.length * 2]
)
await OVM_StateCommitmentChain.appendStateBatch(batch)
await OVM_StateCommitmentChain.appendStateBatch(batch)
})
it('should return the number of inserted batch elements', async () => {
expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(64)
})
})
})
describe('getTotalBatches()', () => {
describe('when no batches have been inserted', () => {
it('should return zero', async () => {
expect(await OVM_StateCommitmentChain.getTotalBatches()).to.equal(0)
})
})
describe('when one batch has been inserted', () => {
beforeEach(async () => {
const batch = [NON_NULL_BYTES32]
Mock__OVM_CanonicalTransactionChain.setReturnValues(
'getTotalElements',
[batch.length]
)
await OVM_StateCommitmentChain.appendStateBatch(batch)
})
it('should return the number of inserted batch elements', async () => {
expect(await OVM_StateCommitmentChain.getTotalBatches()).to.equal(1)
})
})
describe('when 8 batches have been inserted', () => {
beforeEach(async () => {
const batch = [NON_NULL_BYTES32]
Mock__OVM_CanonicalTransactionChain.setReturnValues(
'getTotalElements',
[batch.length * 8]
)
for (let i = 0; i < 8; i++) {
await OVM_StateCommitmentChain.appendStateBatch(batch)
}
})
it('should return the number of inserted batch elements', async () => {
expect(await OVM_StateCommitmentChain.getTotalBatches()).to.equal(8)
})
})
})
describe('verifyElement()', () => {
it('should revert when given an invalid batch header', async () => {
// TODO
})
it('should revert when given an invalid inclusion proof', async () => {
// TODO
})
it('should return true when given a valid proof', async () => {
// TODO
})
})
})
import { expect } from '../../../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory, Signer } from 'ethers'
/* Internal Imports */
import { getProxyManager, MockContract, getMockContract, DUMMY_ACCOUNTS, setProxyTarget, ZERO_ADDRESS, fromHexString, toHexString, makeHexString, NULL_BYTES32, DUMMY_BYTES32, encodeRevertData, REVERT_FLAGS, NON_ZERO_ADDRESS, GAS_LIMIT } from '../../../../../helpers'
describe('OVM_ExecutionManager:opcodes:calling', () => {
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let Proxy_Manager: Contract
before(async () => {
Proxy_Manager = await getProxyManager()
})
let Mock__OVM_StateManager: MockContract
before(async () => {
Mock__OVM_StateManager = await getMockContract('OVM_StateManager')
Mock__OVM_StateManager.setReturnValues('getAccount', (address: string) => {
return [
{
...DUMMY_ACCOUNTS[0].data,
ethAddress: address
}
]
})
await setProxyTarget(
Proxy_Manager,
'OVM_StateManager',
Mock__OVM_StateManager
)
})
let Factory__OVM_ExecutionManager: ContractFactory
before(async () => {
Factory__OVM_ExecutionManager = await ethers.getContractFactory(
'OVM_ExecutionManager'
)
})
let OVM_ExecutionManager: Contract
beforeEach(async () => {
OVM_ExecutionManager = await Factory__OVM_ExecutionManager.deploy(
Proxy_Manager.address
)
})
let Helper_CallTarget: Contract
let Helper_RevertDataViewer: Contract
beforeEach(async () => {
const Factory__Helper_CallTarget = await ethers.getContractFactory(
'Helper_CallTarget'
)
const Factory__Helper_RevertDataViewer = await ethers.getContractFactory(
'Helper_RevertDataViewer'
)
Helper_CallTarget = await Factory__Helper_CallTarget.deploy()
Helper_RevertDataViewer = await Factory__Helper_RevertDataViewer.deploy(
Helper_CallTarget.address
)
})
describe('ovmCALL', () => {
describe('when the OVM_StateManager has the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [true])
})
describe('when the OVM_StateManager has already loaded the account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [true])
})
describe('when the call does not revert', () => {
it('should return the result provided by the target contract', async () => {
const returnData = makeHexString('1234', 32)
expect(
await OVM_ExecutionManager.callStatic.ovmCALL(
GAS_LIMIT,
Helper_CallTarget.address,
Helper_CallTarget.interface.encodeFunctionData(
'doReturn',
[returnData]
)
)
).to.deep.equal([
true,
returnData
])
})
it('should set the ovmADDRESS to the target address', async () => {
expect(
await OVM_ExecutionManager.callStatic.ovmCALL(
GAS_LIMIT,
Helper_CallTarget.address,
Helper_CallTarget.interface.encodeFunctionData(
'doReturnADDRESS',
)
)
).to.deep.equal([
true,
ethers.utils.defaultAbiCoder.encode(
['address'],
[Helper_CallTarget.address]
)
])
})
})
describe('when the call does revert', () => {
describe('with no data', () => {
it('should return false with no data', async () => {
expect(
await OVM_ExecutionManager.callStatic.ovmCALL(
GAS_LIMIT,
Helper_CallTarget.address,
Helper_CallTarget.interface.encodeFunctionData(
'doRevert',
['0x']
)
)
).to.deep.equal([
false,
'0x'
])
})
})
describe('with the INTENTIONAL_REVERT flag', () => {
it('should return false with the flag and user-provided data', async () => {
})
})
describe('with the EXCEEDS_NUISANCE_GAS flag', () => {
it('should return false with the flag', async () => {
})
})
describe('with the INVALID_STATE_ACCESS flag', () => {
it('should revert with the INVALID_STATE_ACCESS flag', () => {
})
})
describe('with the UNSAFE_BYTECODE flag', () => {
it('should return false with the flag and no data', async () => {
})
})
})
})
describe('when the OVM_StateManager has not already loaded the account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [false])
})
describe('when the call parent does not contain enough nuisance gas', () => {
it('should revert with the EXCEEDS_NUISANCE_GAS flag', () => {
})
})
})
})
describe('when the OVM_StateManager does not have the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [false])
})
it('should revert with the INVALID_STATE_ACCESS flag', () => {
})
})
})
describe('ovmSTATICCALL', () => {
describe('when the OVM_StateManager has the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [true])
})
describe('when the OVM_StateManager has already loaded the account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [true])
})
describe('when the call does not revert', () => {
it('should return the result provided by the target contract', async () => {
})
it('should set the context to static', async () => {
})
})
describe('when the call does revert', () => {
describe('with no data', () => {
it('should return false with no data', async () => {
})
})
describe('with the INTENTIONAL_REVERT flag', () => {
it('should return false with the flag and user-provided data', async () => {
})
})
describe('with the EXCEEDS_NUISANCE_GAS flag', () => {
it('should return false with the flag', async () => {
})
})
describe('with the INVALID_STATE_ACCESS flag', () => {
it('should revert with the INVALID_STATE_ACCESS flag', () => {
})
})
describe('with the UNSAFE_BYTECODE flag', () => {
it('should return false with the flag and no data', async () => {
})
})
})
})
describe('when the OVM_StateManager has not already loaded the account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [false])
})
describe('when the call parent does not contain enough nuisance gas', () => {
it('should revert with the EXCEEDS_NUISANCE_GAS flag', () => {
})
})
})
})
describe('when the OVM_StateManager does not have the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [false])
})
it('should revert with the INVALID_STATE_ACCESS flag', () => {
})
})
})
describe('ovmDELEGATECALL', () => {
describe('when the OVM_StateManager has the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [true])
})
describe('when the OVM_StateManager has already loaded the account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [true])
})
describe('when the call does not revert', () => {
it('should return the result provided by the target contract', async () => {
})
it('should retain the previous ovmADDRESS', async () => {
})
})
describe('when the call does revert', () => {
describe('with no data', () => {
it('should return false with no data', async () => {
})
})
describe('with the INTENTIONAL_REVERT flag', () => {
it('should return false with the flag and user-provided data', async () => {
})
})
describe('with the EXCEEDS_NUISANCE_GAS flag', () => {
it('should return false with the flag', async () => {
})
})
describe('with the INVALID_STATE_ACCESS flag', () => {
it('should revert with the INVALID_STATE_ACCESS flag', () => {
})
})
describe('with the UNSAFE_BYTECODE flag', () => {
it('should return false with the flag and no data', async () => {
})
})
})
})
describe('when the OVM_StateManager has not already loaded the account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [false])
})
describe('when the call parent does not contain enough nuisance gas', () => {
it('should revert with the EXCEEDS_NUISANCE_GAS flag', () => {
})
})
})
})
describe('when the OVM_StateManager does not have the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [false])
})
it('should revert with the INVALID_STATE_ACCESS flag', () => {
})
})
})
})
import { expect } from '../../../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory } from 'ethers'
/* Internal Imports */
import { getProxyManager, MockContract, getMockContract, DUMMY_ACCOUNTS, setProxyTarget, ZERO_ADDRESS, fromHexString, toHexString, makeHexString, NULL_BYTES32, DUMMY_BYTES32, encodeRevertData, REVERT_FLAGS } from '../../../../../helpers'
describe('OVM_ExecutionManager:opcodes:code', () => {
let Proxy_Manager: Contract
before(async () => {
Proxy_Manager = await getProxyManager()
})
let Mock__OVM_StateManager: MockContract
before(async () => {
Mock__OVM_StateManager = await getMockContract('OVM_StateManager')
Mock__OVM_StateManager.setReturnValues('getAccount', (address: string) => {
return [
{
...DUMMY_ACCOUNTS[0].data,
ethAddress: address
}
]
})
await setProxyTarget(
Proxy_Manager,
'OVM_StateManager',
Mock__OVM_StateManager
)
})
let Dummy_Contract: Contract
before(async () => {
// We need some contract to query code for, might as well reuse an existing object.
Dummy_Contract = Mock__OVM_StateManager
})
let Factory__OVM_ExecutionManager: ContractFactory
before(async () => {
Factory__OVM_ExecutionManager = await ethers.getContractFactory(
'OVM_ExecutionManager'
)
})
let OVM_ExecutionManager: Contract
beforeEach(async () => {
OVM_ExecutionManager = await Factory__OVM_ExecutionManager.deploy(
Proxy_Manager.address
)
})
let Helper_RevertDataViewer: Contract
beforeEach(async () => {
const Factory__Helper_RevertDataViewer = await ethers.getContractFactory(
'Helper_RevertDataViewer'
)
Helper_RevertDataViewer = await Factory__Helper_RevertDataViewer.deploy(
OVM_ExecutionManager.address
)
})
describe('ovmEXTCODECOPY()', () => {
describe('when the OVM_StateManager has the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [true])
})
describe('when the OVM_StateManager has already loaded the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [true])
})
it('should return the code for a given account', async () => {
const expectedCode = await ethers.provider.getCode(Dummy_Contract.address)
const expectedCodeSize = fromHexString(expectedCode).length
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODECOPY(Dummy_Contract.address, 0, expectedCodeSize)
).to.equal(expectedCode)
})
it('should return empty if the provided length is zero', async () => {
const expectedCode = '0x'
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODECOPY(Dummy_Contract.address, 0, 0)
).to.equal(expectedCode)
})
it('should return offset code when offset is less than total length', async () => {
const fullCode = await ethers.provider.getCode(Dummy_Contract.address)
const fullCodeSize = fromHexString(fullCode).length
const codeOffset = Math.floor(fullCodeSize / 2)
const codeLength = fullCodeSize - codeOffset
const expectedCode = toHexString(fromHexString(fullCode).slice(codeOffset, codeOffset + codeLength))
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODECOPY(Dummy_Contract.address, codeOffset, codeLength)
).to.equal(expectedCode)
})
it('should return less code when length is less than total length', async () => {
const fullCode = await ethers.provider.getCode(Dummy_Contract.address)
const fullCodeSize = fromHexString(fullCode).length
const codeLength = Math.floor(fullCodeSize / 2)
const expectedCode = toHexString(fromHexString(fullCode).slice(0, codeLength))
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODECOPY(Dummy_Contract.address, 0, codeLength)
).to.equal(expectedCode)
})
it('should return extra code when length is greater than total length', async () => {
const fullCode = await ethers.provider.getCode(Dummy_Contract.address)
const fullCodeSize = fromHexString(fullCode).length
const extraLength = fullCodeSize
const codeLength = fullCodeSize + extraLength
const expectedCode = toHexString(Buffer.concat([
fromHexString(fullCode),
fromHexString(makeHexString('00', extraLength))
]))
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODECOPY(Dummy_Contract.address, 0, codeLength)
).to.equal(expectedCode)
})
it('should return extra code when offset is less than total length and length is greater than total length', async () => {
const fullCode = await ethers.provider.getCode(Dummy_Contract.address)
const fullCodeSize = fromHexString(fullCode).length
const extraLength = fullCodeSize
const codeOffset = Math.floor(fullCodeSize / 2)
const codeLength = fullCodeSize - codeOffset + extraLength
const expectedCode = toHexString(Buffer.concat([
fromHexString(fullCode).slice(codeOffset, codeOffset + codeLength),
fromHexString(makeHexString('00', extraLength))
]))
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODECOPY(Dummy_Contract.address, codeOffset, codeLength)
).to.equal(expectedCode)
})
it('should return empty bytes when both offset and length exceed total length', async () => {
const fullCode = await ethers.provider.getCode(Dummy_Contract.address)
const fullCodeSize = fromHexString(fullCode).length
const extraLength = fullCodeSize
const codeOffset = fullCodeSize
const codeLength = fullCodeSize + extraLength
const expectedCode = toHexString(Buffer.concat([
fromHexString(makeHexString('00', codeLength))
]))
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODECOPY(Dummy_Contract.address, codeOffset, codeLength)
).to.equal(expectedCode)
})
})
describe('when the OVM_StateManager has not already loaded the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [false])
})
it('should revert with the EXCEEDS_NUISANCE_GAS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmEXTCODECOPY',
[
Dummy_Contract.address,
0,
0
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.EXCEEDS_NUISANCE_GAS
))
})
})
})
describe('when the OVM_StateManager does not have the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [false])
})
it('should revert with the INVALID_STATE_ACCESS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmEXTCODECOPY',
[
Dummy_Contract.address,
0,
0
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.INVALID_STATE_ACCESS
))
})
})
})
describe('ovmEXTCODESIZE()', () => {
describe('when the OVM_StateManager has the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [true])
})
describe('when the OVM_StateManager has already loaded the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [true])
})
it('should return the code size for a given account', async () => {
const expectedCode = await ethers.provider.getCode(Dummy_Contract.address)
const expectedCodeSize = fromHexString(expectedCode).length
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODESIZE(Dummy_Contract.address)
).to.equal(expectedCodeSize)
})
it('should return zero if the account has no code', async () => {
const expectedCodeSize = 0
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODESIZE(ZERO_ADDRESS)
).to.equal(expectedCodeSize)
})
})
describe('when the OVM_StateManager has not already loaded the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [false])
})
it('should revert with the EXCEEDS_NUISANCE_GAS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmEXTCODESIZE',
[
Dummy_Contract.address,
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.EXCEEDS_NUISANCE_GAS
))
})
})
})
describe('when the OVM_StateManager does not have the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [false])
})
it('should revert with the INVALID_STATE_ACCESS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmEXTCODESIZE',
[
Dummy_Contract.address
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.INVALID_STATE_ACCESS
))
})
})
})
describe('ovmEXTCODEHASH()', () => {
describe('when the OVM_StateManager has the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [true])
})
describe('when the OVM_StateManager has already loaded the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [true])
})
it('should return the code hash for a given account', async () => {
const expectedCode = await ethers.provider.getCode(Dummy_Contract.address)
const expectedCodeHash = ethers.utils.keccak256(expectedCode)
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODEHASH(Dummy_Contract.address)
).to.equal(expectedCodeHash)
})
it('should return zero if the account does not exist', async () => {
const expectedCodeHash = NULL_BYTES32
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODEHASH(ZERO_ADDRESS)
).to.equal(expectedCodeHash)
})
})
describe('when the OVM_StateManager has not already loaded the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [false])
})
it('should revert with the EXCEEDS_NUISANCE_GAS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmEXTCODEHASH',
[
Dummy_Contract.address,
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.EXCEEDS_NUISANCE_GAS
))
})
})
})
describe('when the OVM_StateManager does not have the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [false])
})
it('should revert with the INVALID_STATE_ACCESS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmEXTCODEHASH',
[
Dummy_Contract.address
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.INVALID_STATE_ACCESS
))
})
})
})
})
import { expect } from '../../../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory } from 'ethers'
describe('OVM_ExecutionManager:opcodes:creation', () => {
describe('ovmCREATE', () => {
})
describe('ovmCREATE2', () => {
})
})
import { expect } from '../../../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory } from 'ethers'
/* Internal Imports */
import { getProxyManager, encodeRevertData, REVERT_FLAGS } from '../../../../../helpers'
describe('OVM_ExecutionManager:opcodes:halting', () => {
let Proxy_Manager: Contract
before(async () => {
Proxy_Manager = await getProxyManager()
})
let Factory__OVM_ExecutionManager: ContractFactory
before(async () => {
Factory__OVM_ExecutionManager = await ethers.getContractFactory(
'OVM_ExecutionManager'
)
})
let OVM_ExecutionManager: Contract
beforeEach(async () => {
OVM_ExecutionManager = await Factory__OVM_ExecutionManager.deploy(
Proxy_Manager.address
)
})
let Helper_RevertDataViewer: Contract
beforeEach(async () => {
const Factory__Helper_RevertDataViewer = await ethers.getContractFactory(
'Helper_RevertDataViewer'
)
Helper_RevertDataViewer = await Factory__Helper_RevertDataViewer.deploy(OVM_ExecutionManager.address)
})
describe('ovmREVERT', () => {
it('should revert with the provided data prefixed by the intentional revert flag', async () => {
const revertdata = '12345678'.repeat(10)
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmREVERT',
['0x' + revertdata]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.INTENTIONAL_REVERT,
'0x' + revertdata
))
})
it('should revert with the intentional revert flag if no data is provided', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmREVERT',
['0x']
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.INTENTIONAL_REVERT
))
})
})
})
import { expect } from '../../../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory } from 'ethers'
/* Internal Imports */
import { getProxyManager, MockContract, getMockContract, DUMMY_ACCOUNTS, setProxyTarget, ZERO_ADDRESS, fromHexString, toHexString, makeHexString, NULL_BYTES32, DUMMY_BYTES32, encodeRevertData, REVERT_FLAGS, NON_ZERO_ADDRESS } from '../../../../../helpers'
describe('OVM_ExecutionManager:opcodes:storage', () => {
let Proxy_Manager: Contract
before(async () => {
Proxy_Manager = await getProxyManager()
})
let Mock__OVM_StateManager: MockContract
before(async () => {
Mock__OVM_StateManager = await getMockContract('OVM_StateManager')
await setProxyTarget(
Proxy_Manager,
'OVM_StateManager',
Mock__OVM_StateManager
)
})
let Factory__OVM_ExecutionManager: ContractFactory
before(async () => {
Factory__OVM_ExecutionManager = await ethers.getContractFactory(
'OVM_ExecutionManager'
)
})
let OVM_ExecutionManager: Contract
beforeEach(async () => {
OVM_ExecutionManager = await Factory__OVM_ExecutionManager.deploy(
Proxy_Manager.address
)
})
let Helper_RevertDataViewer: Contract
beforeEach(async () => {
const Factory__Helper_RevertDataViewer = await ethers.getContractFactory(
'Helper_RevertDataViewer'
)
Helper_RevertDataViewer = await Factory__Helper_RevertDataViewer.deploy(
OVM_ExecutionManager.address
)
})
const DUMMY_SLOT_KEY = DUMMY_BYTES32[0]
const DUMMY_SLOT_VALUE = DUMMY_BYTES32[1]
describe('ovmSLOAD', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('getContractStorage', () => {
return [
DUMMY_SLOT_VALUE
]
})
})
describe('when the OVM_StateManager has the corresponding storage slot', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasContractStorage', [true])
})
describe('when the OVM_StateManager has already loaded the storage slot', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetContractStorageLoaded', [true])
})
it('should return the value of the storage slot', async () => {
expect(
await OVM_ExecutionManager.callStatic.ovmSLOAD(
DUMMY_SLOT_KEY
)
).to.equal(DUMMY_SLOT_VALUE)
})
})
describe('when the OVM_StateManager has not already loaded the storage slot', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetContractStorageLoaded', [false])
})
it('should revert with the EXCEEDS_NUISANCE_GAS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmSLOAD',
[
DUMMY_SLOT_KEY
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.EXCEEDS_NUISANCE_GAS
))
})
})
})
describe('when the OVM_StateManager does not have the corresponding storage slot', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasContractStorage', [false])
})
it('should revert with the INVALID_STATE_ACCESS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmSLOAD',
[
DUMMY_SLOT_KEY
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.INVALID_STATE_ACCESS
))
})
})
})
describe('ovmSSTORE', () => {
describe('when the OVM_StateManager has already changed the storage slot', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetContractStorageChanged', [true])
})
it('should modify the storage slot value', async () => {
await expect(
OVM_ExecutionManager.ovmSSTORE(
DUMMY_SLOT_KEY,
DUMMY_SLOT_VALUE
)
).to.not.be.reverted
expect(
Mock__OVM_StateManager.getCallData('putContractStorage', 0)
).to.deep.equal(
[
ZERO_ADDRESS,
DUMMY_SLOT_KEY,
DUMMY_SLOT_VALUE
]
)
})
})
describe('when the OVM_StateManager has not already changed the storage slot', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetContractStorageChanged', [false])
})
it('should revert with the EXCEEDS_NUISANCE_GAS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmSSTORE',
[
DUMMY_SLOT_KEY,
DUMMY_SLOT_VALUE
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.EXCEEDS_NUISANCE_GAS
))
})
})
})
})
/* Internal Imports */
import {
ExecutionManagerTestRunner,
TestDefinition,
GAS_LIMIT,
NON_NULL_BYTES32,
REVERT_FLAGS,
VERIFIED_EMPTY_CONTRACT_HASH,
} from '../../../../helpers'
const DUMMY_REVERT_DATA =
'0xdeadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420'
const test_ovmCALL: TestDefinition = {
name: 'Basic tests for ovmCALL',
preState: {
ExecutionManager: {
ovmStateManager: '$OVM_STATE_MANAGER',
ovmSafetyChecker: '$OVM_SAFETY_CHECKER',
messageRecord: {
nuisanceGasLeft: GAS_LIMIT,
},
},
StateManager: {
owner: '$OVM_EXECUTION_MANAGER',
accounts: {
$DUMMY_OVM_ADDRESS_1: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_2: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_3: {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20),
},
},
},
},
parameters: [
{
name: 'ovmCALL(ADDRESS_1) => ovmADDRESS',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmADDRESS',
expectedReturnValue: '$DUMMY_OVM_ADDRESS_1',
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => ovmSSTORE',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmSSTORE',
functionParams: {
key: NON_NULL_BYTES32,
value: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name:
'ovmCALL(ADDRESS_1) => ovmSSTORE + ovmSLOAD, ovmCALL(ADDRESS_1) => ovmSLOAD',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmSSTORE',
functionParams: {
key: NON_NULL_BYTES32,
value: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
},
{
functionName: 'ovmSLOAD',
functionParams: {
key: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
expectedReturnValue: NON_NULL_BYTES32,
},
],
},
expectedReturnStatus: true,
},
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmSLOAD',
functionParams: {
key: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
expectedReturnValue: NON_NULL_BYTES32,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name:
'ovmCALL(ADDRESS_1) => ovmCALL(ADDRESS_2) => ovmADDRESS + ovmCALLER',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmADDRESS',
expectedReturnValue: '$DUMMY_OVM_ADDRESS_2',
},
{
functionName: 'ovmCALLER',
expectedReturnValue: '$DUMMY_OVM_ADDRESS_1',
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => ovmCALL(ADDRESS_3)',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_3',
calldata: '0x',
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
expectedReturnValue: '0x',
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => INTENTIONAL_REVERT',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'evmREVERT',
returnData: {
flag: REVERT_FLAGS.INTENTIONAL_REVERT,
data: DUMMY_REVERT_DATA,
},
},
],
},
expectedReturnStatus: false,
expectedReturnValue: DUMMY_REVERT_DATA,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => EXCEEDS_NUISANCE_GAS',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'evmREVERT',
returnData: {
flag: REVERT_FLAGS.EXCEEDS_NUISANCE_GAS,
},
},
],
},
expectedReturnStatus: false,
expectedReturnValue: '0x',
},
],
},
],
}
const runner = new ExecutionManagerTestRunner()
runner.run(test_ovmCALL)
/* Internal Imports */
import {
ExecutionManagerTestRunner,
TestDefinition,
GAS_LIMIT,
NULL_BYTES32,
NON_NULL_BYTES32,
REVERT_FLAGS,
DUMMY_BYTECODE,
ZERO_ADDRESS,
VERIFIED_EMPTY_CONTRACT_HASH,
DUMMY_BYTECODE_BYTELEN,
DUMMY_BYTECODE_HASH,
} from '../../../../helpers'
const CREATED_CONTRACT_1 = '0xa1c4ba6fe56bda6db9df39bf45dbfc3cd104bd6f'
const CREATED_CONTRACT_2 = '0x2bda4a99d5be88609d23b1e4ab5d1d34fb1c2feb'
const NESTED_CREATED_CONTRACT = '0xb99a3d1d1e3f0bd867570da4776221c1b0b74ea3'
const DUMMY_REVERT_DATA =
'0xdeadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420'
const test_ovmCREATE: TestDefinition = {
name: 'Basic tests for ovmCREATE',
preState: {
ExecutionManager: {
ovmStateManager: '$OVM_STATE_MANAGER',
ovmSafetyChecker: '$OVM_SAFETY_CHECKER',
messageRecord: {
nuisanceGasLeft: GAS_LIMIT,
},
},
StateManager: {
owner: '$OVM_EXECUTION_MANAGER',
accounts: {
$DUMMY_OVM_ADDRESS_1: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_2: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
[CREATED_CONTRACT_1]: {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20),
},
[CREATED_CONTRACT_2]: {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20),
},
[NESTED_CREATED_CONTRACT]: {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20),
},
},
verifiedContractStorage: {
$DUMMY_OVM_ADDRESS_2: {
[NULL_BYTES32]: true,
},
},
},
},
parameters: [
{
name: 'ovmCREATE, ovmEXTCODESIZE(CREATED)',
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
bytecode: DUMMY_BYTECODE,
},
expectedReturnStatus: true,
expectedReturnValue: CREATED_CONTRACT_1,
},
{
functionName: 'ovmEXTCODESIZE',
functionParams: {
address: CREATED_CONTRACT_1,
},
expectedReturnStatus: true,
expectedReturnValue: DUMMY_BYTECODE_BYTELEN,
},
],
},
{
name: 'ovmCREATE, ovmEXTCODEHASH(CREATED)',
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
bytecode: DUMMY_BYTECODE,
},
expectedReturnStatus: true,
expectedReturnValue: CREATED_CONTRACT_1,
},
{
functionName: 'ovmEXTCODEHASH',
functionParams: {
address: CREATED_CONTRACT_1,
},
expectedReturnStatus: true,
expectedReturnValue: DUMMY_BYTECODE_HASH,
},
],
},
{
name: 'ovmCREATE, ovmEXTCODECOPY(CREATED)',
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
bytecode: DUMMY_BYTECODE,
},
expectedReturnStatus: true,
expectedReturnValue: CREATED_CONTRACT_1,
},
{
functionName: 'ovmEXTCODECOPY',
functionParams: {
address: CREATED_CONTRACT_1,
offset: 0,
length: DUMMY_BYTECODE_BYTELEN,
},
expectedReturnStatus: true,
expectedReturnValue: DUMMY_BYTECODE,
},
],
},
{
name: 'ovmCREATE => ovmREVERT',
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmREVERT',
revertData: DUMMY_REVERT_DATA,
expectedReturnStatus: true,
expectedReturnValue: '0x00',
},
],
},
expectedReturnStatus: true,
expectedReturnValue: ZERO_ADDRESS,
},
],
},
{
name: 'ovmCREATE => ovmREVERT, ovmEXTCODESIZE(CREATED)',
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmREVERT',
revertData: DUMMY_REVERT_DATA,
expectedReturnStatus: true,
expectedReturnValue: '0x00',
},
],
},
expectedReturnStatus: true,
expectedReturnValue: ZERO_ADDRESS,
},
{
functionName: 'ovmEXTCODESIZE',
functionParams: {
address: CREATED_CONTRACT_1,
},
expectedReturnStatus: true,
expectedReturnValue: 0,
},
],
},
{
name: 'ovmCREATE => ovmREVERT, ovmEXTCODEHASH(CREATED)',
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmREVERT',
revertData: DUMMY_REVERT_DATA,
expectedReturnStatus: true,
expectedReturnValue: '0x00',
},
],
},
expectedReturnStatus: true,
expectedReturnValue: ZERO_ADDRESS,
},
{
functionName: 'ovmEXTCODEHASH',
functionParams: {
address: CREATED_CONTRACT_1,
},
expectedReturnStatus: true,
expectedReturnValue: NULL_BYTES32,
},
],
},
{
name: 'ovmCREATE => ovmREVERT, ovmEXTCODECOPY(CREATED)',
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmREVERT',
revertData: DUMMY_REVERT_DATA,
expectedReturnStatus: true,
expectedReturnValue: '0x00',
},
],
},
expectedReturnStatus: true,
expectedReturnValue: ZERO_ADDRESS,
},
{
functionName: 'ovmEXTCODECOPY',
functionParams: {
address: CREATED_CONTRACT_1,
offset: 0,
length: 256,
},
expectedReturnStatus: true,
expectedReturnValue: '0x' + '00'.repeat(256),
},
],
},
{
name: 'ovmCREATE => ovmADDRESS',
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmADDRESS',
expectedReturnValue: CREATED_CONTRACT_1,
},
],
},
expectedReturnStatus: true,
expectedReturnValue: CREATED_CONTRACT_1,
},
],
},
{
name: 'ovmCREATE => ovmSLOAD',
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmSLOAD',
functionParams: {
key: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
expectedReturnValue: NULL_BYTES32,
},
],
},
expectedReturnStatus: true,
expectedReturnValue: CREATED_CONTRACT_1,
},
],
},
{
name: 'ovmCALL => ovmCREATE => ovmCALLER',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmSLOAD',
functionParams: {
key: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
expectedReturnValue: NULL_BYTES32,
},
],
},
expectedReturnStatus: true,
expectedReturnValue: CREATED_CONTRACT_2,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCREATE => ovmSSTORE + ovmSLOAD',
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmSSTORE',
functionParams: {
key: NON_NULL_BYTES32,
value: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
},
{
functionName: 'ovmSLOAD',
functionParams: {
key: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
expectedReturnValue: NON_NULL_BYTES32,
},
],
},
expectedReturnStatus: true,
expectedReturnValue: CREATED_CONTRACT_1,
},
],
},
{
name:
'ovmCREATE => ovmSSTORE, ovmCALL(CREATED) => ovmSLOAD(EXIST) + ovmSLOAD(NONEXIST)',
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmSSTORE',
functionParams: {
key: NON_NULL_BYTES32,
value: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
expectedReturnValue: CREATED_CONTRACT_1,
},
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: CREATED_CONTRACT_1,
subSteps: [
{
functionName: 'ovmSLOAD',
functionParams: {
key: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
expectedReturnValue: NON_NULL_BYTES32,
},
{
functionName: 'ovmSLOAD',
functionParams: {
key: NULL_BYTES32,
},
expectedReturnStatus: true,
expectedReturnValue: NULL_BYTES32,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name:
'ovmCREATE => ovmCALL(ADDRESS_1) => ovmSSTORE, ovmCALL(ADDRESS_1) => ovmSLOAD',
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmSSTORE',
functionParams: {
key: NON_NULL_BYTES32,
value: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
expectedReturnValue: CREATED_CONTRACT_1,
},
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmSLOAD',
functionParams: {
key: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
expectedReturnValue: NON_NULL_BYTES32,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name:
'ovmCREATE => (ovmCALL(ADDRESS_2) => ovmSSTORE) + ovmREVERT, ovmCALL(ADDRESS_2) => ovmSLOAD',
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmSSTORE',
functionParams: {
key: NULL_BYTES32,
value: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
{
functionName: 'ovmREVERT',
revertData: DUMMY_REVERT_DATA,
expectedReturnStatus: true,
expectedReturnValue: '0x00',
},
],
},
expectedReturnStatus: true,
expectedReturnValue: ZERO_ADDRESS,
},
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmSLOAD',
functionParams: {
key: NULL_BYTES32,
},
expectedReturnStatus: true,
expectedReturnValue: NULL_BYTES32,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCREATE => ovmCALL(ADDRESS_NONEXIST)',
expectInvalidStateAccess: true,
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_3',
calldata: '0x',
},
expectedReturnStatus: true,
expectedReturnValue: '0x00',
},
],
},
expectedReturnStatus: false,
expectedReturnValue: {
flag: REVERT_FLAGS.INVALID_STATE_ACCESS,
},
},
],
},
{
name: 'ovmCREATE => ovmCREATE => ovmCALL(ADDRESS_NONEXIST)',
expectInvalidStateAccess: true,
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_3',
calldata: '0x',
},
expectedReturnStatus: true,
expectedReturnValue: '0x00',
},
],
},
expectedReturnStatus: true,
expectedReturnValue: '0x00',
},
],
},
expectedReturnStatus: false,
expectedReturnValue: {
flag: REVERT_FLAGS.INVALID_STATE_ACCESS,
},
},
],
},
{
name: 'ovmCREATE => OUT_OF_GAS',
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
subSteps: [
{
functionName: 'evmINVALID',
},
],
},
expectedReturnStatus: true,
expectedReturnValue: ZERO_ADDRESS,
},
],
},
],
}
const runner = new ExecutionManagerTestRunner()
runner.run(test_ovmCREATE)
/* Internal Imports */
import {
ExecutionManagerTestRunner,
TestDefinition,
GAS_LIMIT,
NULL_BYTES32,
NON_NULL_BYTES32,
REVERT_FLAGS,
DUMMY_BYTECODE,
VERIFIED_EMPTY_CONTRACT_HASH,
} from '../../../../helpers'
const CREATED_CONTRACT_1 = '0x2bda4a99d5be88609d23b1e4ab5d1d34fb1c2feb'
const CREATED_CONTRACT_2 = '0xe0d8be8101f36ebe6b01abacec884422c39a1f62'
const test_ovmDELEGATECALL: TestDefinition = {
name: 'Basic tests for ovmDELEGATECALL',
preState: {
ExecutionManager: {
ovmStateManager: '$OVM_STATE_MANAGER',
ovmSafetyChecker: '$OVM_SAFETY_CHECKER',
messageRecord: {
nuisanceGasLeft: GAS_LIMIT,
},
},
StateManager: {
owner: '$OVM_EXECUTION_MANAGER',
accounts: {
$DUMMY_OVM_ADDRESS_1: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_2: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_3: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_4: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
[CREATED_CONTRACT_1]: {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20),
},
[CREATED_CONTRACT_2]: {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20),
},
},
},
},
parameters: [
{
name: 'ovmCALL(ADDRESS_1) => ovmDELEGATECALL(ADDRESS_2) => ovmADDRESS',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmADDRESS',
expectedReturnValue: '$DUMMY_OVM_ADDRESS_1',
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name:
'ovmCALL(ADDRESS_1) => ovmCALL(ADDRESS_2) => ovmDELEGATECALL(ADDRESS_3) => ovmCALLER',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmCALLER',
expectedReturnValue: '$DUMMY_OVM_ADDRESS_1',
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name:
'ovmCALL(ADDRESS_1) => (ovmDELEGATECALL(ADDRESS_2) => ovmSSTORE) + ovmSLOAD',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmSSTORE',
functionParams: {
key: NON_NULL_BYTES32,
value: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
{
functionName: 'ovmSLOAD',
functionParams: {
key: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
expectedReturnValue: NON_NULL_BYTES32,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name: 'ovmCALL(ADDRESS_1) => (ovmDELEGATECALL(ADDRESS_2) => ovmCREATE)',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmCREATE',
functionParams: {
bytecode: DUMMY_BYTECODE,
},
expectedReturnStatus: true,
expectedReturnValue: CREATED_CONTRACT_1,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name:
'ovmCALL(ADDRESS_1) => ovmCALL(ADDRESS_2) => ovmDELEGATECALL(ADDRESS_3) => ovmDELEGATECALL(ADDRESS_4) => ovmCALLER',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_3',
subSteps: [
{
functionName: 'ovmCALLER',
expectedReturnValue: '$DUMMY_OVM_ADDRESS_1',
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name:
'ovmCALL(ADDRESS_1) => ovmCALL(ADDRESS_2) => ovmDELEGATECALL(ADDRESS_3) => ovmDELEGATECALL(ADDRESS_4) => ovmADDRESS',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_3',
subSteps: [
{
functionName: 'ovmADDRESS',
expectedReturnValue: '$DUMMY_OVM_ADDRESS_2',
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
{
name:
'ovmCALL(ADDRESS_1) => ovmCALL(ADDRESS_2) => ovmDELEGATECALL(ADDRESS_3) => ovmDELEGATECALL(ADDRESS_4) => ovmCREATE',
steps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [
{
functionName: 'ovmDELEGATECALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_3',
subSteps: [
{
functionName: 'ovmCREATE',
functionParams: {
bytecode: DUMMY_BYTECODE,
},
expectedReturnStatus: true,
expectedReturnValue: CREATED_CONTRACT_2,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
expectedReturnStatus: true,
},
],
},
],
}
const runner = new ExecutionManagerTestRunner()
runner.run(test_ovmDELEGATECALL)
/* Internal Imports */
import {
runExecutionManagerTest,
TestDefinition,
GAS_LIMIT,
NULL_BYTES32,
NON_NULL_BYTES32,
REVERT_FLAGS,
DUMMY_BYTECODE,
} from '../../../../helpers'
const test_ovmREVERT: TestDefinition = {
name: 'basic ovmREVERT unit tests',
preState: {
ExecutionManager: {
ovmStateManager: '$OVM_STATE_MANAGER',
ovmSafetyChecker: '$OVM_SAFETY_CHECKER',
messageRecord: {
nuisanceGasLeft: GAS_LIMIT,
},
},
StateManager: {
owner: '$OVM_EXECUTION_MANAGER',
accounts: {
$DUMMY_OVM_ADDRESS_1: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
},
},
},
parameters: [
{
name: 'ovmREVERT inside ovmCALL should cause EM to revert',
parameters: [
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
'$DUMMY_OVM_ADDRESS_1',
[
{
functionName: 'ovmREVERT',
functionParams: ['0xdeadbeef'],
expectedReturnStatus: false,
expectedReturnValues: [
REVERT_FLAGS.INTENTIONAL_REVERT,
'0xdeadbeef',
GAS_LIMIT / 2,
0,
],
},
],
],
expectedReturnStatus: true,
expectedReturnValues: [],
},
],
},
],
},
// TODO: fix this. only way to do it is manually set up and call ovmREVERT directly inside a context which mirrors that during creation.
// {
// name: "ovmREVERT inside ovmCREATE ?",
// parameters: [
// {
// steps: [
// {
// functionName: "ovmCALL",
// functionParams: [
// GAS_LIMIT / 2,
// "$DUMMY_OVM_ADDRESS_1",
// [
// {
// functionName: "ovmCREATE",
// functionParams: [
// USELESS_BYTECODE,
// false, // "create will be successful?"
// [
// {
// functionName: "ovmREVERT",
// functionParams: [ "0xdeadbeef" ],
// expectedReturnStatus: false,
// expectedReturnValues: [ "0x00" ] // no return values for reversion in constructor
// },
// // TODO: check internally flagged storage here
// ]
// ],
// expectedReturnStatus: true,
// expectedReturnValues: [ CREATED_CONTRACT_1 ]
// }
// ],
// ],
// expectedReturnStatus: true,
// expectedReturnValues: []
// }
// ]
// }
// ]
// }
],
}
runExecutionManagerTest(test_ovmREVERT)
/* Internal Imports */
import {
runExecutionManagerTest,
TestDefinition,
GAS_LIMIT,
NULL_BYTES32,
NON_NULL_BYTES32,
REVERT_FLAGS,
DUMMY_BYTECODE,
} from '../../../../helpers'
const test_ovmSLOAD: TestDefinition = {
name:
'External storage manipulation during initcode subcalls should correctly NOT be persisted if ovmREVERTed',
preState: {
ExecutionManager: {
ovmStateManager: '$OVM_STATE_MANAGER',
ovmSafetyChecker: '$OVM_SAFETY_CHECKER',
messageRecord: {
nuisanceGasLeft: GAS_LIMIT,
},
},
StateManager: {
owner: '$OVM_EXECUTION_MANAGER',
accounts: {
$DUMMY_OVM_ADDRESS_1: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_2: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
},
verifiedContractStorage: {
$DUMMY_OVM_ADDRESS_1: {
[NON_NULL_BYTES32]: true,
},
},
},
},
parameters: [
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
'$DUMMY_OVM_ADDRESS_1',
[
{
functionName: 'ovmSLOAD',
functionParams: [NON_NULL_BYTES32],
expectedReturnStatus: true,
expectedReturnValues: [NULL_BYTES32],
},
],
],
expectedReturnStatus: true,
expectedReturnValues: [],
},
],
},
],
}
runExecutionManagerTest(test_ovmSLOAD)
/* Internal Imports */
import {
runExecutionManagerTest,
TestDefinition,
GAS_LIMIT,
NULL_BYTES32,
NON_NULL_BYTES32,
REVERT_FLAGS,
DUMMY_BYTECODE,
} from '../../../../helpers'
const test_ovmSTATICCALL: TestDefinition = {
name: 'Basic checks on staticcall',
preState: {
ExecutionManager: {
ovmStateManager: '$OVM_STATE_MANAGER',
ovmSafetyChecker: '$OVM_SAFETY_CHECKER',
messageRecord: {
nuisanceGasLeft: GAS_LIMIT,
},
},
StateManager: {
owner: '$OVM_EXECUTION_MANAGER',
accounts: {
$DUMMY_OVM_ADDRESS_1: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_2: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
},
verifiedContractStorage: {
$DUMMY_OVM_ADDRESS_2: {
[NON_NULL_BYTES32]: true,
},
},
},
},
parameters: [
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
'$DUMMY_OVM_ADDRESS_1',
[
{
functionName: 'ovmSTATICCALL',
functionParams: [
GAS_LIMIT / 2,
'$DUMMY_OVM_ADDRESS_2',
[
{
functionName: 'ovmSSTORE',
functionParams: [NULL_BYTES32, NULL_BYTES32],
expectedReturnStatus: false,
expectedReturnValues: [
REVERT_FLAGS.STATIC_VIOLATION,
'0x',
GAS_LIMIT / 2,
0,
],
},
{
functionName: 'ovmCREATE',
functionParams: [DUMMY_BYTECODE, false, []],
expectedReturnStatus: false,
expectedReturnValues: [
REVERT_FLAGS.STATIC_VIOLATION,
'0x',
GAS_LIMIT / 2,
0,
],
},
{
functionName: 'ovmSLOAD',
functionParams: [NON_NULL_BYTES32],
expectedReturnStatus: true,
expectedReturnValues: [NULL_BYTES32],
},
{
functionName: 'ovmCALLER',
functionParams: [],
expectedReturnStatus: true,
expectedReturnValues: ['$DUMMY_OVM_ADDRESS_1'],
},
{
functionName: 'ovmADDRESS',
functionParams: [],
expectedReturnStatus: true,
expectedReturnValues: ['$DUMMY_OVM_ADDRESS_2'],
},
],
],
expectedReturnStatus: true,
expectedReturnValues: [],
},
],
],
expectedReturnStatus: true,
expectedReturnValues: [],
},
],
},
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
'$DUMMY_OVM_ADDRESS_1',
[
{
functionName: 'ovmSTATICCALL',
functionParams: [
GAS_LIMIT,
'$DUMMY_OVM_ADDRESS_2',
[
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
'$DUMMY_OVM_ADDRESS_2',
[
{
functionName: 'ovmSLOAD',
functionParams: [NON_NULL_BYTES32],
expectedReturnStatus: true,
expectedReturnValues: [NULL_BYTES32],
},
{
functionName: 'ovmSSTORE',
functionParams: [NULL_BYTES32, NULL_BYTES32],
expectedReturnStatus: false,
expectedReturnValues: [
REVERT_FLAGS.STATIC_VIOLATION,
'0x',
867484476,
2906,
],
},
{
functionName: 'ovmCREATE',
functionParams: [DUMMY_BYTECODE, false, []],
expectedReturnStatus: false,
expectedReturnValues: [
REVERT_FLAGS.STATIC_VIOLATION,
'0x',
867484476,
2906,
],
},
],
],
expectedReturnStatus: true,
expectedReturnValues: [],
},
],
],
expectedReturnStatus: true,
expectedReturnValues: [],
},
],
],
expectedReturnStatus: true,
expectedReturnValues: [],
},
],
},
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
'$DUMMY_OVM_ADDRESS_1',
[
{
functionName: 'ovmSTATICCALL',
functionParams: [
GAS_LIMIT / 2,
'$DUMMY_OVM_ADDRESS_2',
[
{
functionName: 'ovmSTATICCALL',
functionParams: [GAS_LIMIT, '$DUMMY_OVM_ADDRESS_2', []],
expectedReturnStatus: true,
expectedReturnValues: [],
},
{
functionName: 'ovmSSTORE',
functionParams: [NULL_BYTES32, NULL_BYTES32],
expectedReturnStatus: false,
expectedReturnValues: [
REVERT_FLAGS.STATIC_VIOLATION,
'0x',
GAS_LIMIT / 2,
33806,
],
},
],
],
expectedReturnStatus: true,
expectedReturnValues: [],
},
],
],
expectedReturnStatus: true,
expectedReturnValues: [],
},
],
},
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
'$DUMMY_OVM_ADDRESS_1',
[
{
functionName: 'ovmSTATICCALLToRevert',
functionParams: [
GAS_LIMIT / 2,
'$DUMMY_OVM_ADDRESS_2',
[REVERT_FLAGS.STATIC_VIOLATION, '0x', GAS_LIMIT / 2, 0],
],
expectedReturnStatus: true,
expectedReturnValues: [false, '0x'],
},
],
],
expectedReturnStatus: true,
expectedReturnValues: [],
},
],
},
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
'$DUMMY_OVM_ADDRESS_1',
[
{
functionName: 'ovmSTATICCALL',
functionParams: [
GAS_LIMIT,
'$DUMMY_OVM_ADDRESS_1',
[
{
functionName: 'ovmSTATICCALLToRevert',
functionParams: [
GAS_LIMIT / 2,
'$DUMMY_OVM_ADDRESS_2',
[REVERT_FLAGS.STATIC_VIOLATION, '0x', GAS_LIMIT / 2, 0],
],
expectedReturnStatus: true,
expectedReturnValues: [false, '0x'],
},
],
],
expectedReturnStatus: true,
expectedReturnValues: [],
},
],
],
expectedReturnStatus: true,
expectedReturnValues: [],
},
],
},
],
}
runExecutionManagerTest(test_ovmSTATICCALL)
/* Internal Imports */
import {
ExecutionManagerTestRunner,
TestDefinition,
GAS_LIMIT,
NON_NULL_BYTES32,
REVERT_FLAGS,
ZERO_ADDRESS,
VERIFIED_EMPTY_CONTRACT_HASH,
} from '../../../../helpers'
const DUMMY_REVERT_DATA =
'0xdeadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420'
const GAS_METADATA_ADDRESS = '0x06a506a506a506a506a506a506a506a506a506a5'
enum GasMetadataKey {
CURRENT_EPOCH_START_TIMESTAMP,
CUMULATIVE_SEQUENCER_QUEUE_GAS,
CUMULATIVE_L1TOL2_QUEUE_GAS,
PREV_EPOCH_SEQUENCER_QUEUE_GAS,
PREV_EPOCH_L1TOL2_QUEUE_GAS,
}
const keyToBytes32 = (key: GasMetadataKey): string => {
return '0x' + `0${key}`.padStart(64, '0')
}
const test_run: TestDefinition = {
name: 'Basic tests for ovmCALL',
preState: {
ExecutionManager: {
ovmStateManager: '$OVM_STATE_MANAGER',
ovmSafetyChecker: '$OVM_SAFETY_CHECKER',
messageRecord: {
nuisanceGasLeft: GAS_LIMIT,
},
},
StateManager: {
owner: '$OVM_EXECUTION_MANAGER',
accounts: {
$DUMMY_OVM_ADDRESS_1: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_2: {
codeHash: NON_NULL_BYTES32,
ethAddress: '$OVM_CALL_HELPER',
},
$DUMMY_OVM_ADDRESS_3: {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20),
},
},
contractStorage: {
[GAS_METADATA_ADDRESS]: {
[keyToBytes32(GasMetadataKey.CURRENT_EPOCH_START_TIMESTAMP)]: 1,
[keyToBytes32(GasMetadataKey.CUMULATIVE_SEQUENCER_QUEUE_GAS)]: 0,
[keyToBytes32(GasMetadataKey.CUMULATIVE_L1TOL2_QUEUE_GAS)]: 0,
[keyToBytes32(GasMetadataKey.PREV_EPOCH_SEQUENCER_QUEUE_GAS)]: 0,
[keyToBytes32(GasMetadataKey.PREV_EPOCH_L1TOL2_QUEUE_GAS)]: 0,
},
},
verifiedContractStorage: {
[GAS_METADATA_ADDRESS]: {
[keyToBytes32(GasMetadataKey.CURRENT_EPOCH_START_TIMESTAMP)]: true,
[keyToBytes32(GasMetadataKey.CUMULATIVE_SEQUENCER_QUEUE_GAS)]: true,
[keyToBytes32(GasMetadataKey.CUMULATIVE_L1TOL2_QUEUE_GAS)]: true,
[keyToBytes32(GasMetadataKey.PREV_EPOCH_SEQUENCER_QUEUE_GAS)]: true,
[keyToBytes32(GasMetadataKey.PREV_EPOCH_L1TOL2_QUEUE_GAS)]: true,
},
},
},
},
parameters: [
{
name: 'run => ovmCALL(ADDRESS_1) => ovmADDRESS',
focus: true,
steps: [
{
functionName: 'run',
functionParams: {
timestamp: 0,
queueOrigin: 0,
entrypoint: '$OVM_CALL_HELPER',
origin: ZERO_ADDRESS,
msgSender: ZERO_ADDRESS,
gasLimit: GAS_LIMIT,
subSteps: [
{
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_1',
subSteps: [
{
functionName: 'ovmADDRESS',
expectedReturnValue: '$DUMMY_OVM_ADDRESS_1',
},
],
},
expectedReturnStatus: true,
},
],
},
},
],
},
],
}
const runner = new ExecutionManagerTestRunner()
runner.run(test_run)
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract } from 'ethers'
/* Internal Imports */
import { SAFETY_CHECKER_TEST_JSON } from '../../../helpers'
describe('OVM_SafetyChecker', () => {
let OVM_SafetyChecker: Contract
before(async () => {
const Factory__OVM_SafetyChecker = await ethers.getContractFactory(
'OVM_SafetyChecker'
)
OVM_SafetyChecker = await Factory__OVM_SafetyChecker.deploy()
})
describe('isBytecodeSafe()', () => {
for (const testName of Object.keys(SAFETY_CHECKER_TEST_JSON)) {
const test = SAFETY_CHECKER_TEST_JSON[testName]
it(`should correctly classify: ${testName}`, async () => {
expect(await OVM_SafetyChecker.isBytecodeSafe(test.in)).to.equal(
test.out
)
})
}
})
})
...@@ -5,11 +5,7 @@ import { ethers } from '@nomiclabs/buidler' ...@@ -5,11 +5,7 @@ import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory } from 'ethers' import { Contract, ContractFactory } from 'ethers'
/* Internal Imports */ /* Internal Imports */
import { import { DUMMY_ACCOUNTS, DUMMY_BYTES32, toOVMAccount } from '../../../helpers'
DUMMY_ACCOUNTS,
DUMMY_BYTES32,
toOVMAccount
} from '../../../helpers'
describe('OVM_StateManager', () => { describe('OVM_StateManager', () => {
let Factory__OVM_StateManager: ContractFactory let Factory__OVM_StateManager: ContractFactory
...@@ -58,7 +54,9 @@ describe('OVM_StateManager', () => { ...@@ -58,7 +54,9 @@ describe('OVM_StateManager', () => {
expect( expect(
toOVMAccount( toOVMAccount(
await OVM_StateManager.callStatic.getAccount(DUMMY_ACCOUNTS[0].address) await OVM_StateManager.callStatic.getAccount(
DUMMY_ACCOUNTS[0].address
)
) )
).to.deep.equal(DUMMY_ACCOUNTS[0].data) ).to.deep.equal(DUMMY_ACCOUNTS[0].data)
}) })
...@@ -76,7 +74,9 @@ describe('OVM_StateManager', () => { ...@@ -76,7 +74,9 @@ describe('OVM_StateManager', () => {
expect( expect(
toOVMAccount( toOVMAccount(
await OVM_StateManager.callStatic.getAccount(DUMMY_ACCOUNTS[0].address) await OVM_StateManager.callStatic.getAccount(
DUMMY_ACCOUNTS[0].address
)
) )
).to.deep.equal(DUMMY_ACCOUNTS[1].data) ).to.deep.equal(DUMMY_ACCOUNTS[1].data)
}) })
...@@ -107,7 +107,7 @@ describe('OVM_StateManager', () => { ...@@ -107,7 +107,7 @@ describe('OVM_StateManager', () => {
OVM_StateManager.putContractStorage( OVM_StateManager.putContractStorage(
DUMMY_ACCOUNTS[0].address, DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0], DUMMY_BYTES32[0],
DUMMY_BYTES32[1], DUMMY_BYTES32[1]
) )
).to.not.be.reverted ).to.not.be.reverted
}) })
...@@ -116,14 +116,14 @@ describe('OVM_StateManager', () => { ...@@ -116,14 +116,14 @@ describe('OVM_StateManager', () => {
await OVM_StateManager.putContractStorage( await OVM_StateManager.putContractStorage(
DUMMY_ACCOUNTS[0].address, DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0], DUMMY_BYTES32[0],
DUMMY_BYTES32[1], DUMMY_BYTES32[1]
) )
await expect( await expect(
OVM_StateManager.putContractStorage( OVM_StateManager.putContractStorage(
DUMMY_ACCOUNTS[0].address, DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0], DUMMY_BYTES32[0],
DUMMY_BYTES32[2], DUMMY_BYTES32[2]
) )
).to.not.be.reverted ).to.not.be.reverted
}) })
...@@ -134,7 +134,7 @@ describe('OVM_StateManager', () => { ...@@ -134,7 +134,7 @@ describe('OVM_StateManager', () => {
await OVM_StateManager.putContractStorage( await OVM_StateManager.putContractStorage(
DUMMY_ACCOUNTS[0].address, DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0], DUMMY_BYTES32[0],
DUMMY_BYTES32[1], DUMMY_BYTES32[1]
) )
expect( expect(
...@@ -149,13 +149,13 @@ describe('OVM_StateManager', () => { ...@@ -149,13 +149,13 @@ describe('OVM_StateManager', () => {
await OVM_StateManager.putContractStorage( await OVM_StateManager.putContractStorage(
DUMMY_ACCOUNTS[0].address, DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0], DUMMY_BYTES32[0],
DUMMY_BYTES32[1], DUMMY_BYTES32[1]
) )
await OVM_StateManager.putContractStorage( await OVM_StateManager.putContractStorage(
DUMMY_ACCOUNTS[0].address, DUMMY_ACCOUNTS[0].address,
DUMMY_BYTES32[0], DUMMY_BYTES32[0],
DUMMY_BYTES32[2], DUMMY_BYTES32[2]
) )
expect( expect(
......
/* Internal Imports */
import { runExecutionManagerTest } from '../../../helpers/test-utils/test-parsing'
import { NON_NULL_BYTES32, GAS_LIMIT } from '../../../helpers'
runExecutionManagerTest(
{
name: 'Top level test',
preState: {
ExecutionManager: {
ovmStateManager: "$OVM_STATE_MANAGER",
messageRecord: {
nuisanceGasLeft: GAS_LIMIT / 2
}
},
StateManager: {
accounts: {
"$DUMMY_OVM_ADDRESS_1": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_CALL_HELPER"
},
"$DUMMY_OVM_ADDRESS_2": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_CALL_HELPER"
}
}
}
},
parameters: [
{
name: 'Do two ovmCALLs to one address',
postState: {
ExecutionManager: {
messageRecord: {
nuisanceGasLeft: GAS_LIMIT / 2 - (332) * 100 * 1
}
}
},
parameters: [
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmADDRESS',
functionParams: [],
returnStatus: true,
returnValues: ["$DUMMY_OVM_ADDRESS_1"]
},
{
functionName: 'ovmCALLER',
functionParams: [],
returnStatus: true,
returnValues: ["$DUMMY_OVM_ADDRESS_1"]
}
]
],
returnStatus: true,
returnValues: []
},
]
],
returnStatus: true,
returnValues: []
}
]
}
]
},
{
name: 'Do two ovmCALLs to two different addresses',
postState: {
ExecutionManager: {
messageRecord: {
nuisanceGasLeft: GAS_LIMIT / 2 - (332) * 100 * 2
}
}
},
parameters: [
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_2",
[
{
functionName: 'ovmADDRESS',
functionParams: [],
returnStatus: true,
returnValues: ["$DUMMY_OVM_ADDRESS_2"]
},
{
functionName: 'ovmCALLER',
functionParams: [],
returnStatus: true,
returnValues: ["$DUMMY_OVM_ADDRESS_1"]
}
]
],
returnStatus: true,
returnValues: []
},
]
],
returnStatus: true,
returnValues: []
}
]
}
]
}
]
}
)
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory, Signer } from 'ethers'
/* Internal Imports */
import { getProxyManager, ZERO_ADDRESS, NULL_BYTES32 } from '../../../helpers'
const parseQueueElement = (result: any[]): any => {
return {
timestamp: result[0].toNumber(),
batchRoot: result[1],
isL1ToL2Batch: result[2],
}
}
const makeQueueElements = (count: number): any => {
const elements = []
for (let i = 0; i < count; i++) {
elements.push({
timestamp: Date.now(),
batchRoot: NULL_BYTES32,
isL1ToL2Batch: false,
})
}
return elements
}
describe('OVM_L1ToL2TransactionQueue', () => {
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let Proxy_Manager: Contract
before(async () => {
Proxy_Manager = await getProxyManager()
})
let Factory__OVM_L1ToL2TransactionQueue: ContractFactory
before(async () => {
Factory__OVM_L1ToL2TransactionQueue = await ethers.getContractFactory(
'OVM_L1ToL2TransactionQueue'
)
})
let OVM_L1ToL2TransactionQueue: Contract
beforeEach(async () => {
OVM_L1ToL2TransactionQueue = await Factory__OVM_L1ToL2TransactionQueue.deploy(
Proxy_Manager.address
)
})
describe('enqueue()', () => {
it('should allow users to enqueue an element', async () => {
const [element] = makeQueueElements(1)
await expect(OVM_L1ToL2TransactionQueue.enqueue(element)).to.not.be
.reverted
})
it('should allow users to enqueue more than one element', async () => {
const elements = makeQueueElements(10)
for (const element of elements) {
await expect(OVM_L1ToL2TransactionQueue.enqueue(element)).to.not.be
.reverted
}
})
})
describe('dequeue()', () => {
describe('when the sender is not the OVM_CanonicalTransactionChain', () => {
before(async () => {
await Proxy_Manager.setProxy(
'OVM_CanonicalTransactionChain',
ZERO_ADDRESS
)
})
it('should revert', async () => {
await expect(OVM_L1ToL2TransactionQueue.dequeue()).to.be.revertedWith(
'Sender is not allowed to enqueue.'
)
})
})
describe('when the sender is the OVM_CanonicalTransactionChain', () => {
before(async () => {
await Proxy_Manager.setProxy(
'OVM_CanonicalTransactionChain',
await signer.getAddress()
)
})
it('should revert if the queue is empty', async () => {
await expect(OVM_L1ToL2TransactionQueue.dequeue()).to.be.revertedWith(
'Queue is empty.'
)
})
it('should allow users to dequeue an element', async () => {
const [element] = makeQueueElements(1)
await OVM_L1ToL2TransactionQueue.enqueue(element)
await expect(OVM_L1ToL2TransactionQueue.dequeue()).to.not.be.reverted
})
it('should allow users to dequeue more than one element', async () => {
const elements = makeQueueElements(10)
for (const element of elements) {
await OVM_L1ToL2TransactionQueue.enqueue(element)
}
for (const element of elements) {
await expect(OVM_L1ToL2TransactionQueue.dequeue()).to.not.be.reverted
}
})
})
})
describe('size()', () => {
before(async () => {
await Proxy_Manager.setProxy(
'OVM_CanonicalTransactionChain',
await signer.getAddress()
)
})
it('should return zero when no elements are in the queue', async () => {
const size = await OVM_L1ToL2TransactionQueue.size()
expect(size).to.equal(0)
})
it('should increase when new elements are enqueued', async () => {
const elements = makeQueueElements(10)
for (let i = 0; i < elements.length; i++) {
await OVM_L1ToL2TransactionQueue.enqueue(elements[i])
const size = await OVM_L1ToL2TransactionQueue.size()
expect(size).to.equal(i + 1)
}
})
it('should decrease when elements are dequeued', async () => {
const elements = makeQueueElements(10)
for (const element of elements) {
await OVM_L1ToL2TransactionQueue.enqueue(element)
}
for (let i = 0; i < elements.length; i++) {
await OVM_L1ToL2TransactionQueue.dequeue()
const size = await OVM_L1ToL2TransactionQueue.size()
expect(size).to.equal(elements.length - i - 1)
}
})
})
describe('peek()', () => {
before(async () => {
await Proxy_Manager.setProxy(
'OVM_CanonicalTransactionChain',
await signer.getAddress()
)
})
it('should revert when the queue is empty', async () => {
await expect(OVM_L1ToL2TransactionQueue.peek()).to.be.revertedWith(
'Queue is empty.'
)
})
it('should return the front element if only one exists', async () => {
const [element] = makeQueueElements(1)
await OVM_L1ToL2TransactionQueue.enqueue(element)
const front = await OVM_L1ToL2TransactionQueue.peek()
expect(parseQueueElement(front)).to.deep.equal(element)
})
it('should return the front if more than one exists', async () => {
const elements = makeQueueElements(10)
for (const element of elements) {
await OVM_L1ToL2TransactionQueue.enqueue(element)
const front = await OVM_L1ToL2TransactionQueue.peek()
expect(parseQueueElement(front)).to.deep.equal(elements[0])
}
})
it('should return the new front when elements are dequeued', async () => {
const elements = makeQueueElements(10)
for (const element of elements) {
await OVM_L1ToL2TransactionQueue.enqueue(elements)
}
for (let i = 0; i < elements.length - 1; i++) {
const front = await OVM_L1ToL2TransactionQueue.peek()
expect(parseQueueElement(front)).to.deep.equal(elements[i + 1])
await OVM_L1ToL2TransactionQueue.dequeue()
}
})
})
})
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { ContractFactory, Contract, Signer, BigNumber } from 'ethers'
/* Internal Imports */
import {
getProxyManager,
getMockContract,
MockContract,
ZERO_ADDRESS,
NULL_BYTES32,
NON_NULL_BYTES32,
setProxyTarget,
} from '../../../helpers'
const DUMMY_BATCH_HEADER = {
batchIndex: 0,
batchRoot: NULL_BYTES32,
batchSize: 0,
prevTotalElements: 0,
extraData: NULL_BYTES32,
}
const DUMMY_BATCH_PROOF = {
index: 0,
siblings: [NULL_BYTES32],
}
const DUMMY_OVM_TRANSACTION = {
timestamp: 0,
queueOrigin: 0,
entrypoint: ZERO_ADDRESS,
origin: ZERO_ADDRESS,
msgSender: ZERO_ADDRESS,
gasLimit: 0,
data: NULL_BYTES32,
}
describe('OVM_FraudVerifier', () => {
let Proxy_Manager: Contract
before(async () => {
Proxy_Manager = await getProxyManager()
})
let Mock__OVM_StateCommitmentChain: MockContract
let Mock__OVM_CanonicalTransactionChain: MockContract
let Mock__OVM_StateTransitioner: MockContract
let Mock__OVM_StateTransitionerFactory: MockContract
before(async () => {
Mock__OVM_StateCommitmentChain = await getMockContract(
await ethers.getContractFactory('OVM_StateCommitmentChain')
)
Mock__OVM_CanonicalTransactionChain = await getMockContract(
await ethers.getContractFactory('OVM_CanonicalTransactionChain')
)
Mock__OVM_StateTransitioner = await getMockContract(
await ethers.getContractFactory('OVM_StateTransitioner')
)
Mock__OVM_StateTransitionerFactory = await getMockContract(
await ethers.getContractFactory('OVM_StateTransitionerFactory')
)
await setProxyTarget(
Proxy_Manager,
'OVM_StateCommitmentChain',
Mock__OVM_StateCommitmentChain
)
await setProxyTarget(
Proxy_Manager,
'OVM_CanonicalTransactionChain',
Mock__OVM_CanonicalTransactionChain
)
await setProxyTarget(
Proxy_Manager,
'OVM_StateTransitioner',
Mock__OVM_StateTransitioner
)
await setProxyTarget(
Proxy_Manager,
'OVM_StateTransitionerFactory',
Mock__OVM_StateTransitionerFactory
)
Mock__OVM_StateTransitionerFactory.setReturnValues('create', [
Mock__OVM_StateTransitioner.address,
])
})
let Factory__OVM_FraudVerifier: ContractFactory
before(async () => {
Factory__OVM_FraudVerifier = await ethers.getContractFactory(
'OVM_FraudVerifier'
)
})
let OVM_FraudVerifier: Contract
beforeEach(async () => {
OVM_FraudVerifier = await Factory__OVM_FraudVerifier.deploy(
Proxy_Manager.address
)
})
describe('initializeFraudVerification', () => {
describe('when provided an invalid pre-state root inclusion proof', () => {
before(() => {
Mock__OVM_StateCommitmentChain.setReturnValues('verifyElement', [false])
})
it('should revert', async () => {
await expect(
OVM_FraudVerifier.initializeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADER,
DUMMY_BATCH_PROOF,
DUMMY_OVM_TRANSACTION,
DUMMY_BATCH_HEADER,
DUMMY_BATCH_PROOF
)
).to.be.revertedWith('Invalid pre-state root inclusion proof.')
})
})
describe('when provided a valid pre-state root inclusion proof', () => {
before(() => {
Mock__OVM_StateCommitmentChain.setReturnValues('verifyElement', [true])
})
describe('when provided an invalid transaction inclusion proof', () => {
before(() => {
Mock__OVM_CanonicalTransactionChain.setReturnValues('verifyElement', [
false,
])
})
it('should revert', async () => {
await expect(
OVM_FraudVerifier.initializeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADER,
DUMMY_BATCH_PROOF,
DUMMY_OVM_TRANSACTION,
DUMMY_BATCH_HEADER,
DUMMY_BATCH_PROOF
)
).to.be.revertedWith('Invalid transaction inclusion proof.')
})
})
describe('when provided a valid transaction inclusion proof', () => {
before(() => {
Mock__OVM_CanonicalTransactionChain.setReturnValues('verifyElement', [
true,
])
})
it('should deploy a new state transitioner', async () => {
await expect(
OVM_FraudVerifier.initializeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADER,
DUMMY_BATCH_PROOF,
DUMMY_OVM_TRANSACTION,
DUMMY_BATCH_HEADER,
DUMMY_BATCH_PROOF
)
).to.not.be.reverted
expect(
await OVM_FraudVerifier.getStateTransitioner(NULL_BYTES32)
).to.equal(Mock__OVM_StateTransitioner.address)
})
})
})
})
describe('finalizeFraudVerification', () => {
beforeEach(async () => {
Mock__OVM_StateCommitmentChain.setReturnValues('verifyElement', [true])
Mock__OVM_CanonicalTransactionChain.setReturnValues('verifyElement', [
true,
])
await OVM_FraudVerifier.initializeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADER,
DUMMY_BATCH_PROOF,
DUMMY_OVM_TRANSACTION,
DUMMY_BATCH_HEADER,
DUMMY_BATCH_PROOF
)
})
describe('when the transition process is not complete', () => {
before(() => {
Mock__OVM_StateTransitioner.setReturnValues('isComplete', [false])
})
it('should revert', async () => {
await expect(
OVM_FraudVerifier.finalizeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADER,
DUMMY_BATCH_PROOF,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADER,
DUMMY_BATCH_PROOF
)
).to.be.revertedWith(
'State transition process must be completed prior to finalization.'
)
})
})
describe('when the transition process is complete', () => {
before(() => {
Mock__OVM_StateTransitioner.setReturnValues('isComplete', [true])
})
describe('when provided an invalid post-state root index', () => {
const batchProof = {
...DUMMY_BATCH_PROOF,
index: DUMMY_BATCH_PROOF.index + 2,
}
it('should revert', async () => {
await expect(
OVM_FraudVerifier.finalizeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADER,
DUMMY_BATCH_PROOF,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADER,
batchProof
)
).to.be.revertedWith('Invalid post-state root index.')
})
})
describe('when provided a valid post-state root index', () => {
const batchProof = {
...DUMMY_BATCH_PROOF,
index: DUMMY_BATCH_PROOF.index + 1,
}
describe('when provided an invalid pre-state root inclusion proof', () => {
beforeEach(() => {
Mock__OVM_StateCommitmentChain.setReturnValues('verifyElement', [
false,
])
})
it('should revert', async () => {
await expect(
OVM_FraudVerifier.finalizeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADER,
DUMMY_BATCH_PROOF,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADER,
batchProof
)
).to.be.revertedWith('Invalid pre-state root inclusion proof.')
})
})
describe('when provided a valid pre-state root inclusion proof', () => {
before(() => {
Mock__OVM_StateCommitmentChain.setReturnValues('verifyElement', [
true,
])
})
describe('when provided an invalid post-state root inclusion proof', () => {
beforeEach(() => {
Mock__OVM_StateCommitmentChain.setReturnValues(
'verifyElement',
(stateRoot: string, ...args: any) => {
return [stateRoot !== NON_NULL_BYTES32]
}
)
})
it('should revert', async () => {
await expect(
OVM_FraudVerifier.finalizeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADER,
DUMMY_BATCH_PROOF,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADER,
batchProof
)
).to.be.revertedWith('Invalid post-state root inclusion proof.')
})
})
describe('when provided a valid post-state root inclusion proof', () => {
before(() => {
Mock__OVM_StateCommitmentChain.setReturnValues('verifyElement', [
true,
])
})
describe('when the provided post-state root does not differ from the computed one', () => {
before(() => {
Mock__OVM_StateTransitioner.setReturnValues(
'getPostStateRoot',
[NON_NULL_BYTES32]
)
})
it('should revert', async () => {
await expect(
OVM_FraudVerifier.finalizeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADER,
DUMMY_BATCH_PROOF,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADER,
batchProof
)
).to.be.revertedWith(
'State transition has not been proven fraudulent.'
)
})
})
describe('when the provided post-state root differs from the computed one', () => {
before(() => {
Mock__OVM_StateTransitioner.setReturnValues(
'getPostStateRoot',
[NULL_BYTES32]
)
})
it('should succeed and attempt to delete a state batch', async () => {
await OVM_FraudVerifier.finalizeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADER,
DUMMY_BATCH_PROOF,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADER,
batchProof
)
expect(
Mock__OVM_StateCommitmentChain.getCallData(
'deleteStateBatch',
0
)
).to.deep.equal([
Object.values(DUMMY_BATCH_HEADER).map((value) => {
return Number.isInteger(value)
? BigNumber.from(value)
: value
}),
])
})
})
})
})
})
})
})
})
/* tslint:disable:no-empty */
import { expect } from '../../../setup'
describe('OVM_StateTransitioner', () => {
describe('proveContractState', () => {
describe('when provided an invalid code hash', () => {
it('should revert', async () => {})
})
describe('when provided a valid code hash', () => {
describe('when provided an invalid account inclusion proof', () => {
it('should revert', async () => {})
})
describe('when provided a valid account inclusion proof', () => {})
})
})
describe('proveStorageSlot', () => {
describe('when the corresponding account is not proven', () => {
it('should revert', async () => {})
})
describe('when the corresponding account is proven', () => {
describe('when provided an invalid slot inclusion proof', () => {
it('should revert', async () => {})
})
describe('when provided a valid slot inclusion proof', () => {})
})
})
describe('applyTransaction', () => {
// TODO
})
describe('commitContractState', () => {
describe('when the account was not changed', () => {
it('should revert', async () => {})
})
describe('when the account was changed', () => {
describe('when the account has not been committed', () => {
it('should commit the account and update the state', async () => {})
})
describe('when the account was already committed', () => {
it('should revert', () => {})
})
})
})
describe('commitStorageSlot', () => {
describe('when the slot was not changed', () => {
it('should revert', async () => {})
})
describe('when the slot was changed', () => {
describe('when the slot has not been committed', () => {
it('should commit the slot and update the state', async () => {})
})
describe('when the slot was already committed', () => {
it('should revert', () => {})
})
})
})
describe('completeTransition', () => {
describe('when there are uncommitted accounts', () => {
it('should revert', async () => {})
})
describe('when there are uncommitted storage slots', () => {
it('should revert', async () => {})
})
describe('when all state changes are committed', () => {})
})
})
...@@ -2,19 +2,22 @@ ...@@ -2,19 +2,22 @@
import fsExtra from 'fs-extra' import fsExtra from 'fs-extra'
import { internalTask } from '@nomiclabs/buidler/config' import { internalTask } from '@nomiclabs/buidler/config'
import { pluralize } from '@nomiclabs/buidler/internal/util/strings' import { pluralize } from '@nomiclabs/buidler/internal/util/strings'
import { getArtifactFromContractOutput, saveArtifact } from '@nomiclabs/buidler/internal/artifacts' import {
getArtifactFromContractOutput,
saveArtifact,
} from '@nomiclabs/buidler/internal/artifacts'
import { import {
TASK_COMPILE_GET_COMPILER_INPUT, TASK_COMPILE_GET_COMPILER_INPUT,
TASK_BUILD_ARTIFACTS, TASK_BUILD_ARTIFACTS,
TASK_COMPILE_GET_SOURCE_PATHS, TASK_COMPILE_GET_SOURCE_PATHS,
TASK_COMPILE_CHECK_CACHE, TASK_COMPILE_CHECK_CACHE,
TASK_COMPILE_COMPILE TASK_COMPILE_COMPILE,
} from '@nomiclabs/buidler/builtin-tasks/task-names' } from '@nomiclabs/buidler/builtin-tasks/task-names'
internalTask( internalTask(
TASK_COMPILE_GET_COMPILER_INPUT, TASK_COMPILE_GET_COMPILER_INPUT,
async (_, { config, run }, runSuper) => { async (_, { config, run }, runSuper) => {
const input = await runSuper(); const input = await runSuper()
// Insert the "storageLayout" input option. // Insert the "storageLayout" input option.
input.settings.outputSelection['*']['*'].push('storageLayout') input.settings.outputSelection['*']['*'].push('storageLayout')
...@@ -25,51 +28,51 @@ internalTask( ...@@ -25,51 +28,51 @@ internalTask(
internalTask(TASK_BUILD_ARTIFACTS).setAction( internalTask(TASK_BUILD_ARTIFACTS).setAction(
async ({ force }, { config, run }) => { async ({ force }, { config, run }) => {
const sources = await run(TASK_COMPILE_GET_SOURCE_PATHS); const sources = await run(TASK_COMPILE_GET_SOURCE_PATHS)
if (sources.length === 0) { if (sources.length === 0) {
console.log("No Solidity source file available."); console.log('No Solidity source file available.')
return; return
} }
const isCached: boolean = await run(TASK_COMPILE_CHECK_CACHE, { force }); const isCached: boolean = await run(TASK_COMPILE_CHECK_CACHE, { force })
if (isCached) { if (isCached) {
console.log( console.log(
"All contracts have already been compiled, skipping compilation." 'All contracts have already been compiled, skipping compilation.'
); )
return; return
} }
const compilationOutput = await run(TASK_COMPILE_COMPILE); const compilationOutput = await run(TASK_COMPILE_COMPILE)
if (compilationOutput === undefined) { if (compilationOutput === undefined) {
return; return
} }
await fsExtra.ensureDir(config.paths.artifacts); await fsExtra.ensureDir(config.paths.artifacts)
let numberOfContracts = 0; let numberOfContracts = 0
for (const file of Object.values<any>(compilationOutput.contracts)) { for (const file of Object.values<any>(compilationOutput.contracts)) {
for (const [contractName, contractOutput] of Object.entries(file)) { for (const [contractName, contractOutput] of Object.entries(file)) {
const artifact: any = getArtifactFromContractOutput( const artifact: any = getArtifactFromContractOutput(
contractName, contractName,
contractOutput contractOutput
); )
numberOfContracts += 1; numberOfContracts += 1
// Only difference here, set the "storageLayout" field of the artifact. // Only difference here, set the "storageLayout" field of the artifact.
artifact.storageLayout = (contractOutput as any).storageLayout artifact.storageLayout = (contractOutput as any).storageLayout
await saveArtifact(config.paths.artifacts, artifact); await saveArtifact(config.paths.artifacts, artifact)
} }
} }
console.log( console.log(
"Compiled", 'Compiled',
numberOfContracts, numberOfContracts,
pluralize(numberOfContracts, "contract"), pluralize(numberOfContracts, 'contract'),
"successfully" 'successfully'
); )
} }
) )
import { ethers } from '@nomiclabs/buidler' import { ethers } from '@nomiclabs/buidler'
import { hexZeroPad } from 'ethers/lib/utils'
export const encodeRevertData = ( export const encodeRevertData = (
flag: number, flag: number,
...@@ -6,10 +7,32 @@ export const encodeRevertData = ( ...@@ -6,10 +7,32 @@ export const encodeRevertData = (
nuisanceGasLeft: number = 0, nuisanceGasLeft: number = 0,
ovmGasRefund: number = 0 ovmGasRefund: number = 0
): string => { ): string => {
return ethers.utils.defaultAbiCoder.encode( const abiEncoded: string = ethers.utils.defaultAbiCoder.encode(
['uint256','uint256','uint256','bytes'], ['uint256', 'uint256', 'uint256', 'bytes'],
[flag, nuisanceGasLeft, ovmGasRefund, data] [flag, nuisanceGasLeft, ovmGasRefund, data]
) )
return abiEncoded
}
export const decodeRevertData = (revertData: string): any => {
// const length: number = revertData.length/2 - 1
// const reencodedRevertData = '0x' + hexZeroPad('0x' + length.toString(16), 32) + revertData.slice(2)
const decoded = ethers.utils.defaultAbiCoder.decode(
['uint256', 'uint256', 'uint256', 'bytes'],
revertData
)
console.log(`flag is: ${decoded[0].toNumber()}`)
return (
'[revertFlag:' +
Object.keys(REVERT_FLAGS)[decoded[0]] +
', nuisanceGasLeft:' +
decoded[1] +
', ovmGasRefund: ' +
decoded[2] +
', data: ' +
decoded[3] +
']'
)
} }
export const REVERT_FLAGS = { export const REVERT_FLAGS = {
...@@ -18,5 +41,8 @@ export const REVERT_FLAGS = { ...@@ -18,5 +41,8 @@ export const REVERT_FLAGS = {
INTENTIONAL_REVERT: 2, INTENTIONAL_REVERT: 2,
EXCEEDS_NUISANCE_GAS: 3, EXCEEDS_NUISANCE_GAS: 3,
INVALID_STATE_ACCESS: 4, INVALID_STATE_ACCESS: 4,
UNSAFE_BYTECODE: 5 UNSAFE_BYTECODE: 5,
CREATE_COLLISION: 6,
STATIC_VIOLATION: 7,
CREATE_EXCEPTION: 8,
} }
...@@ -19,3 +19,6 @@ export const NULL_BYTES32 = makeHexString('00', 32) ...@@ -19,3 +19,6 @@ export const NULL_BYTES32 = makeHexString('00', 32)
export const NON_NULL_BYTES32 = makeHexString('11', 32) export const NON_NULL_BYTES32 = makeHexString('11', 32)
export const ZERO_ADDRESS = makeAddress('00') export const ZERO_ADDRESS = makeAddress('00')
export const NON_ZERO_ADDRESS = makeAddress('11') export const NON_ZERO_ADDRESS = makeAddress('11')
export const VERIFIED_EMPTY_CONTRACT_HASH =
'0x00004B1DC0DE000000004B1DC0DE000000004B1DC0DE000000004B1DC0DE0000'
import { create2Tests } from './json/create2.test.json'
import { rlpTests } from './json/rlp.test.json'
import { safetyCheckerTests } from './json/safety-checker.test.json'
export const RLP_TEST_JSON = rlpTests
export const CREATE2_TEST_JSON = create2Tests
export const SAFETY_CHECKER_TEST_JSON = safetyCheckerTests
{
"source": "https://eips.ethereum.org/EIPS/eip-1014",
"notes": "added additional tests with more bytecode",
"date": "2020-01-10",
"create2Tests": {
"all zero values": {
"address": "0x0000000000000000000000000000000000000000",
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
"init_code": "0x00",
"result": "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38"
},
"non-zero address": {
"address": "0xdeadbeef00000000000000000000000000000000",
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
"init_code": "0x00",
"result": "0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3"
},
"non-zero address and salt": {
"address": "0xdeadbeef00000000000000000000000000000000",
"salt": "0x000000000000000000000000feed000000000000000000000000000000000000",
"init_code": "0x00",
"result": "0xD04116cDd17beBE565EB2422F2497E06cC1C9833"
},
"non-zero init code": {
"address": "0x0000000000000000000000000000000000000000",
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
"init_code": "0xdeadbeef",
"result": "0x70f2b2914A2a4b783FaEFb75f459A580616Fcb5e"
},
"non-zero address, salt, and short init code": {
"address": "0x00000000000000000000000000000000deadbeef",
"salt": "0x00000000000000000000000000000000000000000000000000000000cafebabe",
"init_code": "0xdeadbeef",
"result": "0x60f3f640a8508fC6a86d45DF051962668E1e8AC7"
},
"non-zero address, salt, and medium init code": {
"address": "0x00000000000000000000000000000000deadbeef",
"salt": "0x00000000000000000000000000000000000000000000000000000000cafebabe",
"init_code": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
"result": "0x1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C"
},
"non-zero address, salt, and long init code": {
"address": "0x00000000000000000000000000000000deadbeef",
"salt": "0x00000000000000000000000000000000000000000000000000000000cafebabe",
"init_code": "0x61309c56600035601c52740100000000000000000000000000000000000000006020526f7fffffffffffffffffffffffffffffff6040527fffffffffffffffffffffffffffffffff8000000000000000000000000000000060605274012a05f1fffffffffffffffffffffffffdabf41c006080527ffffffffffffffffffffffffed5fa0e000000000000000000000000000000000060a0526366d38203600051141561013b57602060046101403734156100b457600080fd5b60043560205181106100c557600080fd5b506000610140511415600654156007541516166100e157600080fd5b33600755610140516006557f556e6973776170205631000000000000000000000000000000000000000000006000557f554e492d563100000000000000000000000000000000000000000000000000006001556012600255005b63422f104360005114156105ab5760606004610140376000341160006101605111164261018051111661016d57600080fd5b6003546101a05260006101a051111561043e576000610140511161019057600080fd5b343031101561019e57600080fd5b343031036103a0526006543b6101b357600080fd5b6006543014156101c257600080fd5b602061046060246370a082316103e05230610400526103fc6006545afa6101e857600080fd5b600050610460516103c0526103a05161020057600080fd5b6103a05134151561021257600061022f565b6103c051346103c0513402041461022857600080fd5b6103c05134025b0460016103a05161023f57600080fd5b6103a05134151561025157600061026e565b6103c051346103c0513402041461026757600080fd5b6103c05134025b0401101561027b57600080fd5b60016103a05161028a57600080fd5b6103a05134151561029c5760006102b9565b6103c051346103c051340204146102b257600080fd5b6103c05134025b0401610480526103a0516102cc57600080fd5b6103a0513415156102de5760006102fb565b6101a051346101a051340204146102f457600080fd5b6101a05134025b046104a052610140516104a0511015610480516101605110151661031e57600080fd5b60043360e05260c052604060c02080546104a051825401101561034057600080fd5b6104a0518154018155506101a0516104a0516101a05101101561036257600080fd5b6104a0516101a051016003556006543b61037b57600080fd5b60065430141561038a57600080fd5b602061058060646323b872dd6104c052336104e052306105005261048051610520526104dc60006006545af16103bf57600080fd5b600050610580516103cf57600080fd5b6104805134337f06239653922ac7bea6aa2b19dc486b9361821d37712eb796adfd38d81de278ca60006000a46104a0516105a0523360007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206105a0a36104a05160005260206000f36105a9565b633b9aca003410156000600654141560006007541415161661045f57600080fd5b306007543b61046d57600080fd5b60075430141561047c57600080fd5b602061024060246306f2bf626101c0526006546101e0526101dc6007545afa6104a457600080fd5b60005061024051146104b557600080fd5b6101605161026052303161028052610280516003556102805160043360e05260c052604060c020556006543b6104ea57600080fd5b6006543014156104f957600080fd5b602061036060646323b872dd6102a052336102c052306102e05261026051610300526102bc60006006545af161052e57600080fd5b6000506103605161053e57600080fd5b6102605134337f06239653922ac7bea6aa2b19dc486b9361821d37712eb796adfd38d81de278ca60006000a461028051610380523360007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6020610380a36102805160005260206000f35b005b63f88bf15a600051141561084a57608060046101403734156105cc57600080fd5b600061018051116000610160511116426101a051116000610140511116166105f357600080fd5b6003546101c05260006101c0511161060a57600080fd5b6006543b61061757600080fd5b60065430141561062657600080fd5b602061028060246370a0823161020052306102205261021c6006545afa61064c57600080fd5b600050610280516101e0526101c05161066457600080fd5b6101c051610140511515610679576000610699565b30316101405130316101405102041461069157600080fd5b303161014051025b046102a0526101c0516106ab57600080fd5b6101c0516101405115156106c05760006106e6565b6101e051610140516101e051610140510204146106dc57600080fd5b6101e05161014051025b046102c052610180516102c0511015610160516102a05110151661070957600080fd5b60043360e05260c052604060c020610140518154101561072857600080fd5b61014051815403815550610140516101c051101561074557600080fd5b610140516101c0510360035560006000600060006102a051336000f161076a57600080fd5b6006543b61077757600080fd5b60065430141561078657600080fd5b6020610380604463a9059cbb6102e05233610300526102c051610320526102fc60006006545af16107b657600080fd5b600050610380516107c657600080fd5b6102c0516102a051337f0fbf06c058b90cb038a618f8c2acbf6145f8b3570fd1fa56abb8f0f3f05b36e860006000a4610140516103a0526000337fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206103a0a360406103c0526103e06102a05181526102c0518160200152506103c0516103e0f3005b6000156109c6575b6101a05261014052610160526101805260006101805111600061016051111661087a57600080fd5b61014051151561088b5760006108ae565b6103e5610140516103e5610140510204146108a557600080fd5b6103e561014051025b6101c0526101c05115156108c35760006108e9565b610180516101c051610180516101c0510204146108df57600080fd5b610180516101c051025b6101e0526101605115156108fe576000610921565b6103e8610160516103e86101605102041461091857600080fd5b6103e861016051025b6101c051610160511515610936576000610959565b6103e8610160516103e86101605102041461095057600080fd5b6103e861016051025b01101561096557600080fd5b6101c05161016051151561097a57600061099d565b6103e8610160516103e86101605102041461099457600080fd5b6103e861016051025b0161020052610200516109af57600080fd5b610200516101e051046000526000516101a0515650005b600015610bf3575b6101a0526101405261016052610180526000610180511160006101605111166109f657600080fd5b610160511515610a07576000610a2d565b61014051610160516101405161016051020414610a2357600080fd5b6101405161016051025b1515610a3a576000610af6565b6103e8610160511515610a4e576000610a74565b61014051610160516101405161016051020414610a6a57600080fd5b6101405161016051025b6103e8610160511515610a88576000610aae565b61014051610160516101405161016051020414610aa457600080fd5b6101405161016051025b020414610aba57600080fd5b6103e8610160511515610ace576000610af4565b61014051610160516101405161016051020414610aea57600080fd5b6101405161016051025b025b6101c05261014051610180511015610b0d57600080fd5b6101405161018051031515610b23576000610b8e565b6103e561014051610180511015610b3957600080fd5b6101405161018051036103e561014051610180511015610b5857600080fd5b610140516101805103020414610b6d57600080fd5b6103e561014051610180511015610b8357600080fd5b610140516101805103025b6101e0526101e051610b9f57600080fd5b6101e0516101c0510460016101e051610bb757600080fd5b6101e0516101c05104011015610bcc57600080fd5b60016101e051610bdb57600080fd5b6101e0516101c05104016000526000516101a0515650005b600015610df4575b6101e0526101405261016052610180526101a0526101c0526000610160511160006101405111164261018051101516610c3357600080fd5b6006543b610c4057600080fd5b600654301415610c4f57600080fd5b60206102a060246370a0823161022052306102405261023c6006545afa610c7557600080fd5b6000506102a051610200526101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a0516102c0516389f2a8716102e05261014051610300526101405130311015610cd657600080fd5b6101405130310361032052610200516103405261034051610320516103005160065801610852565b6103a0526102c0526102a05261028052610260526102405261022052610200526101e0526101c0526101a0526101805261016052610140526103a0516102c052610160516102c0511015610d5157600080fd5b6006543b610d5e57600080fd5b600654301415610d6d57600080fd5b6020610460604463a9059cbb6103c0526101c0516103e0526102c051610400526103dc60006006545af1610da057600080fd5b60005061046051610db057600080fd5b6102c051610140516101a0517fcd60aa75dea3072fbc07ae6d7d856b5dc5f4eee88854f5b4abf7b680ef8bc50f60006000a46102c0516000526000516101e0515650005b63f39b5b9b6000511415610e715760406004610140376101405161016051638c717a3361018052346101a052610140516101c052610160516101e0523361020052336102205261022051610200516101e0516101c0516101a05160065801610bfb565b6102805261016052610140526102805160005260206000f3005b63ad65d76d6000511415610f245760606004610140376044356020518110610e9857600080fd5b5060006101805114153061018051141516610eb257600080fd5b610140516101605161018051638c717a336101a052346101c052610140516101e0526101605161020052336102205261018051610240526102405161022051610200516101e0516101c05160065801610bfb565b6102a0526101805261016052610140526102a05160005260206000f3005b60001561116c575b6101e0526101405261016052610180526101a0526101c0526000610160511160006101405111164261018051101516610f6457600080fd5b6006543b610f7157600080fd5b600654301415610f8057600080fd5b60206102a060246370a0823161022052306102405261023c6006545afa610fa657600080fd5b6000506102a051610200526101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a0516102c05163fd11c2236102e0526101405161030052610160513031101561100757600080fd5b61016051303103610320526102005161034052610340516103205161030051600658016109ce565b6103a0526102c0526102a05261028052610260526102405261022052610200526101e0526101c0526101a0526101805261016052610140526103a0516102c05260016102c051026103e0526103e05161016051101561108d57600080fd5b6103e05161016051036103c05260006103c05111156110c35760006000600060006103c0516101a0516000f16110c257600080fd5b5b6006543b6110d057600080fd5b6006543014156110df57600080fd5b60206104a0604463a9059cbb610400526101c05161042052610140516104405261041c60006006545af161111257600080fd5b6000506104a05161112257600080fd5b6101405160016102c051026101a0517fcd60aa75dea3072fbc07ae6d7d856b5dc5f4eee88854f5b4abf7b680ef8bc50f60006000a460016102c051026000526000516101e0515650005b636b1d4db760005114156111e95760406004610140376101405161016051632dff394e61018052610140516101a052346101c052610160516101e0523361020052336102205261022051610200516101e0516101c0516101a05160065801610f2c565b6102805261016052610140526102805160005260206000f3005b630b573638600051141561129c576060600461014037604435602051811061121057600080fd5b506000610180511415306101805114151661122a57600080fd5b610140516101605161018051632dff394e6101a052610140516101c052346101e0526101605161020052336102205261018051610240526102405161022051610200516101e0516101c05160065801610f2c565b6102a0526101805261016052610140526102a05160005260206000f3005b6000156114b3575b6101e0526101405261016052610180526101a0526101c05260006101605111600061014051111642610180511015166112dc57600080fd5b6006543b6112e957600080fd5b6006543014156112f857600080fd5b60206102a060246370a0823161022052306102405261023c6006545afa61131e57600080fd5b6000506102a051610200526101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a0516102c0516389f2a8716102e0526101405161030052610200516103205230316103405261034051610320516103005160065801610852565b6103a0526102c0526102a05261028052610260526102405261022052610200526101e0526101c0526101a0526101805261016052610140526103a0516102c05260016102c051026103c052610160516103c05110156113ef57600080fd5b60006000600060006103c0516101c0516000f161140b57600080fd5b6006543b61141857600080fd5b60065430141561142757600080fd5b60206104a060646323b872dd6103e0526101a05161040052306104205261014051610440526103fc60006006545af161145f57600080fd5b6000506104a05161146f57600080fd5b6103c051610140516101a0517f7f4091b46c33e918a0f3aa42307641d17bb67029427a5369e54b35398423870560006000a46103c0516000526000516101e0515650005b6395e3c50b600051141561154657606060046101403734156114d457600080fd5b61014051610160516101805163fa1bb7be6101a052610140516101c052610160516101e0526101805161020052336102205233610240526102405161022051610200516101e0516101c051600658016112a4565b6102a0526101805261016052610140526102a05160005260206000f3005b637237e031600051141561160f576080600461014037341561156757600080fd5b606435602051811061157857600080fd5b5060006101a0511415306101a05114151661159257600080fd5b6101405161016051610180516101a05163fa1bb7be6101c052610140516101e0526101605161020052610180516102205233610240526101a05161026052610260516102405161022051610200516101e051600658016112a4565b6102c0526101a0526101805261016052610140526102c05160005260206000f3005b600015611813575b6101e0526101405261016052610180526101a0526101c05260006101405111426101805110151661164757600080fd5b6006543b61165457600080fd5b60065430141561166357600080fd5b60206102a060246370a0823161022052306102405261023c6006545afa61168957600080fd5b6000506102a051610200526101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a0516102c05163fd11c2236102e05261014051610300526102005161032052303161034052610340516103205161030051600658016109ce565b6103a0526102c0526102a05261028052610260526102405261022052610200526101e0526101c0526101a0526101805261016052610140526103a0516102c0526102c05161016051101561174f57600080fd5b6000600060006000610140516101c0516000f161176b57600080fd5b6006543b61177857600080fd5b60065430141561178757600080fd5b602061048060646323b872dd6103c0526101a0516103e05230610400526102c051610420526103dc60006006545af16117bf57600080fd5b600050610480516117cf57600080fd5b610140516102c0516101a0517f7f4091b46c33e918a0f3aa42307641d17bb67029427a5369e54b35398423870560006000a46102c0516000526000516101e0515650005b63013efd8b60005114156118a6576060600461014037341561183457600080fd5b61014051610160516101805163984fe8f66101a052610140516101c052610160516101e0526101805161020052336102205233610240526102405161022051610200516101e0516101c05160065801611617565b6102a0526101805261016052610140526102a05160005260206000f3005b63d4e4841d600051141561196f57608060046101403734156118c757600080fd5b60643560205181106118d857600080fd5b5060006101a0511415306101a0511415166118f257600080fd5b6101405161016051610180516101a05163984fe8f66101c052610140516101e0526101605161020052610180516102205233610240526101a05161026052610260516102405161022051610200516101e05160065801611617565b6102c0526101a0526101805261016052610140526102c05160005260206000f3005b600015611c0a575b610220526101405261016052610180526101a0526101c0526101e0526102005260006101805111600061016051111660006101405111426101a051101516166119bf57600080fd5b600061020051141530610200511415166119d857600080fd5b6006543b6119e557600080fd5b6006543014156119f457600080fd5b60206102e060246370a0823161026052306102805261027c6006545afa611a1a57600080fd5b6000506102e051610240526101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a0516102c0516102e051610300516389f2a871610320526101405161034052610240516103605230316103805261038051610360516103405160065801610852565b6103e052610300526102e0526102c0526102a05261028052610260526102405261022052610200526101e0526101c0526101a0526101805261016052610140526103e05161030052600161030051026104005261018051610400511015611afb57600080fd5b6006543b611b0857600080fd5b600654301415611b1757600080fd5b60206104e060646323b872dd610420526101c051610440523061046052610140516104805261043c60006006545af1611b4f57600080fd5b6000506104e051611b5f57600080fd5b610200513b611b6d57600080fd5b61020051301415611b7d57600080fd5b60206105e0606463ad65d76d6105205261016051610540526101a051610560526101e0516105805261053c61040051610200515af1611bbb57600080fd5b6000506105e0516105005261040051610140516101c0517f7f4091b46c33e918a0f3aa42307641d17bb67029427a5369e54b35398423870560006000a461050051600052600051610220515650005b63ddf7e1a76000511415611d575760a06004610140373415611c2b57600080fd5b6084356020518110611c3c57600080fd5b506007543b611c4a57600080fd5b600754301415611c5957600080fd5b602061028060246306f2bf62610200526101c0516102205261021c6007545afa611c8257600080fd5b600050610280516101e0526101405161016051610180516101a0516101c0516101e051610200516102205161024051610260516102805163204ea33b6102a052610140516102c052610160516102e05261018051610300526101a05161032052336103405233610360526101e0516103805261038051610360516103405161032051610300516102e0516102c05160065801611977565b6103e05261028052610260526102405261022052610200526101e0526101c0526101a0526101805261016052610140526103e05160005260206000f3005b63f552d91b6000511415611ec15760c06004610140373415611d7857600080fd5b6084356020518110611d8957600080fd5b5060a4356020518110611d9b57600080fd5b506007543b611da957600080fd5b600754301415611db857600080fd5b60206102a060246306f2bf62610220526101e0516102405261023c6007545afa611de157600080fd5b6000506102a051610200526101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a05163204ea33b6102c052610140516102e052610160516103005261018051610320526101a0516103405233610360526101c05161038052610200516103a0526103a05161038051610360516103405161032051610300516102e05160065801611977565b610400526102a05261028052610260526102405261022052610200526101e0526101c0526101a0526101805261016052610140526104005160005260206000f3005b6000156121d7575b610220526101405261016052610180526101a0526101c0526101e05261020052600061018051116000610140511116426101a051101516611f0957600080fd5b60006102005114153061020051141516611f2257600080fd5b610200513b611f3057600080fd5b61020051301415611f4057600080fd5b60206102e060246359e9486261026052610140516102805261027c610200515afa611f6a57600080fd5b6000506102e051610240526006543b611f8257600080fd5b600654301415611f9157600080fd5b60206103a060246370a0823161032052306103405261033c6006545afa611fb757600080fd5b6000506103a051610300526101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a0516102c0516102e05161030051610320516103405161036051610380516103a0516103c05163fd11c2236103e05261024051610400526103005161042052303161044052610440516104205161040051600658016109ce565b6104a0526103c0526103a05261038052610360526103405261032052610300526102e0526102c0526102a05261028052610260526102405261022052610200526101e0526101c0526101a0526101805261016052610140526104a0516103c052610240516101805110156103c051610160511015166120c857600080fd5b6006543b6120d557600080fd5b6006543014156120e457600080fd5b602061058060646323b872dd6104c0526101c0516104e05230610500526103c051610520526104dc60006006545af161211c57600080fd5b6000506105805161212c57600080fd5b610200513b61213a57600080fd5b6102005130141561214a57600080fd5b60206106806064630b5736386105c052610140516105e0526101a051610600526101e051610620526105dc61024051610200515af161218857600080fd5b600050610680516105a052610240516103c0516101c0517f7f4091b46c33e918a0f3aa42307641d17bb67029427a5369e54b35398423870560006000a46103c051600052600051610220515650005b63b040d54560005114156123245760a060046101403734156121f857600080fd5b608435602051811061220957600080fd5b506007543b61221757600080fd5b60075430141561222657600080fd5b602061028060246306f2bf62610200526101c0516102205261021c6007545afa61224f57600080fd5b600050610280516101e0526101405161016051610180516101a0516101c0516101e0516102005161022051610240516102605161028051631a7b28f26102a052610140516102c052610160516102e05261018051610300526101a05161032052336103405233610360526101e0516103805261038051610360516103405161032051610300516102e0516102c05160065801611ec9565b6103e05261028052610260526102405261022052610200526101e0526101c0526101a0526101805261016052610140526103e05160005260206000f3005b63f3c0efe9600051141561248e5760c0600461014037341561234557600080fd5b608435602051811061235657600080fd5b5060a435602051811061236857600080fd5b506007543b61237657600080fd5b60075430141561238557600080fd5b60206102a060246306f2bf62610220526101e0516102405261023c6007545afa6123ae57600080fd5b6000506102a051610200526101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a051631a7b28f26102c052610140516102e052610160516103005261018051610320526101a0516103405233610360526101c05161038052610200516103a0526103a05161038051610360516103405161032051610300516102e05160065801611ec9565b610400526102a05261028052610260526102405261022052610200526101e0526101c0526101a0526101805261016052610140526104005160005260206000f3005b63b1cb43bf600051141561255b5760a060046101403734156124af57600080fd5b60843560205181106124c057600080fd5b506101405161016051610180516101a0516101c05163204ea33b6101e0526101405161020052610160516102205261018051610240526101a051610260523361028052336102a0526101c0516102c0526102c0516102a051610280516102605161024051610220516102005160065801611977565b610320526101c0526101a0526101805261016052610140526103205160005260206000f3005b63ec384a3e60005114156126555760c0600461014037341561257c57600080fd5b608435602051811061258d57600080fd5b5060a435602051811061259f57600080fd5b50306101c05114156125b057600080fd5b6101405161016051610180516101a0516101c0516101e05163204ea33b610200526101405161022052610160516102405261018051610260526101a05161028052336102a0526101c0516102c0526101e0516102e0526102e0516102c0516102a0516102805161026051610240516102205160065801611977565b610340526101e0526101c0526101a0526101805261016052610140526103405160005260206000f3005b63ea650c7d60005114156127225760a0600461014037341561267657600080fd5b608435602051811061268757600080fd5b506101405161016051610180516101a0516101c051631a7b28f26101e0526101405161020052610160516102205261018051610240526101a051610260523361028052336102a0526101c0516102c0526102c0516102a051610280516102605161024051610220516102005160065801611ec9565b610320526101c0526101a0526101805261016052610140526103205160005260206000f3005b63981a1327600051141561281c5760c0600461014037341561274357600080fd5b608435602051811061275457600080fd5b5060a435602051811061276657600080fd5b50306101c051141561277757600080fd5b6101405161016051610180516101a0516101c0516101e051631a7b28f2610200526101405161022052610160516102405261018051610260526101a05161028052336102a0526101c0516102c0526101e0516102e0526102e0516102c0516102a0516102805161026051610240516102205160065801611ec9565b610340526101e0526101c0526101a0526101805261016052610140526103405160005260206000f3005b63cd7724c36000511415612918576020600461014037341561283d57600080fd5b6000610140511161284d57600080fd5b6006543b61285a57600080fd5b60065430141561286957600080fd5b602061020060246370a0823161018052306101a05261019c6006545afa61288f57600080fd5b60005061020051610160526101405161016051610180516101a0516101c0516101e051610200516389f2a871610220526101405161024052303161026052610160516102805261028051610260516102405160065801610852565b6102e052610200526101e0526101c0526101a0526101805261016052610140526102e05160005260206000f3005b6359e948626000511415612a27576020600461014037341561293957600080fd5b6000610140511161294957600080fd5b6006543b61295657600080fd5b60065430141561296557600080fd5b602061020060246370a0823161018052306101a05261019c6006545afa61298b57600080fd5b60005061020051610160526101405161016051610180516101a0516101c0516101e051610200516102205163fd11c223610240526101405161026052303161028052610160516102a0526102a0516102805161026051600658016109ce565b6103005261022052610200526101e0526101c0526101a05261018052610160526101405261030051610220526001610220510260005260206000f3005b6395b68fe76000511415612b365760206004610140373415612a4857600080fd5b60006101405111612a5857600080fd5b6006543b612a6557600080fd5b600654301415612a7457600080fd5b602061020060246370a0823161018052306101a05261019c6006545afa612a9a57600080fd5b60005061020051610160526101405161016051610180516101a0516101c0516101e05161020051610220516389f2a871610240526101405161026052610160516102805230316102a0526102a051610280516102605160065801610852565b6103005261022052610200526101e0526101c0526101a05261018052610160526101405261030051610220526001610220510260005260206000f3005b632640f62c6000511415612c325760206004610140373415612b5757600080fd5b60006101405111612b6757600080fd5b6006543b612b7457600080fd5b600654301415612b8357600080fd5b602061020060246370a0823161018052306101a05261019c6006545afa612ba957600080fd5b60005061020051610160526101405161016051610180516101a0516101c0516101e0516102005163fd11c2236102205261014051610240526101605161026052303161028052610280516102605161024051600658016109ce565b6102e052610200526101e0526101c0526101a0526101805261016052610140526102e05160005260206000f3005b639d76ea586000511415612c58573415612c4b57600080fd5b60065460005260206000f3005b63966dae0e6000511415612c7e573415612c7157600080fd5b60075460005260206000f3005b6370a082316000511415612ccd5760206004610140373415612c9f57600080fd5b6004356020518110612cb057600080fd5b5060046101405160e05260c052604060c0205460005260206000f3005b63a9059cbb6000511415612d985760406004610140373415612cee57600080fd5b6004356020518110612cff57600080fd5b5060043360e05260c052604060c0206101605181541015612d1f57600080fd5b6101605181540381555060046101405160e05260c052604060c0208054610160518254011015612d4e57600080fd5b61016051815401815550610160516101805261014051337fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6020610180a3600160005260206000f3005b6323b872dd6000511415612eb35760606004610140373415612db957600080fd5b6004356020518110612dca57600080fd5b506024356020518110612ddc57600080fd5b5060046101405160e05260c052604060c0206101805181541015612dff57600080fd5b6101805181540381555060046101605160e05260c052604060c0208054610180518254011015612e2e57600080fd5b6101805181540181555060056101405160e05260c052604060c0203360e05260c052604060c0206101805181541015612e6657600080fd5b61018051815403815550610180516101a05261016051610140517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206101a0a3600160005260206000f3005b63095ea7b36000511415612f485760406004610140373415612ed457600080fd5b6004356020518110612ee557600080fd5b506101605160053360e05260c052604060c0206101405160e05260c052604060c02055610160516101805261014051337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9256020610180a3600160005260206000f3005b63dd62ed3e6000511415612fb85760406004610140373415612f6957600080fd5b6004356020518110612f7a57600080fd5b506024356020518110612f8c57600080fd5b5060056101405160e05260c052604060c0206101605160e05260c052604060c0205460005260206000f3005b6306fdde036000511415612fde573415612fd157600080fd5b60005460005260206000f3005b6395d89b416000511415613004573415612ff757600080fd5b60015460005260206000f3005b63313ce567600051141561302a57341561301d57600080fd5b60025460005260206000f3005b6318160ddd600051141561305057341561304357600080fd5b60035460005260206000f3005b638c717a33610140523461016052600161018052426101a052336101c052336101e0526101e0516101c0516101a051610180516101605160065801610bfb565b610240526102405b61000461309c0361000460003961000461309c036000f3",
"result": "0x27ff8bbb55a2b5cfd4fb7c350ed6a6a2cdbed5d9"
},
"non-zero address, salt, and very long init code": {
"address": "0x00000000000000000000000000000000deadbeef",
"salt": "0x00000000000000000000000000000000000000000000000000000000cafebabe",
"init_code": "0x60806040526016805460a060020a61ffff02191675010000000000000000000000000000000000000000001790553480156200003a57600080fd5b5060405161016080620062768339810160408181528251602080850151838601516060870151608088015160a089015160c08a015160e08b01516101008c01516101208d0151610140909d01518b8d018c5260178d527f53796e746865746978204e6574776f726b20546f6b656e0000000000000000008a8e01528b51808d01909c5260038c527f534e580000000000000000000000000000000000000000000000000000000000998c0199909952989b969a9599949893979296919590949192918c918c918460128d86818080600160a060020a03811615156200018057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4f776e657220616464726573732063616e6e6f74206265203000000000000000604482015290519081900360640190fd5b60008054600160a060020a031916600160a060020a038316908117825560408051928352602083019190915280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a150600160a060020a03811615156200025157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f776e6572206d757374206e6f74206265207a65726f00000000000000000000604482015290519081900360640190fd5b60038054600160a060020a038316610100810261010060a860020a03199092169190911790915560408051918252517fd5da63a0b864b315bc04128dedbc93888c8529ee6cf47ce664dc204339228c539181900360200190a15060048054600160a060020a038416600160a060020a0319909116811790915560408051918252517ffc80377ca9c49cc11ae6982f390a42db976d5530af7c43889264b13fbbd7c57e9181900360200190a15050600160075560088054600160a060020a031916600160a060020a038816179055845162000333906009906020880190620003f0565b5083516200034990600a906020870190620003f0565b5050600b91909155600c805460ff191660ff909216919091179055505060148054600160a060020a0319908116600160a060020a039d8e16179091556013805482169a8d169a909a179099555050601080548816968a16969096179095556015805487169489169490941790935560128054861692881692909217909155601180548516918716919091179055601680549093169416939093179055506200049592505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200043357805160ff191683800117855562000463565b8280016001018555821562000463579182015b828111156200046357825182559160200191906001019062000446565b506200047192915062000475565b5090565b6200049291905b808211156200047157600081556001016200047c565b90565b615dd180620004a56000396000f30060806040526004361061031e5763ffffffff60e060020a60003504166306fdde03811461032357806309231602146103ad578063095ea7b3146103c75780630b887dae146103ff5780631249c58b14610417578063131b0ae71461042c5780631627540c1461044d57806316b2213f1461046e57806317c70de4146104a157806318160ddd146104b657806319db2228146104cb5780631a5c6095146104ec5780631c8aa5fa1461050757806320714f881461052857806323b872dd14610549578063295da87d14610573578063313ce5671461058b57806332608039146105b65780633278c960146105ea5780633fc6df6e146105ff5780634ffcd9df1461061457806353a47bb71461062957806356e449541461063e578063654a60ac1461065f5780636ac0bf9c1461067d5780636b4dff1f1461069e57806370a08231146106b657806372cb051f146106d757806379ba50971461073c5780638357ce3614610751578063835e119c1461077557806383d625d41461078d578063849cf588146107a55780638a290014146107c65780638da5cb5b146107de57806394524096146107f357806395d89b411461080d578063961a929c1461082257806397107d6d146108375780639cb8a26a146108585780639cbdaeb61461086d5780639f76980714610882578063a311c7c2146108a3578063a430be6c146108c4578063a461fc82146108d9578063a5fdc5de146108ee578063a8913cdb1461090f578063a9059cbb14610933578063ab3e337d14610957578063ab67aa581461098a578063ae2e933b146109f9578063af086c7e14610a0e578063b8225dec14610a23578063bc67f83214610a38578063bd32aa4414610a59578063be45fd6214610a6e578063c40dd66f14610ad7578063c58aaae614610aec578063c9df6df514610b01578063d37c4d8b14610b1b578063d67bdd2514610b3f578063dbd4a42214610b54578063dbf6334014610b69578063dd62ed3e14610b7e578063e2fdcc1714610ba5578063e90dd9e214610bba578063ec55688914610bcf578063ee52a2f314610be4578063f534375214610c02578063fe7f7bc314610c17575b600080fd5b34801561032f57600080fd5b50610338610c2c565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561037257818101518382015260200161035a565b50505050905090810190601f16801561039f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156103b957600080fd5b506103c5600435610cba565b005b3480156103d357600080fd5b506103eb600160a060020a0360043516602435610d79565b604080519115158252519081900360200190f35b34801561040b57600080fd5b506103c5600435610e7f565b34801561042357600080fd5b506103eb61128f565b34801561043857600080fd5b506103c5600160a060020a036004351661179c565b34801561045957600080fd5b506103c5600160a060020a0360043516611822565b34801561047a57600080fd5b5061048f600160a060020a03600435166118da565b60408051918252519081900360200190f35b3480156104ad57600080fd5b5061048f6118ec565b3480156104c257600080fd5b5061048f6118f2565b3480156104d757600080fd5b506103c5600160a060020a03600435166118f8565b3480156104f857600080fd5b5061048f6004356024356119c5565b34801561051357600080fd5b506103c5600160a060020a0360043516611b91565b34801561053457600080fd5b506103c5600160a060020a0360043516611c5e565b34801561055557600080fd5b506103eb600160a060020a0360043581169060243516604435611d8b565b34801561057f57600080fd5b506103c5600435611da4565b34801561059757600080fd5b506105a0611f89565b6040805160ff9092168252519081900360200190f35b3480156105c257600080fd5b506105ce600435611f92565b60408051600160a060020a039092168252519081900360200190f35b3480156105f657600080fd5b506103c5611fad565b34801561060b57600080fd5b506105ce61204b565b34801561062057600080fd5b506105ce61205a565b34801561063557600080fd5b506105ce612069565b34801561064a57600080fd5b506103c5600160a060020a0360043516612078565b34801561066b57600080fd5b5061048f600435602435604435612145565b34801561068957600080fd5b5061048f600160a060020a03600435166121ef565b3480156106aa57600080fd5b506103c5600435612444565b3480156106c257600080fd5b5061048f600160a060020a03600435166124a1565b3480156106e357600080fd5b506106ec612528565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610728578181015183820152602001610710565b505050509050019250505060405180910390f35b34801561074857600080fd5b506103c56125cc565b34801561075d57600080fd5b5061048f600160a060020a03600435166024356126c7565b34801561078157600080fd5b506105ce60043561270b565b34801561079957600080fd5b5061048f600435612733565b3480156107b157600080fd5b506103c5600160a060020a0360043516612a57565b3480156107d257600080fd5b506103c5600435612cc2565b3480156107ea57600080fd5b506105ce612e38565b3480156107ff57600080fd5b506103c56004351515612e47565b34801561081957600080fd5b50610338612f34565b34801561082e57600080fd5b5061048f612f8f565b34801561084357600080fd5b506103c5600160a060020a0360043516612f95565b34801561086457600080fd5b506103c561304d565b34801561087957600080fd5b506105ce6131c6565b34801561088e57600080fd5b506103c5600160a060020a03600435166131d5565b3480156108af57600080fd5b5061048f600160a060020a03600435166132a4565b3480156108d057600080fd5b506105ce6132f3565b3480156108e557600080fd5b5061048f613302565b3480156108fa57600080fd5b5061048f600160a060020a0360043516613309565b34801561091b57600080fd5b5061048f600160a060020a03600435166024356134a0565b34801561093f57600080fd5b506103eb600160a060020a0360043516602435613558565b34801561096357600080fd5b506103eb600160a060020a0360043581169060243590604435906064359060843516613567565b34801561099657600080fd5b50604080516020601f6064356004818101359283018490048402850184019095528184526103eb94600160a060020a0381358116956024803590921695604435953695608494019181908401838280828437509497506136f99650505050505050565b348015610a0557600080fd5b506105ce6137d6565b348015610a1a57600080fd5b506103c56137e5565b348015610a2f57600080fd5b506103eb6138e4565b348015610a4457600080fd5b506103c5600160a060020a03600435166138ed565b348015610a6557600080fd5b506103c5613988565b348015610a7a57600080fd5b50604080516020600460443581810135601f81018490048402850184019095528484526103eb948235600160a060020a0316946024803595369594606494920191908190840183828082843750949750613a359650505050505050565b348015610ae357600080fd5b506105ce613b1c565b348015610af857600080fd5b506105ce613b2b565b348015610b0d57600080fd5b506103c56004351515613b3f565b348015610b2757600080fd5b5061048f600160a060020a0360043516602435613c5d565b348015610b4b57600080fd5b506105ce613e89565b348015610b6057600080fd5b506105ce613e98565b348015610b7557600080fd5b5061048f613ea7565b348015610b8a57600080fd5b5061048f600160a060020a0360043581169060243516613eae565b348015610bb157600080fd5b506105ce613f54565b348015610bc657600080fd5b506105ce613f63565b348015610bdb57600080fd5b506105ce613f72565b348015610bf057600080fd5b506103eb600435602435604435613f81565b348015610c0e57600080fd5b506103eb61416e565b348015610c2357600080fd5b506105ce614190565b6009805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610cb25780601f10610c8757610100808354040283529160200191610cb2565b820191906000526020600020905b815481529060010190602001808311610c9557829003601f168201915b505050505081565b601854600160a060020a03163314610d1c576040805160e560020a62461bcd02815260206004820152601d60248201527f4f6e6c7920676173206c696d6974206f7261636c6520616c6c6f776564000000604482015290519081900360640190fd5b60008111610d74576040805160e560020a62461bcd02815260206004820152601a60248201527f4e6565647320746f2062652067726561746572207468616e2030000000000000604482015290519081900360640190fd5b601755565b6004546000908190600160a060020a03163314801590610da45750600554600160a060020a03163314155b8015610dbb5750600654600160a060020a03163314155b15610dd35760068054600160a060020a031916331790555b50600654600854604080517fda46098c000000000000000000000000000000000000000000000000000000008152600160a060020a0393841660048201819052878516602483015260448201879052915191939092169163da46098c91606480830192600092919082900301818387803b158015610e5057600080fd5b505af1158015610e64573d6000803e3d6000fd5b50505050610e7381858561419f565b600191505b5092915050565b6004546000908190600160a060020a03163314801590610eaa5750600554600160a060020a03163314155b8015610ec15750600654600160a060020a03163314155b15610ed95760068054600160a060020a031916331790555b600054600654600160a060020a03908116911614610f2f576040805160e560020a62461bcd0281526020600482015260136024820152600080516020615d86833981519152604482015290519081900360640190fd5b6000838152600e6020526040902054600160a060020a03161515610f9d576040805160e560020a62461bcd02815260206004820152601460248201527f53796e746820646f6573206e6f74206578697374000000000000000000000000604482015290519081900360640190fd5b6000838152600e602090815260408083205481517f18160ddd0000000000000000000000000000000000000000000000000000000081529151600160a060020a03909116936318160ddd93600480850194919392918390030190829087803b15801561100857600080fd5b505af115801561101c573d6000803e3d6000fd5b505050506040513d602081101561103257600080fd5b505115611089576040805160e560020a62461bcd02815260206004820152601360248201527f53796e746820737570706c792065786973747300000000000000000000000000604482015290519081900360640190fd5b60e960020a622c2229028314156110ea576040805160e560020a62461bcd02815260206004820152601760248201527f43616e6e6f742072656d6f7665205844522073796e7468000000000000000000604482015290519081900360640190fd5b60e260020a631cd554d10283141561114c576040805160e560020a62461bcd02815260206004820152601860248201527f43616e6e6f742072656d6f766520735553442073796e74680000000000000000604482015290519081900360640190fd5b50506000818152600e6020526040812054600160a060020a0316905b600d5481101561124f5781600160a060020a0316600d8281548110151561118b57fe5b600091825260209091200154600160a060020a0316141561124757600d8054829081106111b457fe5b60009182526020909120018054600160a060020a0319169055600d805460001981019081106111df57fe5b600091825260209091200154600d8054600160a060020a03909216918390811061120557fe5b60009182526020909120018054600160a060020a031916600160a060020a0392909216919091179055600d805490611241906000198301615cfe565b5061124f565b600101611168565b50506000818152600e602081815260408084208054600160a060020a03168552600f83529084208490559390925290528054600160a060020a0319169055565b601654600090819081908190600160a060020a031615156112fa576040805160e560020a62461bcd02815260206004820152601b60248201527f52657761726473446973747269627574696f6e206e6f74207365740000000000604482015290519081900360640190fd5b601560009054906101000a9004600160a060020a0316600160a060020a031663cc5c095c6040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561134d57600080fd5b505af1158015611361573d6000803e3d6000fd5b505050506040513d602081101561137757600080fd5b50519250600083116113d3576040805160e560020a62461bcd02815260206004820152601560248201527f4e6f20737570706c79206973206d696e7461626c650000000000000000000000604482015290519081900360640190fd5b601554604080517f7e7961d7000000000000000000000000000000000000000000000000000000008152600481018690529051600160a060020a0390921691637e7961d7916024808201926020929091908290030181600087803b15801561143a57600080fd5b505af115801561144e573d6000803e3d6000fd5b505050506040513d602081101561146457600080fd5b5050601554604080517f9bdd7ac70000000000000000000000000000000000000000000000000000000081529051600160a060020a0390921691639bdd7ac7916004808201926020929091908290030181600087803b1580156114c657600080fd5b505af11580156114da573d6000803e3d6000fd5b505050506040513d60208110156114f057600080fd5b50519150611504838363ffffffff61430716565b6008546016546040805160e060020a6370a08231028152600160a060020a03928316600482018190529151949550919092169263b46310f692916115a691869186916370a08231916024808201926020929091908290030181600087803b15801561156e57600080fd5b505af1158015611582573d6000803e3d6000fd5b505050506040513d602081101561159857600080fd5b50519063ffffffff61431e16565b6040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050600060405180830381600087803b1580156115f857600080fd5b505af115801561160c573d6000803e3d6000fd5b50506016546116289250309150600160a060020a031683614330565b601654604080517f59974e38000000000000000000000000000000000000000000000000000000008152600481018490529051600160a060020a03909216916359974e389160248082019260009290919082900301818387803b15801561168e57600080fd5b505af11580156116a2573d6000803e3d6000fd5b50506008546040805160e060020a6370a0823102815233600482018190529151600160a060020a03909316945063b46310f69350909161170791879186916370a082319160248083019260209291908290030181600087803b15801561156e57600080fd5b6040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050600060405180830381600087803b15801561175957600080fd5b505af115801561176d573d6000803e3d6000fd5b5050505061177c303384614330565b600b5461178f908463ffffffff61431e16565b600b556001935050505090565b600054600160a060020a03163314611800576040805160e560020a62461bcd02815260206004820152602f6024820152600080516020615d468339815191526044820152600080516020615d66833981519152606482015290519081900360840190fd5b60058054600160a060020a031916600160a060020a0392909216919091179055565b600054600160a060020a03163314611886576040805160e560020a62461bcd02815260206004820152602f6024820152600080516020615d468339815191526044820152600080516020615d66833981519152606482015290519081900360840190fd5b60018054600160a060020a038316600160a060020a0319909116811790915560408051918252517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229181900360200190a150565b600f6020526000908152604090205481565b60025481565b600b5481565b600454600160a060020a0316331480159061191e5750600554600160a060020a03163314155b80156119355750600654600160a060020a03163314155b1561194d5760068054600160a060020a031916331790555b600054600654600160a060020a039081169116146119a3576040805160e560020a62461bcd0281526020600482015260136024820152600080516020615d86833981519152604482015290519081900360640190fd5b60108054600160a060020a031916600160a060020a0392909216919091179055565b6000806000601060009054906101000a9004600160a060020a0316600160a060020a0316630f9b49556040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015611a1d57600080fd5b505af1158015611a31573d6000803e3d6000fd5b505050506040513d6020811015611a4757600080fd5b50519150600190507f7300000000000000000000000000000000000000000000000000000000000000600160f860020a031960f860020a600088901a0216148015611a9c575060e260020a631cd554d1028514155b8015611adb57507f6900000000000000000000000000000000000000000000000000000000000000600160f860020a031960f860020a600087901a0216145b80611b6d57507f6900000000000000000000000000000000000000000000000000000000000000600160f860020a031960f860020a600088901a0216148015611b2e575060e260020a631cd554d1028414155b8015611b6d57507f7300000000000000000000000000000000000000000000000000000000000000600160f860020a031960f860020a600087901a0216145b15611b76575060025b611b86828263ffffffff61443216565b92505b505092915050565b600454600160a060020a03163314801590611bb75750600554600160a060020a03163314155b8015611bce5750600654600160a060020a03163314155b15611be65760068054600160a060020a031916331790555b600054600654600160a060020a03908116911614611c3c576040805160e560020a62461bcd0281526020600482015260136024820152600080516020615d86833981519152604482015290519081900360640190fd5b60188054600160a060020a031916600160a060020a0392909216919091179055565b600054600160a060020a03163314611cc2576040805160e560020a62461bcd02815260206004820152602f6024820152600080516020615d468339815191526044820152600080516020615d66833981519152606482015290519081900360840190fd5b600160a060020a0381161515611d22576040805160e560020a62461bcd02815260206004820152601c60248201527f42656e6566696369617279206d757374206e6f74206265207a65726f00000000604482015290519081900360640190fd5b60038054600160a060020a038316610100810274ffffffffffffffffffffffffffffffffffffffff00199092169190911790915560408051918252517fd5da63a0b864b315bc04128dedbc93888c8529ee6cf47ce664dc204339228c539181900360200190a150565b60006060611d9b858585846136f9565b95945050505050565b60045460009081908190819081908190600160a060020a03163314801590611dd75750600554600160a060020a03163314155b8015611dee5750600654600160a060020a03163314155b15611e065760068054600160a060020a031916331790555b60e260020a631cd554d1029550611e27868860e960020a622c222902612145565b600654909550611e4990600160a060020a031660e960020a622c222902613c5d565b600654909450611e6290600160a060020a031687613c5d565b925060008411611ebc576040805160e560020a62461bcd02815260206004820152601260248201527f4e6f206465627420746f20666f72676976650000000000000000000000000000604482015290519081900360640190fd5b848410611ec95784611ecb565b835b9150611ed78285614460565b868310611ee45786611ee6565b825b6000878152600e60205260408082205460065482517f9dc29fac000000000000000000000000000000000000000000000000000000008152600160a060020a0391821660048201526024810186905292519495501692639dc29fac9260448084019391929182900301818387803b158015611f6057600080fd5b505af1158015611f74573d6000803e3d6000fd5b50505050611f806147be565b50505050505050565b600c5460ff1681565b600e60205260009081526040902054600160a060020a031681565b600054600160a060020a03163314612011576040805160e560020a62461bcd02815260206004820152602f6024820152600080516020615d468339815191526044820152600080516020615d66833981519152606482015290519081900360840190fd5b600060028190556003805460ff191690556040517f6adcc7125002935e0aa31697538ebbd65cfddf20431eb6ecdcfc3e238bfd082c9190a1565b601654600160a060020a031681565b601354600160a060020a031681565b600154600160a060020a031681565b600454600160a060020a0316331480159061209e5750600554600160a060020a03163314155b80156120b55750600654600160a060020a03163314155b156120cd5760068054600160a060020a031916331790555b600054600654600160a060020a03908116911614612123576040805160e560020a62461bcd0281526020600482015260136024820152600080516020615d86833981519152604482015290519081900360640190fd5b60138054600160a060020a031916600160a060020a0392909216919091179055565b601354604080517f654a60ac0000000000000000000000000000000000000000000000000000000081526004810186905260248101859052604481018490529051600092600160a060020a03169163654a60ac91606480830192602092919082900301818787803b1580156121b957600080fd5b505af11580156121cd573d6000803e3d6000fd5b505050506040513d60208110156121e357600080fd5b505190505b9392505050565b601354604080517e9919c000000000000000000000000000000000000000000000000000000000815260eb60020a620a69cb02600482018190529151600093849384939092600160a060020a0390921691629919c09160248082019260209290919082900301818887803b15801561226657600080fd5b505af115801561227a573d6000803e3d6000fd5b505050506040513d602081101561229057600080fd5b5051156122e7576040805160e560020a62461bcd02815260206004820152601960248201527f52617465207374616c65206f72206e6f7420612073796e746800000000000000604482015290519081900360640190fd5b6008546040805160e060020a6370a08231028152600160a060020a038881166004830152915191909216916370a082319160248083019260209291908290030181600087803b15801561233957600080fd5b505af115801561234d573d6000803e3d6000fd5b505050506040513d602081101561236357600080fd5b5051601454604080517fb410a034000000000000000000000000000000000000000000000000000000008152905192955061241792600160a060020a039092169163b410a034916004808201926020929091908290030181600087803b1580156123cc57600080fd5b505af11580156123e0573d6000803e3d6000fd5b505050506040513d60208110156123f657600080fd5b505161240b8760eb60020a620a69cb02613c5d565b9063ffffffff6148f116565b9150828210612429576000935061243c565b612439838363ffffffff61430716565b93505b505050919050565b60175481111561249e576040805160e560020a62461bcd02815260206004820152601560248201527f4761732070726963652061626f7665206c696d69740000000000000000000000604482015290519081900360640190fd5b50565b6008546040805160e060020a6370a08231028152600160a060020a038481166004830152915160009392909216916370a082319160248082019260209290919082900301818787803b1580156124f657600080fd5b505af115801561250a573d6000803e3d6000fd5b505050506040513d602081101561252057600080fd5b505192915050565b6060806000600d8054905060405190808252806020026020018201604052801561255c578160200160208202803883390190505b509150600090505b600d548110156125c657600f6000600d8381548110151561258157fe5b6000918252602080832090910154600160a060020a0316835282019290925260400190205482518390839081106125b457fe5b60209081029091010152600101612564565b50919050565b600154600160a060020a03163314612654576040805160e560020a62461bcd02815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527f2063616e20616363657074206f776e6572736869700000000000000000000000606482015290519081900360840190fd5b60005460015460408051600160a060020a03938416815292909116602083015280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a16001805460008054600160a060020a0319908116600160a060020a03841617909155169055565b60008060006126d68585613c5d565b91506126e285856134a0565b90508082106126f45760009250611b89565b612704818363ffffffff61430716565b9250611b89565b600d80548290811061271957fe5b600091825260209091200154600160a060020a0316905081565b601354604080517fac82f60800000000000000000000000000000000000000000000000000000000815260048101849052905160009283928392606092849283928392600160a060020a03169163ac82f60891602480830192602092919082900301818787803b1580156127a657600080fd5b505af11580156127ba573d6000803e3d6000fd5b505050506040513d60208110156127d057600080fd5b5051601354909550600160a060020a031663babcb4236127ee612528565b6040518263ffffffff1660e060020a0281526004018080602001828103825283818151815260200191508051906020019060200280838360005b83811015612840578181015183820152602001612828565b5050505090500192505050600060405180830381600087803b15801561286557600080fd5b505af1158015612879573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160409081528110156128a257600080fd5b8101908080516401000000008111156128ba57600080fd5b820160208101848111156128cd57600080fd5b81518560208202830111640100000000821117156128ea57600080fd5b505060209091015190965094505083159050612950576040805160e560020a62461bcd02815260206004820152600f60248201527f526174657320617265207374616c650000000000000000000000000000000000604482015290519081900360640190fd5b600091505b600d54821015612a3b57612a1c848381518110151561297057fe5b90602001906020020151600d8481548110151561298957fe5b9060005260206000200160009054906101000a9004600160a060020a0316600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b1580156129e457600080fd5b505af11580156129f8573d6000803e3d6000fd5b505050506040513d6020811015612a0e57600080fd5b50519063ffffffff61490616565b9050612a2e868263ffffffff61431e16565b9550600190910190612955565b612a4b868663ffffffff6148f116565b98975050505050505050565b600454600090600160a060020a03163314801590612a805750600554600160a060020a03163314155b8015612a975750600654600160a060020a03163314155b15612aaf5760068054600160a060020a031916331790555b600054600654600160a060020a03908116911614612b05576040805160e560020a62461bcd0281526020600482015260136024820152600080516020615d86833981519152604482015290519081900360640190fd5b81600160a060020a031663dbd06c856040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015612b4357600080fd5b505af1158015612b57573d6000803e3d6000fd5b505050506040513d6020811015612b6d57600080fd5b50516000818152600e6020526040902054909150600160a060020a031615612bdf576040805160e560020a62461bcd02815260206004820152601460248201527f53796e746820616c726561647920657869737473000000000000000000000000604482015290519081900360640190fd5b600160a060020a0382166000908152600f602052604090205415612c4d576040805160e560020a62461bcd02815260206004820152601c60248201527f53796e7468206164647265737320616c72656164792065786973747300000000604482015290519081900360640190fd5b600d8054600181019091557fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb5018054600160a060020a03909316600160a060020a031993841681179091556000828152600e6020908152604080832080549096168417909555918152600f9091529190912055565b600454600090600160a060020a03163314801590612ceb5750600554600160a060020a03163314155b8015612d025750600654600160a060020a03163314155b15612d1a5760068054600160a060020a031916331790555b5060065460e260020a631cd554d10290612d3d90600160a060020a0316826126c7565b821115612d94576040805160e560020a62461bcd02815260206004820152601060248201527f416d6f756e7420746f6f206c6172676500000000000000000000000000000000604482015290519081900360640190fd5b612d9e818361491b565b6000818152600e60205260408082205460065482517f867904b4000000000000000000000000000000000000000000000000000000008152600160a060020a03918216600482015260248101879052925191169263867904b4926044808201939182900301818387803b158015612e1457600080fd5b505af1158015612e28573d6000803e3d6000fd5b50505050612e346147be565b5050565b600054600160a060020a031681565b600454600160a060020a03163314801590612e6d5750600554600160a060020a03163314155b8015612e845750600654600160a060020a03163314155b15612e9c5760068054600160a060020a031916331790555b600054600654600160a060020a03908116911614612ef2576040805160e560020a62461bcd0281526020600482015260136024820152600080516020615d86833981519152604482015290519081900360640190fd5b6016805491151575010000000000000000000000000000000000000000000275ff00000000000000000000000000000000000000000019909216919091179055565b600a805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610cb25780601f10610c8757610100808354040283529160200191610cb2565b60175481565b600054600160a060020a03163314612ff9576040805160e560020a62461bcd02815260206004820152602f6024820152600080516020615d468339815191526044820152600080516020615d66833981519152606482015290519081900360840190fd5b60048054600160a060020a038316600160a060020a0319909116811790915560408051918252517ffc80377ca9c49cc11ae6982f390a42db976d5530af7c43889264b13fbbd7c57e9181900360200190a150565b60008054600160a060020a031633146130b2576040805160e560020a62461bcd02815260206004820152602f6024820152600080516020615d468339815191526044820152600080516020615d66833981519152606482015290519081900360840190fd5b60035460ff16151561310e576040805160e560020a62461bcd02815260206004820152601f60248201527f53656c66204465737472756374206e6f742079657420696e6974696174656400604482015290519081900360640190fd5b426224ea006002540110151561316e576040805160e560020a62461bcd02815260206004820152601b60248201527f53656c662064657374727563742064656c6179206e6f74206d65740000000000604482015290519081900360640190fd5b5060035460408051600160a060020a0361010090930492909216808352905190917f8a09e1677ced846cb537dc2b172043bd05a1a81ad7e0033a7ef8ba762df990b7919081900360200190a180600160a060020a0316ff5b600554600160a060020a031681565b600454600160a060020a031633148015906131fb5750600554600160a060020a03163314155b80156132125750600654600160a060020a03163314155b1561322a5760068054600160a060020a031916331790555b600054600654600160a060020a03908116911614613280576040805160e560020a62461bcd0281526020600482015260136024820152600080516020615d86833981519152604482015290519081900360640190fd5b60088054600160a060020a031916600160a060020a03831617905561249e81614d82565b60008060006132b284613309565b91508115156132c457600092506132ec565b6132d78460eb60020a620a69cb02613c5d565b90506132e9818363ffffffff6148f116565b92505b5050919050565b601254600160a060020a031681565b6224ea0081565b6008546040805160e060020a6370a08231028152600160a060020a0384811660048301529151600093849316916370a0823191602480830192602092919082900301818787803b15801561335c57600080fd5b505af1158015613370573d6000803e3d6000fd5b505050506040513d602081101561338657600080fd5b5051601154909150600160a060020a03161561342f576011546040805160e060020a6370a08231028152600160a060020a038681166004830152915161342c9392909216916370a08231916024808201926020929091908290030181600087803b1580156133f357600080fd5b505af1158015613407573d6000803e3d6000fd5b505050506040513d602081101561341d57600080fd5b5051829063ffffffff61431e16565b90505b601254600160a060020a03161561349a576012546040805160e060020a6370a08231028152600160a060020a03868116600483015291516134979392909216916370a08231916024808201926020929091908290030181600087803b1580156133f357600080fd5b90505b92915050565b6000806134bf60eb60020a620a69cb026134b986613309565b85612145565b9050613550601460009054906101000a9004600160a060020a0316600160a060020a031663b410a0346040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561351757600080fd5b505af115801561352b573d6000803e3d6000fd5b505050506040513d602081101561354157600080fd5b5051829063ffffffff614ec916565b949350505050565b60006060613550848483613a35565b600454600090600160a060020a031633148015906135905750600554600160a060020a03163314155b80156135a75750600654600160a060020a03163314155b156135bf5760068054600160a060020a031916331790555b600654600160a060020a03166000908152600f60205260409020541515613630576040805160e560020a62461bcd02815260206004820152601260248201527f4f6e6c792073796e746820616c6c6f7765640000000000000000000000000000604482015290519081900360640190fd5b84831415613688576040805160e560020a62461bcd02815260206004820152601360248201527f43616e27742062652073616d652073796e746800000000000000000000000000604482015290519081900360640190fd5b600084116136e0576040805160e560020a62461bcd02815260206004820152600b60248201527f5a65726f20616d6f756e74000000000000000000000000000000000000000000604482015290519081900360640190fd5b6136ef86868686866000614ef5565b9695505050505050565b600454600090600160a060020a031633148015906137225750600554600160a060020a03163314155b80156137395750600654600160a060020a03163314155b156137515760068054600160a060020a031916331790555b61375a856121ef565b8311156137b1576040805160e560020a62461bcd02815260206004820152601460248201527f496e73756666696369656e742062616c616e6365000000000000000000000000604482015290519081900360640190fd5b6006546137ca90600160a060020a0316868686866154d2565b50600195945050505050565b601054600160a060020a031681565b6004546000908190600160a060020a031633148015906138105750600554600160a060020a03163314155b80156138275750600654600160a060020a03163314155b1561383f5760068054600160a060020a031916331790555b60065460e260020a631cd554d102925061386290600160a060020a0316836126c7565b905061386e828261491b565b6000828152600e60205260408082205460065482517f867904b4000000000000000000000000000000000000000000000000000000008152600160a060020a03918216600482015260248101869052925191169263867904b4926044808201939182900301818387803b158015612e1457600080fd5b60035460ff1681565b600454600160a060020a03163314806139105750600554600160a060020a031633145b1515613966576040805160e560020a62461bcd02815260206004820152601760248201527f4f6e6c79207468652070726f78792063616e2063616c6c000000000000000000604482015290519081900360640190fd5b60068054600160a060020a031916600160a060020a0392909216919091179055565b600054600160a060020a031633146139ec576040805160e560020a62461bcd02815260206004820152602f6024820152600080516020615d468339815191526044820152600080516020615d66833981519152606482015290519081900360840190fd5b426002556003805460ff19166001179055604080516224ea00815290517fcbd94ca75b8dc45c9d80c77e851670e78843c0d75180cb81db3e2158228fa9a69181900360200190a1565b600454600090600160a060020a03163314801590613a5e5750600554600160a060020a03163314155b8015613a755750600654600160a060020a03163314155b15613a8d5760068054600160a060020a031916331790555b600654613aa290600160a060020a03166121ef565b831115613af9576040805160e560020a62461bcd02815260206004820152601460248201527f496e73756666696369656e742062616c616e6365000000000000000000000000604482015290519081900360640190fd5b600654613b1190600160a060020a03168585856155e2565b506001949350505050565b601554600160a060020a031681565b6003546101009004600160a060020a031681565b601360009054906101000a9004600160a060020a0316600160a060020a0316637dc0d1d06040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015613b9257600080fd5b505af1158015613ba6573d6000803e3d6000fd5b505050506040513d6020811015613bbc57600080fd5b5051600160a060020a03163314613c1d576040805160e560020a62461bcd02815260206004820152601360248201527f4f6e6c79206f7261636c6520616c6c6f77656400000000000000000000000000604482015290519081900360640190fd5b60168054911515740100000000000000000000000000000000000000000274ff000000000000000000000000000000000000000019909216919091179055565b601454604080517f8b3f8088000000000000000000000000000000000000000000000000000000008152600160a060020a0385811660048301528251600094859485948594859485949190911692638b3f8088926024808201939182900301818787803b158015613ccd57600080fd5b505af1158015613ce1573d6000803e3d6000fd5b505050506040513d6040811015613cf757600080fd5b5080516020909101519095509350841515613d155760009550613e7e565b601454604080517f08d95cd5000000000000000000000000000000000000000000000000000000008152600481018790529051613e56928892613e4a92600160a060020a03909216916308d95cd5916024808201926020929091908290030181600087803b158015613d8657600080fd5b505af1158015613d9a573d6000803e3d6000fd5b505050506040513d6020811015613db057600080fd5b5051601454604080517f463177120000000000000000000000000000000000000000000000000000000081529051600160a060020a03909216916346317712916004808201926020929091908290030181600087803b158015613e1257600080fd5b505af1158015613e26573d6000803e3d6000fd5b505050506040513d6020811015613e3c57600080fd5b50519063ffffffff6155f016565b9063ffffffff61560916565b9250613e6187612733565b9150613e7083613e4a84615622565b9050613e7b81615638565b95505b505050505092915050565b600654600160a060020a031681565b601454600160a060020a031681565b600d545b90565b600854604080517fdd62ed3e000000000000000000000000000000000000000000000000000000008152600160a060020a03858116600483015284811660248301529151600093929092169163dd62ed3e9160448082019260209290919082900301818787803b158015613f2157600080fd5b505af1158015613f35573d6000803e3d6000fd5b505050506040513d6020811015613f4b57600080fd5b50519392505050565b601154600160a060020a031681565b600854600160a060020a031681565b600454600160a060020a031681565b600454600090600160a060020a03163314801590613faa5750600554600160a060020a03163314155b8015613fc15750600654600160a060020a03163314155b15613fd95760068054600160a060020a031916331790555b83821415614031576040805160e560020a62461bcd02815260206004820152601960248201527f4d7573742075736520646966666572656e742073796e74687300000000000000604482015290519081900360640190fd5b60008311614089576040805160e560020a62461bcd02815260206004820152600b60248201527f5a65726f20616d6f756e74000000000000000000000000000000000000000000604482015290519081900360640190fd5b6140923a612444565b60165474010000000000000000000000000000000000000000900460ff161561414c576000848152600e60205260408082205460065482517f9dc29fac000000000000000000000000000000000000000000000000000000008152600160a060020a039182166004820152602481018890529251911692639dc29fac926044808201939182900301818387803b15801561412b57600080fd5b505af115801561413f573d6000803e3d6000fd5b50505050600190506121e8565b60065461416790600160a060020a0316858585836001614ef5565b90506121e8565b6016547501000000000000000000000000000000000000000000900460ff1681565b601854600160a060020a031681565b600480546040805160208082018690528251808303820181528284018085527f417070726f76616c28616464726573732c616464726573732c75696e7432353690527f29000000000000000000000000000000000000000000000000000000000000006060840152925191829003606101822060e060020a63907dff9702835260036024840181905260448401829052600160a060020a038a8116606486018190528a821660848701819052600060a4880181905260c09a88019a8b52885160c48901528851939099169963907dff97999497959692959194939092839260e40191908a0190808383885b838110156142a257818101518382015260200161428a565b50505050905090810190601f1680156142cf5780820380516001836020036101000a031916815260200191505b50975050505050505050600060405180830381600087803b1580156142f357600080fd5b505af1158015611f80573d6000803e3d6000fd5b6000808383111561431757600080fd5b5050900390565b6000828201838110156121e857600080fd5b600480546040805160208082018690528251808303820181528284018085527f5472616e7366657228616464726573732c616464726573732c75696e7432353690527f29000000000000000000000000000000000000000000000000000000000000006060840152925191829003606101822060e060020a63907dff9702835260036024840181905260448401829052600160a060020a038a8116606486018190528a821660848701819052600060a4880181905260c09a88019a8b52885160c48901528851939099169963907dff97999497959692959194939092839260e40191908a019080838388838110156142a257818101518382015260200161428a565b6000808315156144455760009150610e78565b5082820282848281151561445557fe5b04146121e857600080fd5b816000808080808061447a60e960020a622c222902612733565b955061448c868863ffffffff61430716565b945060009350600085111561450f576144ab878663ffffffff6155f016565b925061450c837384d626b2bb4d0f064067e4bf80fce7055d8f3e7b63d5e5e6e66040518163ffffffff1660e060020a02815260040160206040518083038186803b1580156144f857600080fd5b505af4158015611582573d6000803e3d6000fd5b93505b8787141561460e57601454600654604080517fa764eb45000000000000000000000000000000000000000000000000000000008152600160a060020a0392831660048201526000602482018190529151929093169263a764eb45926044808301939282900301818387803b15801561458657600080fd5b505af115801561459a573d6000803e3d6000fd5b50505050601460009054906101000a9004600160a060020a0316600160a060020a031663ba08f2996040518163ffffffff1660e060020a028152600401600060405180830381600087803b1580156145f157600080fd5b505af1158015614605573d6000803e3d6000fd5b505050506146bc565b61461e888863ffffffff61430716565b9150614630828663ffffffff6155f016565b601454600654604080517fa764eb45000000000000000000000000000000000000000000000000000000008152600160a060020a03928316600482015260248101859052905193945091169163a764eb459160448082019260009290919082900301818387803b1580156146a357600080fd5b505af11580156146b7573d6000803e3d6000fd5b505050505b601454604080517f463177120000000000000000000000000000000000000000000000000000000081529051600160a060020a0390921691633d31e97b9161476291889185916346317712916004808201926020929091908290030181600087803b15801561472a57600080fd5b505af115801561473e573d6000803e3d6000fd5b505050506040513d602081101561475457600080fd5b50519063ffffffff61560916565b6040518263ffffffff1660e060020a02815260040180828152602001915050600060405180830381600087803b15801561479b57600080fd5b505af11580156147af573d6000803e3d6000fd5b50505050505050505050505050565b601454600654604080517f8b3f8088000000000000000000000000000000000000000000000000000000008152600160a060020a039283166004820152815160009485941692638b3f8088926024808201939182900301818787803b15801561482657600080fd5b505af115801561483a573d6000803e3d6000fd5b505050506040513d604081101561485057600080fd5b508051602090910151601054600654604080517f86645274000000000000000000000000000000000000000000000000000000008152600160a060020a03928316600482015260248101869052604481018590529051949650929450169163866452749160648082019260009290919082900301818387803b1580156148d557600080fd5b505af11580156148e9573d6000803e3d6000fd5b505050505050565b60006121e88383670de0b6b3a764000061565a565b60006121e88383670de0b6b3a764000061569e565b600080600080600080614938888860e960020a622c222902612145565b955061494c60e960020a622c222902612733565b945061495e868663ffffffff61431e16565b9350614970868563ffffffff6155f016565b92506149f5837384d626b2bb4d0f064067e4bf80fce7055d8f3e7b63d5e5e6e66040518163ffffffff1660e060020a02815260040160206040518083038186803b1580156149bd57600080fd5b505af41580156149d1573d6000803e3d6000fd5b505050506040513d60208110156149e757600080fd5b50519063ffffffff61430716565b600654909250614a1790600160a060020a031660e960020a622c222902613c5d565b90506000811115614a4557614a4284614a36888463ffffffff61431e16565b9063ffffffff6155f016565b92505b801515614ab857601460009054906101000a9004600160a060020a0316600160a060020a0316631bfba5956040518163ffffffff1660e060020a028152600401600060405180830381600087803b158015614a9f57600080fd5b505af1158015614ab3573d6000803e3d6000fd5b505050505b601454600654604080517fa764eb45000000000000000000000000000000000000000000000000000000008152600160a060020a039283166004820152602481018790529051919092169163a764eb4591604480830192600092919082900301818387803b158015614b2957600080fd5b505af1158015614b3d573d6000803e3d6000fd5b505050506000601460009054906101000a9004600160a060020a0316600160a060020a031663cd92eba96040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015614b9657600080fd5b505af1158015614baa573d6000803e3d6000fd5b505050506040513d6020811015614bc057600080fd5b50511115614c8c57601454604080517f463177120000000000000000000000000000000000000000000000000000000081529051600160a060020a0390921691633d31e97b91614c3691869185916346317712916004808201926020929091908290030181600087803b15801561472a57600080fd5b6040518263ffffffff1660e060020a02815260040180828152602001915050600060405180830381600087803b158015614c6f57600080fd5b505af1158015614c83573d6000803e3d6000fd5b50505050614d78565b601460009054906101000a9004600160a060020a0316600160a060020a0316633d31e97b7384d626b2bb4d0f064067e4bf80fce7055d8f3e7b63d5e5e6e66040518163ffffffff1660e060020a02815260040160206040518083038186803b158015614cf757600080fd5b505af4158015614d0b573d6000803e3d6000fd5b505050506040513d6020811015614d2157600080fd5b50516040805160e060020a63ffffffff8516028152600481019290925251602480830192600092919082900301818387803b158015614d5f57600080fd5b505af1158015614d73573d6000803e3d6000fd5b505050505b5050505050505050565b6004805460408051600160a060020a038581166020808401919091528351808403820181528385018086527f546f6b656e5374617465557064617465642861646472657373290000000000009052935192839003605a01832060e060020a63907dff97028452600160248501819052604485018290526000606486018190526084860181905260a4860181905260c0988601988952865160c48701528651949097169763907dff979791959294919384938493839260e4909201918a0190808383885b83811015614e5d578181015183820152602001614e45565b50505050905090810190601f168015614e8a5780820380516001836020036101000a031916815260200191505b50975050505050505050600060405180830381600087803b158015614eae57600080fd5b505af1158015614ec2573d6000803e3d6000fd5b5050505050565b6000670de0b6b3a7640000614ee4848463ffffffff61443216565b811515614eed57fe5b049392505050565b6000806000806000808b601060009054906101000a9004600160a060020a0316600160a060020a031663eb1edd616040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015614f5257600080fd5b505af1158015614f66573d6000803e3d6000fd5b505050506040513d6020811015614f7c57600080fd5b5051600160a060020a0382811691161415614fe1576040805160e560020a62461bcd02815260206004820152601760248201527f4665652061646472657373206e6f7420616c6c6f776564000000000000000000604482015290519081900360640190fd5b6016547501000000000000000000000000000000000000000000900460ff161515615056576040805160e560020a62461bcd02815260206004820152601660248201527f45786368616e67696e672069732064697361626c656400000000000000000000604482015290519081900360640190fd5b600e60008d6000191660001916815260200190815260200160002060009054906101000a9004600160a060020a0316600160a060020a0316639dc29fac8e8d6040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050600060405180830381600087803b1580156150e757600080fd5b505af11580156150fb573d6000803e3d6000fd5b5050505061510a8c8c8c612145565b9550859450600093508715615195576151238c8b6119c5565b9250615180615173847384d626b2bb4d0f064067e4bf80fce7055d8f3e7b63907af6c06040518163ffffffff1660e060020a02815260040160206040518083038186803b1580156149bd57600080fd5b879063ffffffff614ec916565b9450615192868663ffffffff61430716565b93505b60008a8152600e60205260408082205481517f867904b4000000000000000000000000000000000000000000000000000000008152600160a060020a038d81166004830152602482018a90529251929091169263867904b49260448084019382900301818387803b15801561520957600080fd5b505af115801561521d573d6000803e3d6000fd5b5050505060008411156153ee5761523e8a8560e960020a622c222902612145565b60e960020a622c2229026000908152600e60209081527f24466f25c15cd38334941647ac559e67e1ead957cc1be398ef066877266eac9254601054604080517feb1edd610000000000000000000000000000000000000000000000000000000081529051959750600160a060020a039283169563867904b495939092169363eb1edd619360048084019492938390030190829087803b1580156152e057600080fd5b505af11580156152f4573d6000803e3d6000fd5b505050506040513d602081101561530a57600080fd5b50516040805160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820186905251604480830192600092919082900301818387803b15801561535757600080fd5b505af115801561536b573d6000803e3d6000fd5b5050601054604080517f22bf55ef000000000000000000000000000000000000000000000000000000008152600481018790529051600160a060020a0390921693506322bf55ef925060248082019260009290919082900301818387803b1580156153d557600080fd5b505af11580156153e9573d6000803e3d6000fd5b505050505b600e60008b6000191660001916815260200190815260200160002060009054906101000a9004600160a060020a0316600160a060020a031663eb6ecc038e8b886040518463ffffffff1660e060020a0281526004018084600160a060020a0316600160a060020a0316815260200183600160a060020a0316600160a060020a031681526020018281526020019350505050600060405180830381600087803b15801561549957600080fd5b505af11580156154ad573d6000803e3d6000fd5b505050506154bf8d8d8d8d898e6156cb565b5060019c9b505050505050505050505050565b600854604080517fdd62ed3e000000000000000000000000000000000000000000000000000000008152600160a060020a03878116600483015288811660248301529151600093929092169163da46098c9188918a9161556b918991879163dd62ed3e9160448082019260209290919082900301818d87803b15801561555757600080fd5b505af11580156149d1573d6000803e3d6000fd5b6040805160e060020a63ffffffff8716028152600160a060020a03948516600482015292909316602483015260448201529051606480830192600092919082900301818387803b1580156155be57600080fd5b505af11580156155d2573d6000803e3d6000fd5b505050506136ef8585858561585f565b6000611d9b8585858561585f565b60006121e883836b033b2e3c9fd0803ce800000061565a565b60006121e883836b033b2e3c9fd0803ce800000061569e565b600061349a82633b9aca0063ffffffff61443216565b60006305f5e10082046005600a82061061565057600a015b600a900492915050565b6000806156808461567487600a870263ffffffff61443216565b9063ffffffff615b3916565b90506005600a825b061061569257600a015b600a9004949350505050565b600080600a83046156b5868663ffffffff61443216565b8115156156be57fe5b0490506005600a82615688565b600480546040805160208082018a90528183018990526060820188905260808201879052600160a060020a0386811660a0808501919091528451808503909101815260c08085018087527f53796e746845786368616e676528616464726573732c627974657333322c756990527f6e743235362c627974657333322c75696e743235362c6164647265737329000060e086015294519384900360fe01842060e060020a63907dff97028552600260248601819052604486018290528e84166064870181905260006084880181905260a48801819052998701978852835160c48801528351949098169863907dff9798939791969295939490938493839260e4909101918a0190808383885b838110156157ee5781810151838201526020016157d6565b50505050905090810190601f16801561581b5780820380516001836020036101000a031916815260200191505b50975050505050505050600060405180830381600087803b15801561583f57600080fd5b505af1158015615853573d6000803e3d6000fd5b50505050505050505050565b6000600160a060020a03841615156158c1576040805160e560020a62461bcd02815260206004820181905260248201527f43616e6e6f74207472616e7366657220746f2074686520302061646472657373604482015290519081900360640190fd5b600160a060020a038416301415615922576040805160e560020a62461bcd02815260206004820152601f60248201527f43616e6e6f74207472616e7366657220746f2074686520636f6e747261637400604482015290519081900360640190fd5b600454600160a060020a0385811691161415615988576040805160e560020a62461bcd02815260206004820152601c60248201527f43616e6e6f74207472616e7366657220746f207468652070726f787900000000604482015290519081900360640190fd5b6008546040805160e060020a6370a08231028152600160a060020a0388811660048301529151919092169163b46310f69188916159eb91889186916370a08231916024808201926020929091908290030181600087803b15801561555757600080fd5b6040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050600060405180830381600087803b158015615a3d57600080fd5b505af1158015615a51573d6000803e3d6000fd5b50506008546040805160e060020a6370a08231028152600160a060020a038981166004830152915191909216935063b46310f692508791615ab891889186916370a08231916024808201926020929091908290030181600087803b15801561156e57600080fd5b6040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050600060405180830381600087803b158015615b0a57600080fd5b505af1158015615b1e573d6000803e3d6000fd5b50505050615b2d858585614330565b613b1185858585615b5c565b600080808311615b4857600080fd5b8284811515615b5357fe5b04949350505050565b6007805460010190819055833b9060009081831115615cf057620186a05a10615b8857620186a0615b8a565b5a5b915085600160a060020a0316828887876040516024018084600160a060020a0316600160a060020a0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b83811015615bf8578181015183820152602001615be0565b50505050905090810190601f168015615c255780820380516001836020036101000a031916815260200191505b5060408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fc0ee0b8a000000000000000000000000000000000000000000000000000000001781529051825192975095508594509250905080838360005b83811015615cac578181015183820152602001615c94565b50505050905090810190601f168015615cd95780820380516001836020036101000a031916815260200191505b5091505060006040518083038160008787f1505050505b6007548114611f8057600080fd5b815481835581811115615d2257600083815260209020615d22918101908301615d27565b505050565b613eab91905b80821115615d415760008155600101615d2d565b509056004f6e6c792074686520636f6e7472616374206f776e6572206d617920706572666f726d207468697320616374696f6e00000000000000000000000000000000004f776e6572206f6e6c792066756e6374696f6e00000000000000000000000000a165627a7a7230582013db7f2ef945de98bb6e1aad7a2961ea3171379aa9c96160599d2cf65d82338f0029000000000000000000000000c011a72400e58ecd99ee497cf89e3775d4bd732f0000000000000000000000005b1b5fea1b99d83ad479df0c222f0492385381dd0000000000000000000000004b9ca5607f1ff8019c1c6a3c2f0cc8de622d5b82000000000000000000000000b64ff7a4a33acdf48d97dab0d764afd0f6176882000000000000000000000000565c9eb432f4ae9633e50e1213ab4f23d8f31f54000000000000000000000000a8cb0b163cefb21f22c72f6a7d243184bd688a5a000000000000000000000000a05e45396703babaa9c276b5e5a9b6e2c175b521000000000000000000000000b671f2210b1f6621a2607ea63e6b2dc3e2464d1f000000000000000000000000971e78e0c92392a4e39099835cf7e6ab535b2227000000000000000000000000ffa72fd80d8a84032d855bfb67036baf45949009000000000000000000000000000000000000000000813f3978f89409843fffea",
"result": "0x4b144999650fdc87b42ed184e73918ac22d70f01"
},
"empty init code": {
"address": "0x0000000000000000000000000000000000000000",
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
"init_code": "0x",
"result": "0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0"
}
}
}
{
"source": "https://github.com/ethereum/tests/blob/develop/RLPTests/rlptest.json",
"notes": "Removed BigInt test, since ethers does not support integers over 10^16: https://github.com/ethers-io/ethers.js/issues/418",
"latestcommit": "b2dcd19973637ac05e17646378dac9cbb2927075",
"date": "2020-01-08",
"rlpTests": {
"emptystring": {
"in": "",
"out": "0x80"
},
"bytestring00": {
"in": "\u0000",
"out": "0x00"
},
"bytestring01": {
"in": "\u0001",
"out": "0x01"
},
"bytestring7F": {
"in": "\u007F",
"out": "0x7f"
},
"shortstring": {
"in": "dog",
"out": "0x83646f67"
},
"shortstring2": {
"in": "Lorem ipsum dolor sit amet, consectetur adipisicing eli",
"out": "0xb74c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e7365637465747572206164697069736963696e6720656c69"
},
"longstring": {
"in": "Lorem ipsum dolor sit amet, consectetur adipisicing elit",
"out": "0xb8384c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e7365637465747572206164697069736963696e6720656c6974"
},
"longstring2": {
"in": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mauris magna, suscipit sed vehicula non, iaculis faucibus tortor. Proin suscipit ultricies malesuada. Duis tortor elit, dictum quis tristique eu, ultrices at risus. Morbi a est imperdiet mi ullamcorper aliquet suscipit nec lorem. Aenean quis leo mollis, vulputate elit varius, consequat enim. Nulla ultrices turpis justo, et posuere urna consectetur nec. Proin non convallis metus. Donec tempor ipsum in mauris congue sollicitudin. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse convallis sem vel massa faucibus, eget lacinia lacus tempor. Nulla quis ultricies purus. Proin auctor rhoncus nibh condimentum mollis. Aliquam consequat enim at metus luctus, a eleifend purus egestas. Curabitur at nibh metus. Nam bibendum, neque at auctor tristique, lorem libero aliquet arcu, non interdum tellus lectus sit amet eros. Cras rhoncus, metus ac ornare cursus, dolor justo ultrices metus, at ullamcorper volutpat",
"out": "0xb904004c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20437572616269747572206d6175726973206d61676e612c20737573636970697420736564207665686963756c61206e6f6e2c20696163756c697320666175636962757320746f72746f722e2050726f696e20737573636970697420756c74726963696573206d616c6573756164612e204475697320746f72746f7220656c69742c2064696374756d2071756973207472697374697175652065752c20756c7472696365732061742072697375732e204d6f72626920612065737420696d70657264696574206d6920756c6c616d636f7270657220616c6971756574207375736369706974206e6563206c6f72656d2e2041656e65616e2071756973206c656f206d6f6c6c69732c2076756c70757461746520656c6974207661726975732c20636f6e73657175617420656e696d2e204e756c6c6120756c74726963657320747572706973206a7573746f2c20657420706f73756572652075726e6120636f6e7365637465747572206e65632e2050726f696e206e6f6e20636f6e76616c6c6973206d657475732e20446f6e65632074656d706f7220697073756d20696e206d617572697320636f6e67756520736f6c6c696369747564696e2e20566573746962756c756d20616e746520697073756d207072696d697320696e206661756369627573206f726369206c756374757320657420756c74726963657320706f737565726520637562696c69612043757261653b2053757370656e646973736520636f6e76616c6c69732073656d2076656c206d617373612066617563696275732c2065676574206c6163696e6961206c616375732074656d706f722e204e756c6c61207175697320756c747269636965732070757275732e2050726f696e20617563746f722072686f6e637573206e69626820636f6e64696d656e74756d206d6f6c6c69732e20416c697175616d20636f6e73657175617420656e696d206174206d65747573206c75637475732c206120656c656966656e6420707572757320656765737461732e20437572616269747572206174206e696268206d657475732e204e616d20626962656e64756d2c206e6571756520617420617563746f72207472697374697175652c206c6f72656d206c696265726f20616c697175657420617263752c206e6f6e20696e74657264756d2074656c6c7573206c65637475732073697420616d65742065726f732e20437261732072686f6e6375732c206d65747573206163206f726e617265206375727375732c20646f6c6f72206a7573746f20756c747269636573206d657475732c20617420756c6c616d636f7270657220766f6c7574706174"
},
"zero": {
"in": 0,
"out": "0x80"
},
"smallint": {
"in": 1,
"out": "0x01"
},
"smallint2": {
"in": 16,
"out": "0x10"
},
"smallint3": {
"in": 79,
"out": "0x4f"
},
"smallint4": {
"in": 127,
"out": "0x7f"
},
"mediumint1": {
"in": 128,
"out": "0x8180"
},
"mediumint2": {
"in": 1000,
"out": "0x8203e8"
},
"mediumint3": {
"in": 100000,
"out": "0x830186a0"
},
"mediumint4": {
"in": "#83729609699884896815286331701780722",
"out": "0x8f102030405060708090a0b0c0d0e0f2"
},
"mediumint5": {
"in": "#105315505618206987246253880190783558935785933862974822347068935681",
"out": "0x9c0100020003000400050006000700080009000a000b000c000d000e01"
},
"emptylist": {
"in": [],
"out": "0xc0"
},
"stringlist": {
"in": [ "dog", "god", "cat" ],
"out": "0xcc83646f6783676f6483636174"
},
"multilist": {
"in": [ "zw", [ 4 ], 1 ],
"out": "0xc6827a77c10401"
},
"shortListMax1": {
"in": [ "asdf", "qwer", "zxcv", "asdf","qwer", "zxcv", "asdf", "qwer", "zxcv", "asdf", "qwer"],
"out": "0xf784617364668471776572847a78637684617364668471776572847a78637684617364668471776572847a78637684617364668471776572"
},
"longList1" : {
"in" : [
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"]
],
"out": "0xf840cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376"
},
"longList2" : {
"in" : [
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"],
["asdf","qwer","zxcv"]
],
"out": "0xf90200cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376cf84617364668471776572847a786376"
},
"listsoflists": {
"in": [ [ [], [] ], [] ],
"out": "0xc4c2c0c0c0"
},
"listsoflists2": {
"in": [ [], [[]], [ [], [[]] ] ],
"out": "0xc7c0c1c0c3c0c1c0"
},
"dictTest1" : {
"in" : [
["key1", "val1"],
["key2", "val2"],
["key3", "val3"],
["key4", "val4"]
],
"out" : "0xecca846b6579318476616c31ca846b6579328476616c32ca846b6579338476616c33ca846b6579348476616c34"
}
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -5,7 +5,7 @@ import { makeAddress } from '../byte-utils' ...@@ -5,7 +5,7 @@ import { makeAddress } from '../byte-utils'
import { OVMAccount } from '../types/ovm-types' import { OVMAccount } from '../types/ovm-types'
export const DUMMY_ACCOUNTS: Array<{ export const DUMMY_ACCOUNTS: Array<{
address: string, address: string
data: OVMAccount data: OVMAccount
}> = [ }> = [
{ {
...@@ -27,5 +27,5 @@ export const DUMMY_ACCOUNTS: Array<{ ...@@ -27,5 +27,5 @@ export const DUMMY_ACCOUNTS: Array<{
codeHash: DUMMY_BYTES32[3], codeHash: DUMMY_BYTES32[3],
ethAddress: NON_ZERO_ADDRESS, ethAddress: NON_ZERO_ADDRESS,
}, },
} },
] ]
/* External Imports */
import { keccak256 } from 'ethers/lib/utils'
export const DUMMY_BYTECODE = '0x123412341234'
export const DUMMY_BYTECODE_BYTELEN = 6
export const DUMMY_BYTECODE_HASH = keccak256(DUMMY_BYTECODE)
...@@ -3,7 +3,7 @@ import { NON_ZERO_ADDRESS } from '../constants' ...@@ -3,7 +3,7 @@ import { NON_ZERO_ADDRESS } from '../constants'
export const DUMMY_CONTEXT = { export const DUMMY_CONTEXT = {
GLOBAL: { GLOBAL: {
ovmCHAINID: 11 ovmCHAINID: 11,
}, },
TRANSACTION: { TRANSACTION: {
ovmORIGIN: NON_ZERO_ADDRESS, ovmORIGIN: NON_ZERO_ADDRESS,
...@@ -15,6 +15,6 @@ export const DUMMY_CONTEXT = { ...@@ -15,6 +15,6 @@ export const DUMMY_CONTEXT = {
MESSAGE: { MESSAGE: {
ovmCALLER: NON_ZERO_ADDRESS, ovmCALLER: NON_ZERO_ADDRESS,
ovmADDRESS: NON_ZERO_ADDRESS, ovmADDRESS: NON_ZERO_ADDRESS,
ovmSTATICCTX: true ovmSTATICCTX: true,
} },
} }
export * from './accounts' export * from './accounts'
export * from './bytes32' export * from './bytes32'
export * from './context' export * from './context'
export * from './bytecode'
...@@ -6,3 +6,5 @@ export * from './mocks' ...@@ -6,3 +6,5 @@ export * from './mocks'
export * from './buffer-utils' export * from './buffer-utils'
export * from './byte-utils' export * from './byte-utils'
export * from './codec' export * from './codec'
export * from './data'
export * from './test-utils'
...@@ -24,7 +24,7 @@ export const bindMockWatcherToVM = (): void => { ...@@ -24,7 +24,7 @@ export const bindMockWatcherToVM = (): void => {
// Set up some things we'll need for later. // Set up some things we'll need for later.
let txid: string let txid: string
let messages: Array<{ const messages: Array<{
address: string address: string
sighash: string sighash: string
calldata: string calldata: string
...@@ -34,7 +34,7 @@ export const bindMockWatcherToVM = (): void => { ...@@ -34,7 +34,7 @@ export const bindMockWatcherToVM = (): void => {
// Modify the vm.runTx function to capture an ID for each transaction. // Modify the vm.runTx function to capture an ID for each transaction.
const originalRunTx = vm.runTx.bind(vm) const originalRunTx = vm.runTx.bind(vm)
const modifiedRunTx = async (opts: any): Promise<any> => { function modifiedRunTx(opts: any): Promise<any> {
// Buidler runs transactions multiple times (e.g., for gas estimation). // Buidler runs transactions multiple times (e.g., for gas estimation).
// Here we're computing a unique ID for each transaction (based on sender, // Here we're computing a unique ID for each transaction (based on sender,
// nonce, and transaction data) so that we don't log calls multiple times. // nonce, and transaction data) so that we don't log calls multiple times.
...@@ -54,7 +54,7 @@ export const bindMockWatcherToVM = (): void => { ...@@ -54,7 +54,7 @@ export const bindMockWatcherToVM = (): void => {
// Modify the pre-message handler to capture calldata. // Modify the pre-message handler to capture calldata.
const originalBeforeMessageHandler = vmTracer['_beforeMessageHandler' as any] const originalBeforeMessageHandler = vmTracer['_beforeMessageHandler' as any]
const modifiedBeforeMessageHandler = async (message: any, next: any) => { function modifiedBeforeMessageHandler(message: any, next: any) {
// We only care about capturing if we're sending to one of our mocks. // We only care about capturing if we're sending to one of our mocks.
const address = message.to const address = message.to
? toHexString(message.to).toLowerCase() ? toHexString(message.to).toLowerCase()
...@@ -93,7 +93,7 @@ export const bindMockWatcherToVM = (): void => { ...@@ -93,7 +93,7 @@ export const bindMockWatcherToVM = (): void => {
// Modify the post-message handler to insert the correct return data. // Modify the post-message handler to insert the correct return data.
const originalAfterMessageHandler = vmTracer['_afterMessageHandler' as any] const originalAfterMessageHandler = vmTracer['_afterMessageHandler' as any]
const modifiedAfterMessageHandler = async (result: any, next: any) => { async function modifiedAfterMessageHandler(result: any, next: any) {
// We don't need to do anything if we haven't stored any mock messages. // We don't need to do anything if we haven't stored any mock messages.
if (messages.length > 0) { if (messages.length > 0) {
// We need to look at the messages backwards since the first result will // We need to look at the messages backwards since the first result will
...@@ -107,12 +107,10 @@ export const bindMockWatcherToVM = (): void => { ...@@ -107,12 +107,10 @@ export const bindMockWatcherToVM = (): void => {
const inputParams = contract.__spec const inputParams = contract.__spec
? contract.__spec.interface.decodeFunctionData( ? contract.__spec.interface.decodeFunctionData(
fn.functionName, fn.functionName,
contract.__spec.interface.getSighash(fn.functionName) + message.calldata.slice(2) contract.__spec.interface.getSighash(fn.functionName) +
) message.calldata.slice(2)
: ethers.utils.defaultAbiCoder.decode(
fn.inputTypes,
message.calldata
) )
: ethers.utils.defaultAbiCoder.decode(fn.inputTypes, message.calldata)
const returnValues = Array.isArray(fn.returnValues) const returnValues = Array.isArray(fn.returnValues)
? fn.returnValues ? fn.returnValues
...@@ -175,18 +173,20 @@ export const bindMockContractToVM = ( ...@@ -175,18 +173,20 @@ export const bindMockContractToVM = (
} }
} }
const sighash = mock.interface.getSighash(functionName) const sig = mock.interface.getSighash(functionName)
return calls[sighash] || [] return calls[sig] || []
} }
if (!Array.isArray(spec)) { if (!Array.isArray(spec)) {
;(mock as any).__spec = spec ;(mock as any).__spec = spec
;(mock as any).__sigmap = Object.keys(mock.interface.functions).reduce(
;(mock as any).__sigmap = Object.keys(mock.interface.functions).reduce((sigmap, fn) => { (sigmap, fn) => {
fn = fn.split('(')[0] fn = fn.split('(')[0]
sigmap[spec.interface.getSighash(fn)] = mock.interface.getSighash(fn) sigmap[spec.interface.getSighash(fn)] = mock.interface.getSighash(fn)
return sigmap return sigmap
}, {}) },
{}
)
} }
;(mock as any).getCallCount = (functionName: string): number => { ;(mock as any).getCallCount = (functionName: string): number => {
...@@ -204,9 +204,7 @@ export const bindMockContractToVM = ( ...@@ -204,9 +204,7 @@ export const bindMockContractToVM = (
const iface = mock.__spec ? mock.__spec.interface : mock.interface const iface = mock.__spec ? mock.__spec.interface : mock.interface
const calldata = iface.getSighash(functionName) + calls[callIndex].slice(10) const calldata = iface.getSighash(functionName) + calls[callIndex].slice(10)
return iface return iface.decodeFunctionData(functionName, calldata).map((element) => {
.decodeFunctionData(functionName, calldata)
.map((element) => {
return element return element
}) })
} }
......
...@@ -6,11 +6,7 @@ import { FunctionFragment, ParamType } from 'ethers/lib/utils' ...@@ -6,11 +6,7 @@ import { FunctionFragment, ParamType } from 'ethers/lib/utils'
/* Internal Imports */ /* Internal Imports */
import { MockContract, MockContractFunction } from './mock-contract.types' import { MockContract, MockContractFunction } from './mock-contract.types'
import { bindMockContractToVM, bindMockWatcherToVM } from './mock-binding' import { bindMockContractToVM, bindMockWatcherToVM } from './mock-binding'
import { import { SolidityCompiler, getDefaultCompiler, compile } from '../compilation'
SolidityCompiler,
getDefaultCompiler,
compile,
} from '../compilation'
/** /**
* Generates contract code for a mock contract. * Generates contract code for a mock contract.
...@@ -210,12 +206,12 @@ export const getMockContract = async ( ...@@ -210,12 +206,12 @@ export const getMockContract = async (
originalDefinePropertyFn(object, name, props) originalDefinePropertyFn(object, name, props)
} }
const MockContract = (await MockContractFactory.deploy()) as MockContract const mock = (await MockContractFactory.deploy()) as MockContract
Object.defineProperty = originalDefinePropertyFn Object.defineProperty = originalDefinePropertyFn
bindMockWatcherToVM() bindMockWatcherToVM()
bindMockContractToVM(MockContract, fns, spec) bindMockContractToVM(mock, fns, spec)
return MockContract return mock
} }
...@@ -2,27 +2,6 @@ ...@@ -2,27 +2,6 @@
import { ethers } from '@nomiclabs/buidler' import { ethers } from '@nomiclabs/buidler'
import { Contract } from 'ethers' import { Contract } from 'ethers'
const getLibraryConfig = (ProxyManager: Contract): any => {
return [
{
name: 'Lib_ByteUtils',
params: []
},
{
name: 'Lib_EthUtils',
params: [ProxyManager.address]
},
{
name: 'Lib_RLPReader',
params: []
},
{
name: 'Lib_RLPWriter',
params: []
}
]
}
export const makeProxies = async ( export const makeProxies = async (
Proxy_Manager: Contract, Proxy_Manager: Contract,
names: string[] names: string[]
...@@ -40,10 +19,7 @@ export const makeProxies = async ( ...@@ -40,10 +19,7 @@ export const makeProxies = async (
Proxy_Manager.address Proxy_Manager.address
) )
await Proxy_Manager.setProxy( await Proxy_Manager.setProxy(name, Proxy_Forwarder.address)
name,
Proxy_Forwarder.address
)
} }
} }
...@@ -54,41 +30,9 @@ export const setProxyTarget = async ( ...@@ -54,41 +30,9 @@ export const setProxyTarget = async (
): Promise<void> => { ): Promise<void> => {
await makeProxies(Proxy_Manager, [name]) await makeProxies(Proxy_Manager, [name])
await Proxy_Manager.setTarget( await Proxy_Manager.setTarget(name, target.address)
name,
target.address
)
} }
export const getProxyManager = async (): Promise<Contract> => { export const getProxyManager = async (): Promise<Contract> => {
const Factory__Proxy_Manager = await ethers.getContractFactory( return (await ethers.getContractFactory('Proxy_Manager')).deploy()
'Proxy_Manager'
)
const Proxy_Manager = await Factory__Proxy_Manager.deploy()
const libraryConfig = getLibraryConfig(Proxy_Manager)
await makeProxies(
Proxy_Manager,
libraryConfig.map((config) => {
return config.name
})
)
for (const config of libraryConfig) {
const Factory__Lib_Contract = await ethers.getContractFactory(
config.name
)
const Lib_Contract = await Factory__Lib_Contract.deploy(
...config.params
)
await Proxy_Manager.setTarget(
config.name,
Lib_Contract.address
)
}
return Proxy_Manager
} }
import bre, { ethers } from '@nomiclabs/buidler' import bre, { ethers } from '@nomiclabs/buidler'
import { Contract, BigNumber, ContractFactory } from "ethers"; import { Contract, BigNumber, ContractFactory } from 'ethers'
import { keccak256, defaultAbiCoder } from "ethers/lib/utils"; import { keccak256, defaultAbiCoder } from 'ethers/lib/utils'
import { remove0x } from '../byte-utils' import { remove0x } from '../byte-utils'
import { readArtifact } from '@nomiclabs/buidler/internal/artifacts'; import { readArtifact } from '@nomiclabs/buidler/internal/artifacts'
const getFlattenedKeys = ( const getFlattenedKeys = (depth: number, value: any): string[] => {
depth: number,
value: any,
): string[] => {
if (depth === 0) { if (depth === 0) {
return [] return []
} }
let keys = Object.keys(value) let keys = Object.keys(value)
if (depth > 1) { if (depth > 1) {
keys = keys.concat( keys = keys.concat(getFlattenedKeys(depth - 1, Object.values(value)[0]))
getFlattenedKeys(
depth - 1,
Object.values(value)[0]
)
)
} }
return keys return keys
...@@ -38,29 +30,25 @@ const toHexString32 = ( ...@@ -38,29 +30,25 @@ const toHexString32 = (
} }
} }
const getFlattenedValues = ( const getFlattenedValues = (depth: number, value: any): any[] => {
depth: number,
value: any
): any[] => {
if (depth > 0) { if (depth > 0) {
return getFlattenedValues( return getFlattenedValues(depth - 1, Object.values(value)[0])
depth - 1,
Object.values(value)[0]
)
} }
if (typeof value === 'object' && value !== null) { if (typeof value === 'object' && value !== null) {
return Object.keys(value).map((key) => { return Object.keys(value).map((key) => {
return { return {
label: key, label: key,
value: toHexString32(value[key]) value: toHexString32(value[key]),
} }
}) })
} else { } else {
return [{ return [
label: "default", {
value: toHexString32(value) label: 'default',
}] value: toHexString32(value),
},
]
} }
} }
...@@ -68,33 +56,24 @@ const getStorageSlotHash = ( ...@@ -68,33 +56,24 @@ const getStorageSlotHash = (
slot: number, slot: number,
depth: number, depth: number,
value: any value: any
): string =>{ ): string => {
let keys = [] let keys = []
if (typeof value === 'object' && value !== null) { if (typeof value === 'object' && value !== null) {
keys = getFlattenedKeys(depth, value) keys = getFlattenedKeys(depth, value)
} }
if (keys.length === 0) { if (keys.length === 0) {
return defaultAbiCoder.encode( return defaultAbiCoder.encode(['uint256'], [slot])
['uint256'],
[slot]
)
} else { } else {
let slotHash = toHexString32(slot) let slotHash = toHexString32(slot)
for (const key of keys) { for (const key of keys) {
slotHash = keccak256( slotHash = keccak256(toHexString32(key) + remove0x(slotHash))
toHexString32(key) +
remove0x(slotHash)
)
} }
return slotHash return slotHash
} }
} }
const parseInputSlots = ( const parseInputSlots = (layout: any, inputTypeName: string): any[] => {
layout: any,
inputTypeName: string
): any[] => {
const inputType = layout.types[inputTypeName] const inputType = layout.types[inputTypeName]
if (inputType.encoding === 'mapping') { if (inputType.encoding === 'mapping') {
...@@ -104,14 +83,16 @@ const parseInputSlots = ( ...@@ -104,14 +83,16 @@ const parseInputSlots = (
return inputType.members.map((member: any) => { return inputType.members.map((member: any) => {
return { return {
label: member.label, label: member.label,
slot: member.slot slot: member.slot,
} }
}) })
} else { } else {
return [{ return [
label: "default", {
slot: 0 label: 'default',
}] slot: 0,
},
]
} }
} else { } else {
throw new Error('Encoding type not supported.') throw new Error('Encoding type not supported.')
...@@ -122,17 +103,23 @@ export const getModifiableStorageFactory = async ( ...@@ -122,17 +103,23 @@ export const getModifiableStorageFactory = async (
name: string name: string
): Promise<ContractFactory> => { ): Promise<ContractFactory> => {
const contractFactory = await ethers.getContractFactory(name) const contractFactory = await ethers.getContractFactory(name)
const proxyFactory = await ethers.getContractFactory('Helper_ModifiableStorage') const proxyFactory = await ethers.getContractFactory(
'Helper_ModifiableStorage'
)
const originalDeploy = contractFactory.deploy.bind(contractFactory) const originalDeploy = contractFactory.deploy.bind(contractFactory)
contractFactory.deploy = async (...args: any[]): Promise<Contract> => { contractFactory.deploy = async (...args: any[]): Promise<Contract> => {
const originalDefinePropertyFn = Object.defineProperty const originalDefinePropertyFn = Object.defineProperty
Object.defineProperty = (object: any, name: string, props: any): void => { Object.defineProperty = (
object: any,
propName: string,
props: any
): void => {
if (props.writable === false) { if (props.writable === false) {
props.writable = true props.writable = true
} }
originalDefinePropertyFn(object, name, props) originalDefinePropertyFn(object, propName, props)
} }
const contract = await originalDeploy(...args) const contract = await originalDeploy(...args)
...@@ -144,14 +131,16 @@ export const getModifiableStorageFactory = async ( ...@@ -144,14 +131,16 @@ export const getModifiableStorageFactory = async (
;(contract as any).__setContractStorage = async (value: any) => { ;(contract as any).__setContractStorage = async (value: any) => {
await setContractStorage( await setContractStorage(
contract, contract,
(await readArtifact(bre.config.paths.artifacts, name) as any).storageLayout, ((await readArtifact(bre.config.paths.artifacts, name)) as any)
.storageLayout,
value value
) )
} }
;(contract as any).__checkContractStorage = async (value: any) => { ;(contract as any).__checkContractStorage = async (value: any) => {
await checkContractStorage( await checkContractStorage(
contract, contract,
(await readArtifact(bre.config.paths.artifacts, name) as any).storageLayout, ((await readArtifact(bre.config.paths.artifacts, name)) as any)
.storageLayout,
value value
) )
} }
...@@ -171,40 +160,63 @@ export const setContractStorage = async ( ...@@ -171,40 +160,63 @@ export const setContractStorage = async (
storage = storage || {} storage = storage || {}
for (const [key, value] of Object.entries(storage)) { for (const [key, value] of Object.entries(storage)) {
const layoutMap = layout.storage.find((layoutMap: any) => { const layoutMap = layout.storage.find((lmap: any) => {
return layoutMap.label === key return lmap.label === key
}) })
const inputSlots = parseInputSlots(layout, layoutMap.type) const inputSlots = parseInputSlots(layout, layoutMap.type)
const slot = parseInt(layoutMap.slot) const slot = parseInt(layoutMap.slot, 10)
const depth = (layoutMap.type.match(/t_mapping/g) || []).length let depth = (layoutMap.type.match(/t_mapping/g) || []).length
if (typeof value !== 'object') { if (typeof value !== 'object') {
const slotHash = getStorageSlotHash(slot, depth, value) const slotHash = getStorageSlotHash(slot, depth, value)
await contract.__setStorageSlot( await contract.__setStorageSlot(slotHash, toHexString32(value as string))
slotHash, } else {
toHexString32(value as string) if (key === 'contractStorage' || key === 'verifiedContractStorage') {
for (const [subKey1, subValue1] of Object.entries(value)) {
for (const [subKey, subValue] of Object.entries(subValue1)) {
const baseSlotHash = getStorageSlotHash(slot, depth, {
[subKey1]: {
[subKey]: subValue,
},
})
const slotValues = getFlattenedValues(depth, {
[subKey1]: {
[subKey]: subValue,
},
})
for (const slotValue of slotValues) {
const slotIndex = inputSlots.find((inputSlot) => {
return inputSlot.label === slotValue.label
}).slot
const slotHash = toHexString32(
BigNumber.from(baseSlotHash).add(slotIndex)
) )
await contract.__setStorageSlot(slotHash, slotValue.value)
}
}
}
} else { } else {
for (const [subKey, subValue] of Object.entries(value)) { for (const [subKey, subValue] of Object.entries(value)) {
const baseSlotHash = getStorageSlotHash(slot, depth, { const baseSlotHash = getStorageSlotHash(slot, depth, {
[subKey]: subValue [subKey]: subValue,
}) })
const slotValues = getFlattenedValues(depth, { const slotValues = getFlattenedValues(depth, {
[subKey]: subValue [subKey]: subValue,
}) })
for (let i = 0; i < slotValues.length; i++) { for (const slotValue of slotValues) {
const slotValue = slotValues[i]
const slotIndex = inputSlots.find((inputSlot) => { const slotIndex = inputSlots.find((inputSlot) => {
return inputSlot.label === slotValue.label return inputSlot.label === slotValue.label
}).slot }).slot
const slotHash = toHexString32(BigNumber.from(baseSlotHash).add(slotIndex)) const slotHash = toHexString32(
BigNumber.from(baseSlotHash).add(slotIndex)
await contract.__setStorageSlot(
slotHash,
slotValue.value
) )
await contract.__setStorageSlot(slotHash, slotValue.value)
}
} }
} }
} }
...@@ -219,45 +231,82 @@ export const checkContractStorage = async ( ...@@ -219,45 +231,82 @@ export const checkContractStorage = async (
storage = storage || {} storage = storage || {}
for (const [key, value] of Object.entries(storage)) { for (const [key, value] of Object.entries(storage)) {
const layoutMap = layout.storage.find((layoutMap: any) => { const layoutMap = layout.storage.find((lmap: any) => {
return layoutMap.label === key return lmap.label === key
}) })
const inputSlots = parseInputSlots(layout, layoutMap.type) const inputSlots = parseInputSlots(layout, layoutMap.type)
const slot = parseInt(layoutMap.slot) const slot = parseInt(layoutMap.slot, 10)
const depth = (layoutMap.type.match(/t_mapping/g) || []).length const depth = (layoutMap.type.match(/t_mapping/g) || []).length
if (typeof value !== 'object') { if (typeof value !== 'object') {
const slotHash = getStorageSlotHash(slot, depth, value) const slotHash = getStorageSlotHash(slot, depth, value)
const retSlotValue = await contract.__getStorageSlot( const retSlotValue = await contract.__getStorageSlot(slotHash)
slotHash
)
if (retSlotValue !== toHexString32(value as string)) { if (retSlotValue !== toHexString32(value as string)) {
throw new Error(`Resulting state of ${key} (${retSlotValue}) did not match expected state (${toHexString32(value as string)})`) throw new Error(
`Resulting state of ${key} (${retSlotValue}) did not match expected state (${toHexString32(
value as string
)})`
)
} }
} else { } else {
for (const [subKey, subValue] of Object.entries(value)) { if (key === 'contractStorage' || key === 'verifiedContractStorage') {
for (const [subKey1, subValue1] of Object.entries(value)) {
for (const [subKey, subValue] of Object.entries(subValue1)) {
const baseSlotHash = getStorageSlotHash(slot, depth, { const baseSlotHash = getStorageSlotHash(slot, depth, {
[subKey]: subValue [subKey1]: {
[subKey]: subValue,
},
}) })
const slotValues = getFlattenedValues(depth, { const slotValues = getFlattenedValues(depth, {
[subKey]: subValue [subKey1]: {
[subKey]: subValue,
},
}) })
for (let i = 0; i < slotValues.length; i++) { for (const slotValue of slotValues) {
const slotValue = slotValues[i]
const slotIndex = inputSlots.find((inputSlot) => { const slotIndex = inputSlots.find((inputSlot) => {
return inputSlot.label === slotValue.label return inputSlot.label === slotValue.label
}).slot }).slot
const slotHash = toHexString32(BigNumber.from(baseSlotHash).add(slotIndex)) const slotHash = toHexString32(
BigNumber.from(baseSlotHash).add(slotIndex)
)
const retSlotValue = await contract.__getStorageSlot(slotHash)
if (retSlotValue !== slotValue.value) {
throw new Error(
`Resulting state of ${slotValue.label} (${retSlotValue}) did not match expected state (${slotValue.value}).`
)
}
}
}
}
} else {
for (const [subKey, subValue] of Object.entries(value)) {
const baseSlotHash = getStorageSlotHash(slot, depth, {
[subKey]: subValue,
})
const slotValues = getFlattenedValues(depth, {
[subKey]: subValue,
})
const retSlotValue = await contract.__getStorageSlot( for (const slotValue of slotValues) {
slotHash const slotIndex = inputSlots.find((inputSlot) => {
return inputSlot.label === slotValue.label
}).slot
const slotHash = toHexString32(
BigNumber.from(baseSlotHash).add(slotIndex)
) )
const retSlotValue = await contract.__getStorageSlot(slotHash)
if (retSlotValue !== slotValue.value) { if (retSlotValue !== slotValue.value) {
throw new Error(`Resulting state of ${slotValue.label} (${retSlotValue}) did not match expected state (${slotValue.value}).`) throw new Error(
`Resulting state of ${slotValue.label} (${retSlotValue}) did not match expected state (${slotValue.value}).`
)
}
} }
} }
} }
......
export * from './test-runner'
export * from './test.types'
/* External Imports */
import { Contract } from 'ethers'
/* Internal Imports */
import { TestStep } from './test.types'
export interface TestCallGenerator {
getCalldata(): string
getReturnData(): string
}
export class DefaultTestGenerator implements TestCallGenerator {
constructor(
protected ovmExecutionManager: Contract,
protected ovmCallHelper: Contract,
protected step: TestStep
) {}
getFunctionParams(): any[] {
return this.step.functionParams
}
getReturnValues(): any[] {
return this.step.returnValues
}
getCalldata(): string {
return this.ovmExecutionManager.interface.encodeFunctionData(
this.step.functionName,
this.getFunctionParams()
)
}
getReturnData(): string {
return this.ovmExecutionManager.interface.encodeFunctionResult(
this.step.functionName,
this.getReturnValues()
)
}
}
export class ovmCALLGenerator extends DefaultTestGenerator {
getCalleeGenerators(): TestCallGenerator[] {
return (this.step.functionParams[2] as TestStep[]).map((step) => {
return getTestGenerator(
step,
this.ovmExecutionManager,
this.ovmCallHelper,
)
})
}
getFunctionParams(): any[] {
return [
this.step.functionParams[0],
this.step.functionParams[1],
this.ovmCallHelper.interface.encodeFunctionData(
'runSteps',
[
{
callsToEM: this.getCalleeGenerators().map((calleeGenerator) => {
return calleeGenerator.getCalldata()
}),
shouldRevert: !this.step.returnStatus
}
]
)
]
}
getReturnValues(): any[] {
return [
this.step.returnStatus,
this.ovmCallHelper.interface.encodeFunctionResult(
'runSteps',
[
this.getCalleeGenerators().map((calleeGenerator) => {
return {
success: true,
data: calleeGenerator.getReturnData()
}
})
]
)
]
}
}
export const getTestGenerator = (
step: TestStep,
ovmExecutionManager: Contract,
ovmCallHelper: Contract
): TestCallGenerator => {
switch (step.functionName) {
case 'ovmCALL':
return new ovmCALLGenerator(
ovmExecutionManager,
ovmCallHelper,
step
)
default:
return new DefaultTestGenerator(
ovmExecutionManager,
ovmCallHelper,
step
)
}
}
import { expect } from '../../setup'
/* External Imports */
import { Contract } from 'ethers'
import { cloneDeep } from 'lodash'
/* Internal Imports */
import { getModifiableStorageFactory } from '../storage/contract-storage'
import { GAS_LIMIT } from '../constants'
import { getTestGenerator } from './test-generation'
import { TestParameters, TestDefinition, isTestDefinition } from './test.types'
const setPlaceholderStrings = (
test: any,
ovmExecutionManager: Contract,
ovmStateManager: Contract,
ovmCallHelper: Contract
): any => {
const setPlaceholder = (
kv: string
): string => {
if (kv === '$OVM_EXECUTION_MANAGER') {
return ovmExecutionManager.address
} else if (kv === '$OVM_STATE_MANAGER') {
return ovmStateManager.address
} else if (kv === '$OVM_CALL_HELPER') {
return ovmCallHelper.address
} else if (kv.startsWith('$DUMMY_OVM_ADDRESS_')) {
return '0x' + kv.split('$DUMMY_OVM_ADDRESS_')[1].padStart(40, '0')
} else {
return kv
}
}
if (Array.isArray(test)) {
test = test.map((element) => {
return setPlaceholderStrings(
element,
ovmExecutionManager,
ovmStateManager,
ovmCallHelper
)
})
} else if (typeof test === 'object' && test !== null) {
for (const key of Object.keys(test)) {
const replacedKey = setPlaceholder(key)
if (replacedKey !== key) {
test[replacedKey] = test[key]
delete test[key]
}
test[replacedKey] = setPlaceholderStrings(
test[replacedKey],
ovmExecutionManager,
ovmStateManager,
ovmCallHelper
)
}
} else if (typeof test === 'string') {
test = setPlaceholder(test)
}
return test
}
const fixtureDeployContracts = async (): Promise <{
OVM_SafetyChecker: Contract,
OVM_StateManager: Contract,
OVM_ExecutionManager: Contract,
OVM_CallHelper: Contract
}> => {
const Factory__OVM_SafetyChecker = await getModifiableStorageFactory(
'OVM_SafetyChecker'
)
const Factory__OVM_StateManager = await getModifiableStorageFactory(
'OVM_StateManager'
)
const Factory__OVM_ExecutionManager = await getModifiableStorageFactory(
'OVM_ExecutionManager'
)
const Factory__Helper_CodeContractForCalls = await getModifiableStorageFactory(
'Helper_CodeContractForCalls'
)
const OVM_SafetyChecker = await Factory__OVM_SafetyChecker.deploy()
const OVM_StateManager = await Factory__OVM_StateManager.deploy()
const OVM_ExecutionManager = await Factory__OVM_ExecutionManager.deploy(OVM_SafetyChecker.address)
const OVM_CallHelper = await Factory__Helper_CodeContractForCalls.deploy()
return {
OVM_SafetyChecker,
OVM_StateManager,
OVM_ExecutionManager,
OVM_CallHelper,
}
}
export const runExecutionManagerTest = (
test: TestDefinition
): void => {
test.preState = test.preState || {}
test.postState = test.postState || {}
describe(`Standard test: ${test.name}`, () => {
test.parameters.map((parameters) => {
if (isTestDefinition(parameters)) {
runExecutionManagerTest(
{
...parameters,
preState: {
...test.preState,
...parameters.preState
},
postState: {
...test.postState,
...parameters.postState
}
}
)
} else {
let OVM_StateManager: Contract
let OVM_ExecutionManager: Contract
let OVM_CallHelper: Contract
beforeEach(async () => {
const contracts = await fixtureDeployContracts()
OVM_StateManager = contracts.OVM_StateManager
OVM_ExecutionManager = contracts.OVM_ExecutionManager
OVM_CallHelper = contracts.OVM_CallHelper
})
let replacedParams: TestParameters
let replacedTest: TestDefinition
beforeEach(async () => {
replacedParams = setPlaceholderStrings(
cloneDeep(parameters),
OVM_ExecutionManager,
OVM_StateManager,
OVM_CallHelper
)
replacedTest = setPlaceholderStrings(
cloneDeep(test),
OVM_ExecutionManager,
OVM_StateManager,
OVM_CallHelper
)
})
beforeEach(async () => {
await OVM_ExecutionManager.__setContractStorage(replacedTest.preState.ExecutionManager)
await OVM_StateManager.__setContractStorage(replacedTest.preState.StateManager)
})
afterEach(async () => {
await OVM_ExecutionManager.__checkContractStorage({
...replacedTest.preState.ExecutionManager,
...replacedTest.postState.ExecutionManager
})
await OVM_StateManager.__checkContractStorage({
...replacedTest.preState.StateManager,
...replacedTest.postState.StateManager
})
})
parameters.steps.map((step, idx) => {
it(`should run test: ${test.name} ${idx}`, async () => {
const testGenerator = getTestGenerator(
replacedParams.steps[idx],
OVM_ExecutionManager,
OVM_CallHelper
)
const callResult = await OVM_ExecutionManager.provider.call({
to: OVM_ExecutionManager.address,
data: testGenerator.getCalldata(),
gasLimit: GAS_LIMIT
})
await OVM_ExecutionManager.signer.sendTransaction({
to: OVM_ExecutionManager.address,
data: testGenerator.getCalldata(),
gasLimit: GAS_LIMIT
})
expect(callResult).to.equal(testGenerator.getReturnData())
})
})
}
})
})
}
import { expect } from '../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, BigNumber, ContractFactory } from 'ethers'
import { cloneDeep } from 'lodash'
/* Internal Imports */
import {
TestDefinition,
ParsedTestStep,
TestParameter,
TestStep,
TestStep_CALL,
TestStep_Run,
isRevertFlagError,
isTestStep_SSTORE,
isTestStep_SLOAD,
isTestStep_CALL,
isTestStep_CREATE,
isTestStep_CREATE2,
isTestStep_Context,
isTestStep_evm,
isTestStep_Run,
isTestStep_EXTCODESIZE,
isTestStep_EXTCODEHASH,
isTestStep_EXTCODECOPY,
isTestStep_REVERT,
} from './test.types'
import { encodeRevertData } from '../codec'
import { getModifiableStorageFactory } from '../storage/contract-storage'
import { GAS_LIMIT, NON_NULL_BYTES32 } from '../constants'
export class ExecutionManagerTestRunner {
private snapshot: string
private contracts: {
OVM_SafetyChecker: Contract
OVM_StateManager: Contract
OVM_ExecutionManager: Contract
Helper_TestRunner: Contract
Factory__Helper_TestRunner_CREATE: ContractFactory
} = {
OVM_SafetyChecker: undefined,
OVM_StateManager: undefined,
OVM_ExecutionManager: undefined,
Helper_TestRunner: undefined,
Factory__Helper_TestRunner_CREATE: undefined,
}
public run(test: TestDefinition) {
test.preState = test.preState || {}
test.postState = test.postState || {}
describe(`OVM_ExecutionManager Test: ${test.name}`, () => {
test.subTests?.map((subTest) => {
this.run({
...subTest,
preState: {
...test.preState,
...subTest.preState,
},
postState: {
...test.postState,
...subTest.postState,
},
})
})
test.parameters?.map((parameter) => {
beforeEach(async () => {
await this.initContracts()
})
let replacedTest: TestDefinition
let replacedParameter: TestParameter
beforeEach(async () => {
replacedTest = this.setPlaceholderStrings(test)
replacedParameter = this.setPlaceholderStrings(parameter)
})
beforeEach(async () => {
await this.contracts.OVM_StateManager.__setContractStorage({
accounts: {
[this.contracts.Helper_TestRunner.address]: {
nonce: 0,
codeHash: NON_NULL_BYTES32,
ethAddress: this.contracts.Helper_TestRunner.address,
},
},
})
})
beforeEach(async () => {
await this.contracts.OVM_ExecutionManager.__setContractStorage(
replacedTest.preState.ExecutionManager
)
await this.contracts.OVM_StateManager.__setContractStorage(
replacedTest.preState.StateManager
)
})
afterEach(async () => {
await this.contracts.OVM_ExecutionManager.__checkContractStorage(
replacedTest.postState.ExecutionManager
)
await this.contracts.OVM_StateManager.__checkContractStorage(
replacedTest.postState.StateManager
)
})
const itfn = parameter.focus ? it.only : it
itfn(`should execute: ${parameter.name}`, async () => {
try {
for (const step of replacedParameter.steps) {
await this.runTestStep(step)
}
} catch (err) {
if (parameter.expectInvalidStateAccess) {
expect(err.toString()).to.contain(
'VM Exception while processing transaction: revert'
)
} else {
throw err
}
}
})
})
})
}
private async initContracts() {
if (this.snapshot) {
await ethers.provider.send('evm_revert', [this.snapshot])
return
}
this.contracts.OVM_SafetyChecker = await (
await ethers.getContractFactory('OVM_SafetyChecker')
).deploy()
this.contracts.OVM_ExecutionManager = await (
await getModifiableStorageFactory('OVM_ExecutionManager')
).deploy(this.contracts.OVM_SafetyChecker.address)
this.contracts.OVM_StateManager = await (
await getModifiableStorageFactory('OVM_StateManager')
).deploy(this.contracts.OVM_ExecutionManager.address)
this.contracts.Helper_TestRunner = await (
await ethers.getContractFactory('Helper_TestRunner')
).deploy()
this.contracts.Factory__Helper_TestRunner_CREATE = await ethers.getContractFactory(
'Helper_TestRunner_CREATE'
)
this.snapshot = await ethers.provider.send('evm_snapshot', [])
}
private setPlaceholderStrings(obj: any) {
const getReplacementString = (kv: string): string => {
if (kv === '$OVM_EXECUTION_MANAGER') {
return this.contracts.OVM_ExecutionManager.address
} else if (kv === '$OVM_STATE_MANAGER') {
return this.contracts.OVM_StateManager.address
} else if (kv === '$OVM_SAFETY_CHECKER') {
return this.contracts.OVM_SafetyChecker.address
} else if (kv === '$OVM_CALL_HELPER') {
return this.contracts.Helper_TestRunner.address
} else if (kv.startsWith('$DUMMY_OVM_ADDRESS_')) {
return '0x' + (kv.split('$DUMMY_OVM_ADDRESS_')[1] + '0').repeat(20)
} else {
return kv
}
}
let ret: any = cloneDeep(obj)
if (Array.isArray(ret)) {
ret = ret.map((element: any) => {
return this.setPlaceholderStrings(element)
})
} else if (typeof ret === 'object' && ret !== null) {
for (const key of Object.keys(ret)) {
const replacedKey = getReplacementString(key)
if (replacedKey !== key) {
ret[replacedKey] = ret[key]
delete ret[key]
}
ret[replacedKey] = this.setPlaceholderStrings(ret[replacedKey])
}
} else if (typeof ret === 'string') {
ret = getReplacementString(ret)
}
return ret
}
private async runTestStep(step: TestStep | TestStep_Run) {
if (isTestStep_Run(step)) {
let calldata: string
if (step.functionParams.data) {
calldata = step.functionParams.data
} else {
const runStep: TestStep_CALL = {
functionName: 'ovmCALL',
functionParams: {
gasLimit: GAS_LIMIT,
target: this.contracts.Helper_TestRunner.address,
subSteps: step.functionParams.subSteps,
},
expectedReturnStatus: true,
}
calldata = this.encodeFunctionData(runStep)
}
await this.contracts.OVM_ExecutionManager.run(
{
timestamp: step.functionParams.timestamp,
queueOrigin: step.functionParams.queueOrigin,
entrypoint: step.functionParams.entrypoint,
origin: step.functionParams.origin,
msgSender: step.functionParams.msgSender,
gasLimit: step.functionParams.gasLimit,
data: calldata,
},
this.contracts.OVM_StateManager.address
)
} else {
await this.contracts.OVM_ExecutionManager.ovmCALL(
GAS_LIMIT / 2,
this.contracts.Helper_TestRunner.address,
this.contracts.Helper_TestRunner.interface.encodeFunctionData(
'runSingleTestStep',
[this.parseTestStep(step)]
)
)
}
}
private parseTestStep(step: TestStep): ParsedTestStep {
return {
functionName: step.functionName,
functionData: this.encodeFunctionData(step),
expectedReturnStatus: this.getReturnStatus(step),
expectedReturnData: this.encodeExpectedReturnData(step),
}
}
private getReturnStatus(step: TestStep): boolean {
if (isTestStep_evm(step)) {
return false
} else if (isTestStep_Context(step)) {
return true
} else {
return step.expectedReturnStatus
}
}
private encodeFunctionData(step: TestStep): string {
if (isTestStep_evm(step)) {
if (isRevertFlagError(step.returnData)) {
return encodeRevertData(
step.returnData.flag,
step.returnData.data,
step.returnData.nuisanceGasLeft,
step.returnData.ovmGasRefund
)
} else {
return step.returnData || '0x'
}
}
let functionParams: any[] = []
if (
isTestStep_SSTORE(step) ||
isTestStep_SLOAD(step) ||
isTestStep_EXTCODESIZE(step) ||
isTestStep_EXTCODEHASH(step) ||
isTestStep_EXTCODECOPY(step)
) {
functionParams = Object.values(step.functionParams)
} else if (isTestStep_CALL(step)) {
functionParams = [
step.functionParams.gasLimit,
step.functionParams.target,
step.functionParams.calldata ||
this.contracts.Helper_TestRunner.interface.encodeFunctionData(
'runMultipleTestSteps',
[
step.functionParams.subSteps.map((subStep) => {
return this.parseTestStep(subStep)
}),
]
),
]
} else if (isTestStep_CREATE(step)) {
functionParams = [
this.contracts.Factory__Helper_TestRunner_CREATE.getDeployTransaction(
step.functionParams.bytecode || '0x',
step.functionParams.subSteps?.map((subStep) => {
return this.parseTestStep(subStep)
}) || []
).data,
]
} else if (isTestStep_REVERT(step)) {
functionParams = [step.revertData || '0x']
}
return this.contracts.OVM_ExecutionManager.interface.encodeFunctionData(
step.functionName,
functionParams
)
}
private encodeExpectedReturnData(step: TestStep): string {
if (isTestStep_evm(step)) {
return '0x'
}
if (isTestStep_REVERT(step)) {
return step.expectedReturnValue || '0x'
}
if (isRevertFlagError(step.expectedReturnValue)) {
return encodeRevertData(
step.expectedReturnValue.flag,
step.expectedReturnValue.data,
step.expectedReturnValue.nuisanceGasLeft,
step.expectedReturnValue.ovmGasRefund
)
}
let returnData: any[] = []
if (isTestStep_CALL(step)) {
if (step.expectedReturnValue === '0x00') {
return step.expectedReturnValue
} else {
returnData = [
step.expectedReturnStatus,
step.expectedReturnValue || '0x',
]
}
} else if (BigNumber.isBigNumber(step.expectedReturnValue)) {
returnData = [step.expectedReturnValue.toHexString()]
} else if (step.expectedReturnValue !== undefined) {
if (step.expectedReturnValue === '0x00') {
return step.expectedReturnValue
} else {
returnData = [step.expectedReturnValue]
}
}
return this.contracts.OVM_ExecutionManager.interface.encodeFunctionResult(
step.functionName,
returnData
)
}
}
/* External Imports */ /* External Imports */
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
export type SolidityFunctionParameter = string | number | BigNumber export type ContextOpcode =
| 'ovmCALLER'
| 'ovmADDRESS'
| 'ovmORIGIN'
| 'ovmTIMESTAMP'
| 'ovmGASLIMIT'
| 'ovmCHAINID'
export interface TestStep { type CallOpcode = 'ovmCALL' | 'ovmSTATICCALL' | 'ovmDELEGATECALL'
type RevertFlagError = {
flag: number
nuisanceGasLeft?: number
ovmGasRefund?: number
data?: string
}
interface TestStep_evm {
functionName: 'evmRETURN' | 'evmREVERT' | 'evmINVALID'
returnData?: string | RevertFlagError
}
interface TestStep_Context {
functionName: ContextOpcode
expectedReturnValue: string | number | BigNumber
}
interface TestStep_REVERT {
functionName: 'ovmREVERT'
revertData?: string
expectedReturnStatus: boolean
expectedReturnValue?: string
}
interface TestStep_EXTCODESIZE {
functionName: 'ovmEXTCODESIZE'
functionParams: {
address: string
}
expectedReturnStatus: boolean
expectedReturnValue: number | RevertFlagError
}
interface TestStep_EXTCODEHASH {
functionName: 'ovmEXTCODEHASH'
functionParams: {
address: string
}
expectedReturnStatus: boolean
expectedReturnValue: string | RevertFlagError
}
interface TestStep_EXTCODECOPY {
functionName: 'ovmEXTCODECOPY'
functionParams: {
address: string
offset: number
length: number
}
expectedReturnStatus: boolean
expectedReturnValue: string | RevertFlagError
}
interface TestStep_SSTORE {
functionName: 'ovmSSTORE'
functionParams: {
key: string
value: string
}
expectedReturnStatus: boolean
expectedReturnValue?: RevertFlagError
}
interface TestStep_SLOAD {
functionName: 'ovmSLOAD'
functionParams: {
key: string
}
expectedReturnStatus: boolean
expectedReturnValue: string | RevertFlagError
}
export interface TestStep_CALL {
functionName: CallOpcode
functionParams: {
gasLimit: number | BigNumber
target: string
calldata?: string
subSteps?: TestStep[]
}
expectedReturnStatus: boolean
expectedReturnValue?: string | RevertFlagError
}
interface TestStep_CREATE {
functionName: 'ovmCREATE'
functionParams: {
bytecode?: string
subSteps?: TestStep[]
}
expectedReturnStatus: boolean
expectedReturnValue: string | RevertFlagError
}
interface TestStep_CREATE2 {
functionName: 'ovmCREATE2'
functionParams: {
salt: string
bytecode?: string
subSteps?: TestStep[]
}
expectedReturnStatus: boolean
expectedReturnValue: string | RevertFlagError
}
export interface TestStep_Run {
functionName: 'run'
functionParams: {
timestamp: number
queueOrigin: number
entrypoint: string
origin: string
msgSender: string
gasLimit: number
data?: string
subSteps?: TestStep[]
}
}
export type TestStep =
| TestStep_Context
| TestStep_SSTORE
| TestStep_SLOAD
| TestStep_CALL
| TestStep_CREATE
| TestStep_CREATE2
| TestStep_EXTCODESIZE
| TestStep_EXTCODEHASH
| TestStep_EXTCODECOPY
| TestStep_REVERT
| TestStep_evm
export interface ParsedTestStep {
functionName: string functionName: string
functionParams: Array<SolidityFunctionParameter | TestStep[]> functionData: string
returnStatus: boolean expectedReturnStatus: boolean
returnValues: any[] expectedReturnData: string
} }
export interface TestParameters { export const isRevertFlagError = (
steps: TestStep[] expectedReturnValue: any
): expectedReturnValue is RevertFlagError => {
return (
typeof expectedReturnValue === 'object' &&
expectedReturnValue !== null &&
expectedReturnValue.flag !== undefined
)
}
export const isTestStep_evm = (step: TestStep): step is TestStep_evm => {
return ['evmRETURN', 'evmREVERT', 'evmINVALID'].includes(step.functionName)
}
export const isTestStep_Context = (
step: TestStep
): step is TestStep_Context => {
return [
'ovmCALLER',
'ovmADDRESS',
'ovmORIGIN',
'ovmTIMESTAMP',
'ovmGASLIMIT',
'ovmCHAINID',
].includes(step.functionName)
}
export const isTestStep_SSTORE = (step: TestStep): step is TestStep_SSTORE => {
return step.functionName === 'ovmSSTORE'
}
export const isTestStep_SLOAD = (step: TestStep): step is TestStep_SLOAD => {
return step.functionName === 'ovmSLOAD'
}
export const isTestStep_EXTCODESIZE = (
step: TestStep
): step is TestStep_EXTCODESIZE => {
return step.functionName === 'ovmEXTCODESIZE'
}
export const isTestStep_EXTCODEHASH = (
step: TestStep
): step is TestStep_EXTCODEHASH => {
return step.functionName === 'ovmEXTCODEHASH'
}
export const isTestStep_EXTCODECOPY = (
step: TestStep
): step is TestStep_EXTCODECOPY => {
return step.functionName === 'ovmEXTCODECOPY'
}
export const isTestStep_REVERT = (step: TestStep): step is TestStep_REVERT => {
return step.functionName === 'ovmREVERT'
}
export const isTestStep_CALL = (step: TestStep): step is TestStep_CALL => {
return ['ovmCALL', 'ovmSTATICCALL', 'ovmDELEGATECALL'].includes(
step.functionName
)
}
export const isTestStep_CREATE = (step: TestStep): step is TestStep_CREATE => {
return step.functionName === 'ovmCREATE'
}
export const isTestStep_CREATE2 = (
step: TestStep
): step is TestStep_CREATE2 => {
return step.functionName === 'ovmCREATE2'
}
export const isTestStep_Run = (
step: TestStep | TestStep_Run
): step is TestStep_Run => {
return step.functionName === 'run'
}
interface TestState {
ExecutionManager: any
StateManager: any
}
export interface TestParameter {
name: string
steps: Array<TestStep | TestStep_Run>
expectInvalidStateAccess?: boolean
focus?: boolean
} }
export interface TestDefinition { export interface TestDefinition {
name: string name: string
preState?: { focus?: boolean
ExecutionManager?: any, preState?: Partial<TestState>
StateManager?: any postState?: Partial<TestState>
}, parameters?: TestParameter[]
parameters: Array<TestParameters | TestDefinition>, subTests?: TestDefinition[]
postState?: {
ExecutionManager?: any,
StateManager?: any
},
}
export const isTestDefinition = (
parameters: TestParameters | TestDefinition
): parameters is TestDefinition => {
return (parameters as TestDefinition).name !== undefined
} }
{ {
"compilerOptions": { "compilerOptions": {
"module": "commonjs",
"target": "es2017",
"declaration": true,
"noImplicitAny": false,
"removeComments": true,
"noLib": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true,
"outDir": "./build", "outDir": "./build",
"baseUrl": "./", "baseUrl": "./",
"resolveJsonModule": true, "resolveJsonModule": true,
"esModuleInterop": true "esModuleInterop": true,
"lib": [
"es7"
],
"typeRoots": [
"node_modules/@types"
]
}, },
"include": ["*.ts", "**/*.ts", "artifacts/*.json"], "include": ["*.ts", "**/*.ts", "artifacts/*.json"],
"exclude": ["./build", "node_modules"], "exclude": ["./build", "node_modules"],
......
{
"extends": [
"tslint:latest",
"tslint-config-prettier",
"tslint-plugin-prettier"
],
"rulesDirectory": [
"tslint-no-focused-test"
],
"rules": {
"ban-types": false,
"comment-format": false,
"interface-name": false,
"max-classes-per-file": false,
"member-ordering": false,
"no-bitwise": false,
"no-empty-interface": false,
"no-implicit-dependencies": [true, "dev"],
"no-string-literal": false,
"no-submodule-imports": false,
"no-unused-expression": false,
"object-literal-sort-keys": false,
"ordered-imports": false,
"prettier": [true, "prettier-config.json"],
"semicolon": false,
"variable-name": false,
"no-focused-test": true,
"no-console": false,
"array-type": false,
"class-name": false
},
"linterOptions": {
"exclude": [
"**/node_modules/**/*"
]
}
}
...@@ -2,6 +2,27 @@ ...@@ -2,6 +2,27 @@
# yarn lockfile v1 # yarn lockfile v1
"@babel/code-frame@^7.0.0":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==
dependencies:
"@babel/highlight" "^7.10.4"
"@babel/helper-validator-identifier@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==
"@babel/highlight@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143"
integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==
dependencies:
"@babel/helper-validator-identifier" "^7.10.4"
chalk "^2.0.0"
js-tokens "^4.0.0"
"@ensdomains/ens@^0.4.4": "@ensdomains/ens@^0.4.4":
version "0.4.5" version "0.4.5"
resolved "https://registry.yarnpkg.com/@ensdomains/ens/-/ens-0.4.5.tgz#e0aebc005afdc066447c6e22feb4eda89a5edbfc" resolved "https://registry.yarnpkg.com/@ensdomains/ens/-/ens-0.4.5.tgz#e0aebc005afdc066447c6e22feb4eda89a5edbfc"
...@@ -2007,6 +2028,11 @@ buffer@^5.0.5, buffer@^5.2.1, buffer@^5.6.0: ...@@ -2007,6 +2028,11 @@ buffer@^5.0.5, buffer@^5.2.1, buffer@^5.6.0:
base64-js "^1.0.2" base64-js "^1.0.2"
ieee754 "^1.1.4" ieee754 "^1.1.4"
builtin-modules@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=
bytes@3.1.0: bytes@3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
...@@ -2129,7 +2155,7 @@ chalk@^1.1.3: ...@@ -2129,7 +2155,7 @@ chalk@^1.1.3:
strip-ansi "^3.0.0" strip-ansi "^3.0.0"
supports-color "^2.0.0" supports-color "^2.0.0"
chalk@^2.4.2: chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.2:
version "2.4.2" version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
...@@ -2352,7 +2378,7 @@ commander@3.0.2: ...@@ -2352,7 +2378,7 @@ commander@3.0.2:
resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e"
integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==
commander@^2.8.1: commander@^2.12.1, commander@^2.8.1:
version "2.20.3" version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
...@@ -3054,6 +3080,14 @@ escape-string-regexp@4.0.0: ...@@ -3054,6 +3080,14 @@ escape-string-regexp@4.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
eslint-plugin-prettier@^2.2.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.7.0.tgz#b4312dcf2c1d965379d7f9d5b5f8aaadc6a45904"
integrity sha512-CStQYJgALoQBw3FsBzH0VOVDRnJ/ZimUlpLm226U8qgqYJfPOY/CPK6wyRInMxh73HSKg5wyRwdS4BVYYHwokA==
dependencies:
fast-diff "^1.1.1"
jest-docblock "^21.0.0"
esprima@^4.0.0: esprima@^4.0.0:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
...@@ -3762,6 +3796,11 @@ fast-deep-equal@^3.1.1: ...@@ -3762,6 +3796,11 @@ fast-deep-equal@^3.1.1:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-diff@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
fast-json-stable-stringify@^2.0.0: fast-json-stable-stringify@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
...@@ -5150,6 +5189,11 @@ iterate-value@^1.0.0: ...@@ -5150,6 +5189,11 @@ iterate-value@^1.0.0:
es-get-iterator "^1.0.2" es-get-iterator "^1.0.2"
iterate-iterator "^1.0.1" iterate-iterator "^1.0.1"
jest-docblock@^21.0.0:
version "21.2.0"
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414"
integrity sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw==
js-sha3@0.5.7, js-sha3@^0.5.7: js-sha3@0.5.7, js-sha3@^0.5.7:
version "0.5.7" version "0.5.7"
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7"
...@@ -5160,7 +5204,7 @@ js-sha3@0.8.0, js-sha3@^0.8.0: ...@@ -5160,7 +5204,7 @@ js-sha3@0.8.0, js-sha3@^0.8.0:
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
"js-tokens@^3.0.0 || ^4.0.0": "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
...@@ -5178,7 +5222,7 @@ js-yaml@3.13.1: ...@@ -5178,7 +5222,7 @@ js-yaml@3.13.1:
argparse "^1.0.7" argparse "^1.0.7"
esprima "^4.0.0" esprima "^4.0.0"
js-yaml@3.14.0: js-yaml@3.14.0, js-yaml@^3.13.1:
version "3.14.0" version "3.14.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==
...@@ -5558,6 +5602,11 @@ liftoff@^3.1.0: ...@@ -5558,6 +5602,11 @@ liftoff@^3.1.0:
rechoir "^0.6.2" rechoir "^0.6.2"
resolve "^1.1.7" resolve "^1.1.7"
lines-and-columns@^1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
load-json-file@^1.0.0: load-json-file@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
...@@ -5916,7 +5965,7 @@ mkdirp@*: ...@@ -5916,7 +5965,7 @@ mkdirp@*:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
mkdirp@0.5.5, mkdirp@^0.5.0, mkdirp@^0.5.1: mkdirp@0.5.5, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3:
version "0.5.5" version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
...@@ -6630,6 +6679,11 @@ prepend-http@^2.0.0: ...@@ -6630,6 +6679,11 @@ prepend-http@^2.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
prettier@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.2.tgz#3050700dae2e4c8b67c4c3f666cdb8af405e1ce5"
integrity sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==
pretty-hrtime@^1.0.0: pretty-hrtime@^1.0.0:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1"
...@@ -7135,7 +7189,7 @@ resolve-url@^0.2.1: ...@@ -7135,7 +7189,7 @@ resolve-url@^0.2.1:
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.4.0, resolve@~1.17.0: resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.4.0, resolve@~1.17.0:
version "1.17.0" version "1.17.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
...@@ -8074,16 +8128,61 @@ ts-node@^9.0.0: ...@@ -8074,16 +8128,61 @@ ts-node@^9.0.0:
source-map-support "^0.5.17" source-map-support "^0.5.17"
yn "3.1.1" yn "3.1.1"
tslib@^1.9.3: tslib@^1.13.0, tslib@^1.7.1, tslib@^1.8.1, tslib@^1.9.3:
version "1.13.0" version "1.13.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
tslint-config-prettier@^1.18.0:
version "1.18.0"
resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz#75f140bde947d35d8f0d238e0ebf809d64592c37"
integrity sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg==
tslint-no-focused-test@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/tslint-no-focused-test/-/tslint-no-focused-test-0.5.0.tgz#e0a93ef3fa64bd91c7e7437d1f183204880a8ed5"
integrity sha512-YK0PSY5XAdJaTzVIXxnUGyvB5VAi+H9yTc3e40YVtu8Ix3+zLSz4ufvX6rXT3nWpim0DR6fxXoL/Zk8JI641Vg==
tslint-plugin-prettier@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/tslint-plugin-prettier/-/tslint-plugin-prettier-2.3.0.tgz#73fe71bf9f03842ac48c104122ca9b1de012ecf4"
integrity sha512-F9e4K03yc9xuvv+A0v1EmjcnDwpz8SpCD8HzqSDe0eyg34cBinwn9JjmnnRrNAs4HdleRQj7qijp+P/JTxt4vA==
dependencies:
eslint-plugin-prettier "^2.2.0"
lines-and-columns "^1.1.6"
tslib "^1.7.1"
tslint@^6.1.3:
version "6.1.3"
resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.3.tgz#5c23b2eccc32487d5523bd3a470e9aa31789d904"
integrity sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==
dependencies:
"@babel/code-frame" "^7.0.0"
builtin-modules "^1.1.1"
chalk "^2.3.0"
commander "^2.12.1"
diff "^4.0.1"
glob "^7.1.1"
js-yaml "^3.13.1"
minimatch "^3.0.4"
mkdirp "^0.5.3"
resolve "^1.3.2"
semver "^5.3.0"
tslib "^1.13.0"
tsutils "^2.29.0"
tsort@0.0.1: tsort@0.0.1:
version "0.0.1" version "0.0.1"
resolved "https://registry.yarnpkg.com/tsort/-/tsort-0.0.1.tgz#e2280f5e817f8bf4275657fd0f9aebd44f5a2786" resolved "https://registry.yarnpkg.com/tsort/-/tsort-0.0.1.tgz#e2280f5e817f8bf4275657fd0f9aebd44f5a2786"
integrity sha1-4igPXoF/i/QnVlf9D5rr1E9aJ4Y= integrity sha1-4igPXoF/i/QnVlf9D5rr1E9aJ4Y=
tsutils@^2.29.0:
version "2.29.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99"
integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==
dependencies:
tslib "^1.8.1"
tunnel-agent@^0.6.0: tunnel-agent@^0.6.0:
version "0.6.0" version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
......
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