Commit 3a0271f8 authored by Kelvin Fichter's avatar Kelvin Fichter Committed by Mark Tyneway

feat(ctb): introduce Types.sol

Introduces a new library, Types.sol, meant to hold various different
structs that get used all over the place.
parent bad725d7
---
'@eth-optimism/contracts-bedrock': patch
---
Introduces Types.sol
This diff is collapsed.
This diff is collapsed.
......@@ -2,4 +2,4 @@
// This file is a generated binding and any manual changes will be lost.
package bindings
var L2ToL1MessagePasserDeployedBin = "0x60806040526004361061005e5760003560e01c806382e3702d1161004357806382e3702d146100c7578063affed0e014610107578063c2b3e5ac1461012b57600080fd5b806344df8e701461008757806354fd4d501461009c57600080fd5b366100825761008033620186a060405180602001604052806000815250610139565b005b600080fd5b34801561009357600080fd5b506100806101ef565b3480156100a857600080fd5b506100b1610227565b6040516100be91906104fb565b60405180910390f35b3480156100d357600080fd5b506100f76100e2366004610515565b60006020819052908152604090205460ff1681565b60405190151581526020016100be565b34801561011357600080fd5b5061011d60015481565b6040519081526020016100be565b61008061013936600461055d565b600061014b60015433863487876102ca565b6000818152602081905260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915554905191925073ffffffffffffffffffffffffffffffffffffffff8616913391907f87bf7b546c8de873abb0db5b579ec131f8d0cf5b14f39933551cf9ced23a6136906101d990349089908990610661565b60405180910390a4505060018054810190555050565b476101f981610309565b60405181907f7967de617a5ac1cc7eba2d6f37570a0135afa950d8bb77cdd35f0d0b4e85a16f90600090a250565b60606102527f0000000000000000000000000000000000000000000000000000000000000000610338565b61027b7f0000000000000000000000000000000000000000000000000000000000000000610338565b6102a47f0000000000000000000000000000000000000000000000000000000000000000610338565b6040516020016102b693929190610689565b604051602081830303815290604052905090565b60008686868686866040516020016102e7969594939291906106ff565b6040516020818303038152906040528051906020012090509695505050505050565b8060405161031690610475565b6040518091039082f0905080158015610333573d6000803e3d6000fd5b505050565b60608160000361037b57505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b81156103a5578061038f81610785565b915061039e9050600a836107ec565b915061037f565b60008167ffffffffffffffff8111156103c0576103c061052e565b6040519080825280601f01601f1916602001820160405280156103ea576020820181803683370190505b5090505b841561046d576103ff600183610800565b915061040c600a86610817565b61041790603061082b565b60f81b81838151811061042c5761042c610843565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610466600a866107ec565b94506103ee565b949350505050565b60088061087383390190565b60005b8381101561049c578181015183820152602001610484565b838111156104ab576000848401525b50505050565b600081518084526104c9816020860160208601610481565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061050e60208301846104b1565b9392505050565b60006020828403121561052757600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060006060848603121561057257600080fd5b833573ffffffffffffffffffffffffffffffffffffffff8116811461059657600080fd5b925060208401359150604084013567ffffffffffffffff808211156105ba57600080fd5b818601915086601f8301126105ce57600080fd5b8135818111156105e0576105e061052e565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156106265761062661052e565b8160405282815289602084870101111561063f57600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b83815282602082015260606040820152600061068060608301846104b1565b95945050505050565b6000845161069b818460208901610481565b80830190507f2e0000000000000000000000000000000000000000000000000000000000000080825285516106d7816001850160208a01610481565b600192019182015283516106f2816002840160208801610481565b0160020195945050505050565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a083015261074a60c08301846104b1565b98975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036107b6576107b6610756565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826107fb576107fb6107bd565b500490565b60008282101561081257610812610756565b500390565b600082610826576108266107bd565b500690565b6000821982111561083e5761083e610756565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfe608060405230fffea164736f6c634300080f000a"
var L2ToL1MessagePasserDeployedBin = "0x60806040526004361061005e5760003560e01c806382e3702d1161004357806382e3702d146100c7578063affed0e014610107578063c2b3e5ac1461012b57600080fd5b806344df8e701461008757806354fd4d501461009c57600080fd5b366100825761008033620186a060405180602001604052806000815250610139565b005b600080fd5b34801561009357600080fd5b50610080610242565b3480156100a857600080fd5b506100b161027a565b6040516100be919061055c565b60405180910390f35b3480156100d357600080fd5b506100f76100e2366004610576565b60006020819052908152604090205460ff1681565b60405190151581526020016100be565b34801561011357600080fd5b5061011d60015481565b6040519081526020016100be565b6100806101393660046105be565b600061019e6040518060c0016040528060015481526020013373ffffffffffffffffffffffffffffffffffffffff1681526020018673ffffffffffffffffffffffffffffffffffffffff1681526020013481526020018581526020018481525061031d565b6000818152602081905260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915554905191925073ffffffffffffffffffffffffffffffffffffffff8616913391907f87bf7b546c8de873abb0db5b579ec131f8d0cf5b14f39933551cf9ced23a61369061022c903490899089906106c2565b60405180910390a4505060018054810190555050565b4761024c8161036a565b60405181907f7967de617a5ac1cc7eba2d6f37570a0135afa950d8bb77cdd35f0d0b4e85a16f90600090a250565b60606102a57f0000000000000000000000000000000000000000000000000000000000000000610399565b6102ce7f0000000000000000000000000000000000000000000000000000000000000000610399565b6102f77f0000000000000000000000000000000000000000000000000000000000000000610399565b604051602001610309939291906106ea565b604051602081830303815290604052905090565b80516020808301516040808501516060860151608087015160a0880151935160009761034d979096959101610760565b604051602081830303815290604052805190602001209050919050565b80604051610377906104d6565b6040518091039082f0905080158015610394573d6000803e3d6000fd5b505050565b6060816000036103dc57505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b811561040657806103f0816107e6565b91506103ff9050600a8361084d565b91506103e0565b60008167ffffffffffffffff8111156104215761042161058f565b6040519080825280601f01601f19166020018201604052801561044b576020820181803683370190505b5090505b84156104ce57610460600183610861565b915061046d600a86610878565b61047890603061088c565b60f81b81838151811061048d5761048d6108a4565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506104c7600a8661084d565b945061044f565b949350505050565b6008806108d483390190565b60005b838110156104fd5781810151838201526020016104e5565b8381111561050c576000848401525b50505050565b6000815180845261052a8160208601602086016104e2565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061056f6020830184610512565b9392505050565b60006020828403121561058857600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806000606084860312156105d357600080fd5b833573ffffffffffffffffffffffffffffffffffffffff811681146105f757600080fd5b925060208401359150604084013567ffffffffffffffff8082111561061b57600080fd5b818601915086601f83011261062f57600080fd5b8135818111156106415761064161058f565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156106875761068761058f565b816040528281528960208487010111156106a057600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b8381528260208201526060604082015260006106e16060830184610512565b95945050505050565b600084516106fc8184602089016104e2565b80830190507f2e000000000000000000000000000000000000000000000000000000000000008082528551610738816001850160208a016104e2565b600192019182015283516107538160028401602088016104e2565b0160020195945050505050565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a08301526107ab60c0830184610512565b98975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610817576108176107b7565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008261085c5761085c61081e565b500490565b600082821015610873576108736107b7565b500390565b6000826108875761088761081e565b500690565b6000821982111561089f5761089f6107b7565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfe608060405230fffea164736f6c634300080f000a"
This diff is collapsed.
......@@ -899,12 +899,14 @@ func TestWithdrawals(t *testing.T) {
opts.Value = nil
tx, err = portal.FinalizeWithdrawalTransaction(
opts,
params.Nonce,
params.Sender,
params.Target,
params.Value,
params.GasLimit,
params.Data,
bindings.TypesWithdrawalTransaction{
Nonce: params.Nonce,
Sender: params.Sender,
Target: params.Target,
Value: params.Value,
GasLimit: params.GasLimit,
Data: params.Data,
},
params.BlockNumber,
params.OutputRootProof,
params.WithdrawalProof,
......
......@@ -154,7 +154,7 @@ type FinalizedWithdrawalParameters struct {
GasLimit *big.Int
BlockNumber *big.Int
Data []byte
OutputRootProof bindings.HashingOutputRootProof
OutputRootProof bindings.TypesOutputRootProof
WithdrawalProof []byte // RLP Encoded list of trie nodes to prove L2 storage
}
......@@ -210,7 +210,7 @@ func FinalizeWithdrawalParameters(ctx context.Context, l2client ProofClient, txH
GasLimit: ev.GasLimit,
BlockNumber: new(big.Int).Set(header.Number),
Data: ev.Data,
OutputRootProof: bindings.HashingOutputRootProof{
OutputRootProof: bindings.TypesOutputRootProof{
Version: [32]byte{}, // Empty for version 1
StateRoot: header.Root,
WithdrawerStorageRoot: p.StorageHash,
......
This diff is collapsed.
......@@ -5,6 +5,7 @@ import {
OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { Semver } from "../universal/Semver.sol";
import { Types } from "../libraries/Types.sol";
/**
* @custom:proxied
......@@ -15,17 +16,6 @@ import { Semver } from "../universal/Semver.sol";
*/
// slither-disable-next-line locked-ether
contract L2OutputOracle is OwnableUpgradeable, Semver {
/**
* @notice OutputProposal represents a commitment to the L2 state.
* The timestamp is the L1 timestamp that the output root is posted.
* This timestamp is used to verify that the finalization period
* has passed since the output root was submitted.
*/
struct OutputProposal {
bytes32 outputRoot;
uint256 timestamp;
}
/**
* @notice Emitted when an output is proposed.
*
......@@ -105,7 +95,7 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
* outputs should not be considered finalized until the finalization period (as defined
* in the Optimism Portal) has passed.
*/
mapping(uint256 => OutputProposal) internal l2Outputs;
mapping(uint256 => Types.OutputProposal) internal l2Outputs;
/**
* @notice Reverts if called by any account other than the proposer.
......@@ -165,7 +155,7 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
address _proposer,
address _owner
) public initializer {
l2Outputs[_startingBlockNumber] = OutputProposal(_genesisL2Output, block.timestamp);
l2Outputs[_startingBlockNumber] = Types.OutputProposal(_genesisL2Output, block.timestamp);
latestBlockNumber = _startingBlockNumber;
__Ownable_init();
changeProposer(_proposer);
......@@ -218,7 +208,7 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
);
}
l2Outputs[_l2BlockNumber] = OutputProposal(_outputRoot, block.timestamp);
l2Outputs[_l2BlockNumber] = Types.OutputProposal(_outputRoot, block.timestamp);
latestBlockNumber = _l2BlockNumber;
emit OutputProposed(_outputRoot, block.timestamp, _l2BlockNumber);
......@@ -233,8 +223,8 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
*
* @param _proposal Represents the output proposal to delete
*/
function deleteL2Output(OutputProposal memory _proposal) external onlyOwner {
OutputProposal memory outputToDelete = l2Outputs[latestBlockNumber];
function deleteL2Output(Types.OutputProposal memory _proposal) external onlyOwner {
Types.OutputProposal memory outputToDelete = l2Outputs[latestBlockNumber];
require(
_proposal.outputRoot == outputToDelete.outputRoot,
......@@ -268,7 +258,7 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
*
* @param _l2BlockNumber The L2 block number of the target block.
*/
function getL2Output(uint256 _l2BlockNumber) external view returns (OutputProposal memory) {
function getL2Output(uint256 _l2BlockNumber) external view returns (Types.OutputProposal memory) {
require(
_l2BlockNumber >= STARTING_BLOCK_NUMBER,
"L2OutputOracle: block number cannot be less than the starting block number."
......@@ -283,7 +273,7 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
? _l2BlockNumber
: _l2BlockNumber + (SUBMISSION_INTERVAL - offset);
OutputProposal memory output = l2Outputs[lookupBlockNumber];
Types.OutputProposal memory output = l2Outputs[lookupBlockNumber];
require(
output.outputRoot != bytes32(0),
"L2OutputOracle: No output found for that block number."
......
......@@ -4,6 +4,7 @@ pragma solidity 0.8.15;
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import { ExcessivelySafeCall } from "excessively-safe-call/src/ExcessivelySafeCall.sol";
import { L2OutputOracle } from "./L2OutputOracle.sol";
import { Types } from "../libraries/Types.sol";
import { Hashing } from "../libraries/Hashing.sol";
import { SecureMerkleTrie } from "../libraries/trie/SecureMerkleTrie.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
......@@ -175,8 +176,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
* @param _l2BlockNumber The number of the L2 block.
*/
function isBlockFinalized(uint256 _l2BlockNumber) external view returns (bool) {
L2OutputOracle.OutputProposal memory proposal = L2_ORACLE.getL2Output(_l2BlockNumber);
Types.OutputProposal memory proposal = L2_ORACLE.getL2Output(_l2BlockNumber);
return _isOutputFinalized(proposal);
}
......@@ -185,7 +185,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
*
* @param _proposal The output proposal to check.
*/
function _isOutputFinalized(L2OutputOracle.OutputProposal memory _proposal)
function _isOutputFinalized(Types.OutputProposal memory _proposal)
internal
view
returns (bool)
......@@ -196,25 +196,15 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
/**
* @notice Finalizes a withdrawal transaction.
*
* @param _nonce Nonce for the provided message.
* @param _sender Message sender address on L2.
* @param _target Target address on L1.
* @param _value ETH to send to the target.
* @param _gasLimit Minumum gas to be forwarded to the target.
* @param _data Data to send to the target.
* @param _tx Withdrawal transaction to finalize.
* @param _l2BlockNumber L2 block number of the outputRoot.
* @param _outputRootProof Inclusion proof of the withdrawer contracts storage root.
* @param _withdrawalProof Inclusion proof for the given withdrawal in the withdrawer contract.
*/
function finalizeWithdrawalTransaction(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes calldata _data,
Types.WithdrawalTransaction memory _tx,
uint256 _l2BlockNumber,
Hashing.OutputRootProof calldata _outputRootProof,
Types.OutputRootProof calldata _outputRootProof,
bytes calldata _withdrawalProof
) external payable {
// Prevent nested withdrawals within withdrawals.
......@@ -226,13 +216,13 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
// Prevent users from creating a deposit transaction where this address is the message
// sender on L2.
require(
_target != address(this),
_tx.target != address(this),
"OptimismPortal: you cannot send messages to the portal contract"
);
// Get the output root. This will fail if there is no
// output root for the given block number.
L2OutputOracle.OutputProposal memory proposal = L2_ORACLE.getL2Output(_l2BlockNumber);
Types.OutputProposal memory proposal = L2_ORACLE.getL2Output(_l2BlockNumber);
// Ensure that enough time has passed since the proposal was submitted before allowing a
// withdrawal. Under the assumption that the fault proof mechanism is operating correctly,
......@@ -248,14 +238,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
// All withdrawals have a unique hash, we'll use this as the identifier for the withdrawal
// and to prevent replay attacks.
bytes32 withdrawalHash = Hashing.hashWithdrawal(
_nonce,
_sender,
_target,
_value,
_gasLimit,
_data
);
bytes32 withdrawalHash = Hashing.hashWithdrawal(_tx);
// Verify that the hash of this withdrawal was stored in the withdrawal contract on L2. If
// this is true, then we know that this withdrawal was actually triggered on L2 can can
......@@ -282,22 +265,22 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
// target contract is at least the gas limit specified by the user. We can do this by
// enforcing that, at this point in time, we still have gaslimit + buffer gas available.
require(
gasleft() >= _gasLimit + FINALIZE_GAS_BUFFER,
gasleft() >= _tx.gasLimit + FINALIZE_GAS_BUFFER,
"OptimismPortal: insufficient gas to finalize withdrawal"
);
// Set the l2Sender so contracts know who triggered this withdrawal on L2.
l2Sender = _sender;
l2Sender = _tx.sender;
// Trigger the call to the target contract. We use excessivelySafeCall because we don't
// care about the returndata and we don't want target contracts to be able to force this
// call to run out of gas.
(bool success, ) = ExcessivelySafeCall.excessivelySafeCall(
_target,
_gasLimit,
_value,
_tx.target,
_tx.gasLimit,
_tx.value,
0,
_data
_tx.data
);
// Reset the l2Sender back to the default value.
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { Types } from "../libraries/Types.sol";
import { Hashing } from "../libraries/Hashing.sol";
import { Burn } from "../libraries/Burn.sol";
import { Semver } from "../universal/Semver.sol";
......@@ -80,12 +81,14 @@ contract L2ToL1MessagePasser is Semver {
bytes memory _data
) public payable {
bytes32 withdrawalHash = Hashing.hashWithdrawal(
nonce,
msg.sender,
_target,
msg.value,
_gasLimit,
_data
Types.WithdrawalTransaction({
nonce: nonce,
sender: msg.sender,
target: _target,
value: msg.value,
gasLimit: _gasLimit,
data: _data
})
);
sentMessages[withdrawalHash] = true;
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Types } from "./Types.sol";
import { Hashing } from "./Hashing.sol";
import { RLPWriter } from "./rlp/RLPWriter.sol";
......@@ -13,38 +14,24 @@ library Encoding {
* @notice RLP encodes the L2 transaction that would be generated when a given deposit is sent
* to the L2 system. Useful for searching for a deposit in the L2 system.
*
* @param _from Address of the sender of the deposit.
* @param _to Address of the receiver of the deposit.
* @param _value ETH value to send to the receiver.
* @param _mint ETH value to mint on L2.
* @param _gasLimit Gas limit to use for the transaction.
* @param _isCreation Whether or not the transaction is a contract creation.
* @param _data Data to send with the transaction.
* @param _l1BlockHash Hash of the L1 block where the deposit was included.
* @param _logIndex Index of the deposit event in the L1 block.
* @param _tx User deposit transaction to encode.
*
* @return RLP encoded L2 deposit transaction.
*/
function encodeDepositTransaction(
address _from,
address _to,
uint256 _value,
uint256 _mint,
uint64 _gasLimit,
bool _isCreation,
bytes memory _data,
bytes32 _l1BlockHash,
uint256 _logIndex
) internal pure returns (bytes memory) {
bytes32 source = Hashing.hashDepositSource(_l1BlockHash, _logIndex);
function encodeDepositTransaction(Types.UserDepositTransaction memory _tx)
internal
pure
returns (bytes memory)
{
bytes32 source = Hashing.hashDepositSource(_tx.l1BlockHash, _tx.logIndex);
bytes[] memory raw = new bytes[](7);
raw[0] = RLPWriter.writeBytes(abi.encodePacked(source));
raw[1] = RLPWriter.writeAddress(_from);
raw[2] = _isCreation ? RLPWriter.writeBytes("") : RLPWriter.writeAddress(_to);
raw[3] = RLPWriter.writeUint(_mint);
raw[4] = RLPWriter.writeUint(_value);
raw[5] = RLPWriter.writeUint(uint256(_gasLimit));
raw[6] = RLPWriter.writeBytes(_data);
raw[1] = RLPWriter.writeAddress(_tx.from);
raw[2] = _tx.isCreation ? RLPWriter.writeBytes("") : RLPWriter.writeAddress(_tx.to);
raw[3] = RLPWriter.writeUint(_tx.mint);
raw[4] = RLPWriter.writeUint(_tx.value);
raw[5] = RLPWriter.writeUint(uint256(_tx.gasLimit));
raw[6] = RLPWriter.writeBytes(_tx.data);
return abi.encodePacked(uint8(0x7e), RLPWriter.writeList(raw));
}
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Types } from "./Types.sol";
import { Encoding } from "./Encoding.sol";
/**
......@@ -8,58 +9,21 @@ import { Encoding } from "./Encoding.sol";
* @notice Hashing handles Optimism's various different hashing schemes.
*/
library Hashing {
/**
* @notice Struct representing the elements that are hashed together to generate an output root
* which itself represents a snapshot of the L2 state.
*/
struct OutputRootProof {
bytes32 version;
bytes32 stateRoot;
bytes32 withdrawerStorageRoot;
bytes32 latestBlockhash;
}
/**
* @notice Computes the hash of the RLP encoded L2 transaction that would be generated when a
* given deposit is sent to the L2 system. Useful for searching for a deposit in the L2
* system.
*
* @param _from Address of the sender of the deposit.
* @param _to Address of the receiver of the deposit.
* @param _value ETH value to send to the receiver.
* @param _mint ETH value to mint on L2.
* @param _gasLimit Gas limit to use for the transaction.
* @param _isCreation Whether or not the transaction is a contract creation.
* @param _data Data to send with the transaction.
* @param _l1BlockHash Hash of the L1 block where the deposit was included.
* @param _logIndex Index of the deposit event in the L1 block.
* @param _tx User deposit transaction to hash.
*
* @return Hash of the RLP encoded L2 deposit transaction.
*/
function hashDepositTransaction(
address _from,
address _to,
uint256 _value,
uint256 _mint,
uint64 _gasLimit,
bool _isCreation,
bytes memory _data,
bytes32 _l1BlockHash,
uint256 _logIndex
) internal pure returns (bytes32) {
bytes memory raw = Encoding.encodeDepositTransaction(
_from,
_to,
_value,
_mint,
_gasLimit,
_isCreation,
_data,
_l1BlockHash,
_logIndex
);
return keccak256(raw);
function hashDepositTransaction(Types.UserDepositTransaction memory _tx)
internal
pure
returns (bytes32)
{
return keccak256(Encoding.encodeDepositTransaction(_tx));
}
/**
......@@ -166,22 +130,20 @@ library Hashing {
/**
* @notice Derives the withdrawal hash according to the encoding in the L2 Withdrawer contract
* @param _nonce Nonce for the provided message.
* @param _sender Message sender address on L2.
* @param _target Target address on L1.
* @param _value ETH to send to the target.
* @param _gasLimit Gas to be forwarded to the target.
* @param _data Data to send to the target.
*
* @param _tx Withdrawal transaction to hash.
*
* @return Hashed withdrawal transaction.
*/
function hashWithdrawal(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
) internal pure returns (bytes32) {
return keccak256(abi.encode(_nonce, _sender, _target, _value, _gasLimit, _data));
function hashWithdrawal(Types.WithdrawalTransaction memory _tx)
internal
pure
returns (bytes32)
{
return
keccak256(
abi.encode(_tx.nonce, _tx.sender, _tx.target, _tx.value, _tx.gasLimit, _tx.data)
);
}
/**
......@@ -192,7 +154,7 @@ library Hashing {
*
* @return Hashed output root proof.
*/
function hashOutputRootProof(OutputRootProof memory _outputRootProof)
function hashOutputRootProof(Types.OutputRootProof memory _outputRootProof)
internal
pure
returns (bytes32)
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
/**
* @title Types
* @notice Contains various types used throughout the Optimism contract system.
*/
library Types {
/**
* @notice OutputProposal represents a commitment to the L2 state. The timestamp is the L1
* timestamp that the output root is posted. This timestamp is used to verify that the
* finalization period has passed since the output root was submitted.
*/
struct OutputProposal {
bytes32 outputRoot;
uint256 timestamp;
}
/**
* @notice Struct representing the elements that are hashed together to generate an output root
* which itself represents a snapshot of the L2 state.
*/
struct OutputRootProof {
bytes32 version;
bytes32 stateRoot;
bytes32 withdrawerStorageRoot;
bytes32 latestBlockhash;
}
/**
* @notice Struct representing a deposit transaction (L1 => L2 transaction) created by an end
* user (as opposed to a system deposit transaction generated by the system).
*/
struct UserDepositTransaction {
address from;
address to;
uint256 value;
uint256 mint;
uint64 gasLimit;
bool isCreation;
bytes data;
bytes32 l1BlockHash;
uint256 logIndex;
}
/**
* @notice Struct representing a withdrawal transaction.
*/
struct WithdrawalTransaction {
uint256 nonce;
address sender;
address target;
uint256 value;
uint256 gasLimit;
bytes data;
}
}
......@@ -2,6 +2,7 @@
pragma solidity 0.8.15;
import { CommonTest } from "./CommonTest.t.sol";
import { Types } from "../libraries/Types.sol";
import { Encoding } from "../libraries/Encoding.sol";
contract Encoding_Test is CommonTest {
......
......@@ -2,6 +2,7 @@
pragma solidity 0.8.15;
import { CommonTest } from "./CommonTest.t.sol";
import { Types } from "../libraries/Types.sol";
import { Hashing } from "../libraries/Hashing.sol";
import { Encoding } from "../libraries/Encoding.sol";
......@@ -64,12 +65,14 @@ contract Hashing_Test is CommonTest {
bytes memory _data
) external {
bytes32 hash = Hashing.hashWithdrawal(
Types.WithdrawalTransaction(
_nonce,
_sender,
_target,
_value,
_gasLimit,
_data
)
);
bytes32 _hash = ffi.hashWithdrawal(
......@@ -90,7 +93,7 @@ contract Hashing_Test is CommonTest {
bytes32 _withdrawerStorageRoot,
bytes32 _latestBlockhash
) external {
Hashing.OutputRootProof memory proof = Hashing.OutputRootProof({
Types.OutputRootProof memory proof = Types.OutputRootProof({
version: _version,
stateRoot: _stateRoot,
withdrawerStorageRoot: _withdrawerStorageRoot,
......@@ -121,6 +124,7 @@ contract Hashing_Test is CommonTest {
uint256 _logIndex
) external {
bytes32 hash = Hashing.hashDepositTransaction(
Types.UserDepositTransaction(
_from,
_to,
_value,
......@@ -130,6 +134,7 @@ contract Hashing_Test is CommonTest {
_data,
bytes32(uint256(0)),
_logIndex
)
);
bytes32 _hash = ffi.hashDepositTransaction(
......
......@@ -4,7 +4,7 @@ pragma solidity 0.8.15;
import { L2OutputOracle_Initializer, NextImpl } from "./CommonTest.t.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { Proxy } from "../universal/Proxy.sol";
import { Types } from "../libraries/Types.sol";
contract L2OutputOracleTest is L2OutputOracle_Initializer {
bytes32 proposedOutput1 = keccak256(abi.encode(1));
......@@ -23,7 +23,7 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer {
assertEq(oracle.proposer(), proposer);
assertEq(oracle.owner(), owner);
L2OutputOracle.OutputProposal memory proposal = oracle.getL2Output(startingBlockNumber);
Types.OutputProposal memory proposal = oracle.getL2Output(startingBlockNumber);
assertEq(proposal.outputRoot, genesisL2Output);
assertEq(proposal.timestamp, initL1Time);
}
......@@ -50,7 +50,7 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer {
vm.prank(proposer);
oracle.proposeL2Output(proposedOutput1, nextBlockNumber, 0, 0);
L2OutputOracle.OutputProposal memory proposal = oracle.getL2Output(nextBlockNumber);
Types.OutputProposal memory proposal = oracle.getL2Output(nextBlockNumber);
assertEq(proposal.outputRoot, proposedOutput1);
assertEq(proposal.timestamp, block.timestamp);
......@@ -61,11 +61,11 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer {
// The block number is too low:
vm.expectRevert("L2OutputOracle: block number cannot be less than the starting block number.");
proposal = oracle.getL2Output(0);
oracle.getL2Output(0);
// The block number is larger than the latest proposed output:
vm.expectRevert("L2OutputOracle: No output found for that block number.");
proposal = oracle.getL2Output(nextBlockNumber + 1);
oracle.getL2Output(nextBlockNumber + 1);
}
// Test: nextBlockNumber() should return the correct value
......@@ -268,10 +268,10 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer {
test_proposingAnotherOutput();
uint256 latestBlockNumber = oracle.latestBlockNumber();
L2OutputOracle.OutputProposal memory proposalToDelete = oracle.getL2Output(
Types.OutputProposal memory proposalToDelete = oracle.getL2Output(
latestBlockNumber
);
L2OutputOracle.OutputProposal memory newLatestOutput = oracle.getL2Output(
Types.OutputProposal memory newLatestOutput = oracle.getL2Output(
latestBlockNumber - submissionInterval
);
......@@ -288,7 +288,7 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer {
uint256 latestBlockNumberAfter = oracle.latestBlockNumber();
assertEq(latestBlockNumber - submissionInterval, latestBlockNumberAfter);
L2OutputOracle.OutputProposal memory proposal = oracle.getL2Output(latestBlockNumberAfter);
Types.OutputProposal memory proposal = oracle.getL2Output(latestBlockNumberAfter);
// validate that the new latest output is as expected.
assertEq(newLatestOutput.outputRoot, proposal.outputRoot);
assertEq(newLatestOutput.timestamp, proposal.timestamp);
......@@ -300,7 +300,7 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer {
function testCannot_deleteL2Output_ifNotOwner() external {
uint256 latestBlockNumber = oracle.latestBlockNumber();
L2OutputOracle.OutputProposal memory proposal = oracle.getL2Output(latestBlockNumber);
Types.OutputProposal memory proposal = oracle.getL2Output(latestBlockNumber);
vm.expectRevert("Ownable: caller is not the owner");
oracle.deleteL2Output(proposal);
......@@ -310,7 +310,7 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer {
test_proposingAnotherOutput();
uint256 previousBlockNumber = oracle.latestBlockNumber() - submissionInterval;
L2OutputOracle.OutputProposal memory proposalToDelete = oracle.getL2Output(
Types.OutputProposal memory proposalToDelete = oracle.getL2Output(
previousBlockNumber
);
......@@ -325,7 +325,7 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer {
test_proposingAnotherOutput();
uint256 latestBlockNumber = oracle.latestBlockNumber();
L2OutputOracle.OutputProposal memory proposalToDelete = oracle.getL2Output(
Types.OutputProposal memory proposalToDelete = oracle.getL2Output(
latestBlockNumber
);
......@@ -354,7 +354,7 @@ contract L2OutputOracleUpgradeable_Test is L2OutputOracle_Initializer {
assertEq(startingTimestamp, oracleImpl.STARTING_TIMESTAMP());
assertEq(l2BlockTime, oracleImpl.L2_BLOCK_TIME());
L2OutputOracle.OutputProposal memory initOutput = oracleImpl.getL2Output(
Types.OutputProposal memory initOutput = oracleImpl.getL2Output(
startingBlockNumber
);
assertEq(genesisL2Output, initOutput.outputRoot);
......
......@@ -3,6 +3,7 @@ pragma solidity 0.8.15;
import { CommonTest } from "./CommonTest.t.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
import { Types } from "../libraries/Types.sol";
import { Hashing } from "../libraries/Hashing.sol";
contract L2ToL1MessagePasserTest is CommonTest {
......@@ -65,12 +66,14 @@ contract L2ToL1MessagePasserTest is CommonTest {
);
bytes32 withdrawalHash = Hashing.hashWithdrawal(
Types.WithdrawalTransaction(
nonce,
alice,
target,
value,
gasLimit,
data
)
);
messagePasser.initiateWithdrawal{ value: value }(
......
......@@ -5,6 +5,7 @@ import { Portal_Initializer, CommonTest, NextImpl } from "./CommonTest.t.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { OptimismPortal } from "../L1/OptimismPortal.sol";
import { Types } from "../libraries/Types.sol";
import { Hashing } from "../libraries/Hashing.sol";
import { Proxy } from "../universal/Proxy.sol";
......@@ -214,11 +215,8 @@ contract OptimismPortal_Test is Portal_Initializer {
assertEq(address(op).balance, NON_ZERO_VALUE);
}
// TODO: test this deeply
// function test_verifyWithdrawal() external {}
function test_cannotFinalizeRecentWithdrawal() external {
Hashing.OutputRootProof memory outputRootProof = Hashing
function test_cannotVerifyRecentWithdrawal() external {
Types.OutputRootProof memory outputRootProof = Types
.OutputRootProof({
version: bytes32(0),
stateRoot: bytes32(0),
......@@ -230,20 +228,21 @@ contract OptimismPortal_Test is Portal_Initializer {
vm.mockCall(
address(op.L2_ORACLE()),
abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
abi.encode(L2OutputOracle.OutputProposal(bytes32(uint256(1)), recentTimestamp))
abi.encode(Types.OutputProposal(bytes32(uint256(1)), recentTimestamp))
);
vm.expectRevert("OptimismPortal: proposal is not yet finalized");
op.finalizeWithdrawalTransaction(0, alice, alice, 0, 0, hex"", 0, outputRootProof, hex"");
op.finalizeWithdrawalTransaction(Types.WithdrawalTransaction(0, alice, alice, 0, 0, hex""), 0, outputRootProof, hex"");
}
function test_invalidWithdrawalProof() external {
vm.mockCall(
address(op.L2_ORACLE()),
abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
abi.encode(L2OutputOracle.OutputProposal(bytes32(uint256(1)), block.timestamp))
abi.encode(Types.OutputProposal(bytes32(uint256(1)), block.timestamp))
);
Hashing.OutputRootProof memory outputRootProof = Hashing
Types.OutputRootProof memory outputRootProof = Types
.OutputRootProof({
version: bytes32(0),
stateRoot: bytes32(0),
......@@ -257,7 +256,7 @@ contract OptimismPortal_Test is Portal_Initializer {
);
vm.expectRevert("OptimismPortal: invalid output root proof");
op.finalizeWithdrawalTransaction(0, alice, alice, 0, 0, hex"", 0, outputRootProof, hex"");
op.finalizeWithdrawalTransaction(Types.WithdrawalTransaction(0, alice, alice, 0, 0, hex""), 0, outputRootProof, hex"");
}
function test_simple_isBlockFinalized() external {
......@@ -267,7 +266,7 @@ contract OptimismPortal_Test is Portal_Initializer {
L2OutputOracle.getL2Output.selector
),
abi.encode(
L2OutputOracle.OutputProposal(
Types.OutputProposal(
bytes32(uint256(1)),
startingBlockNumber
)
......@@ -321,7 +320,6 @@ contract OptimismPortal_Test is Portal_Initializer {
) external {
// Cannot call the optimism portal
vm.assume(_target != address(op));
uint256 _nonce = messagePasser.nonce();
(
......@@ -339,22 +337,23 @@ contract OptimismPortal_Test is Portal_Initializer {
_data
);
Hashing.OutputRootProof memory proof = Hashing.OutputRootProof({
// Ensure the values returned from ffi are correct
assertEq(outputRoot, Hashing.hashOutputRootProof(Types.OutputRootProof({
version: bytes32(uint256(0)),
stateRoot: stateRoot,
withdrawerStorageRoot: storageRoot,
latestBlockhash: bytes32(uint256(0))
});
})));
// Ensure the values returned from ffi are correct
assertEq(outputRoot, Hashing.hashOutputRootProof(proof));
assertEq(withdrawalHash, Hashing.hashWithdrawal(
Types.WithdrawalTransaction(
_nonce,
_sender,
_target,
_value,
uint64(_gasLimit),
_data
)
));
// Mock the call to the oracle
......@@ -378,14 +377,21 @@ contract OptimismPortal_Test is Portal_Initializer {
vm.warp(op.FINALIZATION_PERIOD_SECONDS() + 1);
op.finalizeWithdrawalTransaction{ value: _value }(
Types.WithdrawalTransaction(
messagePasser.nonce() - 1,
_sender,
_target,
_value,
uint64(_gasLimit),
_data,
_data
),
100, // l2BlockNumber
proof,
Types.OutputRootProof({
version: bytes32(uint256(0)),
stateRoot: stateRoot,
withdrawerStorageRoot: storageRoot,
latestBlockhash: bytes32(uint256(0))
}),
withdrawalProof
);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment