Commit 0cb3929e authored by smartcontracts's avatar smartcontracts Committed by GitHub

style(ctb): refactor Hashing and Encoding libs (#2974)

Cleans up CrossDomainUtils, CrossDomainHashing, and WithdrawalVerifier
into two new libraries, Hashing and Encoding. Will be followed up by a
second PR to clean up function names and comments.
Co-authored-by: default avatarmergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
parent 032c5f97
---
'@eth-optimism/contracts-bedrock': patch
---
Move encoding and hashing into Encoding and Hashing libraries
This diff is collapsed.
...@@ -154,7 +154,7 @@ type FinalizedWithdrawalParameters struct { ...@@ -154,7 +154,7 @@ type FinalizedWithdrawalParameters struct {
GasLimit *big.Int GasLimit *big.Int
BlockNumber *big.Int BlockNumber *big.Int
Data []byte Data []byte
OutputRootProof bindings.WithdrawalVerifierOutputRootProof OutputRootProof bindings.HashingOutputRootProof
WithdrawalProof []byte // RLP Encoded list of trie nodes to prove L2 storage WithdrawalProof []byte // RLP Encoded list of trie nodes to prove L2 storage
} }
...@@ -210,7 +210,7 @@ func FinalizeWithdrawalParameters(ctx context.Context, l2client ProofClient, txH ...@@ -210,7 +210,7 @@ func FinalizeWithdrawalParameters(ctx context.Context, l2client ProofClient, txH
GasLimit: ev.GasLimit, GasLimit: ev.GasLimit,
BlockNumber: new(big.Int).Set(header.Number), BlockNumber: new(big.Int).Set(header.Number),
Data: ev.Data, Data: ev.Data,
OutputRootProof: bindings.WithdrawalVerifierOutputRootProof{ OutputRootProof: bindings.HashingOutputRootProof{
Version: [32]byte{}, // Empty for version 1 Version: [32]byte{}, // Empty for version 1
StateRoot: header.Root, StateRoot: header.Root,
WithdrawerStorageRoot: p.StorageHash, WithdrawerStorageRoot: p.StorageHash,
......
...@@ -8,7 +8,6 @@ GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (g ...@@ -8,7 +8,6 @@ GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (g
GasBenchMark_L2OutputOracle:test_appendL2Output_benchmark() (gas: 68673) GasBenchMark_L2OutputOracle:test_appendL2Output_benchmark() (gas: 68673)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 75069) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 75069)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 35373) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 35373)
CrossDomainHashing_Test:test_l2TransactionHash() (gas: 104131)
DeployerWhitelist_Test:test_owner() (gas: 7658) DeployerWhitelist_Test:test_owner() (gas: 7658)
DeployerWhitelist_Test:test_storageSlots() (gas: 33494) DeployerWhitelist_Test:test_storageSlots() (gas: 33494)
GasPriceOracle_Test:test_baseFee() (gas: 8370) GasPriceOracle_Test:test_baseFee() (gas: 8370)
...@@ -24,6 +23,7 @@ GasPriceOracle_Test:test_setL1BaseFeeReverts() (gas: 11717) ...@@ -24,6 +23,7 @@ GasPriceOracle_Test:test_setL1BaseFeeReverts() (gas: 11717)
GasPriceOracle_Test:test_setOverhead() (gas: 36767) GasPriceOracle_Test:test_setOverhead() (gas: 36767)
GasPriceOracle_Test:test_setScalar() (gas: 36818) GasPriceOracle_Test:test_setScalar() (gas: 36818)
GasPriceOracle_Test:test_storageLayout() (gas: 86683) GasPriceOracle_Test:test_storageLayout() (gas: 86683)
Hashing_Test:test_l2TransactionHash() (gas: 104047)
L1BlockTest:test_basefee() (gas: 7531) L1BlockTest:test_basefee() (gas: 7531)
L1BlockTest:test_hash() (gas: 7575) L1BlockTest:test_hash() (gas: 7575)
L1BlockTest:test_number() (gas: 7630) L1BlockTest:test_number() (gas: 7630)
......
...@@ -4,7 +4,8 @@ pragma solidity 0.8.10; ...@@ -4,7 +4,8 @@ pragma solidity 0.8.10;
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import { ExcessivelySafeCall } from "excessively-safe-call/src/ExcessivelySafeCall.sol"; import { ExcessivelySafeCall } from "excessively-safe-call/src/ExcessivelySafeCall.sol";
import { L2OutputOracle } from "./L2OutputOracle.sol"; import { L2OutputOracle } from "./L2OutputOracle.sol";
import { WithdrawalVerifier } from "../libraries/WithdrawalVerifier.sol"; import { Hashing } from "../libraries/Hashing.sol";
import { SecureMerkleTrie } from "../libraries/trie/SecureMerkleTrie.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol"; import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
import { ResourceMetering } from "./ResourceMetering.sol"; import { ResourceMetering } from "./ResourceMetering.sol";
import { Semver } from "../universal/Semver.sol"; import { Semver } from "../universal/Semver.sol";
...@@ -211,7 +212,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -211,7 +212,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
uint256 _gasLimit, uint256 _gasLimit,
bytes calldata _data, bytes calldata _data,
uint256 _l2BlockNumber, uint256 _l2BlockNumber,
WithdrawalVerifier.OutputRootProof calldata _outputRootProof, Hashing.OutputRootProof calldata _outputRootProof,
bytes calldata _withdrawalProof bytes calldata _withdrawalProof
) external payable { ) external payable {
// Prevent nested withdrawals within withdrawals. // Prevent nested withdrawals within withdrawals.
...@@ -241,13 +242,13 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -241,13 +242,13 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
// Verify that the output root can be generated with the elements in the proof. // Verify that the output root can be generated with the elements in the proof.
require( require(
proposal.outputRoot == WithdrawalVerifier._deriveOutputRoot(_outputRootProof), proposal.outputRoot == Hashing._deriveOutputRoot(_outputRootProof),
"OptimismPortal: invalid output root proof" "OptimismPortal: invalid output root proof"
); );
// All withdrawals have a unique hash, we'll use this as the identifier for the withdrawal // All withdrawals have a unique hash, we'll use this as the identifier for the withdrawal
// and to prevent replay attacks. // and to prevent replay attacks.
bytes32 withdrawalHash = WithdrawalVerifier.withdrawalHash( bytes32 withdrawalHash = Hashing.withdrawalHash(
_nonce, _nonce,
_sender, _sender,
_target, _target,
...@@ -260,7 +261,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -260,7 +261,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
// this is true, then we know that this withdrawal was actually triggered on L2 can can // this is true, then we know that this withdrawal was actually triggered on L2 can can
// therefore be relayed on L1. // therefore be relayed on L1.
require( require(
WithdrawalVerifier._verifyWithdrawalInclusion( _verifyWithdrawalInclusion(
withdrawalHash, withdrawalHash,
_outputRootProof.withdrawerStorageRoot, _outputRootProof.withdrawerStorageRoot,
_withdrawalProof _withdrawalProof
...@@ -306,4 +307,33 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -306,4 +307,33 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
// be achieved through contracts built on top of this contract // be achieved through contracts built on top of this contract
emit WithdrawalFinalized(withdrawalHash, success); emit WithdrawalFinalized(withdrawalHash, success);
} }
/**
* @notice Verifies a Merkle Trie inclusion proof that a given withdrawal hash is present in
* the storage of the L2ToL1MessagePasser contract.
*
* @param _withdrawalHash Hash of the withdrawal to verify.
* @param _storageRoot Root of the storage of the L2ToL1MessagePasser contract.
* @param _proof Inclusion proof of the withdrawal hash in the storage root.
*/
function _verifyWithdrawalInclusion(
bytes32 _withdrawalHash,
bytes32 _storageRoot,
bytes memory _proof
) internal pure returns (bool) {
bytes32 storageKey = keccak256(
abi.encode(
_withdrawalHash,
uint256(0) // The withdrawals mapping is at the first slot in the layout.
)
);
return
SecureMerkleTrie.verifyInclusionProof(
abi.encode(storageKey),
hex"01",
_proof,
_storageRoot
);
}
} }
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.9; pragma solidity ^0.8.9;
import { Hashing } from "../libraries/Hashing.sol";
import { Burn } from "../libraries/Burn.sol"; import { Burn } from "../libraries/Burn.sol";
import { Semver } from "../universal/Semver.sol"; import { Semver } from "../universal/Semver.sol";
import { WithdrawalVerifier } from "../libraries/WithdrawalVerifier.sol";
/** /**
* @custom:proxied * @custom:proxied
...@@ -79,7 +79,7 @@ contract L2ToL1MessagePasser is Semver { ...@@ -79,7 +79,7 @@ contract L2ToL1MessagePasser is Semver {
uint256 _gasLimit, uint256 _gasLimit,
bytes memory _data bytes memory _data
) public payable { ) public payable {
bytes32 withdrawalHash = WithdrawalVerifier.withdrawalHash( bytes32 withdrawalHash = Hashing.withdrawalHash(
nonce, nonce,
msg.sender, msg.sender,
_target, _target,
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
/**
* @title CrossDomainUtils
*/
library CrossDomainUtils {
/**
* Generates the correct cross domain calldata for a message.
* @param _target Target contract address.
* @param _sender Message sender address.
* @param _message Message to send to the target.
* @param _messageNonce Nonce for the provided message.
* @return ABI encoded cross domain calldata.
*/
function encodeXDomainCalldata(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce
) internal pure returns (bytes memory) {
return
abi.encodeWithSignature(
"relayMessage(address,address,bytes,uint256)",
_target,
_sender,
_message,
_messageNonce
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { Hashing } from "./Hashing.sol";
import { RLPWriter } from "./rlp/RLPWriter.sol";
/**
* @title Encoding
* @notice Encoding handles Optimism's various different encoding schemes.
*/
library Encoding {
/**
* Generates the correct cross domain calldata for a message.
* @param _target Target contract address.
* @param _sender Message sender address.
* @param _message Message to send to the target.
* @param _messageNonce Nonce for the provided message.
* @return ABI encoded cross domain calldata.
*/
function encodeXDomainCalldata(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce
) internal pure returns (bytes memory) {
return
abi.encodeWithSignature(
"relayMessage(address,address,bytes,uint256)",
_target,
_sender,
_message,
_messageNonce
);
}
/**
* @notice RLP encode a deposit transaction
* This only works for user deposits, not system deposits
* TODO: better name + rearrange the input param ordering?
*/
function L2Transaction(
bytes32 _l1BlockHash,
uint256 _logIndex,
address _from,
address _to,
bool _isCreate,
uint256 _mint,
uint256 _value,
uint256 _gas,
bytes memory _data
) internal pure returns (bytes memory) {
bytes32 source = Hashing.sourceHash(_l1BlockHash, _logIndex);
bytes[] memory raw = new bytes[](7);
raw[0] = RLPWriter.writeBytes(abi.encodePacked(source));
raw[1] = RLPWriter.writeAddress(_from);
if (_isCreate == true) {
require(_to == address(0));
raw[2] = RLPWriter.writeBytes("");
} else {
raw[2] = RLPWriter.writeAddress(_to);
}
raw[3] = RLPWriter.writeUint(_mint);
raw[4] = RLPWriter.writeUint(_value);
raw[5] = RLPWriter.writeUint(_gas);
raw[6] = RLPWriter.writeBytes(_data);
bytes memory encoded = RLPWriter.writeList(raw);
return abi.encodePacked(uint8(0x7e), uint8(0x0), encoded);
}
/**
* @notice Encodes the cross domain message based on the version that
* is encoded in the nonce
*/
function getVersionedEncoding(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
) internal pure returns (bytes memory) {
uint16 version = getVersionFromNonce(_nonce);
if (version == 0) {
return getEncodingV0(_target, _sender, _data, _nonce);
} else if (version == 1) {
return getEncodingV1(_nonce, _sender, _target, _value, _gasLimit, _data);
}
revert("Unknown version.");
}
/**
* @notice Compute the legacy cross domain serialization
*/
function getEncodingV0(
address _target,
address _sender,
bytes memory _data,
uint256 _nonce
) internal pure returns (bytes memory) {
return encodeXDomainCalldata(_target, _sender, _data, _nonce);
}
/**
* @notice Compute the V1 cross domain serialization
*/
function getEncodingV1(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
) internal pure returns (bytes memory) {
return
abi.encodeWithSignature(
"relayMessage(uint256,address,address,uint256,uint256,bytes)",
_nonce,
_sender,
_target,
_value,
_gasLimit,
_data
);
}
/**
* @notice Adds the version to the nonce
*/
function addVersionToNonce(uint256 _nonce, uint16 _version)
internal
pure
returns (uint256 nonce)
{
assembly {
nonce := or(shl(240, _version), _nonce)
}
}
/**
* @notice Gets the version out of the nonce
*/
function getVersionFromNonce(uint256 _nonce) internal pure returns (uint16 version) {
assembly {
version := shr(240, _nonce)
}
}
}
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.10; pragma solidity 0.8.10;
import { CrossDomainUtils } from "./CrossDomainUtils.sol"; import { Encoding } from "./Encoding.sol";
import { RLPWriter } from "./rlp/RLPWriter.sol";
/** /**
* @title CrossDomainHashing * @title Hashing
* This library is responsible for holding cross domain utility * @notice Hashing handles Optimism's various different hashing schemes.
* functions.
* TODO(tynes): merge with CrossDomainUtils
* TODO(tynes): fill out more devdocs
*/ */
library CrossDomainHashing { library Hashing {
/// @notice A struct containing the elements hashed together to generate the output root.
struct OutputRootProof {
bytes32 version;
bytes32 stateRoot;
bytes32 withdrawerStorageRoot;
bytes32 latestBlockhash;
}
/** /**
* @notice Compute the L2 transaction hash given * @notice Compute the L2 transaction hash given
* data about an L1 deposit transaction. This is useful for * data about an L1 deposit transaction. This is useful for
...@@ -41,7 +45,7 @@ library CrossDomainHashing { ...@@ -41,7 +45,7 @@ library CrossDomainHashing {
uint256 _gas, uint256 _gas,
bytes memory _data bytes memory _data
) internal pure returns (bytes32) { ) internal pure returns (bytes32) {
bytes memory raw = L2Transaction( bytes memory raw = Encoding.L2Transaction(
_l1BlockHash, _l1BlockHash,
_logIndex, _logIndex,
_from, _from,
...@@ -69,100 +73,6 @@ library CrossDomainHashing { ...@@ -69,100 +73,6 @@ library CrossDomainHashing {
return keccak256(abi.encode(bytes32(0), depositId)); return keccak256(abi.encode(bytes32(0), depositId));
} }
/**
* @notice RLP encode a deposit transaction
* This only works for user deposits, not system deposits
* TODO: better name + rearrange the input param ordering?
*/
function L2Transaction(
bytes32 _l1BlockHash,
uint256 _logIndex,
address _from,
address _to,
bool _isCreate,
uint256 _mint,
uint256 _value,
uint256 _gas,
bytes memory _data
) internal pure returns (bytes memory) {
bytes32 source = sourceHash(_l1BlockHash, _logIndex);
bytes[] memory raw = new bytes[](7);
raw[0] = RLPWriter.writeBytes(bytes32ToBytes(source));
raw[1] = RLPWriter.writeAddress(_from);
if (_isCreate == true) {
require(_to == address(0));
raw[2] = RLPWriter.writeBytes("");
} else {
raw[2] = RLPWriter.writeAddress(_to);
}
raw[3] = RLPWriter.writeUint(_mint);
raw[4] = RLPWriter.writeUint(_value);
raw[5] = RLPWriter.writeUint(_gas);
raw[6] = RLPWriter.writeBytes(_data);
bytes memory encoded = RLPWriter.writeList(raw);
return abi.encodePacked(uint8(0x7e), uint8(0x0), encoded);
}
/**
* @notice Helper function to turn bytes32 into bytes
*/
function bytes32ToBytes(bytes32 input) internal pure returns (bytes memory) {
bytes memory b = new bytes(32);
assembly {
mstore(add(b, 32), input) // set the bytes data
}
return b;
}
/**
* @notice Adds the version to the nonce
*/
function addVersionToNonce(uint256 _nonce, uint16 _version)
internal
pure
returns (uint256 nonce)
{
assembly {
nonce := or(shl(240, _version), _nonce)
}
}
/**
* @notice Gets the version out of the nonce
*/
function getVersionFromNonce(uint256 _nonce) internal pure returns (uint16 version) {
assembly {
version := shr(240, _nonce)
}
}
/**
* @notice Encodes the cross domain message based on the version that
* is encoded in the nonce
*/
function getVersionedEncoding(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _gasLimit,
bytes memory _data
) internal pure returns (bytes memory) {
uint16 version = getVersionFromNonce(_nonce);
if (version == 0) {
return getEncodingV0(_target, _sender, _data, _nonce);
} else if (version == 1) {
return getEncodingV1(_nonce, _sender, _target, _value, _gasLimit, _data);
}
revert("Unknown version.");
}
/** /**
* @notice Compute the cross domain hash based on the versioned nonce * @notice Compute the cross domain hash based on the versioned nonce
*/ */
...@@ -174,7 +84,7 @@ library CrossDomainHashing { ...@@ -174,7 +84,7 @@ library CrossDomainHashing {
uint256 _gasLimit, uint256 _gasLimit,
bytes memory _data bytes memory _data
) internal pure returns (bytes32) { ) internal pure returns (bytes32) {
uint16 version = getVersionFromNonce(_nonce); uint16 version = Encoding.getVersionFromNonce(_nonce);
if (version == 0) { if (version == 0) {
return getHashV0(_target, _sender, _data, _nonce); return getHashV0(_target, _sender, _data, _nonce);
} else if (version == 1) { } else if (version == 1) {
...@@ -185,56 +95,42 @@ library CrossDomainHashing { ...@@ -185,56 +95,42 @@ library CrossDomainHashing {
} }
/** /**
* @notice Compute the legacy cross domain serialization * @notice Compute the legacy hash of a cross domain message
*/ */
function getEncodingV0( function getHashV0(
address _target, address _target,
address _sender, address _sender,
bytes memory _data, bytes memory _data,
uint256 _nonce uint256 _nonce
) internal pure returns (bytes memory) { ) internal pure returns (bytes32) {
return CrossDomainUtils.encodeXDomainCalldata(_target, _sender, _data, _nonce); return keccak256(Encoding.getEncodingV0(_target, _sender, _data, _nonce));
} }
/** /**
* @notice Compute the V1 cross domain serialization * @notice Compute the V1 hash of a cross domain message
*/ */
function getEncodingV1( function getHashV1(
uint256 _nonce, uint256 _nonce,
address _sender, address _sender,
address _target, address _target,
uint256 _value, uint256 _value,
uint256 _gasLimit, uint256 _gasLimit,
bytes memory _data bytes memory _data
) internal pure returns (bytes memory) {
return
abi.encodeWithSignature(
"relayMessage(uint256,address,address,uint256,uint256,bytes)",
_nonce,
_sender,
_target,
_value,
_gasLimit,
_data
);
}
/**
* @notice Compute the legacy hash of a cross domain message
*/
function getHashV0(
address _target,
address _sender,
bytes memory _data,
uint256 _nonce
) internal pure returns (bytes32) { ) internal pure returns (bytes32) {
return keccak256(getEncodingV0(_target, _sender, _data, _nonce)); return
keccak256(Encoding.getEncodingV1(_nonce, _sender, _target, _value, _gasLimit, _data));
} }
/** /**
* @notice Compute the V1 hash of a cross domain message * @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.
*/ */
function getHashV1( function withdrawalHash(
uint256 _nonce, uint256 _nonce,
address _sender, address _sender,
address _target, address _target,
...@@ -242,6 +138,27 @@ library CrossDomainHashing { ...@@ -242,6 +138,27 @@ library CrossDomainHashing {
uint256 _gasLimit, uint256 _gasLimit,
bytes memory _data bytes memory _data
) internal pure returns (bytes32) { ) internal pure returns (bytes32) {
return keccak256(getEncodingV1(_nonce, _sender, _target, _value, _gasLimit, _data)); return keccak256(abi.encode(_nonce, _sender, _target, _value, _gasLimit, _data));
}
/**
* @notice Derives the output root corresponding to the elements provided in the proof.
* @param _outputRootProof The elements which were hashed together to generate the output root.
* @return Whether or not the output root matches the hashed output of the proof.
*/
function _deriveOutputRoot(OutputRootProof memory _outputRootProof)
internal
pure
returns (bytes32)
{
return
keccak256(
abi.encode(
_outputRootProof.version,
_outputRootProof.stateRoot,
_outputRootProof.withdrawerStorageRoot,
_outputRootProof.latestBlockhash
)
);
} }
} }
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
/* Library Imports */
import { SecureMerkleTrie } from "./trie/SecureMerkleTrie.sol";
import { CrossDomainUtils } from "./CrossDomainUtils.sol";
/**
* @title WithdrawalVerifier
* @notice A library with helper functions for verifying a withdrawal on L1.
*/
library WithdrawalVerifier {
/// @notice A struct containing the elements hashed together to generate the output root.
struct OutputRootProof {
bytes32 version;
bytes32 stateRoot;
bytes32 withdrawerStorageRoot;
bytes32 latestBlockhash;
}
/**
* @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.
*/
function withdrawalHash(
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));
}
/**
* @notice Derives the output root corresponding to the elements provided in the proof.
* @param _outputRootProof The elements which were hashed together to generate the output root.
* @return Whether or not the output root matches the hashed output of the proof.
*/
function _deriveOutputRoot(OutputRootProof memory _outputRootProof)
internal
pure
returns (bytes32)
{
return
keccak256(
abi.encode(
_outputRootProof.version,
_outputRootProof.stateRoot,
_outputRootProof.withdrawerStorageRoot,
_outputRootProof.latestBlockhash
)
);
}
/**
* @notice Verifies a proof that a given withdrawal hash is present in the Withdrawer contract's
* withdrawals mapping.
* @param _withdrawalHash Keccak256 hash of the withdrawal transaction data.
* @param _withdrawerStorageRoot Storage root of the withdrawer predeploy contract.
* @param _withdrawalProof Merkle trie inclusion proof for the desired node.
* @return Whether or not the inclusion proof was successful.
*/
function _verifyWithdrawalInclusion(
bytes32 _withdrawalHash,
bytes32 _withdrawerStorageRoot,
bytes memory _withdrawalProof
) internal pure returns (bool) {
bytes32 storageKey = keccak256(
abi.encode(
_withdrawalHash,
uint256(0) // The withdrawals mapping is at the first slot in the layout.
)
);
return
SecureMerkleTrie.verifyInclusionProof(
abi.encode(storageKey),
hex"01",
_withdrawalProof,
_withdrawerStorageRoot
);
}
}
//SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import { CommonTest } from "./CommonTest.t.sol";
import { Encoding } from "../libraries/Encoding.sol";
contract Encoding_Test is CommonTest {
function test_nonceVersioning(uint240 _nonce, uint16 _version) external {
uint256 nonce = Encoding.addVersionToNonce(uint256(_nonce), _version);
uint16 version = Encoding.getVersionFromNonce(nonce);
assertEq(version, _version);
}
}
...@@ -2,15 +2,10 @@ ...@@ -2,15 +2,10 @@
pragma solidity 0.8.10; pragma solidity 0.8.10;
import { CommonTest } from "./CommonTest.t.sol"; import { CommonTest } from "./CommonTest.t.sol";
import { CrossDomainHashing } from "../libraries/CrossDomainHashing.sol"; import { Hashing } from "../libraries/Hashing.sol";
import { Encoding } from "../libraries/Encoding.sol";
contract CrossDomainHashing_Test is CommonTest {
function test_nonceVersioning(uint240 _nonce, uint16 _version) external {
uint256 nonce = CrossDomainHashing.addVersionToNonce(uint256(_nonce), _version);
uint16 version = CrossDomainHashing.getVersionFromNonce(nonce);
assertEq(version, _version);
}
contract Hashing_Test is CommonTest {
// TODO(tynes): turn this into differential fuzzing // TODO(tynes): turn this into differential fuzzing
// it is very easy to do so with the typescript // it is very easy to do so with the typescript
function test_l2TransactionHash() external { function test_l2TransactionHash() external {
...@@ -24,7 +19,7 @@ contract CrossDomainHashing_Test is CommonTest { ...@@ -24,7 +19,7 @@ contract CrossDomainHashing_Test is CommonTest {
uint256 gas = 0x2dc6c0; uint256 gas = 0x2dc6c0;
bytes memory data = hex""; bytes memory data = hex"";
bytes32 sourceHash = CrossDomainHashing.sourceHash( bytes32 sourceHash = Hashing.sourceHash(
l1BlockHash, l1BlockHash,
logIndex logIndex
); );
...@@ -34,7 +29,7 @@ contract CrossDomainHashing_Test is CommonTest { ...@@ -34,7 +29,7 @@ contract CrossDomainHashing_Test is CommonTest {
0xf923fb07134d7d287cb52c770cc619e17e82606c21a875c92f4c63b65280a5cc 0xf923fb07134d7d287cb52c770cc619e17e82606c21a875c92f4c63b65280a5cc
); );
bytes memory raw = CrossDomainHashing.L2Transaction( bytes memory raw = Encoding.L2Transaction(
l1BlockHash, l1BlockHash,
logIndex, logIndex,
from, from,
...@@ -51,7 +46,7 @@ contract CrossDomainHashing_Test is CommonTest { ...@@ -51,7 +46,7 @@ contract CrossDomainHashing_Test is CommonTest {
hex"7e00f862a0f923fb07134d7d287cb52c770cc619e17e82606c21a875c92f4c63b65280a5cc94f39fd6e51aad88f6f4ce6ab8827279cfffb9226694b79f76ef2c5f0286176833e7b2eee103b1cc3244880e043da617250000880de0b6b3a7640000832dc6c080" hex"7e00f862a0f923fb07134d7d287cb52c770cc619e17e82606c21a875c92f4c63b65280a5cc94f39fd6e51aad88f6f4ce6ab8827279cfffb9226694b79f76ef2c5f0286176833e7b2eee103b1cc3244880e043da617250000880de0b6b3a7640000832dc6c080"
); );
bytes32 digest = CrossDomainHashing.L2TransactionHash( bytes32 digest = Hashing.L2TransactionHash(
l1BlockHash, l1BlockHash,
logIndex, logIndex,
from, from,
......
...@@ -8,15 +8,13 @@ import { L2OutputOracle_Initializer } from "./L2OutputOracle.t.sol"; ...@@ -8,15 +8,13 @@ import { L2OutputOracle_Initializer } from "./L2OutputOracle.t.sol";
/* Libraries */ /* Libraries */
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol"; import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
import { PredeployAddresses } from "../libraries/PredeployAddresses.sol"; import { PredeployAddresses } from "../libraries/PredeployAddresses.sol";
import { CrossDomainUtils } from "../libraries/CrossDomainUtils.sol"; import { Hashing } from "../libraries/Hashing.sol";
import { WithdrawalVerifier } from "../libraries/WithdrawalVerifier.sol"; import { Encoding } from "../libraries/Encoding.sol";
/* Target contract dependencies */ /* Target contract dependencies */
import { L2OutputOracle } from "../L1/L2OutputOracle.sol"; import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { OptimismPortal } from "../L1/OptimismPortal.sol"; import { OptimismPortal } from "../L1/OptimismPortal.sol";
import { CrossDomainHashing } from "../libraries/CrossDomainHashing.sol";
/* Target contract */ /* Target contract */
import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol"; import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol";
...@@ -59,7 +57,7 @@ contract L1CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -59,7 +57,7 @@ contract L1CrossDomainMessenger_Test is Messenger_Initializer {
// the version is encoded in the nonce // the version is encoded in the nonce
function test_L1MessengerMessageVersion() external { function test_L1MessengerMessageVersion() external {
assertEq( assertEq(
CrossDomainHashing.getVersionFromNonce(L1Messenger.messageNonce()), Encoding.getVersionFromNonce(L1Messenger.messageNonce()),
L1Messenger.MESSAGE_VERSION() L1Messenger.MESSAGE_VERSION()
); );
} }
...@@ -77,7 +75,7 @@ contract L1CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -77,7 +75,7 @@ contract L1CrossDomainMessenger_Test is Messenger_Initializer {
0, 0,
100 + L1Messenger.baseGas(hex"ff"), 100 + L1Messenger.baseGas(hex"ff"),
false, false,
CrossDomainHashing.getVersionedEncoding( Encoding.getVersionedEncoding(
L1Messenger.messageNonce(), L1Messenger.messageNonce(),
alice, alice,
recipient, recipient,
...@@ -97,7 +95,7 @@ contract L1CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -97,7 +95,7 @@ contract L1CrossDomainMessenger_Test is Messenger_Initializer {
0, 0,
100 + L1Messenger.baseGas(hex"ff"), 100 + L1Messenger.baseGas(hex"ff"),
false, false,
CrossDomainHashing.getVersionedEncoding( Encoding.getVersionedEncoding(
L1Messenger.messageNonce(), L1Messenger.messageNonce(),
alice, alice,
recipient, recipient,
...@@ -147,7 +145,7 @@ contract L1CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -147,7 +145,7 @@ contract L1CrossDomainMessenger_Test is Messenger_Initializer {
vm.expectEmit(true, true, true, true); vm.expectEmit(true, true, true, true);
bytes32 hash = CrossDomainHashing.getVersionedHash(0, sender, target, 0, 0, hex"1111"); bytes32 hash = Hashing.getVersionedHash(0, sender, target, 0, 0, hex"1111");
emit RelayedMessage(hash); emit RelayedMessage(hash);
......
...@@ -3,13 +3,13 @@ pragma solidity 0.8.10; ...@@ -3,13 +3,13 @@ pragma solidity 0.8.10;
import { Messenger_Initializer } from "./CommonTest.t.sol"; import { Messenger_Initializer } from "./CommonTest.t.sol";
import { CrossDomainUtils } from "../libraries/CrossDomainUtils.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol"; import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol"; import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol"; import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { L2CrossDomainMessenger } from "../L2/L2CrossDomainMessenger.sol"; import { L2CrossDomainMessenger } from "../L2/L2CrossDomainMessenger.sol";
import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol"; import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol";
import { CrossDomainHashing } from "../libraries/CrossDomainHashing.sol"; import { Hashing } from "../libraries/Hashing.sol";
import { Encoding } from "../libraries/Encoding.sol";
contract L2CrossDomainMessenger_Test is Messenger_Initializer { contract L2CrossDomainMessenger_Test is Messenger_Initializer {
// Receiver address for testing // Receiver address for testing
...@@ -32,7 +32,7 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -32,7 +32,7 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer {
function test_L2MessengerMessageVersion() external { function test_L2MessengerMessageVersion() external {
assertEq( assertEq(
CrossDomainHashing.getVersionFromNonce(L2Messenger.messageNonce()), Encoding.getVersionFromNonce(L2Messenger.messageNonce()),
L2Messenger.MESSAGE_VERSION() L2Messenger.MESSAGE_VERSION()
); );
} }
...@@ -44,7 +44,7 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -44,7 +44,7 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer {
L2ToL1MessagePasser.initiateWithdrawal.selector, L2ToL1MessagePasser.initiateWithdrawal.selector,
address(L1Messenger), address(L1Messenger),
100 + L2Messenger.baseGas(hex"ff"), 100 + L2Messenger.baseGas(hex"ff"),
CrossDomainHashing.getVersionedEncoding( Encoding.getVersionedEncoding(
L2Messenger.messageNonce(), L2Messenger.messageNonce(),
alice, alice,
recipient, recipient,
...@@ -63,7 +63,7 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -63,7 +63,7 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer {
address(L1Messenger), address(L1Messenger),
0, 0,
100 + L2Messenger.baseGas(hex"ff"), 100 + L2Messenger.baseGas(hex"ff"),
CrossDomainHashing.getVersionedEncoding( Encoding.getVersionedEncoding(
L2Messenger.messageNonce(), L2Messenger.messageNonce(),
alice, alice,
recipient, recipient,
...@@ -104,7 +104,7 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -104,7 +104,7 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer {
vm.expectEmit(true, true, true, true); vm.expectEmit(true, true, true, true);
bytes32 hash = CrossDomainHashing.getVersionedHash( bytes32 hash = Hashing.getVersionedHash(
0, 0,
sender, sender,
target, target,
......
...@@ -3,7 +3,7 @@ pragma solidity 0.8.10; ...@@ -3,7 +3,7 @@ pragma solidity 0.8.10;
import { CommonTest } from "./CommonTest.t.sol"; import { CommonTest } from "./CommonTest.t.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol"; import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
import { WithdrawalVerifier } from "../libraries/WithdrawalVerifier.sol"; import { Hashing } from "../libraries/Hashing.sol";
contract L2ToL1MessagePasserTest is CommonTest { contract L2ToL1MessagePasserTest is CommonTest {
L2ToL1MessagePasser messagePasser; L2ToL1MessagePasser messagePasser;
...@@ -64,7 +64,7 @@ contract L2ToL1MessagePasserTest is CommonTest { ...@@ -64,7 +64,7 @@ contract L2ToL1MessagePasserTest is CommonTest {
data data
); );
bytes32 withdrawalHash = WithdrawalVerifier.withdrawalHash( bytes32 withdrawalHash = Hashing.withdrawalHash(
nonce, nonce,
alice, alice,
target, target,
......
...@@ -6,7 +6,7 @@ import { Portal_Initializer, CommonTest, NextImpl } from "./CommonTest.t.sol"; ...@@ -6,7 +6,7 @@ import { Portal_Initializer, CommonTest, NextImpl } from "./CommonTest.t.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol"; import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol"; import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { OptimismPortal } from "../L1/OptimismPortal.sol"; import { OptimismPortal } from "../L1/OptimismPortal.sol";
import { WithdrawalVerifier } from "../libraries/WithdrawalVerifier.sol"; import { Hashing } from "../libraries/Hashing.sol";
import { Proxy } from "../universal/Proxy.sol"; import { Proxy } from "../universal/Proxy.sol";
contract OptimismPortal_Test is Portal_Initializer { contract OptimismPortal_Test is Portal_Initializer {
...@@ -230,7 +230,7 @@ contract OptimismPortal_Test is Portal_Initializer { ...@@ -230,7 +230,7 @@ contract OptimismPortal_Test is Portal_Initializer {
// function test_verifyWithdrawal() external {} // function test_verifyWithdrawal() external {}
function test_cannotVerifyRecentWithdrawal() external { function test_cannotVerifyRecentWithdrawal() external {
WithdrawalVerifier.OutputRootProof memory outputRootProof = WithdrawalVerifier Hashing.OutputRootProof memory outputRootProof = Hashing
.OutputRootProof({ .OutputRootProof({
version: bytes32(0), version: bytes32(0),
stateRoot: bytes32(0), stateRoot: bytes32(0),
...@@ -243,7 +243,7 @@ contract OptimismPortal_Test is Portal_Initializer { ...@@ -243,7 +243,7 @@ contract OptimismPortal_Test is Portal_Initializer {
} }
function test_invalidWithdrawalProof() external { function test_invalidWithdrawalProof() external {
WithdrawalVerifier.OutputRootProof memory outputRootProof = WithdrawalVerifier Hashing.OutputRootProof memory outputRootProof = Hashing
.OutputRootProof({ .OutputRootProof({
version: bytes32(0), version: bytes32(0),
stateRoot: bytes32(0), stateRoot: bytes32(0),
......
...@@ -11,7 +11,8 @@ import { ...@@ -11,7 +11,8 @@ import {
ReentrancyGuardUpgradeable ReentrancyGuardUpgradeable
} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import { ExcessivelySafeCall } from "excessively-safe-call/src/ExcessivelySafeCall.sol"; import { ExcessivelySafeCall } from "excessively-safe-call/src/ExcessivelySafeCall.sol";
import { CrossDomainHashing } from "../libraries/CrossDomainHashing.sol"; import { Hashing } from "../libraries/Hashing.sol";
import { Encoding } from "../libraries/Encoding.sol";
/** /**
* @title CrossDomainMessenger * @title CrossDomainMessenger
...@@ -171,7 +172,7 @@ abstract contract CrossDomainMessenger is ...@@ -171,7 +172,7 @@ abstract contract CrossDomainMessenger is
* @return Nonce of the next message to be sent, with added message version. * @return Nonce of the next message to be sent, with added message version.
*/ */
function messageNonce() public view returns (uint256) { function messageNonce() public view returns (uint256) {
return CrossDomainHashing.addVersionToNonce(msgNonce, MESSAGE_VERSION); return Encoding.addVersionToNonce(msgNonce, MESSAGE_VERSION);
} }
/** /**
...@@ -249,7 +250,7 @@ abstract contract CrossDomainMessenger is ...@@ -249,7 +250,7 @@ abstract contract CrossDomainMessenger is
uint256 _minGasLimit, uint256 _minGasLimit,
bytes calldata _message bytes calldata _message
) external payable nonReentrant whenNotPaused { ) external payable nonReentrant whenNotPaused {
bytes32 versionedHash = CrossDomainHashing.getVersionedHash( bytes32 versionedHash = Hashing.getVersionedHash(
_nonce, _nonce,
_sender, _sender,
_target, _target,
......
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