From e1038d377ff3570cd9e27f9543c9c0587e1e3ece Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Mon, 27 Jul 2020 14:37:59 -0400 Subject: [PATCH 01/24] Started standardizing contracts --- .../bridge/L1ToL2TransactionPasser.sol | 10 +- .../bridge/L2ToL1MessageReceiver.sol | 24 +- .../chain/CanonicalTransactionChain.sol | 132 +++++--- .../chain/SequencerBatchSubmitter.sol | 26 +- .../chain/StateCommitmentChain.sol | 46 ++- .../ovm/ExecutionManager.sol | 316 ++++++++++-------- .../optimistic-ethereum/ovm/FraudVerifier.sol | 31 +- .../ovm/FullStateManager.sol | 54 +-- .../ovm/L2ExecutionManager.sol | 12 +- .../ovm/PartialStateManager.sol | 183 +++++++--- .../optimistic-ethereum/ovm/SafetyChecker.sol | 62 +++- .../ovm/StateTransitioner.sol | 29 +- .../ovm/interfaces/IStateTransitioner.sol | 1 + .../ovm/precompiles/L1MessageSender.sol | 25 +- .../ovm/precompiles/L2ToL1MessagePasser.sol | 18 +- .../ovm/test-helpers/StubExecutionManager.sol | 5 +- .../utils/libraries/RLPReader.sol | 53 +-- .../utils/libraries/RLPWriter.sol | 26 +- .../utils/libraries/RollupMerkleUtils.sol | 46 +-- .../libraries}/TransactionParser.sol | 16 +- 20 files changed, 727 insertions(+), 388 deletions(-) rename packages/contracts/contracts/optimistic-ethereum/{ovm => utils/libraries}/TransactionParser.sol (83%) diff --git a/packages/contracts/contracts/optimistic-ethereum/bridge/L1ToL2TransactionPasser.sol b/packages/contracts/contracts/optimistic-ethereum/bridge/L1ToL2TransactionPasser.sol index 67260ebad545d..acf5076c9d140 100644 --- a/packages/contracts/contracts/optimistic-ethereum/bridge/L1ToL2TransactionPasser.sol +++ b/packages/contracts/contracts/optimistic-ethereum/bridge/L1ToL2TransactionPasser.sol @@ -1,6 +1,9 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; +/** + * @title L1ToL2TransactionPasser + */ contract L1ToL2TransactionPasser { /* * Events @@ -18,13 +21,18 @@ contract L1ToL2TransactionPasser { * Contract Variables */ - uint nonce; + uint private nonce; /* * Public Functions */ + /** + * Pass an L1 transaction to the L2 rollup chain. + * @param _ovmEntrypoint Target address for the transaction. + * @param _ovmCalldata Calldata for the transaction. + */ function passTransactionToL2( address _ovmEntrypoint, bytes memory _ovmCalldata diff --git a/packages/contracts/contracts/optimistic-ethereum/bridge/L2ToL1MessageReceiver.sol b/packages/contracts/contracts/optimistic-ethereum/bridge/L2ToL1MessageReceiver.sol index 8cfac3139def5..3c4bc23e39baa 100644 --- a/packages/contracts/contracts/optimistic-ethereum/bridge/L2ToL1MessageReceiver.sol +++ b/packages/contracts/contracts/optimistic-ethereum/bridge/L2ToL1MessageReceiver.sol @@ -1,9 +1,12 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; -/* Internal Imports */ +/* Library Imports */ import { DataTypes } from "../utils/libraries/DataTypes.sol"; +/** + * @title L2ToL1MessageReceiver + */ contract L2ToL1MessageReceiver { /* * Events @@ -40,6 +43,10 @@ contract L2ToL1MessageReceiver { * Constructor */ + /** + * @param _sequencer Current sequencer address. + * @param _blocksUntilFinal Blocks until transactions are considered final. + */ constructor(address _sequencer, uint _blocksUntilFinal) public { sequencer = _sequencer; blocksUntilFinal = _blocksUntilFinal; @@ -50,6 +57,10 @@ contract L2ToL1MessageReceiver { * Public Functions */ + /** + * Adds a message to the queue. + * @param _message Message to add to the queue. + */ function enqueueL2ToL1Message( DataTypes.L2ToL1Message memory _message ) public { @@ -75,6 +86,12 @@ contract L2ToL1MessageReceiver { messageNonce += 1; } + /** + * Verifies that a message was queued and finalized. + * @param _message Message to verify. + * @param _nonce Nonce for the given message. + * @return Whether or not the message is verified. + */ function verifyL2ToL1Message( DataTypes.L2ToL1Message memory _message, uint _nonce @@ -98,6 +115,11 @@ contract L2ToL1MessageReceiver { * Internal Functions */ + /** + * Calculates the hash of a given message. + * @param _message Message to hash. + * @return Hash of the provided message. + */ function getMessageHash( DataTypes.L2ToL1Message memory _message ) internal pure returns (bytes32) { diff --git a/packages/contracts/contracts/optimistic-ethereum/chain/CanonicalTransactionChain.sol b/packages/contracts/contracts/optimistic-ethereum/chain/CanonicalTransactionChain.sol index 4327486a1620a..948ca12ae3fa6 100644 --- a/packages/contracts/contracts/optimistic-ethereum/chain/CanonicalTransactionChain.sol +++ b/packages/contracts/contracts/optimistic-ethereum/chain/CanonicalTransactionChain.sol @@ -1,17 +1,23 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; -/* Internal Imports */ +/* Contract Imports */ +import { L1ToL2TransactionQueue } from "../queue/L1ToL2TransactionQueue.sol"; +import { SafetyTransactionQueue } from "../queue/SafetyTransactionQueue.sol"; + +/* Library Imports */ import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; import { DataTypes } from "../utils/libraries/DataTypes.sol"; import { RollupMerkleUtils } from "../utils/libraries/RollupMerkleUtils.sol"; -import { L1ToL2TransactionQueue } from "../queue/L1ToL2TransactionQueue.sol"; -import { SafetyTransactionQueue } from "../queue/SafetyTransactionQueue.sol"; +/** + * @title CanonicalTransactionChain + */ contract CanonicalTransactionChain is ContractResolver { /* * Contract Variables */ + address public sequencer; uint public forceInclusionPeriod; uint public cumulativeNumElements; @@ -32,6 +38,12 @@ contract CanonicalTransactionChain is ContractResolver { * Constructor */ + /** + * @param _addressResolver Address of the AddressResolver contract. + * @param _sequencer Address of the sequencer. + * @param _l1ToL2TransactionPasserAddress Address of the L1-L2 transaction passing contract. + * @param _forceInclusionPeriod Timeout in seconds when a transaction must be published. + */ constructor( address _addressResolver, address _sequencer, @@ -46,14 +58,23 @@ contract CanonicalTransactionChain is ContractResolver { lastOVMTimestamp = 0; } + /* * Public Functions */ + /** + * @return Total number of published transaction batches. + */ function getBatchesLength() public view returns (uint) { return batches.length; } + /** + * Computes the hash of a batch header. + * @param _batchHeader Header to hash. + * @return Hash of the provided header. + */ function hashBatchHeader( DataTypes.TxChainBatchHeader memory _batchHeader ) public pure returns (bytes32) { @@ -66,12 +87,20 @@ contract CanonicalTransactionChain is ContractResolver { )); } + /** + * Checks whether a sender is allowed to append to the chain. + * @param _sender Address to check. + * @return Whether or not the address can append. + */ function authenticateAppend( address _sender ) public view returns (bool) { return _sender == sequencer; } + /** + * Attempts to append a transaction batch from pending L1 transactions. + */ function appendL1ToL2Batch() public { L1ToL2TransactionQueue l1ToL2Queue = resolveL1ToL2TransactionQueue(); SafetyTransactionQueue safetyQueue = resolveSafetyTransactionQueue(); @@ -87,6 +116,9 @@ contract CanonicalTransactionChain is ContractResolver { l1ToL2Queue.dequeue(); } + /** + * Attempts to append a transaction batch from the safety queue. + */ function appendSafetyBatch() public { L1ToL2TransactionQueue l1ToL2Queue = resolveL1ToL2TransactionQueue(); SafetyTransactionQueue safetyQueue = resolveSafetyTransactionQueue(); @@ -102,39 +134,11 @@ contract CanonicalTransactionChain is ContractResolver { safetyQueue.dequeue(); } - function _appendQueueBatch( - DataTypes.TimestampedHash memory timestampedHash, - bool isL1ToL2Tx - ) internal { - uint timestamp = timestampedHash.timestamp; - - require( - timestamp + forceInclusionPeriod <= now || authenticateAppend(msg.sender), - "Message sender does not have permission to append this batch" - ); - - lastOVMTimestamp = timestamp; - bytes32 elementsMerkleRoot = timestampedHash.txHash; - uint numElementsInBatch = 1; - - bytes32 batchHeaderHash = keccak256(abi.encodePacked( - timestamp, - isL1ToL2Tx, - elementsMerkleRoot, - numElementsInBatch, - cumulativeNumElements // cumulativePrevElements - )); - - batches.push(batchHeaderHash); - cumulativeNumElements += numElementsInBatch; - - if (isL1ToL2Tx) { - emit L1ToL2BatchAppended(batchHeaderHash); - } else { - emit SafetyQueueBatchAppended(batchHeaderHash); - } - } - + /** + * Attempts to append a batch provided by the sequencer. + * @param _txBatch Transaction batch to append. + * @param _timestamp Timestamp for the batch. + */ function appendSequencerBatch( bytes[] memory _txBatch, uint _timestamp @@ -194,11 +198,16 @@ contract CanonicalTransactionChain is ContractResolver { emit SequencerBatchAppended(batchHeaderHash); } - // verifies an element is in the current list at the given position + /** + * Checks that an element is included within a published batch. + * @param _element Element to prove within the batch. + * @param _position Index of the element within the batch. + * @param _inclusionProof Inclusion proof for the element/batch. + */ function verifyElement( - bytes memory _element, // the element of the list being proven - uint _position, // the position in the list of the element being proven - DataTypes.TxElementInclusionProof memory _inclusionProof // inclusion proof in the rollup batch + bytes memory _element, + uint _position, + DataTypes.TxElementInclusionProof memory _inclusionProof ) public view returns (bool) { // For convenience, store the batchHeader DataTypes.TxChainBatchHeader memory batchHeader = _inclusionProof.batchHeader; @@ -225,6 +234,49 @@ contract CanonicalTransactionChain is ContractResolver { } + /* + * Internal Functions + */ + + /** + * Appends a batch. + * @param _timestampedHash Timestamped transaction hash. + * @param _isL1ToL2Tx Whether or not this is an L1-L2 transaction. + */ + function _appendQueueBatch( + DataTypes.TimestampedHash memory _timestampedHash, + bool _isL1ToL2Tx + ) internal { + uint timestamp = _timestampedHash.timestamp; + + require( + timestamp + forceInclusionPeriod <= now || authenticateAppend(msg.sender), + "Message sender does not have permission to append this batch" + ); + + lastOVMTimestamp = timestamp; + bytes32 elementsMerkleRoot = _timestampedHash.txHash; + uint numElementsInBatch = 1; + + bytes32 batchHeaderHash = keccak256(abi.encodePacked( + timestamp, + _isL1ToL2Tx, + elementsMerkleRoot, + numElementsInBatch, + cumulativeNumElements // cumulativePrevElements + )); + + batches.push(batchHeaderHash); + cumulativeNumElements += numElementsInBatch; + + if (_isL1ToL2Tx) { + emit L1ToL2BatchAppended(batchHeaderHash); + } else { + emit SafetyQueueBatchAppended(batchHeaderHash); + } + } + + /* * Contract Resolution */ diff --git a/packages/contracts/contracts/optimistic-ethereum/chain/SequencerBatchSubmitter.sol b/packages/contracts/contracts/optimistic-ethereum/chain/SequencerBatchSubmitter.sol index bf4c124199d9b..c18c8da6f1888 100644 --- a/packages/contracts/contracts/optimistic-ethereum/chain/SequencerBatchSubmitter.sol +++ b/packages/contracts/contracts/optimistic-ethereum/chain/SequencerBatchSubmitter.sol @@ -1,17 +1,18 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; -/* Internal Imports */ -import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; +/* Contract Imports */ import { CanonicalTransactionChain } from "./CanonicalTransactionChain.sol"; import { StateCommitmentChain } from "./StateCommitmentChain.sol"; +/* Library Imports */ +import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; + /** * @title SequencerBatchSubmitter - * @notice Helper contract that allows the sequencer to submit both a state - * commitment batch and tx batch in a single transaction. This ensures - * that # state roots == # of txs, preventing other users from - * submitting state batches to the state chain. + * @notice Helper contract that allows the sequencer to submit both a state commitment batch and tx + * batch in a single transaction. This ensures that # state roots == # of txs, preventing + * other users from submitting state batches to the state chain. */ contract SequencerBatchSubmitter is ContractResolver { /* @@ -20,6 +21,7 @@ contract SequencerBatchSubmitter is ContractResolver { address public sequencer; + /* * Modifiers */ @@ -37,6 +39,10 @@ contract SequencerBatchSubmitter is ContractResolver { * Constructor */ + /** + * @param _addressResolver Address of the AddressResolver contract. + * @param _sequencer Address of the sequencer. + */ constructor( address _addressResolver, address _sequencer @@ -53,12 +59,10 @@ contract SequencerBatchSubmitter is ContractResolver { */ /** - * @notice Append equal sized batches of transactions and state roots to - * their respective chains. + * @notice Append equal sized transaction and state batches to their respective chains. * @param _txBatch An array of transactions. - * @param _txBatchTimestamp The timestamp that will be submitted with the - * tx batch - this timestamp will likely lag - * behind the actual time by a few minutes. + * @param _txBatchTimestamp The timestamp that will be submitted with the tx batch. This + * timestamp will likely lag behind the actual time by a few minutes. * @param _stateBatch An array of 32 byte state roots */ function appendTransitionBatch( diff --git a/packages/contracts/contracts/optimistic-ethereum/chain/StateCommitmentChain.sol b/packages/contracts/contracts/optimistic-ethereum/chain/StateCommitmentChain.sol index de6da619d8510..eea46084b0615 100644 --- a/packages/contracts/contracts/optimistic-ethereum/chain/StateCommitmentChain.sol +++ b/packages/contracts/contracts/optimistic-ethereum/chain/StateCommitmentChain.sol @@ -1,13 +1,18 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; -/* Internal Imports */ +/* Contract Imports */ +import { CanonicalTransactionChain } from "./CanonicalTransactionChain.sol"; +import { FraudVerifier } from "../ovm/FraudVerifier.sol"; + +/* Library Imports */ import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; import { DataTypes } from "../utils/libraries/DataTypes.sol"; import { RollupMerkleUtils } from "../utils/libraries/RollupMerkleUtils.sol"; -import { CanonicalTransactionChain } from "./CanonicalTransactionChain.sol"; -import { FraudVerifier } from "../ovm/FraudVerifier.sol"; +/** + * @title StateCommitmentChain + */ contract StateCommitmentChain is ContractResolver { /* * Contract Variables @@ -16,26 +21,40 @@ contract StateCommitmentChain is ContractResolver { uint public cumulativeNumElements; bytes32[] public batches; + /* * Events */ event StateBatchAppended(bytes32 _batchHeaderHash); + /* * Constructor */ + /** + * @param _addressResolver Address of the AddressResolver contract. + */ constructor(address _addressResolver) public ContractResolver(_addressResolver) {} + /* * Public Functions */ + /** + * @return Total number of published state batches. + */ function getBatchesLength() public view returns (uint) { return batches.length; } + /** + * Computes the hash of a batch header. + * @param _batchHeader Header to hash. + * @return Hash of the provided header. + */ function hashBatchHeader( DataTypes.StateChainBatchHeader memory _batchHeader ) public pure returns (bytes32) { @@ -46,6 +65,10 @@ contract StateCommitmentChain is ContractResolver { )); } + /** + * Attempts to append a state batch. + * @param _stateBatch Batch to append. + */ function appendStateBatch( bytes[] memory _stateBatch ) public { @@ -73,10 +96,15 @@ contract StateCommitmentChain is ContractResolver { emit StateBatchAppended(batchHeaderHash); } - // verifies an element is in the current list at the given position + /** + * Checks that an element is included within a published batch. + * @param _element Element to prove within the batch. + * @param _position Index of the element within the batch. + * @param _inclusionProof Inclusion proof for the element/batch. + */ function verifyElement( - bytes memory _element, // the element of the list being proven - uint _position, // the position in the list of the element being proven + bytes memory _element, + uint _position, DataTypes.StateElementInclusionProof memory _inclusionProof ) public view returns (bool) { DataTypes.StateChainBatchHeader memory batchHeader = _inclusionProof.batchHeader; @@ -99,6 +127,12 @@ contract StateCommitmentChain is ContractResolver { return hashBatchHeader(batchHeader) == batches[_inclusionProof.batchIndex]; } + /** + * Deletes all state batches after and including the given batch index. + * Can only be called by the FraudVerifier contract. + * @param _batchIndex Index of the batch to start deletion from. + * @param _batchHeader Header of batch at the given index. + */ function deleteAfterInclusive( uint _batchIndex, DataTypes.StateChainBatchHeader memory _batchHeader diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/ExecutionManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/ExecutionManager.sol index 4ee3b31c58b99..c87bed0ee3555 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/ExecutionManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/ExecutionManager.sol @@ -1,16 +1,20 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; -/* Internal Imports */ +/* Contract Imports */ +import { L2ToL1MessagePasser } from "./precompiles/L2ToL1MessagePasser.sol"; +import { L1MessageSender } from "./precompiles/L1MessageSender.sol"; +import { StateManager } from "./StateManager.sol"; +import { SafetyChecker } from "./SafetyChecker.sol"; + +/* Library Imports */ import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; import { DataTypes } from "../utils/libraries/DataTypes.sol"; import { ContractAddressGenerator } from "../utils/libraries/ContractAddressGenerator.sol"; import { RLPEncode } from "../utils/libraries/RLPEncode.sol"; -import { L2ToL1MessagePasser } from "./precompiles/L2ToL1MessagePasser.sol"; -import { L1MessageSender } from "./precompiles/L1MessageSender.sol"; -import { StateManager } from "./StateManager.sol"; + +/* Testing Imports */ import { StubSafetyChecker } from "./test-helpers/StubSafetyChecker.sol"; -import { SafetyChecker } from "./SafetyChecker.sol"; /** * @title ExecutionManager @@ -23,15 +27,15 @@ contract ExecutionManager is ContractResolver { * Contract Constants */ - address constant ZERO_ADDRESS = 0x0000000000000000000000000000000000000000; + address constant private ZERO_ADDRESS = 0x0000000000000000000000000000000000000000; - // bitwise right shift 28 * 8 bits so the 4 method ID bytes are in the right-most bytes - bytes32 constant ovmCallMethodId = keccak256("ovmCALL()") >> 224; - bytes32 constant ovmCreateMethodId = keccak256("ovmCREATE()") >> 224; + // Bitwise right shift 28 * 8 bits so the 4 method ID bytes are in the right-most bytes + bytes32 constant private METHOD_ID_OVM_CALL = keccak256("ovmCALL()") >> 224; + bytes32 constant private METHOD_ID_OVM_CREATE = keccak256("ovmCREATE()") >> 224; // Precompile addresses - address constant l2ToL1MessagePasserOvmAddress = 0x4200000000000000000000000000000000000000; - address constant l1MsgSenderAddress = 0x4200000000000000000000000000000000000001; + address constant private L2_L2_OVM_MESSAGE_PASSER = 0x4200000000000000000000000000000000000000; + address constant private L1_MESSAGE_SENDER = 0x4200000000000000000000000000000000000001; /* @@ -45,7 +49,9 @@ contract ExecutionManager is ContractResolver { * Events */ - event ActiveContract(address _activeContract); + event ActiveContract( + address _activeContract + ); event CreatedContract( address _ovmContractAddress, address _codeContractAddress, @@ -72,6 +78,11 @@ contract ExecutionManager is ContractResolver { * Constructor */ + /** + * @param _addressResolver Address of the AddressResolver contract. + * @param _owner Address of the owner of this contract. + * @param _blockGasLimit Gas limit for OVM blocks. + */ constructor( address _addressResolver, address _owner, @@ -90,9 +101,9 @@ contract ExecutionManager is ContractResolver { // Deploy custom precompiles L2ToL1MessagePasser l1ToL2MessagePasser = new L2ToL1MessagePasser(address(this)); - stateManager.associateCodeContract(l2ToL1MessagePasserOvmAddress, address(l1ToL2MessagePasser)); + stateManager.associateCodeContract(L2_L2_OVM_MESSAGE_PASSER, address(l1ToL2MessagePasser)); L1MessageSender l1MessageSender = new L1MessageSender(address(this)); - stateManager.associateCodeContract(l1MsgSenderAddress, address(l1MessageSender)); + stateManager.associateCodeContract(L1_MESSAGE_SENDER, address(l1MessageSender)); executionContext.gasLimit = _blockGasLimit; executionContext.chainId = 108; @@ -107,21 +118,23 @@ contract ExecutionManager is ContractResolver { */ /** - * @notice Sets a new state manager to be associated with the execution manager. - * This is used when we want to swap out a new backend to be used for a different execution. + * Sets a new state manager to be associated with the execution manager. + * This is used when we want to swap out a new backend to be used for a + * different execution. + * @param _stateManagerAddress Address of the new StateManager. */ function setStateManager(address _stateManagerAddress) public { addressResolver.setAddress("StateManager", _stateManagerAddress); } /** - * @notice Increments the provided address's nonce. - * This is only used by the sequencer to correct nonces when transactions fail. - * @param addr The address of the nonce to increment. + * Increments the provided address's nonce. This is only used by the + * sequencer to correct nonces when transactions fail. + * @param _addr The address of the nonce to increment. */ - function incrementNonce(address addr) public { + function incrementNonce(address _addr) public { StateManager stateManager = resolveStateManager(); - stateManager.incrementOvmContractNonce(addr); + stateManager.incrementOvmContractNonce(_addr); } @@ -130,9 +143,9 @@ contract ExecutionManager is ContractResolver { *********************/ /** - * @notice Execute an Externally Owned Account (EOA) call. This will accept all information required - * for an OVM transaction as well as a signature from an EOA. First we will calculate the - * sender address (EOA address) and then we will perform the call. + * Execute an Externally Owned Account (EOA) call. This will accept all information required + * for an OVM transaction as well as a signature from an EOA. First we will calculate the + * sender address (EOA address) and then we will perform the call. * @param _timestamp The timestamp which should be used for this call's context. * @param _queueOrigin The parent-chain queue from which this call originated. * @param _nonce The current nonce of the EOA. @@ -181,8 +194,8 @@ contract ExecutionManager is ContractResolver { } /** - * @notice Execute an unsigned EOA transaction. Note that unsigned EOA calls are unauthenticated. - * This means that they should not be allowed for normal execution. + * Execute an unsigned EOA transaction. Note that unsigned EOA calls are unauthenticated. + * This means that they should not be allowed for normal execution. * @param _timestamp The timestamp which should be used for this call's context. * @param _queueOrigin The parent-chain queue from which this call originated. * @param _ovmEntrypoint The contract which this transaction should be executed against. @@ -217,7 +230,7 @@ contract ExecutionManager is ContractResolver { // Check if we're creating -- ovmEntrypoint == ZERO_ADDRESS if (isCreate) { - methodId = ovmCreateMethodId; + methodId = METHOD_ID_OVM_CREATE; callSize = _callBytes.length + 4; // Emit event that we are creating a contract with an EOA @@ -225,7 +238,7 @@ contract ExecutionManager is ContractResolver { address _newOvmContractAddress = contractAddressGenerator.getAddressFromCREATE(_fromAddress, _nonce); emit EOACreatedContract(_newOvmContractAddress); } else { - methodId = ovmCallMethodId; + methodId = METHOD_ID_OVM_CALL; callSize = _callBytes.length + 32 + 4; // Creates will get incremented, but calls need to be as well! @@ -281,8 +294,9 @@ contract ExecutionManager is ContractResolver { } /** - * @notice Recover the EOA of an ECDSA-signed Ethereum transaction. Note some values will be set to zero by default. - * Additionally, the `to=ZERO_ADDRESS` is reserved for contract creation transactions. + * Recover the EOA of an ECDSA-signed Ethereum transaction. Note some values will be set to + * zero by default. Additionally, the `to=ZERO_ADDRESS` is reserved for contract creation + * transactions. * @param _nonce The nonce of the transaction. * @param _to The entrypoint / recipient of the transaction. * @param _callData The calldata which will be applied to the entrypoint contract. @@ -337,7 +351,8 @@ contract ExecutionManager is ContractResolver { ***********************/ /** - * @notice CALLER opcode (msg.sender) -- this gets the caller of the currently-running contract. + * @notice CALLER opcode (msg.sender) + * Retrieves the caller of the currently-running contract. * Note: Calling this requires a CALL, which changes the CALLER, which is why we use executionContext. * * This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. @@ -363,7 +378,8 @@ contract ExecutionManager is ContractResolver { } /** - * @notice ADDRESS opcode -- Gets the address of the currently-running contract. + * @notice ADDRESS opcode + * Gets the address of the currently-running contract. * Note: Calling this requires a CALL, which changes the ADDRESS, which is why we use executionContext. * * This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. @@ -389,7 +405,8 @@ contract ExecutionManager is ContractResolver { } /** - * @notice TIMESTAMP opcode -- this gets the current timestamp. Since the L2 value for this + * @notice TIMESTAMP opcode + * This gets the current timestamp. Since the L2 value for this * will necessarily be different than L1, this needs to be overridden for the OVM. * Note: This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. * Below format of the bytes expected as input and written as output: @@ -407,7 +424,8 @@ contract ExecutionManager is ContractResolver { } /** - * @notice CHAINID opcode -- this gets the chain id. Since the L2 value for this + * @notice CHAINID opcode + * This gets the chain id. Since the L2 value for this * will necessarily be different than L1, this needs to be overridden for the OVM. * Note: This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. * Below format of the bytes expected as input and written as output: @@ -425,7 +443,8 @@ contract ExecutionManager is ContractResolver { } /** - * @notice GASLIMIT opcode -- this gets the gas limit for the current transaction. Since the L2 value for this + * @notice GASLIMIT opcode + * This gets the gas limit for the current transaction. Since the L2 value for this * may be different than L1, this needs to be overridden for the OVM. * Note: This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. * Below format of the bytes expected as input and written as output: @@ -443,7 +462,7 @@ contract ExecutionManager is ContractResolver { } /** - * @notice Gets the gas limit for fraud proofs. This value exists to make sure that fraud proofs + * Gets the gas limit for fraud proofs. This value exists to make sure that fraud proofs * don't require an excessive amount of gas that is not feasible on L1. * Note: This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. * Below format of the bytes expected as input and written as output: @@ -461,7 +480,7 @@ contract ExecutionManager is ContractResolver { } /** - * @notice Gets the queue origin in the current Execution Context. + * Gets the queue origin in the current Execution Context. * Note: This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. * Below format of the bytes expected as input and written as output: * calldata: 4 bytes: [methodID (bytes4)] @@ -478,7 +497,7 @@ contract ExecutionManager is ContractResolver { } /** - * @notice This gets whether or not this contract is currently in a static call context. + * This gets whether or not this contract is currently in a static call context. * Note: This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. * Below format of the bytes expected as input and written as output: * calldata: 4 bytes: [methodID (bytes4)] @@ -495,7 +514,7 @@ contract ExecutionManager is ContractResolver { } /** - * @notice ORIGIN opcode (tx.origin) -- this gets the origin address of the + * ORIGIN opcode (tx.origin) -- this gets the origin address of the * externally owned account that initiated this transaction. * Note: If we are in a transaction that wasn't initiated by an externally * owned account this function will revert. @@ -519,12 +538,13 @@ contract ExecutionManager is ContractResolver { } } - /**************************** - * Contract Creation Opcode * - ****************************/ + /***************************** + * Contract Creation Opcodes * + *****************************/ /** - * @notice CREATE opcode -- deploying a new ovm contract to a CREATE address. + * @notice CREATE opcode + * Deploying a new ovm contract to a CREATE address. * Note: This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. * Below format of the bytes expected as input and written as output: * calldata: variable-length bytes: @@ -584,7 +604,8 @@ contract ExecutionManager is ContractResolver { } /** - * @notice CREATE2 opcode -- deploying a new ovm contract to a CREATE2 address. + * @notice CREATE2 opcode + * Deploying a new ovm contract to a CREATE2 address. * Note: This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. * Below format of the bytes expected as input and written as output: * calldata: variable-length bytes: @@ -638,62 +659,13 @@ contract ExecutionManager is ContractResolver { } } - /********* Utils *********/ - - /** - * @notice Create a new contract at some OVM contract address. - * @param _newOvmContractAddress The desired OVM contract address for this new contract we will deploy. - * @param _ovmInitcode The initcode for our new contract - * @return True if this succeeded, false otherwise. - */ - function createNewContract(address _newOvmContractAddress, bytes memory _ovmInitcode) internal { - StateManager stateManager = resolveStateManager(); - SafetyChecker safetyChecker = resolveSafetyChecker(); - - require(safetyChecker.isBytecodeSafe(_ovmInitcode), "Contract init (creation) code is not safe"); - // Switch the context to be the new contract - (address oldMsgSender, address oldActiveContract) = switchActiveContract(_newOvmContractAddress); - - // Deploy the _ovmInitcode as a code contract -- Note the init script will run in the newly set context - address codeContractAddress = deployCodeContract(_ovmInitcode); - // Get the runtime bytecode - bytes memory codeContractBytecode = stateManager.getCodeContractBytecode(codeContractAddress); - // Safety check the runtime bytecode - require(safetyChecker.isBytecodeSafe(codeContractBytecode), "Contract runtime (deployed) bytecode is not safe"); - - // Associate the code contract with our ovm contract - stateManager.associateCodeContract(_newOvmContractAddress, codeContractAddress); - - // Get the code contract address to be emitted by a CreatedContract event - bytes32 codeContractHash = keccak256(codeContractBytecode); - - // Revert to the previous the context - restoreContractContext(oldMsgSender, oldActiveContract); - - // Emit CreatedContract event! We've created a new contract! - emit CreatedContract(_newOvmContractAddress, codeContractAddress, codeContractHash); - } - - /** - * @notice Deploys a code contract, and then registers it to the state - * @param _ovmContractInitcode The bytecode of the contract to be deployed - * @return the codeContractAddress. - */ - function deployCodeContract(bytes memory _ovmContractInitcode) internal returns(address codeContractAddress) { - // Deploy a new contract with this _ovmContractInitCode - assembly { - // Set our codeContractAddress to the address returned by our CREATE operation - codeContractAddress := create(0, add(_ovmContractInitcode, 0x20), mload(_ovmContractInitcode)) - } - return codeContractAddress; - } - /************************ * Contract CALL Opcodes * ************************/ /** - * @notice CALL opcode -- simply calls a particular code contract with the desired OVM contract context. + * @notice CALL opcode + * Simply calls a particular code contract with the desired OVM contract context. * Note: This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. * Below format of the bytes expected as input and written as output: * calldata: variable-length bytes: @@ -762,7 +734,8 @@ contract ExecutionManager is ContractResolver { } /** - * @notice STATICCALL opcode -- calls the code in question without allowing state modification. + * @notice STATICCALL opcode + * Calls the code in question without allowing state modification. * Note: This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. * Below format of the bytes expected as input and written as output: * calldata: variable-length bytes: @@ -833,7 +806,8 @@ contract ExecutionManager is ContractResolver { } /** - * @notice DELEGATECALL opcode -- calls the code in question without changing the OVM contract context. + * @notice DELEGATECALL opcode + * Calls the code in question without changing the OVM contract context. * Note: This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. * Below format of the bytes expected as input and written as output: * calldata: variable-length bytes: @@ -893,7 +867,8 @@ contract ExecutionManager is ContractResolver { ****************************/ /** - * @notice Load a value from storage. Note each contract has it's own storage. + * @notice SLOAD opcode + * Load a value from storage. Note each contract has it's own storage. * Note: This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. * Below format of the bytes expected as input and written as output: * calldata: 36 bytes: @@ -919,7 +894,8 @@ contract ExecutionManager is ContractResolver { } /** - * @notice Store a value. Note each contract has it's own storage. + * @notice SSTORE opcode + * Store a value. Note each contract has it's own storage. * Note: This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. * Below format of the bytes expected as input and written as output: * calldata: 68 bytes: @@ -951,7 +927,8 @@ contract ExecutionManager is ContractResolver { ************************/ /** - * @notice Executes the extcodesize operation for the contract address provided. + * @notice EXTCODESIZE opcode + * Executes the extcodesize operation for the contract address provided. * Note: This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. * Below format of the bytes expected as input and written as output: * calldata: 36 bytes: @@ -978,7 +955,8 @@ contract ExecutionManager is ContractResolver { } /** - * @notice Executes the extcodehash operation for the contract address provided. + * @notice EXTCODEHASH opcode + * Executes the extcodehash operation for the contract address provided. * Note: This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. * Below format of the bytes expected as input and written as output: * calldata: 36 bytes: @@ -1008,7 +986,8 @@ contract ExecutionManager is ContractResolver { } /** - * @notice Executes the extcodecopy operation for the contract address, index, and length provided. + * @notice EXTCODECOPY opcode + * Executes the extcodecopy operation for the contract address, index, and length provided. * Note: This is a raw function, so there are no listed (ABI-encoded) inputs / outputs. * Below format of the bytes expected as input and written as output: * calldata: 100 bytes: @@ -1044,16 +1023,99 @@ contract ExecutionManager is ContractResolver { } } - /********* - * Utils * - *********/ + /** + * @notice Getter for the execution context's L1MessageSender. Used by the + * L1MessageSender precompile. + * @return The L1MessageSender in our current execution context. + */ + function getL1MessageSender() public returns(address) { + require( + executionContext.ovmActiveContract == L1_MESSAGE_SENDER, + "Only the L1MessageSender precompile is allowed to call getL1MessageSender(...)!" + ); + + require( + executionContext.l1MessageSender != ZERO_ADDRESS, + "L1MessageSender not set!" + ); + + require( + executionContext.ovmMsgSender == ZERO_ADDRESS, + "L1MessageSender only accessible in entrypoint contract!" + ); + + return executionContext.l1MessageSender; + } + + function getStateManagerAddress() public view returns (address) { + StateManager stateManager = resolveStateManager(); + return address(stateManager); + } + + + /* + * Internal Functions + */ + + /** + * Create a new contract at some OVM contract address. + * @param _newOvmContractAddress The desired OVM contract address for this new contract we will deploy. + * @param _ovmInitcode The initcode for our new contract + * @return True if this succeeded, false otherwise. + */ + function createNewContract( + address _newOvmContractAddress, + bytes memory _ovmInitcode + ) internal { + StateManager stateManager = resolveStateManager(); + SafetyChecker safetyChecker = resolveSafetyChecker(); + + require(safetyChecker.isBytecodeSafe(_ovmInitcode), "Contract init (creation) code is not safe"); + // Switch the context to be the new contract + (address oldMsgSender, address oldActiveContract) = switchActiveContract(_newOvmContractAddress); + + // Deploy the _ovmInitcode as a code contract -- Note the init script will run in the newly set context + address codeContractAddress = deployCodeContract(_ovmInitcode); + // Get the runtime bytecode + bytes memory codeContractBytecode = stateManager.getCodeContractBytecode(codeContractAddress); + // Safety check the runtime bytecode + require(safetyChecker.isBytecodeSafe(codeContractBytecode), "Contract runtime (deployed) bytecode is not safe"); + + // Associate the code contract with our ovm contract + stateManager.associateCodeContract(_newOvmContractAddress, codeContractAddress); + + // Get the code contract address to be emitted by a CreatedContract event + bytes32 codeContractHash = keccak256(codeContractBytecode); + + // Revert to the previous the context + restoreContractContext(oldMsgSender, oldActiveContract); + + // Emit CreatedContract event! We've created a new contract! + emit CreatedContract(_newOvmContractAddress, codeContractAddress, codeContractHash); + } /** - * @notice Initialize a new context, setting the timestamp, queue origin, - * and gasLimit as well as zeroing out the msgSender of the - * previous context. NOTE: this zeroing may not technically be - * needed as the context should always end up as zero at the end of - * each execution. + * Deploys a code contract, and then registers it to the state + * @param _ovmContractInitcode The bytecode of the contract to be deployed + * @return the codeContractAddress. + */ + function deployCodeContract( + bytes memory _ovmContractInitcode + ) internal returns(address codeContractAddress) { + // Deploy a new contract with this _ovmContractInitCode + assembly { + // Set our codeContractAddress to the address returned by our CREATE operation + codeContractAddress := create(0, add(_ovmContractInitcode, 0x20), mload(_ovmContractInitcode)) + } + return codeContractAddress; + } + + /** + * Initialize a new context, setting the timestamp, queue origin, + * and gasLimit as well as zeroing out the msgSender of the + * previous context. NOTE: this zeroing may not technically be + * needed as the context should always end up as zero at the end of + * each execution. * @param _timestamp The timestamp which should be used for this context. * @param _queueOrigin Queue from which this transaction was sent. * @param _ovmTxOrigin The tx.origin for the currently executing @@ -1079,8 +1141,7 @@ contract ExecutionManager is ContractResolver { } /** - * @notice Change the active contract to be something new. This is used - * when a new contract is called. + * Change the active contract to be something new. This is used when a new contract is called. * @param _newActiveContract The new active contract * @return The old msgSender and activeContract. This will be used when we * restore the old active contract. @@ -1105,7 +1166,7 @@ contract ExecutionManager is ContractResolver { } /** - * @notice Restore the contract context to some old values. + * Restore the contract context to some old values. * @param _msgSender The msgSender to be restored. * @param _activeContract The activeContract to be restored. */ @@ -1118,35 +1179,6 @@ contract ExecutionManager is ContractResolver { executionContext.ovmMsgSender = _msgSender; } - /** - * @notice Getter for the execution context's L1MessageSender. Used by the - * L1MessageSender precompile. - * @return The L1MessageSender in our current execution context. - */ - function getL1MessageSender() public returns(address) { - require( - executionContext.ovmActiveContract == l1MsgSenderAddress, - "Only the L1MessageSender precompile is allowed to call getL1MessageSender(...)!" - ); - - require( - executionContext.l1MessageSender != ZERO_ADDRESS, - "L1MessageSender not set!" - ); - - require( - executionContext.ovmMsgSender == ZERO_ADDRESS, - "L1MessageSender only accessible in entrypoint contract!" - ); - - return executionContext.l1MessageSender; - } - - function getStateManagerAddress() public view returns (address) { - StateManager stateManager = resolveStateManager(); - return address(stateManager); - } - /* * Contract Resolution diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/FraudVerifier.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/FraudVerifier.sol index cb423e6522fb8..2e3a360784e45 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/FraudVerifier.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/FraudVerifier.sol @@ -1,16 +1,20 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; -/* Internal Imports */ -import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; -import { DataTypes } from "../utils/libraries/DataTypes.sol"; -import { RLPWriter } from "../utils/libraries/RLPWriter.sol"; +/* Contract Imports */ import { StateCommitmentChain } from "../chain/StateCommitmentChain.sol"; import { CanonicalTransactionChain } from "../chain/CanonicalTransactionChain.sol"; import { StateTransitioner } from "./StateTransitioner.sol"; import { IStateTransitioner } from "./interfaces/IStateTransitioner.sol"; + +/* Library Imports */ +import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; +import { DataTypes } from "../utils/libraries/DataTypes.sol"; +import { RLPWriter } from "../utils/libraries/RLPWriter.sol"; +import { TransactionParser } from "../utils/libraries/TransactionParser.sol"; + +/* Testing Imports */ import { StubStateTransitioner } from "./test-helpers/StubStateTransitioner.sol"; -import { TransactionParser } from "./TransactionParser.sol"; /** * @title FraudVerifier @@ -23,13 +27,16 @@ contract FraudVerifier is ContractResolver { */ mapping (uint256 => IStateTransitioner) public stateTransitioners; - - bool isTest; + bool private isTest; /* * Constructor */ + /** + * @param _addressResolver Address of the AddressResolver contract. + * @param _isTest Whether or not to throw into testing mode. + */ constructor( address _addressResolver, bool _isTest @@ -43,7 +50,7 @@ contract FraudVerifier is ContractResolver { */ /** - * @notice Initializes the fraud proof verification process. Creates a new + * Initializes the fraud proof verification process. Creates a new * StateTransitioner instance if none already exists for the given state * transition index. * @param _preStateTransitionIndex Index of the state transition suspected @@ -116,7 +123,7 @@ contract FraudVerifier is ContractResolver { } /** - * @notice Finalizes the fraud verification process. Checks that the state + * Finalizes the fraud verification process. Checks that the state * transitioner has executed the transition to completion and that the * resulting state root differs from the one previous published. * @param _preStateTransitionIndex Index of the state transition suspected @@ -198,7 +205,7 @@ contract FraudVerifier is ContractResolver { } /** - * @notice Utility; checks whether a StateTransitioner exists for a given + * Utility; checks whether a StateTransitioner exists for a given * state transition index. Can be used by clients to preemtively avoid * attempts to initialize the same StateTransitioner multiple times. * @param _stateTransitionIndex Index of the state transition suspected to @@ -224,7 +231,7 @@ contract FraudVerifier is ContractResolver { */ /** - * @notice Utility; verifies that a given state root is valid. Mostly just + * Utility; verifies that a given state root is valid. Mostly just * a convenience wrapper around the current verification method within * StateCommitmentChain. * @param _stateRoot State trie root to prove is included in the commitment @@ -250,7 +257,7 @@ contract FraudVerifier is ContractResolver { } /** - * @notice Utility; verifies that a given transaction is valid. Mostly just + * Utility; verifies that a given transaction is valid. Mostly just * a convenience wrapper around the current verification method within * CanonicalTransactionChain. * @param _transaction OVM transaction data to verify. diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/FullStateManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/FullStateManager.sol index f63cb4515006a..54e8b767c17c2 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/FullStateManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/FullStateManager.sol @@ -1,7 +1,7 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; -/* Internal Imports */ +/* Contract Imports */ import { StateManager } from "./StateManager.sol"; import { SafetyChecker } from "./SafetyChecker.sol"; @@ -12,15 +12,20 @@ import { SafetyChecker } from "./SafetyChecker.sol"; */ contract FullStateManager is StateManager { /* - * Contract Variables + * Contract Constants */ - address ZERO_ADDRESS = 0x0000000000000000000000000000000000000000; + address constant private ZERO_ADDRESS = 0x0000000000000000000000000000000000000000; + + + /* + * Contract Variables + */ - mapping(address=>mapping(bytes32=>bytes32)) ovmContractStorage; - mapping(address=>uint) ovmContractNonces; - mapping(address=>address) ovmAddressToCodeContractAddress; - mapping(address=>address) codeContractAddressToOvmAddress; + mapping(address => mapping(bytes32 => bytes32)) private ovmContractStorage; + mapping(address => uint) private ovmContractNonces; + mapping(address => address) private ovmAddressToCodeContractAddress; + mapping(address => address) private codeContractAddressToOvmAddress; /* @@ -33,7 +38,7 @@ contract FullStateManager is StateManager { ***********/ /** - * @notice Get storage for OVM contract at some slot. + * Get storage for OVM contract at some slot. * @param _ovmContractAddress The contract we're getting storage of. * @param _slot The slot we're querying. * @return The bytes32 value stored at the particular slot. @@ -45,6 +50,12 @@ contract FullStateManager is StateManager { return ovmContractStorage[_ovmContractAddress][_slot]; } + /** + * Get storage without touching state. + * @param _ovmContractAddress The contract we're getting storage of. + * @param _slot The slot we're querying. + * @return The bytes32 value stored at the particular slot. + */ function getStorageView( address _ovmContractAddress, bytes32 _slot @@ -53,7 +64,7 @@ contract FullStateManager is StateManager { } /** - * @notice Set storage for OVM contract at some slot. + * Set storage for OVM contract at some slot. * @param _ovmContractAddress The contract we're setting storage of. * @param _slot The slot we're setting. * @param _value The value we will set the storage to. @@ -72,7 +83,7 @@ contract FullStateManager is StateManager { **********/ /** - * @notice Get the nonce for a particular OVM contract + * Get the nonce for a particular OVM contract. * @param _ovmContractAddress The contract we're getting the nonce of. * @return The contract nonce used for contract creation. */ @@ -82,6 +93,11 @@ contract FullStateManager is StateManager { return ovmContractNonces[_ovmContractAddress]; } + /** + * Get a nonce without touching state. + * @param _ovmContractAddress The contract we're getting the nonce of. + * @return The contract nonce used for contract creation. + */ function getOvmContractNonceView( address _ovmContractAddress ) public view returns (uint) { @@ -89,7 +105,7 @@ contract FullStateManager is StateManager { } /** - * @notice Set the nonce for a particular OVM contract + * Set the nonce for a particular OVM contract. * @param _ovmContractAddress The contract we're setting the nonce of. * @param _value The new nonce. */ @@ -101,7 +117,7 @@ contract FullStateManager is StateManager { } /** - * @notice Increment the nonce for a particular OVM contract. + * Increment the nonce for a particular OVM contract. * @param _ovmContractAddress The contract we're incrementing by 1 the nonce of. */ function incrementOvmContractNonce( @@ -116,8 +132,8 @@ contract FullStateManager is StateManager { ******************/ /** - * @notice Attaches some code contract to the desired OVM contract. This allows the Execution Manager - * to later on get the code contract address to perform calls for this OVM contract. + * Attaches some code contract to the desired OVM contract. This allows the Execution Manager + * to later on get the code contract address to perform calls for this OVM contract. * @param _ovmContractAddress The address of the OVM contract we'd like to associate with some code. * @param _codeContractAddress The address of the code contract that's been deployed. */ @@ -130,7 +146,7 @@ contract FullStateManager is StateManager { } /** - * @notice Marks a contract as newly created. Unused within this implementation. + * Marks a contract as newly created. Unused within this implementation. * @param _ovmContractAddress Address to mark as newly created. */ function associateCreatedContract( @@ -140,7 +156,7 @@ contract FullStateManager is StateManager { } /** - * @notice Lookup the code contract for some OVM contract, allowing CALL opcodes to be performed. + * Lookup the code contract for some OVM contract, allowing CALL opcodes to be performed. * @param _ovmContractAddress The address of the OVM contract. * @return The associated code contract address. */ @@ -162,8 +178,8 @@ contract FullStateManager is StateManager { } /** - * @notice Get the bytecode at some contract address. NOTE: This is code taken from Solidity docs here: - * https://solidity.readthedocs.io/en/v0.5.0/assembly.html#example + * Get the bytecode at some contract address. NOTE: This is code taken from Solidity docs here: + * https://solidity.readthedocs.io/en/v0.5.0/assembly.html#example * @param _codeContractAddress The address of the code contract. * @return The bytecode at this address. */ @@ -186,7 +202,7 @@ contract FullStateManager is StateManager { } /** - * @notice Get the hash of the deployed bytecode of some code contract. + * Get the hash of the deployed bytecode of some code contract. * @param _codeContractAddress The address of the code contract. * @return The hash of the bytecode at this address. */ diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/L2ExecutionManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/L2ExecutionManager.sol index e22eeae98590b..1904ad75d62f9 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/L2ExecutionManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/L2ExecutionManager.sol @@ -1,22 +1,22 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; -/* Internal Imports */ +/* Contract Imports */ import { ExecutionManager } from "./ExecutionManager.sol"; /** * @title L2ExecutionManager - * @notice This extension of ExecutionManager that should only run in L2 because it has optimistic execution details - * that are unnecessary and inefficient to run in L1. + * @notice This extension of ExecutionManager that should only run in L2 because it has optimistic + * execution details that are unnecessary and inefficient to run in L1. */ contract L2ExecutionManager is ExecutionManager { /* * Contract Variables */ - mapping(bytes32 => bytes32) ovmHashToEvmHash; - mapping(bytes32 => bytes32) evmHashToOvmHash; - mapping(bytes32 => bytes) ovmHashToOvmTx; + mapping(bytes32 => bytes32) private ovmHashToEvmHash; + mapping(bytes32 => bytes32) private evmHashToOvmHash; + mapping(bytes32 => bytes) private ovmHashToOvmTx; /* diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol index 41f7d596d4e6d..ff2d11809044f 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol @@ -1,11 +1,13 @@ pragma experimental ABIEncoderV2; -/* Internal Imports */ -import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; +/* Contract Imports */ import { StateManager } from "./StateManager.sol"; import { StateTransitioner } from "./StateTransitioner.sol"; import { ExecutionManager } from "./ExecutionManager.sol"; +/* Library Imports */ +import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; + /** * @title PartialStateManager * @notice The PartialStateManager is used for the on-chain fraud proof checker. @@ -13,26 +15,40 @@ import { ExecutionManager } from "./ExecutionManager.sol"; * is unlike the FullStateManager which has access to every storage slot. */ contract PartialStateManager is ContractResolver { + /* + * Contract Constants + */ + address constant ZERO_ADDRESS = 0x0000000000000000000000000000000000000000; - StateTransitioner stateTransitioner; - mapping(address=>mapping(bytes32=>bytes32)) ovmContractStorage; - mapping(address=>uint) ovmContractNonces; - mapping(address=>address) ovmAddressToCodeContractAddress; + /* + * Contract Variables + */ + + StateTransitioner private stateTransitioner; + + mapping(address => mapping(bytes32 => bytes32)) private ovmContractStorage; + mapping(address => uint) private ovmContractNonces; + mapping(address => address) private ovmAddressToCodeContractAddress; bool public existsInvalidStateAccessFlag; - mapping(address=>mapping(bytes32=>bool)) public isVerifiedStorage; - mapping(address=>bool) public isVerifiedContract; - mapping(uint=>bytes32) updatedStorageSlotContract; - mapping(uint=>bytes32) updatedStorageSlotKey; - mapping(address=>mapping(bytes32=>bool)) storageSlotTouched; + mapping(address => mapping(bytes32 => bool)) public isVerifiedStorage; + mapping(address => bool) public isVerifiedContract; + mapping(uint => bytes32) private updatedStorageSlotContract; + mapping(uint => bytes32) private updatedStorageSlotKey; + mapping(address => mapping(bytes32 => bool)) private storageSlotTouched; uint public updatedStorageSlotCounter; - mapping(uint=>address) updatedContracts; - mapping(address=>bool) contractTouched; + mapping(uint => address) private updatedContracts; + mapping(address => bool) private contractTouched; uint public updatedContractsCounter; + + /* + * Modifiers + */ + modifier onlyStateTransitioner { require(msg.sender == address(stateTransitioner)); _; @@ -44,8 +60,14 @@ contract PartialStateManager is ContractResolver { _; } + + /* + * Constructor + */ + /** - * @notice Construct a new PartialStateManager + * @param _addressResolver Address of the AddressResolver contract. + * @param _stateTransitioner Address of the StateTransitioner attached to this contract. */ constructor( address _addressResolver, @@ -57,45 +79,50 @@ contract PartialStateManager is ContractResolver { stateTransitioner = StateTransitioner(_stateTransitioner); } + + /* + * Public Functions + */ + /** - * @notice Initialize a new transaction execution + * Initialize a new transaction execution */ - function initNewTransactionExecution() onlyStateTransitioner external { + function initNewTransactionExecution() public onlyStateTransitioner { existsInvalidStateAccessFlag = false; updatedStorageSlotCounter = 0; updatedContractsCounter = 0; } - function flagIfNotVerifiedStorage(address _ovmContractAddress, bytes32 _slot) private { - if (!isVerifiedStorage[_ovmContractAddress][_slot]) { - existsInvalidStateAccessFlag = true; - } - } - - function flagIfNotVerifiedContract(address _ovmContractAddress) private { - if (!isVerifiedContract[_ovmContractAddress]) { - existsInvalidStateAccessFlag = true; - } - } - /**************** * Pre-Execution * ****************/ + /** + * Inserts a verified storage slot. + * @param _ovmContractAddress Address to insert a slot for. + * @param _slot ID of the slot to insert. + * @param _value Value for the provided slot. + */ function insertVerifiedStorage( address _ovmContractAddress, bytes32 _slot, bytes32 _value - ) external onlyStateTransitioner { + ) public onlyStateTransitioner { isVerifiedStorage[_ovmContractAddress][_slot] = true; ovmContractStorage[_ovmContractAddress][_slot] = _value; } + /** + * Inserts a verified contract address. + * @param _ovmContractAddress Address of the contract on the OVM. + * @param _codeContractAddress Address of the contract on the EVM. + * @param _nonce Nonce for the provided contract. + */ function insertVerifiedContract( address _ovmContractAddress, address _codeContractAddress, uint _nonce - ) external onlyStateTransitioner { + ) public onlyStateTransitioner { isVerifiedContract[_ovmContractAddress] = true; ovmContractNonces[_ovmContractAddress] = _nonce; ovmAddressToCodeContractAddress[_ovmContractAddress] = _codeContractAddress; @@ -105,6 +132,10 @@ contract PartialStateManager is ContractResolver { * Post-Execution * *****************/ + /** + * Peeks the next storage slot to update. + * @return Information about the next storage slot to update. + */ function peekUpdatedStorageSlot() public view returns ( address storageSlotContract, bytes32 storageSlotKey, @@ -119,6 +150,10 @@ contract PartialStateManager is ContractResolver { return (storageSlotContract, storageSlotKey, storageSlotValue); } + /** + * Pops the next storage slot to update. + * @return Information about the next storage slot to update. + */ function popUpdatedStorageSlot() public onlyStateTransitioner returns ( address storageSlotContract, bytes32 storageSlotKey, @@ -138,6 +173,10 @@ contract PartialStateManager is ContractResolver { return (storageSlotContract, storageSlotKey, storageSlotValue); } + /** + * Peeks the next account state to update. + * @return Information about the next account state to update. + */ function peekUpdatedContract() public view returns ( address ovmContractAddress, uint contractNonce, @@ -156,6 +195,10 @@ contract PartialStateManager is ContractResolver { return (ovmContractAddress, contractNonce, codeHash); } + /** + * Pops the next account state to update. + * @return Information about the next account state to update. + */ function popUpdatedContract() public onlyStateTransitioner returns ( address ovmContractAddress, uint contractNonce, @@ -177,7 +220,7 @@ contract PartialStateManager is ContractResolver { **********/ /** - * @notice Get storage for OVM contract at some slot. + * Get storage for OVM contract at some slot. * @param _ovmContractAddress The contract we're getting storage of. * @param _slot The slot we're querying. * @return The bytes32 value stored at the particular slot. @@ -185,12 +228,18 @@ contract PartialStateManager is ContractResolver { function getStorage( address _ovmContractAddress, bytes32 _slot - ) onlyExecutionManager public returns (bytes32) { + ) public onlyExecutionManager returns (bytes32) { flagIfNotVerifiedStorage(_ovmContractAddress, _slot); return ovmContractStorage[_ovmContractAddress][_slot]; } + /** + * Get a storage slot without changing state. + * @param _ovmContractAddress The contract we're getting storage of. + * @param _slot The slot we're querying. + * @return The bytes32 value stored at the particular slot. + */ function getStorageView( address _ovmContractAddress, bytes32 _slot @@ -199,7 +248,7 @@ contract PartialStateManager is ContractResolver { } /** - * @notice Set storage for OVM contract at some slot. + * Set storage for OVM contract at some slot. * @param _ovmContractAddress The contract we're setting storage of. * @param _slot The slot we're setting. * @param _value The value we will set the storage to. @@ -208,7 +257,7 @@ contract PartialStateManager is ContractResolver { address _ovmContractAddress, bytes32 _slot, bytes32 _value - ) onlyExecutionManager public { + ) public onlyExecutionManager { if (!storageSlotTouched[_ovmContractAddress][_slot]) { updatedStorageSlotContract[updatedStorageSlotCounter] = bytes32(bytes20(_ovmContractAddress)); updatedStorageSlotKey[updatedStorageSlotCounter] = _slot; @@ -226,18 +275,23 @@ contract PartialStateManager is ContractResolver { *********/ /** - * @notice Get the nonce for a particular OVM contract + * Get the nonce for a particular OVM contract. * @param _ovmContractAddress The contract we're getting the nonce of. * @return The contract nonce used for contract creation. */ function getOvmContractNonce( address _ovmContractAddress - ) onlyExecutionManager public returns (uint) { + ) public onlyExecutionManager returns (uint) { flagIfNotVerifiedContract(_ovmContractAddress); return ovmContractNonces[_ovmContractAddress]; } + /** + * Get a contract nonce without touching state. + * @param _ovmContractAddress The contract we're getting the nonce of. + * @return The contract nonce used for contract creation. + */ function getOvmContractNonceView( address _ovmContractAddress ) public view returns (uint) { @@ -245,14 +299,14 @@ contract PartialStateManager is ContractResolver { } /** - * @notice Set the nonce for a particular OVM contract + * Set the nonce for a particular OVM contract * @param _ovmContractAddress The contract we're setting the nonce of. * @param _value The new nonce. */ function setOvmContractNonce( address _ovmContractAddress, uint _value - ) onlyExecutionManager public { + ) public onlyExecutionManager { // TODO: Figure out if we actually need to verify contracts here. //flagIfNotVerifiedContract(_ovmContractAddress); @@ -267,12 +321,12 @@ contract PartialStateManager is ContractResolver { } /** - * @notice Increment the nonce for a particular OVM contract. + * Increment the nonce for a particular OVM contract. * @param _ovmContractAddress The contract we're incrementing by 1 the nonce of. */ function incrementOvmContractNonce( address _ovmContractAddress - ) onlyExecutionManager public { + ) public onlyExecutionManager { flagIfNotVerifiedContract(_ovmContractAddress); if (!contractTouched[_ovmContractAddress]) { @@ -289,35 +343,34 @@ contract PartialStateManager is ContractResolver { /***************** * Contract Codes * *****************/ - // This is used when CALLing a contract /** - * @notice Attaches some code contract to the desired OVM contract. This allows the Execution Manager - * to later on get the code contract address to perform calls for this OVM contract. + * Attaches some code contract to the desired OVM contract. This allows the Execution Manager + * to later on get the code contract address to perform calls for this OVM contract. * @param _ovmContractAddress The address of the OVM contract we'd like to associate with some code. * @param _codeContractAddress The address of the code contract that's been deployed. */ function associateCodeContract( address _ovmContractAddress, address _codeContractAddress - ) onlyExecutionManager public { + ) public onlyExecutionManager { ovmAddressToCodeContractAddress[_ovmContractAddress] = _codeContractAddress; } /** - * @notice Marks an address as newly created via ovmCREATE. Sets its nonce to zero and automatically + * Marks an address as newly created via ovmCREATE. Sets its nonce to zero and automatically * marks the contract as verified. * @param _ovmContractAddress Address of the contract to mark as verified. */ function associateCreatedContract( address _ovmContractAddress - ) onlyExecutionManager public { + ) public onlyExecutionManager { isVerifiedContract[_ovmContractAddress] = true; setOvmContractNonce(_ovmContractAddress, 0); } /** - * @notice Lookup the code contract for some OVM contract, allowing CALL opcodes to be performed. + * Lookup the code contract for some OVM contract, allowing CALL opcodes to be performed. * @param _ovmContractAddress The address of the OVM contract. * @return The associated code contract address. */ @@ -328,21 +381,21 @@ contract PartialStateManager is ContractResolver { } /** - * @notice Lookup the code contract for some OVM contract, allowing ovmCALL operations to be performed. + * Lookup the code contract for some OVM contract, allowing ovmCALL operations to be performed. * @param _ovmContractAddress The address of the OVM contract. * @return The associated code contract address. */ function getCodeContractAddressFromOvmAddress( address _ovmContractAddress - ) onlyExecutionManager public returns(address) { + ) public onlyExecutionManager returns(address) { flagIfNotVerifiedContract(_ovmContractAddress); return ovmAddressToCodeContractAddress[_ovmContractAddress]; } /** - * @notice Get the bytecode at some code address. NOTE: This is code taken from Solidity docs here: - * https://solidity.readthedocs.io/en/v0.5.0/assembly.html#example + * Get the bytecode at some code address. NOTE: This is code taken from Solidity docs here: + * https://solidity.readthedocs.io/en/v0.5.0/assembly.html#example * @param _codeContractAddress The address of the code contract. * @return The bytecode at this address. */ @@ -369,7 +422,7 @@ contract PartialStateManager is ContractResolver { } /** - * @notice Get the hash of the deployed bytecode of some code contract. + * Get the hash of the deployed bytecode of some code contract. * @param _codeContractAddress The address of the code contract. * @return The hash of the bytecode at this address. */ @@ -387,6 +440,32 @@ contract PartialStateManager is ContractResolver { } + /* + * Private Functions + */ + + /** + * Flags a storage slot if not verified. + * @param _ovmContractAddress OVM contract address to flag a slot for. + * @param _slot Slot ID to flag. + */ + function flagIfNotVerifiedStorage(address _ovmContractAddress, bytes32 _slot) private { + if (!isVerifiedStorage[_ovmContractAddress][_slot]) { + existsInvalidStateAccessFlag = true; + } + } + + /** + * Flags a contract if not verified. + * @param _ovmContractAddress OVM contract address to flag. + */ + function flagIfNotVerifiedContract(address _ovmContractAddress) private { + if (!isVerifiedContract[_ovmContractAddress]) { + existsInvalidStateAccessFlag = true; + } + } + + /* * Contract Resolution */ diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/SafetyChecker.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/SafetyChecker.sol index 61618f81932ce..6c6d88f148f91 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/SafetyChecker.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/SafetyChecker.sol @@ -1,9 +1,12 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; -import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; +/* Contract Imports */ import { ExecutionManager } from "./ExecutionManager.sol"; +/* Library Imports */ +import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; + /** * @title SafetyChecker * @notice Safety Checker contract used to check whether or not bytecode is @@ -12,8 +15,21 @@ import { ExecutionManager } from "./ExecutionManager.sol"; * 2. All CALLs are to the Execution Manager and have no value. */ contract SafetyChecker is ContractResolver { + /* + * Contract Variables + */ + uint256 public opcodeWhitelistMask; + + /* + * Constructor + */ + + /** + * @param _addressResolver Address of the AddressResolver contract. + * @param _opcodeWhitelistMask Whitelist mask of allowed opcodes. + */ constructor( address _addressResolver, uint256 _opcodeWhitelistMask @@ -24,26 +40,13 @@ contract SafetyChecker is ContractResolver { opcodeWhitelistMask = _opcodeWhitelistMask; } - /** - * @notice Converts the 20 bytes at _start of _bytes into an address. - * @param _bytes The bytes to extract the address from. - * @param _start The start index from which to extract the address from - * (e.g. 0 if _bytes starts with the address). - * @return Bytes converted to an address. - */ - function toAddress( - bytes memory _bytes, - uint256 _start - ) internal pure returns (address addr) { - require(_bytes.length >= (_start + 20), "Addresses must be at least 20 bytes"); - assembly { - addr := mload(add(add(_bytes, 20), _start)) - } - } + /* + * Public Functions + */ /** - * @notice Returns whether or not all of the provided bytecode is safe. + * Returns whether or not all of the provided bytecode is safe. * @dev More info on creation vs. runtime bytecode: * https://medium.com/authereum/bytecode-and-init-code-and-runtime-code-oh-my-7bcd89065904. * @param _bytecode The bytecode to safety check. This can be either @@ -150,6 +153,29 @@ contract SafetyChecker is ContractResolver { } + /* + * Internal Functions + */ + + /** + * Converts the 20 bytes at _start of _bytes into an address. + * @param _bytes The bytes to extract the address from. + * @param _start The start index from which to extract the address from + * (e.g. 0 if _bytes starts with the address). + * @return Bytes converted to an address. + */ + function toAddress( + bytes memory _bytes, + uint256 _start + ) internal pure returns (address addr) { + require(_bytes.length >= (_start + 20), "Addresses must be at least 20 bytes"); + + assembly { + addr := mload(add(add(_bytes, 20), _start)) + } + } + + /* * Contract Resolution */ diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/StateTransitioner.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/StateTransitioner.sol index da460b0cfde32..4d3ea46efc31d 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/StateTransitioner.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/StateTransitioner.sol @@ -1,14 +1,17 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; +/* Contract Imports */ import { FraudVerifier } from "./FraudVerifier.sol"; import { PartialStateManager } from "./PartialStateManager.sol"; import { ExecutionManager } from "./ExecutionManager.sol"; import { IStateTransitioner } from "./interfaces/IStateTransitioner.sol"; + +/* Library Imports */ import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; import { DataTypes } from "../utils/libraries/DataTypes.sol"; import { EthMerkleTrie } from "../utils/libraries/EthMerkleTrie.sol"; -import { TransactionParser } from "./TransactionParser.sol"; +import { TransactionParser } from "../utils/libraries/TransactionParser.sol"; /** * @title StateTransitioner @@ -30,8 +33,8 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { * Contract Constants */ - bytes32 constant BYTES32_NULL = bytes32(''); - uint256 constant UINT256_NULL = uint256(0); + bytes32 constant private BYTES32_NULL = bytes32(''); + uint256 constant private UINT256_NULL = uint256(0); /* @@ -65,6 +68,12 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { * Constructor */ + /** + * @param _addressResolver Address of the AddressResolver contract. + * @param _transitionIndex Index of the state transition to execute. + * @param _preStateRoot Root of the state before the transition. + * @param _ovmTransactionHash Hash of the transaction being executed. + */ constructor( address _addressResolver, uint _transitionIndex, @@ -94,7 +103,7 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { *****************************/ /** - * @notice Allows a user to prove the state for a given contract. Currently + * Allows a user to prove the state for a given contract. Currently * only requires that the user prove the nonce. Only callable before the * transaction suspected to be fraudulent has been executed. * @param _ovmContractAddress Address of the contract on the OVM. @@ -144,7 +153,7 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { } /** - * @notice Allows a user to prove the value of a given storage slot for + * Allows a user to prove the value of a given storage slot for * some contract. Only callable before the transaction suspected to be * fraudulent has been executed. * @param _ovmContractAddress Address of the contract on the OVM. @@ -192,7 +201,7 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { *************************/ /** - * @notice Executes the transaction suspected to be fraudulent via the + * Executes the transaction suspected to be fraudulent via the * ExecutionManager. Will revert if the transaction attempts to access * state that has not been proven during the pre-execution phase. */ @@ -233,7 +242,7 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { ******************************/ /** - * @notice Updates the root of the state trie by making a modification to + * Updates the root of the state trie by making a modification to * a contract's storage slot. Contract storage to be modified depends on a * stack of slots modified during execution. * @param _stateTrieWitness Merkle trie inclusion proof for the contract @@ -263,7 +272,7 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { } /** - * @notice Updates the root of the state trie by making a modification to + * Updates the root of the state trie by making a modification to * a contract's state. Contract to be modified depends on a stack of * contract states modified during execution. * @param _stateTrieWitness Merkle trie inclusion proof for the contract @@ -299,7 +308,7 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { } /** - * @notice Finalizes the state transition process once all state trie or + * Finalizes the state transition process once all state trie or * storage trie modifications have been reflected in the state root. */ function completeTransition() @@ -319,7 +328,7 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { } /** - * @notice Utility; checks whether the process is complete. + * Utility; checks whether the process is complete. * @return `true` if the process is complete, `false` otherwise. */ function isComplete() public view returns (bool) { diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/interfaces/IStateTransitioner.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/interfaces/IStateTransitioner.sol index d7c5508c19530..33826f7b62741 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/interfaces/IStateTransitioner.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/interfaces/IStateTransitioner.sol @@ -1,6 +1,7 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; +/* Library Imports */ import { DataTypes } from "../../utils/libraries/DataTypes.sol"; /** diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/precompiles/L1MessageSender.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/precompiles/L1MessageSender.sol index 4dd919bb8dd52..d5430299b9a65 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/precompiles/L1MessageSender.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/precompiles/L1MessageSender.sol @@ -1,17 +1,34 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; +/* Contract Imports */ import { ExecutionManager } from "../ExecutionManager.sol"; +/** + * @title L1MessageSender + */ contract L1MessageSender { - ExecutionManager executionManager; + /* + * Contract Variables + */ - constructor( - address _executionManagerAddress - ) public { + ExecutionManager private executionManager; + + + /* + * Constructor + */ + + /** + * @param _executionManagerAddress Address of the ExecutionManager contract. + */ + constructor(address _executionManagerAddress) public { executionManager = ExecutionManager(_executionManagerAddress); } + /** + * @return L1 message sender address (msg.sender). + */ function getL1MessageSender() public returns (address) { return executionManager.getL1MessageSender(); } diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/precompiles/L2ToL1MessagePasser.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/precompiles/L2ToL1MessagePasser.sol index fc8525e142e54..1374332ef7f6d 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/precompiles/L2ToL1MessagePasser.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/precompiles/L2ToL1MessagePasser.sol @@ -1,6 +1,9 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; +/** + * @title L2ToL1MessagePasser + */ contract L2ToL1MessagePasser { /* * Events @@ -17,14 +20,17 @@ contract L2ToL1MessagePasser { * Contract Variables */ - uint nonce; - address executionManagerAddress; + uint private nonce; + address private executionManagerAddress; /* * Constructor */ + /** + * @param _executionManagerAddress Address of the ExecutionManager contract. + */ constructor(address _executionManagerAddress) public { executionManagerAddress = _executionManagerAddress; } @@ -34,6 +40,10 @@ contract L2ToL1MessagePasser { * Public Functions */ + /** + * Passes a message to L1. + * @param _messageData Message to pass to L1. + */ function passMessageToL1(bytes memory _messageData) public { // For now, to be trustfully relayed by sequencer to L1, so just emit // an event for the sequencer to pick up. @@ -50,6 +60,10 @@ contract L2ToL1MessagePasser { * Internal Functions */ + /** + * Retrieves the OVM message caller. + * @return Address of the message caller. + */ function getCALLER() internal returns (address) { bytes32 methodId = keccak256("ovmCALLER()"); address addr = executionManagerAddress; diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubExecutionManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubExecutionManager.sol index 3a10a382a3ebd..aeb21fe66ce48 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubExecutionManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubExecutionManager.sol @@ -2,11 +2,8 @@ pragma solidity ^0.5.0; import { PartialStateManager } from "../PartialStateManager.sol"; -/** - * @title StubExecutionManager - */ contract StubExecutionManager { - PartialStateManager stateManager; + PartialStateManager private stateManager; function setStateManager(address _stateManagerAddress) external { stateManager = PartialStateManager(_stateManagerAddress); diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPReader.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPReader.sol index cdc078122e005..eab4dcd0da647 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPReader.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPReader.sol @@ -1,25 +1,38 @@ -/* -* @author Hamdi Allam hamdi.allam97@gmail.com -* Please reach out with any questions or concerns -*/ pragma solidity ^0.5.0; +/** + * @title RLPReader + * @author Hamdi Allam hamdi.allam97@gmail.com + */ library RLPReader { - uint8 constant STRING_SHORT_START = 0x80; - uint8 constant STRING_LONG_START = 0xb8; - uint8 constant LIST_SHORT_START = 0xc0; - uint8 constant LIST_LONG_START = 0xf8; + /* + * Contract Constants + */ + + uint8 constant private STRING_SHORT_START = 0x80; + uint8 constant private STRING_LONG_START = 0xb8; + uint8 constant private LIST_SHORT_START = 0xc0; + uint8 constant private LIST_LONG_START = 0xf8; + uint8 constant private WORD_SIZE = 32; - uint8 constant WORD_SIZE = 32; + + /* + * Structs + */ struct RLPItem { uint len; uint memPtr; } + /* - * @param item RLP encoded bytes - */ + * Internal Functions + */ + + /** + * @param item RLP encoded bytes + */ function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { uint memPtr; assembly { @@ -29,23 +42,23 @@ library RLPReader { return RLPItem(item.length, memPtr); } - /* - * @param item RLP encoded bytes - */ + /** + * @param item RLP encoded bytes + */ function rlpLen(RLPItem memory item) internal pure returns (uint) { return item.len; } - /* - * @param item RLP encoded bytes - */ + /** + * @param item RLP encoded bytes + */ function payloadLen(RLPItem memory item) internal pure returns (uint) { return item.len - _payloadOffset(item.memPtr); } - /* - * @param item RLP encoded list in bytes - */ + /** + * @param item RLP encoded list in bytes + */ function toList(RLPItem memory item) internal pure returns (RLPItem[] memory result) { require(isList(item)); diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPWriter.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPWriter.sol index 5660872814fba..f78eb90812679 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPWriter.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPWriter.sol @@ -9,11 +9,11 @@ pragma experimental ABIEncoderV2; */ library RLPWriter { /* - * Public functions + * Internal Functions */ /** - * @dev RLP encodes a byte string. + * RLP encodes a byte string. * @param self The byte string to encode. * @return The RLP encoded string in bytes. */ @@ -28,7 +28,7 @@ library RLPWriter { } /** - * @dev RLP encodes a list of RLP encoded byte byte strings. + * RLP encodes a list of RLP encoded byte byte strings. * @param self The list of RLP encoded byte strings. * @return The RLP encoded list of items in bytes. */ @@ -38,7 +38,7 @@ library RLPWriter { } /** - * @dev RLP encodes a string. + * RLP encodes a string. * @param self The string to encode. * @return The RLP encoded string in bytes. */ @@ -47,7 +47,7 @@ library RLPWriter { } /** - * @dev RLP encodes an address. + * RLP encodes an address. * @param self The address to encode. * @return The RLP encoded address in bytes. */ @@ -63,7 +63,7 @@ library RLPWriter { } /** - * @dev RLP encodes a uint. + * RLP encodes a uint. * @param self The uint to encode. * @return The RLP encoded uint in bytes. */ @@ -72,7 +72,7 @@ library RLPWriter { } /** - * @dev RLP encodes an int. + * RLP encodes an int. * @param self The int to encode. * @return The RLP encoded int in bytes. */ @@ -81,7 +81,7 @@ library RLPWriter { } /** - * @dev RLP encodes a bool. + * RLP encodes a bool. * @param self The bool to encode. * @return The RLP encoded bool in bytes. */ @@ -97,7 +97,7 @@ library RLPWriter { */ /** - * @dev Encode the first byte, followed by the `len` in binary form if `length` is more than 55. + * Encode the first byte, followed by the `len` in binary form if `length` is more than 55. * @param len The length of the string or the payload. * @param offset 128 if item is string, 192 if item is list. * @return RLP encoded bytes. @@ -125,7 +125,7 @@ library RLPWriter { } /** - * @dev Encode integer in big endian binary form with no leading zeroes. + * Encode integer in big endian binary form with no leading zeroes. * @notice TODO: This should be optimized with assembly to save gas costs. * @param _x The integer to encode. * @return RLP encoded bytes. @@ -149,7 +149,7 @@ library RLPWriter { } /** - * @dev Copies a piece of memory to another location. + * Copies a piece of memory to another location. * @notice From: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol. * @param _dest Destination location. * @param _src Source location. @@ -177,7 +177,7 @@ library RLPWriter { } /** - * @dev Flattens a list of byte strings into one byte string. + * Flattens a list of byte strings into one byte string. * @notice From: https://github.com/sammayo/solidity-rlp-encoder/blob/master/RLPEncode.sol. * @param _list List of byte strings to flatten. * @return The flattened byte string. @@ -211,7 +211,7 @@ library RLPWriter { } /** - * @dev Concatenates two bytes. + * Concatenates two bytes. * @notice From: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol. * @param _preBytes First byte string. * @param _postBytes Second byte string. diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RollupMerkleUtils.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RollupMerkleUtils.sol index 91b2470aa5e8e..83a2d8fb0bce4 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RollupMerkleUtils.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RollupMerkleUtils.sol @@ -1,8 +1,8 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; -/* - * Merkle Tree Utilities for Rollup +/** + * @title RollupMerkleUtils */ contract RollupMerkleUtils { /* @@ -29,8 +29,8 @@ contract RollupMerkleUtils { */ /** - * @notice Initialize a new SparseMerkleUtils contract, computing the - * default hashes for the sparse merkle tree (SMT). + * Initialize a new SparseMerkleUtils contract, computing the + * default hashes for the sparse merkle tree (SMT). */ constructor() public { setDefaultHashes(); @@ -42,7 +42,7 @@ contract RollupMerkleUtils { */ /** - * @notice Get the sparse merkle root computed from some set of data blocks. + * Get the sparse merkle root computed from some set of data blocks. * @param _dataBlocks The data being used to generate the tree. * @return the sparse merkle tree root */ @@ -94,7 +94,7 @@ contract RollupMerkleUtils { } /** - * @notice Calculate root from an inclusion proof. + * Calculate root from an inclusion proof. * @param _dataBlock The data block we're calculating root for. * @param _path The path from the leaf to the root. * @param _siblings The sibling nodes along the way. @@ -124,7 +124,7 @@ contract RollupMerkleUtils { } /** - * @notice Verify an inclusion proof. + * Verify an inclusion proof. * @param _root The root of the tree we are verifying inclusion for. * @param _dataBlock The data block we're verifying inclusion for. * @param _path The path from the leaf to the root. @@ -149,7 +149,7 @@ contract RollupMerkleUtils { } /** - * @notice Update the stored tree / root with a particular dataBlock at some path (no siblings needed) + * Update the stored tree / root with a particular dataBlock at some path (no siblings needed) * @param _dataBlock The data block we're storing/verifying * @param _path The path from the leaf to the root / the index of the leaf. */ @@ -169,7 +169,7 @@ contract RollupMerkleUtils { } /** - * @notice Store a particular merkle proof & verify that the root did not change. + * Store a particular merkle proof & verify that the root did not change. * @param _dataBlock The data block we're storing/verifying * @param _path The path from the leaf to the root / the index of the leaf. * @param _siblings The sibling nodes along the way. @@ -189,7 +189,7 @@ contract RollupMerkleUtils { } /** - * @notice Store a particular dataBlock & its intermediate nodes in the tree + * Store a particular dataBlock & its intermediate nodes in the tree * @param _dataBlock The data block we're storing. * @param _path The path from the leaf to the root / the index of the leaf. * @param _siblings The sibling nodes along the way. @@ -205,7 +205,7 @@ contract RollupMerkleUtils { } /** - * @notice Store a particular leaf hash & its intermediate nodes in the tree + * Store a particular leaf hash & its intermediate nodes in the tree * @param _leaf The leaf we're storing. * @param _path The path from the leaf to the root / the index of the leaf. * @param _siblings The sibling nodes along the way. @@ -239,8 +239,8 @@ contract RollupMerkleUtils { } /** - * @notice Get siblings for a leaf at a particular index of the tree. - * This is used for updates which don't include sibling nodes. + * Get siblings for a leaf at a particular index of the tree. + * This is used for updates which don't include sibling nodes. * @param _path The path from the leaf to the root / the index of the leaf. * @return The sibling nodes along the way. */ @@ -265,7 +265,7 @@ contract RollupMerkleUtils { } /** - * @notice Get our stored tree's root + * Get our stored tree's root * @return The merkle root of the tree */ function getRoot() public view returns (bytes32) { @@ -273,7 +273,7 @@ contract RollupMerkleUtils { } /** - * @notice Set the tree root and height of the stored tree + * Set the tree root and height of the stored tree * @param _root The merkle root of the tree * @param _height The height of the tree */ @@ -283,7 +283,7 @@ contract RollupMerkleUtils { } /** - * @notice Store node in the (in-storage) sparse merkle tree + * Store node in the (in-storage) sparse merkle tree * @param _parent The parent node * @param _leftChild The left child of the parent in the tree * @param _rightChild The right child of the parent in the tree @@ -312,7 +312,7 @@ contract RollupMerkleUtils { } /** - * @notice Get the children of some parent in the tree + * Get the children of some parent in the tree * @param _parent The parent node * @return (rightChild, leftChild) -- the two children of the parent */ @@ -326,8 +326,8 @@ contract RollupMerkleUtils { } /** - * @notice Get the right sibling key. Note that these keys overwrite the first bit of the hash - to signify if it is on the right side of the parent or on the left + * Get the right sibling key. Note that these keys overwrite the first bit of the hash + * to signify if it is on the right side of the parent or on the left * @param _parent The parent node * @return the key for the left sibling (0 as the first bit) */ @@ -336,8 +336,8 @@ contract RollupMerkleUtils { } /** - * @notice Get the right sibling key. Note that these keys overwrite the first bit of the hash - to signify if it is on the right side of the parent or on the left + * Get the right sibling key. Note that these keys overwrite the first bit of the hash + * to signify if it is on the right side of the parent or on the left * @param _parent The parent node * @return the key for the right sibling (1 as the first bit) */ @@ -351,7 +351,7 @@ contract RollupMerkleUtils { */ /** - * @notice Set default hashes + * Set default hashes. */ function setDefaultHashes() internal { // Set the initial default hash. @@ -363,7 +363,7 @@ contract RollupMerkleUtils { } /** - * @notice Get the parent of two children nodes in the tree + * Get the parent of two children nodes in the tree * @param _left The left child * @param _right The right child * @return The parent node diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/TransactionParser.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/TransactionParser.sol similarity index 83% rename from packages/contracts/contracts/optimistic-ethereum/ovm/TransactionParser.sol rename to packages/contracts/contracts/optimistic-ethereum/utils/libraries/TransactionParser.sol index d9628acd2c382..7ca5af1e62fa1 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/TransactionParser.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/TransactionParser.sol @@ -1,12 +1,20 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; -import { DataTypes } from "../utils/libraries/DataTypes.sol"; -import { RLPWriter } from "../utils/libraries/RLPWriter.sol"; +/* Library Imports */ +import { DataTypes } from "./DataTypes.sol"; +import { RLPWriter } from "./RLPWriter.sol"; +/** + * @title TransactionParser + */ library TransactionParser { + /* + * Internal Functions + */ + /** - * @notice Utility; computes the hash of a given transaction. + * Utility; computes the hash of a given transaction. * @param _transaction OVM transaction to hash. * @return Hash of the provided transaction. */ @@ -18,7 +26,7 @@ library TransactionParser { } /** - * @notice Utility; RLP encodes an OVMTransactionData struct. + * Utility; RLP encodes an OVMTransactionData struct. * @dev Likely to be changed (if not moved to another contract). Currently * remaining here as to avoid modifying CanonicalTransactionChain. Unclear * whether or not this is the correct transaction structure, but it should From 82b0853223b942b84e812afcb834eb41047bb0eb Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Mon, 27 Jul 2020 15:50:45 -0400 Subject: [PATCH 02/24] Updated all function signatures --- .../bridge/L1ToL2TransactionPasser.sol | 4 +- .../bridge/L2ToL1MessageReceiver.sol | 43 ++-- .../chain/CanonicalTransactionChain.sol | 76 +++++-- .../chain/SequencerBatchSubmitter.sol | 17 +- .../chain/StateCommitmentChain.sol | 65 ++++-- .../ovm/ExecutionManager.sol | 215 +++++++++++++----- .../optimistic-ethereum/ovm/FraudVerifier.sol | 44 +++- .../ovm/FullStateManager.sol | 66 ++++-- .../ovm/L2ExecutionManager.sol | 22 +- .../ovm/PartialStateManager.sol | 162 +++++++++---- .../optimistic-ethereum/ovm/SafetyChecker.sol | 18 +- .../ovm/StateTransitioner.sol | 51 ++++- .../ovm/interfaces/IStateTransitioner.sol | 9 + .../ovm/precompiles/L1MessageSender.sol | 16 +- .../ovm/precompiles/L2ToL1MessagePasser.sol | 17 +- .../ovm/test-helpers/StubExecutionManager.sol | 28 ++- .../ovm/test-helpers/StubSafetyChecker.sol | 20 +- .../test-helpers/StubStateTransitioner.sol | 47 +++- .../queue/L1ToL2TransactionQueue.sol | 50 +++- .../optimistic-ethereum/queue/RollupQueue.sol | 201 +++++++++------- .../queue/SafetyTransactionQueue.sol | 39 +++- .../utils/libraries/BytesLib.sol | 50 +++- .../libraries/ContractAddressGenerator.sol | 48 +++- .../utils/libraries/EthMerkleTrie.sol | 106 +++++++-- .../utils/libraries/MerkleTrie.sol | 209 +++++++++++++---- .../utils/libraries/RLPEncode.sol | 99 +++++++- .../utils/libraries/RLPReader.sol | 146 +++++++++--- .../utils/libraries/RLPWriter.sol | 101 ++++++-- .../utils/libraries/RollupMerkleUtils.sol | 113 +++++++-- .../utils/libraries/TransactionParser.sol | 12 +- .../utils/resolvers/AddressResolver.sol | 29 ++- .../utils/resolvers/ContractResolver.sol | 34 ++- packages/contracts/docs/contract-standards.md | 207 +++++++++++++++++ 33 files changed, 1908 insertions(+), 456 deletions(-) create mode 100644 packages/contracts/docs/contract-standards.md diff --git a/packages/contracts/contracts/optimistic-ethereum/bridge/L1ToL2TransactionPasser.sol b/packages/contracts/contracts/optimistic-ethereum/bridge/L1ToL2TransactionPasser.sol index acf5076c9d140..26037c7ce8e89 100644 --- a/packages/contracts/contracts/optimistic-ethereum/bridge/L1ToL2TransactionPasser.sol +++ b/packages/contracts/contracts/optimistic-ethereum/bridge/L1ToL2TransactionPasser.sol @@ -36,7 +36,9 @@ contract L1ToL2TransactionPasser { function passTransactionToL2( address _ovmEntrypoint, bytes memory _ovmCalldata - ) public { + ) + public + { // TODO: Actually create/enqueue a rollup block with this message. // We are simply mocking this functionality for now. diff --git a/packages/contracts/contracts/optimistic-ethereum/bridge/L2ToL1MessageReceiver.sol b/packages/contracts/contracts/optimistic-ethereum/bridge/L2ToL1MessageReceiver.sol index 3c4bc23e39baa..37c55477b8321 100644 --- a/packages/contracts/contracts/optimistic-ethereum/bridge/L2ToL1MessageReceiver.sol +++ b/packages/contracts/contracts/optimistic-ethereum/bridge/L2ToL1MessageReceiver.sol @@ -8,6 +8,16 @@ import { DataTypes } from "../utils/libraries/DataTypes.sol"; * @title L2ToL1MessageReceiver */ contract L2ToL1MessageReceiver { + /* + * Structs + */ + + struct EnqueuedL2ToL1Message { + DataTypes.L2ToL1Message message; + uint l1BlockEnqueued; + } + + /* * Events */ @@ -19,16 +29,6 @@ contract L2ToL1MessageReceiver { ); - /* - * Structs - */ - - struct EnqueuedL2ToL1Message { - DataTypes.L2ToL1Message message; - uint l1BlockEnqueued; - } - - /* * Contract Variables */ @@ -47,7 +47,12 @@ contract L2ToL1MessageReceiver { * @param _sequencer Current sequencer address. * @param _blocksUntilFinal Blocks until transactions are considered final. */ - constructor(address _sequencer, uint _blocksUntilFinal) public { + constructor( + address _sequencer, + uint _blocksUntilFinal + ) + public + { sequencer = _sequencer; blocksUntilFinal = _blocksUntilFinal; } @@ -63,7 +68,9 @@ contract L2ToL1MessageReceiver { */ function enqueueL2ToL1Message( DataTypes.L2ToL1Message memory _message - ) public { + ) + public + { require( msg.sender == sequencer, "For now, only our trusted sequencer can enqueue messages." @@ -95,7 +102,11 @@ contract L2ToL1MessageReceiver { function verifyL2ToL1Message( DataTypes.L2ToL1Message memory _message, uint _nonce - ) public view returns (bool) { + ) + public + view + returns (bool) + { // The enqueued message for the given nonce must match the _message // being verified. bytes32 givenMessageHash = getMessageHash(_message); @@ -122,7 +133,11 @@ contract L2ToL1MessageReceiver { */ function getMessageHash( DataTypes.L2ToL1Message memory _message - ) internal pure returns (bytes32) { + ) + internal + pure + returns (bytes32) + { return keccak256(abi.encode(_message)); } } \ No newline at end of file diff --git a/packages/contracts/contracts/optimistic-ethereum/chain/CanonicalTransactionChain.sol b/packages/contracts/contracts/optimistic-ethereum/chain/CanonicalTransactionChain.sol index 948ca12ae3fa6..04de0fc82df59 100644 --- a/packages/contracts/contracts/optimistic-ethereum/chain/CanonicalTransactionChain.sol +++ b/packages/contracts/contracts/optimistic-ethereum/chain/CanonicalTransactionChain.sol @@ -14,6 +14,15 @@ import { RollupMerkleUtils } from "../utils/libraries/RollupMerkleUtils.sol"; * @title CanonicalTransactionChain */ contract CanonicalTransactionChain is ContractResolver { + /* + * Events + */ + + event L1ToL2BatchAppended( bytes32 _batchHeaderHash); + event SafetyQueueBatchAppended( bytes32 _batchHeaderHash); + event SequencerBatchAppended(bytes32 _batchHeaderHash); + + /* * Contract Variables */ @@ -25,15 +34,6 @@ contract CanonicalTransactionChain is ContractResolver { uint public lastOVMTimestamp; - /* - * Events - */ - - event L1ToL2BatchAppended( bytes32 _batchHeaderHash); - event SafetyQueueBatchAppended( bytes32 _batchHeaderHash); - event SequencerBatchAppended(bytes32 _batchHeaderHash); - - /* * Constructor */ @@ -66,7 +66,11 @@ contract CanonicalTransactionChain is ContractResolver { /** * @return Total number of published transaction batches. */ - function getBatchesLength() public view returns (uint) { + function getBatchesLength() + public + view + returns (uint) + { return batches.length; } @@ -77,7 +81,11 @@ contract CanonicalTransactionChain is ContractResolver { */ function hashBatchHeader( DataTypes.TxChainBatchHeader memory _batchHeader - ) public pure returns (bytes32) { + ) + public + pure + returns (bytes32) + { return keccak256(abi.encodePacked( _batchHeader.timestamp, _batchHeader.isL1ToL2Tx, @@ -94,14 +102,20 @@ contract CanonicalTransactionChain is ContractResolver { */ function authenticateAppend( address _sender - ) public view returns (bool) { + ) + public + view + returns (bool) + { return _sender == sequencer; } /** * Attempts to append a transaction batch from pending L1 transactions. */ - function appendL1ToL2Batch() public { + function appendL1ToL2Batch() + public + { L1ToL2TransactionQueue l1ToL2Queue = resolveL1ToL2TransactionQueue(); SafetyTransactionQueue safetyQueue = resolveSafetyTransactionQueue(); @@ -119,7 +133,9 @@ contract CanonicalTransactionChain is ContractResolver { /** * Attempts to append a transaction batch from the safety queue. */ - function appendSafetyBatch() public { + function appendSafetyBatch() + public + { L1ToL2TransactionQueue l1ToL2Queue = resolveL1ToL2TransactionQueue(); SafetyTransactionQueue safetyQueue = resolveSafetyTransactionQueue(); @@ -142,7 +158,9 @@ contract CanonicalTransactionChain is ContractResolver { function appendSequencerBatch( bytes[] memory _txBatch, uint _timestamp - ) public { + ) + public + { L1ToL2TransactionQueue l1ToL2Queue = resolveL1ToL2TransactionQueue(); SafetyTransactionQueue safetyQueue = resolveSafetyTransactionQueue(); @@ -208,7 +226,11 @@ contract CanonicalTransactionChain is ContractResolver { bytes memory _element, uint _position, DataTypes.TxElementInclusionProof memory _inclusionProof - ) public view returns (bool) { + ) + public + view + returns (bool) + { // For convenience, store the batchHeader DataTypes.TxChainBatchHeader memory batchHeader = _inclusionProof.batchHeader; @@ -246,7 +268,9 @@ contract CanonicalTransactionChain is ContractResolver { function _appendQueueBatch( DataTypes.TimestampedHash memory _timestampedHash, bool _isL1ToL2Tx - ) internal { + ) + internal + { uint timestamp = _timestampedHash.timestamp; require( @@ -281,15 +305,27 @@ contract CanonicalTransactionChain is ContractResolver { * Contract Resolution */ - function resolveL1ToL2TransactionQueue() internal view returns (L1ToL2TransactionQueue) { + function resolveL1ToL2TransactionQueue() + internal + view + returns (L1ToL2TransactionQueue) + { return L1ToL2TransactionQueue(resolveContract("L1ToL2TransactionQueue")); } - function resolveSafetyTransactionQueue() internal view returns (SafetyTransactionQueue) { + function resolveSafetyTransactionQueue() + internal + view + returns (SafetyTransactionQueue) + { return SafetyTransactionQueue(resolveContract("SafetyTransactionQueue")); } - function resolveRollupMerkleUtils() internal view returns (RollupMerkleUtils) { + function resolveRollupMerkleUtils() + internal + view + returns (RollupMerkleUtils) + { return RollupMerkleUtils(resolveContract("RollupMerkleUtils")); } } diff --git a/packages/contracts/contracts/optimistic-ethereum/chain/SequencerBatchSubmitter.sol b/packages/contracts/contracts/optimistic-ethereum/chain/SequencerBatchSubmitter.sol index c18c8da6f1888..e7aa6ab9942fc 100644 --- a/packages/contracts/contracts/optimistic-ethereum/chain/SequencerBatchSubmitter.sol +++ b/packages/contracts/contracts/optimistic-ethereum/chain/SequencerBatchSubmitter.sol @@ -69,7 +69,10 @@ contract SequencerBatchSubmitter is ContractResolver { bytes[] memory _txBatch, uint _txBatchTimestamp, bytes[] memory _stateBatch - ) public onlySequencer { + ) + public + onlySequencer + { require( _stateBatch.length == _txBatch.length, "Must append the same number of state roots and transactions" @@ -87,11 +90,19 @@ contract SequencerBatchSubmitter is ContractResolver { * Contract Resolution */ - function resolveCanonicalTransactionChain() internal view returns (CanonicalTransactionChain) { + function resolveCanonicalTransactionChain() + internal + view + returns (CanonicalTransactionChain) + { return CanonicalTransactionChain(resolveContract("CanonicalTransactionChain")); } - function resolveStateCommitmentChain() internal view returns (StateCommitmentChain) { + function resolveStateCommitmentChain() + internal + view + returns (StateCommitmentChain) + { return StateCommitmentChain(resolveContract("StateCommitmentChain")); } } diff --git a/packages/contracts/contracts/optimistic-ethereum/chain/StateCommitmentChain.sol b/packages/contracts/contracts/optimistic-ethereum/chain/StateCommitmentChain.sol index eea46084b0615..0d2f3fe6057a2 100644 --- a/packages/contracts/contracts/optimistic-ethereum/chain/StateCommitmentChain.sol +++ b/packages/contracts/contracts/optimistic-ethereum/chain/StateCommitmentChain.sol @@ -15,18 +15,18 @@ import { RollupMerkleUtils } from "../utils/libraries/RollupMerkleUtils.sol"; */ contract StateCommitmentChain is ContractResolver { /* - * Contract Variables - */ + * Events + */ - uint public cumulativeNumElements; - bytes32[] public batches; + event StateBatchAppended(bytes32 _batchHeaderHash); /* - * Events - */ + * Contract Variables + */ - event StateBatchAppended(bytes32 _batchHeaderHash); + uint public cumulativeNumElements; + bytes32[] public batches; /* @@ -36,7 +36,12 @@ contract StateCommitmentChain is ContractResolver { /** * @param _addressResolver Address of the AddressResolver contract. */ - constructor(address _addressResolver) public ContractResolver(_addressResolver) {} + constructor( + address _addressResolver + ) + public + ContractResolver(_addressResolver) + {} /* @@ -46,7 +51,11 @@ contract StateCommitmentChain is ContractResolver { /** * @return Total number of published state batches. */ - function getBatchesLength() public view returns (uint) { + function getBatchesLength() + public + view + returns (uint) + { return batches.length; } @@ -57,7 +66,11 @@ contract StateCommitmentChain is ContractResolver { */ function hashBatchHeader( DataTypes.StateChainBatchHeader memory _batchHeader - ) public pure returns (bytes32) { + ) + public + pure + returns (bytes32) + { return keccak256(abi.encodePacked( _batchHeader.elementsMerkleRoot, _batchHeader.numElementsInBatch, @@ -71,7 +84,9 @@ contract StateCommitmentChain is ContractResolver { */ function appendStateBatch( bytes[] memory _stateBatch - ) public { + ) + public + { CanonicalTransactionChain canonicalTransactionChain = resolveCanonicalTransactionChain(); RollupMerkleUtils merkleUtils = resolveRollupMerkleUtils(); @@ -106,7 +121,11 @@ contract StateCommitmentChain is ContractResolver { bytes memory _element, uint _position, DataTypes.StateElementInclusionProof memory _inclusionProof - ) public view returns (bool) { + ) + public + view + returns (bool) + { DataTypes.StateChainBatchHeader memory batchHeader = _inclusionProof.batchHeader; if (_position != _inclusionProof.indexInBatch + batchHeader.cumulativePrevElements) { @@ -136,7 +155,9 @@ contract StateCommitmentChain is ContractResolver { function deleteAfterInclusive( uint _batchIndex, DataTypes.StateChainBatchHeader memory _batchHeader - ) public { + ) + public + { FraudVerifier fraudVerifier = resolveFraudVerifier(); require( @@ -164,15 +185,27 @@ contract StateCommitmentChain is ContractResolver { * Contract Resolution */ - function resolveCanonicalTransactionChain() internal view returns (CanonicalTransactionChain) { + function resolveCanonicalTransactionChain() + internal + view + returns (CanonicalTransactionChain) + { return CanonicalTransactionChain(resolveContract("CanonicalTransactionChain")); } - function resolveFraudVerifier() internal view returns (FraudVerifier) { + function resolveFraudVerifier() + internal + view + returns (FraudVerifier) + { return FraudVerifier(resolveContract("FraudVerifier")); } - function resolveRollupMerkleUtils() internal view returns (RollupMerkleUtils) { + function resolveRollupMerkleUtils() + internal + view + returns (RollupMerkleUtils) + { return RollupMerkleUtils(resolveContract("RollupMerkleUtils")); } } diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/ExecutionManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/ExecutionManager.sol index c87bed0ee3555..6612b338efcda 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/ExecutionManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/ExecutionManager.sol @@ -23,28 +23,6 @@ import { StubSafetyChecker } from "./test-helpers/StubSafetyChecker.sol"; * backend. Only state / contracts from that backend will be accessed. */ contract ExecutionManager is ContractResolver { - /* - * Contract Constants - */ - - address constant private ZERO_ADDRESS = 0x0000000000000000000000000000000000000000; - - // Bitwise right shift 28 * 8 bits so the 4 method ID bytes are in the right-most bytes - bytes32 constant private METHOD_ID_OVM_CALL = keccak256("ovmCALL()") >> 224; - bytes32 constant private METHOD_ID_OVM_CREATE = keccak256("ovmCREATE()") >> 224; - - // Precompile addresses - address constant private L2_L2_OVM_MESSAGE_PASSER = 0x4200000000000000000000000000000000000000; - address constant private L1_MESSAGE_SENDER = 0x4200000000000000000000000000000000000001; - - - /* - * Contract Variables - */ - - DataTypes.ExecutionContext executionContext; - - /* * Events */ @@ -74,6 +52,28 @@ contract ExecutionManager is ContractResolver { ); + /* + * Contract Constants + */ + + address constant private ZERO_ADDRESS = 0x0000000000000000000000000000000000000000; + + // Bitwise right shift 28 * 8 bits so the 4 method ID bytes are in the right-most bytes + bytes32 constant private METHOD_ID_OVM_CALL = keccak256("ovmCALL()") >> 224; + bytes32 constant private METHOD_ID_OVM_CREATE = keccak256("ovmCREATE()") >> 224; + + // Precompile addresses + address constant private L2_L2_OVM_MESSAGE_PASSER = 0x4200000000000000000000000000000000000000; + address constant private L1_MESSAGE_SENDER = 0x4200000000000000000000000000000000000001; + + + /* + * Contract Variables + */ + + DataTypes.ExecutionContext executionContext; + + /* * Constructor */ @@ -123,7 +123,11 @@ contract ExecutionManager is ContractResolver { * different execution. * @param _stateManagerAddress Address of the new StateManager. */ - function setStateManager(address _stateManagerAddress) public { + function setStateManager( + address _stateManagerAddress + ) + public + { addressResolver.setAddress("StateManager", _stateManagerAddress); } @@ -132,7 +136,11 @@ contract ExecutionManager is ContractResolver { * sequencer to correct nonces when transactions fail. * @param _addr The address of the nonce to increment. */ - function incrementNonce(address _addr) public { + function incrementNonce( + address _addr + ) + public + { StateManager stateManager = resolveStateManager(); stateManager.incrementOvmContractNonce(_addr); } @@ -164,7 +172,9 @@ contract ExecutionManager is ContractResolver { uint8 _v, bytes32 _r, bytes32 _s - ) public { + ) + public + { StateManager stateManager = resolveStateManager(); // Get EOA address @@ -211,7 +221,9 @@ contract ExecutionManager is ContractResolver { address _fromAddress, address _l1MsgSenderAddress, bool _allowRevert - ) public { + ) + public + { StateManager stateManager = resolveStateManager(); require(_timestamp > 0, "Timestamp must be greater than 0"); @@ -311,7 +323,11 @@ contract ExecutionManager is ContractResolver { uint8 _v, bytes32 _r, bytes32 _s - ) public view returns (address) { + ) + public + view + returns (address) + { RLPEncode rlp = resolveRLPEncode(); bytes[] memory message = new bytes[](9); @@ -360,7 +376,10 @@ contract ExecutionManager is ContractResolver { * calldata: 4 bytes: [methodID (bytes4)] * returndata: 32-byte CALLER address containing the left-padded, big-endian encoding of the address. */ - function ovmCALLER() public view { + function ovmCALLER() + public + view + { // First make sure the ovmMsgSender was set require( executionContext.ovmMsgSender != ZERO_ADDRESS, @@ -387,7 +406,10 @@ contract ExecutionManager is ContractResolver { * calldata: 4 bytes: [methodID (bytes4)] * returndata: 32-byte ADDRESS containing the left-padded, big-endian encoding of the address. */ - function ovmADDRESS() public view { + function ovmADDRESS() + public + view + { // First make sure the ovmMsgSender was set require( executionContext.ovmActiveContract != ZERO_ADDRESS, @@ -413,7 +435,10 @@ contract ExecutionManager is ContractResolver { * calldata: 4 bytes: [methodID (bytes4)] * returndata: uint256 representing the current timestamp. */ - function ovmTIMESTAMP() public view { + function ovmTIMESTAMP() + public + view + { uint t = executionContext.timestamp; assembly { @@ -432,7 +457,10 @@ contract ExecutionManager is ContractResolver { * calldata: 4 bytes: [methodID (bytes4)] * returndata: uint256 representing the current timestamp. */ - function ovmCHAINID() public view { + function ovmCHAINID() + public + view + { uint chainId = 108; assembly { @@ -451,7 +479,10 @@ contract ExecutionManager is ContractResolver { * calldata: 4 bytes: [methodID (bytes4)] * returndata: uint256 representing the current gas limit. */ - function ovmGASLIMIT() public view { + function ovmGASLIMIT() + public + view + { uint g = executionContext.gasLimit; assembly { @@ -469,7 +500,10 @@ contract ExecutionManager is ContractResolver { * calldata: 4 bytes: [methodID (bytes4)] * returndata: uint256 representing the fraud proof gas limit. */ - function ovmBlockGasLimit() public view { + function ovmBlockGasLimit() + public + view + { uint g = executionContext.gasLimit; assembly { @@ -486,7 +520,10 @@ contract ExecutionManager is ContractResolver { * calldata: 4 bytes: [methodID (bytes4)] * returndata: uint256 representing the current queue origin. */ - function ovmQueueOrigin() public view { + function ovmQueueOrigin() + public + view + { uint q = executionContext.queueOrigin; assembly { @@ -503,7 +540,10 @@ contract ExecutionManager is ContractResolver { * calldata: 4 bytes: [methodID (bytes4)] * returndata: uint256 of 1 if in a static context and 0 if not. */ - function isStaticContext() public view { + function isStaticContext() + public + view + { uint staticContext = executionContext.inStaticContext ? 1 : 0; assembly { @@ -523,7 +563,10 @@ contract ExecutionManager is ContractResolver { * Below format of the bytes expected as input and written as output: * returndata: 32-byte ORIGIN address containing the left-padded, big-endian encoding of the address. */ - function ovmORIGIN() public view { + function ovmORIGIN() + public + view + { require( executionContext.ovmTxOrigin != ZERO_ADDRESS, "Error: attempting to access non-existent txOrigin." @@ -552,7 +595,9 @@ contract ExecutionManager is ContractResolver { * [ovmInitcode (bytes (variable length))] * returndata: [newOvmContractAddress (as bytes32)] -- will be all 0s if this create failed. */ - function ovmCREATE() public { + function ovmCREATE() + public + { StateManager stateManager = resolveStateManager(); if (executionContext.inStaticContext) { @@ -614,7 +659,9 @@ contract ExecutionManager is ContractResolver { * [ovmInitcode (bytes (variable length))] * returndata: [newOvmContractAddress (as bytes32)] -- will be all 0s if this create failed. */ - function ovmCREATE2() public { + function ovmCREATE2() + public + { if (executionContext.inStaticContext) { // Cannot create new contracts from a STATICCALL -- return 0 address assembly { @@ -674,7 +721,9 @@ contract ExecutionManager is ContractResolver { * [callBytes (bytes (variable length))] * returndata: [variable-length bytes returned from call] */ - function ovmCALL() public { + function ovmCALL() + public + { StateManager stateManager = resolveStateManager(); uint callSize; bytes memory _callBytes; @@ -744,7 +793,9 @@ contract ExecutionManager is ContractResolver { * [callBytes (bytes (variable length))] * returndata: [variable-length bytes returned from call] */ - function ovmSTATICCALL() public { + function ovmSTATICCALL() + public + { StateManager stateManager = resolveStateManager(); uint callSize; bytes memory _callBytes; @@ -816,7 +867,9 @@ contract ExecutionManager is ContractResolver { * [callBytes (bytes (variable length))] * returndata: [variable-length bytes returned from call] */ - function ovmDELEGATECALL() public { + function ovmDELEGATECALL() + public + { StateManager stateManager = resolveStateManager(); uint callSize; bytes memory _callBytes; @@ -876,7 +929,9 @@ contract ExecutionManager is ContractResolver { * [storageSlot (bytes32)] * returndata: [storageValue (bytes32)] */ - function ovmSLOAD() public { + function ovmSLOAD() + public + { StateManager stateManager = resolveStateManager(); bytes32 _storageSlot; assembly { @@ -904,7 +959,9 @@ contract ExecutionManager is ContractResolver { * [storageValue (bytes32)] * returndata: empty. */ - function ovmSSTORE() public { + function ovmSSTORE() + public + { StateManager stateManager = resolveStateManager(); require(!executionContext.inStaticContext, "Cannot call SSTORE from within a STATICCALL."); @@ -936,7 +993,10 @@ contract ExecutionManager is ContractResolver { * [targetOvmContractAddress (address as bytes32 (left-padded, big-endian))] * returndata: 32 bytes: the big-endian codesize int. */ - function ovmEXTCODESIZE() public view { + function ovmEXTCODESIZE() + public + view + { StateManager stateManager = resolveStateManager(); bytes32 _targetAddressBytes; assembly { @@ -964,7 +1024,10 @@ contract ExecutionManager is ContractResolver { * [targetOvmContractAddress (address as bytes32 (left-padded, big-endian))] * returndata: 32 bytes: the hash. */ - function ovmEXTCODEHASH() public view { + function ovmEXTCODEHASH() + public + view + { StateManager stateManager = resolveStateManager(); bytes32 _targetAddressBytes; assembly { @@ -997,7 +1060,10 @@ contract ExecutionManager is ContractResolver { * [length (uint (32))] * returndata: length (input param) bytes of contract at address, starting at index. */ - function ovmEXTCODECOPY() public view { + function ovmEXTCODECOPY() + public + view + { StateManager stateManager = resolveStateManager(); bytes32 _targetAddressBytes; uint _index; @@ -1028,7 +1094,10 @@ contract ExecutionManager is ContractResolver { * L1MessageSender precompile. * @return The L1MessageSender in our current execution context. */ - function getL1MessageSender() public returns(address) { + function getL1MessageSender() + public + returns(address) + { require( executionContext.ovmActiveContract == L1_MESSAGE_SENDER, "Only the L1MessageSender precompile is allowed to call getL1MessageSender(...)!" @@ -1047,7 +1116,11 @@ contract ExecutionManager is ContractResolver { return executionContext.l1MessageSender; } - function getStateManagerAddress() public view returns (address) { + function getStateManagerAddress() + public + view + returns (address) + { StateManager stateManager = resolveStateManager(); return address(stateManager); } @@ -1066,7 +1139,9 @@ contract ExecutionManager is ContractResolver { function createNewContract( address _newOvmContractAddress, bytes memory _ovmInitcode - ) internal { + ) + internal + { StateManager stateManager = resolveStateManager(); SafetyChecker safetyChecker = resolveSafetyChecker(); @@ -1101,7 +1176,10 @@ contract ExecutionManager is ContractResolver { */ function deployCodeContract( bytes memory _ovmContractInitcode - ) internal returns(address codeContractAddress) { + ) + internal + returns(address codeContractAddress) + { // Deploy a new contract with this _ovmContractInitCode assembly { // Set our codeContractAddress to the address returned by our CREATE operation @@ -1127,7 +1205,9 @@ contract ExecutionManager is ContractResolver { uint _queueOrigin, address _ovmTxOrigin, address _l1MsgSender - ) internal { + ) + internal + { // First zero out the context for good measure (Note ZERO_ADDRESS is // reserved for the genesis contract & initial msgSender). restoreContractContext(ZERO_ADDRESS, ZERO_ADDRESS); @@ -1148,7 +1228,10 @@ contract ExecutionManager is ContractResolver { */ function switchActiveContract( address _newActiveContract - ) internal returns (address _oldMsgSender, address _oldActiveContract) { + ) + internal + returns (address _oldMsgSender, address _oldActiveContract) + { // Store references to the old context _oldActiveContract = executionContext.ovmActiveContract; _oldMsgSender = executionContext.ovmMsgSender; @@ -1173,7 +1256,9 @@ contract ExecutionManager is ContractResolver { function restoreContractContext( address _msgSender, address _activeContract - ) internal { + ) + internal + { // Revert back to the old context executionContext.ovmActiveContract = _activeContract; executionContext.ovmMsgSender = _msgSender; @@ -1184,19 +1269,35 @@ contract ExecutionManager is ContractResolver { * Contract Resolution */ - function resolveSafetyChecker() internal view returns (SafetyChecker) { + function resolveSafetyChecker() + internal + view + returns (SafetyChecker) + { return SafetyChecker(resolveContract("SafetyChecker")); } - function resolveContractAddressGenerator() internal view returns (ContractAddressGenerator) { + function resolveContractAddressGenerator() + internal + view + returns (ContractAddressGenerator) + { return ContractAddressGenerator(resolveContract("ContractAddressGenerator")); } - function resolveRLPEncode() internal view returns (RLPEncode) { + function resolveRLPEncode() + internal + view + returns (RLPEncode) + { return RLPEncode(resolveContract("RLPEncode")); } - function resolveStateManager() internal view returns (StateManager) { + function resolveStateManager() + internal + view + returns (StateManager) + { return StateManager(resolveContract("StateManager")); } } diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/FraudVerifier.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/FraudVerifier.sol index 2e3a360784e45..0ea84638a9dcb 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/FraudVerifier.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/FraudVerifier.sol @@ -29,6 +29,7 @@ contract FraudVerifier is ContractResolver { mapping (uint256 => IStateTransitioner) public stateTransitioners; bool private isTest; + /* * Constructor */ @@ -40,7 +41,10 @@ contract FraudVerifier is ContractResolver { constructor( address _addressResolver, bool _isTest - ) public ContractResolver(_addressResolver) { + ) + public + ContractResolver(_addressResolver) + { isTest = _isTest; } @@ -72,7 +76,9 @@ contract FraudVerifier is ContractResolver { DataTypes.StateElementInclusionProof memory _preStateRootProof, DataTypes.OVMTransactionData memory _transactionData, DataTypes.TxElementInclusionProof memory _transactionProof - ) public { + ) + public + { // For user convenience; no point in carrying out extra work here if a // StateTransitioner instance already exists for the given state // transition index. Return early to save the user some gas. @@ -141,7 +147,9 @@ contract FraudVerifier is ContractResolver { DataTypes.StateElementInclusionProof memory _preStateRootProof, bytes32 _postStateRoot, DataTypes.StateElementInclusionProof memory _postStateRootProof - ) public { + ) + public + { IStateTransitioner stateTransitioner = stateTransitioners[_preStateTransitionIndex]; // Fraud cannot be verified until the StateTransitioner has fully @@ -216,7 +224,11 @@ contract FraudVerifier is ContractResolver { function hasStateTransitioner( uint256 _stateTransitionIndex, bytes32 _preStateRoot - ) public view returns (bool) { + ) + public + view + returns (bool) + { IStateTransitioner stateTransitioner = stateTransitioners[_stateTransitionIndex]; return ( @@ -247,7 +259,11 @@ contract FraudVerifier is ContractResolver { bytes32 _stateRoot, uint256 _stateRootIndex, DataTypes.StateElementInclusionProof memory _stateRootProof - ) internal view returns (bool) { + ) + internal + view + returns (bool) + { StateCommitmentChain stateCommitmentChain = resolveStateCommitmentChain(); return stateCommitmentChain.verifyElement( abi.encodePacked(_stateRoot), @@ -272,7 +288,11 @@ contract FraudVerifier is ContractResolver { DataTypes.OVMTransactionData memory _transaction, uint256 _transactionIndex, DataTypes.TxElementInclusionProof memory _transactionProof - ) internal view returns (bool) { + ) + internal + view + returns (bool) + { CanonicalTransactionChain canonicalTransactionChain = resolveCanonicalTransactionChain(); return canonicalTransactionChain.verifyElement( TransactionParser.encodeTransactionData(_transaction), @@ -286,11 +306,19 @@ contract FraudVerifier is ContractResolver { * Contract Resolution */ - function resolveCanonicalTransactionChain() internal view returns (CanonicalTransactionChain) { + function resolveCanonicalTransactionChain() + internal + view + returns (CanonicalTransactionChain) + { return CanonicalTransactionChain(resolveContract("CanonicalTransactionChain")); } - function resolveStateCommitmentChain() internal view returns (StateCommitmentChain) { + function resolveStateCommitmentChain() + internal + view + returns (StateCommitmentChain) + { return StateCommitmentChain(resolveContract("StateCommitmentChain")); } } diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/FullStateManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/FullStateManager.sol index 54e8b767c17c2..99680b912b8a2 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/FullStateManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/FullStateManager.sol @@ -46,7 +46,10 @@ contract FullStateManager is StateManager { function getStorage( address _ovmContractAddress, bytes32 _slot - ) public returns (bytes32) { + ) + public + returns (bytes32) + { return ovmContractStorage[_ovmContractAddress][_slot]; } @@ -59,7 +62,11 @@ contract FullStateManager is StateManager { function getStorageView( address _ovmContractAddress, bytes32 _slot - ) public view returns (bytes32) { + ) + public + view + returns (bytes32) + { return ovmContractStorage[_ovmContractAddress][_slot]; } @@ -73,7 +80,9 @@ contract FullStateManager is StateManager { address _ovmContractAddress, bytes32 _slot, bytes32 _value - ) public { + ) + public + { ovmContractStorage[_ovmContractAddress][_slot] = _value; } @@ -89,7 +98,10 @@ contract FullStateManager is StateManager { */ function getOvmContractNonce( address _ovmContractAddress - ) public returns (uint) { + ) + public + returns (uint) + { return ovmContractNonces[_ovmContractAddress]; } @@ -100,7 +112,11 @@ contract FullStateManager is StateManager { */ function getOvmContractNonceView( address _ovmContractAddress - ) public view returns (uint) { + ) + public + view + returns (uint) + { return ovmContractNonces[_ovmContractAddress]; } @@ -112,7 +128,9 @@ contract FullStateManager is StateManager { function setOvmContractNonce( address _ovmContractAddress, uint _value - ) public { + ) + public + { ovmContractNonces[_ovmContractAddress] = _value; } @@ -122,7 +140,9 @@ contract FullStateManager is StateManager { */ function incrementOvmContractNonce( address _ovmContractAddress - ) public { + ) + public + { ovmContractNonces[_ovmContractAddress] += 1; } @@ -140,7 +160,9 @@ contract FullStateManager is StateManager { function associateCodeContract( address _ovmContractAddress, address _codeContractAddress - ) public { + ) + public + { ovmAddressToCodeContractAddress[_ovmContractAddress] = _codeContractAddress; codeContractAddressToOvmAddress[_codeContractAddress] = _ovmContractAddress; } @@ -151,7 +173,9 @@ contract FullStateManager is StateManager { */ function associateCreatedContract( address _ovmContractAddress - ) public { + ) + public + { return; } @@ -162,7 +186,11 @@ contract FullStateManager is StateManager { */ function getCodeContractAddressFromOvmAddress( address _ovmContractAddress - ) public view returns (address) { + ) + public + view + returns (address) + { return ovmAddressToCodeContractAddress[_ovmContractAddress]; } @@ -173,7 +201,11 @@ contract FullStateManager is StateManager { */ function getOvmAddressFromCodeContractAddress( address _codeContractAddress - ) public view returns (address) { + ) + public + view + returns (address) + { return codeContractAddressToOvmAddress[_codeContractAddress]; } @@ -185,7 +217,11 @@ contract FullStateManager is StateManager { */ function getCodeContractBytecode( address _codeContractAddress - ) public view returns (bytes memory codeContractBytecode) { + ) + public + view + returns (bytes memory codeContractBytecode) + { assembly { // retrieve the size of the code let size := extcodesize(_codeContractAddress) @@ -208,7 +244,11 @@ contract FullStateManager is StateManager { */ function getCodeContractHash( address _codeContractAddress - ) public view returns (bytes32 _codeContractHash) { + ) + public + view + returns (bytes32 _codeContractHash) + { // TODO: Look up cached hash values eventually to avoid having to load all of this bytecode bytes memory codeContractBytecode = getCodeContractBytecode(_codeContractAddress); _codeContractHash = keccak256(codeContractBytecode); diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/L2ExecutionManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/L2ExecutionManager.sol index 1904ad75d62f9..2915e3420788a 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/L2ExecutionManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/L2ExecutionManager.sol @@ -52,7 +52,9 @@ contract L2ExecutionManager is ExecutionManager { bytes32 ovmTransactionHash, bytes32 internalTransactionHash, bytes memory signedOvmTx - ) public { + ) + public + { evmHashToOvmHash[internalTransactionHash] = ovmTransactionHash; ovmHashToEvmHash[ovmTransactionHash] = internalTransactionHash; ovmHashToOvmTx[ovmTransactionHash] = signedOvmTx; @@ -65,7 +67,11 @@ contract L2ExecutionManager is ExecutionManager { */ function getOvmTransactionHash( bytes32 evmTransactionHash - ) public view returns (bytes32) { + ) + public + view + returns (bytes32) + { return evmHashToOvmHash[evmTransactionHash]; } @@ -76,7 +82,11 @@ contract L2ExecutionManager is ExecutionManager { */ function getInternalTransactionHash( bytes32 ovmTransactionHash - ) public view returns (bytes32) { + ) + public + view + returns (bytes32) + { return ovmHashToEvmHash[ovmTransactionHash]; } @@ -87,7 +97,11 @@ contract L2ExecutionManager is ExecutionManager { */ function getOvmTransaction( bytes32 ovmTransactionHash - ) public view returns (bytes memory) { + ) + public + view + returns (bytes memory) + { return ovmHashToOvmTx[ovmTransactionHash]; } } diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol index ff2d11809044f..87b5d8250e3d6 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol @@ -87,7 +87,10 @@ contract PartialStateManager is ContractResolver { /** * Initialize a new transaction execution */ - function initNewTransactionExecution() public onlyStateTransitioner { + function initNewTransactionExecution() + public + onlyStateTransitioner + { existsInvalidStateAccessFlag = false; updatedStorageSlotCounter = 0; updatedContractsCounter = 0; @@ -107,7 +110,10 @@ contract PartialStateManager is ContractResolver { address _ovmContractAddress, bytes32 _slot, bytes32 _value - ) public onlyStateTransitioner { + ) + public + onlyStateTransitioner + { isVerifiedStorage[_ovmContractAddress][_slot] = true; ovmContractStorage[_ovmContractAddress][_slot] = _value; } @@ -122,7 +128,10 @@ contract PartialStateManager is ContractResolver { address _ovmContractAddress, address _codeContractAddress, uint _nonce - ) public onlyStateTransitioner { + ) + public + onlyStateTransitioner + { isVerifiedContract[_ovmContractAddress] = true; ovmContractNonces[_ovmContractAddress] = _nonce; ovmAddressToCodeContractAddress[_ovmContractAddress] = _codeContractAddress; @@ -136,11 +145,15 @@ contract PartialStateManager is ContractResolver { * Peeks the next storage slot to update. * @return Information about the next storage slot to update. */ - function peekUpdatedStorageSlot() public view returns ( - address storageSlotContract, - bytes32 storageSlotKey, - bytes32 storageSlotValue - ) { + function peekUpdatedStorageSlot() + public + view + returns ( + address storageSlotContract, + bytes32 storageSlotKey, + bytes32 storageSlotValue + ) + { require(updatedStorageSlotCounter > 0, "No more elements to update."); storageSlotContract = address(bytes20(updatedStorageSlotContract[updatedStorageSlotCounter - 1])); @@ -154,11 +167,15 @@ contract PartialStateManager is ContractResolver { * Pops the next storage slot to update. * @return Information about the next storage slot to update. */ - function popUpdatedStorageSlot() public onlyStateTransitioner returns ( - address storageSlotContract, - bytes32 storageSlotKey, - bytes32 storageSlotValue - ) { + function popUpdatedStorageSlot() + public + onlyStateTransitioner + returns ( + address storageSlotContract, + bytes32 storageSlotKey, + bytes32 storageSlotValue + ) + { ( storageSlotContract, storageSlotKey, @@ -177,11 +194,15 @@ contract PartialStateManager is ContractResolver { * Peeks the next account state to update. * @return Information about the next account state to update. */ - function peekUpdatedContract() public view returns ( - address ovmContractAddress, - uint contractNonce, - bytes32 codeHash - ) { + function peekUpdatedContract() + public + view + returns ( + address ovmContractAddress, + uint contractNonce, + bytes32 codeHash + ) + { require(updatedContractsCounter > 0, "No more elements to update."); ovmContractAddress = address(bytes20(updatedContracts[updatedContractsCounter - 1])); @@ -199,11 +220,15 @@ contract PartialStateManager is ContractResolver { * Pops the next account state to update. * @return Information about the next account state to update. */ - function popUpdatedContract() public onlyStateTransitioner returns ( - address ovmContractAddress, - uint contractNonce, - bytes32 codeHash - ) { + function popUpdatedContract() + public + onlyStateTransitioner + returns ( + address ovmContractAddress, + uint contractNonce, + bytes32 codeHash + ) + { ( ovmContractAddress, contractNonce, @@ -228,7 +253,11 @@ contract PartialStateManager is ContractResolver { function getStorage( address _ovmContractAddress, bytes32 _slot - ) public onlyExecutionManager returns (bytes32) { + ) + public + onlyExecutionManager + returns (bytes32) + { flagIfNotVerifiedStorage(_ovmContractAddress, _slot); return ovmContractStorage[_ovmContractAddress][_slot]; @@ -243,7 +272,11 @@ contract PartialStateManager is ContractResolver { function getStorageView( address _ovmContractAddress, bytes32 _slot - ) public view returns (bytes32) { + ) + public + view + returns (bytes32) + { return ovmContractStorage[_ovmContractAddress][_slot]; } @@ -257,7 +290,10 @@ contract PartialStateManager is ContractResolver { address _ovmContractAddress, bytes32 _slot, bytes32 _value - ) public onlyExecutionManager { + ) + public + onlyExecutionManager + { if (!storageSlotTouched[_ovmContractAddress][_slot]) { updatedStorageSlotContract[updatedStorageSlotCounter] = bytes32(bytes20(_ovmContractAddress)); updatedStorageSlotKey[updatedStorageSlotCounter] = _slot; @@ -281,7 +317,11 @@ contract PartialStateManager is ContractResolver { */ function getOvmContractNonce( address _ovmContractAddress - ) public onlyExecutionManager returns (uint) { + ) + public + onlyExecutionManager + returns (uint) + { flagIfNotVerifiedContract(_ovmContractAddress); return ovmContractNonces[_ovmContractAddress]; @@ -294,7 +334,11 @@ contract PartialStateManager is ContractResolver { */ function getOvmContractNonceView( address _ovmContractAddress - ) public view returns (uint) { + ) + public + view + returns (uint) + { return ovmContractNonces[_ovmContractAddress]; } @@ -306,7 +350,10 @@ contract PartialStateManager is ContractResolver { function setOvmContractNonce( address _ovmContractAddress, uint _value - ) public onlyExecutionManager { + ) + public + onlyExecutionManager + { // TODO: Figure out if we actually need to verify contracts here. //flagIfNotVerifiedContract(_ovmContractAddress); @@ -326,7 +373,10 @@ contract PartialStateManager is ContractResolver { */ function incrementOvmContractNonce( address _ovmContractAddress - ) public onlyExecutionManager { + ) + public + onlyExecutionManager + { flagIfNotVerifiedContract(_ovmContractAddress); if (!contractTouched[_ovmContractAddress]) { @@ -353,7 +403,10 @@ contract PartialStateManager is ContractResolver { function associateCodeContract( address _ovmContractAddress, address _codeContractAddress - ) public onlyExecutionManager { + ) + public + onlyExecutionManager + { ovmAddressToCodeContractAddress[_ovmContractAddress] = _codeContractAddress; } @@ -364,7 +417,10 @@ contract PartialStateManager is ContractResolver { */ function associateCreatedContract( address _ovmContractAddress - ) public onlyExecutionManager { + ) + public + onlyExecutionManager + { isVerifiedContract[_ovmContractAddress] = true; setOvmContractNonce(_ovmContractAddress, 0); } @@ -376,7 +432,11 @@ contract PartialStateManager is ContractResolver { */ function getCodeContractAddressView( address _ovmContractAddress - ) public view returns (address) { + ) + public + view + returns (address) + { return ovmAddressToCodeContractAddress[_ovmContractAddress]; } @@ -387,7 +447,11 @@ contract PartialStateManager is ContractResolver { */ function getCodeContractAddressFromOvmAddress( address _ovmContractAddress - ) public onlyExecutionManager returns(address) { + ) + public + onlyExecutionManager + returns(address) + { flagIfNotVerifiedContract(_ovmContractAddress); return ovmAddressToCodeContractAddress[_ovmContractAddress]; @@ -401,7 +465,11 @@ contract PartialStateManager is ContractResolver { */ function getCodeContractBytecode( address _codeContractAddress - ) public view returns (bytes memory codeContractBytecode) { + ) + public + view + returns (bytes memory codeContractBytecode) + { // NOTE: We don't need to verify that this is an authenticated contract // because this will always be proceeded by a call to // getCodeContractAddressFromOvmAddress(address _ovmContractAddress) in the EM which does this check. @@ -428,7 +496,11 @@ contract PartialStateManager is ContractResolver { */ function getCodeContractHash( address _codeContractAddress - ) public view returns (bytes32 _codeContractHash) { + ) + public + view + returns (bytes32 _codeContractHash) + { // NOTE: We don't need to verify that this is an authenticated contract // because this will always be proceeded by a call to // getCodeContractAddressFromOvmAddress(address _ovmContractAddress) in the EM which does this check. @@ -449,7 +521,12 @@ contract PartialStateManager is ContractResolver { * @param _ovmContractAddress OVM contract address to flag a slot for. * @param _slot Slot ID to flag. */ - function flagIfNotVerifiedStorage(address _ovmContractAddress, bytes32 _slot) private { + function flagIfNotVerifiedStorage( + address _ovmContractAddress, + bytes32 _slot + ) + private + { if (!isVerifiedStorage[_ovmContractAddress][_slot]) { existsInvalidStateAccessFlag = true; } @@ -459,7 +536,11 @@ contract PartialStateManager is ContractResolver { * Flags a contract if not verified. * @param _ovmContractAddress OVM contract address to flag. */ - function flagIfNotVerifiedContract(address _ovmContractAddress) private { + function flagIfNotVerifiedContract( + address _ovmContractAddress + ) + private + { if (!isVerifiedContract[_ovmContractAddress]) { existsInvalidStateAccessFlag = true; } @@ -470,7 +551,10 @@ contract PartialStateManager is ContractResolver { * Contract Resolution */ - function resolveExecutionManager() internal view returns (ExecutionManager) { + function resolveExecutionManager() + internal + view returns (ExecutionManager) + { return ExecutionManager(resolveContract("ExecutionManager")); } } \ No newline at end of file diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/SafetyChecker.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/SafetyChecker.sol index 6c6d88f148f91..b3180c7119398 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/SafetyChecker.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/SafetyChecker.sol @@ -58,7 +58,11 @@ contract SafetyChecker is ContractResolver { */ function isBytecodeSafe( bytes memory _bytecode - ) public view returns (bool) { + ) + public + view + returns (bool) + { bool seenJUMP = false; bool insideUnreachableCode = false; uint256[] memory ops = new uint256[](_bytecode.length); @@ -167,7 +171,11 @@ contract SafetyChecker is ContractResolver { function toAddress( bytes memory _bytes, uint256 _start - ) internal pure returns (address addr) { + ) + internal + pure + returns (address addr) + { require(_bytes.length >= (_start + 20), "Addresses must be at least 20 bytes"); assembly { @@ -180,7 +188,11 @@ contract SafetyChecker is ContractResolver { * Contract Resolution */ - function resolveExecutionManager() internal view returns (ExecutionManager) { + function resolveExecutionManager() + internal + view + returns (ExecutionManager) + { return ExecutionManager(resolveContract("ExecutionManager")); } } diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/StateTransitioner.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/StateTransitioner.sol index 4d3ea46efc31d..c59a3a3d2d4d3 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/StateTransitioner.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/StateTransitioner.sol @@ -55,7 +55,9 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { * Modifiers */ - modifier onlyDuringPhase(TransitionPhases _phase) { + modifier onlyDuringPhase( + TransitionPhases _phase + ) { require( currentTransitionPhase == _phase, "Must be called during the correct phase." @@ -79,7 +81,10 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { uint _transitionIndex, bytes32 _preStateRoot, bytes32 _ovmTransactionHash - ) public ContractResolver(_addressResolver) { + ) + public + ContractResolver(_addressResolver) + { transitionIndex = _transitionIndex; preStateRoot = _preStateRoot; stateRoot = _preStateRoot; @@ -117,7 +122,10 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { address _codeContractAddress, uint256 _nonce, bytes memory _stateTrieWitness - ) public onlyDuringPhase(TransitionPhases.PreExecution) { + ) + public + onlyDuringPhase(TransitionPhases.PreExecution) + { bytes32 codeHash; assembly { codeHash := extcodehash(_codeContractAddress) @@ -170,7 +178,10 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { bytes32 _value, bytes memory _stateTrieWitness, bytes memory _storageTrieWitness - ) public onlyDuringPhase(TransitionPhases.PreExecution) { + ) + public + onlyDuringPhase(TransitionPhases.PreExecution) + { require( stateManager.isVerifiedContract(_ovmContractAddress), "Contract must be verified before proving storage!" @@ -207,7 +218,9 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { */ function applyTransaction( DataTypes.OVMTransactionData memory _transactionData - ) public { + ) + public + { require( TransactionParser.getTransactionHash(_transactionData) == ovmTransactionHash, "Provided transaction does not match the original transaction." @@ -253,7 +266,10 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { function proveUpdatedStorageSlot( bytes memory _stateTrieWitness, bytes memory _storageTrieWitness - ) public onlyDuringPhase(TransitionPhases.PostExecution) { + ) + public + onlyDuringPhase(TransitionPhases.PostExecution) + { ( address storageSlotContract, bytes32 storageSlotKey, @@ -280,7 +296,10 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { */ function proveUpdatedContract( bytes memory _stateTrieWitness - ) public onlyDuringPhase(TransitionPhases.PostExecution) { + ) + public + onlyDuringPhase(TransitionPhases.PostExecution) + { ( address ovmContractAddress, uint contractNonce, @@ -331,7 +350,11 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { * Utility; checks whether the process is complete. * @return `true` if the process is complete, `false` otherwise. */ - function isComplete() public view returns (bool) { + function isComplete() + public + view + returns (bool) + { return (currentTransitionPhase == TransitionPhases.Complete); } @@ -340,11 +363,19 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { * Contract Resolution */ - function resolveExecutionManager() internal view returns (ExecutionManager) { + function resolveExecutionManager() + internal + view + returns (ExecutionManager) + { return ExecutionManager(resolveContract("ExecutionManager")); } - function resolveEthMerkleTrie() internal view returns (EthMerkleTrie) { + function resolveEthMerkleTrie() + internal + view + returns (EthMerkleTrie) + { return EthMerkleTrie(resolveContract("EthMerkleTrie")); } } diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/interfaces/IStateTransitioner.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/interfaces/IStateTransitioner.sol index 33826f7b62741..784a2e61b3f8a 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/interfaces/IStateTransitioner.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/interfaces/IStateTransitioner.sol @@ -8,9 +8,18 @@ import { DataTypes } from "../../utils/libraries/DataTypes.sol"; * @title IStateTransitioner */ contract IStateTransitioner { + /* + * Contract Variables + */ + bytes32 public preStateRoot; bytes32 public stateRoot; + + /* + * Public Functions + */ + function proveContractInclusion( address _ovmContractAddress, address _codeContractAddress, diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/precompiles/L1MessageSender.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/precompiles/L1MessageSender.sol index d5430299b9a65..182be3ac80e05 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/precompiles/L1MessageSender.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/precompiles/L1MessageSender.sol @@ -22,14 +22,26 @@ contract L1MessageSender { /** * @param _executionManagerAddress Address of the ExecutionManager contract. */ - constructor(address _executionManagerAddress) public { + constructor( + address _executionManagerAddress + ) + public + { executionManager = ExecutionManager(_executionManagerAddress); } + + /* + * Public Functions + */ + /** * @return L1 message sender address (msg.sender). */ - function getL1MessageSender() public returns (address) { + function getL1MessageSender() + public + returns (address) + { return executionManager.getL1MessageSender(); } } \ No newline at end of file diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/precompiles/L2ToL1MessagePasser.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/precompiles/L2ToL1MessagePasser.sol index 1374332ef7f6d..e22084f83700c 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/precompiles/L2ToL1MessagePasser.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/precompiles/L2ToL1MessagePasser.sol @@ -31,7 +31,11 @@ contract L2ToL1MessagePasser { /** * @param _executionManagerAddress Address of the ExecutionManager contract. */ - constructor(address _executionManagerAddress) public { + constructor( + address _executionManagerAddress + ) + public + { executionManagerAddress = _executionManagerAddress; } @@ -44,7 +48,11 @@ contract L2ToL1MessagePasser { * Passes a message to L1. * @param _messageData Message to pass to L1. */ - function passMessageToL1(bytes memory _messageData) public { + function passMessageToL1( + bytes memory _messageData + ) + public + { // For now, to be trustfully relayed by sequencer to L1, so just emit // an event for the sequencer to pick up. @@ -64,7 +72,10 @@ contract L2ToL1MessagePasser { * Retrieves the OVM message caller. * @return Address of the message caller. */ - function getCALLER() internal returns (address) { + function getCALLER() + internal + returns (address) + { bytes32 methodId = keccak256("ovmCALLER()"); address addr = executionManagerAddress; diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubExecutionManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubExecutionManager.sol index aeb21fe66ce48..755c3c99cd61c 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubExecutionManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubExecutionManager.sol @@ -1,14 +1,36 @@ pragma solidity ^0.5.0; +/* Contract Imports */ import { PartialStateManager } from "../PartialStateManager.sol"; +/** + * @title StubExecutionManager + */ contract StubExecutionManager { + /* + * Contract Variables + */ + PartialStateManager private stateManager; - function setStateManager(address _stateManagerAddress) external { + + /* + * External Functions + */ + + function setStateManager( + address _stateManagerAddress + ) + external + { stateManager = PartialStateManager(_stateManagerAddress); } + + /* + * Public Functions + */ + function executeTransaction( uint _timestamp, uint _queueOrigin, @@ -17,7 +39,9 @@ contract StubExecutionManager { address _fromAddress, address _l1MsgSenderAddress, bool _allowRevert - ) public { + ) + public + { // Just call the state manager to store values a couple times stateManager.setStorage( 0x1111111111111111111111111111111111111111, diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubSafetyChecker.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubSafetyChecker.sol index 969c512fac2c2..cd1af645f5acd 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubSafetyChecker.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubSafetyChecker.sol @@ -7,7 +7,19 @@ import { SafetyChecker } from "../SafetyChecker.sol"; * @notice This stubbed safety checker always returns TRUE when `isBytecodeSafe(...) is called. */ contract StubSafetyChecker is SafetyChecker { - constructor() public SafetyChecker(address(0x0), 0) {} + /* + * Constructor + */ + + constructor() + public + SafetyChecker(address(0x0), 0) + {} + + + /* + * Public Functions + */ /** * @notice Returns true. @@ -15,7 +27,11 @@ contract StubSafetyChecker is SafetyChecker { */ function isBytecodeSafe( bytes memory _bytecode - ) public view returns (bool) { + ) + public + view + returns (bool) + { return true; } } diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubStateTransitioner.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubStateTransitioner.sol index 43b28e8aa76d3..e55b2cadadc96 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubStateTransitioner.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubStateTransitioner.sol @@ -39,7 +39,6 @@ contract StubStateTransitioner is IStateTransitioner, ContractResolver { bytes32 public preStateRoot; bytes32 public stateRoot; bytes32 public ovmTransactionHash; - FraudVerifier public fraudVerifier; @@ -52,7 +51,10 @@ contract StubStateTransitioner is IStateTransitioner, ContractResolver { uint256 _transitionIndex, bytes32 _preStateRoot, bytes32 _ovmTransactionHash - ) public ContractResolver(_addressResolver) { + ) + public + ContractResolver(_addressResolver) + { transitionIndex = _transitionIndex; preStateRoot = _preStateRoot; stateRoot = _preStateRoot; @@ -62,12 +64,19 @@ contract StubStateTransitioner is IStateTransitioner, ContractResolver { fraudVerifier = FraudVerifier(msg.sender); } + + /* + * Public Functions + */ + function proveContractInclusion( address _ovmContractAddress, address _codeContractAddress, uint256 _nonce, bytes memory _stateTrieWitness - ) public { + ) + public + { return; } @@ -77,38 +86,56 @@ contract StubStateTransitioner is IStateTransitioner, ContractResolver { bytes32 _value, bytes memory _stateTrieWitness, bytes memory _storageTrieWitness - ) public { + ) + public + { return; } function applyTransaction( DataTypes.OVMTransactionData memory _transactionData - ) public { + ) + public + { return; } function proveUpdatedStorageSlot( bytes memory _stateTrieWitness, bytes memory _storageTrieWitness - ) public { + ) + public + { return; } function proveUpdatedContract( bytes memory _stateTrieWitness - ) public { + ) + public + { return; } - function completeTransition() public { + function completeTransition() + public + { currentTransitionPhase = TransitionPhases.Complete; } - function isComplete() public view returns (bool) { + function isComplete() + public + view + returns (bool) + { return (currentTransitionPhase == TransitionPhases.Complete); } - function setStateRoot(bytes32 _stateRoot) public { + function setStateRoot( + bytes32 _stateRoot + ) + public + { stateRoot = _stateRoot; } } diff --git a/packages/contracts/contracts/optimistic-ethereum/queue/L1ToL2TransactionQueue.sol b/packages/contracts/contracts/optimistic-ethereum/queue/L1ToL2TransactionQueue.sol index 4134f4021b2a5..d66261946a2fe 100644 --- a/packages/contracts/contracts/optimistic-ethereum/queue/L1ToL2TransactionQueue.sol +++ b/packages/contracts/contracts/optimistic-ethereum/queue/L1ToL2TransactionQueue.sol @@ -1,14 +1,28 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; -/* Internal Imports */ -import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; +/* Contract Imports */ import { CanonicalTransactionChain } from "../chain/CanonicalTransactionChain.sol"; import { RollupQueue } from "./RollupQueue.sol"; +/* Library Imports */ +import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; + +/** + * @title L1ToL2TransactionQueue + */ contract L1ToL2TransactionQueue is ContractResolver, RollupQueue { + /* + * Contract Variables + */ + address public l1ToL2TransactionPasser; + + /* + * Constructor + */ + constructor( address _addressResolver, address _l1ToL2TransactionPasser @@ -19,15 +33,35 @@ contract L1ToL2TransactionQueue is ContractResolver, RollupQueue { l1ToL2TransactionPasser = _l1ToL2TransactionPasser; } - function authenticateEnqueue(address _sender) public view returns (bool) { + + /* + * Public Functions + */ + + function authenticateEnqueue( + address _sender + ) + public + view + returns (bool) + { return _sender == l1ToL2TransactionPasser; } - function authenticateDequeue(address _sender) public view returns (bool) { + function authenticateDequeue( + address _sender + ) + public + view + returns (bool) + { return _sender == address(resolveCanonicalTransactionChain()); } - function isCalldataTxQueue() public returns (bool) { + function isCalldataTxQueue() + public + returns (bool) + { return false; } @@ -36,7 +70,11 @@ contract L1ToL2TransactionQueue is ContractResolver, RollupQueue { * Contract Resolution */ - function resolveCanonicalTransactionChain() internal view returns (CanonicalTransactionChain) { + function resolveCanonicalTransactionChain() + internal + view + returns (CanonicalTransactionChain) + { return CanonicalTransactionChain(resolveContract("CanonicalTransactionChain")); } } diff --git a/packages/contracts/contracts/optimistic-ethereum/queue/RollupQueue.sol b/packages/contracts/contracts/optimistic-ethereum/queue/RollupQueue.sol index 758d30aec743f..6994f97ff6cea 100644 --- a/packages/contracts/contracts/optimistic-ethereum/queue/RollupQueue.sol +++ b/packages/contracts/contracts/optimistic-ethereum/queue/RollupQueue.sol @@ -1,93 +1,130 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; -/* Internal Imports */ +/* Library Imports */ import { DataTypes } from "../utils/libraries/DataTypes.sol"; +/** + * @title RollupQueue + */ contract RollupQueue { - /* - * Contract Variables - */ - - DataTypes.TimestampedHash[] public batchHeaders; - uint256 public front; - - /* - * Events - */ - event CalldataTxEnqueued(); - event L1ToL2TxEnqueued(bytes _tx); - - /* - /* - * Public Functions - */ - - function getBatchHeadersLength() public view returns (uint) { - return batchHeaders.length; - } - - function isEmpty() public view returns (bool) { - return front >= batchHeaders.length; - } - - function peek() public view returns (DataTypes.TimestampedHash memory) { - require(!isEmpty(), "Queue is empty, no element to peek at"); - return batchHeaders[front]; - } - - function peekTimestamp() public view returns (uint) { - DataTypes.TimestampedHash memory frontBatch = peek(); - return frontBatch.timestamp; - } - - function authenticateEnqueue( - address _sender - ) public view returns (bool) { - return true; - } - - function authenticateDequeue( - address _sender - ) public view returns (bool) { - return true; - } - - function isCalldataTxQueue() public returns (bool) { - return true; - } - - function enqueueTx(bytes memory _tx) public { - // Authentication. - require( - authenticateEnqueue(msg.sender), - "Message sender does not have permission to enqueue" - ); - - bytes32 txHash = keccak256(_tx); - - batchHeaders.push(DataTypes.TimestampedHash({ - timestamp: now, - txHash: txHash - })); - - if (isCalldataTxQueue()) { - emit CalldataTxEnqueued(); - } else { - emit L1ToL2TxEnqueued(_tx); + /* + * Events + */ + event CalldataTxEnqueued(); + event L1ToL2TxEnqueued(bytes _tx); + + + /* + * Contract Variables + */ + + DataTypes.TimestampedHash[] public batchHeaders; + uint256 public front; + + + /* + * Public Functions + */ + + function getBatchHeadersLength() + public + view + returns (uint) + { + return batchHeaders.length; } - } - function dequeue() public { - // Authentication. - require( - authenticateDequeue(msg.sender), - "Message sender does not have permission to dequeue" - ); + function isEmpty() + public + view + returns (bool) + { + return front >= batchHeaders.length; + } - require(front < batchHeaders.length, "Cannot dequeue from an empty queue"); + function peek() + public + view + returns (DataTypes.TimestampedHash memory) + { + require(!isEmpty(), "Queue is empty, no element to peek at"); + return batchHeaders[front]; + } - delete batchHeaders[front]; - front++; - } + function peekTimestamp() + public + view + returns (uint) + { + DataTypes.TimestampedHash memory frontBatch = peek(); + return frontBatch.timestamp; + } + + function authenticateEnqueue( + address _sender + ) + public + view + returns (bool) + { + return true; + } + + function authenticateDequeue( + address _sender + ) + public + view + returns (bool) + { + return true; + } + + function isCalldataTxQueue() + public + returns (bool) + { + return true; + } + + function enqueueTx( + bytes memory _tx + ) + public + { + // Authentication. + require( + authenticateEnqueue(msg.sender), + "Message sender does not have permission to enqueue" + ); + + bytes32 txHash = keccak256(_tx); + + batchHeaders.push(DataTypes.TimestampedHash({ + timestamp: now, + txHash: txHash + })); + + if (isCalldataTxQueue()) { + emit CalldataTxEnqueued(); + } else { + emit L1ToL2TxEnqueued(_tx); + } + } + + function dequeue() + public + { + // Authentication. + require( + authenticateDequeue(msg.sender), + "Message sender does not have permission to dequeue" + ); + + require(front < batchHeaders.length, "Cannot dequeue from an empty queue"); + + delete batchHeaders[front]; + front++; + } } diff --git a/packages/contracts/contracts/optimistic-ethereum/queue/SafetyTransactionQueue.sol b/packages/contracts/contracts/optimistic-ethereum/queue/SafetyTransactionQueue.sol index e6921e4f8c2b3..f4d2b475bafd2 100644 --- a/packages/contracts/contracts/optimistic-ethereum/queue/SafetyTransactionQueue.sol +++ b/packages/contracts/contracts/optimistic-ethereum/queue/SafetyTransactionQueue.sol @@ -1,15 +1,40 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; -/* Internal Imports */ -import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; +/* Contract Imports */ import { CanonicalTransactionChain } from "../chain/CanonicalTransactionChain.sol"; import { RollupQueue } from "./RollupQueue.sol"; +/* Library Imports */ +import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; + +/** + * @title SafetyTransactionQueue + */ contract SafetyTransactionQueue is ContractResolver, RollupQueue { - constructor(address _addressResolver) public ContractResolver(_addressResolver) {} + /* + * Constructor + */ + + constructor( + address _addressResolver + ) + public + ContractResolver(_addressResolver) + {} + + + /* + * Public Functions + */ - function authenticateDequeue(address _sender) public view returns (bool) { + function authenticateDequeue( + address _sender + ) + public + view + returns (bool) + { return _sender == address(resolveCanonicalTransactionChain()); } @@ -18,7 +43,11 @@ contract SafetyTransactionQueue is ContractResolver, RollupQueue { * Contract Resolution */ - function resolveCanonicalTransactionChain() internal view returns (CanonicalTransactionChain) { + function resolveCanonicalTransactionChain() + internal + view + returns (CanonicalTransactionChain) + { return CanonicalTransactionChain(resolveContract("CanonicalTransactionChain")); } } diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/BytesLib.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/BytesLib.sol index 0694c82eb96ad..edaf34b54cbb7 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/BytesLib.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/BytesLib.sol @@ -1,3 +1,5 @@ +pragma solidity ^0.5.0; + /* * @title Solidity Bytes Arrays Utils * @author Gonçalo Sá @@ -5,10 +7,11 @@ * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. */ -pragma solidity ^0.5.0; - - library BytesLib { + /* + * Internal Functions + */ + function concat( bytes memory _preBytes, bytes memory _postBytes @@ -167,7 +170,13 @@ library BytesLib { return slice(_bytes, _start, _bytes.length - _start); } - function toBytes32(bytes memory _bytes) internal pure returns (bytes32) { + function toBytes32( + bytes memory _bytes + ) + internal + pure + returns (bytes32) + { bytes32 ret; assembly { ret := mload(add(_bytes, 32)) @@ -175,11 +184,23 @@ library BytesLib { return ret; } - function toUint256(bytes memory _bytes) internal pure returns (uint256) { + function toUint256( + bytes memory _bytes + ) + internal + pure + returns (uint256) + { return uint256(toBytes32(_bytes)); } - function toNibbles(bytes memory _bytes) internal pure returns (bytes memory) { + 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++) { @@ -190,7 +211,13 @@ library BytesLib { return nibbles; } - function fromNibbles(bytes memory _bytes) internal pure returns (bytes memory) { + 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++) { @@ -200,7 +227,14 @@ library BytesLib { return ret; } - function equal(bytes memory _bytes, bytes memory _other) internal pure returns (bool) { + function equal( + bytes memory _bytes, + bytes memory _other + ) + internal + pure + returns (bool) + { return keccak256(_bytes) == keccak256(_other); } } \ No newline at end of file diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/ContractAddressGenerator.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/ContractAddressGenerator.sol index 5fa775fad612a..5cb36b9a320e7 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/ContractAddressGenerator.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/ContractAddressGenerator.sol @@ -11,12 +11,28 @@ import { RLPEncode } from "./RLPEncode.sol"; * Ethereum mainchain. */ contract ContractAddressGenerator { - RLPEncode rlp; + /* + * Contract Variables + */ + + RLPEncode private rlp; + - constructor() public { + /* + * Constructor + */ + + constructor() + public + { rlp = new RLPEncode(); } + + /* + * Public Functions + */ + /** * @notice Generate a contract address using CREATE. * @param _origin The address of the contract which is calling CREATE. @@ -24,7 +40,14 @@ contract ContractAddressGenerator { * each time CREATE is called). * @return Address of the contract to be created. */ - function getAddressFromCREATE(address _origin, uint _nonce) public view returns (address) { + function getAddressFromCREATE( + address _origin, + uint _nonce + ) + public + view + returns (address) + { // Create a list of RLP encoded parameters. bytes[] memory list = new bytes[](2); list[0] = rlp.encodeAddress(_origin); @@ -49,7 +72,11 @@ contract ContractAddressGenerator { address _origin, bytes32 _salt, bytes memory _ovmInitcode - ) public pure returns (address) { + ) + public + pure + returns (address) + { // Hash all of the parameters together. bytes32 hashedData = keccak256(abi.encodePacked( byte(0xff), @@ -61,6 +88,11 @@ contract ContractAddressGenerator { return getAddressFromHash(hashedData); } + + /* + * Internal Functions + */ + /** * @dev Determines an address from a 32 byte hash. Since addresses are only * 20 bytes, we need to retrieve the last 20 bytes from the original @@ -68,7 +100,13 @@ contract ContractAddressGenerator { * @param _hash Hash to convert to an address. * @return Hash converted to an address. */ - function getAddressFromHash(bytes32 _hash) internal pure returns (address) { + function getAddressFromHash( + bytes32 _hash + ) + internal + pure + returns (address) + { return address(bytes20(uint160(uint256(_hash)))); } } diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/EthMerkleTrie.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/EthMerkleTrie.sol index 97f3fe0336269..5f548b34bc690 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/EthMerkleTrie.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/EthMerkleTrie.sol @@ -1,6 +1,7 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; +/* Library Imports */ import { MerkleTrie } from "./MerkleTrie.sol"; import { RLPWriter } from "./RLPWriter.sol"; import { RLPReader } from "./RLPReader.sol"; @@ -11,8 +12,13 @@ import { DataTypes } from "./DataTypes.sol"; * @notice Convenience wrapper for ETH-related trie operations. */ contract EthMerkleTrie is MerkleTrie { - bytes32 constant BYTES32_NULL = bytes32(''); - uint256 constant UINT256_NULL = uint256(0); + /* + * Contract Constants + */ + + bytes32 constant private BYTES32_NULL = bytes32(''); + uint256 constant private UINT256_NULL = uint256(0); + /* * Public Functions @@ -37,7 +43,11 @@ contract EthMerkleTrie is MerkleTrie { bytes memory _stateTrieWitness, bytes memory _storageTrieWitness, bytes32 _stateTrieRoot - ) public pure returns (bool) { + ) + public + pure + returns (bool) + { // Retrieve the current storage root. DataTypes.AccountState memory accountState = getAccountState( _address, @@ -73,7 +83,11 @@ contract EthMerkleTrie is MerkleTrie { bytes memory _stateTrieWitness, bytes memory _storageTrieWitness, bytes32 _stateTrieRoot - ) public pure returns (bytes32) { + ) + public + pure + returns (bytes32) + { // Retreive the old storage root. DataTypes.AccountState memory accountState = getAccountState( _address, @@ -114,7 +128,11 @@ contract EthMerkleTrie is MerkleTrie { DataTypes.ProofMatrix memory _proofMatrix, bytes memory _stateTrieWitness, bytes32 _stateTrieRoot - ) public pure returns (bool) { + ) + public + pure + returns (bool) + { // Pull the current account state. DataTypes.AccountState memory accountState = getAccountState( _address, @@ -145,7 +163,11 @@ contract EthMerkleTrie is MerkleTrie { uint256 _nonce, bytes memory _stateTrieWitness, bytes32 _stateTrieRoot - ) public pure returns (bool) { + ) + public + pure + returns (bool) + { return proveAccountState( _address, DataTypes.AccountState({ @@ -179,7 +201,11 @@ contract EthMerkleTrie is MerkleTrie { uint256 _balance, bytes memory _stateTrieWitness, bytes32 _stateTrieRoot - ) public pure returns (bool) { + ) + public + pure + returns (bool) + { return proveAccountState( _address, DataTypes.AccountState({ @@ -213,7 +239,11 @@ contract EthMerkleTrie is MerkleTrie { bytes32 _storageRoot, bytes memory _stateTrieWitness, bytes32 _stateTrieRoot - ) public pure returns (bool) { + ) + public + pure + returns (bool) + { return proveAccountState( _address, DataTypes.AccountState({ @@ -247,7 +277,11 @@ contract EthMerkleTrie is MerkleTrie { bytes32 _codeHash, bytes memory _stateTrieWitness, bytes32 _stateTrieRoot - ) public pure returns (bool) { + ) + public + pure + returns (bool) + { return proveAccountState( _address, DataTypes.AccountState({ @@ -283,7 +317,11 @@ contract EthMerkleTrie is MerkleTrie { DataTypes.ProofMatrix memory _proofMatrix, bytes memory _stateTrieWitness, bytes32 _stateTrieRoot - ) public pure returns (bytes32) { + ) + public + pure + returns (bytes32) + { DataTypes.AccountState memory newAccountState = _accountState; // If the user has provided everything, don't bother pulling the @@ -344,7 +382,11 @@ contract EthMerkleTrie is MerkleTrie { uint256 _nonce, bytes memory _stateTrieWitness, bytes32 _stateTrieRoot - ) public pure returns (bytes32) { + ) + public + pure + returns (bytes32) + { return updateAccountState( _address, DataTypes.AccountState({ @@ -378,7 +420,11 @@ contract EthMerkleTrie is MerkleTrie { uint256 _balance, bytes memory _stateTrieWitness, bytes32 _stateTrieRoot - ) public pure returns (bytes32) { + ) + public + pure + returns (bytes32) + { return updateAccountState( _address, DataTypes.AccountState({ @@ -412,7 +458,11 @@ contract EthMerkleTrie is MerkleTrie { bytes32 _storageRoot, bytes memory _stateTrieWitness, bytes32 _stateTrieRoot - ) public pure returns (bytes32) { + ) + public + pure + returns (bytes32) + { return updateAccountState( _address, DataTypes.AccountState({ @@ -446,7 +496,11 @@ contract EthMerkleTrie is MerkleTrie { bytes32 _codeHash, bytes memory _stateTrieWitness, bytes32 _stateTrieRoot - ) public pure returns (bytes32) { + ) + public + pure + returns (bytes32) + { return updateAccountState( _address, DataTypes.AccountState({ @@ -478,7 +532,11 @@ contract EthMerkleTrie is MerkleTrie { */ function decodeAccountState( bytes memory _encodedAccountState - ) internal pure returns (DataTypes.AccountState memory) { + ) + internal + pure + returns (DataTypes.AccountState memory) + { RLPReader.RLPItem[] memory accountState = RLPReader.toList(RLPReader.toRlpItem(_encodedAccountState)); return DataTypes.AccountState({ @@ -496,7 +554,11 @@ contract EthMerkleTrie is MerkleTrie { */ function encodeAccountState( DataTypes.AccountState memory _accountState - ) internal pure returns (bytes memory) { + ) + internal + pure + returns (bytes memory) + { bytes[] memory raw = new bytes[](4); // Unfortunately we can't create this array outright because @@ -521,7 +583,11 @@ contract EthMerkleTrie is MerkleTrie { address _address, bytes memory _stateTrieWitness, bytes32 _stateTrieRoot - ) internal pure returns (DataTypes.AccountState memory) { + ) + internal + pure + returns (DataTypes.AccountState memory) + { DataTypes.AccountState memory DEFAULT_ACCOUNT_STATE = DataTypes.AccountState({ nonce: UINT256_NULL, balance: UINT256_NULL, @@ -555,7 +621,11 @@ contract EthMerkleTrie is MerkleTrie { address _address, bytes memory _stateTrieWitness, bytes32 _stateTrieRoot - ) internal pure returns (bytes32) { + ) + internal + pure + returns (bytes32) + { bytes memory encodedAccountState = encodeAccountState(_accountState); return update( diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/MerkleTrie.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/MerkleTrie.sol index 67bafd86c3cf2..cbdb25c19454d 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/MerkleTrie.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/MerkleTrie.sol @@ -1,11 +1,34 @@ -// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.5.0; -import './BytesLib.sol'; -import './RLPReader.sol'; -import './RLPWriter.sol'; +/* Library Imports */ +import { BytesLib } from "./BytesLib.sol"; +import { RLPReader } from "./RLPReader.sol"; +import { RLPWriter } from "./RLPWriter.sol"; +/** + * @title MerkleTrie + */ contract MerkleTrie { + /* + * Data Structures + */ + + enum NodeType { + BranchNode, + ExtensionNode, + LeafNode + } + + struct TrieNode { + bytes encoded; + 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. @@ -27,17 +50,6 @@ contract MerkleTrie { bytes1 constant RLP_NULL = bytes1(0x80); bytes constant RLP_NULL_BYTES = hex'80'; - enum NodeType { - BranchNode, - ExtensionNode, - LeafNode - } - - struct TrieNode { - bytes encoded; - RLPReader.RLPItem[] decoded; - } - /* * Public Functions @@ -60,7 +72,11 @@ contract MerkleTrie { bytes memory _value, bytes memory _proof, bytes32 _root - ) public pure returns (bool) { + ) + public + pure + returns (bool) + { return verifyProof(_key, _value, _proof, _root, true); } @@ -81,7 +97,11 @@ contract MerkleTrie { bytes memory _value, bytes memory _proof, bytes32 _root - ) public pure returns (bool) { + ) + public + pure + returns (bool) + { return verifyProof(_key, _value, _proof, _root, false); } @@ -101,7 +121,11 @@ contract MerkleTrie { bytes memory _value, bytes memory _proof, bytes32 _root - ) public pure returns (bytes32) { + ) + public + pure + returns (bytes32) + { TrieNode[] memory proof = parseProof(_proof); (uint256 pathLength, bytes memory keyRemainder, ) = walkNodePath(proof, _key, _root); @@ -121,7 +145,11 @@ contract MerkleTrie { bytes memory _key, bytes memory _proof, bytes32 _root - ) public pure returns (bool, bytes memory) { + ) + public + pure + returns (bool, bytes memory) + { TrieNode[] memory proof = parseProof(_proof); (uint256 pathLength, bytes memory keyRemainder, ) = walkNodePath(proof, _key, _root); @@ -158,7 +186,11 @@ contract MerkleTrie { bytes memory _proof, bytes32 _root, bool _inclusion - ) internal pure returns (bool) { + ) + internal + pure + returns (bool) + { TrieNode[] memory proof = parseProof(_proof); (uint256 pathLength, bytes memory keyRemainder, bool isFinalNode) = walkNodePath(proof, _key, _root); @@ -193,11 +225,15 @@ contract MerkleTrie { TrieNode[] memory _proof, bytes memory _key, bytes32 _root - ) internal pure returns ( - uint256, - bytes memory, - bool - ) { + ) + internal + pure + returns ( + uint256, + bytes memory, + bool + ) + { uint256 pathLength = 0; bytes memory key = BytesLib.toNibbles(_key); @@ -308,7 +344,11 @@ contract MerkleTrie { uint256 _pathLength, bytes memory _keyRemainder, bytes memory _value - ) internal pure returns (TrieNode[] memory) { + ) + internal + pure + returns (TrieNode[] memory) + { bytes memory keyRemainder = _keyRemainder; // Most of our logic depends on the status of the last node in the path. @@ -428,7 +468,11 @@ contract MerkleTrie { function getUpdatedTrieRoot( TrieNode[] memory _nodes, bytes memory _key - ) internal pure returns (bytes32) { + ) + internal + pure + returns (bytes32) + { bytes memory key = BytesLib.toNibbles(_key); // Some variables to keep track of during iteration. @@ -485,7 +529,11 @@ contract MerkleTrie { */ function parseProof( bytes memory _proof - ) internal pure returns (TrieNode[] memory) { + ) + internal + pure + returns (TrieNode[] memory) + { RLPReader.RLPItem[] memory nodes = RLPReader.toList(RLPReader.toRlpItem(_proof)); TrieNode[] memory proof = new TrieNode[](nodes.length); @@ -509,7 +557,11 @@ contract MerkleTrie { */ function getNodeID( RLPReader.RLPItem memory _node - ) internal pure returns (bytes32) { + ) + internal + pure + returns (bytes32) + { bytes memory nodeID; if (_node.len < 32) { @@ -530,7 +582,11 @@ contract MerkleTrie { */ function getNodePath( TrieNode memory _node - ) internal pure returns (bytes memory) { + ) + internal + pure + returns (bytes memory) + { return BytesLib.toNibbles(RLPReader.toBytes(_node.decoded[0])); } @@ -542,7 +598,11 @@ contract MerkleTrie { */ function getNodeKey( TrieNode memory _node - ) internal pure returns (bytes memory) { + ) + internal + pure + returns (bytes memory) + { return removeHexPrefix(getNodePath(_node)); } @@ -553,7 +613,11 @@ contract MerkleTrie { */ function getNodeValue( TrieNode memory _node - ) internal pure returns (bytes memory) { + ) + internal + pure + returns (bytes memory) + { return RLPReader.toBytes(_node.decoded[_node.decoded.length - 1]); } @@ -565,7 +629,11 @@ contract MerkleTrie { */ function getNodeHash( bytes memory _encoded - ) internal pure returns (bytes memory) { + ) + internal + pure + returns (bytes memory) + { if (_encoded.length < 32) { return _encoded; } else { @@ -580,7 +648,11 @@ contract MerkleTrie { */ function getNodeType( TrieNode memory _node - ) internal pure returns (NodeType) { + ) + internal + pure + returns (NodeType) + { if (_node.decoded.length == BRANCH_NODE_LENGTH) { return NodeType.BranchNode; } else if (_node.decoded.length == LEAF_OR_EXTENSION_NODE_LENGTH) { @@ -604,7 +676,14 @@ contract MerkleTrie { * @param _b Second nibble array. * @return Number of shared nibbles. */ - function getSharedNibbleLength(bytes memory _a, bytes memory _b) internal pure returns (uint256) { + function getSharedNibbleLength( + bytes memory _a, + bytes memory _b + ) + internal + pure + returns (uint256) + { uint256 i = 0; while (_a.length > i && _b.length > i && _a[i] == _b[i]) { i++; @@ -619,7 +698,11 @@ contract MerkleTrie { */ function makeNode( bytes[] memory _raw - ) internal pure returns (TrieNode memory) { + ) + internal + pure + returns (TrieNode memory) + { bytes memory encoded = RLPWriter.encodeList(_raw); return TrieNode({ @@ -635,7 +718,11 @@ contract MerkleTrie { */ function makeNode( RLPReader.RLPItem[] memory _items - ) internal pure returns (TrieNode memory) { + ) + internal + pure + returns (TrieNode memory) + { bytes[] memory raw = new bytes[](_items.length); for (uint256 i = 0; i < _items.length; i++) { raw[i] = RLPReader.toRlpBytes(_items[i]); @@ -652,7 +739,11 @@ contract MerkleTrie { function makeExtensionNode( bytes memory _key, bytes memory _value - ) internal pure returns (TrieNode memory) { + ) + internal + pure + returns (TrieNode memory) + { bytes[] memory raw = new bytes[](2); bytes memory key = addHexPrefix(_key, false); raw[0] = RLPWriter.encodeBytes(BytesLib.fromNibbles(key)); @@ -672,7 +763,11 @@ contract MerkleTrie { function makeLeafNode( bytes memory _key, bytes memory _value - ) internal pure returns (TrieNode memory) { + ) + internal + pure + returns (TrieNode memory) + { bytes[] memory raw = new bytes[](2); bytes memory key = addHexPrefix(_key, true); raw[0] = RLPWriter.encodeBytes(BytesLib.fromNibbles(key)); @@ -684,7 +779,11 @@ contract MerkleTrie { * @notice Creates an empty branch node. * @return Empty branch node as a TrieNode stuct. */ - function makeEmptyBranchNode() internal pure returns (TrieNode memory) { + function makeEmptyBranchNode() + internal + pure + returns (TrieNode memory) + { bytes[] memory raw = new bytes[](BRANCH_NODE_LENGTH); for (uint256 i = 0; i < raw.length; i++) { raw[i] = RLP_NULL_BYTES; @@ -701,7 +800,11 @@ contract MerkleTrie { function editBranchValue( TrieNode memory _branch, bytes memory _value - ) internal pure returns (TrieNode memory) { + ) + internal + pure + returns (TrieNode memory) + { bytes memory encoded = RLPWriter.encodeBytes(_value); _branch.decoded[_branch.decoded.length - 1] = RLPReader.toRlpItem(encoded); return makeNode(_branch.decoded); @@ -718,7 +821,11 @@ contract MerkleTrie { TrieNode memory _branch, uint8 _index, bytes memory _value - ) internal pure returns (TrieNode memory) { + ) + internal + pure + returns (TrieNode memory) + { bytes memory encoded = _value.length < 32 ? _value : RLPWriter.encodeBytes(_value); _branch.decoded[_index] = RLPReader.toRlpItem(encoded); return makeNode(_branch.decoded); @@ -733,7 +840,11 @@ contract MerkleTrie { function addHexPrefix( bytes memory _key, bool _isLeaf - ) internal pure returns (bytes memory) { + ) + internal + pure + returns (bytes memory) + { uint8 prefix = _isLeaf ? uint8(0x02) : uint8(0x00); uint8 offset = uint8(_key.length % 2); bytes memory prefixed = new bytes(2 - offset); @@ -748,7 +859,11 @@ contract MerkleTrie { */ function removeHexPrefix( bytes memory _path - ) internal pure returns (bytes memory) { + ) + internal + pure + returns (bytes memory) + { if (uint8(_path[0]) % 2 == 0) { return BytesLib.slice(_path, 2); } else { @@ -771,7 +886,11 @@ contract MerkleTrie { uint256 _aLength, TrieNode[] memory _b, uint256 _bLength - ) internal pure returns (TrieNode[] memory) { + ) + internal + pure + returns (TrieNode[] memory) + { TrieNode[] memory ret = new TrieNode[](_aLength + _bLength); // Copy elements from the first array. diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPEncode.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPEncode.sol index 6dc28539181eb..e1788fc307f0b 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPEncode.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPEncode.sol @@ -16,7 +16,13 @@ contract RLPEncode { * @param self The byte string to encode. * @return The RLP encoded string in bytes. */ - function encodeBytes(bytes memory self) public pure returns (bytes memory) { + function encodeBytes( + bytes memory self + ) + public + pure + returns (bytes memory) + { bytes memory encoded; if (self.length == 1 && uint8(self[0]) < 128) { encoded = self; @@ -31,7 +37,13 @@ contract RLPEncode { * @param self The list of RLP encoded byte strings. * @return The RLP encoded list of items in bytes. */ - function encodeList(bytes[] memory self) public pure returns (bytes memory) { + function encodeList( + bytes[] memory self + ) + public + pure + returns (bytes memory) + { bytes memory list = flatten(self); return concat(encodeLength(list.length, 192), list); } @@ -41,7 +53,13 @@ contract RLPEncode { * @param self The string to encode. * @return The RLP encoded string in bytes. */ - function encodeString(string memory self) public pure returns (bytes memory) { + function encodeString( + string memory self + ) + public + pure + returns (bytes memory) + { return encodeBytes(bytes(self)); } @@ -50,7 +68,13 @@ contract RLPEncode { * @param self The address to encode. * @return The RLP encoded address in bytes. */ - function encodeAddress(address self) public pure returns (bytes memory) { + function encodeAddress( + address self + ) + public + pure + returns (bytes memory) + { bytes memory inputBytes; assembly { let m := mload(0x40) @@ -66,7 +90,13 @@ contract RLPEncode { * @param self The uint to encode. * @return The RLP encoded uint in bytes. */ - function encodeUint(uint self) public pure returns (bytes memory) { + function encodeUint( + uint self + ) + public + pure + returns (bytes memory) + { return encodeBytes(toBinary(self)); } @@ -75,7 +105,13 @@ contract RLPEncode { * @param self The int to encode. * @return The RLP encoded int in bytes. */ - function encodeInt(int self) public pure returns (bytes memory) { + function encodeInt( + int self + ) + public + pure + returns (bytes memory) + { return encodeUint(uint(self)); } @@ -84,7 +120,13 @@ contract RLPEncode { * @param self The bool to encode. * @return The RLP encoded bool in bytes. */ - function encodeBool(bool self) public pure returns (bytes memory) { + function encodeBool( + bool self + ) + public + pure + returns (bytes memory) + { bytes memory encoded = new bytes(1); encoded[0] = (self ? bytes1(0x01) : bytes1(0x80)); return encoded; @@ -101,7 +143,14 @@ contract RLPEncode { * @param offset 128 if item is string, 192 if item is list. * @return RLP encoded bytes. */ - function encodeLength(uint len, uint offset) private pure returns (bytes memory) { + function encodeLength( + uint len, + uint offset + ) + private + pure + returns (bytes memory) + { bytes memory encoded; if (len < 56) { encoded = new bytes(1); @@ -129,7 +178,13 @@ contract RLPEncode { * @param _x The integer to encode. * @return RLP encoded bytes. */ - function toBinary(uint _x) private pure returns (bytes memory) { + function toBinary( + uint _x + ) + private + pure + returns (bytes memory) + { bytes memory b = new bytes(32); assembly { mstore(add(b, 32), _x) @@ -154,7 +209,14 @@ contract RLPEncode { * @param _src Source location. * @param _len Length of memory to copy. */ - function memcpy(uint _dest, uint _src, uint _len) private pure { + function memcpy( + uint _dest, + uint _src, + uint _len + ) + private + pure + { uint dest = _dest; uint src = _src; uint len = _len; @@ -181,7 +243,13 @@ contract RLPEncode { * @param _list List of byte strings to flatten. * @return The flattened byte string. */ - function flatten(bytes[] memory _list) private pure returns (bytes memory) { + function flatten( + bytes[] memory _list + ) + private + pure + returns (bytes memory) + { if (_list.length == 0) { return new bytes(0); } @@ -216,7 +284,14 @@ contract RLPEncode { * @param _postBytes Second byte string. * @return Both byte string combined. */ - function concat(bytes memory _preBytes, bytes memory _postBytes) private pure returns (bytes memory) { + function concat( + bytes memory _preBytes, + bytes memory _postBytes + ) + private + pure + returns (bytes memory) + { bytes memory tempBytes; assembly { diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPReader.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPReader.sol index eab4dcd0da647..9e96028c5ca61 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPReader.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPReader.sol @@ -5,6 +5,16 @@ pragma solidity ^0.5.0; * @author Hamdi Allam hamdi.allam97@gmail.com */ library RLPReader { + /* + * Data Structures + */ + + struct RLPItem { + uint len; + uint memPtr; + } + + /* * Contract Constants */ @@ -16,16 +26,6 @@ library RLPReader { uint8 constant private WORD_SIZE = 32; - /* - * Structs - */ - - struct RLPItem { - uint len; - uint memPtr; - } - - /* * Internal Functions */ @@ -33,7 +33,13 @@ library RLPReader { /** * @param item RLP encoded bytes */ - function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { + function toRlpItem( + bytes memory item + ) + internal + pure + returns (RLPItem memory) + { uint memPtr; assembly { memPtr := add(item, 0x20) @@ -45,21 +51,39 @@ library RLPReader { /** * @param item RLP encoded bytes */ - function rlpLen(RLPItem memory item) internal pure returns (uint) { + function rlpLen( + RLPItem memory item + ) + internal + pure + returns (uint) + { return item.len; } /** * @param item RLP encoded bytes */ - function payloadLen(RLPItem memory item) internal pure returns (uint) { + function payloadLen( + RLPItem memory item + ) + internal + pure + returns (uint) + { return item.len - _payloadOffset(item.memPtr); } /** * @param item RLP encoded list in bytes */ - function toList(RLPItem memory item) internal pure returns (RLPItem[] memory result) { + function toList( + RLPItem memory item + ) + internal + pure + returns (RLPItem[] memory result) + { require(isList(item)); uint items = numItems(item); @@ -75,7 +99,13 @@ library RLPReader { } // @return indicator whether encoded payload is a list. negate this function call for isData. - function isList(RLPItem memory item) internal pure returns (bool) { + function isList( + RLPItem memory item + ) + internal + pure + returns (bool) + { if (item.len == 0) return false; uint8 byte0; @@ -92,7 +122,13 @@ library RLPReader { /** RLPItem conversions into data types **/ // @returns raw rlp encoding in bytes - function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) { + function toRlpBytes( + RLPItem memory item + ) + internal + pure + returns (bytes memory) + { bytes memory result = new bytes(item.len); if (result.length == 0) return result; @@ -106,7 +142,13 @@ library RLPReader { } // any non-zero byte is considered true - function toBoolean(RLPItem memory item) internal pure returns (bool) { + function toBoolean( + RLPItem memory item + ) + internal + pure + returns (bool) + { require(item.len == 1); uint result; uint memPtr = item.memPtr; @@ -117,14 +159,26 @@ library RLPReader { return result == 0 ? false : true; } - function toAddress(RLPItem memory item) internal pure returns (address) { + function toAddress( + RLPItem memory item + ) + internal + pure + returns (address) + { // 1 byte for the length prefix require(item.len == 21); return address(toUint(item)); } - function toUint(RLPItem memory item) internal pure returns (uint) { + function toUint( + RLPItem memory item + ) + internal + pure + returns (uint) + { require(item.len > 0 && item.len <= 33); uint offset = _payloadOffset(item.memPtr); @@ -145,7 +199,13 @@ library RLPReader { } // enforces 32 byte length - function toUintStrict(RLPItem memory item) internal pure returns (uint) { + function toUintStrict( + RLPItem memory item + ) + internal + pure + returns (uint) + { // one byte prefix require(item.len == 33); @@ -158,7 +218,13 @@ library RLPReader { return result; } - function toBytes(RLPItem memory item) internal pure returns (bytes memory) { + function toBytes( + RLPItem memory item + ) + internal + pure + returns (bytes memory) + { require(item.len > 0); uint offset = _payloadOffset(item.memPtr); @@ -174,12 +240,19 @@ library RLPReader { return result; } + /* - * Private Helpers - */ + * Private Functions + */ // @return number of payload items inside an encoded list. - function numItems(RLPItem memory item) private pure returns (uint) { + function numItems( + RLPItem memory item + ) + private + pure + returns (uint) + { if (item.len == 0) return 0; uint count = 0; @@ -194,7 +267,13 @@ library RLPReader { } // @return entire rlp item byte length - function _itemLength(uint memPtr) private pure returns (uint len) { + function _itemLength( + uint memPtr + ) + private + pure + returns (uint len) + { uint byte0; assembly { byte0 := byte(0, mload(memPtr)) @@ -233,7 +312,13 @@ library RLPReader { } // @return number of bytes until the data - function _payloadOffset(uint memPtr) private pure returns (uint) { + function _payloadOffset( + uint memPtr + ) + private + pure + returns (uint) + { uint byte0; assembly { byte0 := byte(0, mload(memPtr)) @@ -254,7 +339,14 @@ library RLPReader { * @param dest Pointer to destination * @param len Amount of memory to copy from the source */ - function copy(uint src, uint dest, uint len) private pure { + function copy( + uint src, + uint dest, + uint len + ) + private + pure + { if (len == 0) return; // copy as many word sizes as possible diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPWriter.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPWriter.sol index f78eb90812679..f2a0a2ef34dfa 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPWriter.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPWriter.sol @@ -17,7 +17,13 @@ library RLPWriter { * @param self The byte string to encode. * @return The RLP encoded string in bytes. */ - function encodeBytes(bytes memory self) internal pure returns (bytes memory) { + function encodeBytes( + bytes memory self + ) + internal + pure + returns (bytes memory) + { bytes memory encoded; if (self.length == 1 && uint8(self[0]) < 128) { encoded = self; @@ -32,7 +38,13 @@ library RLPWriter { * @param self The list of RLP encoded byte strings. * @return The RLP encoded list of items in bytes. */ - function encodeList(bytes[] memory self) internal pure returns (bytes memory) { + function encodeList( + bytes[] memory self + ) + internal + pure + returns (bytes memory) + { bytes memory list = flatten(self); return concat(encodeLength(list.length, 192), list); } @@ -42,7 +54,13 @@ library RLPWriter { * @param self The string to encode. * @return The RLP encoded string in bytes. */ - function encodeString(string memory self) internal pure returns (bytes memory) { + function encodeString( + string memory self + ) + internal + pure + returns (bytes memory) + { return encodeBytes(bytes(self)); } @@ -51,7 +69,13 @@ library RLPWriter { * @param self The address to encode. * @return The RLP encoded address in bytes. */ - function encodeAddress(address self) internal pure returns (bytes memory) { + function encodeAddress( + address self + ) + internal + pure + returns (bytes memory) + { bytes memory inputBytes; assembly { let m := mload(0x40) @@ -67,7 +91,13 @@ library RLPWriter { * @param self The uint to encode. * @return The RLP encoded uint in bytes. */ - function encodeUint(uint self) internal pure returns (bytes memory) { + function encodeUint( + uint self + ) + internal + pure + returns (bytes memory) + { return encodeBytes(toBinary(self)); } @@ -76,7 +106,13 @@ library RLPWriter { * @param self The int to encode. * @return The RLP encoded int in bytes. */ - function encodeInt(int self) internal pure returns (bytes memory) { + function encodeInt( + int self + ) + internal + pure + returns (bytes memory) + { return encodeUint(uint(self)); } @@ -85,7 +121,13 @@ library RLPWriter { * @param self The bool to encode. * @return The RLP encoded bool in bytes. */ - function encodeBool(bool self) internal pure returns (bytes memory) { + function encodeBool( + bool self + ) + internal + pure + returns (bytes memory) + { bytes memory encoded = new bytes(1); encoded[0] = (self ? bytes1(0x01) : bytes1(0x80)); return encoded; @@ -93,7 +135,7 @@ library RLPWriter { /* - * Private functions + * Private Functions */ /** @@ -102,7 +144,14 @@ library RLPWriter { * @param offset 128 if item is string, 192 if item is list. * @return RLP encoded bytes. */ - function encodeLength(uint len, uint offset) private pure returns (bytes memory) { + function encodeLength( + uint len, + uint offset + ) + private + pure + returns (bytes memory) + { bytes memory encoded; if (len < 56) { encoded = new bytes(1); @@ -130,7 +179,13 @@ library RLPWriter { * @param _x The integer to encode. * @return RLP encoded bytes. */ - function toBinary(uint _x) private pure returns (bytes memory) { + function toBinary( + uint _x + ) + private + pure + returns (bytes memory) + { bytes memory b = new bytes(32); assembly { mstore(add(b, 32), _x) @@ -155,7 +210,14 @@ library RLPWriter { * @param _src Source location. * @param _len Length of memory to copy. */ - function memcpy(uint _dest, uint _src, uint _len) private pure { + function memcpy( + uint _dest, + uint _src, + uint _len + ) + private + pure + { uint dest = _dest; uint src = _src; uint len = _len; @@ -182,7 +244,13 @@ library RLPWriter { * @param _list List of byte strings to flatten. * @return The flattened byte string. */ - function flatten(bytes[] memory _list) private pure returns (bytes memory) { + function flatten( + bytes[] memory _list + ) + private + pure + returns (bytes memory) + { if (_list.length == 0) { return new bytes(0); } @@ -217,7 +285,14 @@ library RLPWriter { * @param _postBytes Second byte string. * @return Both byte string combined. */ - function concat(bytes memory _preBytes, bytes memory _postBytes) private pure returns (bytes memory) { + function concat( + bytes memory _preBytes, + bytes memory _postBytes + ) + private + pure + returns (bytes memory) + { bytes memory tempBytes; assembly { diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RollupMerkleUtils.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RollupMerkleUtils.sol index 83a2d8fb0bce4..d61ac3ade4ba3 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RollupMerkleUtils.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RollupMerkleUtils.sol @@ -6,7 +6,7 @@ pragma experimental ABIEncoderV2; */ contract RollupMerkleUtils { /* - * Structs + * Data Structures */ struct SparseMerkleTree { @@ -32,7 +32,9 @@ contract RollupMerkleUtils { * Initialize a new SparseMerkleUtils contract, computing the * default hashes for the sparse merkle tree (SMT). */ - constructor() public { + constructor() + public + { setDefaultHashes(); } @@ -48,7 +50,11 @@ contract RollupMerkleUtils { */ function getMerkleRoot( bytes[] memory _dataBlocks - ) public view returns (bytes32) { + ) + public + view + returns (bytes32) + { uint nextLevelLength = _dataBlocks.length; uint currentLevel = 0; @@ -104,7 +110,11 @@ contract RollupMerkleUtils { bytes memory _dataBlock, uint _path, bytes32[] memory _siblings - ) public pure returns (bytes32) { + ) + public + pure + returns (bytes32) + { // First compute the leaf node. bytes32 computedNode = keccak256(_dataBlock); @@ -136,7 +146,11 @@ contract RollupMerkleUtils { bytes memory _dataBlock, uint _path, bytes32[] memory _siblings - ) public pure returns (bool) { + ) + public + pure + returns (bool) + { // First compute the leaf node bytes32 calculatedRoot = computeInclusionProofRoot( _dataBlock, @@ -153,7 +167,12 @@ contract RollupMerkleUtils { * @param _dataBlock The data block we're storing/verifying * @param _path The path from the leaf to the root / the index of the leaf. */ - function update(bytes memory _dataBlock, uint _path) public { + function update( + bytes memory _dataBlock, + uint _path + ) + public + { bytes32[] memory siblings = getSiblings(_path); store(_dataBlock, _path, siblings); } @@ -163,7 +182,12 @@ contract RollupMerkleUtils { * @param _leaf The leaf we're storing/verifying * @param _path The path from the leaf to the root / the index of the leaf. */ - function updateLeaf(bytes32 _leaf, uint _path) public { + function updateLeaf( + bytes32 _leaf, + uint _path + ) + public + { bytes32[] memory siblings = getSiblings(_path); storeLeaf(_leaf, _path, siblings); } @@ -178,7 +202,9 @@ contract RollupMerkleUtils { bytes memory _dataBlock, uint _path, bytes32[] memory _siblings - ) public { + ) + public + { bytes32 oldRoot = tree.root; store(_dataBlock, _path, _siblings); @@ -198,7 +224,9 @@ contract RollupMerkleUtils { bytes memory _dataBlock, uint _path, bytes32[] memory _siblings - ) public { + ) + public + { // Compute the leaf node & store the leaf bytes32 leaf = keccak256(_dataBlock); storeLeaf(leaf, _path, _siblings); @@ -214,7 +242,9 @@ contract RollupMerkleUtils { bytes32 _leaf, uint _path, bytes32[] memory _siblings - ) public { + ) + public + { // First compute the leaf node bytes32 computedNode = _leaf; @@ -244,7 +274,13 @@ contract RollupMerkleUtils { * @param _path The path from the leaf to the root / the index of the leaf. * @return The sibling nodes along the way. */ - function getSiblings(uint _path) public view returns (bytes32[] memory) { + function getSiblings( + uint _path + ) + public + view + returns (bytes32[] memory) + { bytes32[] memory siblings = new bytes32[](tree.height); bytes32 computedNode = tree.root; @@ -268,7 +304,11 @@ contract RollupMerkleUtils { * Get our stored tree's root * @return The merkle root of the tree */ - function getRoot() public view returns (bytes32) { + function getRoot() + public + view + returns (bytes32) + { return tree.root; } @@ -277,7 +317,12 @@ contract RollupMerkleUtils { * @param _root The merkle root of the tree * @param _height The height of the tree */ - function setMerkleRootAndHeight(bytes32 _root, uint _height) public { + function setMerkleRootAndHeight( + bytes32 _root, + uint _height + ) + public + { tree.root = _root; tree.height = _height; } @@ -292,7 +337,9 @@ contract RollupMerkleUtils { bytes32 _parent, bytes32 _leftChild, bytes32 _rightChild - ) public { + ) + public + { tree.nodes[getLeftSiblingKey(_parent)] = _leftChild; tree.nodes[getRightSiblingKey(_parent)] = _rightChild; } @@ -307,7 +354,11 @@ contract RollupMerkleUtils { function getNthBitFromRight( uint _intVal, uint _index - ) public pure returns (uint8) { + ) + public + pure + returns (uint8) + { return uint8(_intVal >> _index & 1); } @@ -318,7 +369,11 @@ contract RollupMerkleUtils { */ function getChildren( bytes32 _parent - ) public view returns (bytes32, bytes32) { + ) + public + view + returns (bytes32, bytes32) + { return ( tree.nodes[getLeftSiblingKey(_parent)], tree.nodes[getRightSiblingKey(_parent)] @@ -331,7 +386,13 @@ contract RollupMerkleUtils { * @param _parent The parent node * @return the key for the left sibling (0 as the first bit) */ - function getLeftSiblingKey(bytes32 _parent) public pure returns(bytes32) { + function getLeftSiblingKey( + bytes32 _parent + ) + public + pure + returns(bytes32) + { return _parent & 0x0111111111111111111111111111111111111111111111111111111111111111; } @@ -341,7 +402,13 @@ contract RollupMerkleUtils { * @param _parent The parent node * @return the key for the right sibling (1 as the first bit) */ - function getRightSiblingKey(bytes32 _parent) public pure returns(bytes32) { + function getRightSiblingKey( + bytes32 _parent + ) + public + pure + returns(bytes32) + { return _parent | 0x1000000000000000000000000000000000000000000000000000000000000000; } @@ -353,7 +420,9 @@ contract RollupMerkleUtils { /** * Set default hashes. */ - function setDefaultHashes() internal { + function setDefaultHashes() + internal + { // Set the initial default hash. defaultHashes[0] = keccak256(abi.encodePacked(uint(0))); @@ -371,7 +440,11 @@ contract RollupMerkleUtils { function getParent( bytes32 _left, bytes32 _right - ) internal pure returns(bytes32) { + ) + internal + pure + returns(bytes32) + { return keccak256(abi.encodePacked(_left, _right)); } } \ No newline at end of file diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/TransactionParser.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/TransactionParser.sol index 7ca5af1e62fa1..cec226afffccd 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/TransactionParser.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/TransactionParser.sol @@ -20,7 +20,11 @@ library TransactionParser { */ function getTransactionHash( DataTypes.OVMTransactionData memory _transaction - ) internal pure returns (bytes32) { + ) + internal + pure + returns (bytes32) + { bytes memory encodedTransaction = encodeTransactionData(_transaction); return keccak256(encodedTransaction); } @@ -36,7 +40,11 @@ library TransactionParser { */ function encodeTransactionData( DataTypes.OVMTransactionData memory _transactionData - ) internal pure returns (bytes memory) { + ) + internal + pure + returns (bytes memory) + { bytes[] memory raw = new bytes[](7); raw[0] = RLPWriter.encodeUint(_transactionData.timestamp); diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/resolvers/AddressResolver.sol b/packages/contracts/contracts/optimistic-ethereum/utils/resolvers/AddressResolver.sol index 1860672572b23..47cdf58aac514 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/resolvers/AddressResolver.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/resolvers/AddressResolver.sol @@ -1,13 +1,36 @@ pragma solidity ^0.5.0; +/** + * @title AddressResolver + */ contract AddressResolver { - mapping (bytes32 => address) addresses; + /* + * Contract Variables + */ - function setAddress(string memory _name, address _address) public { + mapping (bytes32 => address) private addresses; + + + /* + * Public Functions + */ + + function setAddress( + string memory _name, + address _address + ) + public + { addresses[keccak256(abi.encodePacked(_name))] = _address; } - function getAddress(string memory _name) public view returns (address) { + function getAddress( + string memory _name + ) + public + view + returns (address) + { return addresses[keccak256(abi.encodePacked(_name))]; } } \ No newline at end of file diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/resolvers/ContractResolver.sol b/packages/contracts/contracts/optimistic-ethereum/utils/resolvers/ContractResolver.sol index 4eace392b1a07..214814ef277b3 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/resolvers/ContractResolver.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/resolvers/ContractResolver.sol @@ -1,15 +1,43 @@ pragma solidity ^0.5.0; +/* Library Imports */ import { AddressResolver } from "./AddressResolver.sol"; +/** + * @title ContractResolver + */ contract ContractResolver { - AddressResolver addressResolver; + /* + * Contract Variables + */ - constructor(address _addressResolver) public { + AddressResolver internal addressResolver; + + + /* + * Constructor + */ + + constructor( + address _addressResolver + ) + public + { addressResolver = AddressResolver(_addressResolver); } - function resolveContract(string memory _name) public view returns (address) { + + /* + * Public Functions + */ + + function resolveContract( + string memory _name + ) + public + view + returns (address) + { return addressResolver.getAddress(_name); } } \ No newline at end of file diff --git a/packages/contracts/docs/contract-standards.md b/packages/contracts/docs/contract-standards.md new file mode 100644 index 0000000000000..2a1f31839af11 --- /dev/null +++ b/packages/contracts/docs/contract-standards.md @@ -0,0 +1,207 @@ +# Contract Standards + +## Style Guide +### Maximum Line Length +Maximum line length is set globally to 100 characters. For a standard reading experience, however, the following exceptions are in place: + +1. Structs, enums, and events + +Structs, enums, and events should be defined over several lines even if their definitions would be under 100 characters. + +Ex.: +``` +event SomeEvent( + uint256 _someEventParameter +); +``` + +2. Function signatures + +Similarly, function signatures should always be defined over several lines. This allows us to easily read the various sections of the function no matter the actual length of the signature. + +Ex.: +``` +function otherExternalFunction( + uint256 _someFunctionParameter +) + external + someModifier + returns (uint256) +{ + // Some other action. +} +``` + +## Standard Contract Layout + +```solidity +pragma solidity ^0.5.0; +pragma experimental ABIEncoderV2; + +/* Contract Imports */ +import { WWWW } from "./path/to/contract.sol"; +import { XXXX } from "./path/to/other/contract.sol"; + +/* Library Imports */ +import { YYYY } from "./path/to/library.sol"; +import { ZZZZ } from "./path/to/other/library.sol"; + +/** + * @title ContractName + * @dev Some useful description. + */ +contract ContractName { + /* + * Data Structures + */ + + struct SomeStruct { + uint256 value; + } + + enum SomeEnum { + SomeEnumValue, + OtherEnumValue + } + + + /* + * Events + */ + + event SomeEvent( + uint256 _someEventParameter, + address _otherEventParameter + ); + + + /* + * Contract Constants + */ + + uint256 constant public SOME_SNAKE_CASE_CONSTANT; + bytes32 constant private OTHER_SNAKE_CASE_CONSTANT; + + + /* + * Contract Variables + */ + + uint256 public someCamelCaseVariable; + uint8 private otherCamelCaseVariable; + + + /* + * Modifiers + */ + + modifier someModifier( + uint256 _someModifierParameter + ) { + require( + someRequirement, + "Did not pass the requirement" + ); + _; + } + + + /* + * Constructor + */ + + /** + * @param _someConstructorParameter Some constructor parameter + */ + constructor( + uint256 _someConstructorParameter + ) + public + { + // Some constructor action. + } + + + /* + * External Functions + */ + + /** + * A function that does something. + * @param _someFunctionParameter A function parameter. + */ + function someExternalFunction( + uint256 _someFunctionParameter + ) + external + { + // Some action. + } + + /** + * A function that does another thing. + * @param _someFunctionParameter A function parameter. + * @return Some value. + */ + function otherExternalFunction( + uint256 _someFunctionParameter + ) + external + someModifier + returns (uint256) + { + // Some other action. + } + + + /* + * Public Functions + */ + + /** + * A public function that does something. + * @param _someFunctionParameter A function parameter. + */ + function somePublicFunction( + uint256 _someFunctionParameter + ) + public + { + // Some action. + } + + + /* + * Internal Functions + */ + + /** + * An internal function that does something. + * @param _someFunctionParameter A function parameter. + */ + function someInternalFunction( + uint256 _someFunctionParameter + ) + internal + { + // Some action. + } + + + /* + * Private Functions + */ + + /** + * A private function that does something. + * @param _someFunctionParameter A function parameter. + */ + function somePrivateFunction( + uint256 _someFunctionParameter + ) + private + { + // Some action. + } +} +``` \ No newline at end of file From e55abdf222ae2de1e7f640fc9face733ef4b40fc Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Mon, 27 Jul 2020 15:57:37 -0400 Subject: [PATCH 03/24] Added some function comments --- .../optimistic-ethereum/queue/SafetyTransactionQueue.sol | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/contracts/contracts/optimistic-ethereum/queue/SafetyTransactionQueue.sol b/packages/contracts/contracts/optimistic-ethereum/queue/SafetyTransactionQueue.sol index f4d2b475bafd2..e9c5213e8487b 100644 --- a/packages/contracts/contracts/optimistic-ethereum/queue/SafetyTransactionQueue.sol +++ b/packages/contracts/contracts/optimistic-ethereum/queue/SafetyTransactionQueue.sol @@ -16,6 +16,9 @@ contract SafetyTransactionQueue is ContractResolver, RollupQueue { * Constructor */ + /** + * @param _addressResolver Address of the AddressResolver contract. + */ constructor( address _addressResolver ) @@ -28,6 +31,11 @@ contract SafetyTransactionQueue is ContractResolver, RollupQueue { * Public Functions */ + /** + * Checks that a sender is authenticated to dequeue. + * @param _sender Address to check. + * @return Whether or not the sender can dequeue. + */ function authenticateDequeue( address _sender ) From 50fda758407fd07a9e4841f68284af4a62535cd5 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Mon, 27 Jul 2020 16:05:39 -0400 Subject: [PATCH 04/24] Added remaining function comments --- .../queue/L1ToL2TransactionQueue.sol | 18 +++++++++ .../optimistic-ethereum/queue/RollupQueue.sol | 37 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/packages/contracts/contracts/optimistic-ethereum/queue/L1ToL2TransactionQueue.sol b/packages/contracts/contracts/optimistic-ethereum/queue/L1ToL2TransactionQueue.sol index d66261946a2fe..46af012a7c95f 100644 --- a/packages/contracts/contracts/optimistic-ethereum/queue/L1ToL2TransactionQueue.sol +++ b/packages/contracts/contracts/optimistic-ethereum/queue/L1ToL2TransactionQueue.sol @@ -23,6 +23,10 @@ contract L1ToL2TransactionQueue is ContractResolver, RollupQueue { * Constructor */ + /** + * @param _addressResolver Address of the AddressResolver contract. + * @param _l1ToL2TransactionPasser Address of the L1-L2 transaction passer. + */ constructor( address _addressResolver, address _l1ToL2TransactionPasser @@ -38,6 +42,11 @@ contract L1ToL2TransactionQueue is ContractResolver, RollupQueue { * Public Functions */ + /** + * Checks whether a sender is allowed to enqueue. + * @param _sender Sender address to check. + * @return Whether or not the sender can enqueue. + */ function authenticateEnqueue( address _sender ) @@ -48,6 +57,11 @@ contract L1ToL2TransactionQueue is ContractResolver, RollupQueue { return _sender == l1ToL2TransactionPasser; } + /** + * Checks whether a sender is allowed to dequeue. + * @param _sender Sender address to check. + * @return Whether or not the sender can dequeue. + */ function authenticateDequeue( address _sender ) @@ -58,6 +72,10 @@ contract L1ToL2TransactionQueue is ContractResolver, RollupQueue { return _sender == address(resolveCanonicalTransactionChain()); } + /** + * Checks whether this is a calldata transaction queue. + * @return Whether or not this is a calldata tx queue. + */ function isCalldataTxQueue() public returns (bool) diff --git a/packages/contracts/contracts/optimistic-ethereum/queue/RollupQueue.sol b/packages/contracts/contracts/optimistic-ethereum/queue/RollupQueue.sol index 6994f97ff6cea..65e7cfb598502 100644 --- a/packages/contracts/contracts/optimistic-ethereum/queue/RollupQueue.sol +++ b/packages/contracts/contracts/optimistic-ethereum/queue/RollupQueue.sol @@ -27,6 +27,10 @@ contract RollupQueue { * Public Functions */ + /** + * Gets the total number of batches. + * @return Total submitted batches. + */ function getBatchHeadersLength() public view @@ -35,6 +39,10 @@ contract RollupQueue { return batchHeaders.length; } + /** + * Checks if the queue is empty. + * @return Whether or not the queue is empty. + */ function isEmpty() public view @@ -43,6 +51,10 @@ contract RollupQueue { return front >= batchHeaders.length; } + /** + * Peeks the front element on the queue. + * @return Front queue element. + */ function peek() public view @@ -52,6 +64,10 @@ contract RollupQueue { return batchHeaders[front]; } + /** + * Peeks the timestamp of the front element on the queue. + * @return Front queue element timestamp. + */ function peekTimestamp() public view @@ -61,6 +77,11 @@ contract RollupQueue { return frontBatch.timestamp; } + /** + * Checks whether a sender is allowed to enqueue. + * @param _sender Sender address to check. + * @return Whether or not the sender can enqueue. + */ function authenticateEnqueue( address _sender ) @@ -71,6 +92,11 @@ contract RollupQueue { return true; } + /** + * Checks whether a sender is allowed to dequeue. + * @param _sender Sender address to check. + * @return Whether or not the sender can dequeue. + */ function authenticateDequeue( address _sender ) @@ -81,6 +107,10 @@ contract RollupQueue { return true; } + /** + * Checks if this is a calldata transaction queue. + * @return Whether or not this is a calldata tx queue. + */ function isCalldataTxQueue() public returns (bool) @@ -88,6 +118,10 @@ contract RollupQueue { return true; } + /** + * Attempts to enqueue a transaction. + * @param _tx Transaction data to enqueue. + */ function enqueueTx( bytes memory _tx ) @@ -113,6 +147,9 @@ contract RollupQueue { } } + /** + * Attempts to dequeue a transaction. + */ function dequeue() public { From ad4883c19333e19032631ff26e473abffa73ff03 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Mon, 27 Jul 2020 16:55:44 -0400 Subject: [PATCH 05/24] Added SolPP support --- package.json | 7 +- packages/contracts/buidler-env.d.ts | 3 +- packages/contracts/buidler.config.ts | 21 +++ .../optimistic-ethereum/ovm/FraudVerifier.sol | 39 +++--- .../ovm/PartialStateManager.sol | 35 +++++ .../utils/libraries/RLPReader.sol | 2 +- packages/contracts/package.json | 4 +- .../test/contracts/ovm/FraudVerifier.spec.ts | 2 +- .../test/test-helpers/resolution/config.ts | 2 +- packages/contracts/tsconfig.json | 3 +- yarn.lock | 120 +++++++++++++----- 11 files changed, 178 insertions(+), 60 deletions(-) diff --git a/package.json b/package.json index d9dc2d5bf0398..897154bde8d34 100644 --- a/package.json +++ b/package.json @@ -22,14 +22,15 @@ "test": "yarn test:core && yarn test:integration", "build": "yarn run patch && lerna link && wsrun -p $(yarn --silent run pkgparse) -r --fast-exit --stages --exclude-missing build", "all": "yarn clean && yarn build && yarn test && yarn fix && yarn lint", - "release": "yarn clean && yarn run build && lerna publish --force-publish --exact -m \"chore(@ethereum-optimism) publish %s release\"", + "release": "yarn clean && yarn run build && lesrna publish --force-publish --exact -m \"chore(@ethereum-optimism) publish %s release\"", "release:patch": "yarn clean && yarn build && lerna version prerelease --yes && lerna publish from-package --yes --force-publish -m \"chore(@ethereum-optimism) publish %s release\"", "release:rc": "yarn clean && yarn build && lerna version prerelease --yes && lerna publish from-package --yes --force-publish --npm-tag=rc -m \"chore(@ethereum-optimism) publish %s release\"", "release:alpha": "yarn clean && yarn build && lerna version prerelease --yes && lerna publish from-package --yes --force-publish --dist-tag=alpha -m \"chore(@ethereum-optimism) publish %s release\"", "release:beta": "yarn clean && yarn build && lerna version prerelease --yes && lerna publish from-package --yes --force-publish --dist-tag=beta -m \"chore(@ethereum-optimism) publish %s release\"", - "patch": "yarn run patch:ganache && yarn run patch:waffle", + "patch": "yarn run patch:ganache && yarn run patch:waffle && yarn run patch:solpp", "patch:ganache": "rm node_modules/ganache-core/typings/index.d.ts ||:", - "patch:waffle": "sed -ie 's!Ganache.GanacheOpts!any!g' node_modules/ethereum-waffle/dist/waffle.d.ts" + "patch:waffle": "sed -ie 's!Ganache.GanacheOpts!any!g' node_modules/ethereum-waffle/dist/waffle.d.ts", + "patch:solpp": "sed -ie 's!const {!let {!g' packages/contracts/node_modules/solpp/src/resolver.js" }, "repository": "git+https://github.com/ethereum-optimism/optimism-monorepo.git", "keywords": [ diff --git a/packages/contracts/buidler-env.d.ts b/packages/contracts/buidler-env.d.ts index e787fca214747..47d04c09d8eea 100644 --- a/packages/contracts/buidler-env.d.ts +++ b/packages/contracts/buidler-env.d.ts @@ -1,2 +1,3 @@ /// -/// \ No newline at end of file +/// +/// \ No newline at end of file diff --git a/packages/contracts/buidler.config.ts b/packages/contracts/buidler.config.ts index 7fa4a8bd0316d..c47b18fc97a87 100644 --- a/packages/contracts/buidler.config.ts +++ b/packages/contracts/buidler.config.ts @@ -7,10 +7,26 @@ import { usePlugin('@nomiclabs/buidler-ethers') usePlugin('@nomiclabs/buidler-waffle') +usePlugin('@nomiclabs/buidler-solpp') usePlugin('solidity-coverage') import './plugins/hijack-compiler' +const parseSolppFlags = (): { [flag: string]: boolean } => { + const flags: { [flag: string]: boolean } = {} + + const solppEnv = process.env.SOLPP_FLAGS + if (!solppEnv) { + return flags + } + + for (const flag of solppEnv.split(',')) { + flags[flag] = true + } + + return flags +} + const config: BuidlerConfig = { networks: { buidlerevm: { @@ -25,6 +41,11 @@ const config: BuidlerConfig = { mocha: { timeout: 50000, }, + solpp: { + defs: { + ...parseSolppFlags() + } + } } export default config diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/FraudVerifier.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/FraudVerifier.sol index 0ea84638a9dcb..71f89bb7c734f 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/FraudVerifier.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/FraudVerifier.sol @@ -36,17 +36,13 @@ contract FraudVerifier is ContractResolver { /** * @param _addressResolver Address of the AddressResolver contract. - * @param _isTest Whether or not to throw into testing mode. */ constructor( - address _addressResolver, - bool _isTest + address _addressResolver ) public ContractResolver(_addressResolver) - { - isTest = _isTest; - } + {} /* @@ -111,21 +107,22 @@ contract FraudVerifier is ContractResolver { // this is handled by the hasStateTransitioner check above, which would // fail when the existing StateTransitioner's pre-state root does not // match the provided one. - if (isTest) { - stateTransitioners[_preStateTransitionIndex] = new StubStateTransitioner( - address(addressResolver), - _preStateTransitionIndex, - _preStateRoot, - TransactionParser.getTransactionHash(_transactionData) - ); - } else { - stateTransitioners[_preStateTransitionIndex] = new StateTransitioner( - address(addressResolver), - _preStateTransitionIndex, - _preStateRoot, - TransactionParser.getTransactionHash(_transactionData) - ); - } + + // #if FLAG_IS_TEST + stateTransitioners[_preStateTransitionIndex] = new StubStateTransitioner( + address(addressResolver), + _preStateTransitionIndex, + _preStateRoot, + TransactionParser.getTransactionHash(_transactionData) + ); + // #else + stateTransitioners[_preStateTransitionIndex] = new StateTransitioner( + address(addressResolver), + _preStateTransitionIndex, + _preStateRoot, + TransactionParser.getTransactionHash(_transactionData) + ); + // #endif } /** diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol index 87b5d8250e3d6..44254e8a48f0e 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol @@ -8,6 +8,9 @@ import { ExecutionManager } from "./ExecutionManager.sol"; /* Library Imports */ import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; +/* Testing Imports */ +import { console } from "@nomiclabs/buidler/console.sol"; + /** * @title PartialStateManager * @notice The PartialStateManager is used for the on-chain fraud proof checker. @@ -91,6 +94,10 @@ contract PartialStateManager is ContractResolver { public onlyStateTransitioner { + // #if FLAG_IS_DEBUG + console.log("Initializing new transaction execution."); + // #endif + existsInvalidStateAccessFlag = false; updatedStorageSlotCounter = 0; updatedContractsCounter = 0; @@ -114,6 +121,15 @@ contract PartialStateManager is ContractResolver { public onlyStateTransitioner { + // #if FLAG_IS_DEBUG + console.log("Inserting verified storage slot."); + console.log("Contract address: %s", _ovmContractAddress); + console.log("Slot ID:"); + console.logBytes32(_slot); + console.log("Slot value:"); + console.logBytes32(_value); + // #endif + isVerifiedStorage[_ovmContractAddress][_slot] = true; ovmContractStorage[_ovmContractAddress][_slot] = _value; } @@ -132,6 +148,13 @@ contract PartialStateManager is ContractResolver { public onlyStateTransitioner { + // #if FLAG_IS_DEBUG + console.log("Inserting verified contract."); + console.log("OVM contract address: %s", _ovmContractAddress); + console.log("Code contract address: %s", _codeContractAddress); + console.log("Contract nonce: %s", _nonce); + // #endif + isVerifiedContract[_ovmContractAddress] = true; ovmContractNonces[_ovmContractAddress] = _nonce; ovmAddressToCodeContractAddress[_ovmContractAddress] = _codeContractAddress; @@ -528,6 +551,13 @@ contract PartialStateManager is ContractResolver { private { if (!isVerifiedStorage[_ovmContractAddress][_slot]) { + // #if FLAG_IS_DEBUG + console.log("Flagging as unverified because of a storage slot access."); + console.log("Contract address: %s", _ovmContractAddress); + console.log("Slot ID:"); + console.logBytes32(_slot); + // #endif + existsInvalidStateAccessFlag = true; } } @@ -542,6 +572,11 @@ contract PartialStateManager is ContractResolver { private { if (!isVerifiedContract[_ovmContractAddress]) { + // #if FLAG_IS_DEBUG + console.log("Flagging as unverified because of a contract access."); + console.log("Contract address: %s", _ovmContractAddress); + // #endif + existsInvalidStateAccessFlag = true; } } diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPReader.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPReader.sol index 9e96028c5ca61..69cb16e6c4cd4 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPReader.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/RLPReader.sol @@ -287,7 +287,7 @@ library RLPReader { else if (byte0 < LIST_SHORT_START) { assembly { - let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is + let byteLen := sub(byte0, 0xb7) // number of bytes the actual length is memPtr := add(memPtr, 1) // skip over the first byte /* 32 byte word size */ diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 7f80cbd3d5435..376ba7d091565 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -23,7 +23,7 @@ "scripts": { "all": "yarn clean && yarn build && yarn test && yarn fix && yarn lint", "test": "yarn run test:contracts", - "test:contracts": "buidler test --show-stack-traces", + "test:contracts": "cross-env SOLPP_FLAGS=\"FLAG_IS_TEST,FLAG_IS_DEBUG\" buidler test --show-stack-traces", "coverage": "yarn run coverage:contracts", "coverage:contracts": "buidler coverage --network coverage --show-stack-traces --testfiles \"test/contracts/**/*.spec.ts\"", "build": "yarn run build:contracts && yarn run build:typescript", @@ -41,6 +41,7 @@ "@eth-optimism/solc-transpiler": "^0.0.1-alpha.27", "@nomiclabs/buidler": "^1.3.8", "@nomiclabs/buidler-ethers": "^2.0.0", + "@nomiclabs/buidler-solpp": "^1.3.3", "@nomiclabs/buidler-waffle": "^2.0.0", "@nomiclabs/ethereumjs-vm": "^4.1.1", "@types/lodash": "^4.14.157", @@ -59,6 +60,7 @@ "typescript": "^3.9.5" }, "devDependencies": { + "cross-env": "^7.0.2", "glob": "^7.1.6", "solidity-coverage": "^0.7.9" } diff --git a/packages/contracts/test/contracts/ovm/FraudVerifier.spec.ts b/packages/contracts/test/contracts/ovm/FraudVerifier.spec.ts index 9fc3ff5cef4be..e89df5e29c48a 100644 --- a/packages/contracts/test/contracts/ovm/FraudVerifier.spec.ts +++ b/packages/contracts/test/contracts/ovm/FraudVerifier.spec.ts @@ -198,7 +198,7 @@ describe('FraudVerifier', () => { 'FraudVerifier', { factory: FraudVerifier, - params: [resolver.addressResolver.address, true], + params: [resolver.addressResolver.address], } ) }) diff --git a/packages/contracts/test/test-helpers/resolution/config.ts b/packages/contracts/test/test-helpers/resolution/config.ts index 846856a772fd1..e7c075a843bf0 100644 --- a/packages/contracts/test/test-helpers/resolution/config.ts +++ b/packages/contracts/test/test-helpers/resolution/config.ts @@ -56,7 +56,7 @@ export const getDefaultDeployConfig = async ( }, FraudVerifier: { factory: await ethers.getContractFactory('FraudVerifier'), - params: [addressResolver.address, true], + params: [addressResolver.address], }, } } diff --git a/packages/contracts/tsconfig.json b/packages/contracts/tsconfig.json index 5ee2b35bcb556..bf02bc2232778 100644 --- a/packages/contracts/tsconfig.json +++ b/packages/contracts/tsconfig.json @@ -11,6 +11,7 @@ "./buidler.config.ts", "./buidler-env.d.ts", "./node_modules/@nomiclabs/buidler-ethers/src/type-extensions.d.ts", - "./node_modules/@nomiclabs/buidler-waffle/src/type-extensions.d.ts" + "./node_modules/@nomiclabs/buidler-waffle/src/type-extensions.d.ts", + "./node_modules/@nomiclabs/buidler-solpp/src/type-extensions.d.ts" ] } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index b2fe00b79bc1e..c1f89d9621f36 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1229,6 +1229,14 @@ resolved "https://registry.yarnpkg.com/@nomiclabs/buidler-ethers/-/buidler-ethers-2.0.0.tgz#f972df98a5d9f32fce974749f3ba9d964f057f2a" integrity sha512-Lf5XLClEeWYo6jVrGAqGBAcKTOP6IAChAR4qcDS36BkQnWakoRKcoSbwhr2YmTNTRAvgDWTmjQYbV17udJ+Alw== +"@nomiclabs/buidler-solpp@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@nomiclabs/buidler-solpp/-/buidler-solpp-1.3.3.tgz#8342cffbeccc8c69289e85ffe4410e26a6126147" + integrity sha512-2JybosKJ1tzWk5drUJJ5tPaPUj5/8mzXOMggvDAGDlgb9Nmo1kXTrwASU6zo5kNdsIaKpghPC4z5qJi4+V7E4Q== + dependencies: + fs-extra "^7.0.1" + solpp "^0.10.1" + "@nomiclabs/buidler-waffle@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@nomiclabs/buidler-waffle/-/buidler-waffle-2.0.0.tgz#425dd952898e63494b696f9556896975603684ff" @@ -1902,6 +1910,11 @@ ansi-wrap@0.1.0, ansi-wrap@^0.1.0: resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= +antlr4@^4.7.1: + version "4.8.0" + resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.8.0.tgz#f938ec171be7fc2855cd3a533e87647185b32b6a" + integrity sha512-en/MxQ4OkPgGJQ3wD/muzj1uDnFSzdFIhc2+c6bHZokWkuBb6RRvFjpWhPxWLbgQvaEzldJZ0GSQpfSAaE3hqg== + any-promise@1.3.0, any-promise@^1.0.0, any-promise@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -2226,6 +2239,14 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2" integrity sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA== +axios@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3" + integrity sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g== + dependencies: + follow-redirects "1.5.10" + is-buffer "^2.0.2" + axios@^0.19.0: version "0.19.2" resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" @@ -2886,6 +2907,14 @@ bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.2, bluebird@^3.5.3, bluebird@^3. resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +bn-str-256@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/bn-str-256/-/bn-str-256-1.9.1.tgz#898cebee70a3edc3968f97b4cebbc4771025aa82" + integrity sha512-u3muv3WO5sYv9nUQsPnDGLg731yNt/MOlKPK5pmBVqClcl7tY97tyfKxw8ed44HVrpi+7dkgJgQpbXP47a3GoQ== + dependencies: + decimal.js-light "^2.5.0" + lodash "^4.17.11" + bn.js@4.11.6: version "4.11.6" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" @@ -3624,7 +3653,7 @@ commander@3.0.2: resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== -commander@^2.12.1, commander@^2.8.1: +commander@^2.12.1, commander@^2.19.0, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -3902,6 +3931,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +cross-env@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.2.tgz#bd5ed31339a93a3418ac4f3ca9ca3403082ae5f9" + integrity sha512-KZP/bMEOJEDCkDQAyRhu3RL2ZO/SUVrxQVI0G3YEQ+OLbRA3c6zgixe8Mq8a/z7+HKlNEjo8oiLUs8iRijY2Rw== + dependencies: + cross-spawn "^7.0.1" + cross-fetch@^2.1.0, cross-fetch@^2.1.1: version "2.2.3" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.3.tgz#e8a0b3c54598136e037f8650f8e823ccdfac198e" @@ -3930,6 +3966,15 @@ cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + crypto-browserify@3.12.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -4037,6 +4082,11 @@ decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decimal.js-light@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.0.tgz#ca7faf504c799326df94b0ab920424fdfc125348" + integrity sha512-b3VJCbd2hwUpeRGG3Toob+CRo8W22xplipNhP3tN7TSVB/cyMX71P1vM2Xjc9H74uV6dS2hDDmo/rHq8L87Upg== + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -5093,7 +5143,7 @@ ethereumjs-util@^4.3.0: ethereum-cryptography "^0.1.3" rlp "^2.0.0" -ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5, ethereumjs-util@^5.2.0: +ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5: version "5.2.1" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz#a833f0e5fca7e5b361384dc76301a721f537bf65" integrity sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ== @@ -7048,7 +7098,7 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@~2.0.3: +is-buffer@^2.0.2, is-buffer@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== @@ -7863,15 +7913,6 @@ level-ws@0.0.0: readable-stream "~1.0.15" xtend "~2.1.1" -level-ws@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-1.0.0.tgz#19a22d2d4ac57b18cc7c6ecc4bd23d899d8f603b" - integrity sha512-RXEfCmkd6WWFlArh3X8ONvQPm8jNpfA0s/36M4QzLqrLEIt1iJE9WBHLZ5vZJK6haMjJPJGJCQWfjMNnRcq/9Q== - dependencies: - inherits "^2.0.3" - readable-stream "^2.2.8" - xtend "^4.0.1" - level-ws@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-2.0.0.tgz#207a07bcd0164a0ec5d62c304b4615c54436d339" @@ -8421,20 +8462,7 @@ merkle-patricia-tree@2.3.2, merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2 rlp "^2.0.0" semaphore ">=1.0.1" -merkle-patricia-tree@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-3.0.0.tgz#448d85415565df72febc33ca362b8b614f5a58f8" - integrity sha512-soRaMuNf/ILmw3KWbybaCjhx86EYeBbD8ph0edQCTed0JN/rxDt1EBN52Ajre3VyGo+91f8+/rfPIRQnnGMqmQ== - dependencies: - async "^2.6.1" - ethereumjs-util "^5.2.0" - level-mem "^3.0.1" - level-ws "^1.0.0" - readable-stream "^3.0.6" - rlp "^2.0.0" - semaphore ">=1.0.1" - -"merkle-patricia-tree@git+https://github.com/kfichter/merkle-patricia-tree": +merkle-patricia-tree@^3.0.0, "merkle-patricia-tree@git+https://github.com/kfichter/merkle-patricia-tree": version "3.0.0" resolved "git+https://github.com/kfichter/merkle-patricia-tree#ebd10c405be8ae909f1f82dea275a0e9ec1c8e46" dependencies: @@ -8830,7 +8858,7 @@ mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -mz@^2.5.0, mz@^2.6.0: +mz@^2.5.0, mz@^2.6.0, mz@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== @@ -9640,6 +9668,11 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" @@ -10233,7 +10266,7 @@ read@1, read@~1.0.1: dependencies: mute-stream "~0.0.4" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.8, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -10246,7 +10279,7 @@ read@1, read@~1.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -"readable-stream@2 || 3", readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.0, readable-stream@^3.4.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@^3.0.2, readable-stream@^3.1.0, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -10941,11 +10974,23 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + shelljs@^0.8.3: version "0.8.4" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" @@ -11131,6 +11176,21 @@ solidity-coverage@^0.7.9: shelljs "^0.8.3" web3 "1.2.6" +solpp@^0.10.1: + version "0.10.2" + resolved "https://registry.yarnpkg.com/solpp/-/solpp-0.10.2.tgz#fb25c9032ccd9396c5169a4a9feabe1d402524b3" + integrity sha512-NtDgMzJXVD7NofxolLDspvukEMA++tktab7pobGI11xf46LXSOWICthVzfA+bmItcGAnFcNKwDX6s1GqNlf6HQ== + dependencies: + antlr4 "^4.7.1" + axios "^0.18.0" + bn-str-256 "^1.9.1" + commander "^2.19.0" + ethereumjs-util "^6.0.0" + lodash "^4.17.11" + mz "^2.7.0" + resolve "^1.10.0" + semver "^5.6.0" + sort-keys-length@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188" @@ -13970,7 +14030,7 @@ which@1.3.1, which@^1.1.1, which@^1.2.14, which@^1.2.9, which@^1.3.1: dependencies: isexe "^2.0.0" -which@2.0.2: +which@2.0.2, which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== From c7d09b922791a3c2cecfabbb69ab5a3c0d3a51e5 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Mon, 27 Jul 2020 16:57:23 -0400 Subject: [PATCH 06/24] Linted files --- packages/contracts/buidler.config.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/contracts/buidler.config.ts b/packages/contracts/buidler.config.ts index c47b18fc97a87..545df30c2b35f 100644 --- a/packages/contracts/buidler.config.ts +++ b/packages/contracts/buidler.config.ts @@ -14,7 +14,7 @@ import './plugins/hijack-compiler' const parseSolppFlags = (): { [flag: string]: boolean } => { const flags: { [flag: string]: boolean } = {} - + const solppEnv = process.env.SOLPP_FLAGS if (!solppEnv) { return flags @@ -43,9 +43,9 @@ const config: BuidlerConfig = { }, solpp: { defs: { - ...parseSolppFlags() - } - } + ...parseSolppFlags(), + }, + }, } export default config From ac0e280a142d76ff758e5c31e5a8163a151a0c15 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Mon, 27 Jul 2020 17:52:17 -0400 Subject: [PATCH 07/24] Converted libraries to real libraries --- .../ovm/ExecutionManager.sol | 51 ++-- .../optimistic-ethereum/ovm/FraudVerifier.sol | 1 - .../ovm/StateTransitioner.sol | 20 +- .../test-helpers/StubStateTransitioner.sol | 5 +- .../libraries/ContractAddressGenerator.sol | 40 +-- .../utils/libraries/DataTypes.sol | 2 +- .../utils/libraries/EthMerkleTrie.sol | 47 ++-- .../utils/libraries/MerkleTrie.sol | 63 ++--- .../mocks/MockContractAddressGenerator.sol | 36 +++ .../libraries/mocks/MockEthMerkleTrie.sol | 236 ++++++++++++++++++ .../utils/libraries/mocks/MockMerkleTrie.sol | 76 ++++++ .../utils/libraries/mocks/MockRLPWriter.sol | 77 ++++++ .../src/deployment/default-config.ts | 15 -- packages/contracts/src/deployment/types.ts | 12 - .../{utils => ovm}/SafetyChecker.spec.ts | 0 .../utils/ContractAddressGenerator.spec.ts | 2 +- .../contracts/utils/EthMerkleTrie.spec.ts | 2 +- .../test/contracts/utils/MerkleTrie.spec.ts | 3 +- .../{RLPEncode.spec.ts => RLPWriter.spec.ts} | 4 +- .../test/test-helpers/resolution/config.ts | 12 - 20 files changed, 521 insertions(+), 183 deletions(-) create mode 100644 packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockContractAddressGenerator.sol create mode 100644 packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockEthMerkleTrie.sol create mode 100644 packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockMerkleTrie.sol create mode 100644 packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockRLPWriter.sol rename packages/contracts/test/contracts/{utils => ovm}/SafetyChecker.spec.ts (100%) rename packages/contracts/test/contracts/utils/{RLPEncode.spec.ts => RLPWriter.spec.ts} (94%) diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/ExecutionManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/ExecutionManager.sol index 6612b338efcda..fb2e73ada65df 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/ExecutionManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/ExecutionManager.sol @@ -11,7 +11,7 @@ import { SafetyChecker } from "./SafetyChecker.sol"; import { ContractResolver } from "../utils/resolvers/ContractResolver.sol"; import { DataTypes } from "../utils/libraries/DataTypes.sol"; import { ContractAddressGenerator } from "../utils/libraries/ContractAddressGenerator.sol"; -import { RLPEncode } from "../utils/libraries/RLPEncode.sol"; +import { RLPWriter } from "../utils/libraries/RLPWriter.sol"; /* Testing Imports */ import { StubSafetyChecker } from "./test-helpers/StubSafetyChecker.sol"; @@ -246,8 +246,7 @@ contract ExecutionManager is ContractResolver { callSize = _callBytes.length + 4; // Emit event that we are creating a contract with an EOA - ContractAddressGenerator contractAddressGenerator = resolveContractAddressGenerator(); - address _newOvmContractAddress = contractAddressGenerator.getAddressFromCREATE(_fromAddress, _nonce); + address _newOvmContractAddress = ContractAddressGenerator.getAddressFromCREATE(_fromAddress, _nonce); emit EOACreatedContract(_newOvmContractAddress); } else { methodId = METHOD_ID_OVM_CALL; @@ -328,27 +327,25 @@ contract ExecutionManager is ContractResolver { view returns (address) { - RLPEncode rlp = resolveRLPEncode(); - bytes[] memory message = new bytes[](9); - message[0] = rlp.encodeUint(_nonce); // Nonce - message[1] = rlp.encodeUint(0); // Gas price - message[2] = rlp.encodeUint(executionContext.gasLimit); // Gas limit + message[0] = RLPWriter.encodeUint(_nonce); // Nonce + message[1] = RLPWriter.encodeUint(0); // Gas price + message[2] = RLPWriter.encodeUint(executionContext.gasLimit); // Gas limit // To -- Special rlp encoding handling if _to is the ZERO_ADDRESS if (_to == ZERO_ADDRESS) { - message[3] = rlp.encodeUint(0); + message[3] = RLPWriter.encodeUint(0); } else { - message[3] = rlp.encodeAddress(_to); + message[3] = RLPWriter.encodeAddress(_to); } - message[4] = rlp.encodeUint(0); // Value - message[5] = rlp.encodeBytes(_callData); // Data - message[6] = rlp.encodeUint(executionContext.chainId); // ChainID - message[7] = rlp.encodeUint(0); // Zeros for R - message[8] = rlp.encodeUint(0); // Zeros for S + message[4] = RLPWriter.encodeUint(0); // Value + message[5] = RLPWriter.encodeBytes(_callData); // Data + message[6] = RLPWriter.encodeUint(executionContext.chainId); // ChainID + message[7] = RLPWriter.encodeUint(0); // Zeros for R + message[8] = RLPWriter.encodeUint(0); // Zeros for S - bytes memory encodedMessage = rlp.encodeList(message); + bytes memory encodedMessage = RLPWriter.encodeList(message); bytes32 hash = keccak256(abi.encodePacked(encodedMessage)); /* @@ -625,8 +622,7 @@ contract ExecutionManager is ContractResolver { // First we need to generate the CREATE address address creator = executionContext.ovmActiveContract; uint creatorNonce = stateManager.getOvmContractNonce(creator); - ContractAddressGenerator contractAddressGenerator = resolveContractAddressGenerator(); - address _newOvmContractAddress = contractAddressGenerator.getAddressFromCREATE(creator, creatorNonce); + address _newOvmContractAddress = ContractAddressGenerator.getAddressFromCREATE(creator, creatorNonce); // Next we need to actually create the contract in our state at that address createNewContract(_newOvmContractAddress, _ovmInitcode); @@ -689,8 +685,7 @@ contract ExecutionManager is ContractResolver { // First we need to generate the CREATE2 address address creator = executionContext.ovmActiveContract; - ContractAddressGenerator contractAddressGenerator = resolveContractAddressGenerator(); - address _newOvmContractAddress = contractAddressGenerator.getAddressFromCREATE2(creator, _salt, _ovmInitcode); + address _newOvmContractAddress = ContractAddressGenerator.getAddressFromCREATE2(creator, _salt, _ovmInitcode); // Next we need to actually create the contract in our state at that address createNewContract(_newOvmContractAddress, _ovmInitcode); @@ -1277,22 +1272,6 @@ contract ExecutionManager is ContractResolver { return SafetyChecker(resolveContract("SafetyChecker")); } - function resolveContractAddressGenerator() - internal - view - returns (ContractAddressGenerator) - { - return ContractAddressGenerator(resolveContract("ContractAddressGenerator")); - } - - function resolveRLPEncode() - internal - view - returns (RLPEncode) - { - return RLPEncode(resolveContract("RLPEncode")); - } - function resolveStateManager() internal view diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/FraudVerifier.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/FraudVerifier.sol index 71f89bb7c734f..ec01104b928bb 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/FraudVerifier.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/FraudVerifier.sol @@ -27,7 +27,6 @@ contract FraudVerifier is ContractResolver { */ mapping (uint256 => IStateTransitioner) public stateTransitioners; - bool private isTest; /* diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/StateTransitioner.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/StateTransitioner.sol index c59a3a3d2d4d3..8ed1e85ccdd12 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/StateTransitioner.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/StateTransitioner.sol @@ -131,9 +131,8 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { codeHash := extcodehash(_codeContractAddress) } - EthMerkleTrie ethMerkleTrie = resolveEthMerkleTrie(); require ( - ethMerkleTrie.proveAccountState( + EthMerkleTrie.proveAccountState( _ovmContractAddress, DataTypes.AccountState({ nonce: _nonce, @@ -187,9 +186,8 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { "Contract must be verified before proving storage!" ); - EthMerkleTrie ethMerkleTrie = resolveEthMerkleTrie(); require ( - ethMerkleTrie.proveAccountStorageSlotValue( + EthMerkleTrie.proveAccountStorageSlotValue( _ovmContractAddress, _slot, _value, @@ -276,8 +274,7 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { bytes32 storageSlotValue ) = stateManager.popUpdatedStorageSlot(); - EthMerkleTrie ethMerkleTrie = resolveEthMerkleTrie(); - stateRoot = ethMerkleTrie.updateAccountStorageSlotValue( + stateRoot = EthMerkleTrie.updateAccountStorageSlotValue( storageSlotContract, storageSlotKey, storageSlotValue, @@ -306,8 +303,7 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { bytes32 codeHash ) = stateManager.popUpdatedContract(); - EthMerkleTrie ethMerkleTrie = resolveEthMerkleTrie(); - stateRoot = ethMerkleTrie.updateAccountState( + stateRoot = EthMerkleTrie.updateAccountState( ovmContractAddress, DataTypes.AccountState({ nonce: contractNonce, @@ -370,12 +366,4 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { { return ExecutionManager(resolveContract("ExecutionManager")); } - - function resolveEthMerkleTrie() - internal - view - returns (EthMerkleTrie) - { - return EthMerkleTrie(resolveContract("EthMerkleTrie")); - } } diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubStateTransitioner.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubStateTransitioner.sol index e55b2cadadc96..26acf66c52284 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubStateTransitioner.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/test-helpers/StubStateTransitioner.sol @@ -1,11 +1,14 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; +/* Contract Imports */ import { FraudVerifier } from "../FraudVerifier.sol"; import { ExecutionManager } from "../ExecutionManager.sol"; +import { IStateTransitioner } from "../interfaces/IStateTransitioner.sol"; + +/* Library Imports */ import { ContractResolver } from "../../utils/resolvers/ContractResolver.sol"; import { DataTypes } from "../../utils/libraries/DataTypes.sol"; -import { IStateTransitioner } from "../interfaces/IStateTransitioner.sol"; /** * @title StubStateTransitioner diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/ContractAddressGenerator.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/ContractAddressGenerator.sol index 5cb36b9a320e7..6236c61910ff1 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/ContractAddressGenerator.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/ContractAddressGenerator.sol @@ -2,7 +2,7 @@ pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; /* Internal Imports */ -import { RLPEncode } from "./RLPEncode.sol"; +import { RLPWriter } from "./RLPWriter.sol"; /** * @title ContractAddressGenerator @@ -10,27 +10,9 @@ import { RLPEncode } from "./RLPEncode.sol"; * This is used in Rollup to make sure we have address parity with the * Ethereum mainchain. */ -contract ContractAddressGenerator { +library ContractAddressGenerator { /* - * Contract Variables - */ - - RLPEncode private rlp; - - - /* - * Constructor - */ - - constructor() - public - { - rlp = new RLPEncode(); - } - - - /* - * Public Functions + * Internal Functions */ /** @@ -44,17 +26,17 @@ contract ContractAddressGenerator { address _origin, uint _nonce ) - public - view + internal + pure returns (address) { // Create a list of RLP encoded parameters. bytes[] memory list = new bytes[](2); - list[0] = rlp.encodeAddress(_origin); - list[1] = rlp.encodeUint(_nonce); + list[0] = RLPWriter.encodeAddress(_origin); + list[1] = RLPWriter.encodeUint(_nonce); // RLP encode the list itself. - bytes memory encodedList = rlp.encodeList(list); + bytes memory encodedList = RLPWriter.encodeList(list); // Return an address from the hash of the encoded list. return getAddressFromHash(keccak256(encodedList)); @@ -73,7 +55,7 @@ contract ContractAddressGenerator { bytes32 _salt, bytes memory _ovmInitcode ) - public + internal pure returns (address) { @@ -90,7 +72,7 @@ contract ContractAddressGenerator { /* - * Internal Functions + * Private Functions */ /** @@ -103,7 +85,7 @@ contract ContractAddressGenerator { function getAddressFromHash( bytes32 _hash ) - internal + private pure returns (address) { diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/DataTypes.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/DataTypes.sol index 19035b7b5ab32..d5e8c10d75f9b 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/DataTypes.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/DataTypes.sol @@ -5,7 +5,7 @@ pragma experimental ABIEncoderV2; * @title DataTypes * @notice Main data structures which to be used in rollup smart contracts. */ -contract DataTypes { +library DataTypes { struct L2ToL1Message { address ovmSender; bytes callData; diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/EthMerkleTrie.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/EthMerkleTrie.sol index 5f548b34bc690..9ca0b504041fa 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/EthMerkleTrie.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/EthMerkleTrie.sol @@ -11,17 +11,18 @@ import { DataTypes } from "./DataTypes.sol"; /** * @notice Convenience wrapper for ETH-related trie operations. */ -contract EthMerkleTrie is MerkleTrie { +library EthMerkleTrie { /* * Contract Constants */ bytes32 constant private BYTES32_NULL = bytes32(''); uint256 constant private UINT256_NULL = uint256(0); + bytes constant private RLP_NULL_BYTES = hex'80'; /* - * Public Functions + * Internal Functions */ /** @@ -44,7 +45,7 @@ contract EthMerkleTrie is MerkleTrie { bytes memory _storageTrieWitness, bytes32 _stateTrieRoot ) - public + internal pure returns (bool) { @@ -56,7 +57,7 @@ contract EthMerkleTrie is MerkleTrie { ); // Verify inclusion of the given k/v pair in the storage trie. - return verifyInclusionProof( + return MerkleTrie.verifyInclusionProof( abi.encodePacked(_key), abi.encodePacked(_value), _storageTrieWitness, @@ -84,7 +85,7 @@ contract EthMerkleTrie is MerkleTrie { bytes memory _storageTrieWitness, bytes32 _stateTrieRoot ) - public + internal pure returns (bytes32) { @@ -96,7 +97,7 @@ contract EthMerkleTrie is MerkleTrie { ); // Generate a new storage root. - accountState.storageRoot = update( + accountState.storageRoot = MerkleTrie.update( abi.encodePacked(_key), abi.encodePacked(_value), _storageTrieWitness, @@ -129,7 +130,7 @@ contract EthMerkleTrie is MerkleTrie { bytes memory _stateTrieWitness, bytes32 _stateTrieRoot ) - public + internal pure returns (bool) { @@ -164,7 +165,7 @@ contract EthMerkleTrie is MerkleTrie { bytes memory _stateTrieWitness, bytes32 _stateTrieRoot ) - public + internal pure returns (bool) { @@ -202,7 +203,7 @@ contract EthMerkleTrie is MerkleTrie { bytes memory _stateTrieWitness, bytes32 _stateTrieRoot ) - public + internal pure returns (bool) { @@ -240,7 +241,7 @@ contract EthMerkleTrie is MerkleTrie { bytes memory _stateTrieWitness, bytes32 _stateTrieRoot ) - public + internal pure returns (bool) { @@ -278,7 +279,7 @@ contract EthMerkleTrie is MerkleTrie { bytes memory _stateTrieWitness, bytes32 _stateTrieRoot ) - public + internal pure returns (bool) { @@ -318,7 +319,7 @@ contract EthMerkleTrie is MerkleTrie { bytes memory _stateTrieWitness, bytes32 _stateTrieRoot ) - public + internal pure returns (bytes32) { @@ -383,7 +384,7 @@ contract EthMerkleTrie is MerkleTrie { bytes memory _stateTrieWitness, bytes32 _stateTrieRoot ) - public + internal pure returns (bytes32) { @@ -421,7 +422,7 @@ contract EthMerkleTrie is MerkleTrie { bytes memory _stateTrieWitness, bytes32 _stateTrieRoot ) - public + internal pure returns (bytes32) { @@ -459,7 +460,7 @@ contract EthMerkleTrie is MerkleTrie { bytes memory _stateTrieWitness, bytes32 _stateTrieRoot ) - public + internal pure returns (bytes32) { @@ -497,7 +498,7 @@ contract EthMerkleTrie is MerkleTrie { bytes memory _stateTrieWitness, bytes32 _stateTrieRoot ) - public + internal pure returns (bytes32) { @@ -522,7 +523,7 @@ contract EthMerkleTrie is MerkleTrie { /* - * Internal Functions + * Private Functions */ /** @@ -533,7 +534,7 @@ contract EthMerkleTrie is MerkleTrie { function decodeAccountState( bytes memory _encodedAccountState ) - internal + private pure returns (DataTypes.AccountState memory) { @@ -555,7 +556,7 @@ contract EthMerkleTrie is MerkleTrie { function encodeAccountState( DataTypes.AccountState memory _accountState ) - internal + private pure returns (bytes memory) { @@ -584,7 +585,7 @@ contract EthMerkleTrie is MerkleTrie { bytes memory _stateTrieWitness, bytes32 _stateTrieRoot ) - internal + private pure returns (DataTypes.AccountState memory) { @@ -598,7 +599,7 @@ contract EthMerkleTrie is MerkleTrie { ( bool exists, bytes memory encodedAccountState - ) = get( + ) = MerkleTrie.get( abi.encodePacked(_address), _stateTrieWitness, _stateTrieRoot @@ -622,13 +623,13 @@ contract EthMerkleTrie is MerkleTrie { bytes memory _stateTrieWitness, bytes32 _stateTrieRoot ) - internal + private pure returns (bytes32) { bytes memory encodedAccountState = encodeAccountState(_accountState); - return update( + return MerkleTrie.update( abi.encodePacked(_address), encodedAccountState, _stateTrieWitness, diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/MerkleTrie.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/MerkleTrie.sol index cbdb25c19454d..73b27ece64dfd 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/MerkleTrie.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/MerkleTrie.sol @@ -8,7 +8,7 @@ import { RLPWriter } from "./RLPWriter.sol"; /** * @title MerkleTrie */ -contract MerkleTrie { +library MerkleTrie { /* * Data Structures */ @@ -52,7 +52,7 @@ contract MerkleTrie { /* - * Public Functions + * Internal Functions */ /** @@ -73,7 +73,7 @@ contract MerkleTrie { bytes memory _proof, bytes32 _root ) - public + internal pure returns (bool) { @@ -98,7 +98,7 @@ contract MerkleTrie { bytes memory _proof, bytes32 _root ) - public + internal pure returns (bool) { @@ -122,7 +122,7 @@ contract MerkleTrie { bytes memory _proof, bytes32 _root ) - public + internal pure returns (bytes32) { @@ -146,7 +146,7 @@ contract MerkleTrie { bytes memory _proof, bytes32 _root ) - public + internal pure returns (bool, bytes memory) { @@ -162,8 +162,9 @@ contract MerkleTrie { ); } + /* - * Internal Functions + * Private Functions */ /** @@ -187,7 +188,7 @@ contract MerkleTrie { bytes32 _root, bool _inclusion ) - internal + private pure returns (bool) { @@ -226,7 +227,7 @@ contract MerkleTrie { bytes memory _key, bytes32 _root ) - internal + private pure returns ( uint256, @@ -345,7 +346,7 @@ contract MerkleTrie { bytes memory _keyRemainder, bytes memory _value ) - internal + private pure returns (TrieNode[] memory) { @@ -469,7 +470,7 @@ contract MerkleTrie { TrieNode[] memory _nodes, bytes memory _key ) - internal + private pure returns (bytes32) { @@ -530,7 +531,7 @@ contract MerkleTrie { function parseProof( bytes memory _proof ) - internal + private pure returns (TrieNode[] memory) { @@ -558,7 +559,7 @@ contract MerkleTrie { function getNodeID( RLPReader.RLPItem memory _node ) - internal + private pure returns (bytes32) { @@ -583,7 +584,7 @@ contract MerkleTrie { function getNodePath( TrieNode memory _node ) - internal + private pure returns (bytes memory) { @@ -599,7 +600,7 @@ contract MerkleTrie { function getNodeKey( TrieNode memory _node ) - internal + private pure returns (bytes memory) { @@ -614,7 +615,7 @@ contract MerkleTrie { function getNodeValue( TrieNode memory _node ) - internal + private pure returns (bytes memory) { @@ -630,7 +631,7 @@ contract MerkleTrie { function getNodeHash( bytes memory _encoded ) - internal + private pure returns (bytes memory) { @@ -649,7 +650,7 @@ contract MerkleTrie { function getNodeType( TrieNode memory _node ) - internal + private pure returns (NodeType) { @@ -680,7 +681,7 @@ contract MerkleTrie { bytes memory _a, bytes memory _b ) - internal + private pure returns (uint256) { @@ -699,7 +700,7 @@ contract MerkleTrie { function makeNode( bytes[] memory _raw ) - internal + private pure returns (TrieNode memory) { @@ -719,7 +720,7 @@ contract MerkleTrie { function makeNode( RLPReader.RLPItem[] memory _items ) - internal + private pure returns (TrieNode memory) { @@ -739,8 +740,8 @@ contract MerkleTrie { function makeExtensionNode( bytes memory _key, bytes memory _value - ) - internal + ) + private pure returns (TrieNode memory) { @@ -763,8 +764,8 @@ contract MerkleTrie { function makeLeafNode( bytes memory _key, bytes memory _value - ) - internal + ) + private pure returns (TrieNode memory) { @@ -780,7 +781,7 @@ contract MerkleTrie { * @return Empty branch node as a TrieNode stuct. */ function makeEmptyBranchNode() - internal + private pure returns (TrieNode memory) { @@ -801,7 +802,7 @@ contract MerkleTrie { TrieNode memory _branch, bytes memory _value ) - internal + private pure returns (TrieNode memory) { @@ -822,7 +823,7 @@ contract MerkleTrie { uint8 _index, bytes memory _value ) - internal + private pure returns (TrieNode memory) { @@ -841,7 +842,7 @@ contract MerkleTrie { bytes memory _key, bool _isLeaf ) - internal + private pure returns (bytes memory) { @@ -860,7 +861,7 @@ contract MerkleTrie { function removeHexPrefix( bytes memory _path ) - internal + private pure returns (bytes memory) { @@ -887,7 +888,7 @@ contract MerkleTrie { TrieNode[] memory _b, uint256 _bLength ) - internal + private pure returns (TrieNode[] memory) { diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockContractAddressGenerator.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockContractAddressGenerator.sol new file mode 100644 index 0000000000000..5f396f33d69f0 --- /dev/null +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockContractAddressGenerator.sol @@ -0,0 +1,36 @@ +pragma solidity ^0.5.0; + +/* Library Imports */ +import { ContractAddressGenerator } from "../ContractAddressGenerator.sol"; + +contract MockContractAddressGenerator { + function getAddressFromCREATE( + address _origin, + uint _nonce + ) + public + pure + returns (address) + { + return ContractAddressGenerator.getAddressFromCREATE( + _origin, + _nonce + ); + } + + function getAddressFromCREATE2( + address _origin, + bytes32 _salt, + bytes memory _ovmInitcode + ) + internal + pure + returns (address) + { + return ContractAddressGenerator.getAddressFromCREATE2( + _origin, + _salt, + _ovmInitcode + ); + } +} \ No newline at end of file diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockEthMerkleTrie.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockEthMerkleTrie.sol new file mode 100644 index 0000000000000..91fdd9a5e8abc --- /dev/null +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockEthMerkleTrie.sol @@ -0,0 +1,236 @@ +pragma solidity ^0.5.0; +pragma experimental ABIEncoderV2; + +/* Library Imports */ +import { DataTypes } from "../DataTypes.sol"; +import { EthMerkleTrie } from "../EthMerkleTrie.sol"; + +contract MockEthMerkleTrie { + function proveAccountStorageSlotValue( + address _address, + bytes32 _key, + bytes32 _value, + bytes memory _stateTrieWitness, + bytes memory _storageTrieWitness, + bytes32 _stateTrieRoot + ) + public + pure + returns (bool) + { + return EthMerkleTrie.proveAccountStorageSlotValue( + _address, + _key, + _value, + _stateTrieWitness, + _storageTrieWitness, + _stateTrieRoot + ); + } + + function updateAccountStorageSlotValue( + address _address, + bytes32 _key, + bytes32 _value, + bytes memory _stateTrieWitness, + bytes memory _storageTrieWitness, + bytes32 _stateTrieRoot + ) + public + pure + returns (bytes32) + { + return EthMerkleTrie.updateAccountStorageSlotValue( + _address, + _key, + _value, + _stateTrieWitness, + _storageTrieWitness, + _stateTrieRoot + ); + } + + function proveAccountState( + address _address, + DataTypes.AccountState memory _accountState, + DataTypes.ProofMatrix memory _proofMatrix, + bytes memory _stateTrieWitness, + bytes32 _stateTrieRoot + ) + public + pure + returns (bool) + { + return EthMerkleTrie.proveAccountState( + _address, + _accountState, + _proofMatrix, + _stateTrieWitness, + _stateTrieRoot + ); + } + + function proveAccountNonce( + address _address, + uint256 _nonce, + bytes memory _stateTrieWitness, + bytes32 _stateTrieRoot + ) + public + pure + returns (bool) + { + return EthMerkleTrie.proveAccountNonce( + _address, + _nonce, + _stateTrieWitness, + _stateTrieRoot + ); + } + + function proveAccountBalance( + address _address, + uint256 _balance, + bytes memory _stateTrieWitness, + bytes32 _stateTrieRoot + ) + public + pure + returns (bool) + { + return EthMerkleTrie.proveAccountBalance( + _address, + _balance, + _stateTrieWitness, + _stateTrieRoot + ); + } + + function proveAccountStorageRoot( + address _address, + bytes32 _storageRoot, + bytes memory _stateTrieWitness, + bytes32 _stateTrieRoot + ) + public + pure + returns (bool) + { + return EthMerkleTrie.proveAccountStorageRoot( + _address, + _storageRoot, + _stateTrieWitness, + _stateTrieRoot + ); + } + + function proveAccountCodeHash( + address _address, + bytes32 _codeHash, + bytes memory _stateTrieWitness, + bytes32 _stateTrieRoot + ) + public + pure + returns (bool) + { + return EthMerkleTrie.proveAccountCodeHash( + _address, + _codeHash, + _stateTrieWitness, + _stateTrieRoot + ); + } + + function updateAccountState( + address _address, + DataTypes.AccountState memory _accountState, + DataTypes.ProofMatrix memory _proofMatrix, + bytes memory _stateTrieWitness, + bytes32 _stateTrieRoot + ) + public + pure + returns (bytes32) + { + return EthMerkleTrie.updateAccountState( + _address, + _accountState, + _proofMatrix, + _stateTrieWitness, + _stateTrieRoot + ); + } + + function updateAccountNonce( + address _address, + uint256 _nonce, + bytes memory _stateTrieWitness, + bytes32 _stateTrieRoot + ) + public + pure + returns (bytes32) + { + return EthMerkleTrie.updateAccountNonce( + _address, + _nonce, + _stateTrieWitness, + _stateTrieRoot + ); + } + + function updateAccountBalance( + address _address, + uint256 _balance, + bytes memory _stateTrieWitness, + bytes32 _stateTrieRoot + ) + public + pure + returns (bytes32) + { + return EthMerkleTrie.updateAccountBalance( + _address, + _balance, + _stateTrieWitness, + _stateTrieRoot + ); + } + + function updateAccountStorageRoot( + address _address, + bytes32 _storageRoot, + bytes memory _stateTrieWitness, + bytes32 _stateTrieRoot + ) + public + pure + returns (bytes32) + { + return EthMerkleTrie.updateAccountStorageRoot( + _address, + _storageRoot, + _stateTrieWitness, + _stateTrieRoot + ); + } + + function updateAccountCodeHash( + address _address, + bytes32 _codeHash, + bytes memory _stateTrieWitness, + bytes32 _stateTrieRoot + ) + public + pure + returns (bytes32) + { + return EthMerkleTrie.updateAccountCodeHash( + _address, + _codeHash, + _stateTrieWitness, + _stateTrieRoot + ); + } +} \ No newline at end of file diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockMerkleTrie.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockMerkleTrie.sol new file mode 100644 index 0000000000000..f269d9e0bfb88 --- /dev/null +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockMerkleTrie.sol @@ -0,0 +1,76 @@ +pragma solidity ^0.5.0; + +/* Library Imports */ +import { MerkleTrie } from "../MerkleTrie.sol"; + +contract MockMerkleTrie { + function verifyInclusionProof( + bytes memory _key, + bytes memory _value, + bytes memory _proof, + bytes32 _root + ) + public + pure + returns (bool) + { + return MerkleTrie.verifyInclusionProof( + _key, + _value, + _proof, + _root + ); + } + + function verifyExclusionProof( + bytes memory _key, + bytes memory _value, + bytes memory _proof, + bytes32 _root + ) + public + pure + returns (bool) + { + return MerkleTrie.verifyExclusionProof( + _key, + _value, + _proof, + _root + ); + } + + function update( + bytes memory _key, + bytes memory _value, + bytes memory _proof, + bytes32 _root + ) + public + pure + returns (bytes32) + { + return MerkleTrie.update( + _key, + _value, + _proof, + _root + ); + } + + function get( + bytes memory _key, + bytes memory _proof, + bytes32 _root + ) + public + pure + returns (bool, bytes memory) + { + return MerkleTrie.get( + _key, + _proof, + _root + ); + } +} \ No newline at end of file diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockRLPWriter.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockRLPWriter.sol new file mode 100644 index 0000000000000..c0f0bb8ceac13 --- /dev/null +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockRLPWriter.sol @@ -0,0 +1,77 @@ +pragma solidity ^0.5.0; +pragma experimental ABIEncoderV2; + +/* Library Imports */ +import { RLPWriter } from "../RLPWriter.sol"; + +contract MockRLPWriter { + function encodeBytes( + bytes memory self + ) + internal + pure + returns (bytes memory) + { + return RLPWriter.encodeBytes(self); + } + + function encodeList( + bytes[] memory self + ) + internal + pure + returns (bytes memory) + { + return RLPWriter.encodeList(self); + } + + function encodeString( + string memory self + ) + internal + pure + returns (bytes memory) + { + return RLPWriter.encodeString(self); + } + + function encodeAddress( + address self + ) + internal + pure + returns (bytes memory) + { + return RLPWriter.encodeAddress(self); + } + + function encodeUint( + uint self + ) + internal + pure + returns (bytes memory) + { + return RLPWriter.encodeUint(self); + } + + function encodeInt( + int self + ) + internal + pure + returns (bytes memory) + { + return RLPWriter.encodeInt(self); + } + + function encodeBool( + bool self + ) + internal + pure + returns (bytes memory) + { + return RLPWriter.encodeBool(self); + } +} \ No newline at end of file diff --git a/packages/contracts/src/deployment/default-config.ts b/packages/contracts/src/deployment/default-config.ts index 097ba4dabfdb9..3cd4b5f810a77 100644 --- a/packages/contracts/src/deployment/default-config.ts +++ b/packages/contracts/src/deployment/default-config.ts @@ -69,21 +69,6 @@ export const getDefaultContractDeployConfig = async ( params: [addressResolver.address, true], signer: wallet, }, - ContractAddressGenerator: { - factory: getContractFactory('ContractAddressGenerator'), - params: [], - signer: wallet, - }, - EthMerkleTrie: { - factory: getContractFactory('EthMerkleTrie'), - params: [], - signer: wallet, - }, - RLPEncode: { - factory: getContractFactory('RLPEncode'), - params: [], - signer: wallet, - }, RollupMerkleUtils: { factory: getContractFactory('RollupMerkleUtils'), params: [], diff --git a/packages/contracts/src/deployment/types.ts b/packages/contracts/src/deployment/types.ts index 671cf7d8e14ff..af9e326f382bf 100644 --- a/packages/contracts/src/deployment/types.ts +++ b/packages/contracts/src/deployment/types.ts @@ -24,9 +24,6 @@ export type ContractFactoryName = | 'ExecutionManager' | 'SafetyChecker' | 'FraudVerifier' - | 'ContractAddressGenerator' - | 'EthMerkleTrie' - | 'RLPEncode' | 'RollupMerkleUtils' export interface ContractDeployConfig { @@ -38,9 +35,6 @@ export interface ContractDeployConfig { ExecutionManager: ContractDeployOptions SafetyChecker: ContractDeployOptions FraudVerifier: ContractDeployOptions - ContractAddressGenerator: ContractDeployOptions - EthMerkleTrie: ContractDeployOptions - RLPEncode: ContractDeployOptions RollupMerkleUtils: ContractDeployOptions } @@ -53,9 +47,6 @@ interface ContractMapping { executionManager: Contract safetyChecker: Contract fraudVerifier: Contract - contractAddressGenerator: Contract - ethMerkleTrie: Contract - rlpEncode: Contract rollupMerkleUtils: Contract } @@ -73,9 +64,6 @@ export const factoryToContractName = { ExecutionManager: 'executionManager', SafetyChecker: 'safetyChecker', FraudVerifier: 'fraudVerifier', - ContractAddressGenerator: 'contractAddressGenerator', - EthMerkleTrie: 'ethMerkleTrie', - RLPEncode: 'rlpEncode', RollupMerkleUtils: 'rollupMerkleUtils', } diff --git a/packages/contracts/test/contracts/utils/SafetyChecker.spec.ts b/packages/contracts/test/contracts/ovm/SafetyChecker.spec.ts similarity index 100% rename from packages/contracts/test/contracts/utils/SafetyChecker.spec.ts rename to packages/contracts/test/contracts/ovm/SafetyChecker.spec.ts diff --git a/packages/contracts/test/contracts/utils/ContractAddressGenerator.spec.ts b/packages/contracts/test/contracts/utils/ContractAddressGenerator.spec.ts index 38a85e3f68e16..85e7bbd9fcb37 100644 --- a/packages/contracts/test/contracts/utils/ContractAddressGenerator.spec.ts +++ b/packages/contracts/test/contracts/utils/ContractAddressGenerator.spec.ts @@ -19,7 +19,7 @@ describe('ContractAddressGenerator', () => { let ContractAddressGenerator: ContractFactory beforeEach(async () => { ContractAddressGenerator = await ethers.getContractFactory( - 'ContractAddressGenerator' + 'MockContractAddressGenerator' ) }) diff --git a/packages/contracts/test/contracts/utils/EthMerkleTrie.spec.ts b/packages/contracts/test/contracts/utils/EthMerkleTrie.spec.ts index 090f203687add..54a4ff9dbc7c8 100644 --- a/packages/contracts/test/contracts/utils/EthMerkleTrie.spec.ts +++ b/packages/contracts/test/contracts/utils/EthMerkleTrie.spec.ts @@ -23,7 +23,7 @@ describe('EthMerkleTrie', () => { let Trie: ContractFactory let trie: Contract before(async () => { - Trie = await ethers.getContractFactory('EthMerkleTrie') + Trie = await ethers.getContractFactory('MockEthMerkleTrie') trie = await Trie.deploy() }) diff --git a/packages/contracts/test/contracts/utils/MerkleTrie.spec.ts b/packages/contracts/test/contracts/utils/MerkleTrie.spec.ts index cc7e734b5b2d9..7c81c3a485f1a 100644 --- a/packages/contracts/test/contracts/utils/MerkleTrie.spec.ts +++ b/packages/contracts/test/contracts/utils/MerkleTrie.spec.ts @@ -21,12 +21,11 @@ describe('MerkleTrie', () => { let MerkleTrie: ContractFactory before(async () => { - MerkleTrie = await ethers.getContractFactory('MerkleTrie') + MerkleTrie = await ethers.getContractFactory('MockMerkleTrie') }) let merkleTrie: Contract beforeEach(async () => { - MerkleTrie = await ethers.getContractFactory('MerkleTrie') merkleTrie = await MerkleTrie.deploy() }) diff --git a/packages/contracts/test/contracts/utils/RLPEncode.spec.ts b/packages/contracts/test/contracts/utils/RLPWriter.spec.ts similarity index 94% rename from packages/contracts/test/contracts/utils/RLPEncode.spec.ts rename to packages/contracts/test/contracts/utils/RLPWriter.spec.ts index 69398205e19eb..3cf13ac1bbd5d 100644 --- a/packages/contracts/test/contracts/utils/RLPEncode.spec.ts +++ b/packages/contracts/test/contracts/utils/RLPWriter.spec.ts @@ -8,11 +8,11 @@ import { Contract, ContractFactory } from 'ethers' import { RLP_TEST_JSON } from '../../test-helpers' /* Tests */ -describe('RLP Encoder', () => { +describe('RLPWriter', () => { let RlpWriter: ContractFactory let rlpWriter: Contract before(async () => { - RlpWriter = await ethers.getContractFactory('RLPEncode') + RlpWriter = await ethers.getContractFactory('MockRLPWriter') rlpWriter = await RlpWriter.deploy() }) diff --git a/packages/contracts/test/test-helpers/resolution/config.ts b/packages/contracts/test/test-helpers/resolution/config.ts index e7c075a843bf0..4778be9c4f2b9 100644 --- a/packages/contracts/test/test-helpers/resolution/config.ts +++ b/packages/contracts/test/test-helpers/resolution/config.ts @@ -67,18 +67,6 @@ export const getDefaultDeployConfig = async ( */ export const getLibraryDeployConfig = async (): Promise => { return { - ContractAddressGenerator: { - factory: await ethers.getContractFactory('ContractAddressGenerator'), - params: [], - }, - EthMerkleTrie: { - factory: await ethers.getContractFactory('EthMerkleTrie'), - params: [], - }, - RLPEncode: { - factory: await ethers.getContractFactory('RLPEncode'), - params: [], - }, RollupMerkleUtils: { factory: await ethers.getContractFactory('RollupMerkleUtils'), params: [], From 1797677dd56357ece98af330171620ad3fc54d2d Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Mon, 27 Jul 2020 17:54:12 -0400 Subject: [PATCH 08/24] Fixed FraudVerifier deploy config --- packages/contracts/src/deployment/default-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/src/deployment/default-config.ts b/packages/contracts/src/deployment/default-config.ts index 3cd4b5f810a77..1d64843849ca1 100644 --- a/packages/contracts/src/deployment/default-config.ts +++ b/packages/contracts/src/deployment/default-config.ts @@ -66,7 +66,7 @@ export const getDefaultContractDeployConfig = async ( }, FraudVerifier: { factory: getContractFactory('FraudVerifier'), - params: [addressResolver.address, true], + params: [addressResolver.address], signer: wallet, }, RollupMerkleUtils: { From 410bcc925971f2dc61cd03dd8ac5c66d0fc0db79 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Tue, 28 Jul 2020 13:39:31 -0400 Subject: [PATCH 09/24] Made MockRLPWriter functions public --- .../mocks/MockContractAddressGenerator.sol | 2 +- .../utils/libraries/mocks/MockRLPWriter.sol | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockContractAddressGenerator.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockContractAddressGenerator.sol index 5f396f33d69f0..e5f5798641bbc 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockContractAddressGenerator.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockContractAddressGenerator.sol @@ -23,7 +23,7 @@ contract MockContractAddressGenerator { bytes32 _salt, bytes memory _ovmInitcode ) - internal + public pure returns (address) { diff --git a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockRLPWriter.sol b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockRLPWriter.sol index c0f0bb8ceac13..81f9904fc3d1b 100644 --- a/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockRLPWriter.sol +++ b/packages/contracts/contracts/optimistic-ethereum/utils/libraries/mocks/MockRLPWriter.sol @@ -8,7 +8,7 @@ contract MockRLPWriter { function encodeBytes( bytes memory self ) - internal + public pure returns (bytes memory) { @@ -18,7 +18,7 @@ contract MockRLPWriter { function encodeList( bytes[] memory self ) - internal + public pure returns (bytes memory) { @@ -28,7 +28,7 @@ contract MockRLPWriter { function encodeString( string memory self ) - internal + public pure returns (bytes memory) { @@ -38,7 +38,7 @@ contract MockRLPWriter { function encodeAddress( address self ) - internal + public pure returns (bytes memory) { @@ -48,7 +48,7 @@ contract MockRLPWriter { function encodeUint( uint self ) - internal + public pure returns (bytes memory) { @@ -58,7 +58,7 @@ contract MockRLPWriter { function encodeInt( int self ) - internal + public pure returns (bytes memory) { @@ -68,7 +68,7 @@ contract MockRLPWriter { function encodeBool( bool self ) - internal + public pure returns (bytes memory) { From a0ceba22b5e10ab15baa3f8690191789e4e467b4 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Tue, 28 Jul 2020 13:56:58 -0400 Subject: [PATCH 10/24] Added coverage scripts --- package.json | 1 + packages/contracts/package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 897154bde8d34..dacdd049614eb 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "test:integration": "wsrun -p $(yarn --silent run pkgparse:integration) --fast-exit --serial --no-prefix --exclude-missing --timeout 5000 test", "test:core": "wsrun -p $(yarn --silent run pkgparse:core) --fast-exit --parallel --no-prefix --exclude-missing --timeout 5000 test", "test": "yarn test:core && yarn test:integration", + "coverage": "wsrun -p $(yarn --silent run pkgparse:core) --fast-exit --parallel --no-prefix --exclude-missing --timeout 5000 coverage", "build": "yarn run patch && lerna link && wsrun -p $(yarn --silent run pkgparse) -r --fast-exit --stages --exclude-missing build", "all": "yarn clean && yarn build && yarn test && yarn fix && yarn lint", "release": "yarn clean && yarn run build && lesrna publish --force-publish --exact -m \"chore(@ethereum-optimism) publish %s release\"", diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 376ba7d091565..4074b0d379dae 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -25,7 +25,7 @@ "test": "yarn run test:contracts", "test:contracts": "cross-env SOLPP_FLAGS=\"FLAG_IS_TEST,FLAG_IS_DEBUG\" buidler test --show-stack-traces", "coverage": "yarn run coverage:contracts", - "coverage:contracts": "buidler coverage --network coverage --show-stack-traces --testfiles \"test/contracts/**/*.spec.ts\"", + "coverage:contracts": "cross-env SOLPP_FLAGS=\"FLAG_IS_TEST\" buidler coverage --network coverage --show-stack-traces --testfiles \"test/contracts/**/*.spec.ts\"", "build": "yarn run build:contracts && yarn run build:typescript", "build:contracts": "buidler compile", "build:typescript": "tsc -p .", From 38061b34ae7ea19b4e10762a914769df07e7211a Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Tue, 28 Jul 2020 15:07:28 -0400 Subject: [PATCH 11/24] Removed duplicated test helpers --- .../chain/CanonicalTransactionChain.spec.ts | 307 ++++-- .../chain/SequencerBatchSubmitter.spec.ts | 46 +- .../chain/StateCommitmentChain.spec.ts | 89 +- .../test/contracts/ovm/FraudVerifier.spec.ts | 122 +-- .../contracts/ovm/L2ExecutionManager.spec.ts | 14 +- .../contracts/ovm/PartialStateManager.spec.ts | 1 - .../test/contracts/ovm/SafetyChecker.spec.ts | 57 +- .../contracts/ovm/StateTransitioner.spec.ts | 50 +- .../ExecutionManager.call-opcodes.spec.ts | 215 ++-- .../ExecutionManager.code-opcodes.spec.ts | 3 - .../ExecutionManager.context-opcodes.spec.ts | 112 +- .../ExecutionManager.create-opcodes.spec.ts | 28 +- .../ExecutionManager.executeCall.spec.ts | 2 - .../ExecutionManager.l1-l2-opcodes.spec.ts | 85 +- ...ecutionManager.recover-eoa-address.spec.ts | 3 - .../ExecutionManager.storage-opcodes.spec.ts | 24 +- .../test/test-helpers/batch-helpers.ts | 182 ++++ .../contracts/test/test-helpers/constants.ts | 45 +- .../test/test-helpers/ethereum-helpers.ts | 8 + .../test-helpers/execution-manager-helpers.ts | 148 +++ packages/contracts/test/test-helpers/index.ts | 1 + .../test/test-helpers/ovm-helpers.ts | 41 +- .../test/test-helpers/types/index.ts | 1 - .../test/test-helpers/types/opcodes.ts | 990 ------------------ 24 files changed, 856 insertions(+), 1718 deletions(-) create mode 100644 packages/contracts/test/test-helpers/execution-manager-helpers.ts delete mode 100644 packages/contracts/test/test-helpers/types/opcodes.ts diff --git a/packages/contracts/test/contracts/chain/CanonicalTransactionChain.spec.ts b/packages/contracts/test/contracts/chain/CanonicalTransactionChain.spec.ts index ad79b75f49240..a4fd8c1ef5d0c 100644 --- a/packages/contracts/test/contracts/chain/CanonicalTransactionChain.spec.ts +++ b/packages/contracts/test/contracts/chain/CanonicalTransactionChain.spec.ts @@ -7,12 +7,17 @@ import { Contract, Signer, ContractFactory } from 'ethers' /* Internal Imports */ import { + DEFAULT_FORCE_INCLUSION_PERIOD, makeRandomBatchOfSize, TxQueueBatch, TxChainBatch, makeAddressResolver, deployAndRegister, AddressResolverMapping, + appendSequencerBatch, + appendAndGenerateSequencerBatch, + enqueueAndGenerateSafetyBatch, + enqueueAndGenerateL1ToL2Batch, } from '../../test-helpers' /* Logging */ @@ -21,7 +26,6 @@ const log = getLogger('canonical-tx-chain', true) /* Tests */ describe('CanonicalTransactionChain', () => { const provider = ethers.provider - const FORCE_INCLUSION_PERIOD = 600 //600 seconds = 10 minutes const DEFAULT_BATCH = ['0x1234', '0x5678'] const DEFAULT_TX = '0x1234' @@ -38,82 +42,6 @@ describe('CanonicalTransactionChain', () => { ] = await ethers.getSigners() }) - let canonicalTxChain: Contract - let l1ToL2Queue: Contract - let safetyQueue: Contract - - const appendSequencerBatch = async (batch: string[]): Promise => { - const timestamp = Math.floor(Date.now() / 1000) - // Submit the rollup batch on-chain - await canonicalTxChain - .connect(sequencer) - .appendSequencerBatch(batch, timestamp) - return timestamp - } - - const appendAndGenerateSequencerBatch = async ( - batch: string[], - batchIndex: number = 0, - cumulativePrevElements: number = 0 - ): Promise => { - const timestamp = await appendSequencerBatch(batch) - return createTxChainBatch( - batch, - timestamp, - false, - batchIndex, - cumulativePrevElements - ) - } - - const createTxChainBatch = async ( - batch: string[], - timestamp: number, - isL1ToL2Tx: boolean, - batchIndex: number = 0, - cumulativePrevElements: number = 0 - ): Promise => { - const localBatch = new TxChainBatch( - timestamp, - isL1ToL2Tx, - batchIndex, - cumulativePrevElements, - batch - ) - await localBatch.generateTree() - return localBatch - } - - const enqueueAndGenerateL1ToL2Batch = async ( - _tx: string - ): Promise => { - // Submit the rollup batch on-chain - const enqueueTx = await l1ToL2Queue - .connect(l1ToL2TransactionPasser) - .enqueueTx(_tx) - const localBatch = await generateQueueBatch(_tx, enqueueTx.hash) - return localBatch - } - const enqueueAndGenerateSafetyBatch = async ( - _tx: string - ): Promise => { - const enqueueTx = await safetyQueue.connect(randomWallet).enqueueTx(_tx) - const localBatch = await generateQueueBatch(_tx, enqueueTx.hash) - return localBatch - } - - const generateQueueBatch = async ( - _tx: string, - _txHash: string - ): Promise => { - const txReceipt = await provider.getTransactionReceipt(_txHash) - const timestamp = (await provider.getBlock(txReceipt.blockNumber)).timestamp - // Generate a local version of the rollup batch - const localBatch = new TxQueueBatch(_tx, timestamp) - await localBatch.generateTree() - return localBatch - } - let resolver: AddressResolverMapping before(async () => { resolver = await makeAddressResolver(wallet) @@ -134,6 +62,9 @@ describe('CanonicalTransactionChain', () => { ) }) + let canonicalTxChain: Contract + let l1ToL2Queue: Contract + let safetyQueue: Contract beforeEach(async () => { canonicalTxChain = await deployAndRegister( resolver.addressResolver, @@ -145,7 +76,7 @@ describe('CanonicalTransactionChain', () => { resolver.addressResolver.address, await sequencer.getAddress(), await l1ToL2TransactionPasser.getAddress(), - FORCE_INCLUSION_PERIOD, + DEFAULT_FORCE_INCLUSION_PERIOD, ], } ) @@ -176,7 +107,11 @@ describe('CanonicalTransactionChain', () => { describe('appendSequencerBatch()', async () => { it('should not throw when appending a batch from the sequencer', async () => { - await appendSequencerBatch(DEFAULT_BATCH) + await appendSequencerBatch( + canonicalTxChain, + sequencer, + DEFAULT_BATCH + ) }) it('should throw if submitting an empty batch', async () => { @@ -184,14 +119,18 @@ describe('CanonicalTransactionChain', () => { await TestUtils.assertRevertsAsync( 'Cannot submit an empty batch', async () => { - await appendSequencerBatch(emptyBatch) + await appendSequencerBatch( + canonicalTxChain, + sequencer, + emptyBatch + ) } ) }) it('should revert if submitting a batch older than the inclusion period', async () => { const timestamp = Math.floor(Date.now() / 1000) - const oldTimestamp = timestamp - (FORCE_INCLUSION_PERIOD + 1) + const oldTimestamp = timestamp - (DEFAULT_FORCE_INCLUSION_PERIOD + 1) await TestUtils.assertRevertsAsync( 'Cannot submit a batch with a timestamp older than the sequencer inclusion period', async () => { @@ -204,7 +143,7 @@ describe('CanonicalTransactionChain', () => { it('should not revert if submitting a 5 minute old batch', async () => { const timestamp = Math.floor(Date.now() / 1000) - const oldTimestamp = timestamp - FORCE_INCLUSION_PERIOD / 2 + const oldTimestamp = timestamp - DEFAULT_FORCE_INCLUSION_PERIOD / 2 await canonicalTxChain .connect(sequencer) .appendSequencerBatch(DEFAULT_BATCH, oldTimestamp) @@ -224,7 +163,11 @@ describe('CanonicalTransactionChain', () => { }) it('should revert if submitting a new batch with a timestamp older than last batch timestamp', async () => { - const timestamp = await appendSequencerBatch(DEFAULT_BATCH) + const timestamp = await appendSequencerBatch( + canonicalTxChain, + sequencer, + DEFAULT_BATCH + ) const oldTimestamp = timestamp - 1 await TestUtils.assertRevertsAsync( 'Timestamps must monotonically increase', @@ -237,13 +180,21 @@ describe('CanonicalTransactionChain', () => { }) it('should add to batches array', async () => { - await appendSequencerBatch(DEFAULT_BATCH) + await appendSequencerBatch( + canonicalTxChain, + sequencer, + DEFAULT_BATCH + ) const batchesLength = await canonicalTxChain.getBatchesLength() batchesLength.toNumber().should.equal(1) }) it('should update cumulativeNumElements correctly', async () => { - await appendSequencerBatch(DEFAULT_BATCH) + await appendSequencerBatch( + canonicalTxChain, + sequencer, + DEFAULT_BATCH + ) const cumulativeNumElements = await canonicalTxChain.cumulativeNumElements.call() cumulativeNumElements.toNumber().should.equal(DEFAULT_BATCH.length) }) @@ -259,7 +210,11 @@ describe('CanonicalTransactionChain', () => { }) it('should calculate batchHeaderHash correctly', async () => { - const localBatch = await appendAndGenerateSequencerBatch(DEFAULT_BATCH) + const localBatch = await appendAndGenerateSequencerBatch( + canonicalTxChain, + sequencer, + DEFAULT_BATCH + ) const expectedBatchHeaderHash = await localBatch.hashBatchHeader() const calculatedBatchHeaderHash = await canonicalTxChain.batches(0) calculatedBatchHeaderHash.should.equal(expectedBatchHeaderHash) @@ -272,6 +227,8 @@ describe('CanonicalTransactionChain', () => { const batch = makeRandomBatchOfSize(batchIndex + 1) const cumulativePrevElements = expectedNumElements const localBatch = await appendAndGenerateSequencerBatch( + canonicalTxChain, + sequencer, batch, batchIndex, cumulativePrevElements @@ -292,7 +249,12 @@ describe('CanonicalTransactionChain', () => { describe('when there is a batch in the L1toL2Queue', async () => { let localBatch beforeEach(async () => { - localBatch = await enqueueAndGenerateL1ToL2Batch(DEFAULT_TX) + localBatch = await enqueueAndGenerateL1ToL2Batch( + provider, + l1ToL2Queue, + l1ToL2TransactionPasser, + DEFAULT_TX + ) }) it('should successfully append a batch with an older timestamp', async () => { @@ -310,7 +272,7 @@ describe('CanonicalTransactionChain', () => { it('should revert when there is an older batch in the L1ToL2Queue', async () => { const snapshotID = await provider.send('evm_snapshot', []) - await provider.send('evm_increaseTime', [FORCE_INCLUSION_PERIOD]) + await provider.send('evm_increaseTime', [DEFAULT_FORCE_INCLUSION_PERIOD]) const newTimestamp = localBatch.timestamp + 60 await TestUtils.assertRevertsAsync( 'Must process older L1ToL2Queue batches first to enforce timestamp monotonicity', @@ -327,7 +289,12 @@ describe('CanonicalTransactionChain', () => { describe('when there is a batch in the SafetyQueue', async () => { let localBatch beforeEach(async () => { - localBatch = await enqueueAndGenerateSafetyBatch(DEFAULT_TX) + localBatch = await enqueueAndGenerateSafetyBatch( + provider, + safetyQueue, + randomWallet, + DEFAULT_TX + ) }) it('should successfully append a batch with an older timestamp', async () => { @@ -345,7 +312,7 @@ describe('CanonicalTransactionChain', () => { it('should revert when there is an older batch in the SafetyQueue', async () => { const snapshotID = await provider.send('evm_snapshot', []) - await provider.send('evm_increaseTime', [FORCE_INCLUSION_PERIOD]) + await provider.send('evm_increaseTime', [DEFAULT_FORCE_INCLUSION_PERIOD]) const newTimestamp = localBatch.timestamp + 60 await TestUtils.assertRevertsAsync( 'Must process older SafetyQueue batches first to enforce timestamp monotonicity', @@ -363,11 +330,21 @@ describe('CanonicalTransactionChain', () => { let l1ToL2Timestamp let snapshotID beforeEach(async () => { - const localSafetyBatch = await enqueueAndGenerateSafetyBatch(DEFAULT_TX) + const localSafetyBatch = await enqueueAndGenerateSafetyBatch( + provider, + safetyQueue, + randomWallet, + DEFAULT_TX + ) safetyTimestamp = localSafetyBatch.timestamp snapshotID = await provider.send('evm_snapshot', []) - await provider.send('evm_increaseTime', [FORCE_INCLUSION_PERIOD / 2]) - const localL1ToL2Batch = await enqueueAndGenerateL1ToL2Batch(DEFAULT_TX) + await provider.send('evm_increaseTime', [DEFAULT_FORCE_INCLUSION_PERIOD / 2]) + const localL1ToL2Batch = await enqueueAndGenerateL1ToL2Batch( + provider, + l1ToL2Queue, + l1ToL2TransactionPasser, + DEFAULT_TX + ) l1ToL2Timestamp = localL1ToL2Batch.timestamp }) afterEach(async () => { @@ -400,7 +377,7 @@ describe('CanonicalTransactionChain', () => { }) it('should revert when appending a batch with a timestamp newer than both batches', async () => { - await provider.send('evm_increaseTime', [FORCE_INCLUSION_PERIOD / 10]) // increase time by 60 seconds + await provider.send('evm_increaseTime', [DEFAULT_FORCE_INCLUSION_PERIOD / 10]) // increase time by 60 seconds const oldTimestamp = l1ToL2Timestamp + 1 await TestUtils.assertRevertsAsync( 'Must process older L1ToL2Queue batches first to enforce timestamp monotonicity', @@ -418,11 +395,21 @@ describe('CanonicalTransactionChain', () => { let safetyTimestamp let snapshotID beforeEach(async () => { - const localL1ToL2Batch = await enqueueAndGenerateL1ToL2Batch(DEFAULT_TX) + const localL1ToL2Batch = await enqueueAndGenerateL1ToL2Batch( + provider, + l1ToL2Queue, + l1ToL2TransactionPasser, + DEFAULT_TX + ) l1ToL2Timestamp = localL1ToL2Batch.timestamp snapshotID = await provider.send('evm_snapshot', []) - await provider.send('evm_increaseTime', [FORCE_INCLUSION_PERIOD / 2]) - const localSafetyBatch = await enqueueAndGenerateSafetyBatch(DEFAULT_TX) + await provider.send('evm_increaseTime', [DEFAULT_FORCE_INCLUSION_PERIOD / 2]) + const localSafetyBatch = await enqueueAndGenerateSafetyBatch( + provider, + safetyQueue, + randomWallet, + DEFAULT_TX + ) safetyTimestamp = localSafetyBatch.timestamp }) afterEach(async () => { @@ -455,7 +442,7 @@ describe('CanonicalTransactionChain', () => { }) it('should revert when appending a batch with a timestamp newer than both batches', async () => { - await provider.send('evm_increaseTime', [FORCE_INCLUSION_PERIOD / 10]) // increase time by 60 seconds + await provider.send('evm_increaseTime', [DEFAULT_FORCE_INCLUSION_PERIOD / 10]) // increase time by 60 seconds const newTimestamp = safetyTimestamp + 1 await TestUtils.assertRevertsAsync( 'Must process older L1ToL2Queue batches first to enforce timestamp monotonicity', @@ -472,7 +459,12 @@ describe('CanonicalTransactionChain', () => { describe('appendL1ToL2Batch()', async () => { describe('when there is a batch in the L1toL2Queue', async () => { beforeEach(async () => { - await enqueueAndGenerateL1ToL2Batch(DEFAULT_TX) + await enqueueAndGenerateL1ToL2Batch( + provider, + l1ToL2Queue, + l1ToL2TransactionPasser, + DEFAULT_TX + ) }) it('should successfully dequeue a L1ToL2Batch', async () => { @@ -513,7 +505,7 @@ describe('CanonicalTransactionChain', () => { it('should allow non-sequencer to appendL1ToL2Batch after inclusion period has elapsed', async () => { const snapshotID = await provider.send('evm_snapshot', []) - await provider.send('evm_increaseTime', [FORCE_INCLUSION_PERIOD]) + await provider.send('evm_increaseTime', [DEFAULT_FORCE_INCLUSION_PERIOD]) await canonicalTxChain.appendL1ToL2Batch() await provider.send('evm_revert', [snapshotID]) }) @@ -522,9 +514,19 @@ describe('CanonicalTransactionChain', () => { describe('when there is a batch in both the SafetyQueue and L1toL2Queue', async () => { it('should revert when the SafetyQueue batch is older', async () => { const snapshotID = await provider.send('evm_snapshot', []) - await enqueueAndGenerateSafetyBatch(DEFAULT_TX) + await enqueueAndGenerateSafetyBatch( + provider, + safetyQueue, + randomWallet, + DEFAULT_TX + ) await provider.send('evm_increaseTime', [10]) - await enqueueAndGenerateL1ToL2Batch(DEFAULT_TX) + await enqueueAndGenerateL1ToL2Batch( + provider, + l1ToL2Queue, + l1ToL2TransactionPasser, + DEFAULT_TX + ) await TestUtils.assertRevertsAsync( 'Must process older SafetyQueue batches first to enforce timestamp monotonicity', async () => { @@ -536,9 +538,19 @@ describe('CanonicalTransactionChain', () => { it('should succeed when the L1ToL2Queue batch is older', async () => { const snapshotID = await provider.send('evm_snapshot', []) - await enqueueAndGenerateL1ToL2Batch(DEFAULT_TX) + await enqueueAndGenerateL1ToL2Batch( + provider, + l1ToL2Queue, + l1ToL2TransactionPasser, + DEFAULT_TX + ) await provider.send('evm_increaseTime', [10]) - await enqueueAndGenerateSafetyBatch(DEFAULT_TX) + await enqueueAndGenerateSafetyBatch( + provider, + safetyQueue, + randomWallet, + DEFAULT_TX + ) await canonicalTxChain.connect(sequencer).appendL1ToL2Batch() await provider.send('evm_revert', [snapshotID]) }) @@ -557,7 +569,12 @@ describe('CanonicalTransactionChain', () => { describe('appendSafetyBatch()', async () => { describe('when there is a batch in the SafetyQueue', async () => { beforeEach(async () => { - await enqueueAndGenerateSafetyBatch(DEFAULT_TX) + await enqueueAndGenerateSafetyBatch( + provider, + safetyQueue, + randomWallet, + DEFAULT_TX + ) }) it('should successfully dequeue a SafetyBatch', async () => { @@ -598,7 +615,7 @@ describe('CanonicalTransactionChain', () => { it('should allow non-sequencer to appendSafetyBatch after force inclusion period has elapsed', async () => { const snapshotID = await provider.send('evm_snapshot', []) - await provider.send('evm_increaseTime', [FORCE_INCLUSION_PERIOD]) + await provider.send('evm_increaseTime', [DEFAULT_FORCE_INCLUSION_PERIOD]) await canonicalTxChain.appendSafetyBatch() await provider.send('evm_revert', [snapshotID]) }) @@ -606,9 +623,19 @@ describe('CanonicalTransactionChain', () => { it('should revert when trying to appendSafetyBatch when there is an older batch in the L1ToL2Queue ', async () => { const snapshotID = await provider.send('evm_snapshot', []) - await enqueueAndGenerateL1ToL2Batch(DEFAULT_TX) + await enqueueAndGenerateL1ToL2Batch( + provider, + l1ToL2Queue, + l1ToL2TransactionPasser, + DEFAULT_TX + ) await provider.send('evm_increaseTime', [10]) - await enqueueAndGenerateSafetyBatch(DEFAULT_TX) + await enqueueAndGenerateSafetyBatch( + provider, + safetyQueue, + randomWallet, + DEFAULT_TX + ) await TestUtils.assertRevertsAsync( 'Must process older L1ToL2Queue batches first to enforce timestamp monotonicity', async () => { @@ -620,9 +647,19 @@ describe('CanonicalTransactionChain', () => { it('should succeed when there are only newer batches in the L1ToL2Queue ', async () => { const snapshotID = await provider.send('evm_snapshot', []) - await enqueueAndGenerateSafetyBatch(DEFAULT_TX) + await enqueueAndGenerateSafetyBatch( + provider, + safetyQueue, + randomWallet, + DEFAULT_TX + ) await provider.send('evm_increaseTime', [10]) - await enqueueAndGenerateL1ToL2Batch(DEFAULT_TX) + await enqueueAndGenerateL1ToL2Batch( + provider, + l1ToL2Queue, + l1ToL2TransactionPasser, + DEFAULT_TX + ) await canonicalTxChain.connect(sequencer).appendSafetyBatch() await provider.send('evm_revert', [snapshotID]) }) @@ -645,6 +682,8 @@ describe('CanonicalTransactionChain', () => { const batchSize = batchIndex * batchIndex + 1 // 1, 2, 5, 10 const batch = makeRandomBatchOfSize(batchSize) const localBatch = await appendAndGenerateSequencerBatch( + canonicalTxChain, + sequencer, batch, batchIndex, cumulativePrevElements @@ -671,7 +710,12 @@ describe('CanonicalTransactionChain', () => { }) it('should return true for valid element from a l1ToL2Batch', async () => { - const l1ToL2Batch = await enqueueAndGenerateL1ToL2Batch(DEFAULT_TX) + const l1ToL2Batch = await enqueueAndGenerateL1ToL2Batch( + provider, + l1ToL2Queue, + l1ToL2TransactionPasser, + DEFAULT_TX + ) await canonicalTxChain.connect(sequencer).appendL1ToL2Batch() const localBatch = new TxChainBatch( l1ToL2Batch.timestamp, //timestamp @@ -695,7 +739,12 @@ describe('CanonicalTransactionChain', () => { }) it('should return true for valid element from a SafetyBatch', async () => { - const safetyBatch = await enqueueAndGenerateSafetyBatch(DEFAULT_TX) + const safetyBatch = await enqueueAndGenerateSafetyBatch( + provider, + safetyQueue, + randomWallet, + DEFAULT_TX + ) await canonicalTxChain.connect(sequencer).appendSafetyBatch() const localBatch = new TxChainBatch( safetyBatch.timestamp, //timestamp @@ -720,7 +769,11 @@ describe('CanonicalTransactionChain', () => { it('should return false for wrong position with wrong indexInBatch', async () => { const batch = ['0x1234', '0x4567', '0x890a', '0x4567', '0x890a', '0xabcd'] - const localBatch = await appendAndGenerateSequencerBatch(batch) + const localBatch = await appendAndGenerateSequencerBatch( + canonicalTxChain, + sequencer, + batch + ) const elementIndex = 1 const element = batch[elementIndex] const position = localBatch.getPosition(elementIndex) @@ -739,7 +792,11 @@ describe('CanonicalTransactionChain', () => { it('should return false for wrong position and matching indexInBatch', async () => { const batch = ['0x1234', '0x4567', '0x890a', '0x4567', '0x890a', '0xabcd'] - const localBatch = await appendAndGenerateSequencerBatch(batch) + const localBatch = await appendAndGenerateSequencerBatch( + canonicalTxChain, + sequencer, + batch + ) const elementIndex = 1 const element = batch[elementIndex] const position = localBatch.getPosition(elementIndex) @@ -769,6 +826,8 @@ describe('CanonicalTransactionChain', () => { } ) const localBatch: TxChainBatch = await appendAndGenerateSequencerBatch( + canonicalTxChain, + sequencer, DEFAULT_BATCH ) @@ -789,7 +848,12 @@ describe('CanonicalTransactionChain', () => { txEnqueued = true }) - await enqueueAndGenerateSafetyBatch(DEFAULT_TX) + await enqueueAndGenerateSafetyBatch( + provider, + safetyQueue, + randomWallet, + DEFAULT_TX + ) await sleep(5_000) @@ -803,6 +867,9 @@ describe('CanonicalTransactionChain', () => { }) const localBatch: TxQueueBatch = await enqueueAndGenerateL1ToL2Batch( + provider, + l1ToL2Queue, + l1ToL2TransactionPasser, DEFAULT_TX ) @@ -827,6 +894,9 @@ describe('CanonicalTransactionChain', () => { ) const localBatch: TxQueueBatch = await enqueueAndGenerateL1ToL2Batch( + provider, + l1ToL2Queue, + l1ToL2TransactionPasser, DEFAULT_TX ) await canonicalTxChain.connect(sequencer).appendL1ToL2Batch() @@ -858,7 +928,12 @@ describe('CanonicalTransactionChain', () => { } ) - const localBatch = await enqueueAndGenerateSafetyBatch(DEFAULT_TX) + const localBatch = await enqueueAndGenerateSafetyBatch( + provider, + safetyQueue, + randomWallet, + DEFAULT_TX + ) await canonicalTxChain.connect(sequencer).appendSafetyBatch() const front = await safetyQueue.front() diff --git a/packages/contracts/test/contracts/chain/SequencerBatchSubmitter.spec.ts b/packages/contracts/test/contracts/chain/SequencerBatchSubmitter.spec.ts index bb3ea66d781cd..9223bd3bd365d 100644 --- a/packages/contracts/test/contracts/chain/SequencerBatchSubmitter.spec.ts +++ b/packages/contracts/test/contracts/chain/SequencerBatchSubmitter.spec.ts @@ -7,11 +7,12 @@ import { Contract, Signer, ContractFactory } from 'ethers' /* Internal Imports */ import { - StateChainBatch, - TxChainBatch, + DEFAULT_FORCE_INCLUSION_PERIOD, makeAddressResolver, AddressResolverMapping, deployAndRegister, + generateTxBatch, + generateStateBatch, } from '../../test-helpers' /* Logging */ @@ -21,7 +22,6 @@ const log = getLogger('batch-submitter', true) describe('SequencerBatchSubmitter', () => { const DEFAULT_STATE_BATCH = ['0x1234', '0x5678'] const DEFAULT_TX_BATCH = ['0xabcd', '0xef12'] - const FORCE_INCLUSION_PERIOD = 600 let wallet: Signer let sequencer: Signer @@ -38,41 +38,6 @@ describe('SequencerBatchSubmitter', () => { ] = await ethers.getSigners() }) - let stateChain: Contract - let canonicalTxChain: Contract - let sequencerBatchSubmitter: Contract - - const generateStateBatch = async ( - batch: string[], - batchIndex: number = 0, - cumulativePrevElements: number = 0 - ): Promise => { - const localBatch = new StateChainBatch( - batchIndex, - cumulativePrevElements, - batch - ) - await localBatch.generateTree() - return localBatch - } - - const generateTxBatch = async ( - batch: string[], - timestamp: number, - batchIndex: number = 0, - cumulativePrevElements: number = 0 - ): Promise => { - const localBatch = new TxChainBatch( - timestamp, - false, - batchIndex, - cumulativePrevElements, - batch - ) - await localBatch.generateTree() - return localBatch - } - let resolver: AddressResolverMapping before(async () => { resolver = await makeAddressResolver(wallet) @@ -98,6 +63,9 @@ describe('SequencerBatchSubmitter', () => { ) }) + let stateChain: Contract + let canonicalTxChain: Contract + let sequencerBatchSubmitter: Contract beforeEach(async () => { sequencerBatchSubmitter = await deployAndRegister( resolver.addressResolver, @@ -122,7 +90,7 @@ describe('SequencerBatchSubmitter', () => { resolver.addressResolver.address, sequencerBatchSubmitter.address, await l1ToL2TransactionPasser.getAddress(), - FORCE_INCLUSION_PERIOD, + DEFAULT_FORCE_INCLUSION_PERIOD, ], } ) diff --git a/packages/contracts/test/contracts/chain/StateCommitmentChain.spec.ts b/packages/contracts/test/contracts/chain/StateCommitmentChain.spec.ts index 7e3edcff79a74..d79094a34039a 100644 --- a/packages/contracts/test/contracts/chain/StateCommitmentChain.spec.ts +++ b/packages/contracts/test/contracts/chain/StateCommitmentChain.spec.ts @@ -7,11 +7,13 @@ import { Contract, Signer, ContractFactory } from 'ethers' /* Internal Imports */ import { + DEFAULT_FORCE_INCLUSION_PERIOD, makeRandomBatchOfSize, - StateChainBatch, makeAddressResolver, deployAndRegister, AddressResolverMapping, + appendAndGenerateStateBatch, + appendTxBatch, } from '../../test-helpers' /* Logging */ @@ -32,7 +34,6 @@ describe('StateCommitmentChain', () => { '0x1234', '0x5678', ] - const FORCE_INCLUSION_PERIOD = 600 let wallet: Signer let sequencer: Signer @@ -49,33 +50,6 @@ describe('StateCommitmentChain', () => { ] = await ethers.getSigners() }) - let stateChain: Contract - let canonicalTxChain: Contract - - const appendAndGenerateStateBatch = async ( - batch: string[], - batchIndex: number = 0, - cumulativePrevElements: number = 0 - ): Promise => { - await stateChain.appendStateBatch(batch) - // Generate a local version of the rollup batch - const localBatch = new StateChainBatch( - batchIndex, - cumulativePrevElements, - batch - ) - await localBatch.generateTree() - return localBatch - } - - const appendTxBatch = async (batch: string[]): Promise => { - const timestamp = Math.floor(Date.now() / 1000) - // Submit the rollup batch on-chain - await canonicalTxChain - .connect(sequencer) - .appendSequencerBatch(batch, timestamp) - } - let resolver: AddressResolverMapping before(async () => { resolver = await makeAddressResolver(wallet) @@ -92,6 +66,8 @@ describe('StateCommitmentChain', () => { ) }) + let stateChain: Contract + let canonicalTxChain: Contract before(async () => { canonicalTxChain = await deployAndRegister( resolver.addressResolver, @@ -103,12 +79,17 @@ describe('StateCommitmentChain', () => { resolver.addressResolver.address, await sequencer.getAddress(), await l1ToL2TransactionPasser.getAddress(), - FORCE_INCLUSION_PERIOD, + DEFAULT_FORCE_INCLUSION_PERIOD, ], } ) - await appendTxBatch(DEFAULT_TX_BATCH) + await appendTxBatch( + canonicalTxChain, + sequencer, + DEFAULT_TX_BATCH + ) + await resolver.addressResolver.setAddress( 'FraudVerifier', await fraudVerifier.getAddress() @@ -157,7 +138,10 @@ describe('StateCommitmentChain', () => { }) it('should calculate batchHeaderHash correctly', async () => { - const localBatch = await appendAndGenerateStateBatch(DEFAULT_STATE_BATCH) + const localBatch = await appendAndGenerateStateBatch( + stateChain, + DEFAULT_STATE_BATCH + ) const expectedBatchHeaderHash = await localBatch.hashBatchHeader() const calculatedBatchHeaderHash = await stateChain.batches(0) calculatedBatchHeaderHash.should.equal(expectedBatchHeaderHash) @@ -170,6 +154,7 @@ describe('StateCommitmentChain', () => { const batch = makeRandomBatchOfSize(batchIndex + 1) const cumulativePrevElements = expectedNumElements const localBatch = await appendAndGenerateStateBatch( + stateChain, batch, batchIndex, cumulativePrevElements @@ -202,7 +187,11 @@ describe('StateCommitmentChain', () => { describe('verifyElement() ', async () => { it('should return true for valid elements for different batches and elements', async () => { // add enough transaction batches so # txs > # state roots - await appendTxBatch(DEFAULT_TX_BATCH) + await appendTxBatch( + canonicalTxChain, + sequencer, + DEFAULT_TX_BATCH + ) const numBatches = 4 let cumulativePrevElements = 0 @@ -210,6 +199,7 @@ describe('StateCommitmentChain', () => { const batchSize = batchIndex * batchIndex + 1 // 1, 2, 5, 10 const batch = makeRandomBatchOfSize(batchSize) const localBatch = await appendAndGenerateStateBatch( + stateChain, batch, batchIndex, cumulativePrevElements @@ -237,7 +227,10 @@ describe('StateCommitmentChain', () => { it('should return false for wrong position with wrong indexInBatch', async () => { const batch = ['0x1234', '0x4567', '0x890a', '0x4567', '0x890a', '0xabcd'] - const localBatch = await appendAndGenerateStateBatch(batch) + const localBatch = await appendAndGenerateStateBatch( + stateChain, + batch + ) const elementIndex = 1 const element = batch[elementIndex] const position = localBatch.getPosition(elementIndex) @@ -256,7 +249,10 @@ describe('StateCommitmentChain', () => { it('should return false for wrong position and matching indexInBatch', async () => { const batch = ['0x1234', '0x4567', '0x890a', '0x4567', '0x890a', '0xabcd'] - const localBatch = await appendAndGenerateStateBatch(batch) + const localBatch = await appendAndGenerateStateBatch( + stateChain, + batch + ) const elementIndex = 1 const element = batch[elementIndex] const position = localBatch.getPosition(elementIndex) @@ -280,7 +276,10 @@ describe('StateCommitmentChain', () => { it('should not allow deletion from address other than fraud verifier', async () => { const cumulativePrevElements = 0 const batchIndex = 0 - const localBatch = await appendAndGenerateStateBatch(DEFAULT_STATE_BATCH) + const localBatch = await appendAndGenerateStateBatch( + stateChain, + DEFAULT_STATE_BATCH + ) const batchHeader = { elementsMerkleRoot: await localBatch.elementsMerkleTree.getRootHash(), numElementsInBatch: DEFAULT_STATE_BATCH.length, @@ -301,6 +300,7 @@ describe('StateCommitmentChain', () => { const cumulativePrevElements = 0 const batchIndex = 0 const localBatch = await appendAndGenerateStateBatch( + stateChain, DEFAULT_STATE_BATCH ) const batchHeader = { @@ -321,6 +321,7 @@ describe('StateCommitmentChain', () => { it('should successfully append a batch after deletion', async () => { const localBatch = await appendAndGenerateStateBatch( + stateChain, DEFAULT_STATE_BATCH ) const expectedBatchHeaderHash = await localBatch.hashBatchHeader() @@ -335,6 +336,7 @@ describe('StateCommitmentChain', () => { for (let batchIndex = 0; batchIndex < 5; batchIndex++) { const cumulativePrevElements = batchIndex * DEFAULT_STATE_BATCH.length const localBatch = await appendAndGenerateStateBatch( + stateChain, DEFAULT_STATE_BATCH, batchIndex, cumulativePrevElements @@ -358,7 +360,10 @@ describe('StateCommitmentChain', () => { it('should revert if batchHeader is incorrect', async () => { const cumulativePrevElements = 0 const batchIndex = 0 - const localBatch = await appendAndGenerateStateBatch(DEFAULT_STATE_BATCH) + const localBatch = await appendAndGenerateStateBatch( + stateChain, + DEFAULT_STATE_BATCH + ) const batchHeader = { elementsMerkleRoot: await localBatch.elementsMerkleTree.getRootHash(), numElementsInBatch: DEFAULT_STATE_BATCH.length + 1, // increment to make header incorrect @@ -378,7 +383,10 @@ describe('StateCommitmentChain', () => { it('should revert if trying to delete a batch outside of valid range', async () => { const cumulativePrevElements = 0 const batchIndex = 1 // outside of range - const localBatch = await appendAndGenerateStateBatch(DEFAULT_STATE_BATCH) + const localBatch = await appendAndGenerateStateBatch( + stateChain, + DEFAULT_STATE_BATCH + ) const batchHeader = { elementsMerkleRoot: await localBatch.elementsMerkleTree.getRootHash(), numElementsInBatch: DEFAULT_STATE_BATCH.length + 1, // increment to make header incorrect @@ -401,7 +409,10 @@ describe('StateCommitmentChain', () => { stateChain.on(stateChain.filters['StateBatchAppended'](), (...data) => { receivedBatchHeaderHash = data[0] }) - const localBatch = await appendAndGenerateStateBatch(DEFAULT_STATE_BATCH) + const localBatch = await appendAndGenerateStateBatch( + stateChain, + DEFAULT_STATE_BATCH + ) await sleep(5_000) diff --git a/packages/contracts/test/contracts/ovm/FraudVerifier.spec.ts b/packages/contracts/test/contracts/ovm/FraudVerifier.spec.ts index e89df5e29c48a..61d4d3b4f3925 100644 --- a/packages/contracts/test/contracts/ovm/FraudVerifier.spec.ts +++ b/packages/contracts/test/contracts/ovm/FraudVerifier.spec.ts @@ -1,7 +1,6 @@ import { expect } from '../../setup' /* External Imports */ -import * as rlp from 'rlp' import { ethers } from '@nomiclabs/buidler' import { Contract, ContractFactory, Signer } from 'ethers' import { TestUtils } from '@eth-optimism/core-utils' @@ -10,127 +9,32 @@ import { TestUtils } from '@eth-optimism/core-utils' import { TxChainBatch, StateChainBatch, - toHexString, makeAddressResolver, deployAndRegister, AddressResolverMapping, + makeDummyOvmTransaction, + encodeOvmTransaction, + appendAndGenerateTransactionBatch, + appendAndGenerateStateBatch } from '../../test-helpers' -interface OVMTransactionData { - timestamp: number - queueOrigin: number - ovmEntrypoint: string - callBytes: string - fromAddress: string - l1MsgSenderAddress: string - allowRevert: boolean -} - -const NULL_ADDRESS = '0x' + '00'.repeat(20) -const FORCE_INCLUSION_PERIOD = 600 - -const makeDummyTransaction = (calldata: string): OVMTransactionData => { - return { - timestamp: Math.floor(Date.now() / 1000), - queueOrigin: 0, - ovmEntrypoint: NULL_ADDRESS, - callBytes: calldata, - fromAddress: NULL_ADDRESS, - l1MsgSenderAddress: NULL_ADDRESS, - allowRevert: false, - } -} - -const encodeTransaction = (transaction: OVMTransactionData): string => { - return toHexString( - rlp.encode([ - transaction.timestamp, - transaction.queueOrigin, - transaction.ovmEntrypoint, - transaction.callBytes, - transaction.fromAddress, - transaction.l1MsgSenderAddress, - transaction.allowRevert ? 1 : 0, - ]) - ) -} - -const appendTransactionBatch = async ( - canonicalTransactionChain: Contract, - sequencer: Signer, - batch: string[] -): Promise => { - const timestamp = Math.floor(Date.now() / 1000) - - await canonicalTransactionChain - .connect(sequencer) - .appendSequencerBatch(batch, timestamp) - - return timestamp -} - -const appendAndGenerateTransactionBatch = async ( - canonicalTransactionChain: Contract, - sequencer: Signer, - batch: string[], - batchIndex: number = 0, - cumulativePrevElements: number = 0 -): Promise => { - const timestamp = await appendTransactionBatch( - canonicalTransactionChain, - sequencer, - batch - ) - - const localBatch = new TxChainBatch( - timestamp, - false, - batchIndex, - cumulativePrevElements, - batch - ) - - await localBatch.generateTree() - - return localBatch -} - -const appendAndGenerateStateBatch = async ( - stateCommitmentChain: Contract, - batch: string[], - batchIndex: number = 0, - cumulativePrevElements: number = 0 -): Promise => { - await stateCommitmentChain.appendStateBatch(batch) - - const localBatch = new StateChainBatch( - batchIndex, - cumulativePrevElements, - batch - ) - - await localBatch.generateTree() - - return localBatch -} - -const DUMMY_STATE_BATCH = [ - '0x' + '01'.repeat(32), - '0x' + '02'.repeat(32), - '0x' + '03'.repeat(32), - '0x' + '04'.repeat(32), -] - /* Tests */ describe('FraudVerifier', () => { + const DUMMY_STATE_BATCH = [ + '0x' + '01'.repeat(32), + '0x' + '02'.repeat(32), + '0x' + '03'.repeat(32), + '0x' + '04'.repeat(32), + ] + // Must create these when the tests are executed or the timestamp will be // invalid when we have a lot of tests to run. const DUMMY_TRANSACTION_BATCH = DUMMY_STATE_BATCH.map((element) => { - return makeDummyTransaction(element) + return makeDummyOvmTransaction(element) }) const ENCODED_DUMMY_TRANSACTION_BATCH = DUMMY_TRANSACTION_BATCH.map( (transaction) => { - return encodeTransaction(transaction) + return encodeOvmTransaction(transaction) } ) diff --git a/packages/contracts/test/contracts/ovm/L2ExecutionManager.spec.ts b/packages/contracts/test/contracts/ovm/L2ExecutionManager.spec.ts index a89dd6c7ca8d1..d98e4e223b709 100644 --- a/packages/contracts/test/contracts/ovm/L2ExecutionManager.spec.ts +++ b/packages/contracts/test/contracts/ovm/L2ExecutionManager.spec.ts @@ -2,25 +2,23 @@ import '../../setup' /* External Imports */ import { ethers } from '@nomiclabs/buidler' -import { add0x, getLogger } from '@eth-optimism/core-utils' +import { add0x, getLogger, NULL_ADDRESS } from '@eth-optimism/core-utils' import { Contract, Signer, ContractFactory } from 'ethers' /* Internal Imports */ import { - DEFAULT_OPCODE_WHITELIST_MASK, GAS_LIMIT, makeAddressResolver, - deployAndRegister, AddressResolverMapping, + fillHexBytes } from '../../test-helpers' /* Logging */ const log = getLogger('l2-execution-manager-calls', true) -export const abi = new ethers.utils.AbiCoder() -const zero32: string = add0x('00'.repeat(32)) -const key: string = add0x('01'.repeat(32)) -const value: string = add0x('02'.repeat(32)) +const zero32: string = fillHexBytes('00') +const key: string = fillHexBytes('01') +const value: string = fillHexBytes('02') describe('L2 Execution Manager', () => { let wallet: Signer @@ -42,7 +40,7 @@ describe('L2 Execution Manager', () => { beforeEach(async () => { l2ExecutionManager = await L2ExecutionManager.deploy( resolver.addressResolver.address, - '0x' + '00'.repeat(20), + NULL_ADDRESS, GAS_LIMIT ) }) diff --git a/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts b/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts index db94a37a523ee..d9fdfd3b69fec 100644 --- a/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts +++ b/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts @@ -8,7 +8,6 @@ import { Contract, ContractFactory, Signer } from 'ethers' /* Internal Imports */ import { makeAddressResolver, - deployAndRegister, AddressResolverMapping, } from '../../test-helpers' diff --git a/packages/contracts/test/contracts/ovm/SafetyChecker.spec.ts b/packages/contracts/test/contracts/ovm/SafetyChecker.spec.ts index 4a6040e96a764..aa16759c8a03e 100644 --- a/packages/contracts/test/contracts/ovm/SafetyChecker.spec.ts +++ b/packages/contracts/test/contracts/ovm/SafetyChecker.spec.ts @@ -2,15 +2,18 @@ import '../../setup' /* External Imports */ import { ethers } from '@nomiclabs/buidler' -import { getLogger, add0x, remove0x } from '@eth-optimism/core-utils' import { Contract, ContractFactory, Signer } from 'ethers' +import { getLogger, add0x, remove0x } from '@eth-optimism/core-utils' +import { EVMOpcode, Opcode } from '@eth-optimism/rollup-core' /* Internal Imports */ import { DEFAULT_OPCODE_WHITELIST_MASK, DEFAULT_UNSAFE_OPCODES, - EVMOpcode, - Opcode, + WHITELISTED_NOT_HALTING_OR_CALL, + HALTING_OPCODES, + HALTING_OPCODES_NO_JUMP, + JUMP_OPCODES, makeAddressResolver, deployAndRegister, AddressResolverMapping, @@ -19,22 +22,10 @@ import { /* Logging */ const log = getLogger('safety-checker', true) -/* Helpers */ -const executionManagerAddress = add0x('12'.repeat(20)) // Test Execution Manager address 0x121...212 -const haltingOpcodes: EVMOpcode[] = Opcode.HALTING_OP_CODES -const haltingOpcodesNoJump: EVMOpcode[] = haltingOpcodes.filter( - (x) => x.name !== 'JUMP' -) -const jumps: EVMOpcode[] = [Opcode.JUMP, Opcode.JUMPI] -const whitelistedNotHaltingOrCALL: EVMOpcode[] = Opcode.ALL_OP_CODES.filter( - (x) => - DEFAULT_UNSAFE_OPCODES.indexOf(x) < 0 && - haltingOpcodes.indexOf(x) < 0 && - x.name !== 'CALL' -) - /* Tests */ describe('Safety Checker', () => { + const executionManagerAddress = add0x('12'.repeat(20)) // Test Execution Manager address 0x121...212 + let wallet: Signer before(async () => { ;[wallet] = await ethers.getSigners() @@ -93,7 +84,7 @@ describe('Safety Checker', () => { }) it('should correctly classify whitelisted', async () => { - for (const opcode of whitelistedNotHaltingOrCALL) { + for (const opcode of WHITELISTED_NOT_HALTING_OR_CALL) { if (!opcode.name.startsWith('PUSH')) { const res: boolean = await safetyChecker.isBytecodeSafe( `0x${opcode.code.toString('hex')}` @@ -157,7 +148,7 @@ describe('Safety Checker', () => { 'hex' ) - for (const opcode of whitelistedNotHaltingOrCALL) { + for (const opcode of WHITELISTED_NOT_HALTING_OR_CALL) { bytecode += `${opcode.code.toString('hex')}${invalidOpcode.repeat( opcode.programBytesConsumed )}` @@ -173,7 +164,7 @@ describe('Safety Checker', () => { 'hex' ) - for (const opcode of whitelistedNotHaltingOrCALL) { + for (const opcode of WHITELISTED_NOT_HALTING_OR_CALL) { bytecode += `${opcode.code.toString('hex')}${invalidOpcode.repeat( opcode.programBytesConsumed )}` @@ -192,7 +183,7 @@ describe('Safety Checker', () => { describe('handles unreachable code', async () => { it(`skips unreachable bytecode after a halting opcode`, async () => { - for (const haltingOp of haltingOpcodes) { + for (const haltingOp of HALTING_OPCODES) { let bytecode: string = '0x' bytecode += haltingOp.code.toString('hex') for (const opcode of DEFAULT_UNSAFE_OPCODES) { @@ -206,7 +197,7 @@ describe('Safety Checker', () => { } }) it('skips bytecode after an unreachable JUMPDEST', async () => { - for (const haltingOp of haltingOpcodesNoJump) { + for (const haltingOp of HALTING_OPCODES_NO_JUMP) { let bytecode: string = '0x' bytecode += haltingOp.code.toString('hex') bytecode += Opcode.JUMPDEST.code.toString('hex') @@ -222,8 +213,8 @@ describe('Safety Checker', () => { }) it('parses opcodes after a reachable JUMPDEST', async () => { - for (const haltingOp of haltingOpcodesNoJump) { - for (const jump of jumps) { + for (const haltingOp of HALTING_OPCODES_NO_JUMP) { + for (const jump of JUMP_OPCODES) { let bytecode: string = '0x' bytecode += jump.code.toString('hex') bytecode += Opcode.JUMPDEST.code.toString('hex') // JUMPDEST here so that the haltingOp is reachable @@ -269,8 +260,8 @@ describe('Safety Checker', () => { }) it('should correctly handle alternating reachable/uncreachable code ending in reachable, valid code', async () => { - for (const haltingOp of haltingOpcodesNoJump) { - for (const jump of jumps) { + for (const haltingOp of HALTING_OPCODES_NO_JUMP) { + for (const jump of JUMP_OPCODES) { let bytecode: string = '0x' bytecode += jump.code.toString('hex') // JUMPDEST here so that the haltingOp is reachable @@ -283,7 +274,7 @@ describe('Safety Checker', () => { } bytecode += Opcode.JUMPDEST.code.toString('hex') // Reachable, valid code - for (const opcode of whitelistedNotHaltingOrCALL) { + for (const opcode of WHITELISTED_NOT_HALTING_OR_CALL) { bytecode += opcode.code.toString('hex') } } @@ -297,8 +288,8 @@ describe('Safety Checker', () => { }).timeout(30_000) it('should correctly handle alternating reachable/uncreachable code ending in reachable, invalid code', async () => { - for (const haltingOp of haltingOpcodesNoJump) { - for (const jump of jumps) { + for (const haltingOp of HALTING_OPCODES_NO_JUMP) { + for (const jump of JUMP_OPCODES) { let bytecode: string = '0x' bytecode += jump.code.toString('hex') // JUMPDEST here so that the haltingOp is reachable @@ -311,7 +302,7 @@ describe('Safety Checker', () => { } bytecode += Opcode.JUMPDEST.code.toString('hex') // Reachable, valid code - for (const opcode of whitelistedNotHaltingOrCALL) { + for (const opcode of WHITELISTED_NOT_HALTING_OR_CALL) { bytecode += opcode.code.toString('hex') } } @@ -381,7 +372,7 @@ describe('Safety Checker', () => { } }) it(`rejects invalid CALLs using opcodes other than PUSH or DUP to set gas`, async () => { - const invalidGasSetters: EVMOpcode[] = whitelistedNotHaltingOrCALL.filter( + const invalidGasSetters: EVMOpcode[] = WHITELISTED_NOT_HALTING_OR_CALL.filter( (x) => !x.name.startsWith('PUSH') && !x.name.startsWith('DUP') ) log.debug(`Invalid Gas Setters ${invalidGasSetters.map((x) => x.name)}`) @@ -406,7 +397,7 @@ describe('Safety Checker', () => { } }) it(`rejects invalid CALLs using opcodes other than PUSH1 to set value`, async () => { - const invalidValueSetters: EVMOpcode[] = whitelistedNotHaltingOrCALL.filter( + const invalidValueSetters: EVMOpcode[] = WHITELISTED_NOT_HALTING_OR_CALL.filter( (x) => x.name !== 'PUSH1' ) log.debug( @@ -436,7 +427,7 @@ describe('Safety Checker', () => { } }).timeout(20_000) it(`rejects invalid CALLs using opcodes other than PUSH20 to set address`, async () => { - const invalidAddressSetters: EVMOpcode[] = whitelistedNotHaltingOrCALL.filter( + const invalidAddressSetters: EVMOpcode[] = WHITELISTED_NOT_HALTING_OR_CALL.filter( (x) => x.name !== 'PUSH20' ) log.debug( diff --git a/packages/contracts/test/contracts/ovm/StateTransitioner.spec.ts b/packages/contracts/test/contracts/ovm/StateTransitioner.spec.ts index 12713135b778b..0f3feae263e08 100644 --- a/packages/contracts/test/contracts/ovm/StateTransitioner.spec.ts +++ b/packages/contracts/test/contracts/ovm/StateTransitioner.spec.ts @@ -22,15 +22,17 @@ import { compile, makeStateTrieUpdateTest, StateTrieUpdateTest, - toHexString, makeAddressResolver, AddressResolverMapping, + makeDummyOvmTransaction, + encodeOvmTransaction, + OVMTransactionData, + getCodeHash } from '../../test-helpers' /* Logging */ const log = getLogger('state-transitioner', true) -const NULL_ADDRESS = '0x' + '00'.repeat(20) const DUMMY_ACCOUNT_ADDRESSES = [ '0x548855F6073c3430285c61Ed0ABf62F12084aA41', '0xD80e66Cbc34F06d24a0a4fDdD6f2aDB41ac1517D', @@ -38,28 +40,6 @@ const DUMMY_ACCOUNT_ADDRESSES = [ '0x808E5eCe9a8EA2cdce515764139Ee24bEF7098b4', ] -interface OVMTransactionData { - timestamp: number - queueOrigin: number - ovmEntrypoint: string - callBytes: string - fromAddress: string - l1MsgSenderAddress: string - allowRevert: boolean -} - -const makeDummyTransaction = (calldata: string): OVMTransactionData => { - return { - timestamp: Math.floor(Date.now() / 1000), - queueOrigin: 0, - ovmEntrypoint: NULL_ADDRESS, - callBytes: calldata, - fromAddress: NULL_ADDRESS, - l1MsgSenderAddress: NULL_ADDRESS, - allowRevert: false, - } -} - const EMPTY_ACCOUNT_STATE = (): StateTrieNode => { return cloneDeep({ nonce: 0, @@ -107,20 +87,6 @@ const DUMMY_STATE_TRIE = { }, } -const encodeTransaction = (transaction: OVMTransactionData): string => { - return toHexString( - rlp.encode([ - transaction.timestamp, - transaction.queueOrigin, - transaction.ovmEntrypoint, - transaction.callBytes, - transaction.fromAddress, - transaction.l1MsgSenderAddress, - transaction.allowRevert ? 1 : 0, - ]) - ) -} - const makeStateTrie = (account: string, state: any, storage: any[]): any => { return { [account]: { @@ -131,10 +97,6 @@ const makeStateTrie = (account: string, state: any, storage: any[]): any => { } } -const getCodeHash = async (provider: any, address: string): Promise => { - return keccak256(await provider.getCode(address)) -} - const makeTransactionData = async ( TargetFactory: ContractFactory, target: Contract, @@ -267,7 +229,7 @@ const initStateTransitioner = async ( addressResolver.address, 10, stateTrieRoot, - keccak256(encodeTransaction(transactionData)) + keccak256(encodeOvmTransaction(transactionData)) ) const stateManager = StateManager.attach( await stateTransitioner.stateManager() @@ -415,7 +377,7 @@ describe('StateTransitioner', () => { StateManager, resolver.addressResolver, test.stateTrieRoot, - makeDummyTransaction('0x00') + makeDummyOvmTransaction('0x00') ) }) diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.call-opcodes.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.call-opcodes.spec.ts index 30c4d8ad98ac8..53b5dd0f7b29d 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.call-opcodes.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.call-opcodes.spec.ts @@ -5,48 +5,30 @@ import { ethers } from '@nomiclabs/buidler' import { getLogger, remove0x, - add0x, TestUtils, - getCurrentTime, ZERO_ADDRESS, NULL_ADDRESS, } from '@eth-optimism/core-utils' import { Contract, ContractFactory, Signer } from 'ethers' -import { fromPairs } from 'lodash' /* Internal Imports */ import { GAS_LIMIT, - DEFAULT_OPCODE_WHITELIST_MASK, + OVM_METHOD_IDS, Address, manuallyDeployOvmContract, addressToBytes32Address, didCreateSucceed, - encodeMethodId, - encodeRawArguments, makeAddressResolver, deployAndRegister, AddressResolverMapping, + executeTestTransaction, + executePersistedTestTransaction } from '../../../test-helpers' /* Logging */ const log = getLogger('execution-manager-calls', true) -export const abi = new ethers.utils.AbiCoder() - -const methodIds = fromPairs( - [ - 'makeCall', - 'makeStaticCall', - 'makeStaticCallThenCall', - 'staticFriendlySLOAD', - 'notStaticFriendlySSTORE', - 'notStaticFriendlyCREATE', - 'notStaticFriendlyCREATE2', - 'makeDelegateCall', - ].map((methodId) => [methodId, encodeMethodId(methodId)]) -) - const sloadKey: string = '11'.repeat(32) const unpopultedSLOADResult: string = '00'.repeat(32) const populatedSLOADResult: string = '22'.repeat(32) @@ -131,9 +113,10 @@ describe('Execution Manager -- Call opcodes', () => { describe('ovmCALL', async () => { it('properly executes ovmCALL to SLOAD', async () => { - const result: string = await executeTransaction( + const result: string = await executeTestTransaction( + executionManager, callContractAddress, - methodIds.staticFriendlySLOAD, + 'staticFriendlySLOAD', [sloadKey] ) log.debug(`Result: [${result}]`) @@ -142,20 +125,23 @@ describe('Execution Manager -- Call opcodes', () => { }) it('properly executes ovmCALL to SSTORE', async () => { - await executePersistedTransaction( + await executePersistedTestTransaction( + executionManager, + wallet, callContractAddress, - methodIds.makeCall, + 'makeCall', [ addressToBytes32Address(callContract2Address), - methodIds.notStaticFriendlySSTORE, + OVM_METHOD_IDS.notStaticFriendlySSTORE, sloadKey, populatedSLOADResult, ] ) - const result: string = await executeTransaction( + const result: string = await executeTestTransaction( + executionManager, callContract2Address, - methodIds.staticFriendlySLOAD, + 'staticFriendlySLOAD', [sloadKey] ) @@ -166,9 +152,10 @@ describe('Execution Manager -- Call opcodes', () => { }) it('properly executes ovmCALL to CREATE', async () => { - const result: string = await executeTransaction( + const result: string = await executeTestTransaction( + executionManager, callContract2Address, - methodIds.notStaticFriendlyCREATE, + 'notStaticFriendlyCREATE', [deployTx.data] ) @@ -183,9 +170,10 @@ describe('Execution Manager -- Call opcodes', () => { }) it('properly executes ovmCALL to CREATE2', async () => { - const result: string = await executeTransaction( + const result: string = await executeTestTransaction( + executionManager, callContract2Address, - methodIds.notStaticFriendlyCREATE2, + 'notStaticFriendlyCREATE2', [0, deployTx.data] ) @@ -202,21 +190,24 @@ describe('Execution Manager -- Call opcodes', () => { describe('ovmDELEGATECALL', async () => { it('properly executes ovmDELEGATECALL to SSTORE', async () => { - await executePersistedTransaction( + await executePersistedTestTransaction( + executionManager, + wallet, callContractAddress, - methodIds.makeDelegateCall, + 'makeDelegateCall', [ addressToBytes32Address(callContract2Address), - methodIds.notStaticFriendlySSTORE, + OVM_METHOD_IDS.notStaticFriendlySSTORE, sloadKey, populatedSLOADResult, ] ) // Stored in contract 2 via delegate call but accessed via contract 1 - const result: string = await executeTransaction( + const result: string = await executeTestTransaction( + executionManager, callContractAddress, - methodIds.staticFriendlySLOAD, + 'staticFriendlySLOAD', [sloadKey] ) @@ -227,9 +218,10 @@ describe('Execution Manager -- Call opcodes', () => { 'SLOAD should yield stored result!' ) - const contract2Result: string = await executeTransaction( + const contract2Result: string = await executeTestTransaction( + executionManager, callContract2Address, - methodIds.staticFriendlySLOAD, + 'staticFriendlySLOAD', [sloadKey] ) @@ -244,22 +236,25 @@ describe('Execution Manager -- Call opcodes', () => { it('properly executes nested ovmDELEGATECALLs to SSTORE', async () => { // contract 1 delegate calls contract 2 delegate calls contract 3 - const result = await executePersistedTransaction( + const result = await executePersistedTestTransaction( + executionManager, + wallet, callContractAddress, - methodIds.makeDelegateCall, + 'makeDelegateCall', [ addressToBytes32Address(callContract2Address), - methodIds.makeDelegateCall, + OVM_METHOD_IDS.makeDelegateCall, addressToBytes32Address(callContract3Address), - methodIds.notStaticFriendlySSTORE, + OVM_METHOD_IDS.notStaticFriendlySSTORE, sloadKey, populatedSLOADResult, ] ) - const contract1Result: string = await executeTransaction( + const contract1Result: string = await executeTestTransaction( + executionManager, callContractAddress, - methodIds.staticFriendlySLOAD, + 'staticFriendlySLOAD', [sloadKey] ) @@ -271,9 +266,10 @@ describe('Execution Manager -- Call opcodes', () => { 'SLOAD should yield stored data!' ) - const contract2Result: string = await executeTransaction( + const contract2Result: string = await executeTestTransaction( + executionManager, callContract2Address, - methodIds.staticFriendlySLOAD, + 'staticFriendlySLOAD', [sloadKey] ) @@ -285,9 +281,10 @@ describe('Execution Manager -- Call opcodes', () => { 'SLOAD should not yield any data (0 x 32 bytes)!' ) - const contract3Result: string = await executeTransaction( + const contract3Result: string = await executeTestTransaction( + executionManager, callContract3Address, - methodIds.staticFriendlySLOAD, + 'staticFriendlySLOAD', [sloadKey] ) @@ -303,12 +300,13 @@ describe('Execution Manager -- Call opcodes', () => { describe('ovmSTATICCALL', async () => { it('properly executes ovmSTATICCALL to SLOAD', async () => { - const result = await executeTransaction( + const result = await executeTestTransaction( + executionManager, callContractAddress, - methodIds.makeStaticCall, + 'makeStaticCall', [ addressToBytes32Address(callContract2Address), - methodIds.staticFriendlySLOAD, + OVM_METHOD_IDS.staticFriendlySLOAD, sloadKey, ] ) @@ -319,14 +317,15 @@ describe('Execution Manager -- Call opcodes', () => { }) it('properly executes nested ovmSTATICCALL to SLOAD', async () => { - const result = await executeTransaction( + const result = await executeTestTransaction( + executionManager, callContractAddress, - methodIds.makeStaticCall, + 'makeStaticCall', [ addressToBytes32Address(callContract2Address), - methodIds.makeStaticCall, + OVM_METHOD_IDS.makeStaticCall, addressToBytes32Address(callContract2Address), - methodIds.staticFriendlySLOAD, + OVM_METHOD_IDS.staticFriendlySLOAD, sloadKey, ] ) @@ -338,21 +337,24 @@ describe('Execution Manager -- Call opcodes', () => { it('successfully makes static call then call', async () => { // Should not throw - await executeTransaction( + await executeTestTransaction( + executionManager, callContractAddress, - methodIds.makeStaticCallThenCall, + 'makeStaticCallThenCall', [addressToBytes32Address(callContractAddress)] ) }) it('remains in static context when exiting nested static context', async () => { await TestUtils.assertThrowsAsync(async () => { - await executePersistedTransaction( + await executePersistedTestTransaction( + executionManager, + wallet, callContractAddress, - methodIds.makeStaticCall, + 'makeStaticCall', [ addressToBytes32Address(callContractAddress), - methodIds.makeStaticCallThenCall, + OVM_METHOD_IDS.makeStaticCallThenCall, addressToBytes32Address(callContractAddress), ] ) @@ -361,12 +363,14 @@ describe('Execution Manager -- Call opcodes', () => { it('fails on ovmSTATICCALL to SSTORE', async () => { await TestUtils.assertThrowsAsync(async () => { - await executePersistedTransaction( + await executePersistedTestTransaction( + executionManager, + wallet, callContractAddress, - methodIds.makeStaticCall, + 'makeStaticCall', [ addressToBytes32Address(callContractAddress), - methodIds.notStaticFriendlySSTORE, + OVM_METHOD_IDS.notStaticFriendlySSTORE, sloadKey, populatedSLOADResult, ] @@ -375,12 +379,14 @@ describe('Execution Manager -- Call opcodes', () => { }) it('Fails to create on ovmSTATICCALL to CREATE -- tx', async () => { - const hash = await executePersistedTransaction( + const hash = await executePersistedTestTransaction( + executionManager, + wallet, callContractAddress, - methodIds.makeStaticCall, + 'makeStaticCall', [ addressToBytes32Address(callContractAddress), - methodIds.notStaticFriendlyCREATE, + OVM_METHOD_IDS.notStaticFriendlyCREATE, deployTx.data, ] ) @@ -390,12 +396,13 @@ describe('Execution Manager -- Call opcodes', () => { }) it('Fails to create on ovmSTATICCALL to CREATE -- call', async () => { - const address = await executeTransaction( + const address = await executeTestTransaction( + executionManager, callContractAddress, - methodIds.makeStaticCall, + 'makeStaticCall', [ addressToBytes32Address(callContractAddress), - methodIds.notStaticFriendlyCREATE, + OVM_METHOD_IDS.notStaticFriendlyCREATE, deployTx.data, ] ) @@ -407,12 +414,14 @@ describe('Execution Manager -- Call opcodes', () => { }) it('fails on ovmSTATICCALL to CREATE2 -- tx', async () => { - const hash = await executePersistedTransaction( + const hash = await executePersistedTestTransaction( + executionManager, + wallet, callContractAddress, - methodIds.makeStaticCall, + 'makeStaticCall', [ addressToBytes32Address(callContractAddress), - methodIds.notStaticFriendlyCREATE2, + OVM_METHOD_IDS.notStaticFriendlyCREATE2, 0, deployTx.data, ] @@ -423,12 +432,13 @@ describe('Execution Manager -- Call opcodes', () => { }) it('fails on ovmSTATICCALL to CREATE2 -- call', async () => { - const res = await executeTransaction( + const res = await executeTestTransaction( + executionManager, callContractAddress, - methodIds.makeStaticCall, + 'makeStaticCall', [ addressToBytes32Address(callContractAddress), - methodIds.notStaticFriendlyCREATE2, + OVM_METHOD_IDS.notStaticFriendlyCREATE2, 0, deployTx.data, ] @@ -440,57 +450,4 @@ describe('Execution Manager -- Call opcodes', () => { ) }) }) - - const executePersistedTransaction = async ( - contractAddress: string, - methodId: string, - args: any[] - ): Promise => { - const callBytes = add0x(methodId + encodeRawArguments(args)) - const data = executionManager.interface.encodeFunctionData( - 'executeTransaction', - [ - getCurrentTime(), - 0, - callContractAddress, - callBytes, - ZERO_ADDRESS, - ZERO_ADDRESS, - true, - ] - ) - - const receipt = await wallet.sendTransaction({ - to: executionManager.address, - data: add0x(data), - gasLimit: GAS_LIMIT, - }) - - return receipt.hash - } - - const executeTransaction = async ( - contractAddress: string, - methodId: string, - args: any[] - ): Promise => { - const callBytes = add0x(methodId + encodeRawArguments(args)) - const data = executionManager.interface.encodeFunctionData( - 'executeTransaction', - [ - getCurrentTime(), - 0, - contractAddress, - callBytes, - ZERO_ADDRESS, - ZERO_ADDRESS, - true, - ] - ) - return executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit: GAS_LIMIT, - }) - } }) diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.code-opcodes.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.code-opcodes.spec.ts index 1ada856fc5f4b..09ee14898c7aa 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.code-opcodes.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.code-opcodes.spec.ts @@ -15,7 +15,6 @@ import { Contract, ContractFactory, Signer } from 'ethers' /* Internal Imports */ import { GAS_LIMIT, - DEFAULT_OPCODE_WHITELIST_MASK, Address, manuallyDeployOvmContract, executeOVMCall, @@ -28,8 +27,6 @@ import { /* Logging */ const log = getLogger('execution-manager-code-opcodes', true) -export const abi = new ethers.utils.AbiCoder() - /* Tests */ describe('Execution Manager -- Code-related opcodes', () => { const provider = ethers.provider diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.context-opcodes.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.context-opcodes.spec.ts index 5a961c5591e08..f38c5c1a73c12 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.context-opcodes.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.context-opcodes.spec.ts @@ -7,47 +7,28 @@ import { hexStrToNumber, remove0x, add0x, - ZERO_ADDRESS, TestUtils, getCurrentTime, NULL_ADDRESS, } from '@eth-optimism/core-utils' import { Contract, ContractFactory, Signer } from 'ethers' -import { fromPairs } from 'lodash' /* Internal Imports */ import { + OVM_METHOD_IDS, GAS_LIMIT, - DEFAULT_OPCODE_WHITELIST_MASK, Address, manuallyDeployOvmContract, addressToBytes32Address, - encodeRawArguments, - encodeMethodId, makeAddressResolver, deployAndRegister, AddressResolverMapping, + executeTestTransaction, } from '../../../test-helpers' /* Logging */ const log = getLogger('execution-manager-context', true) -export const abi = new ethers.utils.AbiCoder() - -const methodIds = fromPairs( - [ - 'callThroughExecutionManager', - 'getADDRESS', - 'getCALLER', - 'getGASLIMIT', - 'getQueueOrigin', - 'getTIMESTAMP', - 'getCHAINID', - 'ovmADDRESS', - 'ovmCALLER', - ].map((methodId) => [methodId, encodeMethodId(methodId)]) -) - /* Tests */ describe('Execution Manager -- Context opcodes', () => { const provider = ethers.provider @@ -115,15 +96,21 @@ describe('Execution Manager -- Context opcodes', () => { describe('ovmCALLER', async () => { it('reverts when CALLER is not set', async () => { await TestUtils.assertThrowsAsync(async () => { - await executeTransaction(contractAddress, methodIds.ovmCALLER, []) + await executeTestTransaction( + executionManager, + contractAddress, + 'ovmCALLER', + [] + ) }) }) it('properly retrieves CALLER when caller is set', async () => { - const result = await executeTransaction( + const result = await executeTestTransaction( + executionManager, contractAddress, - methodIds.callThroughExecutionManager, - [contract2Address32, methodIds.getCALLER] + 'callThroughExecutionManager', + [contract2Address32, OVM_METHOD_IDS.getCALLER] ) log.debug(`CALLER result: ${result}`) @@ -135,15 +122,21 @@ describe('Execution Manager -- Context opcodes', () => { describe('ovmADDRESS', async () => { it('reverts when ADDRESS is not set', async () => { await TestUtils.assertThrowsAsync(async () => { - await executeTransaction(contractAddress, methodIds.ovmADDRESS, []) + await executeTestTransaction( + executionManager, + contractAddress, + 'ovmADDRESS', + [] + ) }) }) it('properly retrieves ADDRESS when address is set', async () => { - const result = await executeTransaction( + const result = await executeTestTransaction( + executionManager, contractAddress, - methodIds.callThroughExecutionManager, - [contract2Address32, methodIds.getADDRESS] + 'callThroughExecutionManager', + [contract2Address32, OVM_METHOD_IDS.getADDRESS] ) log.debug(`ADDRESS result: ${result}`) @@ -156,10 +149,11 @@ describe('Execution Manager -- Context opcodes', () => { describe('ovmTIMESTAMP', async () => { it('properly retrieves TIMESTAMP', async () => { const timestamp: number = getCurrentTime() - const result = await executeTransaction( + const result = await executeTestTransaction( + executionManager, contractAddress, - methodIds.callThroughExecutionManager, - [contract2Address32, methodIds.getTIMESTAMP] + 'callThroughExecutionManager', + [contract2Address32, OVM_METHOD_IDS.getTIMESTAMP] ) log.debug(`TIMESTAMP result: ${result}`) @@ -175,10 +169,11 @@ describe('Execution Manager -- Context opcodes', () => { describe('ovmCHAINID', async () => { it('properly retrieves CHAINID', async () => { const chainId: number = 108 - const result = await executeTransaction( + const result = await executeTestTransaction( + executionManager, contractAddress, - methodIds.callThroughExecutionManager, - [contract2Address32, methodIds.getCHAINID] + 'callThroughExecutionManager', + [contract2Address32, OVM_METHOD_IDS.getCHAINID] ) log.debug(`CHAINID result: ${result}`) @@ -190,10 +185,11 @@ describe('Execution Manager -- Context opcodes', () => { describe('ovmGASLIMIT', async () => { it('properly retrieves GASLIMIT', async () => { - const result = await executeTransaction( + const result = await executeTestTransaction( + executionManager, contractAddress, - methodIds.callThroughExecutionManager, - [contract2Address32, methodIds.getGASLIMIT] + 'callThroughExecutionManager', + [contract2Address32, OVM_METHOD_IDS.getGASLIMIT] ) log.debug(`GASLIMIT result: ${result}`) @@ -206,10 +202,11 @@ describe('Execution Manager -- Context opcodes', () => { describe('ovmQueueOrigin', async () => { it('gets Queue Origin when it is 0', async () => { const queueOrigin: string = '00'.repeat(32) - const result = await executeTransaction( + const result = await executeTestTransaction( + executionManager, contractAddress, - methodIds.callThroughExecutionManager, - [contract2Address32, methodIds.getQueueOrigin] + 'callThroughExecutionManager', + [contract2Address32, OVM_METHOD_IDS.getQueueOrigin] ) log.debug(`QUEUE ORIGIN result: ${result}`) @@ -220,10 +217,11 @@ describe('Execution Manager -- Context opcodes', () => { it('properly retrieves Queue Origin when queue origin is set', async () => { const queueOrigin: string = '00'.repeat(30) + '1111' - const result = await executeTransaction( + const result = await executeTestTransaction( + executionManager, contractAddress, - methodIds.callThroughExecutionManager, - [contract2Address32, methodIds.getQueueOrigin], + 'callThroughExecutionManager', + [contract2Address32, OVM_METHOD_IDS.getQueueOrigin], add0x(queueOrigin) ) @@ -233,30 +231,4 @@ describe('Execution Manager -- Context opcodes', () => { remove0x(result).should.equal(queueOrigin, 'Queue origins do not match.') }) }) - - const executeTransaction = async ( - address: string, - methodId: string, - args: any[], - queueOrigin = ZERO_ADDRESS - ): Promise => { - const callBytes = add0x(methodId + encodeRawArguments(args)) - const data = executionManager.interface.encodeFunctionData( - 'executeTransaction', - [ - getCurrentTime(), - queueOrigin, - address, - callBytes, - ZERO_ADDRESS, - ZERO_ADDRESS, - true, - ] - ) - return executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit: GAS_LIMIT, - }) - } }) diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.create-opcodes.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.create-opcodes.spec.ts index 305267d023000..7fb7e90011246 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.create-opcodes.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.create-opcodes.spec.ts @@ -5,35 +5,25 @@ import { ethers } from '@nomiclabs/buidler' import { getLogger, remove0x, - add0x, TestUtils, NULL_ADDRESS, } from '@eth-optimism/core-utils' import { Contract, ContractFactory, Signer } from 'ethers' -import { fromPairs } from 'lodash' /* Internal Imports */ import { DEFAULT_OPCODE_WHITELIST_MASK, GAS_LIMIT, executeOVMCall, - encodeMethodId, - encodeRawArguments, makeAddressResolver, deployAndRegister, AddressResolverMapping, + encodeFunctionData, } from '../../../test-helpers' /* Logging */ const log = getLogger('execution-manager-create', true) -const methodIds = fromPairs( - ['ovmCREATE', 'ovmCREATE2'].map((methodId) => [ - methodId, - encodeMethodId(methodId), - ]) -) - /* Tests */ describe('ExecutionManager -- Create opcodes', () => { let wallet: Signer @@ -113,9 +103,11 @@ describe('ExecutionManager -- Create opcodes', () => { safetyChecker.address ) - const data = add0x( - methodIds.ovmCREATE + encodeRawArguments([deployInvalidTx.data]) + const data = encodeFunctionData( + 'ovmCREATE', + [deployInvalidTx.data] ) + await TestUtils.assertRevertsAsync( 'Contract init (creation) code is not safe', async () => { @@ -136,8 +128,9 @@ describe('ExecutionManager -- Create opcodes', () => { stubSafetyChecker.address ) - const data = add0x( - methodIds.ovmCREATE2 + encodeRawArguments([0, deployTx.data]) + const data = encodeFunctionData( + 'ovmCREATE2', + [0, deployTx.data] ) // Now actually apply it to our execution manager @@ -160,8 +153,9 @@ describe('ExecutionManager -- Create opcodes', () => { safetyChecker.address ) - const data = add0x( - methodIds.ovmCREATE2 + encodeRawArguments([0, deployInvalidTx.data]) + const data = encodeFunctionData( + 'ovmCREATE2', + [0, deployInvalidTx.data] ) await TestUtils.assertRevertsAsync( diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.executeCall.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.executeCall.spec.ts index 6de8b64c83c5e..82cf6614871a1 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.executeCall.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.executeCall.spec.ts @@ -30,8 +30,6 @@ import { /* Logging */ const log = getLogger('execution-manager-calls', true) -export const abi = new ethers.utils.AbiCoder() - /* Tests */ describe('Execution Manager -- Call opcodes', () => { const provider = ethers.provider diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.l1-l2-opcodes.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.l1-l2-opcodes.spec.ts index 36043d1efa5f1..23573821f0bfb 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.l1-l2-opcodes.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.l1-l2-opcodes.spec.ts @@ -9,89 +9,28 @@ import { bufToHexString, ZERO_ADDRESS, NULL_ADDRESS, + abi, } from '@eth-optimism/core-utils' import { Contract, Signer, ContractFactory } from 'ethers' import * as ethereumjsAbi from 'ethereumjs-abi' -import { cloneDeep, fromPairs } from 'lodash' /* Internal Imports */ import { GAS_LIMIT, - DEFAULT_OPCODE_WHITELIST_MASK, L2_TO_L1_MESSAGE_PASSER_OVM_ADDRESS, Address, manuallyDeployOvmContract, addressToBytes32Address, - encodeMethodId, - encodeRawArguments, makeAddressResolver, deployAndRegister, AddressResolverMapping, + callExecutionManagerExecuteTransaction, + encodeFunctionData } from '../../../test-helpers' -/* Contract Imports */ -import * as ExecutionManagerJson from '../../../../artifacts/ExecutionManager.json' - /* Logging */ const log = getLogger('l2-to-l1-messaging', true) -export const abi = new ethers.utils.AbiCoder() - -const methodIds = fromPairs( - ['makeCall'].map((methodId) => [methodId, encodeMethodId(methodId)]) -) - -/*********** - * HELPERS * - **********/ - -/** - * Override the ABI description of a particular function, changing it's `constant` & `outputs` values. - * @param {Array} an abi object. - * @param {string} the name of the function we would like to change. - * @param {Object} an object containing the new `constant` & `outputs` values. - */ -function overrideAbiFunctionData( - abiDefinition: any, - functionName: string, - functionData: { constant: boolean; outputs: any[]; stateMutability: string } -): void { - for (const functionDefinition of abiDefinition) { - if (functionDefinition.name === functionName) { - functionDefinition.constant = functionData.constant - functionDefinition.outputs = functionData.outputs.map((output) => { - return { internalType: output, name: '', type: output } - }) - functionDefinition.stateMutability = functionData.stateMutability - } - } -} - -/** - * Use executeTransaction with `eth_call`. - * @param {ethers.Contract} an ExecutionManager contract instance used for it's address & provider. - * @param {Array} an array of parameters which should be fed into `executeTransaction(...)`. - * @param {OutputTypes} an array ABI types which should be used to decode the output of the call. - */ -function callExecutionManagerExecuteTransaction( - executionManager: Contract, - parameters: any[], - outputTypes: any[] -): Promise { - const modifiedAbi = cloneDeep(ExecutionManagerJson.abi) - overrideAbiFunctionData(modifiedAbi, 'executeTransaction', { - constant: true, - outputs: outputTypes, - stateMutability: 'view', - }) - const callableExecutionManager = new Contract( - executionManager.address, - modifiedAbi, - executionManager.provider - ) - return callableExecutionManager.executeTransaction.apply(null, parameters) -} - /* Tests */ describe('Execution Manager -- L1 <-> L2 Opcodes', () => { const provider = ethers.provider @@ -140,17 +79,15 @@ describe('Execution Manager -- L1 <-> L2 Opcodes', () => { describe('OVM L2 -> L1 message passer', () => { it(`Should emit the right msg.sender and calldata when an L2->L1 call is made`, async () => { const bytesToSendToL1 = '0x123412341234deadbeef' - const callBytes = add0x( - methodIds.makeCall + - encodeRawArguments([ - addressToBytes32Address(L2_TO_L1_MESSAGE_PASSER_OVM_ADDRESS), - ethereumjsAbi.methodID('passMessageToL1', ['bytes']), - abi.encode(['bytes'], [bytesToSendToL1]), - ]) - ) - const passMessageToL1MethodId = bufToHexString( - ethereumjsAbi.methodID('passMessageToL1', ['bytes']) + const callBytes = encodeFunctionData( + 'makeCall', + [ + addressToBytes32Address(L2_TO_L1_MESSAGE_PASSER_OVM_ADDRESS), + ethereumjsAbi.methodID('passMessageToL1', ['bytes']), + abi.encode(['bytes'], [bytesToSendToL1]), + ] ) + const data = executionManager.interface.encodeFunctionData( 'executeTransaction', [ diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.recover-eoa-address.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.recover-eoa-address.spec.ts index 8718cf2eb7a14..0093c45110cc3 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.recover-eoa-address.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.recover-eoa-address.spec.ts @@ -8,7 +8,6 @@ import { Contract, ContractFactory } from 'ethers' /* Internal Imports */ import { CHAIN_ID, - DEFAULT_OPCODE_WHITELIST_MASK, GAS_LIMIT, signTransaction, getSignedComponents, @@ -21,8 +20,6 @@ import { /* Logging */ const log = getLogger('execution-manager-recover-eoa-address', true) -export const abi = new ethers.utils.AbiCoder() - /* Tests */ describe('Execution Manager -- Recover EOA Address', () => { const [wallet] = getWallets() diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.storage-opcodes.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.storage-opcodes.spec.ts index a4dc971b59153..339bcc6e30086 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.storage-opcodes.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.storage-opcodes.spec.ts @@ -2,18 +2,17 @@ import '../../../setup' /* External Imports */ import { ethers } from '@nomiclabs/buidler' -import { abi, getLogger, add0x, NULL_ADDRESS } from '@eth-optimism/core-utils' +import { abi, getLogger, NULL_ADDRESS } from '@eth-optimism/core-utils' import { Contract, Signer, ContractFactory } from 'ethers' /* Internal Imports */ import { - DEFAULT_OPCODE_WHITELIST_MASK, GAS_LIMIT, - encodeMethodId, - encodeRawArguments, + encodeFunctionData, makeAddressResolver, deployAndRegister, AddressResolverMapping, + fillHexBytes, } from '../../../test-helpers' /* Logging */ @@ -22,8 +21,8 @@ const log = getLogger('execution-manager-storage', true) /* Tests */ describe('ExecutionManager -- Storage opcodes', () => { const provider = ethers.provider - const ONE_FILLED_BYTES_32 = '0x' + '11'.repeat(32) - const TWO_FILLED_BYTES_32 = '0x' + '22'.repeat(32) + const ONE_FILLED_BYTES_32 = fillHexBytes('11') + const TWO_FILLED_BYTES_32 = fillHexBytes('22') let wallet: Signer before(async () => { @@ -54,10 +53,11 @@ describe('ExecutionManager -- Storage opcodes', () => { }) const sstore = async (): Promise => { - const data = add0x( - encodeMethodId('ovmSSTORE') + - encodeRawArguments([ONE_FILLED_BYTES_32, TWO_FILLED_BYTES_32]) + const data = encodeFunctionData( + 'ovmSSTORE', + [ONE_FILLED_BYTES_32, TWO_FILLED_BYTES_32] ) + // Now actually apply it to our execution manager const tx = await wallet.sendTransaction({ to: executionManager.address, @@ -94,9 +94,9 @@ describe('ExecutionManager -- Storage opcodes', () => { it('loads a value immediately after it is stored', async () => { await sstore() - const data = add0x( - encodeMethodId('ovmSLOAD') + - encodeRawArguments([ONE_FILLED_BYTES_32, TWO_FILLED_BYTES_32]) + const data = encodeFunctionData( + 'ovmSLOAD', + [ONE_FILLED_BYTES_32, TWO_FILLED_BYTES_32] ) // Now actually apply it to our execution manager diff --git a/packages/contracts/test/test-helpers/batch-helpers.ts b/packages/contracts/test/test-helpers/batch-helpers.ts index f1b547fdeedfc..6342caddae7b7 100644 --- a/packages/contracts/test/test-helpers/batch-helpers.ts +++ b/packages/contracts/test/test-helpers/batch-helpers.ts @@ -1,3 +1,6 @@ +import { Contract, Signer } from "ethers" +import { TxChainBatch, TxQueueBatch, StateChainBatch } from "./types" + export function makeRepeatedBytes(value: string, length: number): string { const repeated = value.repeat((length * 2) / value.length + 1) return '0x' + repeated.slice(0, length * 2) @@ -14,3 +17,182 @@ export function makeRandomBlockOfSize(blockSize: number): string[] { export function makeRandomBatchOfSize(batchSize: number): string[] { return makeRandomBlockOfSize(batchSize) } + +export const appendSequencerBatch = async ( + canonicalTransactionChain: Contract, + sequencer: Signer, + batch: string[] +): Promise => { + const timestamp = Math.floor(Date.now() / 1000) + // Submit the rollup batch on-chain + await canonicalTransactionChain + .connect(sequencer) + .appendSequencerBatch(batch, timestamp) + return timestamp +} + +export const appendAndGenerateSequencerBatch = async ( + canonicalTransactionChain: Contract, + sequencer: Signer, + batch: string[], + batchIndex: number = 0, + cumulativePrevElements: number = 0 +): Promise => { + const timestamp = await appendSequencerBatch( + canonicalTransactionChain, + sequencer, + batch + ) + + return createTxChainBatch( + batch, + timestamp, + false, + batchIndex, + cumulativePrevElements + ) +} + +export const createTxChainBatch = async ( + batch: string[], + timestamp: number, + isL1ToL2Tx: boolean, + batchIndex: number = 0, + cumulativePrevElements: number = 0 +): Promise => { + const localBatch = new TxChainBatch( + timestamp, + isL1ToL2Tx, + batchIndex, + cumulativePrevElements, + batch + ) + await localBatch.generateTree() + return localBatch +} + +export const enqueueAndGenerateL1ToL2Batch = async ( + provider: any, + l1ToL2Queue: Contract, + l1ToL2TransactionPasser: Signer, + tx: string +): Promise => { + // Submit the rollup batch on-chain + const enqueueTx = await l1ToL2Queue + .connect(l1ToL2TransactionPasser) + .enqueueTx(tx) + const localBatch = await generateQueueBatch(provider, tx, enqueueTx.hash) + return localBatch +} + +export const enqueueAndGenerateSafetyBatch = async ( + provider: any, + safetyQueue: Contract, + randomWallet: Signer, + tx: string +): Promise => { + const enqueueTx = await safetyQueue.connect(randomWallet).enqueueTx(tx) + const localBatch = await generateQueueBatch(provider, tx, enqueueTx.hash) + return localBatch +} + +export const generateQueueBatch = async ( + provider: any, + tx: string, + txHash: string +): Promise => { + const txReceipt = await provider.getTransactionReceipt(txHash) + const timestamp = (await provider.getBlock(txReceipt.blockNumber)).timestamp + // Generate a local version of the rollup batch + const localBatch = new TxQueueBatch(tx, timestamp) + await localBatch.generateTree() + return localBatch +} + +export const generateStateBatch = async ( + batch: string[], + batchIndex: number = 0, + cumulativePrevElements: number = 0 +): Promise => { + const localBatch = new StateChainBatch( + batchIndex, + cumulativePrevElements, + batch + ) + await localBatch.generateTree() + return localBatch +} + +export const generateTxBatch = async ( + batch: string[], + timestamp: number, + batchIndex: number = 0, + cumulativePrevElements: number = 0 +): Promise => { + const localBatch = new TxChainBatch( + timestamp, + false, + batchIndex, + cumulativePrevElements, + batch + ) + await localBatch.generateTree() + return localBatch +} + +export const appendAndGenerateStateBatch = async ( + stateChain: Contract, + batch: string[], + batchIndex: number = 0, + cumulativePrevElements: number = 0 +): Promise => { + await stateChain.appendStateBatch(batch) + // Generate a local version of the rollup batch + const localBatch = new StateChainBatch( + batchIndex, + cumulativePrevElements, + batch + ) + await localBatch.generateTree() + return localBatch +} + +export const appendTxBatch = async ( + canonicalTxChain: Contract, + sequencer: Signer, + batch: string[] +): Promise => { + const timestamp = Math.floor(Date.now() / 1000) + // Submit the rollup batch on-chain + await canonicalTxChain + .connect(sequencer) + .appendSequencerBatch(batch, timestamp) + + return timestamp +} + +export const appendAndGenerateTransactionBatch = async ( + canonicalTransactionChain: Contract, + sequencer: Signer, + batch: string[], + batchIndex: number = 0, + cumulativePrevElements: number = 0 +): Promise => { + const timestamp = await appendTxBatch( + canonicalTransactionChain, + sequencer, + batch + ) + + const localBatch = new TxChainBatch( + timestamp, + false, + batchIndex, + cumulativePrevElements, + batch + ) + + await localBatch.generateTree() + + return localBatch +} \ No newline at end of file diff --git a/packages/contracts/test/test-helpers/constants.ts b/packages/contracts/test/test-helpers/constants.ts index ff913374b3a55..c96155a5334f6 100644 --- a/packages/contracts/test/test-helpers/constants.ts +++ b/packages/contracts/test/test-helpers/constants.ts @@ -3,9 +3,10 @@ import { ethers } from 'ethers' import { defaultAccounts } from 'ethereum-waffle' /* Internal Imports */ -import { EVMOpcode, Opcode } from './types' +import { EVMOpcode, Opcode, DEFAULT_UNSAFE_OPCODES } from '@eth-optimism/rollup-core' export { ZERO_ADDRESS } from '@eth-optimism/core-utils' +export { DEFAULT_UNSAFE_OPCODES } from '@eth-optimism/rollup-core' export const DEFAULT_ACCOUNTS = defaultAccounts export const DEFAULT_ACCOUNTS_BUIDLER = defaultAccounts.map((account) => { @@ -15,32 +16,6 @@ export const DEFAULT_ACCOUNTS_BUIDLER = defaultAccounts.map((account) => { } }) -export const DEFAULT_UNSAFE_OPCODES: EVMOpcode[] = [ - Opcode.ADDRESS, - Opcode.BALANCE, - Opcode.BLOCKHASH, - Opcode.CALLCODE, - Opcode.CALLER, - Opcode.CHAINID, - Opcode.COINBASE, - Opcode.CREATE, - Opcode.CREATE2, - Opcode.DELEGATECALL, - Opcode.DIFFICULTY, - Opcode.EXTCODESIZE, - Opcode.EXTCODECOPY, - Opcode.EXTCODEHASH, - Opcode.GASLIMIT, - Opcode.GASPRICE, - Opcode.NUMBER, - Opcode.ORIGIN, - Opcode.SELFBALANCE, - Opcode.SELFDESTRUCT, - Opcode.SLOAD, - Opcode.SSTORE, - Opcode.STATICCALL, - Opcode.TIMESTAMP, -] export const GAS_LIMIT = 1_000_000_000 export const DEFAULT_OPCODE_WHITELIST_MASK = @@ -52,3 +27,19 @@ export const L2_TO_L1_MESSAGE_PASSER_OVM_ADDRESS = export const CHAIN_ID = 108 export const ZERO_UINT = '00'.repeat(32) export const DEFAULT_FORCE_INCLUSION_PERIOD = 600 + +export const HALTING_OPCODES = Opcode.HALTING_OP_CODES +export const HALTING_OPCODES_NO_JUMP = HALTING_OPCODES.filter( + (x) => x.name !== 'JUMP' +) +export const JUMP_OPCODES = [Opcode.JUMP, Opcode.JUMPI] +export const WHITELISTED_NOT_HALTING_OR_CALL = Opcode.ALL_OP_CODES.filter( + (x) => + DEFAULT_UNSAFE_OPCODES.indexOf(x) < 0 && + HALTING_OPCODES.indexOf(x) < 0 && + x.name !== 'CALL' +) + +export const fillHexBytes = (byte: string): string => { + return '0x' + byte.repeat(32); +} \ No newline at end of file diff --git a/packages/contracts/test/test-helpers/ethereum-helpers.ts b/packages/contracts/test/test-helpers/ethereum-helpers.ts index c2f4a0b9ef0bb..cbeb4ea91a6a8 100644 --- a/packages/contracts/test/test-helpers/ethereum-helpers.ts +++ b/packages/contracts/test/test-helpers/ethereum-helpers.ts @@ -140,3 +140,11 @@ export const compile = ( } return JSON.parse(compiler.compile(JSON.stringify(input))) } + +export const encodeFunctionData = (functionName: string, functionParams: any[] = []): string => { + return add0x(encodeMethodId(functionName) + encodeRawArguments(functionParams)) +} + +export const getCodeHash = async (provider: any, address: string): Promise => { + return keccak256(await provider.getCode(address)) +} \ No newline at end of file diff --git a/packages/contracts/test/test-helpers/execution-manager-helpers.ts b/packages/contracts/test/test-helpers/execution-manager-helpers.ts new file mode 100644 index 0000000000000..044c06e4b6539 --- /dev/null +++ b/packages/contracts/test/test-helpers/execution-manager-helpers.ts @@ -0,0 +1,148 @@ +/* External Imports */ +import { Contract, Signer } from 'ethers' +import { fromPairs, cloneDeep } from 'lodash' +import { getCurrentTime, ZERO_ADDRESS, add0x } from '@eth-optimism/core-utils' + +/* Internal Imports */ +import { GAS_LIMIT } from './constants' +import { encodeMethodId, encodeFunctionData } from './ethereum-helpers' + +/* Contract Imports */ +import * as ExecutionManagerJson from '../../artifacts/ExecutionManager.json' + +export const OVM_METHOD_IDS = fromPairs( + [ + 'makeCall', + 'makeDelegateCall', + 'makeStaticCall', + 'makeStaticCallThenCall', + 'callThroughExecutionManager', + 'staticFriendlySLOAD', + 'notStaticFriendlySSTORE', + 'notStaticFriendlyCREATE', + 'notStaticFriendlyCREATE2', + 'getADDRESS', + 'getCALLER', + 'getGASLIMIT', + 'getQueueOrigin', + 'getTIMESTAMP', + 'getCHAINID', + 'ovmADDRESS', + 'ovmCALLER', + 'ovmCREATE', + 'ovmCREATE2' + ].map((methodId) => [methodId, encodeMethodId(methodId)]) +) + +/** + * Override the ABI description of a particular function, changing it's `constant` & `outputs` values. + * @param {Array} an abi object. + * @param {string} the name of the function we would like to change. + * @param {Object} an object containing the new `constant` & `outputs` values. + */ +export function overrideAbiFunctionData( + abiDefinition: any, + functionName: string, + functionData: { constant: boolean; outputs: any[]; stateMutability: string } +): void { + for (const functionDefinition of abiDefinition) { + if (functionDefinition.name === functionName) { + functionDefinition.constant = functionData.constant + functionDefinition.outputs = functionData.outputs.map((output) => { + return { internalType: output, name: '', type: output } + }) + functionDefinition.stateMutability = functionData.stateMutability + } + } +} + +/** + * Use executeTransaction with `eth_call`. + * @param {ethers.Contract} an ExecutionManager contract instance used for it's address & provider. + * @param {Array} an array of parameters which should be fed into `executeTransaction(...)`. + * @param {OutputTypes} an array ABI types which should be used to decode the output of the call. + */ +export function callExecutionManagerExecuteTransaction( + executionManager: Contract, + parameters: any[], + outputTypes: any[] +): Promise { + const modifiedAbi = cloneDeep(ExecutionManagerJson.abi) + overrideAbiFunctionData(modifiedAbi, 'executeTransaction', { + constant: true, + outputs: outputTypes, + stateMutability: 'view', + }) + const callableExecutionManager = new Contract( + executionManager.address, + modifiedAbi, + executionManager.provider + ) + return callableExecutionManager.executeTransaction.apply(null, parameters) +} + +export const executePersistedTestTransaction = async ( + executionManager: Contract, + wallet: Signer, + callContractAddress: string, + methodName: string, + args: any[] +): Promise => { + const callBytes = encodeFunctionData( + methodName, + args + ) + + const data = executionManager.interface.encodeFunctionData( + 'executeTransaction', + [ + getCurrentTime(), + 0, + callContractAddress, + callBytes, + ZERO_ADDRESS, + ZERO_ADDRESS, + true, + ] + ) + + const receipt = await wallet.sendTransaction({ + to: executionManager.address, + data: add0x(data), + gasLimit: GAS_LIMIT, + }) + + return receipt.hash +} + +export const executeTestTransaction = async ( + executionManager: Contract, + contractAddress: string, + methodName: string, + args: any[], + queueOrigin = ZERO_ADDRESS +): Promise => { + const callBytes = encodeFunctionData( + methodName, + args + ) + + const data = executionManager.interface.encodeFunctionData( + 'executeTransaction', + [ + getCurrentTime(), + queueOrigin, + contractAddress, + callBytes, + ZERO_ADDRESS, + ZERO_ADDRESS, + true, + ] + ) + + return executionManager.provider.call({ + to: executionManager.address, + data, + gasLimit: GAS_LIMIT, + }) +} \ No newline at end of file diff --git a/packages/contracts/test/test-helpers/index.ts b/packages/contracts/test/test-helpers/index.ts index f8f72c544c3f6..1ed42e3700a40 100644 --- a/packages/contracts/test/test-helpers/index.ts +++ b/packages/contracts/test/test-helpers/index.ts @@ -7,3 +7,4 @@ export * from './ovm-helpers' export * from './trie-helpers' export * from './wallet-helpers' export * from './resolution' +export * from './execution-manager-helpers' \ No newline at end of file diff --git a/packages/contracts/test/test-helpers/ovm-helpers.ts b/packages/contracts/test/test-helpers/ovm-helpers.ts index 60bb0bac1a4ae..de92d5a939d27 100644 --- a/packages/contracts/test/test-helpers/ovm-helpers.ts +++ b/packages/contracts/test/test-helpers/ovm-helpers.ts @@ -1,6 +1,7 @@ /* External Imports */ +import * as rlp from 'rlp' import { ethers, Signer, Contract, ContractFactory } from 'ethers' -import { Log, TransactionReceipt, JsonRpcProvider } from 'ethers/providers' +import { Log, TransactionReceipt } from 'ethers/providers' import { abi, add0x, @@ -11,6 +12,7 @@ import { numberToHexString, bufToHexString, getCurrentTime, + NULL_ADDRESS, } from '@eth-optimism/core-utils' /* Internal Imports */ @@ -20,6 +22,7 @@ import { encodeMethodId, encodeRawArguments } from './ethereum-helpers' /* Contract Imports */ import { getContractInterface } from '../../index' +import { toHexString } from './trie-helpers' const ExecutionManagerInterface = getContractInterface('ExecutionManager') const logger = getLogger('contracts:test-helpers', true) @@ -320,3 +323,39 @@ export const executeOVMCall = async ( gasLimit: GAS_LIMIT, }) } + +export interface OVMTransactionData { + timestamp: number + queueOrigin: number + ovmEntrypoint: string + callBytes: string + fromAddress: string + l1MsgSenderAddress: string + allowRevert: boolean +} + +export const makeDummyOvmTransaction = (calldata: string): OVMTransactionData => { + return { + timestamp: Math.floor(Date.now() / 1000), + queueOrigin: 0, + ovmEntrypoint: NULL_ADDRESS, + callBytes: calldata, + fromAddress: NULL_ADDRESS, + l1MsgSenderAddress: NULL_ADDRESS, + allowRevert: false, + } +} + +export const encodeOvmTransaction = (transaction: OVMTransactionData): string => { + return toHexString( + rlp.encode([ + transaction.timestamp, + transaction.queueOrigin, + transaction.ovmEntrypoint, + transaction.callBytes, + transaction.fromAddress, + transaction.l1MsgSenderAddress, + transaction.allowRevert ? 1 : 0, + ]) + ) +} \ No newline at end of file diff --git a/packages/contracts/test/test-helpers/types/index.ts b/packages/contracts/test/test-helpers/types/index.ts index dad87e889748c..84db67574782c 100644 --- a/packages/contracts/test/test-helpers/types/index.ts +++ b/packages/contracts/test/test-helpers/types/index.ts @@ -1,3 +1,2 @@ export * from './batch' export * from './convenience' -export * from './opcodes' diff --git a/packages/contracts/test/test-helpers/types/opcodes.ts b/packages/contracts/test/test-helpers/types/opcodes.ts deleted file mode 100644 index 25a5a5cceeeaa..0000000000000 --- a/packages/contracts/test/test-helpers/types/opcodes.ts +++ /dev/null @@ -1,990 +0,0 @@ -import { bufToHexString, remove0x } from '@eth-optimism/core-utils' - -export interface EVMOpcode { - name: string - code: Buffer - programBytesConsumed: number -} - -export interface EVMOpcodeAndBytes { - opcode: EVMOpcode - consumedBytes: Buffer - tag?: OpcodeTag -} - -export type EVMBytecode = EVMOpcodeAndBytes[] - -export interface OpcodeTag { - padPUSH: boolean // whether this PUSHN should be turned into a PUSH(N+1) to preempt later changes to consumedBytes in transpilation. - reasonTagged: string | OpcodeTagReason - metadata: any -} - -export enum OpcodeTagReason { - IS_CONSTANT_OFFSET, - IS_DEPLOY_CODECOPY_OFFSET, - IS_DEPLOY_CODE_LENGTH, - IS_CONSTRUCTOR_INPUTS_OFFSET, - IS_PUSH_BINARY_SEARCH_NODE_LOCATION, - IS_BINARY_SEARCH_NODE_JUMPDEST, - IS_PUSH_JUMPDEST_MATCH_SUCCESS_LOCATION, - IS_PUSH_OPCODE_FUNCTION_LOCATION, - IS_JUMP_TO_OPCODE_FUNCTION, - IS_OPCODE_FUNCTION_JUMPDEST, - IS_OPCODE_FUNCTION_RETURN_JUMP, - IS_OPCODE_FUNCTION_RETURN_JUMPDEST, -} - -export class Opcode { - public static readonly STOP: EVMOpcode = { - code: Buffer.from('00', 'hex'), - name: 'STOP', - programBytesConsumed: 0, - } - public static readonly ADD: EVMOpcode = { - code: Buffer.from('01', 'hex'), - name: 'ADD', - programBytesConsumed: 0, - } - public static readonly MUL: EVMOpcode = { - code: Buffer.from('02', 'hex'), - name: 'MUL', - programBytesConsumed: 0, - } - public static readonly SUB: EVMOpcode = { - code: Buffer.from('03', 'hex'), - name: 'SUB', - programBytesConsumed: 0, - } - public static readonly DIV: EVMOpcode = { - code: Buffer.from('04', 'hex'), - name: 'DIV', - programBytesConsumed: 0, - } - public static readonly SDIV: EVMOpcode = { - code: Buffer.from('05', 'hex'), - name: 'SDIV', - programBytesConsumed: 0, - } - public static readonly MOD: EVMOpcode = { - code: Buffer.from('06', 'hex'), - name: 'MOD', - programBytesConsumed: 0, - } - public static readonly SMOD: EVMOpcode = { - code: Buffer.from('07', 'hex'), - name: 'SMOD', - programBytesConsumed: 0, - } - public static readonly ADDMOD: EVMOpcode = { - code: Buffer.from('08', 'hex'), - name: 'ADDMOD', - programBytesConsumed: 0, - } - public static readonly MULMOD: EVMOpcode = { - code: Buffer.from('09', 'hex'), - name: 'MULMOD', - programBytesConsumed: 0, - } - public static readonly EXP: EVMOpcode = { - code: Buffer.from('0a', 'hex'), - name: 'EXP', - programBytesConsumed: 0, - } - public static readonly SIGNEXTEND: EVMOpcode = { - code: Buffer.from('0b', 'hex'), - name: 'SIGNEXTEND', - programBytesConsumed: 0, - } - - // gap - - public static readonly LT: EVMOpcode = { - code: Buffer.from('10', 'hex'), - name: 'LT', - programBytesConsumed: 0, - } - public static readonly GT: EVMOpcode = { - code: Buffer.from('11', 'hex'), - name: 'GT', - programBytesConsumed: 0, - } - public static readonly SLT: EVMOpcode = { - code: Buffer.from('12', 'hex'), - name: 'SLT', - programBytesConsumed: 0, - } - public static readonly SGT: EVMOpcode = { - code: Buffer.from('13', 'hex'), - name: 'SGT', - programBytesConsumed: 0, - } - public static readonly EQ: EVMOpcode = { - code: Buffer.from('14', 'hex'), - name: 'EQ', - programBytesConsumed: 0, - } - public static readonly ISZERO: EVMOpcode = { - code: Buffer.from('15', 'hex'), - name: 'ISZERO', - programBytesConsumed: 0, - } - public static readonly AND: EVMOpcode = { - code: Buffer.from('16', 'hex'), - name: 'AND', - programBytesConsumed: 0, - } - public static readonly OR: EVMOpcode = { - code: Buffer.from('17', 'hex'), - name: 'OR', - programBytesConsumed: 0, - } - public static readonly XOR: EVMOpcode = { - code: Buffer.from('18', 'hex'), - name: 'XOR', - programBytesConsumed: 0, - } - public static readonly NOT: EVMOpcode = { - code: Buffer.from('19', 'hex'), - name: 'NOT', - programBytesConsumed: 0, - } - public static readonly BYTE: EVMOpcode = { - code: Buffer.from('1a', 'hex'), - name: 'BYTE', - programBytesConsumed: 0, - } - public static readonly SHL: EVMOpcode = { - code: Buffer.from('1b', 'hex'), - name: 'SHL', - programBytesConsumed: 0, - } - public static readonly SHR: EVMOpcode = { - code: Buffer.from('1c', 'hex'), - name: 'SHR', - programBytesConsumed: 0, - } - public static readonly SAR: EVMOpcode = { - code: Buffer.from('1d', 'hex'), - name: 'SAR', - programBytesConsumed: 0, - } - - // gap - - public static readonly SHA3: EVMOpcode = { - code: Buffer.from('20', 'hex'), - name: 'SHA3', - programBytesConsumed: 0, - } - - // gap - - public static readonly ADDRESS: EVMOpcode = { - code: Buffer.from('30', 'hex'), - name: 'ADDRESS', - programBytesConsumed: 0, - } - public static readonly BALANCE: EVMOpcode = { - code: Buffer.from('31', 'hex'), - name: 'BALANCE', - programBytesConsumed: 0, - } - public static readonly ORIGIN: EVMOpcode = { - code: Buffer.from('32', 'hex'), - name: 'ORIGIN', - programBytesConsumed: 0, - } - public static readonly CALLER: EVMOpcode = { - code: Buffer.from('33', 'hex'), - name: 'CALLER', - programBytesConsumed: 0, - } - public static readonly CALLVALUE: EVMOpcode = { - code: Buffer.from('34', 'hex'), - name: 'CALLVALUE', - programBytesConsumed: 0, - } - public static readonly CALLDATALOAD: EVMOpcode = { - code: Buffer.from('35', 'hex'), - name: 'CALLDATALOAD', - programBytesConsumed: 0, - } - public static readonly CALLDATASIZE: EVMOpcode = { - code: Buffer.from('36', 'hex'), - name: 'CALLDATASIZE', - programBytesConsumed: 0, - } - public static readonly CALLDATACOPY: EVMOpcode = { - code: Buffer.from('37', 'hex'), - name: 'CALLDATACOPY', - programBytesConsumed: 0, - } - public static readonly CODESIZE: EVMOpcode = { - code: Buffer.from('38', 'hex'), - name: 'CODESIZE', - programBytesConsumed: 0, - } - public static readonly CODECOPY: EVMOpcode = { - code: Buffer.from('39', 'hex'), - name: 'CODECOPY', - programBytesConsumed: 0, - } - public static readonly GASPRICE: EVMOpcode = { - code: Buffer.from('3a', 'hex'), - name: 'GASPRICE', - programBytesConsumed: 0, - } - public static readonly EXTCODESIZE: EVMOpcode = { - code: Buffer.from('3b', 'hex'), - name: 'EXTCODESIZE', - programBytesConsumed: 0, - } - public static readonly EXTCODECOPY: EVMOpcode = { - code: Buffer.from('3c', 'hex'), - name: 'EXTCODECOPY', - programBytesConsumed: 0, - } - public static readonly RETURNDATASIZE: EVMOpcode = { - code: Buffer.from('3d', 'hex'), - name: 'RETURNDATASIZE', - programBytesConsumed: 0, - } - public static readonly RETURNDATACOPY: EVMOpcode = { - code: Buffer.from('3e', 'hex'), - name: 'RETURNDATACOPY', - programBytesConsumed: 0, - } - public static readonly EXTCODEHASH: EVMOpcode = { - code: Buffer.from('3f', 'hex'), - name: 'EXTCODEHASH', - programBytesConsumed: 0, - } - public static readonly BLOCKHASH: EVMOpcode = { - code: Buffer.from('40', 'hex'), - name: 'BLOCKHASH', - programBytesConsumed: 0, - } - public static readonly COINBASE: EVMOpcode = { - code: Buffer.from('41', 'hex'), - name: 'COINBASE', - programBytesConsumed: 0, - } - public static readonly TIMESTAMP: EVMOpcode = { - code: Buffer.from('42', 'hex'), - name: 'TIMESTAMP', - programBytesConsumed: 0, - } - public static readonly NUMBER: EVMOpcode = { - code: Buffer.from('43', 'hex'), - name: 'NUMBER', - programBytesConsumed: 0, - } - public static readonly DIFFICULTY: EVMOpcode = { - code: Buffer.from('44', 'hex'), - name: 'DIFFICULTY', - programBytesConsumed: 0, - } - public static readonly GASLIMIT: EVMOpcode = { - code: Buffer.from('45', 'hex'), - name: 'GASLIMIT', - programBytesConsumed: 0, - } - public static readonly CHAINID: EVMOpcode = { - code: Buffer.from('46', 'hex'), - name: 'CHAINID', - programBytesConsumed: 0, - } - public static readonly SELFBALANCE: EVMOpcode = { - code: Buffer.from('47', 'hex'), - name: 'SELFBALANCE', - programBytesConsumed: 0, - } - - // gap - - public static readonly POP: EVMOpcode = { - code: Buffer.from('50', 'hex'), - name: 'POP', - programBytesConsumed: 0, - } - public static readonly MLOAD: EVMOpcode = { - code: Buffer.from('51', 'hex'), - name: 'MLOAD', - programBytesConsumed: 0, - } - public static readonly MSTORE: EVMOpcode = { - code: Buffer.from('52', 'hex'), - name: 'MSTORE', - programBytesConsumed: 0, - } - public static readonly MSTORE8: EVMOpcode = { - code: Buffer.from('53', 'hex'), - name: 'MSTORE8', - programBytesConsumed: 0, - } - public static readonly SLOAD: EVMOpcode = { - code: Buffer.from('54', 'hex'), - name: 'SLOAD', - programBytesConsumed: 0, - } - public static readonly SSTORE: EVMOpcode = { - code: Buffer.from('55', 'hex'), - name: 'SSTORE', - programBytesConsumed: 0, - } - public static readonly JUMP: EVMOpcode = { - code: Buffer.from('56', 'hex'), - name: 'JUMP', - programBytesConsumed: 0, - } - public static readonly JUMPI: EVMOpcode = { - code: Buffer.from('57', 'hex'), - name: 'JUMPI', - programBytesConsumed: 0, - } - public static readonly PC: EVMOpcode = { - code: Buffer.from('58', 'hex'), - name: 'PC', - programBytesConsumed: 0, - } - public static readonly MSIZE: EVMOpcode = { - code: Buffer.from('59', 'hex'), - name: 'MSIZE', - programBytesConsumed: 0, - } - public static readonly GAS: EVMOpcode = { - code: Buffer.from('5a', 'hex'), - name: 'GAS', - programBytesConsumed: 0, - } - public static readonly JUMPDEST: EVMOpcode = { - code: Buffer.from('5b', 'hex'), - name: 'JUMPDEST', - programBytesConsumed: 0, - } - - // gap - - public static readonly PUSH1: EVMOpcode = { - code: Buffer.from('60', 'hex'), - name: 'PUSH1', - programBytesConsumed: 1, - } - public static readonly PUSH2: EVMOpcode = { - code: Buffer.from('61', 'hex'), - name: 'PUSH2', - programBytesConsumed: 2, - } - public static readonly PUSH3: EVMOpcode = { - code: Buffer.from('62', 'hex'), - name: 'PUSH3', - programBytesConsumed: 3, - } - public static readonly PUSH4: EVMOpcode = { - code: Buffer.from('63', 'hex'), - name: 'PUSH4', - programBytesConsumed: 4, - } - public static readonly PUSH5: EVMOpcode = { - code: Buffer.from('64', 'hex'), - name: 'PUSH5', - programBytesConsumed: 5, - } - public static readonly PUSH6: EVMOpcode = { - code: Buffer.from('65', 'hex'), - name: 'PUSH6', - programBytesConsumed: 6, - } - public static readonly PUSH7: EVMOpcode = { - code: Buffer.from('66', 'hex'), - name: 'PUSH7', - programBytesConsumed: 7, - } - public static readonly PUSH8: EVMOpcode = { - code: Buffer.from('67', 'hex'), - name: 'PUSH8', - programBytesConsumed: 8, - } - public static readonly PUSH9: EVMOpcode = { - code: Buffer.from('68', 'hex'), - name: 'PUSH9', - programBytesConsumed: 9, - } - public static readonly PUSH10: EVMOpcode = { - code: Buffer.from('69', 'hex'), - name: 'PUSH10', - programBytesConsumed: 10, - } - public static readonly PUSH11: EVMOpcode = { - code: Buffer.from('6a', 'hex'), - name: 'PUSH11', - programBytesConsumed: 11, - } - public static readonly PUSH12: EVMOpcode = { - code: Buffer.from('6b', 'hex'), - name: 'PUSH12', - programBytesConsumed: 12, - } - public static readonly PUSH13: EVMOpcode = { - code: Buffer.from('6c', 'hex'), - name: 'PUSH13', - programBytesConsumed: 13, - } - public static readonly PUSH14: EVMOpcode = { - code: Buffer.from('6d', 'hex'), - name: 'PUSH14', - programBytesConsumed: 14, - } - public static readonly PUSH15: EVMOpcode = { - code: Buffer.from('6e', 'hex'), - name: 'PUSH15', - programBytesConsumed: 15, - } - public static readonly PUSH16: EVMOpcode = { - code: Buffer.from('6f', 'hex'), - name: 'PUSH16', - programBytesConsumed: 16, - } - public static readonly PUSH17: EVMOpcode = { - code: Buffer.from('70', 'hex'), - name: 'PUSH17', - programBytesConsumed: 17, - } - public static readonly PUSH18: EVMOpcode = { - code: Buffer.from('71', 'hex'), - name: 'PUSH18', - programBytesConsumed: 18, - } - public static readonly PUSH19: EVMOpcode = { - code: Buffer.from('72', 'hex'), - name: 'PUSH19', - programBytesConsumed: 19, - } - public static readonly PUSH20: EVMOpcode = { - code: Buffer.from('73', 'hex'), - name: 'PUSH20', - programBytesConsumed: 20, - } - public static readonly PUSH21: EVMOpcode = { - code: Buffer.from('74', 'hex'), - name: 'PUSH21', - programBytesConsumed: 21, - } - public static readonly PUSH22: EVMOpcode = { - code: Buffer.from('75', 'hex'), - name: 'PUSH22', - programBytesConsumed: 22, - } - public static readonly PUSH23: EVMOpcode = { - code: Buffer.from('76', 'hex'), - name: 'PUSH23', - programBytesConsumed: 23, - } - public static readonly PUSH24: EVMOpcode = { - code: Buffer.from('77', 'hex'), - name: 'PUSH24', - programBytesConsumed: 24, - } - public static readonly PUSH25: EVMOpcode = { - code: Buffer.from('78', 'hex'), - name: 'PUSH25', - programBytesConsumed: 25, - } - public static readonly PUSH26: EVMOpcode = { - code: Buffer.from('79', 'hex'), - name: 'PUSH26', - programBytesConsumed: 26, - } - public static readonly PUSH27: EVMOpcode = { - code: Buffer.from('7a', 'hex'), - name: 'PUSH27', - programBytesConsumed: 27, - } - public static readonly PUSH28: EVMOpcode = { - code: Buffer.from('7b', 'hex'), - name: 'PUSH28', - programBytesConsumed: 28, - } - public static readonly PUSH29: EVMOpcode = { - code: Buffer.from('7c', 'hex'), - name: 'PUSH29', - programBytesConsumed: 29, - } - public static readonly PUSH30: EVMOpcode = { - code: Buffer.from('7d', 'hex'), - name: 'PUSH30', - programBytesConsumed: 30, - } - public static readonly PUSH31: EVMOpcode = { - code: Buffer.from('7e', 'hex'), - name: 'PUSH31', - programBytesConsumed: 31, - } - public static readonly PUSH32: EVMOpcode = { - code: Buffer.from('7f', 'hex'), - name: 'PUSH32', - programBytesConsumed: 32, - } - - public static readonly DUP1: EVMOpcode = { - code: Buffer.from('80', 'hex'), - name: 'DUP1', - programBytesConsumed: 0, - } - public static readonly DUP2: EVMOpcode = { - code: Buffer.from('81', 'hex'), - name: 'DUP2', - programBytesConsumed: 0, - } - public static readonly DUP3: EVMOpcode = { - code: Buffer.from('82', 'hex'), - name: 'DUP3', - programBytesConsumed: 0, - } - public static readonly DUP4: EVMOpcode = { - code: Buffer.from('83', 'hex'), - name: 'DUP4', - programBytesConsumed: 0, - } - public static readonly DUP5: EVMOpcode = { - code: Buffer.from('84', 'hex'), - name: 'DUP5', - programBytesConsumed: 0, - } - public static readonly DUP6: EVMOpcode = { - code: Buffer.from('85', 'hex'), - name: 'DUP6', - programBytesConsumed: 0, - } - public static readonly DUP7: EVMOpcode = { - code: Buffer.from('86', 'hex'), - name: 'DUP7', - programBytesConsumed: 0, - } - public static readonly DUP8: EVMOpcode = { - code: Buffer.from('87', 'hex'), - name: 'DUP8', - programBytesConsumed: 0, - } - public static readonly DUP9: EVMOpcode = { - code: Buffer.from('88', 'hex'), - name: 'DUP9', - programBytesConsumed: 0, - } - public static readonly DUP10: EVMOpcode = { - code: Buffer.from('89', 'hex'), - name: 'DUP10', - programBytesConsumed: 0, - } - public static readonly DUP11: EVMOpcode = { - code: Buffer.from('8a', 'hex'), - name: 'DUP11', - programBytesConsumed: 0, - } - public static readonly DUP12: EVMOpcode = { - code: Buffer.from('8b', 'hex'), - name: 'DUP12', - programBytesConsumed: 0, - } - public static readonly DUP13: EVMOpcode = { - code: Buffer.from('8c', 'hex'), - name: 'DUP13', - programBytesConsumed: 0, - } - public static readonly DUP14: EVMOpcode = { - code: Buffer.from('8d', 'hex'), - name: 'DUP14', - programBytesConsumed: 0, - } - public static readonly DUP15: EVMOpcode = { - code: Buffer.from('8e', 'hex'), - name: 'DUP15', - programBytesConsumed: 0, - } - public static readonly DUP16: EVMOpcode = { - code: Buffer.from('8f', 'hex'), - name: 'DUP16', - programBytesConsumed: 0, - } - - public static readonly SWAP1: EVMOpcode = { - code: Buffer.from('90', 'hex'), - name: 'SWAP1', - programBytesConsumed: 0, - } - public static readonly SWAP2: EVMOpcode = { - code: Buffer.from('91', 'hex'), - name: 'SWAP2', - programBytesConsumed: 0, - } - public static readonly SWAP3: EVMOpcode = { - code: Buffer.from('92', 'hex'), - name: 'SWAP3', - programBytesConsumed: 0, - } - public static readonly SWAP4: EVMOpcode = { - code: Buffer.from('93', 'hex'), - name: 'SWAP4', - programBytesConsumed: 0, - } - public static readonly SWAP5: EVMOpcode = { - code: Buffer.from('94', 'hex'), - name: 'SWAP5', - programBytesConsumed: 0, - } - public static readonly SWAP6: EVMOpcode = { - code: Buffer.from('95', 'hex'), - name: 'SWAP6', - programBytesConsumed: 0, - } - public static readonly SWAP7: EVMOpcode = { - code: Buffer.from('96', 'hex'), - name: 'SWAP7', - programBytesConsumed: 0, - } - public static readonly SWAP8: EVMOpcode = { - code: Buffer.from('97', 'hex'), - name: 'SWAP8', - programBytesConsumed: 0, - } - public static readonly SWAP9: EVMOpcode = { - code: Buffer.from('98', 'hex'), - name: 'SWAP9', - programBytesConsumed: 0, - } - public static readonly SWAP10: EVMOpcode = { - code: Buffer.from('99', 'hex'), - name: 'SWAP10', - programBytesConsumed: 0, - } - public static readonly SWAP11: EVMOpcode = { - code: Buffer.from('9a', 'hex'), - name: 'SWAP11', - programBytesConsumed: 0, - } - public static readonly SWAP12: EVMOpcode = { - code: Buffer.from('9b', 'hex'), - name: 'SWAP12', - programBytesConsumed: 0, - } - public static readonly SWAP13: EVMOpcode = { - code: Buffer.from('9c', 'hex'), - name: 'SWAP13', - programBytesConsumed: 0, - } - public static readonly SWAP14: EVMOpcode = { - code: Buffer.from('9d', 'hex'), - name: 'SWAP14', - programBytesConsumed: 0, - } - public static readonly SWAP15: EVMOpcode = { - code: Buffer.from('9e', 'hex'), - name: 'SWAP15', - programBytesConsumed: 0, - } - public static readonly SWAP16: EVMOpcode = { - code: Buffer.from('9f', 'hex'), - name: 'SWAP16', - programBytesConsumed: 0, - } - - public static readonly LOG0: EVMOpcode = { - code: Buffer.from('a0', 'hex'), - name: 'LOG0', - programBytesConsumed: 0, - } - public static readonly LOG1: EVMOpcode = { - code: Buffer.from('a1', 'hex'), - name: 'LOG1', - programBytesConsumed: 0, - } - public static readonly LOG2: EVMOpcode = { - code: Buffer.from('a2', 'hex'), - name: 'LOG2', - programBytesConsumed: 0, - } - public static readonly LOG3: EVMOpcode = { - code: Buffer.from('a3', 'hex'), - name: 'LOG3', - programBytesConsumed: 0, - } - public static readonly LOG4: EVMOpcode = { - code: Buffer.from('a4', 'hex'), - name: 'LOG4', - programBytesConsumed: 0, - } - - // gap - - public static readonly CREATE: EVMOpcode = { - code: Buffer.from('f0', 'hex'), - name: 'CREATE', - programBytesConsumed: 0, - } - public static readonly CALL: EVMOpcode = { - code: Buffer.from('f1', 'hex'), - name: 'CALL', - programBytesConsumed: 0, - } - public static readonly CALLCODE: EVMOpcode = { - code: Buffer.from('f2', 'hex'), - name: 'CALLCODE', - programBytesConsumed: 0, - } - public static readonly RETURN: EVMOpcode = { - code: Buffer.from('f3', 'hex'), - name: 'RETURN', - programBytesConsumed: 0, - } - public static readonly DELEGATECALL: EVMOpcode = { - code: Buffer.from('f4', 'hex'), - name: 'DELEGATECALL', - programBytesConsumed: 0, - } - public static readonly CREATE2: EVMOpcode = { - code: Buffer.from('f5', 'hex'), - name: 'CREATE2', - programBytesConsumed: 0, - } - - // gap - - public static readonly STATICCALL: EVMOpcode = { - code: Buffer.from('fa', 'hex'), - name: 'STATICCALL', - programBytesConsumed: 0, - } - - // gap - - public static readonly REVERT: EVMOpcode = { - code: Buffer.from('fd', 'hex'), - name: 'REVERT', - programBytesConsumed: 0, - } - public static readonly INVALID: EVMOpcode = { - code: Buffer.from('fe', 'hex'), - name: 'INVALID', - programBytesConsumed: 0, - } - public static readonly SELFDESTRUCT: EVMOpcode = { - code: Buffer.from('ff', 'hex'), - name: 'SELFDESTRUCT', - programBytesConsumed: 0, - } - - public static readonly ALL_OP_CODES: EVMOpcode[] = [ - Opcode.STOP, - Opcode.ADD, - Opcode.MUL, - Opcode.SUB, - Opcode.DIV, - Opcode.SDIV, - Opcode.MOD, - Opcode.SMOD, - Opcode.ADDMOD, - Opcode.MULMOD, - Opcode.EXP, - Opcode.SIGNEXTEND, - - Opcode.LT, - Opcode.GT, - Opcode.SLT, - Opcode.SGT, - Opcode.EQ, - Opcode.ISZERO, - Opcode.AND, - Opcode.OR, - Opcode.XOR, - Opcode.NOT, - Opcode.BYTE, - Opcode.SHL, - Opcode.SHR, - Opcode.SAR, - - Opcode.SHA3, - - Opcode.ADDRESS, - Opcode.BALANCE, - Opcode.ORIGIN, - Opcode.CALLER, - Opcode.CALLVALUE, - Opcode.CALLDATALOAD, - Opcode.CALLDATASIZE, - Opcode.CALLDATACOPY, - Opcode.CODESIZE, - Opcode.CODECOPY, - Opcode.GASPRICE, - Opcode.EXTCODESIZE, - Opcode.EXTCODECOPY, - Opcode.RETURNDATASIZE, - Opcode.RETURNDATACOPY, - Opcode.EXTCODEHASH, - Opcode.BLOCKHASH, - Opcode.COINBASE, - Opcode.TIMESTAMP, - Opcode.NUMBER, - Opcode.DIFFICULTY, - Opcode.GASLIMIT, - Opcode.CHAINID, - Opcode.SELFBALANCE, - - Opcode.POP, - Opcode.MLOAD, - Opcode.MSTORE, - Opcode.MSTORE8, - Opcode.SLOAD, - Opcode.SSTORE, - Opcode.JUMP, - Opcode.JUMPI, - Opcode.PC, - Opcode.MSIZE, - Opcode.GAS, - Opcode.JUMPDEST, - - Opcode.PUSH1, - Opcode.PUSH2, - Opcode.PUSH3, - Opcode.PUSH4, - Opcode.PUSH5, - Opcode.PUSH6, - Opcode.PUSH7, - Opcode.PUSH8, - Opcode.PUSH9, - Opcode.PUSH10, - Opcode.PUSH11, - Opcode.PUSH12, - Opcode.PUSH13, - Opcode.PUSH14, - Opcode.PUSH15, - Opcode.PUSH16, - Opcode.PUSH17, - Opcode.PUSH18, - Opcode.PUSH19, - Opcode.PUSH20, - Opcode.PUSH21, - Opcode.PUSH22, - Opcode.PUSH23, - Opcode.PUSH24, - Opcode.PUSH25, - Opcode.PUSH26, - Opcode.PUSH27, - Opcode.PUSH28, - Opcode.PUSH29, - Opcode.PUSH30, - Opcode.PUSH31, - Opcode.PUSH32, - - Opcode.DUP1, - Opcode.DUP2, - Opcode.DUP3, - Opcode.DUP4, - Opcode.DUP5, - Opcode.DUP6, - Opcode.DUP7, - Opcode.DUP8, - Opcode.DUP9, - Opcode.DUP10, - Opcode.DUP11, - Opcode.DUP12, - Opcode.DUP13, - Opcode.DUP14, - Opcode.DUP15, - Opcode.DUP16, - - Opcode.SWAP1, - Opcode.SWAP2, - Opcode.SWAP3, - Opcode.SWAP4, - Opcode.SWAP5, - Opcode.SWAP6, - Opcode.SWAP7, - Opcode.SWAP8, - Opcode.SWAP9, - Opcode.SWAP10, - Opcode.SWAP11, - Opcode.SWAP12, - Opcode.SWAP13, - Opcode.SWAP14, - Opcode.SWAP15, - Opcode.SWAP16, - - Opcode.LOG0, - Opcode.LOG1, - Opcode.LOG2, - Opcode.LOG3, - Opcode.LOG4, - - Opcode.CREATE, - Opcode.CALL, - Opcode.CALLCODE, - Opcode.RETURN, - Opcode.DELEGATECALL, - Opcode.CREATE2, - - Opcode.STATICCALL, - - Opcode.REVERT, - Opcode.INVALID, - Opcode.SELFDESTRUCT, - ] - - public static readonly HALTING_OP_CODES: EVMOpcode[] = [ - Opcode.STOP, - Opcode.JUMP, - Opcode.RETURN, - Opcode.REVERT, - Opcode.INVALID, - ] - - public static readonly JUMP_OP_CODES: EVMOpcode[] = [ - Opcode.JUMP, - Opcode.JUMPI, - ] - - private static readonly nameToOpcode: Map = new Map< - string, - EVMOpcode - >(Opcode.ALL_OP_CODES.map((x) => [x.name, x])) - private static readonly codeToOpcode: Map = new Map< - string, - EVMOpcode - >(Opcode.ALL_OP_CODES.map((x) => [x.code.toString('hex'), x])) - - public static parseByName(name: string): EVMOpcode | undefined { - return this.nameToOpcode.get(name) - } - - public static parseByCode(code: Buffer): EVMOpcode | undefined { - if (!code) { - return undefined - } - - return this.codeToOpcode.get(code.toString('hex')) - } - - public static parseByNumber(code: number): EVMOpcode | undefined { - if (code === undefined || code === null) { - return undefined - } - - if (code < 16) { - return this.codeToOpcode.get(`0${code.toString(16)}`) - } - - return this.codeToOpcode.get(code.toString(16)) - } - - public static getCodeNumber(opcode: EVMOpcode): number { - return parseInt(remove0x(bufToHexString(opcode.code)), 16) - } - - public static isPUSHOpcode(opcode: EVMOpcode): boolean { - const num: number = Opcode.getCodeNumber(opcode) - return ( - num >= Opcode.getCodeNumber(Opcode.PUSH1) && - num <= Opcode.getCodeNumber(Opcode.PUSH32) - ) - } -} From 4f80674bbcf979efb130035dd4e716e26fcc2bc8 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Tue, 28 Jul 2020 15:07:41 -0400 Subject: [PATCH 12/24] Linted files --- .../chain/CanonicalTransactionChain.spec.ts | 56 +++++++++---------- .../chain/StateCommitmentChain.spec.ts | 22 ++------ .../test/contracts/ovm/FraudVerifier.spec.ts | 2 +- .../contracts/ovm/L2ExecutionManager.spec.ts | 2 +- .../contracts/ovm/PartialStateManager.spec.ts | 5 +- .../contracts/ovm/StateTransitioner.spec.ts | 2 +- .../ExecutionManager.call-opcodes.spec.ts | 2 +- .../ExecutionManager.create-opcodes.spec.ts | 15 +---- .../ExecutionManager.l1-l2-opcodes.spec.ts | 15 ++--- .../ExecutionManager.storage-opcodes.spec.ts | 16 +++--- .../test/test-helpers/batch-helpers.ts | 6 +- .../contracts/test/test-helpers/constants.ts | 11 ++-- .../test/test-helpers/ethereum-helpers.ts | 16 ++++-- .../test-helpers/execution-manager-helpers.ts | 14 ++--- packages/contracts/test/test-helpers/index.ts | 2 +- .../test/test-helpers/ovm-helpers.ts | 10 +++- 16 files changed, 88 insertions(+), 108 deletions(-) diff --git a/packages/contracts/test/contracts/chain/CanonicalTransactionChain.spec.ts b/packages/contracts/test/contracts/chain/CanonicalTransactionChain.spec.ts index a4fd8c1ef5d0c..6be43d8ed4912 100644 --- a/packages/contracts/test/contracts/chain/CanonicalTransactionChain.spec.ts +++ b/packages/contracts/test/contracts/chain/CanonicalTransactionChain.spec.ts @@ -107,11 +107,7 @@ describe('CanonicalTransactionChain', () => { describe('appendSequencerBatch()', async () => { it('should not throw when appending a batch from the sequencer', async () => { - await appendSequencerBatch( - canonicalTxChain, - sequencer, - DEFAULT_BATCH - ) + await appendSequencerBatch(canonicalTxChain, sequencer, DEFAULT_BATCH) }) it('should throw if submitting an empty batch', async () => { @@ -119,11 +115,7 @@ describe('CanonicalTransactionChain', () => { await TestUtils.assertRevertsAsync( 'Cannot submit an empty batch', async () => { - await appendSequencerBatch( - canonicalTxChain, - sequencer, - emptyBatch - ) + await appendSequencerBatch(canonicalTxChain, sequencer, emptyBatch) } ) }) @@ -180,21 +172,13 @@ describe('CanonicalTransactionChain', () => { }) it('should add to batches array', async () => { - await appendSequencerBatch( - canonicalTxChain, - sequencer, - DEFAULT_BATCH - ) + await appendSequencerBatch(canonicalTxChain, sequencer, DEFAULT_BATCH) const batchesLength = await canonicalTxChain.getBatchesLength() batchesLength.toNumber().should.equal(1) }) it('should update cumulativeNumElements correctly', async () => { - await appendSequencerBatch( - canonicalTxChain, - sequencer, - DEFAULT_BATCH - ) + await appendSequencerBatch(canonicalTxChain, sequencer, DEFAULT_BATCH) const cumulativeNumElements = await canonicalTxChain.cumulativeNumElements.call() cumulativeNumElements.toNumber().should.equal(DEFAULT_BATCH.length) }) @@ -272,7 +256,9 @@ describe('CanonicalTransactionChain', () => { it('should revert when there is an older batch in the L1ToL2Queue', async () => { const snapshotID = await provider.send('evm_snapshot', []) - await provider.send('evm_increaseTime', [DEFAULT_FORCE_INCLUSION_PERIOD]) + await provider.send('evm_increaseTime', [ + DEFAULT_FORCE_INCLUSION_PERIOD, + ]) const newTimestamp = localBatch.timestamp + 60 await TestUtils.assertRevertsAsync( 'Must process older L1ToL2Queue batches first to enforce timestamp monotonicity', @@ -312,7 +298,9 @@ describe('CanonicalTransactionChain', () => { it('should revert when there is an older batch in the SafetyQueue', async () => { const snapshotID = await provider.send('evm_snapshot', []) - await provider.send('evm_increaseTime', [DEFAULT_FORCE_INCLUSION_PERIOD]) + await provider.send('evm_increaseTime', [ + DEFAULT_FORCE_INCLUSION_PERIOD, + ]) const newTimestamp = localBatch.timestamp + 60 await TestUtils.assertRevertsAsync( 'Must process older SafetyQueue batches first to enforce timestamp monotonicity', @@ -338,7 +326,9 @@ describe('CanonicalTransactionChain', () => { ) safetyTimestamp = localSafetyBatch.timestamp snapshotID = await provider.send('evm_snapshot', []) - await provider.send('evm_increaseTime', [DEFAULT_FORCE_INCLUSION_PERIOD / 2]) + await provider.send('evm_increaseTime', [ + DEFAULT_FORCE_INCLUSION_PERIOD / 2, + ]) const localL1ToL2Batch = await enqueueAndGenerateL1ToL2Batch( provider, l1ToL2Queue, @@ -377,7 +367,9 @@ describe('CanonicalTransactionChain', () => { }) it('should revert when appending a batch with a timestamp newer than both batches', async () => { - await provider.send('evm_increaseTime', [DEFAULT_FORCE_INCLUSION_PERIOD / 10]) // increase time by 60 seconds + await provider.send('evm_increaseTime', [ + DEFAULT_FORCE_INCLUSION_PERIOD / 10, + ]) // increase time by 60 seconds const oldTimestamp = l1ToL2Timestamp + 1 await TestUtils.assertRevertsAsync( 'Must process older L1ToL2Queue batches first to enforce timestamp monotonicity', @@ -403,7 +395,9 @@ describe('CanonicalTransactionChain', () => { ) l1ToL2Timestamp = localL1ToL2Batch.timestamp snapshotID = await provider.send('evm_snapshot', []) - await provider.send('evm_increaseTime', [DEFAULT_FORCE_INCLUSION_PERIOD / 2]) + await provider.send('evm_increaseTime', [ + DEFAULT_FORCE_INCLUSION_PERIOD / 2, + ]) const localSafetyBatch = await enqueueAndGenerateSafetyBatch( provider, safetyQueue, @@ -442,7 +436,9 @@ describe('CanonicalTransactionChain', () => { }) it('should revert when appending a batch with a timestamp newer than both batches', async () => { - await provider.send('evm_increaseTime', [DEFAULT_FORCE_INCLUSION_PERIOD / 10]) // increase time by 60 seconds + await provider.send('evm_increaseTime', [ + DEFAULT_FORCE_INCLUSION_PERIOD / 10, + ]) // increase time by 60 seconds const newTimestamp = safetyTimestamp + 1 await TestUtils.assertRevertsAsync( 'Must process older L1ToL2Queue batches first to enforce timestamp monotonicity', @@ -505,7 +501,9 @@ describe('CanonicalTransactionChain', () => { it('should allow non-sequencer to appendL1ToL2Batch after inclusion period has elapsed', async () => { const snapshotID = await provider.send('evm_snapshot', []) - await provider.send('evm_increaseTime', [DEFAULT_FORCE_INCLUSION_PERIOD]) + await provider.send('evm_increaseTime', [ + DEFAULT_FORCE_INCLUSION_PERIOD, + ]) await canonicalTxChain.appendL1ToL2Batch() await provider.send('evm_revert', [snapshotID]) }) @@ -615,7 +613,9 @@ describe('CanonicalTransactionChain', () => { it('should allow non-sequencer to appendSafetyBatch after force inclusion period has elapsed', async () => { const snapshotID = await provider.send('evm_snapshot', []) - await provider.send('evm_increaseTime', [DEFAULT_FORCE_INCLUSION_PERIOD]) + await provider.send('evm_increaseTime', [ + DEFAULT_FORCE_INCLUSION_PERIOD, + ]) await canonicalTxChain.appendSafetyBatch() await provider.send('evm_revert', [snapshotID]) }) diff --git a/packages/contracts/test/contracts/chain/StateCommitmentChain.spec.ts b/packages/contracts/test/contracts/chain/StateCommitmentChain.spec.ts index d79094a34039a..984ced05eaf1d 100644 --- a/packages/contracts/test/contracts/chain/StateCommitmentChain.spec.ts +++ b/packages/contracts/test/contracts/chain/StateCommitmentChain.spec.ts @@ -84,11 +84,7 @@ describe('StateCommitmentChain', () => { } ) - await appendTxBatch( - canonicalTxChain, - sequencer, - DEFAULT_TX_BATCH - ) + await appendTxBatch(canonicalTxChain, sequencer, DEFAULT_TX_BATCH) await resolver.addressResolver.setAddress( 'FraudVerifier', @@ -187,11 +183,7 @@ describe('StateCommitmentChain', () => { describe('verifyElement() ', async () => { it('should return true for valid elements for different batches and elements', async () => { // add enough transaction batches so # txs > # state roots - await appendTxBatch( - canonicalTxChain, - sequencer, - DEFAULT_TX_BATCH - ) + await appendTxBatch(canonicalTxChain, sequencer, DEFAULT_TX_BATCH) const numBatches = 4 let cumulativePrevElements = 0 @@ -227,10 +219,7 @@ describe('StateCommitmentChain', () => { it('should return false for wrong position with wrong indexInBatch', async () => { const batch = ['0x1234', '0x4567', '0x890a', '0x4567', '0x890a', '0xabcd'] - const localBatch = await appendAndGenerateStateBatch( - stateChain, - batch - ) + const localBatch = await appendAndGenerateStateBatch(stateChain, batch) const elementIndex = 1 const element = batch[elementIndex] const position = localBatch.getPosition(elementIndex) @@ -249,10 +238,7 @@ describe('StateCommitmentChain', () => { it('should return false for wrong position and matching indexInBatch', async () => { const batch = ['0x1234', '0x4567', '0x890a', '0x4567', '0x890a', '0xabcd'] - const localBatch = await appendAndGenerateStateBatch( - stateChain, - batch - ) + const localBatch = await appendAndGenerateStateBatch(stateChain, batch) const elementIndex = 1 const element = batch[elementIndex] const position = localBatch.getPosition(elementIndex) diff --git a/packages/contracts/test/contracts/ovm/FraudVerifier.spec.ts b/packages/contracts/test/contracts/ovm/FraudVerifier.spec.ts index 61d4d3b4f3925..d63cc5b65a3ac 100644 --- a/packages/contracts/test/contracts/ovm/FraudVerifier.spec.ts +++ b/packages/contracts/test/contracts/ovm/FraudVerifier.spec.ts @@ -15,7 +15,7 @@ import { makeDummyOvmTransaction, encodeOvmTransaction, appendAndGenerateTransactionBatch, - appendAndGenerateStateBatch + appendAndGenerateStateBatch, } from '../../test-helpers' /* Tests */ diff --git a/packages/contracts/test/contracts/ovm/L2ExecutionManager.spec.ts b/packages/contracts/test/contracts/ovm/L2ExecutionManager.spec.ts index d98e4e223b709..013fd1a7899b4 100644 --- a/packages/contracts/test/contracts/ovm/L2ExecutionManager.spec.ts +++ b/packages/contracts/test/contracts/ovm/L2ExecutionManager.spec.ts @@ -10,7 +10,7 @@ import { GAS_LIMIT, makeAddressResolver, AddressResolverMapping, - fillHexBytes + fillHexBytes, } from '../../test-helpers' /* Logging */ diff --git a/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts b/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts index d9fdfd3b69fec..465f9125dee18 100644 --- a/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts +++ b/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts @@ -6,10 +6,7 @@ import { getLogger } from '@eth-optimism/core-utils' import { Contract, ContractFactory, Signer } from 'ethers' /* Internal Imports */ -import { - makeAddressResolver, - AddressResolverMapping, -} from '../../test-helpers' +import { makeAddressResolver, AddressResolverMapping } from '../../test-helpers' /* Logging */ const log = getLogger('partial-state-manager', true) diff --git a/packages/contracts/test/contracts/ovm/StateTransitioner.spec.ts b/packages/contracts/test/contracts/ovm/StateTransitioner.spec.ts index 0f3feae263e08..771a8a0790591 100644 --- a/packages/contracts/test/contracts/ovm/StateTransitioner.spec.ts +++ b/packages/contracts/test/contracts/ovm/StateTransitioner.spec.ts @@ -27,7 +27,7 @@ import { makeDummyOvmTransaction, encodeOvmTransaction, OVMTransactionData, - getCodeHash + getCodeHash, } from '../../test-helpers' /* Logging */ diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.call-opcodes.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.call-opcodes.spec.ts index 53b5dd0f7b29d..1ee712fcd7137 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.call-opcodes.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.call-opcodes.spec.ts @@ -23,7 +23,7 @@ import { deployAndRegister, AddressResolverMapping, executeTestTransaction, - executePersistedTestTransaction + executePersistedTestTransaction, } from '../../../test-helpers' /* Logging */ diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.create-opcodes.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.create-opcodes.spec.ts index 7fb7e90011246..45aa926bb0d84 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.create-opcodes.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.create-opcodes.spec.ts @@ -103,10 +103,7 @@ describe('ExecutionManager -- Create opcodes', () => { safetyChecker.address ) - const data = encodeFunctionData( - 'ovmCREATE', - [deployInvalidTx.data] - ) + const data = encodeFunctionData('ovmCREATE', [deployInvalidTx.data]) await TestUtils.assertRevertsAsync( 'Contract init (creation) code is not safe', @@ -128,10 +125,7 @@ describe('ExecutionManager -- Create opcodes', () => { stubSafetyChecker.address ) - const data = encodeFunctionData( - 'ovmCREATE2', - [0, deployTx.data] - ) + const data = encodeFunctionData('ovmCREATE2', [0, deployTx.data]) // Now actually apply it to our execution manager const result = await executionManager.provider.call({ @@ -153,10 +147,7 @@ describe('ExecutionManager -- Create opcodes', () => { safetyChecker.address ) - const data = encodeFunctionData( - 'ovmCREATE2', - [0, deployInvalidTx.data] - ) + const data = encodeFunctionData('ovmCREATE2', [0, deployInvalidTx.data]) await TestUtils.assertRevertsAsync( 'Contract init (creation) code is not safe', diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.l1-l2-opcodes.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.l1-l2-opcodes.spec.ts index 23573821f0bfb..10531cd7349c5 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.l1-l2-opcodes.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.l1-l2-opcodes.spec.ts @@ -25,7 +25,7 @@ import { deployAndRegister, AddressResolverMapping, callExecutionManagerExecuteTransaction, - encodeFunctionData + encodeFunctionData, } from '../../../test-helpers' /* Logging */ @@ -79,14 +79,11 @@ describe('Execution Manager -- L1 <-> L2 Opcodes', () => { describe('OVM L2 -> L1 message passer', () => { it(`Should emit the right msg.sender and calldata when an L2->L1 call is made`, async () => { const bytesToSendToL1 = '0x123412341234deadbeef' - const callBytes = encodeFunctionData( - 'makeCall', - [ - addressToBytes32Address(L2_TO_L1_MESSAGE_PASSER_OVM_ADDRESS), - ethereumjsAbi.methodID('passMessageToL1', ['bytes']), - abi.encode(['bytes'], [bytesToSendToL1]), - ] - ) + const callBytes = encodeFunctionData('makeCall', [ + addressToBytes32Address(L2_TO_L1_MESSAGE_PASSER_OVM_ADDRESS), + ethereumjsAbi.methodID('passMessageToL1', ['bytes']), + abi.encode(['bytes'], [bytesToSendToL1]), + ]) const data = executionManager.interface.encodeFunctionData( 'executeTransaction', diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.storage-opcodes.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.storage-opcodes.spec.ts index 339bcc6e30086..afe0ef14796c6 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.storage-opcodes.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.storage-opcodes.spec.ts @@ -53,10 +53,10 @@ describe('ExecutionManager -- Storage opcodes', () => { }) const sstore = async (): Promise => { - const data = encodeFunctionData( - 'ovmSSTORE', - [ONE_FILLED_BYTES_32, TWO_FILLED_BYTES_32] - ) + const data = encodeFunctionData('ovmSSTORE', [ + ONE_FILLED_BYTES_32, + TWO_FILLED_BYTES_32, + ]) // Now actually apply it to our execution manager const tx = await wallet.sendTransaction({ @@ -94,10 +94,10 @@ describe('ExecutionManager -- Storage opcodes', () => { it('loads a value immediately after it is stored', async () => { await sstore() - const data = encodeFunctionData( - 'ovmSLOAD', - [ONE_FILLED_BYTES_32, TWO_FILLED_BYTES_32] - ) + const data = encodeFunctionData('ovmSLOAD', [ + ONE_FILLED_BYTES_32, + TWO_FILLED_BYTES_32, + ]) // Now actually apply it to our execution manager const result = await executionManager.provider.call({ diff --git a/packages/contracts/test/test-helpers/batch-helpers.ts b/packages/contracts/test/test-helpers/batch-helpers.ts index 6342caddae7b7..7e347bb7eb1cd 100644 --- a/packages/contracts/test/test-helpers/batch-helpers.ts +++ b/packages/contracts/test/test-helpers/batch-helpers.ts @@ -1,5 +1,5 @@ -import { Contract, Signer } from "ethers" -import { TxChainBatch, TxQueueBatch, StateChainBatch } from "./types" +import { Contract, Signer } from 'ethers' +import { TxChainBatch, TxQueueBatch, StateChainBatch } from './types' export function makeRepeatedBytes(value: string, length: number): string { const repeated = value.repeat((length * 2) / value.length + 1) @@ -195,4 +195,4 @@ export const appendAndGenerateTransactionBatch = async ( await localBatch.generateTree() return localBatch -} \ No newline at end of file +} diff --git a/packages/contracts/test/test-helpers/constants.ts b/packages/contracts/test/test-helpers/constants.ts index c96155a5334f6..f82a2e39ef98b 100644 --- a/packages/contracts/test/test-helpers/constants.ts +++ b/packages/contracts/test/test-helpers/constants.ts @@ -3,7 +3,11 @@ import { ethers } from 'ethers' import { defaultAccounts } from 'ethereum-waffle' /* Internal Imports */ -import { EVMOpcode, Opcode, DEFAULT_UNSAFE_OPCODES } from '@eth-optimism/rollup-core' +import { + EVMOpcode, + Opcode, + DEFAULT_UNSAFE_OPCODES, +} from '@eth-optimism/rollup-core' export { ZERO_ADDRESS } from '@eth-optimism/core-utils' export { DEFAULT_UNSAFE_OPCODES } from '@eth-optimism/rollup-core' @@ -16,7 +20,6 @@ export const DEFAULT_ACCOUNTS_BUIDLER = defaultAccounts.map((account) => { } }) - export const GAS_LIMIT = 1_000_000_000 export const DEFAULT_OPCODE_WHITELIST_MASK = '0x600a0000000000000000001fffffffffffffffff0fcf000063f000013fff0fff' @@ -41,5 +44,5 @@ export const WHITELISTED_NOT_HALTING_OR_CALL = Opcode.ALL_OP_CODES.filter( ) export const fillHexBytes = (byte: string): string => { - return '0x' + byte.repeat(32); -} \ No newline at end of file + return '0x' + byte.repeat(32) +} diff --git a/packages/contracts/test/test-helpers/ethereum-helpers.ts b/packages/contracts/test/test-helpers/ethereum-helpers.ts index cbeb4ea91a6a8..4e25c60965a72 100644 --- a/packages/contracts/test/test-helpers/ethereum-helpers.ts +++ b/packages/contracts/test/test-helpers/ethereum-helpers.ts @@ -141,10 +141,18 @@ export const compile = ( return JSON.parse(compiler.compile(JSON.stringify(input))) } -export const encodeFunctionData = (functionName: string, functionParams: any[] = []): string => { - return add0x(encodeMethodId(functionName) + encodeRawArguments(functionParams)) +export const encodeFunctionData = ( + functionName: string, + functionParams: any[] = [] +): string => { + return add0x( + encodeMethodId(functionName) + encodeRawArguments(functionParams) + ) } -export const getCodeHash = async (provider: any, address: string): Promise => { +export const getCodeHash = async ( + provider: any, + address: string +): Promise => { return keccak256(await provider.getCode(address)) -} \ No newline at end of file +} diff --git a/packages/contracts/test/test-helpers/execution-manager-helpers.ts b/packages/contracts/test/test-helpers/execution-manager-helpers.ts index 044c06e4b6539..92255eef48dc6 100644 --- a/packages/contracts/test/test-helpers/execution-manager-helpers.ts +++ b/packages/contracts/test/test-helpers/execution-manager-helpers.ts @@ -30,7 +30,7 @@ export const OVM_METHOD_IDS = fromPairs( 'ovmADDRESS', 'ovmCALLER', 'ovmCREATE', - 'ovmCREATE2' + 'ovmCREATE2', ].map((methodId) => [methodId, encodeMethodId(methodId)]) ) @@ -88,10 +88,7 @@ export const executePersistedTestTransaction = async ( methodName: string, args: any[] ): Promise => { - const callBytes = encodeFunctionData( - methodName, - args - ) + const callBytes = encodeFunctionData(methodName, args) const data = executionManager.interface.encodeFunctionData( 'executeTransaction', @@ -122,10 +119,7 @@ export const executeTestTransaction = async ( args: any[], queueOrigin = ZERO_ADDRESS ): Promise => { - const callBytes = encodeFunctionData( - methodName, - args - ) + const callBytes = encodeFunctionData(methodName, args) const data = executionManager.interface.encodeFunctionData( 'executeTransaction', @@ -145,4 +139,4 @@ export const executeTestTransaction = async ( data, gasLimit: GAS_LIMIT, }) -} \ No newline at end of file +} diff --git a/packages/contracts/test/test-helpers/index.ts b/packages/contracts/test/test-helpers/index.ts index 1ed42e3700a40..c3d6c9bc74f6c 100644 --- a/packages/contracts/test/test-helpers/index.ts +++ b/packages/contracts/test/test-helpers/index.ts @@ -7,4 +7,4 @@ export * from './ovm-helpers' export * from './trie-helpers' export * from './wallet-helpers' export * from './resolution' -export * from './execution-manager-helpers' \ No newline at end of file +export * from './execution-manager-helpers' diff --git a/packages/contracts/test/test-helpers/ovm-helpers.ts b/packages/contracts/test/test-helpers/ovm-helpers.ts index de92d5a939d27..2e2e6ed0f63c1 100644 --- a/packages/contracts/test/test-helpers/ovm-helpers.ts +++ b/packages/contracts/test/test-helpers/ovm-helpers.ts @@ -334,7 +334,9 @@ export interface OVMTransactionData { allowRevert: boolean } -export const makeDummyOvmTransaction = (calldata: string): OVMTransactionData => { +export const makeDummyOvmTransaction = ( + calldata: string +): OVMTransactionData => { return { timestamp: Math.floor(Date.now() / 1000), queueOrigin: 0, @@ -346,7 +348,9 @@ export const makeDummyOvmTransaction = (calldata: string): OVMTransactionData => } } -export const encodeOvmTransaction = (transaction: OVMTransactionData): string => { +export const encodeOvmTransaction = ( + transaction: OVMTransactionData +): string => { return toHexString( rlp.encode([ transaction.timestamp, @@ -358,4 +362,4 @@ export const encodeOvmTransaction = (transaction: OVMTransactionData): string => transaction.allowRevert ? 1 : 0, ]) ) -} \ No newline at end of file +} From b960c287b37955c1182d78ba3e882195a7e3c02b Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Tue, 28 Jul 2020 16:32:30 -0400 Subject: [PATCH 13/24] Started adding empty testts --- packages/contracts/package.json | 3 +- .../chain/CanonicalTransactionChain.spec.ts | 15 ++ .../contracts/ovm/PartialStateManager.spec.ts | 155 +++++++++++++++--- .../contracts/ovm/StateTransitioner.spec.ts | 50 +++++- .../test/test-helpers/ethereum-helpers.ts | 3 +- yarn.lock | 47 ++++++ 6 files changed, 237 insertions(+), 36 deletions(-) diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 4074b0d379dae..8ced431e3d793 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -25,7 +25,7 @@ "test": "yarn run test:contracts", "test:contracts": "cross-env SOLPP_FLAGS=\"FLAG_IS_TEST,FLAG_IS_DEBUG\" buidler test --show-stack-traces", "coverage": "yarn run coverage:contracts", - "coverage:contracts": "cross-env SOLPP_FLAGS=\"FLAG_IS_TEST\" buidler coverage --network coverage --show-stack-traces --testfiles \"test/contracts/**/*.spec.ts\"", + "coverage:contracts": "cross-env SOLPP_FLAGS=\"FLAG_IS_TEST\" buidler coverage --network coverage --show-stack-traces --testfiles \"test/contracts/**/StateTransitioner.spec.ts\"", "build": "yarn run build:contracts && yarn run build:typescript", "build:contracts": "buidler compile", "build:typescript": "tsc -p .", @@ -38,6 +38,7 @@ "dependencies": { "@eth-optimism/core-db": "^0.0.1-alpha.25", "@eth-optimism/core-utils": "^0.0.1-alpha.25", + "@eth-optimism/rollup-core": "^0.0.1-alpha.28", "@eth-optimism/solc-transpiler": "^0.0.1-alpha.27", "@nomiclabs/buidler": "^1.3.8", "@nomiclabs/buidler-ethers": "^2.0.0", diff --git a/packages/contracts/test/contracts/chain/CanonicalTransactionChain.spec.ts b/packages/contracts/test/contracts/chain/CanonicalTransactionChain.spec.ts index 6be43d8ed4912..e1b6f8393e81c 100644 --- a/packages/contracts/test/contracts/chain/CanonicalTransactionChain.spec.ts +++ b/packages/contracts/test/contracts/chain/CanonicalTransactionChain.spec.ts @@ -1,3 +1,4 @@ +/* tslint:disable:no-empty */ import '../../setup' /* External Imports */ @@ -105,6 +106,16 @@ describe('CanonicalTransactionChain', () => { ) }) + describe('hashBatchHeader(...)', async () => { + it('should correctly compute the hash of a given batch', async () => {}) + }) + + describe('authenticateAppend(...)', async () => { + it('should return true when the sender is the sequencer', async () => {}) + + it('should return false when the sender is not the sequencer', async () => {}) + }) + describe('appendSequencerBatch()', async () => { it('should not throw when appending a batch from the sequencer', async () => { await appendSequencerBatch(canonicalTxChain, sequencer, DEFAULT_BATCH) @@ -814,6 +825,10 @@ describe('CanonicalTransactionChain', () => { ) isIncluded.should.equal(false) }) + + it('should return false when element data is invalid', async () => {}) + + it('should return false when siblings are invalid', async () => {}) }) describe('Event Emitting', () => { diff --git a/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts b/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts index 465f9125dee18..085f06cdf3ada 100644 --- a/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts +++ b/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts @@ -1,3 +1,4 @@ +/* tslint:disable:no-empty */ import '../../setup' /* External Imports */ @@ -41,39 +42,141 @@ describe('PartialStateManager', () => { ) }) - describe('Pre-Execution', async () => { - describe('Storage Verification', async () => { - it('does not set existsInvalidStateAccessFlag=true if getStorage(contract, key) is called with a verified value', async () => { - const address = '0x' + '01'.repeat(20) - const key = '0x' + '01'.repeat(32) - const value = '0x' + '01'.repeat(32) + describe('PartialStateManager', () => { + describe('initNewTransactionExecution()', async () => { + it('should set the initial state', async () => {}) - // First verify the value - await partialStateManager.insertVerifiedStorage(address, key, value) - // Then access - await partialStateManager.getStorage(address, key) + it('should fail if not called by the state transitioner', async () => {}) + }) - const existsInvalidStateAccessFlag = await partialStateManager.existsInvalidStateAccessFlag() - existsInvalidStateAccessFlag.should.equal(false) - }) + describe('insertVerifiedStorage(...)', async () => { + it('should mark a storage slot as verified', async () => {}) - it('sets existsInvalidStateAccessFlag=true if getStorage(contract, key) is called without being verified', async () => { - const address = '0x' + '01'.repeat(20) - const key = '0x' + '01'.repeat(32) + it('should fail if not called by the state transitioner', async () => {}) + }) - // Attempt to get unverified storage! - await partialStateManager.getStorage(address, key) + describe('insertVerifiedContract(...)', async () => { + it('should mark a contract as verified', async () => {}) - const existsInvalidStateAccessFlag = await partialStateManager.existsInvalidStateAccessFlag() - existsInvalidStateAccessFlag.should.equal(true) - }) + it('should fail if not called by the state transitioner', async () => {}) }) - describe('Contract Verification', async () => { - // TODO + describe('peekUpdatedStorageSlot()', async () => { + it('should return the last storage slot on the queue', async () => {}) + + it('should fail if there are no storage slots to be updated', async () => {}) + }) + + describe('popUpdatedStorageSlot()', async () => { + it('should return the last storage slot on the queue and remove it', async () => {}) + + it('should fail if there are no storage slots to be updated', async () => {}) + + it('should fail if not called by the state transitioner', async () => {}) + }) + + describe('peekUpdatedContract()', async () => { + it('should return the last contract on the queue', async () => {}) + + it('should fail if there are no contracts to be updated', async () => {}) + }) + + describe('popUpdatedContract()', async () => { + it('should return the last contract on the queue and remove it', async () => {}) + + it('should fail if there are no contracts to be updated', async () => {}) + + it('should fail if not called by the state transitioner', async () => {}) + }) + + describe('getStorageView(...)', async () => { + it('should return the value of a storage slot for a given address', async () => {}) + + it('should return null bytes if the storage slot is not set', async () => {}) + }) + + describe('getStorage(...)', async () => { + it('should return the value of a storage slot when it exists', async () => {}) + + it('should return null bytes and flag if the storage slot is not set', async () => {}) + + it('should fail if not called by the execution manager', async () => {}) + }) + + describe('setStorage(...)', async () => { + it('should set the storage slot for a given address', async () => {}) + + it('should not change the counter if the slot has already been touched', async () => {}) + + it('should fail if not called by the execution manager', async () => {}) + }) + + describe('getOvmContractNonceView(...)', async () => { + it('should get the nonce for a given address', async () => {}) + + it('should return zero if the address has not been set', async () => {}) + }) + + describe('getOvmContractNonce(...)', async () => { + it('should get the nonce for a given address', async () => {}) + + it('should return zero and flag if the address has not been set', async () => {}) + + it('should fail if not called by the execution manager', async () => {}) + }) + + describe('setOvmContractNonce(...)', async () => { + it('should set the nonce for an address', async () => {}) + + it('should not change the counter if the address has already been touched', async () => {}) + + it('should fail if not called by the execution manager', async () => {}) + }) + + describe('incrementOvmContractNonce(...)', async () => { + it('should increase the contract nonce by one', async () => {}) + + it('should not change the counter if the address has already been touched', async () => {}) + + it('should fail if not called by the execution manager', async () => {}) + }) + + describe('associateCodeContract(...)', async () => { + it('should set the code contract address for a given ovm contract', async () => {}) + + it('should fail if not called by the execution manager', async () => {}) + }) + + describe('associateCreatedContract(...)', async () => { + it('should mark the contract as verified and set its nonce to zero', async () => {}) + + it('should fail if not called by the execution manager', async () => {}) + }) + + describe('getCodeContractAddressView(...)', async () => { + it('should return the code contract for a given ovm contract', async () => {}) + + it('should return null bytes if the contract is not associated', async () => {}) + }) + + describe('getCodeContractAddressFromOvmAddress(...)', async () => { + it('should return the code contract address when it exists', async () => {}) + + it('should return null bytes and flag when the contract does not exist', async () => {}) + + it('should fail if not called by the execution manager', async () => {}) + }) + + describe('getCodeContractBytecode(...)', async () => { + it('should get the bytecode of a contract at the given address', async () => {}) + + it('should fail if the contract does not exist', async () => {}) + }) + + describe('getCodeContractHash(...)', async () => { + it('should get the hash of the bytecode at the given address', async () => {}) + + it('should fail if the contract does not exist', async () => {}) }) - }) - describe('Post-Execution', async () => { - // TODO }) }) diff --git a/packages/contracts/test/contracts/ovm/StateTransitioner.spec.ts b/packages/contracts/test/contracts/ovm/StateTransitioner.spec.ts index 771a8a0790591..d67368310478b 100644 --- a/packages/contracts/test/contracts/ovm/StateTransitioner.spec.ts +++ b/packages/contracts/test/contracts/ovm/StateTransitioner.spec.ts @@ -1,8 +1,8 @@ +/* tslint:disable:no-empty */ import { expect } from '../../setup' /* External Imports */ import * as path from 'path' -import * as rlp from 'rlp' import { ethers } from '@nomiclabs/buidler' import { getLogger, TestUtils, remove0x } from '@eth-optimism/core-utils' import * as solc from '@eth-optimism/solc-transpiler' @@ -419,6 +419,14 @@ describe('StateTransitioner', () => { await stateManager.isVerifiedContract(fraudTester.address) ).to.equal(false) }) + + it('should fail if the code contract has not been deployed', async () => {}) + + it('should fail if the code contract is invalid', async () => {}) + + it('should fail if the state trie witness is invalid', async () => {}) + + it('should fail if the transaction has been executed', async () => {}) }) describe('proveStorageSlotInclusion(...)', async () => { @@ -474,6 +482,14 @@ describe('StateTransitioner', () => { ).to.equal(false) }) }) + + it('should fail if the transaction has already been executed', async () => {}) + + it('should fail if the provided contract has not been verified', async () => {}) + + it('should fail if the state trie witness is invalid', async () => {}) + + it('should fail if the storage trie witness is invalid', async () => {}) }) describe('applyTransaction(...)', async () => { @@ -672,6 +688,10 @@ describe('StateTransitioner', () => { STATE_TRANSITIONER_PHASES.PRE_EXECUTION ) }) + + it('should succeed even if the underlying transaction reverts', async () => {}) + + it('should fail if the provided transaction data does not match the original data', async () => {}) }) describe('Post-Execution', async () => { @@ -803,6 +823,12 @@ describe('StateTransitioner', () => { expect(await stateTransitioner.stateRoot()).to.equal(newStateTrieRoot) expect(await stateManager.updatedStorageSlotCounter()).to.equal(0) }) + + it('should revert if the transaction has not been executed', async () => {}) + + it('should revert if the provided state trie witness is invalid', async () => {}) + + it('should revert if the provided storage trie witness is invalid', async () => {}) }) describe('proveUpdatedContract(...)', async () => { @@ -890,6 +916,14 @@ describe('StateTransitioner', () => { expect(await stateTransitioner.stateRoot()).to.equal(newStateTrieRoot) expect(await stateManager.updatedContractsCounter()).to.equal(0) }) + + it('should update when an externally owned account has been modified', async () => {}) + + it('should update when multiple EOAs have been modified', async () => {}) + + it('should revert if the transaction has not been executed yet', async () => {}) + + it('should revert if the provided state trie witness is invalid', async () => {}) }) describe('completeTransition(...)', async () => { @@ -952,9 +986,7 @@ describe('StateTransitioner', () => { expect(await stateManager.updatedStorageSlotCounter()).to.equal(0) await stateTransitioner.completeTransition() - expect(await stateTransitioner.currentTransitionPhase()).to.equal( - STATE_TRANSITIONER_PHASES.COMPLETE - ) + expect(await stateTransitioner.isComplete()).to.equal(true) }) it('should correctly finalize when storage slots are changed', async () => { @@ -990,10 +1022,14 @@ describe('StateTransitioner', () => { expect(await stateManager.updatedStorageSlotCounter()).to.equal(0) await stateTransitioner.completeTransition() - expect(await stateTransitioner.currentTransitionPhase()).to.equal( - STATE_TRANSITIONER_PHASES.COMPLETE - ) + expect(await stateTransitioner.isComplete()).to.equal(true) }) + + it('should not finalize if the transaction has not been executed yet', async () => {}) + + it('should not finalize if there are still storage slots to be updated', async () => {}) + + it('should not finalize if there are still contracts to be updated', async () => {}) }) }) }) diff --git a/packages/contracts/test/test-helpers/ethereum-helpers.ts b/packages/contracts/test/test-helpers/ethereum-helpers.ts index 4e25c60965a72..47e1effc09861 100644 --- a/packages/contracts/test/test-helpers/ethereum-helpers.ts +++ b/packages/contracts/test/test-helpers/ethereum-helpers.ts @@ -1,12 +1,11 @@ import * as path from 'path' import * as fs from 'fs' -import { Transaction } from 'ethers/utils' +import { Transaction, keccak256 } from 'ethers/utils' import { Log } from 'ethers/providers' import * as ethereumjsAbi from 'ethereumjs-abi' import { add0x, remove0x, - keccak256, abi, strToHexStr, bufferUtils, diff --git a/yarn.lock b/yarn.lock index c1f89d9621f36..f21c0e264e85c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -39,6 +39,53 @@ resolved "https://registry.yarnpkg.com/@ensdomains/resolver/-/resolver-0.2.4.tgz#c10fe28bf5efbf49bff4666d909aed0265efbc89" integrity sha512-bvaTH34PMCbv6anRa9I/0zjLJgY4EuznbEMgbV77JBCQ9KNC46rzi0avuxpOfu+xDjPEtSFGqVEOr5GlUSGudA== +"@eth-optimism/core-db@^0.0.1-alpha.27": + version "0.0.1-alpha.27" + resolved "https://registry.yarnpkg.com/@eth-optimism/core-db/-/core-db-0.0.1-alpha.27.tgz#c1b5bab856d5bca44d26de9685e2fa9f1b48b579" + integrity sha512-kKkGvss/w7q57NTTFWbY1B3VKI0soClBHDvcP1t5CJrNw7ggkSrlgEbx+LsriTd/vn+fL5iUgwCIcUJn711A0Q== + dependencies: + "@eth-optimism/core-utils" "^0.0.1-alpha.27" + abstract-leveldown "^6.2.2" + async-lock "^1.2.2" + chai "^4.2.0" + chai-as-promised "^7.1.1" + debug "^4.1.1" + ethers "^4.0.39" + level "^6.0.0" + memdown "^4.0.0" + +"@eth-optimism/core-utils@^0.0.1-alpha.27": + version "0.0.1-alpha.27" + resolved "https://registry.yarnpkg.com/@eth-optimism/core-utils/-/core-utils-0.0.1-alpha.27.tgz#b43c8b5cd8ee89de7294c5f93aef5c47ee846cf5" + integrity sha512-OLQONkCevn6xaAyFlxOrL5BkE8BycvicBk0+RDL13g4OXSl/WkLaAjzUJcxYsGDzDUaB7yPUcwLQwUaT6xmcww== + dependencies: + abstract-leveldown "^6.2.2" + async-lock "^1.2.2" + axios "^0.19.0" + bn.js "^4.11.8" + body-parser "^1.19.0" + chai "^4.2.0" + chai-as-promised "^7.1.1" + debug "^4.1.1" + dotenv "^8.2.0" + ethereumjs-util "^6.2.0" + ethers "^4.0.37" + express "^4.17.1" + memdown "^4.0.0" + ts-md5 "^1.2.4" + uuid "^3.3.3" + +"@eth-optimism/rollup-core@^0.0.1-alpha.28": + version "0.0.1-alpha.28" + resolved "https://registry.yarnpkg.com/@eth-optimism/rollup-core/-/rollup-core-0.0.1-alpha.28.tgz#845de41ed266e0f1fd738776cb68bfe1fa391104" + integrity sha512-NYNIkokGCJwsZAe4pWO3FGQV4g/AmTuHZsMiYrpwIJ8+sc3cWNDWl6gyOksaofBMjRES4cGDLoU3xvO1jzI7Tw== + dependencies: + "@eth-optimism/core-db" "^0.0.1-alpha.27" + "@eth-optimism/core-utils" "^0.0.1-alpha.27" + async-lock "^1.2.2" + ethereum-waffle "2.1.0" + ethers "^4.0.39" + "@ethereum-waffle/chai@^3.0.2": version "3.0.2" resolved "https://registry.yarnpkg.com/@ethereum-waffle/chai/-/chai-3.0.2.tgz#5492398abbf2b64ec2524deac78777ee62d02d08" From f00ea24a123a165c1338e475ab333211301de25e Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Tue, 28 Jul 2020 17:11:18 -0400 Subject: [PATCH 14/24] Added comment to debug CI --- package.json | 2 +- .../execution-manager/ExecutionManager.purity-checking.spec.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index dacdd049614eb..7da78050dfa5f 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "coverage": "wsrun -p $(yarn --silent run pkgparse:core) --fast-exit --parallel --no-prefix --exclude-missing --timeout 5000 coverage", "build": "yarn run patch && lerna link && wsrun -p $(yarn --silent run pkgparse) -r --fast-exit --stages --exclude-missing build", "all": "yarn clean && yarn build && yarn test && yarn fix && yarn lint", - "release": "yarn clean && yarn run build && lesrna publish --force-publish --exact -m \"chore(@ethereum-optimism) publish %s release\"", + "release": "yarn clean && yarn run build && lerna publish --force-publish --exact -m \"chore(@ethereum-optimism) publish %s release\"", "release:patch": "yarn clean && yarn build && lerna version prerelease --yes && lerna publish from-package --yes --force-publish -m \"chore(@ethereum-optimism) publish %s release\"", "release:rc": "yarn clean && yarn build && lerna version prerelease --yes && lerna publish from-package --yes --force-publish --npm-tag=rc -m \"chore(@ethereum-optimism) publish %s release\"", "release:alpha": "yarn clean && yarn build && lerna version prerelease --yes && lerna publish from-package --yes --force-publish --dist-tag=alpha -m \"chore(@ethereum-optimism) publish %s release\"", diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.purity-checking.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.purity-checking.spec.ts index c5caaaebb2bd5..c2e18e68f032d 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.purity-checking.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.purity-checking.spec.ts @@ -103,6 +103,8 @@ describe('Execution Manager -- Safety Checking', () => { AddThree, [] ) + console.log('Execution Manager Address:', executionManager.address) + console.log('AddThree Bytecode:', AddThree.bytecode) const createSucceeded = await didCreateSucceed( executionManager, receipt.transactionHash From a6ef6b469941bc6c8b4879b03dd258412bfdfc87 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Tue, 28 Jul 2020 17:27:00 -0400 Subject: [PATCH 15/24] Added a few more tests --- .../ovm/PartialStateManager.sol | 1 + .../contracts/ovm/L2ExecutionManager.spec.ts | 20 +- .../contracts/ovm/PartialStateManager.spec.ts | 176 +++++++++--------- 3 files changed, 101 insertions(+), 96 deletions(-) diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol index 44254e8a48f0e..de8d180226d30 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol @@ -1,3 +1,4 @@ +pragma solidity ^0.5.0; pragma experimental ABIEncoderV2; /* Contract Imports */ diff --git a/packages/contracts/test/contracts/ovm/L2ExecutionManager.spec.ts b/packages/contracts/test/contracts/ovm/L2ExecutionManager.spec.ts index 013fd1a7899b4..7e5b3b145c398 100644 --- a/packages/contracts/test/contracts/ovm/L2ExecutionManager.spec.ts +++ b/packages/contracts/test/contracts/ovm/L2ExecutionManager.spec.ts @@ -20,6 +20,12 @@ const zero32: string = fillHexBytes('00') const key: string = fillHexBytes('01') const value: string = fillHexBytes('02') +const fakeSignedTx = add0x( + Buffer.from('derp') + .toString('hex') + .repeat(20) +) + describe('L2 Execution Manager', () => { let wallet: Signer before(async () => { @@ -45,17 +51,13 @@ describe('L2 Execution Manager', () => { ) }) - describe('Store OVM transactions', async () => { - const fakeSignedTx = add0x( - Buffer.from('derp') - .toString('hex') - .repeat(20) - ) - + describe('storeOvmTransaction(...)', async () => { it('properly maps OVM tx hash to internal tx hash', async () => { await l2ExecutionManager.storeOvmTransaction(key, value, fakeSignedTx) }) + }) + describe('getInternalTransactionHash(...)', async () => { it('properly reads non-existent mapping', async () => { const result = await l2ExecutionManager.getInternalTransactionHash(key) result.should.equal(zero32, 'Incorrect unpopulated result!') @@ -66,13 +68,17 @@ describe('L2 Execution Manager', () => { const result = await l2ExecutionManager.getInternalTransactionHash(key) result.should.equal(value, 'Incorrect hash mapped!') }) + }) + describe('getOvmTransactionHash(...)', async () => { it('properly reads existing internal tx hash -> OVM tx hash mapping', async () => { await l2ExecutionManager.storeOvmTransaction(key, value, fakeSignedTx) const result = await l2ExecutionManager.getOvmTransactionHash(value) result.should.equal(key, 'Incorrect hash mapped!') }) + }) + describe('getOvmTransaction(...)', async () => { it('properly reads existing OVM tx hash -> OVM tx mapping', async () => { await l2ExecutionManager.storeOvmTransaction(key, value, fakeSignedTx) const result = await l2ExecutionManager.getOvmTransaction(key) diff --git a/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts b/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts index 085f06cdf3ada..61ab1690c7482 100644 --- a/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts +++ b/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts @@ -42,141 +42,139 @@ describe('PartialStateManager', () => { ) }) - describe('PartialStateManager', () => { - describe('initNewTransactionExecution()', async () => { - it('should set the initial state', async () => {}) + describe('initNewTransactionExecution()', async () => { + it('should set the initial state', async () => {}) - it('should fail if not called by the state transitioner', async () => {}) - }) + it('should fail if not called by the state transitioner', async () => {}) + }) - describe('insertVerifiedStorage(...)', async () => { - it('should mark a storage slot as verified', async () => {}) + describe('insertVerifiedStorage(...)', async () => { + it('should mark a storage slot as verified', async () => {}) - it('should fail if not called by the state transitioner', async () => {}) - }) + it('should fail if not called by the state transitioner', async () => {}) + }) - describe('insertVerifiedContract(...)', async () => { - it('should mark a contract as verified', async () => {}) + describe('insertVerifiedContract(...)', async () => { + it('should mark a contract as verified', async () => {}) - it('should fail if not called by the state transitioner', async () => {}) - }) + it('should fail if not called by the state transitioner', async () => {}) + }) - describe('peekUpdatedStorageSlot()', async () => { - it('should return the last storage slot on the queue', async () => {}) + describe('peekUpdatedStorageSlot()', async () => { + it('should return the last storage slot on the queue', async () => {}) - it('should fail if there are no storage slots to be updated', async () => {}) - }) + it('should fail if there are no storage slots to be updated', async () => {}) + }) - describe('popUpdatedStorageSlot()', async () => { - it('should return the last storage slot on the queue and remove it', async () => {}) + describe('popUpdatedStorageSlot()', async () => { + it('should return the last storage slot on the queue and remove it', async () => {}) - it('should fail if there are no storage slots to be updated', async () => {}) + it('should fail if there are no storage slots to be updated', async () => {}) - it('should fail if not called by the state transitioner', async () => {}) - }) + it('should fail if not called by the state transitioner', async () => {}) + }) - describe('peekUpdatedContract()', async () => { - it('should return the last contract on the queue', async () => {}) + describe('peekUpdatedContract()', async () => { + it('should return the last contract on the queue', async () => {}) - it('should fail if there are no contracts to be updated', async () => {}) - }) + it('should fail if there are no contracts to be updated', async () => {}) + }) - describe('popUpdatedContract()', async () => { - it('should return the last contract on the queue and remove it', async () => {}) + describe('popUpdatedContract()', async () => { + it('should return the last contract on the queue and remove it', async () => {}) - it('should fail if there are no contracts to be updated', async () => {}) + it('should fail if there are no contracts to be updated', async () => {}) - it('should fail if not called by the state transitioner', async () => {}) - }) + it('should fail if not called by the state transitioner', async () => {}) + }) - describe('getStorageView(...)', async () => { - it('should return the value of a storage slot for a given address', async () => {}) + describe('getStorageView(...)', async () => { + it('should return the value of a storage slot for a given address', async () => {}) - it('should return null bytes if the storage slot is not set', async () => {}) - }) + it('should return null bytes if the storage slot is not set', async () => {}) + }) - describe('getStorage(...)', async () => { - it('should return the value of a storage slot when it exists', async () => {}) + describe('getStorage(...)', async () => { + it('should return the value of a storage slot when it exists', async () => {}) - it('should return null bytes and flag if the storage slot is not set', async () => {}) + it('should return null bytes and flag if the storage slot is not set', async () => {}) - it('should fail if not called by the execution manager', async () => {}) - }) + it('should fail if not called by the execution manager', async () => {}) + }) - describe('setStorage(...)', async () => { - it('should set the storage slot for a given address', async () => {}) + describe('setStorage(...)', async () => { + it('should set the storage slot for a given address', async () => {}) - it('should not change the counter if the slot has already been touched', async () => {}) + it('should not change the counter if the slot has already been touched', async () => {}) - it('should fail if not called by the execution manager', async () => {}) - }) + it('should fail if not called by the execution manager', async () => {}) + }) - describe('getOvmContractNonceView(...)', async () => { - it('should get the nonce for a given address', async () => {}) + describe('getOvmContractNonceView(...)', async () => { + it('should get the nonce for a given address', async () => {}) - it('should return zero if the address has not been set', async () => {}) - }) + it('should return zero if the address has not been set', async () => {}) + }) - describe('getOvmContractNonce(...)', async () => { - it('should get the nonce for a given address', async () => {}) + describe('getOvmContractNonce(...)', async () => { + it('should get the nonce for a given address', async () => {}) - it('should return zero and flag if the address has not been set', async () => {}) + it('should return zero and flag if the address has not been set', async () => {}) - it('should fail if not called by the execution manager', async () => {}) - }) + it('should fail if not called by the execution manager', async () => {}) + }) - describe('setOvmContractNonce(...)', async () => { - it('should set the nonce for an address', async () => {}) + describe('setOvmContractNonce(...)', async () => { + it('should set the nonce for an address', async () => {}) - it('should not change the counter if the address has already been touched', async () => {}) + it('should not change the counter if the address has already been touched', async () => {}) - it('should fail if not called by the execution manager', async () => {}) - }) + it('should fail if not called by the execution manager', async () => {}) + }) - describe('incrementOvmContractNonce(...)', async () => { - it('should increase the contract nonce by one', async () => {}) + describe('incrementOvmContractNonce(...)', async () => { + it('should increase the contract nonce by one', async () => {}) - it('should not change the counter if the address has already been touched', async () => {}) + it('should not change the counter if the address has already been touched', async () => {}) - it('should fail if not called by the execution manager', async () => {}) - }) + it('should fail if not called by the execution manager', async () => {}) + }) - describe('associateCodeContract(...)', async () => { - it('should set the code contract address for a given ovm contract', async () => {}) + describe('associateCodeContract(...)', async () => { + it('should set the code contract address for a given ovm contract', async () => {}) - it('should fail if not called by the execution manager', async () => {}) - }) + it('should fail if not called by the execution manager', async () => {}) + }) - describe('associateCreatedContract(...)', async () => { - it('should mark the contract as verified and set its nonce to zero', async () => {}) + describe('associateCreatedContract(...)', async () => { + it('should mark the contract as verified and set its nonce to zero', async () => {}) - it('should fail if not called by the execution manager', async () => {}) - }) + it('should fail if not called by the execution manager', async () => {}) + }) - describe('getCodeContractAddressView(...)', async () => { - it('should return the code contract for a given ovm contract', async () => {}) + describe('getCodeContractAddressView(...)', async () => { + it('should return the code contract for a given ovm contract', async () => {}) - it('should return null bytes if the contract is not associated', async () => {}) - }) + it('should return null bytes if the contract is not associated', async () => {}) + }) - describe('getCodeContractAddressFromOvmAddress(...)', async () => { - it('should return the code contract address when it exists', async () => {}) + describe('getCodeContractAddressFromOvmAddress(...)', async () => { + it('should return the code contract address when it exists', async () => {}) - it('should return null bytes and flag when the contract does not exist', async () => {}) + it('should return null bytes and flag when the contract does not exist', async () => {}) - it('should fail if not called by the execution manager', async () => {}) - }) + it('should fail if not called by the execution manager', async () => {}) + }) - describe('getCodeContractBytecode(...)', async () => { - it('should get the bytecode of a contract at the given address', async () => {}) + describe('getCodeContractBytecode(...)', async () => { + it('should get the bytecode of a contract at the given address', async () => {}) - it('should fail if the contract does not exist', async () => {}) - }) + it('should fail if the contract does not exist', async () => {}) + }) - describe('getCodeContractHash(...)', async () => { - it('should get the hash of the bytecode at the given address', async () => {}) + describe('getCodeContractHash(...)', async () => { + it('should get the hash of the bytecode at the given address', async () => {}) - it('should fail if the contract does not exist', async () => {}) - }) + it('should fail if the contract does not exist', async () => {}) }) }) From fd71cea32b2b6811b8b5c053e013701b642b9fa2 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Tue, 28 Jul 2020 17:40:07 -0400 Subject: [PATCH 16/24] Updated buidler config --- packages/contracts/buidler.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/contracts/buidler.config.ts b/packages/contracts/buidler.config.ts index 545df30c2b35f..66d02985942a7 100644 --- a/packages/contracts/buidler.config.ts +++ b/packages/contracts/buidler.config.ts @@ -45,6 +45,7 @@ const config: BuidlerConfig = { defs: { ...parseSolppFlags(), }, + collapseEmptyLines: true, }, } From 6a4de094ae0d2e2e5840420d0428e0e181dc61ee Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Tue, 28 Jul 2020 18:19:48 -0400 Subject: [PATCH 17/24] Added last basic tests --- packages/contracts/package.json | 2 +- .../ExecutionManager.context-opcodes.spec.ts | 38 +++++++++++++++++++ .../ExecutionManager.purity-checking.spec.ts | 3 +- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 8ced431e3d793..3d7c592393c43 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -25,7 +25,7 @@ "test": "yarn run test:contracts", "test:contracts": "cross-env SOLPP_FLAGS=\"FLAG_IS_TEST,FLAG_IS_DEBUG\" buidler test --show-stack-traces", "coverage": "yarn run coverage:contracts", - "coverage:contracts": "cross-env SOLPP_FLAGS=\"FLAG_IS_TEST\" buidler coverage --network coverage --show-stack-traces --testfiles \"test/contracts/**/StateTransitioner.spec.ts\"", + "coverage:contracts": "cross-env SOLPP_FLAGS=\"FLAG_IS_TEST\" buidler coverage --network coverage --show-stack-traces --testfiles \"test/contracts/**/*.spec.ts\"", "build": "yarn run build:contracts && yarn run build:typescript", "build:contracts": "buidler compile", "build:typescript": "tsc -p .", diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.context-opcodes.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.context-opcodes.spec.ts index f38c5c1a73c12..f26439e686c4e 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.context-opcodes.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.context-opcodes.spec.ts @@ -231,4 +231,42 @@ describe('Execution Manager -- Context opcodes', () => { remove0x(result).should.equal(queueOrigin, 'Queue origins do not match.') }) }) + + describe('ovmBlockGasLimit', async () => { + it('should retrieve the block gas limit', async () => { + + }) + }) + + describe('isStaticContext', async () => { + it('should be true when inside a static context', async () => { + + }) + + it('should be false when not in a static context', async () => { + + }) + }) + + describe('ovmORIGIN', async () => { + it('should give us the origin of the transaction', async () => { + + }) + + it('should revert if the transaction has no origin', async () => { + + }) + }) + + describe('setStateManager', async () => { + it('should allow us to change the state manager address', async () => { + + }) + }) + + describe('incrementNonce', async () => { + it('should increment a contract nonce', async () => { + + }) + }) }) diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.purity-checking.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.purity-checking.spec.ts index c2e18e68f032d..b53cf0fff6706 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.purity-checking.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.purity-checking.spec.ts @@ -103,8 +103,7 @@ describe('Execution Manager -- Safety Checking', () => { AddThree, [] ) - console.log('Execution Manager Address:', executionManager.address) - console.log('AddThree Bytecode:', AddThree.bytecode) + const createSucceeded = await didCreateSucceed( executionManager, receipt.transactionHash From 6194777b7bbbdffdbd5b27b1f47a74ccc0539016 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Tue, 28 Jul 2020 18:32:13 -0400 Subject: [PATCH 18/24] Modifications to fix tests --- .../ExecutionManager.context-opcodes.spec.ts | 29 +++++-------------- .../test/test-helpers/ethereum-helpers.ts | 4 +-- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.context-opcodes.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.context-opcodes.spec.ts index f26439e686c4e..361d29cf0d014 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.context-opcodes.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.context-opcodes.spec.ts @@ -1,3 +1,4 @@ +/* tslint:disable:no-empty */ import { should } from '../../../setup' /* External Imports */ @@ -233,40 +234,26 @@ describe('Execution Manager -- Context opcodes', () => { }) describe('ovmBlockGasLimit', async () => { - it('should retrieve the block gas limit', async () => { - - }) + it('should retrieve the block gas limit', async () => {}) }) describe('isStaticContext', async () => { - it('should be true when inside a static context', async () => { - - }) + it('should be true when inside a static context', async () => {}) - it('should be false when not in a static context', async () => { - - }) + it('should be false when not in a static context', async () => {}) }) describe('ovmORIGIN', async () => { - it('should give us the origin of the transaction', async () => { + it('should give us the origin of the transaction', async () => {}) - }) - - it('should revert if the transaction has no origin', async () => { - - }) + it('should revert if the transaction has no origin', async () => {}) }) describe('setStateManager', async () => { - it('should allow us to change the state manager address', async () => { - - }) + it('should allow us to change the state manager address', async () => {}) }) describe('incrementNonce', async () => { - it('should increment a contract nonce', async () => { - - }) + it('should increment a contract nonce', async () => {}) }) }) diff --git a/packages/contracts/test/test-helpers/ethereum-helpers.ts b/packages/contracts/test/test-helpers/ethereum-helpers.ts index 47e1effc09861..4d7870edb33e5 100644 --- a/packages/contracts/test/test-helpers/ethereum-helpers.ts +++ b/packages/contracts/test/test-helpers/ethereum-helpers.ts @@ -25,9 +25,9 @@ export const buildCreate2Address = ( saltHex: string, byteCode: string ): string => { - const preimage: string = `ff${remove0x(creatorAddress)}${remove0x( + const preimage: string = `0xff${remove0x(creatorAddress)}${remove0x( saltHex - )}${keccak256(byteCode)}` + )}${remove0x(keccak256(byteCode))}` return add0x( keccak256(preimage) .slice(-40) From ae8ac3582c69445cdf6188e13d3cbdb28eb80814 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Thu, 30 Jul 2020 16:12:33 -0400 Subject: [PATCH 19/24] Filled in remaining tests --- .../ovm/PartialStateManager.sol | 16 +- .../ovm/StateTransitioner.sol | 6 +- .../test-helpers/ContextContract.sol | 108 +++ .../contracts/test-helpers/FraudTester.sol | 4 + packages/contracts/package.json | 2 +- .../chain/CanonicalTransactionChain.spec.ts | 86 ++- .../contracts/ovm/PartialStateManager.spec.ts | 711 +++++++++++++++--- .../contracts/ovm/StateTransitioner.spec.ts | 619 +++++++++++++-- .../ExecutionManager.context-opcodes.spec.ts | 76 +- .../test-helpers/execution-manager-helpers.ts | 9 +- 10 files changed, 1473 insertions(+), 164 deletions(-) diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol index de8d180226d30..bc1eb0c542c0f 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol @@ -32,20 +32,20 @@ contract PartialStateManager is ContractResolver { StateTransitioner private stateTransitioner; - mapping(address => mapping(bytes32 => bytes32)) private ovmContractStorage; - mapping(address => uint) private ovmContractNonces; - mapping(address => address) private ovmAddressToCodeContractAddress; + mapping(address => mapping(bytes32 => bytes32)) public ovmContractStorage; + mapping(address => uint) public ovmContractNonces; + mapping(address => address) public ovmAddressToCodeContractAddress; bool public existsInvalidStateAccessFlag; mapping(address => mapping(bytes32 => bool)) public isVerifiedStorage; mapping(address => bool) public isVerifiedContract; - mapping(uint => bytes32) private updatedStorageSlotContract; - mapping(uint => bytes32) private updatedStorageSlotKey; - mapping(address => mapping(bytes32 => bool)) private storageSlotTouched; + mapping(uint => bytes32) public updatedStorageSlotContract; + mapping(uint => bytes32) public updatedStorageSlotKey; + mapping(address => mapping(bytes32 => bool)) public storageSlotTouched; uint public updatedStorageSlotCounter; - mapping(uint => address) private updatedContracts; - mapping(address => bool) private contractTouched; + mapping(uint => address) public updatedContracts; + mapping(address => bool) public contractTouched; uint public updatedContractsCounter; diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/StateTransitioner.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/StateTransitioner.sol index 8ed1e85ccdd12..0c5d34e0283d1 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/StateTransitioner.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/StateTransitioner.sol @@ -332,11 +332,11 @@ contract StateTransitioner is IStateTransitioner, ContractResolver { { require( stateManager.updatedStorageSlotCounter() == 0, - "There's still updated storage to account for!" + "All storage updates must be proven to complete the transition." ); require( - stateManager.updatedStorageSlotCounter() == 0, - "There's still updated contracts to account for!" + stateManager.updatedContractsCounter() == 0, + "All contract updates must be proven to complete the transition." ); currentTransitionPhase = TransitionPhases.Complete; diff --git a/packages/contracts/contracts/test-helpers/ContextContract.sol b/packages/contracts/contracts/test-helpers/ContextContract.sol index 53eeef34e9673..6e515a20915b1 100644 --- a/packages/contracts/contracts/test-helpers/ContextContract.sol +++ b/packages/contracts/contracts/test-helpers/ContextContract.sol @@ -40,6 +40,33 @@ contract ContextContract { } } + function staticCallThroughExecutionManager() public { + // bitwise right shift 28 * 8 bits so the 4 method ID bytes are in the right-most bytes + bytes32 methodId = keccak256("ovmSTATICCALL()") >> 224; + address addr = executionManagerAddress; + + assembly { + let callBytes := mload(0x40) + calldatacopy(callBytes, 0, calldatasize) + + // replace the first 4 bytes with the right methodID + mstore8(callBytes, shr(24, methodId)) + mstore8(add(callBytes, 1), shr(16, methodId)) + mstore8(add(callBytes, 2), shr(8, methodId)) + mstore8(add(callBytes, 3), methodId) + + // overwrite call params + let result := mload(0x40) + let success := call(gas, addr, 0, callBytes, calldatasize, result, 32) + + if eq(success, 0) { + revert(0, 0) + } + + return(result, returndatasize) + } + } + function getCALLER() public { // bitwise right shift 28 * 8 bits so the 4 method ID bytes are in the right-most bytes bytes32 methodId = keccak256("ovmCALLER()") >> 224; @@ -228,4 +255,85 @@ contract ContextContract { return(result, returndatasize) } } + + function ovmBlockGasLimit() public { + // bitwise right shift 28 * 8 bits so the 4 method ID bytes are in the right-most bytes + bytes32 methodId = keccak256("ovmBlockGasLimit()") >> 224; + address addr = executionManagerAddress; + + assembly { + let callBytes := mload(0x40) + calldatacopy(callBytes, 0, calldatasize) + + // replace the first 4 bytes with the right methodID + mstore8(callBytes, shr(24, methodId)) + mstore8(add(callBytes, 1), shr(16, methodId)) + mstore8(add(callBytes, 2), shr(8, methodId)) + mstore8(add(callBytes, 3), methodId) + + // overwrite call params + let result := mload(0x40) + let success := call(gas, addr, 0, callBytes, calldatasize, result, 32) + + if eq(success, 0) { + revert(0, 0) + } + + return(result, returndatasize) + } + } + + function isStaticContext() public { + // bitwise right shift 28 * 8 bits so the 4 method ID bytes are in the right-most bytes + bytes32 methodId = keccak256("isStaticContext()") >> 224; + address addr = executionManagerAddress; + + assembly { + let callBytes := mload(0x40) + calldatacopy(callBytes, 0, calldatasize) + + // replace the first 4 bytes with the right methodID + mstore8(callBytes, shr(24, methodId)) + mstore8(add(callBytes, 1), shr(16, methodId)) + mstore8(add(callBytes, 2), shr(8, methodId)) + mstore8(add(callBytes, 3), methodId) + + // overwrite call params + let result := mload(0x40) + let success := call(gas, addr, 0, callBytes, calldatasize, result, 32) + + if eq(success, 0) { + revert(0, 0) + } + + return(result, returndatasize) + } + } + + function ovmORIGIN() public { + // bitwise right shift 28 * 8 bits so the 4 method ID bytes are in the right-most bytes + bytes32 methodId = keccak256("ovmORIGIN()") >> 224; + address addr = executionManagerAddress; + + assembly { + let callBytes := mload(0x40) + calldatacopy(callBytes, 0, calldatasize) + + // replace the first 4 bytes with the right methodID + mstore8(callBytes, shr(24, methodId)) + mstore8(add(callBytes, 1), shr(16, methodId)) + mstore8(add(callBytes, 2), shr(8, methodId)) + mstore8(add(callBytes, 3), methodId) + + // overwrite call params + let result := mload(0x40) + let success := call(gas, addr, 0, callBytes, calldatasize, result, 32) + + if eq(success, 0) { + revert(0, 0) + } + + return(result, returndatasize) + } + } } diff --git a/packages/contracts/contracts/test-helpers/FraudTester.sol b/packages/contracts/contracts/test-helpers/FraudTester.sol index 62b1dcd7220a0..a1d1b12e16f97 100644 --- a/packages/contracts/contracts/test-helpers/FraudTester.sol +++ b/packages/contracts/contracts/test-helpers/FraudTester.sol @@ -3,6 +3,10 @@ pragma solidity ^0.5.0; contract BaseFraudTester { mapping (bytes32 => bytes32) public builtInStorage; + function doRevert() public { + revert("Performing a revert for testing."); + } + function setStorage(bytes32 key, bytes32 value) public { builtInStorage[key] = value; } diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 3d7c592393c43..3e4c797b38df5 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -23,7 +23,7 @@ "scripts": { "all": "yarn clean && yarn build && yarn test && yarn fix && yarn lint", "test": "yarn run test:contracts", - "test:contracts": "cross-env SOLPP_FLAGS=\"FLAG_IS_TEST,FLAG_IS_DEBUG\" buidler test --show-stack-traces", + "test:contracts": "cross-env SOLPP_FLAGS=\"FLAG_IS_TEST,FLAG_IS_DEBUG\" buidler test \"test/contracts/ovm/StateTransitioner.spec.ts\" --show-stack-traces", "coverage": "yarn run coverage:contracts", "coverage:contracts": "cross-env SOLPP_FLAGS=\"FLAG_IS_TEST\" buidler coverage --network coverage --show-stack-traces --testfiles \"test/contracts/**/*.spec.ts\"", "build": "yarn run build:contracts && yarn run build:typescript", diff --git a/packages/contracts/test/contracts/chain/CanonicalTransactionChain.spec.ts b/packages/contracts/test/contracts/chain/CanonicalTransactionChain.spec.ts index e1b6f8393e81c..f53c7847d959f 100644 --- a/packages/contracts/test/contracts/chain/CanonicalTransactionChain.spec.ts +++ b/packages/contracts/test/contracts/chain/CanonicalTransactionChain.spec.ts @@ -1,4 +1,3 @@ -/* tslint:disable:no-empty */ import '../../setup' /* External Imports */ @@ -17,6 +16,7 @@ import { AddressResolverMapping, appendSequencerBatch, appendAndGenerateSequencerBatch, + createTxChainBatch, enqueueAndGenerateSafetyBatch, enqueueAndGenerateL1ToL2Batch, } from '../../test-helpers' @@ -107,13 +107,44 @@ describe('CanonicalTransactionChain', () => { }) describe('hashBatchHeader(...)', async () => { - it('should correctly compute the hash of a given batch', async () => {}) + it('should correctly compute the hash of a given batch', async () => { + const localBatch = await createTxChainBatch( + DEFAULT_BATCH, + Math.floor(Date.now() / 1000), + false, + 0, + 0 + ) + + const expectedBatchHeaderHash = await localBatch.hashBatchHeader() + const calculatedBatchHeaderHash = await canonicalTxChain.hashBatchHeader({ + timestamp: localBatch.timestamp, + isL1ToL2Tx: localBatch.isL1ToL2Tx, + elementsMerkleRoot: localBatch.elementsMerkleTree.getRootHash(), + numElementsInBatch: localBatch.elements.length, + cumulativePrevElements: localBatch.cumulativePrevElements, + }) + + calculatedBatchHeaderHash.should.equal(expectedBatchHeaderHash) + }) }) describe('authenticateAppend(...)', async () => { - it('should return true when the sender is the sequencer', async () => {}) + it('should return true when the sender is the sequencer', async () => { + const result = await canonicalTxChain.authenticateAppend( + await sequencer.getAddress() + ) + + result.should.equal(true) + }) - it('should return false when the sender is not the sequencer', async () => {}) + it('should return false when the sender is not the sequencer', async () => { + const result = await canonicalTxChain.authenticateAppend( + await randomWallet.getAddress() + ) + + result.should.equal(false) + }) }) describe('appendSequencerBatch()', async () => { @@ -820,15 +851,56 @@ describe('CanonicalTransactionChain', () => { elementInclusionProof.indexInBatch++ const isIncluded = await canonicalTxChain.verifyElement( element, - wrongPosition, + position, + elementInclusionProof + ) + isIncluded.should.equal(false) + }) + + it('should return false when element data is invalid', async () => { + const batch = ['0x1234', '0x4567', '0x890a', '0x4567', '0x890a', '0xabcd'] + const localBatch = await appendAndGenerateSequencerBatch( + canonicalTxChain, + sequencer, + batch + ) + const elementIndex = 1 + const position = localBatch.getPosition(elementIndex) + const elementInclusionProof = await localBatch.getElementInclusionProof( + elementIndex + ) + + const isIncluded = await canonicalTxChain.verifyElement( + batch[elementIndex + 1], // Wrong element + position, elementInclusionProof ) + isIncluded.should.equal(false) }) - it('should return false when element data is invalid', async () => {}) + it('should return false when siblings are invalid', async () => { + const batch = ['0x1234', '0x4567', '0x890a', '0x4567', '0x890a', '0xabcd'] + const localBatch = await appendAndGenerateSequencerBatch( + canonicalTxChain, + sequencer, + batch + ) + const elementIndex = 1 + const element = batch[elementIndex] + const position = localBatch.getPosition(elementIndex) + const elementInclusionProof = await localBatch.getElementInclusionProof( + elementIndex + 1 // Proof for the wrong thing + ) - it('should return false when siblings are invalid', async () => {}) + const isIncluded = await canonicalTxChain.verifyElement( + element, + position, + elementInclusionProof + ) + + isIncluded.should.equal(false) + }) }) describe('Event Emitting', () => { diff --git a/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts b/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts index 61ab1690c7482..1b7b6bd5bc9b5 100644 --- a/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts +++ b/packages/contracts/test/contracts/ovm/PartialStateManager.spec.ts @@ -1,9 +1,8 @@ -/* tslint:disable:no-empty */ import '../../setup' /* External Imports */ import { ethers } from '@nomiclabs/buidler' -import { getLogger } from '@eth-optimism/core-utils' +import { getLogger, TestUtils, NULL_ADDRESS } from '@eth-optimism/core-utils' import { Contract, ContractFactory, Signer } from 'ethers' /* Internal Imports */ @@ -14,9 +13,38 @@ const log = getLogger('partial-state-manager', true) /* Begin tests */ describe('PartialStateManager', () => { + const DUMMY_CONTRACTS = [ + '0x' + '01'.repeat(20), + '0x' + '02'.repeat(20), + '0x' + '03'.repeat(20), + ] + + const DUMMY_CODE_CONTRACTS = [ + '0x' + '10'.repeat(20), + '0x' + '20'.repeat(20), + '0x' + '30'.repeat(20), + ] + + const DUMMY_SLOTS = [ + '0x' + '04'.repeat(32), + '0x' + '05'.repeat(32), + '0x' + '06'.repeat(32), + ] + + const DUMMY_VALUES = [ + '0x' + '07'.repeat(32), + '0x' + '08'.repeat(32), + '0x' + '09'.repeat(32), + ] + + const NULL_BYTES32 = '0x' + '00'.repeat(32) + const RLP_NULL_HASH = + '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' + let wallet: Signer + let randomWallet: Signer before(async () => { - ;[wallet] = await ethers.getSigners() + ;[wallet, randomWallet] = await ethers.getSigners() }) let resolver: AddressResolverMapping @@ -43,138 +71,649 @@ describe('PartialStateManager', () => { }) describe('initNewTransactionExecution()', async () => { - it('should set the initial state', async () => {}) - - it('should fail if not called by the state transitioner', async () => {}) + it('should set the initial state', async () => { + await partialStateManager.initNewTransactionExecution() + + const existsInvalidStateAccessFlag = await partialStateManager.existsInvalidStateAccessFlag() + const updatedStorageSlotCounter = await partialStateManager.updatedStorageSlotCounter() + const updatedContractsCounter = await partialStateManager.updatedContractsCounter() + + existsInvalidStateAccessFlag.should.equal(false) + updatedStorageSlotCounter.should.equal(0) + updatedContractsCounter.should.equal(0) + }) + + it('should fail if not called by the state transitioner', async () => { + await TestUtils.assertThrowsAsync(async () => { + await partialStateManager.initNewTransactionExecution({ + from: await randomWallet.getAddress(), + }) + }) + }) }) describe('insertVerifiedStorage(...)', async () => { - it('should mark a storage slot as verified', async () => {}) - - it('should fail if not called by the state transitioner', async () => {}) + it('should mark a storage slot as verified', async () => { + await partialStateManager.insertVerifiedStorage( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0], + DUMMY_VALUES[0] + ) + + const isVerifiedStorage = await partialStateManager.isVerifiedStorage( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0] + ) + const ovmContractStorage = await partialStateManager.ovmContractStorage( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0] + ) + + isVerifiedStorage.should.equal(true) + ovmContractStorage.should.equal(DUMMY_VALUES[0]) + }) + + it('should fail if not called by the state transitioner', async () => { + await TestUtils.assertThrowsAsync(async () => { + await partialStateManager.insertVerifiedStorage( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0], + DUMMY_VALUES[0], + { + from: await randomWallet.getAddress(), + } + ) + }) + }) }) describe('insertVerifiedContract(...)', async () => { - it('should mark a contract as verified', async () => {}) - - it('should fail if not called by the state transitioner', async () => {}) - }) - - describe('peekUpdatedStorageSlot()', async () => { - it('should return the last storage slot on the queue', async () => {}) - - it('should fail if there are no storage slots to be updated', async () => {}) - }) - - describe('popUpdatedStorageSlot()', async () => { - it('should return the last storage slot on the queue and remove it', async () => {}) - - it('should fail if there are no storage slots to be updated', async () => {}) - - it('should fail if not called by the state transitioner', async () => {}) + it('should mark a contract as verified', async () => { + const nonce = 1234 + await partialStateManager.insertVerifiedContract( + DUMMY_CONTRACTS[0], + DUMMY_CODE_CONTRACTS[0], + nonce + ) + + const isVerifiedContract = await partialStateManager.isVerifiedContract( + DUMMY_CONTRACTS[0] + ) + const ovmContractNonce = await partialStateManager.ovmContractNonces( + DUMMY_CONTRACTS[0] + ) + const codeContractAddress = await partialStateManager.ovmAddressToCodeContractAddress( + DUMMY_CONTRACTS[0] + ) + + isVerifiedContract.should.equal(true) + ovmContractNonce.should.equal(nonce) + codeContractAddress.should.equal(DUMMY_CODE_CONTRACTS[0]) + }) + + it('should fail if not called by the state transitioner', async () => { + await TestUtils.assertThrowsAsync(async () => { + const nonce = 1234 + await partialStateManager.insertVerifiedContract( + DUMMY_CONTRACTS[0], + DUMMY_CODE_CONTRACTS[0], + nonce, + { + from: await randomWallet.getAddress(), + } + ) + }) + }) }) - describe('peekUpdatedContract()', async () => { - it('should return the last contract on the queue', async () => {}) - - it('should fail if there are no contracts to be updated', async () => {}) - }) - - describe('popUpdatedContract()', async () => { - it('should return the last contract on the queue and remove it', async () => {}) - - it('should fail if there are no contracts to be updated', async () => {}) - - it('should fail if not called by the state transitioner', async () => {}) + describe('setStorage(...)', async () => { + it('should set the storage slot for a given address', async () => { + await partialStateManager.setStorage( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0], + DUMMY_VALUES[0] + ) + + const slotContract = await partialStateManager.updatedStorageSlotContract( + 0 + ) + const slotKey = await partialStateManager.updatedStorageSlotKey(0) + const slotTouched = await partialStateManager.storageSlotTouched( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0] + ) + const slotValue = await partialStateManager.ovmContractStorage( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0] + ) + const counter = await partialStateManager.updatedStorageSlotCounter() + + slotContract.should.equal(DUMMY_CONTRACTS[0] + '00'.repeat(12)) + slotKey.should.equal(DUMMY_SLOTS[0]) + slotTouched.should.equal(true) + slotValue.should.equal(DUMMY_VALUES[0]) + counter.should.equal(1) + }) + + it('should not change the counter if the slot has already been touched', async () => { + await partialStateManager.setStorage( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0], + DUMMY_VALUES[0] + ) + + await partialStateManager.setStorage( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0], + DUMMY_VALUES[1] + ) + + const slotContract = await partialStateManager.updatedStorageSlotContract( + 0 + ) + const slotKey = await partialStateManager.updatedStorageSlotKey(0) + const slotTouched = await partialStateManager.storageSlotTouched( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0] + ) + const slotValue = await partialStateManager.ovmContractStorage( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0] + ) + const counter = await partialStateManager.updatedStorageSlotCounter() + + slotContract.should.equal(DUMMY_CONTRACTS[0] + '00'.repeat(12)) + slotKey.should.equal(DUMMY_SLOTS[0]) + slotTouched.should.equal(true) + slotValue.should.equal(DUMMY_VALUES[1]) + counter.should.equal(1) + }) + + it('should fail if not called by the execution manager', async () => { + await TestUtils.assertThrowsAsync(async () => { + await partialStateManager.setStorage( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0], + DUMMY_VALUES[0], + { + from: await randomWallet.getAddress(), + } + ) + }) + }) }) describe('getStorageView(...)', async () => { - it('should return the value of a storage slot for a given address', async () => {}) - - it('should return null bytes if the storage slot is not set', async () => {}) + it('should return the value of a storage slot for a given address', async () => { + await partialStateManager.setStorage( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0], + DUMMY_VALUES[0] + ) + + const value = await partialStateManager.getStorageView( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0] + ) + + value.should.equal(DUMMY_VALUES[0]) + }) + + it('should return null bytes if the storage slot is not set', async () => { + const value = await partialStateManager.getStorageView( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0] + ) + + value.should.equal(NULL_BYTES32) + }) }) describe('getStorage(...)', async () => { - it('should return the value of a storage slot when it exists', async () => {}) - - it('should return null bytes and flag if the storage slot is not set', async () => {}) - - it('should fail if not called by the execution manager', async () => {}) + it('should return the value of a storage slot when it exists', async () => { + await partialStateManager.insertVerifiedStorage( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0], + DUMMY_VALUES[0] + ) + + await partialStateManager.getStorage(DUMMY_CONTRACTS[0], DUMMY_SLOTS[0]) + const flagged = await partialStateManager.existsInvalidStateAccessFlag() + + flagged.should.equal(false) + }) + + it('should return null bytes and flag if the storage slot is not set', async () => { + await partialStateManager.getStorage(DUMMY_CONTRACTS[0], DUMMY_SLOTS[0]) + const flagged = await partialStateManager.existsInvalidStateAccessFlag() + + flagged.should.equal(true) + }) + + it('should fail if not called by the execution manager', async () => { + await TestUtils.assertThrowsAsync(async () => { + await partialStateManager.getStorage( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0], + { + from: await randomWallet.getAddress(), + } + ) + }) + }) }) - describe('setStorage(...)', async () => { - it('should set the storage slot for a given address', async () => {}) - - it('should not change the counter if the slot has already been touched', async () => {}) - - it('should fail if not called by the execution manager', async () => {}) + describe('setOvmContractNonce(...)', async () => { + it('should set the nonce for an address', async () => { + const nonce = 1234 + await partialStateManager.setOvmContractNonce(DUMMY_CONTRACTS[0], nonce) + + const contract = await partialStateManager.updatedContracts(0) + const counter = await partialStateManager.updatedContractsCounter() + const touched = await partialStateManager.contractTouched( + DUMMY_CONTRACTS[0] + ) + const updatedNonce = await partialStateManager.ovmContractNonces( + DUMMY_CONTRACTS[0] + ) + + contract.should.equal(DUMMY_CONTRACTS[0]) + counter.should.equal(1) + touched.should.equal(true) + updatedNonce.should.equal(nonce) + }) + + it('should not change the counter if the address has already been touched', async () => { + const nonce = 1234 + await partialStateManager.setOvmContractNonce(DUMMY_CONTRACTS[0], nonce) + + const newNonce = 5678 + await partialStateManager.setOvmContractNonce( + DUMMY_CONTRACTS[0], + newNonce + ) + + const contract = await partialStateManager.updatedContracts(0) + const counter = await partialStateManager.updatedContractsCounter() + const touched = await partialStateManager.contractTouched( + DUMMY_CONTRACTS[0] + ) + const updatedNonce = await partialStateManager.ovmContractNonces( + DUMMY_CONTRACTS[0] + ) + + contract.should.equal(DUMMY_CONTRACTS[0]) + counter.should.equal(1) + touched.should.equal(true) + updatedNonce.should.equal(newNonce) + }) + + it('should fail if not called by the execution manager', async () => { + await TestUtils.assertThrowsAsync(async () => { + const nonce = 1234 + await partialStateManager.setOvmContractNonce( + DUMMY_CONTRACTS[0], + nonce, + { + from: await randomWallet.getAddress(), + } + ) + }) + }) }) describe('getOvmContractNonceView(...)', async () => { - it('should get the nonce for a given address', async () => {}) + it('should get the nonce for a given address', async () => { + const nonce = 1234 + await partialStateManager.setOvmContractNonce(DUMMY_CONTRACTS[0], nonce) - it('should return zero if the address has not been set', async () => {}) - }) + const result = await partialStateManager.getOvmContractNonceView( + DUMMY_CONTRACTS[0] + ) - describe('getOvmContractNonce(...)', async () => { - it('should get the nonce for a given address', async () => {}) + result.should.equal(nonce) + }) - it('should return zero and flag if the address has not been set', async () => {}) + it('should return zero if the address has not been set', async () => { + const result = await partialStateManager.getOvmContractNonceView( + DUMMY_CONTRACTS[0] + ) - it('should fail if not called by the execution manager', async () => {}) + result.should.equal(0) + }) }) - describe('setOvmContractNonce(...)', async () => { - it('should set the nonce for an address', async () => {}) - - it('should not change the counter if the address has already been touched', async () => {}) - - it('should fail if not called by the execution manager', async () => {}) + describe('getOvmContractNonce(...)', async () => { + it('should get the nonce for a given address', async () => { + const nonce = 1234 + await partialStateManager.insertVerifiedContract( + DUMMY_CONTRACTS[0], + DUMMY_CODE_CONTRACTS[0], + nonce + ) + + await partialStateManager.getOvmContractNonce(DUMMY_CONTRACTS[0]) + const flagged = await partialStateManager.existsInvalidStateAccessFlag() + + flagged.should.equal(false) + }) + + it('should return zero and flag if the address has not been set', async () => { + await partialStateManager.getOvmContractNonce(DUMMY_CONTRACTS[0]) + const flagged = await partialStateManager.existsInvalidStateAccessFlag() + + flagged.should.equal(true) + }) + + it('should fail if not called by the execution manager', async () => { + await TestUtils.assertThrowsAsync(async () => { + await partialStateManager.getOvmContractNonce(DUMMY_CONTRACTS[0], { + from: await randomWallet.getAddress(), + }) + }) + }) }) describe('incrementOvmContractNonce(...)', async () => { - it('should increase the contract nonce by one', async () => {}) - - it('should not change the counter if the address has already been touched', async () => {}) - - it('should fail if not called by the execution manager', async () => {}) + it('should increase the contract nonce by one', async () => { + const nonce = 1234 + await partialStateManager.setOvmContractNonce(DUMMY_CONTRACTS[0], nonce) + await partialStateManager.incrementOvmContractNonce(DUMMY_CONTRACTS[0]) + + const contract = await partialStateManager.updatedContracts(0) + const counter = await partialStateManager.updatedContractsCounter() + const touched = await partialStateManager.contractTouched( + DUMMY_CONTRACTS[0] + ) + const result = await partialStateManager.ovmContractNonces( + DUMMY_CONTRACTS[0] + ) + + contract.should.equal(DUMMY_CONTRACTS[0]) + counter.should.equal(1) + touched.should.equal(true) + result.should.equal(nonce + 1) + }) + + it('should not change the counter if the address has already been touched', async () => { + const nonce = 1234 + await partialStateManager.setOvmContractNonce(DUMMY_CONTRACTS[0], nonce) + await partialStateManager.incrementOvmContractNonce(DUMMY_CONTRACTS[0]) + await partialStateManager.incrementOvmContractNonce(DUMMY_CONTRACTS[0]) + + const contract = await partialStateManager.updatedContracts(0) + const counter = await partialStateManager.updatedContractsCounter() + const touched = await partialStateManager.contractTouched( + DUMMY_CONTRACTS[0] + ) + const result = await partialStateManager.ovmContractNonces( + DUMMY_CONTRACTS[0] + ) + + contract.should.equal(DUMMY_CONTRACTS[0]) + counter.should.equal(1) + touched.should.equal(true) + result.should.equal(nonce + 2) + }) + + it('should flag if not verified', async () => { + await partialStateManager.incrementOvmContractNonce(DUMMY_CONTRACTS[0]) + + const flagged = await partialStateManager.existsInvalidStateAccessFlag() + + flagged.should.equal(true) + }) + + it('should fail if not called by the execution manager', async () => { + await TestUtils.assertThrowsAsync(async () => { + await partialStateManager.incrementOvmContractNonce( + DUMMY_CONTRACTS[0], + { + from: await randomWallet.getAddress(), + } + ) + }) + }) }) describe('associateCodeContract(...)', async () => { - it('should set the code contract address for a given ovm contract', async () => {}) - - it('should fail if not called by the execution manager', async () => {}) + it('should set the code contract address for a given ovm contract', async () => { + await partialStateManager.associateCodeContract( + DUMMY_CONTRACTS[0], + DUMMY_CODE_CONTRACTS[0] + ) + + const associated = await partialStateManager.ovmAddressToCodeContractAddress( + DUMMY_CONTRACTS[0] + ) + + associated.should.equal(DUMMY_CODE_CONTRACTS[0]) + }) + + it('should fail if not called by the execution manager', async () => { + await TestUtils.assertThrowsAsync(async () => { + await partialStateManager.associateCodeContract( + DUMMY_CONTRACTS[0], + DUMMY_CODE_CONTRACTS[0], + { + from: await randomWallet.getAddress(), + } + ) + }) + }) }) describe('associateCreatedContract(...)', async () => { - it('should mark the contract as verified and set its nonce to zero', async () => {}) + it('should mark the contract as verified and set its nonce to zero', async () => { + await partialStateManager.associateCreatedContract(DUMMY_CONTRACTS[0]) + + const isVerifiedContract = await partialStateManager.isVerifiedContract( + DUMMY_CONTRACTS[0] + ) + const nonce = await partialStateManager.getOvmContractNonceView( + DUMMY_CONTRACTS[0] + ) + + isVerifiedContract.should.equal(true) + nonce.should.equal(0) + }) + + it('should fail if not called by the execution manager', async () => { + await TestUtils.assertThrowsAsync(async () => { + await partialStateManager.associateCreatedContract(DUMMY_CONTRACTS[0], { + from: await randomWallet.getAddress(), + }) + }) + }) + }) - it('should fail if not called by the execution manager', async () => {}) + describe('peekUpdatedStorageSlot()', async () => { + it('should return the last storage slot on the queue', async () => { + await partialStateManager.setStorage( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0], + DUMMY_VALUES[0] + ) + + const [ + updatedContract, + updatedSlot, + updatedValue, + ] = await partialStateManager.peekUpdatedStorageSlot() + + updatedContract.should.equal(DUMMY_CONTRACTS[0]) + updatedSlot.should.equal(DUMMY_SLOTS[0]) + updatedValue.should.equal(DUMMY_VALUES[0]) + }) + + it('should fail if there are no storage slots to be updated', async () => { + await TestUtils.assertThrowsAsync(async () => { + await partialStateManager.peekUpdatedStorageSlot() + }) + }) }) - describe('getCodeContractAddressView(...)', async () => { - it('should return the code contract for a given ovm contract', async () => {}) + describe('popUpdatedStorageSlot()', async () => { + it('should return the last storage slot on the queue and remove it', async () => { + await partialStateManager.setStorage( + DUMMY_CONTRACTS[0], + DUMMY_SLOTS[0], + DUMMY_VALUES[0] + ) + + await partialStateManager.popUpdatedStorageSlot() + const counter = await partialStateManager.updatedStorageSlotCounter() + + counter.should.equal(0) + }) + + it('should fail if there are no storage slots to be updated', async () => { + await TestUtils.assertThrowsAsync(async () => { + await partialStateManager.popUpdatedStorageSlot() + }) + }) + + it('should fail if not called by the state transitioner', async () => { + await TestUtils.assertThrowsAsync(async () => { + await partialStateManager.popUpdatedStorageSlot({ + from: await randomWallet.getAddress(), + }) + }) + }) + }) - it('should return null bytes if the contract is not associated', async () => {}) + describe('peekUpdatedContract()', async () => { + it('should return the last contract on the queue', async () => { + await partialStateManager.associateCreatedContract(DUMMY_CONTRACTS[0]) + await partialStateManager.associateCodeContract( + DUMMY_CONTRACTS[0], + DUMMY_CODE_CONTRACTS[0] + ) + + const [ + updatedContract, + updatedNonce, + updatedCodeHash, + ] = await partialStateManager.peekUpdatedContract() + + updatedContract.should.equal(DUMMY_CONTRACTS[0]) + updatedNonce.should.equal(0) + updatedCodeHash.should.equal(NULL_BYTES32) + }) + + it('should fail if there are no contracts to be updated', async () => { + await TestUtils.assertThrowsAsync(async () => { + await partialStateManager.peekUpdatedContract() + }) + }) }) - describe('getCodeContractAddressFromOvmAddress(...)', async () => { - it('should return the code contract address when it exists', async () => {}) + describe('popUpdatedContract()', async () => { + it('should return the last contract on the queue and remove it', async () => { + await partialStateManager.associateCreatedContract(DUMMY_CONTRACTS[0]) + await partialStateManager.associateCodeContract( + DUMMY_CONTRACTS[0], + DUMMY_CODE_CONTRACTS[0] + ) + + await partialStateManager.popUpdatedContract() + const counter = await partialStateManager.updatedContractsCounter() + + counter.should.equal(0) + }) + + it('should fail if there are no contracts to be updated', async () => { + await TestUtils.assertThrowsAsync(async () => { + await partialStateManager.popUpdatedContract() + }) + }) + + it('should fail if not called by the state transitioner', async () => { + await TestUtils.assertThrowsAsync(async () => { + await partialStateManager.popUpdatedContract({ + from: await randomWallet.getAddress(), + }) + }) + }) + }) - it('should return null bytes and flag when the contract does not exist', async () => {}) + describe('getCodeContractAddressView(...)', async () => { + it('should return the code contract for a given ovm contract', async () => { + await partialStateManager.associateCodeContract( + DUMMY_CONTRACTS[0], + DUMMY_CODE_CONTRACTS[0] + ) + + const codeAddress = await partialStateManager.getCodeContractAddressView( + DUMMY_CONTRACTS[0] + ) + + codeAddress.should.equal(DUMMY_CODE_CONTRACTS[0]) + }) + + it('should return null bytes if the contract is not associated', async () => { + const codeAddress = await partialStateManager.getCodeContractAddressView( + DUMMY_CONTRACTS[0] + ) + + codeAddress.should.equal(NULL_ADDRESS) + }) + }) - it('should fail if not called by the execution manager', async () => {}) + describe('getCodeContractAddressFromOvmAddress(...)', async () => { + it('should return the code contract address when it exists', async () => { + await partialStateManager.insertVerifiedContract( + DUMMY_CONTRACTS[0], + DUMMY_CODE_CONTRACTS[0], + 0 + ) + + await partialStateManager.getCodeContractAddressFromOvmAddress( + DUMMY_CONTRACTS[0] + ) + const flagged = await partialStateManager.existsInvalidStateAccessFlag() + + flagged.should.equal(false) + }) + + it('should return null bytes and flag when the contract does not exist', async () => { + await partialStateManager.getCodeContractAddressFromOvmAddress( + DUMMY_CONTRACTS[0] + ) + const flagged = await partialStateManager.existsInvalidStateAccessFlag() + + flagged.should.equal(true) + }) + + it('should fail if not called by the execution manager', async () => { + await TestUtils.assertThrowsAsync(async () => { + await partialStateManager.getCodeContractAddressFromOvmAddress( + DUMMY_CONTRACTS[0], + { + from: await randomWallet.getAddress(), + } + ) + }) + }) }) describe('getCodeContractBytecode(...)', async () => { - it('should get the bytecode of a contract at the given address', async () => {}) + it('should get the bytecode of a contract at the given address', async () => { + const bytecode = await partialStateManager.getCodeContractBytecode( + DUMMY_CONTRACTS[0] + ) - it('should fail if the contract does not exist', async () => {}) + bytecode.should.equal('0x') + }) }) describe('getCodeContractHash(...)', async () => { - it('should get the hash of the bytecode at the given address', async () => {}) + it('should get the hash of the bytecode at the given address', async () => { + const codehash = await partialStateManager.getCodeContractHash( + DUMMY_CONTRACTS[0] + ) - it('should fail if the contract does not exist', async () => {}) + codehash.should.equal(RLP_NULL_HASH) + }) }) }) diff --git a/packages/contracts/test/contracts/ovm/StateTransitioner.spec.ts b/packages/contracts/test/contracts/ovm/StateTransitioner.spec.ts index d67368310478b..ae61483c04719 100644 --- a/packages/contracts/test/contracts/ovm/StateTransitioner.spec.ts +++ b/packages/contracts/test/contracts/ovm/StateTransitioner.spec.ts @@ -1,4 +1,3 @@ -/* tslint:disable:no-empty */ import { expect } from '../../setup' /* External Imports */ @@ -102,7 +101,8 @@ const makeTransactionData = async ( target: Contract, wallet: Signer, functionName: string, - functionArgs: any[] + functionArgs: any[], + eoa?: boolean ): Promise => { const calldata = TargetFactory.interface.encodeFunctionData( functionName, @@ -114,7 +114,7 @@ const makeTransactionData = async ( queueOrigin: 1, ovmEntrypoint: target.address, callBytes: calldata, - fromAddress: target.address, + fromAddress: eoa ? await wallet.getAddress() : target.address, l1MsgSenderAddress: await wallet.getAddress(), allowRevert: false, } @@ -124,7 +124,10 @@ const proveAllStorageUpdates = async ( stateTransitioner: Contract, stateManager: Contract, stateTrie: StateTrieMap -): Promise => { +): Promise<{ + trie: StateTrieMap + newStateTrieRoot: string +}> => { let updateTest: AccountStorageUpdateTest let trie = cloneDeep(stateTrie) @@ -160,14 +163,20 @@ const proveAllStorageUpdates = async ( ]) } - return updateTest.newStateTrieRoot + return { + trie, + newStateTrieRoot: updateTest.newStateTrieRoot, + } } const proveAllContractUpdates = async ( stateTransitioner: Contract, stateManager: Contract, stateTrie: StateTrieMap -): Promise => { +): Promise<{ + trie: StateTrieMap + newStateTrieRoot: string +}> => { let updateTest: StateTrieUpdateTest let trie = cloneDeep(stateTrie) @@ -207,7 +216,10 @@ const proveAllContractUpdates = async ( ]) } - return updateTest.newStateTrieRoot + return { + trie, + newStateTrieRoot: updateTest.newStateTrieRoot, + } } const getMappingStorageSlot = (key: string, index: number): string => { @@ -349,23 +361,53 @@ describe('StateTransitioner', () => { let stateTrie: any let test: AccountStorageProofTest + let eoaTest: AccountStorageProofTest + let wrongTest: AccountStorageProofTest before(async () => { - stateTrie = makeStateTrie( - fraudTester.address, - { - nonce: 0, - balance: 0, - storageRoot: null, - codeHash: await getCodeHash(ethers.provider, fraudTester.address), + stateTrie = { + ...makeStateTrie( + fraudTester.address, + { + nonce: 0, + balance: 0, + storageRoot: null, + codeHash: await getCodeHash(ethers.provider, fraudTester.address), + }, + DUMMY_ACCOUNT_STORAGE() + ), + ...{ + [await wallet.getAddress()]: { + state: { + nonce: 0, + balance: 0, + storageRoot: null, + codeHash: await getCodeHash( + ethers.provider, + await wallet.getAddress() + ), + }, + storage: DUMMY_ACCOUNT_STORAGE(), + }, }, - DUMMY_ACCOUNT_STORAGE() - ) + } test = await makeAccountStorageProofTest( stateTrie, fraudTester.address, DUMMY_ACCOUNT_STORAGE()[0].key ) + + eoaTest = await makeAccountStorageProofTest( + stateTrie, + await wallet.getAddress(), + DUMMY_ACCOUNT_STORAGE()[0].key + ) + + wrongTest = await makeAccountStorageProofTest( + stateTrie, + DUMMY_ACCOUNT_ADDRESSES[0], + DUMMY_ACCOUNT_STORAGE()[1].key + ) }) let stateTransitioner: Contract @@ -420,13 +462,39 @@ describe('StateTransitioner', () => { ).to.equal(false) }) - it('should fail if the code contract has not been deployed', async () => {}) + it('should fail if the code contract is invalid', async () => { + try { + await stateTransitioner.proveContractInclusion( + fraudTester.address, + await wallet.getAddress(), // Wrong code contract address. + 0, + test.stateTrieWitness + ) + } catch (e) { + expect(e.toString()).to.contain('Invalid account state provided.') + } - it('should fail if the code contract is invalid', async () => {}) + expect( + await stateManager.isVerifiedContract(fraudTester.address) + ).to.equal(false) + }) - it('should fail if the state trie witness is invalid', async () => {}) + it('should fail if the state trie witness is invalid', async () => { + try { + await stateTransitioner.proveContractInclusion( + fraudTester.address, + fraudTester.address, + 0, + wrongTest.stateTrieWitness // Wrong witness + ) + } catch (e) { + expect(e.toString()).to.contain('Invalid large internal hash') + } - it('should fail if the transaction has been executed', async () => {}) + expect( + await stateManager.isVerifiedContract(fraudTester.address) + ).to.equal(false) + }) }) describe('proveStorageSlotInclusion(...)', async () => { @@ -481,15 +549,88 @@ describe('StateTransitioner', () => { ) ).to.equal(false) }) - }) - it('should fail if the transaction has already been executed', async () => {}) + it('should fail if the provided contract has not been verified', async () => { + // Not proving contract inclusion first. + + try { + await stateTransitioner.proveStorageSlotInclusion( + fraudTester.address, + DUMMY_ACCOUNT_STORAGE()[0].key, + DUMMY_ACCOUNT_STORAGE()[0].val, + test.stateTrieWitness, + test.storageTrieWitness + ) + } catch (e) { + expect(e.toString()).to.contain( + 'Contract must be verified before proving storage!' + ) + } + + expect( + await stateManager.isVerifiedStorage( + fraudTester.address, + DUMMY_ACCOUNT_STORAGE()[0].key + ) + ).to.equal(false) + }) - it('should fail if the provided contract has not been verified', async () => {}) + it('should fail if the state trie witness is invalid', async () => { + await stateTransitioner.proveContractInclusion( + fraudTester.address, + fraudTester.address, + 0, + test.stateTrieWitness + ) - it('should fail if the state trie witness is invalid', async () => {}) + try { + await stateTransitioner.proveStorageSlotInclusion( + fraudTester.address, + DUMMY_ACCOUNT_STORAGE()[0].key, + DUMMY_ACCOUNT_STORAGE()[0].val, + wrongTest.stateTrieWitness, // Wrong state trie witness + test.storageTrieWitness + ) + } catch (e) { + expect(e.toString()).to.contain('Invalid large internal hash') + } - it('should fail if the storage trie witness is invalid', async () => {}) + expect( + await stateManager.isVerifiedStorage( + fraudTester.address, + DUMMY_ACCOUNT_STORAGE()[0].key + ) + ).to.equal(false) + }) + + it('should fail if the storage trie witness is invalid', async () => { + await stateTransitioner.proveContractInclusion( + fraudTester.address, + fraudTester.address, + 0, + test.stateTrieWitness + ) + + try { + await stateTransitioner.proveStorageSlotInclusion( + fraudTester.address, + DUMMY_ACCOUNT_STORAGE()[0].key, + DUMMY_ACCOUNT_STORAGE()[0].val, + test.stateTrieWitness, + wrongTest.storageTrieWitness // Wrong storage trie witness + ) + } catch (e) { + expect(e.toString()).to.contain('Invalid large internal hash') + } + + expect( + await stateManager.isVerifiedStorage( + fraudTester.address, + DUMMY_ACCOUNT_STORAGE()[0].key + ) + ).to.equal(false) + }) + }) }) describe('applyTransaction(...)', async () => { @@ -689,9 +830,74 @@ describe('StateTransitioner', () => { ) }) - it('should succeed even if the underlying transaction reverts', async () => {}) + it('should succeed even if the underlying transaction reverts', async () => { + ;[ + stateTransitioner, + stateManager, + transactionData, + ] = await initStateTransitioner( + StateTransitioner, + StateManager, + resolver.addressResolver, + test.stateTrieRoot, + await makeTransactionData( + FraudTester, + fraudTester, + wallet, + 'doRevert', + [] + ) + ) + + await stateTransitioner.proveContractInclusion( + fraudTester.address, + fraudTester.address, + 0, + test.stateTrieWitness + ) - it('should fail if the provided transaction data does not match the original data', async () => {}) + await stateTransitioner.applyTransaction(transactionData) + + expect(await stateTransitioner.currentTransitionPhase()).to.equal( + STATE_TRANSITIONER_PHASES.POST_EXECUTION + ) + }) + + it('should fail if the provided transaction data does not match the original data', async () => { + ;[ + stateTransitioner, + stateManager, + transactionData, + ] = await initStateTransitioner( + StateTransitioner, + StateManager, + resolver.addressResolver, + test.stateTrieRoot, + await makeTransactionData( + FraudTester, + fraudTester, + wallet, + 'doRevert', + [] + ) + ) + + await TestUtils.assertRevertsAsync( + 'Provided transaction does not match the original transaction.', + async () => { + await stateTransitioner.applyTransaction({ + ...transactionData, + ...{ + timestamp: 12345, // Wrong timestamp. + }, + }) + } + ) + + expect(await stateTransitioner.currentTransitionPhase()).to.equal( + STATE_TRANSITIONER_PHASES.PRE_EXECUTION + ) + }) }) describe('Post-Execution', async () => { @@ -726,7 +932,7 @@ describe('StateTransitioner', () => { expect(await stateManager.updatedStorageSlotCounter()).to.equal(1) - const newStateTrieRoot = await proveAllStorageUpdates( + const { trie, newStateTrieRoot } = await proveAllStorageUpdates( stateTransitioner, stateManager, stateTrie @@ -770,7 +976,7 @@ describe('StateTransitioner', () => { expect(await stateManager.updatedStorageSlotCounter()).to.equal(3) - const newStateTrieRoot = await proveAllStorageUpdates( + const { trie, newStateTrieRoot } = await proveAllStorageUpdates( stateTransitioner, stateManager, stateTrie @@ -814,7 +1020,7 @@ describe('StateTransitioner', () => { expect(await stateManager.updatedStorageSlotCounter()).to.equal(1) - const newStateTrieRoot = await proveAllStorageUpdates( + const { trie, newStateTrieRoot } = await proveAllStorageUpdates( stateTransitioner, stateManager, stateTrie @@ -824,11 +1030,123 @@ describe('StateTransitioner', () => { expect(await stateManager.updatedStorageSlotCounter()).to.equal(0) }) - it('should revert if the transaction has not been executed', async () => {}) + it('should revert if the provided state trie witness is invalid', async () => { + ;[ + stateTransitioner, + stateManager, + transactionData, + ] = await initStateTransitioner( + StateTransitioner, + StateManager, + resolver.addressResolver, + test.stateTrieRoot, + await makeTransactionData( + FraudTester, + fraudTester, + wallet, + 'setStorage', + [keccak256('0xabc'), keccak256('0xdef')] + ) + ) + + await stateTransitioner.proveContractInclusion( + fraudTester.address, + fraudTester.address, + 0, + test.stateTrieWitness + ) + + await stateTransitioner.applyTransaction(transactionData) + + expect(await stateManager.updatedStorageSlotCounter()).to.equal(1) + + const [ + storageSlotContract, + storageSlotKey, + storageSlotValue, + ] = await stateManager.peekUpdatedStorageSlot() + + const updateTest = await makeAccountStorageUpdateTest( + stateTrie, + storageSlotContract, + storageSlotKey, + storageSlotValue + ) + + const oldStateRoot = await stateTransitioner.stateRoot() - it('should revert if the provided state trie witness is invalid', async () => {}) + await TestUtils.assertRevertsAsync( + 'Invalid large internal hash', + async () => { + await stateTransitioner.proveUpdatedStorageSlot( + wrongTest.stateTrieWitness, // Wrong state trie witness + updateTest.storageTrieWitness + ) + } + ) - it('should revert if the provided storage trie witness is invalid', async () => {}) + expect(await stateTransitioner.stateRoot()).to.equal(oldStateRoot) + expect(await stateManager.updatedStorageSlotCounter()).to.equal(1) + }) + + it('should revert if the provided storage trie witness is invalid', async () => { + ;[ + stateTransitioner, + stateManager, + transactionData, + ] = await initStateTransitioner( + StateTransitioner, + StateManager, + resolver.addressResolver, + test.stateTrieRoot, + await makeTransactionData( + FraudTester, + fraudTester, + wallet, + 'setStorage', + [keccak256('0xabc'), keccak256('0xdef')] + ) + ) + + await stateTransitioner.proveContractInclusion( + fraudTester.address, + fraudTester.address, + 0, + test.stateTrieWitness + ) + + await stateTransitioner.applyTransaction(transactionData) + + expect(await stateManager.updatedStorageSlotCounter()).to.equal(1) + + const [ + storageSlotContract, + storageSlotKey, + storageSlotValue, + ] = await stateManager.peekUpdatedStorageSlot() + + const updateTest = await makeAccountStorageUpdateTest( + stateTrie, + storageSlotContract, + storageSlotKey, + storageSlotValue + ) + + const oldStateRoot = await stateTransitioner.stateRoot() + + await TestUtils.assertRevertsAsync( + 'Invalid large internal hash', + async () => { + await stateTransitioner.proveUpdatedStorageSlot( + updateTest.stateTrieWitness, + wrongTest.storageTrieWitness // Wrong storage trie witness + ) + } + ) + + expect(await stateTransitioner.stateRoot()).to.equal(oldStateRoot) + expect(await stateManager.updatedStorageSlotCounter()).to.equal(1) + }) }) describe('proveUpdatedContract(...)', async () => { @@ -863,7 +1181,7 @@ describe('StateTransitioner', () => { // One update for each new contract, plus one nonce update for the creating contract. expect(await stateManager.updatedContractsCounter()).to.equal(2) - const newStateTrieRoot = await proveAllContractUpdates( + const { trie, newStateTrieRoot } = await proveAllContractUpdates( stateTransitioner, stateManager, stateTrie @@ -907,7 +1225,7 @@ describe('StateTransitioner', () => { // One update for each new contract, plus one nonce update for the creating contract. expect(await stateManager.updatedContractsCounter()).to.equal(4) - const newStateTrieRoot = await proveAllContractUpdates( + const { trie, newStateTrieRoot } = await proveAllContractUpdates( stateTransitioner, stateManager, stateTrie @@ -917,13 +1235,99 @@ describe('StateTransitioner', () => { expect(await stateManager.updatedContractsCounter()).to.equal(0) }) - it('should update when an externally owned account has been modified', async () => {}) + it('should update when an externally owned account has been modified', async () => { + ;[ + stateTransitioner, + stateManager, + transactionData, + ] = await initStateTransitioner( + StateTransitioner, + StateManager, + resolver.addressResolver, + test.stateTrieRoot, + await makeTransactionData( + FraudTester, + fraudTester, + wallet, + 'createContract', + ['0x' + MicroFraudTesterJson.evm.bytecode.object], + true // EOA transaction. + ) + ) - it('should update when multiple EOAs have been modified', async () => {}) + await stateTransitioner.proveContractInclusion( + fraudTester.address, + fraudTester.address, + 0, + test.stateTrieWitness + ) - it('should revert if the transaction has not been executed yet', async () => {}) + await stateTransitioner.proveContractInclusion( + await wallet.getAddress(), + await wallet.getAddress(), + 0, + eoaTest.stateTrieWitness + ) + + await stateTransitioner.applyTransaction(transactionData) + + expect(await stateManager.updatedContractsCounter()).to.equal(3) + + const { trie, newStateTrieRoot } = await proveAllContractUpdates( + stateTransitioner, + stateManager, + stateTrie + ) + + expect(await stateTransitioner.stateRoot()).to.equal(newStateTrieRoot) + expect(await stateManager.updatedContractsCounter()).to.equal(0) + }) + + it('should revert if the provided state trie witness is invalid', async () => { + ;[ + stateTransitioner, + stateManager, + transactionData, + ] = await initStateTransitioner( + StateTransitioner, + StateManager, + resolver.addressResolver, + test.stateTrieRoot, + await makeTransactionData( + FraudTester, + fraudTester, + wallet, + 'createContract', + ['0x' + MicroFraudTesterJson.evm.bytecode.object] + ) + ) + + await stateTransitioner.proveContractInclusion( + fraudTester.address, + fraudTester.address, + 0, + test.stateTrieWitness + ) + + await stateTransitioner.applyTransaction(transactionData) + + // One update for each new contract, plus one nonce update for the creating contract. + expect(await stateManager.updatedContractsCounter()).to.equal(2) - it('should revert if the provided state trie witness is invalid', async () => {}) + const oldStateRoot = await stateTransitioner.stateRoot() + + await TestUtils.assertRevertsAsync( + 'Invalid large internal hash', + async () => { + await stateTransitioner.proveUpdatedContract( + wrongTest.stateTrieWitness + ) // Wrong state trie witness + } + ) + + expect(await stateTransitioner.stateRoot()).to.equal(oldStateRoot) + expect(await stateManager.updatedContractsCounter()).to.equal(2) + }) }) describe('completeTransition(...)', async () => { @@ -984,6 +1388,10 @@ describe('StateTransitioner', () => { await stateTransitioner.applyTransaction(transactionData) expect(await stateManager.updatedStorageSlotCounter()).to.equal(0) + expect(await stateManager.updatedContractsCounter()).to.equal(1) + + await proveAllContractUpdates(stateTransitioner, stateManager, trie) + expect(await stateManager.updatedContractsCounter()).to.equal(0) await stateTransitioner.completeTransition() expect(await stateTransitioner.isComplete()).to.equal(true) @@ -1017,19 +1425,144 @@ describe('StateTransitioner', () => { await stateTransitioner.applyTransaction(transactionData) expect(await stateManager.updatedStorageSlotCounter()).to.equal(1) + expect(await stateManager.updatedContractsCounter()).to.equal(1) - await proveAllStorageUpdates(stateTransitioner, stateManager, stateTrie) + const { trie, newStateTrieRoot } = await proveAllContractUpdates( + stateTransitioner, + stateManager, + stateTrie + ) + expect(await stateManager.updatedContractsCounter()).to.equal(0) + + await proveAllStorageUpdates(stateTransitioner, stateManager, trie) expect(await stateManager.updatedStorageSlotCounter()).to.equal(0) await stateTransitioner.completeTransition() expect(await stateTransitioner.isComplete()).to.equal(true) }) - it('should not finalize if the transaction has not been executed yet', async () => {}) + it('should not finalize if the transaction has not been executed yet', async () => { + ;[ + stateTransitioner, + stateManager, + transactionData, + ] = await initStateTransitioner( + StateTransitioner, + StateManager, + resolver.addressResolver, + test.stateTrieRoot, + await makeTransactionData( + FraudTester, + fraudTester, + wallet, + 'setStorage', + [keccak256('0xabc'), keccak256('0xdef')] + ) + ) - it('should not finalize if there are still storage slots to be updated', async () => {}) + await stateTransitioner.proveContractInclusion( + fraudTester.address, + fraudTester.address, + 0, + test.stateTrieWitness + ) + + // Don't apply the transaction + + await TestUtils.assertRevertsAsync( + 'Must be called during the correct phase.', + async () => { + await stateTransitioner.completeTransition() + } + ) + + expect(await stateTransitioner.isComplete()).to.equal(false) + }) + + it('should not finalize if there are still storage slots to be updated', async () => { + ;[ + stateTransitioner, + stateManager, + transactionData, + ] = await initStateTransitioner( + StateTransitioner, + StateManager, + resolver.addressResolver, + test.stateTrieRoot, + await makeTransactionData( + FraudTester, + fraudTester, + wallet, + 'setStorage', + [keccak256('0xabc'), keccak256('0xdef')] + ) + ) - it('should not finalize if there are still contracts to be updated', async () => {}) + await stateTransitioner.proveContractInclusion( + fraudTester.address, + fraudTester.address, + 0, + test.stateTrieWitness + ) + + await stateTransitioner.applyTransaction(transactionData) + expect(await stateManager.updatedStorageSlotCounter()).to.equal(1) + + // Don't prove the storage slot updates. + + await TestUtils.assertRevertsAsync( + 'All storage updates must be proven to complete the transition.', + async () => { + await stateTransitioner.completeTransition() + } + ) + + expect(await stateTransitioner.isComplete()).to.equal(false) + }) + + it('should not finalize if there are still contracts to be updated', async () => { + ;[ + stateTransitioner, + stateManager, + transactionData, + ] = await initStateTransitioner( + StateTransitioner, + StateManager, + resolver.addressResolver, + test.stateTrieRoot, + await makeTransactionData( + FraudTester, + fraudTester, + wallet, + 'setStorage', + [keccak256('0xabc'), keccak256('0xdef')] + ) + ) + + await stateTransitioner.proveContractInclusion( + fraudTester.address, + fraudTester.address, + 0, + test.stateTrieWitness + ) + + await stateTransitioner.applyTransaction(transactionData) + expect(await stateManager.updatedStorageSlotCounter()).to.equal(1) + + await proveAllStorageUpdates(stateTransitioner, stateManager, stateTrie) + expect(await stateManager.updatedStorageSlotCounter()).to.equal(0) + + // Don't prove the contract updates. + + await TestUtils.assertRevertsAsync( + 'All contract updates must be proven to complete the transition.', + async () => { + await stateTransitioner.completeTransition() + } + ) + + expect(await stateTransitioner.isComplete()).to.equal(false) + }) }) }) }) diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.context-opcodes.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.context-opcodes.spec.ts index 361d29cf0d014..e1031c8e58041 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.context-opcodes.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.context-opcodes.spec.ts @@ -1,4 +1,3 @@ -/* tslint:disable:no-empty */ import { should } from '../../../setup' /* External Imports */ @@ -11,8 +10,9 @@ import { TestUtils, getCurrentTime, NULL_ADDRESS, + ZERO_ADDRESS, } from '@eth-optimism/core-utils' -import { Contract, ContractFactory, Signer } from 'ethers' +import { Contract, ContractFactory, Signer, BigNumber } from 'ethers' /* Internal Imports */ import { @@ -234,26 +234,74 @@ describe('Execution Manager -- Context opcodes', () => { }) describe('ovmBlockGasLimit', async () => { - it('should retrieve the block gas limit', async () => {}) + it('should retrieve the block gas limit', async () => { + const result = await executeTestTransaction( + executionManager, + contractAddress, + 'callThroughExecutionManager', + [contract2Address32, OVM_METHOD_IDS.ovmBlockGasLimit] + ) + + const resultNum = BigNumber.from(result).toNumber() + resultNum.should.equal(GAS_LIMIT, 'Block gas limit was incorrect') + }) }) describe('isStaticContext', async () => { - it('should be true when inside a static context', async () => {}) + it('should be true when inside a static context', async () => { + const result = await executeTestTransaction( + executionManager, + contractAddress, + 'staticCallThroughExecutionManager', + [contract2Address32, OVM_METHOD_IDS.isStaticContext] + ) - it('should be false when not in a static context', async () => {}) - }) + const resultNum = BigNumber.from(result).toNumber() + resultNum.should.equal(1, 'Context is not static but should be') + }) - describe('ovmORIGIN', async () => { - it('should give us the origin of the transaction', async () => {}) + it('should be false when not in a static context', async () => { + const result = await executeTestTransaction( + executionManager, + contractAddress, + 'callThroughExecutionManager', + [contract2Address32, OVM_METHOD_IDS.isStaticContext] + ) - it('should revert if the transaction has no origin', async () => {}) + const resultNum = BigNumber.from(result).toNumber() + resultNum.should.equal(0, 'Context is static but should not be') + }) }) - describe('setStateManager', async () => { - it('should allow us to change the state manager address', async () => {}) - }) + describe('ovmORIGIN', async () => { + it('should give us the origin of the transaction', async () => { + const origin = await wallet.getAddress() + const result = await executeTestTransaction( + executionManager, + contractAddress, + 'callThroughExecutionManager', + [contract2Address32, OVM_METHOD_IDS.ovmORIGIN], + ZERO_ADDRESS, + origin + ) + + result.should.equal( + addressToBytes32Address(origin), + 'Returned origin is incorrect' + ) + }) - describe('incrementNonce', async () => { - it('should increment a contract nonce', async () => {}) + it('should revert if the transaction has no origin', async () => { + await TestUtils.assertThrowsAsync(async () => { + await executeTestTransaction( + executionManager, + contractAddress, + 'callThroughExecutionManager', + [contract2Address32, OVM_METHOD_IDS.ovmORIGIN], + ZERO_ADDRESS, + ZERO_ADDRESS + ) + }) + }) }) }) diff --git a/packages/contracts/test/test-helpers/execution-manager-helpers.ts b/packages/contracts/test/test-helpers/execution-manager-helpers.ts index 92255eef48dc6..65453bf1c0eb6 100644 --- a/packages/contracts/test/test-helpers/execution-manager-helpers.ts +++ b/packages/contracts/test/test-helpers/execution-manager-helpers.ts @@ -17,6 +17,7 @@ export const OVM_METHOD_IDS = fromPairs( 'makeStaticCall', 'makeStaticCallThenCall', 'callThroughExecutionManager', + 'staticCallThroughExecutionManager', 'staticFriendlySLOAD', 'notStaticFriendlySSTORE', 'notStaticFriendlyCREATE', @@ -31,6 +32,9 @@ export const OVM_METHOD_IDS = fromPairs( 'ovmCALLER', 'ovmCREATE', 'ovmCREATE2', + 'ovmORIGIN', + 'ovmBlockGasLimit', + 'isStaticContext', ].map((methodId) => [methodId, encodeMethodId(methodId)]) ) @@ -117,7 +121,8 @@ export const executeTestTransaction = async ( contractAddress: string, methodName: string, args: any[], - queueOrigin = ZERO_ADDRESS + queueOrigin = ZERO_ADDRESS, + ovmTxOrign = ZERO_ADDRESS ): Promise => { const callBytes = encodeFunctionData(methodName, args) @@ -128,7 +133,7 @@ export const executeTestTransaction = async ( queueOrigin, contractAddress, callBytes, - ZERO_ADDRESS, + ovmTxOrign, ZERO_ADDRESS, true, ] From d3bc120512cf293c446d7f8ee24decd7587cb7ac Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Thu, 30 Jul 2020 16:28:10 -0400 Subject: [PATCH 20/24] Various small changes as per comments --- .../ovm/ExecutionManager.sol | 32 +++++++------------ .../ovm/FullStateManager.sol | 2 +- .../ovm/L2ExecutionManager.sol | 15 +++++++++ .../ovm/PartialStateManager.sol | 2 +- .../optimistic-ethereum/ovm/StateManager.sol | 2 +- .../ExecutionManager.purity-checking.spec.ts | 2 -- 6 files changed, 29 insertions(+), 26 deletions(-) diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/ExecutionManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/ExecutionManager.sol index fb2e73ada65df..7c6ab45125503 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/ExecutionManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/ExecutionManager.sol @@ -63,7 +63,7 @@ contract ExecutionManager is ContractResolver { bytes32 constant private METHOD_ID_OVM_CREATE = keccak256("ovmCREATE()") >> 224; // Precompile addresses - address constant private L2_L2_OVM_MESSAGE_PASSER = 0x4200000000000000000000000000000000000000; + address constant private L2_TO_L1_OVM_MESSAGE_PASSER = 0x4200000000000000000000000000000000000000; address constant private L1_MESSAGE_SENDER = 0x4200000000000000000000000000000000000001; @@ -100,8 +100,8 @@ contract ExecutionManager is ContractResolver { } // Deploy custom precompiles - L2ToL1MessagePasser l1ToL2MessagePasser = new L2ToL1MessagePasser(address(this)); - stateManager.associateCodeContract(L2_L2_OVM_MESSAGE_PASSER, address(l1ToL2MessagePasser)); + L2ToL1MessagePasser l2ToL1MessagePasser = new L2ToL1MessagePasser(address(this)); + stateManager.associateCodeContract(L2_TO_L1_OVM_MESSAGE_PASSER, address(l2ToL1MessagePasser)); L1MessageSender l1MessageSender = new L1MessageSender(address(this)); stateManager.associateCodeContract(L1_MESSAGE_SENDER, address(l1MessageSender)); @@ -131,20 +131,6 @@ contract ExecutionManager is ContractResolver { addressResolver.setAddress("StateManager", _stateManagerAddress); } - /** - * Increments the provided address's nonce. This is only used by the - * sequencer to correct nonces when transactions fail. - * @param _addr The address of the nonce to increment. - */ - function incrementNonce( - address _addr - ) - public - { - StateManager stateManager = resolveStateManager(); - stateManager.incrementOvmContractNonce(_addr); - } - /********************* * Execute EOA Calls * @@ -204,7 +190,7 @@ contract ExecutionManager is ContractResolver { } /** - * Execute an unsigned EOA transaction. Note that unsigned EOA calls are unauthenticated. + * Execute a transaction. Note that unsigned EOA calls are unauthenticated. * This means that they should not be allowed for normal execution. * @param _timestamp The timestamp which should be used for this call's context. * @param _queueOrigin The parent-chain queue from which this call originated. @@ -628,7 +614,7 @@ contract ExecutionManager is ContractResolver { createNewContract(_newOvmContractAddress, _ovmInitcode); // Insert the newly created contract into our state manager. - stateManager.associateCreatedContract(_newOvmContractAddress); + stateManager.registerCreatedContract(_newOvmContractAddress); // We also need to increment the contract nonce stateManager.incrementOvmContractNonce(creator); @@ -1085,8 +1071,8 @@ contract ExecutionManager is ContractResolver { } /** - * @notice Getter for the execution context's L1MessageSender. Used by the - * L1MessageSender precompile. + * Getter for the execution context's L1MessageSender. Used by the + * L1MessageSender precompile. * @return The L1MessageSender in our current execution context. */ function getL1MessageSender() @@ -1111,6 +1097,10 @@ contract ExecutionManager is ContractResolver { return executionContext.l1MessageSender; } + /** + * Queries the address of the state manager. + * @return State manager address. + */ function getStateManagerAddress() public view diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/FullStateManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/FullStateManager.sol index 99680b912b8a2..77c987d209525 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/FullStateManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/FullStateManager.sol @@ -171,7 +171,7 @@ contract FullStateManager is StateManager { * Marks a contract as newly created. Unused within this implementation. * @param _ovmContractAddress Address to mark as newly created. */ - function associateCreatedContract( + function registerCreatedContract( address _ovmContractAddress ) public diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/L2ExecutionManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/L2ExecutionManager.sol index 2915e3420788a..77bf8cd688379 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/L2ExecutionManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/L2ExecutionManager.sol @@ -3,6 +3,7 @@ pragma experimental ABIEncoderV2; /* Contract Imports */ import { ExecutionManager } from "./ExecutionManager.sol"; +import { StateManager } from "./StateManager.sol"; /** * @title L2ExecutionManager @@ -41,6 +42,20 @@ contract L2ExecutionManager is ExecutionManager { * Public Functions */ + /** + * Increments the provided address's nonce. This is only used by the + * sequencer to correct nonces when transactions fail. + * @param _addr The address of the nonce to increment. + */ + function incrementNonce( + address _addr + ) + public + { + StateManager stateManager = resolveStateManager(); + stateManager.incrementOvmContractNonce(_addr); + } + /** * @notice Stores the provided OVM transaction, mapping its hash to its value and its hash to the EVM tx hash with which it's associated. diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol index 44254e8a48f0e..46bbba956fb47 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/PartialStateManager.sol @@ -438,7 +438,7 @@ contract PartialStateManager is ContractResolver { * marks the contract as verified. * @param _ovmContractAddress Address of the contract to mark as verified. */ - function associateCreatedContract( + function registerCreatedContract( address _ovmContractAddress ) public diff --git a/packages/contracts/contracts/optimistic-ethereum/ovm/StateManager.sol b/packages/contracts/contracts/optimistic-ethereum/ovm/StateManager.sol index 6da522a7bf91e..349b0d33b709b 100644 --- a/packages/contracts/contracts/optimistic-ethereum/ovm/StateManager.sol +++ b/packages/contracts/contracts/optimistic-ethereum/ovm/StateManager.sol @@ -21,7 +21,7 @@ contract StateManager { // Contract code storage / contract address retrieval function associateCodeContract(address _ovmContractAddress, address _codeContractAddress) public; - function associateCreatedContract(address _ovmContractAddress) public; + function registerCreatedContract(address _ovmContractAddress) public; function getCodeContractAddressFromOvmAddress(address _ovmContractAddress) external view returns(address); function getOvmAddressFromCodeContractAddress(address _codeContractAddress) external view returns(address); function getCodeContractBytecode( diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.purity-checking.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.purity-checking.spec.ts index c2e18e68f032d..c5caaaebb2bd5 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.purity-checking.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.purity-checking.spec.ts @@ -103,8 +103,6 @@ describe('Execution Manager -- Safety Checking', () => { AddThree, [] ) - console.log('Execution Manager Address:', executionManager.address) - console.log('AddThree Bytecode:', AddThree.bytecode) const createSucceeded = await didCreateSucceed( executionManager, receipt.transactionHash From fcc9140d1374c51e1dd9c31e9f4ae898eae5fea5 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Thu, 30 Jul 2020 18:32:53 -0400 Subject: [PATCH 21/24] Minor fix to add CHAINID back to unsafe opcodes --- packages/contracts/test/test-helpers/constants.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/contracts/test/test-helpers/constants.ts b/packages/contracts/test/test-helpers/constants.ts index f82a2e39ef98b..4f1beb73aa24e 100644 --- a/packages/contracts/test/test-helpers/constants.ts +++ b/packages/contracts/test/test-helpers/constants.ts @@ -4,13 +4,11 @@ import { defaultAccounts } from 'ethereum-waffle' /* Internal Imports */ import { - EVMOpcode, Opcode, - DEFAULT_UNSAFE_OPCODES, + DEFAULT_UNSAFE_OPCODES as UNSAFE_OPCODES, } from '@eth-optimism/rollup-core' export { ZERO_ADDRESS } from '@eth-optimism/core-utils' -export { DEFAULT_UNSAFE_OPCODES } from '@eth-optimism/rollup-core' export const DEFAULT_ACCOUNTS = defaultAccounts export const DEFAULT_ACCOUNTS_BUIDLER = defaultAccounts.map((account) => { @@ -31,6 +29,10 @@ export const CHAIN_ID = 108 export const ZERO_UINT = '00'.repeat(32) export const DEFAULT_FORCE_INCLUSION_PERIOD = 600 +export const DEFAULT_UNSAFE_OPCODES = UNSAFE_OPCODES.concat([ + Opcode.CHAINID +]) + export const HALTING_OPCODES = Opcode.HALTING_OP_CODES export const HALTING_OPCODES_NO_JUMP = HALTING_OPCODES.filter( (x) => x.name !== 'JUMP' From be845f2eaa271d068477c7f384b944dbb5348289 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Thu, 30 Jul 2020 18:58:14 -0400 Subject: [PATCH 22/24] Fixed linting error --- packages/contracts/test/test-helpers/constants.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/contracts/test/test-helpers/constants.ts b/packages/contracts/test/test-helpers/constants.ts index 4f1beb73aa24e..f05a51205acdc 100644 --- a/packages/contracts/test/test-helpers/constants.ts +++ b/packages/contracts/test/test-helpers/constants.ts @@ -29,9 +29,7 @@ export const CHAIN_ID = 108 export const ZERO_UINT = '00'.repeat(32) export const DEFAULT_FORCE_INCLUSION_PERIOD = 600 -export const DEFAULT_UNSAFE_OPCODES = UNSAFE_OPCODES.concat([ - Opcode.CHAINID -]) +export const DEFAULT_UNSAFE_OPCODES = UNSAFE_OPCODES.concat([Opcode.CHAINID]) export const HALTING_OPCODES = Opcode.HALTING_OP_CODES export const HALTING_OPCODES_NO_JUMP = HALTING_OPCODES.filter( From 4ef6abcd93eb47cd50a7623359737684e82d27c0 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Fri, 31 Jul 2020 15:21:40 -0400 Subject: [PATCH 23/24] Removed duplicate deploy logic --- packages/contracts/src/deployment/index.ts | 1 + .../contracts/test/test-helpers/constants.ts | 3 +- .../test/test-helpers/deployment-helpers.ts | 48 ++++++++++ packages/contracts/test/test-helpers/index.ts | 2 +- .../test/test-helpers/resolution/config.ts | 93 ------------------ .../test/test-helpers/resolution/index.ts | 2 - .../test/test-helpers/resolution/resolver.ts | 95 ------------------- .../test/test-helpers/resolution/types.ts | 60 ------------ .../test/test-helpers/types/deployment.ts | 1 + .../test/test-helpers/types/index.ts | 1 + 10 files changed, 53 insertions(+), 253 deletions(-) create mode 100644 packages/contracts/test/test-helpers/deployment-helpers.ts delete mode 100644 packages/contracts/test/test-helpers/resolution/config.ts delete mode 100644 packages/contracts/test/test-helpers/resolution/index.ts delete mode 100644 packages/contracts/test/test-helpers/resolution/resolver.ts delete mode 100644 packages/contracts/test/test-helpers/resolution/types.ts create mode 100644 packages/contracts/test/test-helpers/types/deployment.ts diff --git a/packages/contracts/src/deployment/index.ts b/packages/contracts/src/deployment/index.ts index 086de396b196b..2b2879f29e041 100644 --- a/packages/contracts/src/deployment/index.ts +++ b/packages/contracts/src/deployment/index.ts @@ -1 +1,2 @@ export * from './contract-deploy' +export { AddressResolverMapping, RollupDeployConfig } from './types' diff --git a/packages/contracts/test/test-helpers/constants.ts b/packages/contracts/test/test-helpers/constants.ts index f05a51205acdc..3169009bab2b4 100644 --- a/packages/contracts/test/test-helpers/constants.ts +++ b/packages/contracts/test/test-helpers/constants.ts @@ -1,13 +1,12 @@ /* External Imports */ import { ethers } from 'ethers' import { defaultAccounts } from 'ethereum-waffle' - -/* Internal Imports */ import { Opcode, DEFAULT_UNSAFE_OPCODES as UNSAFE_OPCODES, } from '@eth-optimism/rollup-core' +/* Internal Imports */ export { ZERO_ADDRESS } from '@eth-optimism/core-utils' export const DEFAULT_ACCOUNTS = defaultAccounts diff --git a/packages/contracts/test/test-helpers/deployment-helpers.ts b/packages/contracts/test/test-helpers/deployment-helpers.ts new file mode 100644 index 0000000000000..34d45c84fd47c --- /dev/null +++ b/packages/contracts/test/test-helpers/deployment-helpers.ts @@ -0,0 +1,48 @@ +/* External Imports */ +import { ethers } from '@nomiclabs/buidler' +import { Contract, Signer, Wallet, ContractFactory } from 'ethers' + +/* Internal Imports */ +import { + RollupDeployConfig, + AddressResolverMapping, + deployAllContracts, + deployAndRegister as originalDeployAndRegister, +} from '../../src' +import { GAS_LIMIT, DEFAULT_FORCE_INCLUSION_PERIOD } from './constants' + +export const makeAddressResolver = async ( + wallet: Signer | Wallet +): Promise => { + const [owner, sequencer, l1ToL2TransactionPasser] = await ethers.getSigners() + + const config: RollupDeployConfig = { + signer: wallet, + rollupOptions: { + gasLimit: GAS_LIMIT, + forceInclusionPeriod: DEFAULT_FORCE_INCLUSION_PERIOD, + owner: wallet, + sequencer, + l1ToL2TransactionPasser, + }, + } + + return deployAllContracts(config) +} + +export const deployAndRegister = async ( + addressResolver: Contract, + signer: Signer, + name: string, + deployConfig: { + factory: ContractFactory + params: any[] + } +): Promise => { + return originalDeployAndRegister(addressResolver, name, { + ...deployConfig, + ...{ + signer, + }, + }) +} diff --git a/packages/contracts/test/test-helpers/index.ts b/packages/contracts/test/test-helpers/index.ts index c3d6c9bc74f6c..1e4b00a70d16c 100644 --- a/packages/contracts/test/test-helpers/index.ts +++ b/packages/contracts/test/test-helpers/index.ts @@ -6,5 +6,5 @@ export * from './ethereum-helpers' export * from './ovm-helpers' export * from './trie-helpers' export * from './wallet-helpers' -export * from './resolution' export * from './execution-manager-helpers' +export * from './deployment-helpers' diff --git a/packages/contracts/test/test-helpers/resolution/config.ts b/packages/contracts/test/test-helpers/resolution/config.ts deleted file mode 100644 index 4778be9c4f2b9..0000000000000 --- a/packages/contracts/test/test-helpers/resolution/config.ts +++ /dev/null @@ -1,93 +0,0 @@ -/* External Imports */ -import { ethers } from '@nomiclabs/buidler' -import { Contract } from 'ethers' - -/* Internal Imports */ -import { AddressResolverDeployConfig, AddressResolverConfig } from './types' -import { GAS_LIMIT, DEFAULT_FORCE_INCLUSION_PERIOD } from '../constants' - -/** - * Generates the default deployment configuration. Runs as an async function - * because we need to get the contract factories async via buidler. - * @param addressResolver Address resolver contract to connect to. - * @returns Default address resolver deployment configuration. - */ -export const getDefaultDeployConfig = async ( - addressResolver: Contract -): Promise => { - const [owner, sequencer, l1ToL2TransactionPasser] = await ethers.getSigners() - - return { - L1ToL2TransactionQueue: { - factory: await ethers.getContractFactory('L1ToL2TransactionQueue'), - params: [ - addressResolver.address, - await l1ToL2TransactionPasser.getAddress(), - ], - }, - SafetyTransactionQueue: { - factory: await ethers.getContractFactory('SafetyTransactionQueue'), - params: [addressResolver.address], - }, - CanonicalTransactionChain: { - factory: await ethers.getContractFactory('CanonicalTransactionChain'), - params: [ - addressResolver.address, - await sequencer.getAddress(), - await l1ToL2TransactionPasser.getAddress(), - DEFAULT_FORCE_INCLUSION_PERIOD, - ], - }, - StateCommitmentChain: { - factory: await ethers.getContractFactory('StateCommitmentChain'), - params: [addressResolver.address], - }, - StateManager: { - factory: await ethers.getContractFactory('FullStateManager'), - params: [], - }, - ExecutionManager: { - factory: await ethers.getContractFactory('ExecutionManager'), - params: [addressResolver.address, await owner.getAddress(), GAS_LIMIT], - }, - SafetyChecker: { - factory: await ethers.getContractFactory('StubSafetyChecker'), - params: [], - }, - FraudVerifier: { - factory: await ethers.getContractFactory('FraudVerifier'), - params: [addressResolver.address], - }, - } -} - -/** - * Generates the deployment configuration for various libraries. - * @returns Library deployment configuration. - */ -export const getLibraryDeployConfig = async (): Promise => { - return { - RollupMerkleUtils: { - factory: await ethers.getContractFactory('RollupMerkleUtils'), - params: [], - }, - } -} - -/** - * Given a config, generates the default config and merges the two. - * @param addressResolver Address resolver to connect to the config. - * @param config User-provided configuration. - * @returns Config merged with default config. - */ -export const makeDeployConfig = async ( - addressResolver: Contract, - config: Partial -): Promise => { - const defaultDeployConfig = await getDefaultDeployConfig(addressResolver) - - return { - ...defaultDeployConfig, - ...config.deployConfig, - } -} diff --git a/packages/contracts/test/test-helpers/resolution/index.ts b/packages/contracts/test/test-helpers/resolution/index.ts deleted file mode 100644 index 8fd4eaea78bcc..0000000000000 --- a/packages/contracts/test/test-helpers/resolution/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './resolver' -export { AddressResolverMapping } from './types' diff --git a/packages/contracts/test/test-helpers/resolution/resolver.ts b/packages/contracts/test/test-helpers/resolution/resolver.ts deleted file mode 100644 index 1b0df11b9ab5c..0000000000000 --- a/packages/contracts/test/test-helpers/resolution/resolver.ts +++ /dev/null @@ -1,95 +0,0 @@ -/* External Imports */ -import { ethers } from '@nomiclabs/buidler' -import { Contract, Signer } from 'ethers' - -/* Internal Imports */ -import { - AddressResolverConfig, - ContractDeployConfig, - AddressResolverMapping, - factoryToContractName, -} from './types' -import { getLibraryDeployConfig, makeDeployConfig } from './config' - -/** - * Deploys all necessary libraries. - * @param addressResolver Address resolver to attach libraries to. - * @param signer Signer to deploy libraries from. - */ -const deployLibraries = async ( - addressResolver: Contract, - signer: Signer -): Promise => { - const libraryDeployConfig = await getLibraryDeployConfig() - - for (const name of Object.keys(libraryDeployConfig)) { - await deployAndRegister( - addressResolver, - signer, - name, - libraryDeployConfig[name] - ) - } -} - -/** - * Deploys a contract and registers it with the address resolver. - * @param addressResolver Address resolver to register to. - * @param signer Wallet to deploy the contract from. - * @param name Name of the contract within the resolver. - * @param deployConfig Contract deployment configuration. - * @returns Ethers Contract instance. - */ -export const deployAndRegister = async ( - addressResolver: Contract, - signer: Signer, - name: string, - deployConfig: ContractDeployConfig -): Promise => { - deployConfig.factory.connect(signer) - const deployedContract = await deployConfig.factory.deploy( - ...deployConfig.params - ) - await addressResolver.setAddress(name, deployedContract.address) - return deployedContract -} - -/** - * Creates an address resolver based on some user config. Defaults used for any - * values not provided for the user. - * @param signer Wallet to deploy all contracts from. - * @param config Config used to deploy various contracts. - * @returns Object containing the resolver and any deployed contracts. - */ -export const makeAddressResolver = async ( - signer: Signer, - config: Partial = {} -): Promise => { - const AddressResolver = await ethers.getContractFactory('AddressResolver') - const addressResolver = await AddressResolver.deploy() - - await deployLibraries(addressResolver, signer) - - const deployConfig = await makeDeployConfig(addressResolver, config) - - const contracts: any = {} - for (const name of Object.keys(deployConfig)) { - if ( - config.dependencies === undefined || - config.dependencies.includes(name as any) - ) { - const contractName = factoryToContractName[name] - contracts[contractName] = await deployAndRegister( - addressResolver, - signer, - name, - deployConfig[name] - ) - } - } - - return { - addressResolver, - contracts, - } -} diff --git a/packages/contracts/test/test-helpers/resolution/types.ts b/packages/contracts/test/test-helpers/resolution/types.ts deleted file mode 100644 index 7238a22ad885b..0000000000000 --- a/packages/contracts/test/test-helpers/resolution/types.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* External Imports */ -import { Contract, ContractFactory } from 'ethers' - -export interface ContractDeployConfig { - factory: ContractFactory - params: any[] -} - -type ContractFactoryName = - | 'L1ToL2TransactionQueue' - | 'SafetyTransactionQueue' - | 'CanonicalTransactionChain' - | 'StateCommitmentChain' - | 'StateManager' - | 'ExecutionManager' - | 'SafetyChecker' - | 'FraudVerifier' - -export interface AddressResolverDeployConfig { - L1ToL2TransactionQueue: ContractDeployConfig - SafetyTransactionQueue: ContractDeployConfig - CanonicalTransactionChain: ContractDeployConfig - StateCommitmentChain: ContractDeployConfig - StateManager: ContractDeployConfig - ExecutionManager: ContractDeployConfig - SafetyChecker: ContractDeployConfig - FraudVerifier: ContractDeployConfig -} - -export interface AddressResolverConfig { - deployConfig: AddressResolverDeployConfig - dependencies: ContractFactoryName[] -} - -interface ContractMapping { - l1ToL2TransactionQueue: Contract - safetyTransactionQueue: Contract - canonicalTransactionChain: Contract - stateCommitmentChain: Contract - stateManager: Contract - executionManager: Contract - safetyChecker: Contract - fraudVerifier: Contract -} - -export interface AddressResolverMapping { - addressResolver: Contract - contracts: ContractMapping -} - -export const factoryToContractName = { - L1ToL2TransactionQueue: 'l1ToL2TransactionQueue', - SafetyTransactionQueue: 'safetyTransactionQueue', - CanonicalTransactionChain: 'canonicalTransactionChain', - StateCommitmentChain: 'stateCommitmentChain', - StateManager: 'stateManager', - ExecutionManager: 'executionManager', - SafetyChecker: 'safetyChecker', - FraudVerifier: 'fraudVerifier', -} diff --git a/packages/contracts/test/test-helpers/types/deployment.ts b/packages/contracts/test/test-helpers/types/deployment.ts new file mode 100644 index 0000000000000..042c7ff66f483 --- /dev/null +++ b/packages/contracts/test/test-helpers/types/deployment.ts @@ -0,0 +1 @@ +export { AddressResolverMapping } from '../../../src' diff --git a/packages/contracts/test/test-helpers/types/index.ts b/packages/contracts/test/test-helpers/types/index.ts index 84db67574782c..b54e860661b2d 100644 --- a/packages/contracts/test/test-helpers/types/index.ts +++ b/packages/contracts/test/test-helpers/types/index.ts @@ -1,2 +1,3 @@ export * from './batch' export * from './convenience' +export * from './deployment' From 7ca81277504a92e0d1c9ae0562c9993048438423 Mon Sep 17 00:00:00 2001 From: Kelvin Fichter Date: Fri, 31 Jul 2020 16:48:39 -0400 Subject: [PATCH 24/24] Minor bugfix for getWallets --- .../execution-manager/ExecutionManager.executeCall.spec.ts | 2 +- .../ExecutionManager.recover-eoa-address.spec.ts | 2 +- packages/contracts/test/test-helpers/wallet-helpers.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.executeCall.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.executeCall.spec.ts index 82cf6614871a1..e2fa3dd0bfee0 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.executeCall.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.executeCall.spec.ts @@ -34,7 +34,7 @@ const log = getLogger('execution-manager-calls', true) describe('Execution Manager -- Call opcodes', () => { const provider = ethers.provider - const [wallet] = getWallets() + const [wallet] = getWallets(provider) let resolver: AddressResolverMapping before(async () => { diff --git a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.recover-eoa-address.spec.ts b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.recover-eoa-address.spec.ts index 0093c45110cc3..1c9cf584213ec 100644 --- a/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.recover-eoa-address.spec.ts +++ b/packages/contracts/test/contracts/ovm/execution-manager/ExecutionManager.recover-eoa-address.spec.ts @@ -22,7 +22,7 @@ const log = getLogger('execution-manager-recover-eoa-address', true) /* Tests */ describe('Execution Manager -- Recover EOA Address', () => { - const [wallet] = getWallets() + const [wallet] = getWallets(ethers.provider) let resolver: AddressResolverMapping before(async () => { diff --git a/packages/contracts/test/test-helpers/wallet-helpers.ts b/packages/contracts/test/test-helpers/wallet-helpers.ts index 809fdf14908df..518aad10e4c14 100644 --- a/packages/contracts/test/test-helpers/wallet-helpers.ts +++ b/packages/contracts/test/test-helpers/wallet-helpers.ts @@ -4,9 +4,9 @@ import { ethers, Wallet } from 'ethers' /* Internal Imports */ import { DEFAULT_ACCOUNTS } from './constants' -export const getWallets = (): Wallet[] => { +export const getWallets = (provider?: any): Wallet[] => { return DEFAULT_ACCOUNTS.map((account) => { - return new ethers.Wallet(account.secretKey) + return new ethers.Wallet(account.secretKey, provider) }) }