Commit 200a083e authored by Elena Gesheva's avatar Elena Gesheva Committed by Kelvin Fichter

Reduce gas costs of deposits (#667)

* Remove messageNonce from BaseCrossDomainMessenger and use CTC queue lenght instead

Remove Abs_BaseCrossDomainMessenger and restore dedicated nonce generation in OVM_L2CrossDomainMessenger

Fix typo

* Remove sentMessages mapping from L1CrossDomainMessenger storage
and use the nonce to check for existence of replayed transaction

* Refactor out common library function for getting cross domain calldata

* Post rebase fixes

* Use the queueIndex to check the transaction was enqueued

* Fix tests for L1CrossDomainMessenger.replayMessage
Also make that test work with an actual CanonicalTransactionChain implementation rather than a smock

* Lint fixes

* Optimise the resolve calls into the AddressManager lib

* Rename the nonce parameter to be clear

* Update test name
Co-authored-by: default avatarben-chain <ben@pseudonym.party>

* Rename getXDomainCalldata to encodeXDomainCalldata to match the new Lib_CrossDomainUtils
Co-authored-by: default avatarben-chain <ben@pseudonym.party>
parent 2e72fd90
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iAbs_BaseCrossDomainMessenger } from "../../../iOVM/bridge/messaging/iAbs_BaseCrossDomainMessenger.sol";
/**
* @title Abs_BaseCrossDomainMessenger
* @dev The Base Cross Domain Messenger is an abstract contract providing the interface and common
* functionality used in the L1 and L2 Cross Domain Messengers. It can also serve as a template for
* developers wishing to implement a custom bridge contract to suit their needs.
*
* Compiler used: defined by child contract
* Runtime target: defined by child contract
*/
abstract contract Abs_BaseCrossDomainMessenger is iAbs_BaseCrossDomainMessenger {
/*************
* Constants *
*************/
// The default x-domain message sender being set to a non-zero value makes
// deployment a bit more expensive, but in exchange the refund on every call to
// `relayMessage` by the L1 and L2 messengers will be higher.
address internal constant DEFAULT_XDOMAIN_SENDER = 0x000000000000000000000000000000000000dEaD;
/*************
* Variables *
*************/
mapping (bytes32 => bool) public relayedMessages;
mapping (bytes32 => bool) public successfulMessages;
mapping (bytes32 => bool) public sentMessages;
uint256 public messageNonce;
address internal xDomainMsgSender = DEFAULT_XDOMAIN_SENDER;
/***************
* Constructor *
***************/
constructor() {}
/********************
* Public Functions *
********************/
function xDomainMessageSender()
public
override
view
returns (
address
)
{
require(xDomainMsgSender != DEFAULT_XDOMAIN_SENDER, "xDomainMessageSender is not set");
return xDomainMsgSender;
}
/**
* Sends a cross domain message to the target messenger.
* @param _target Target contract address.
* @param _message Message to send to the target.
* @param _gasLimit Gas limit for the provided message.
*/
function sendMessage(
address _target,
bytes memory _message,
uint32 _gasLimit
)
override
public
{
bytes memory xDomainCalldata = _getXDomainCalldata(
_target,
msg.sender,
_message,
messageNonce
);
messageNonce += 1;
sentMessages[keccak256(xDomainCalldata)] = true;
_sendXDomainMessage(xDomainCalldata, _gasLimit);
emit SentMessage(xDomainCalldata);
}
/**********************
* Internal Functions *
**********************/
/**
* Generates the correct cross domain calldata for a message.
* @param _target Target contract address.
* @param _sender Message sender address.
* @param _message Message to send to the target.
* @param _messageNonce Nonce for the provided message.
* @return ABI encoded cross domain calldata.
*/
function _getXDomainCalldata(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce
)
internal
pure
returns (
bytes memory
)
{
return abi.encodeWithSignature(
"relayMessage(address,address,bytes,uint256)",
_target,
_sender,
_message,
_messageNonce
);
}
/**
* Sends a cross domain message.
* param // Message to send.
* param // Gas limit for the provided message.
*/
function _sendXDomainMessage(
bytes memory, // _message,
uint256 // _gasLimit
)
virtual
internal
{
revert("Implement me in child contracts!");
}
}
...@@ -3,19 +3,17 @@ pragma solidity >0.5.0 <0.8.0; ...@@ -3,19 +3,17 @@ pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
/* Library Imports */ /* Library Imports */
import { Lib_OVMCodec } from "../../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../../libraries/resolver/Lib_AddressResolver.sol"; import { Lib_AddressResolver } from "../../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_OVMCodec } from "../../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressManager } from "../../../libraries/resolver/Lib_AddressManager.sol"; import { Lib_AddressManager } from "../../../libraries/resolver/Lib_AddressManager.sol";
import { Lib_SecureMerkleTrie } from "../../../libraries/trie/Lib_SecureMerkleTrie.sol"; import { Lib_SecureMerkleTrie } from "../../../libraries/trie/Lib_SecureMerkleTrie.sol";
import { Lib_CrossDomainUtils } from "../../../libraries/bridge/Lib_CrossDomainUtils.sol";
/* Interface Imports */ /* Interface Imports */
import { iOVM_L1CrossDomainMessenger } from "../../../iOVM/bridge/messaging/iOVM_L1CrossDomainMessenger.sol"; import { iOVM_L1CrossDomainMessenger } from "../../../iOVM/bridge/messaging/iOVM_L1CrossDomainMessenger.sol";
import { iOVM_CanonicalTransactionChain } from "../../../iOVM/chain/iOVM_CanonicalTransactionChain.sol"; import { iOVM_CanonicalTransactionChain } from "../../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
import { iOVM_StateCommitmentChain } from "../../../iOVM/chain/iOVM_StateCommitmentChain.sol"; import { iOVM_StateCommitmentChain } from "../../../iOVM/chain/iOVM_StateCommitmentChain.sol";
/* Contract Imports */
import { Abs_BaseCrossDomainMessenger } from "./Abs_BaseCrossDomainMessenger.sol";
/* External Imports */ /* External Imports */
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
...@@ -32,7 +30,6 @@ import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/ ...@@ -32,7 +30,6 @@ import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/
*/ */
contract OVM_L1CrossDomainMessenger is contract OVM_L1CrossDomainMessenger is
iOVM_L1CrossDomainMessenger, iOVM_L1CrossDomainMessenger,
Abs_BaseCrossDomainMessenger,
Lib_AddressResolver, Lib_AddressResolver,
OwnableUpgradeable, OwnableUpgradeable,
PausableUpgradeable, PausableUpgradeable,
...@@ -51,11 +48,24 @@ contract OVM_L1CrossDomainMessenger is ...@@ -51,11 +48,24 @@ contract OVM_L1CrossDomainMessenger is
bytes32 indexed _xDomainCalldataHash bytes32 indexed _xDomainCalldataHash
); );
/*************
* Constants *
*************/
// The default x-domain message sender being set to a non-zero value makes
// deployment a bit more expensive, but in exchange the refund on every call to
// `relayMessage` by the L1 and L2 messengers will be higher.
address internal constant DEFAULT_XDOMAIN_SENDER = 0x000000000000000000000000000000000000dEaD;
/********************** /**********************
* Contract Variables * * Contract Variables *
**********************/ **********************/
mapping (bytes32 => bool) public blockedMessages; mapping (bytes32 => bool) public blockedMessages;
mapping (bytes32 => bool) public relayedMessages;
mapping (bytes32 => bool) public successfulMessages;
address internal xDomainMsgSender = DEFAULT_XDOMAIN_SENDER;
/*************** /***************
* Constructor * * Constructor *
...@@ -155,6 +165,48 @@ contract OVM_L1CrossDomainMessenger is ...@@ -155,6 +165,48 @@ contract OVM_L1CrossDomainMessenger is
emit MessageAllowed(_xDomainCalldataHash); emit MessageAllowed(_xDomainCalldataHash);
} }
function xDomainMessageSender()
public
override
view
returns (
address
)
{
require(xDomainMsgSender != DEFAULT_XDOMAIN_SENDER, "xDomainMessageSender is not set");
return xDomainMsgSender;
}
/**
* Sends a cross domain message to the target messenger.
* @param _target Target contract address.
* @param _message Message to send to the target.
* @param _gasLimit Gas limit for the provided message.
*/
function sendMessage(
address _target,
bytes memory _message,
uint32 _gasLimit
)
override
public
{
address ovmCanonicalTransactionChain = resolve("OVM_CanonicalTransactionChain");
// Use the CTC queue length as nonce
uint40 nonce = iOVM_CanonicalTransactionChain(ovmCanonicalTransactionChain).getQueueLength();
bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata(
_target,
msg.sender,
_message,
nonce
);
address l2CrossDomainMessenger = resolve("OVM_L2CrossDomainMessenger");
_sendXDomainMessage(ovmCanonicalTransactionChain, l2CrossDomainMessenger, xDomainCalldata, _gasLimit);
emit SentMessage(xDomainCalldata);
}
/** /**
* Relays a cross domain message to a contract. * Relays a cross domain message to a contract.
* @inheritdoc iOVM_L1CrossDomainMessenger * @inheritdoc iOVM_L1CrossDomainMessenger
...@@ -172,7 +224,7 @@ contract OVM_L1CrossDomainMessenger is ...@@ -172,7 +224,7 @@ contract OVM_L1CrossDomainMessenger is
onlyRelayer onlyRelayer
whenNotPaused whenNotPaused
{ {
bytes memory xDomainCalldata = _getXDomainCalldata( bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata(
_target, _target,
_sender, _sender,
_message, _message,
...@@ -232,25 +284,40 @@ contract OVM_L1CrossDomainMessenger is ...@@ -232,25 +284,40 @@ contract OVM_L1CrossDomainMessenger is
address _target, address _target,
address _sender, address _sender,
bytes memory _message, bytes memory _message,
uint256 _messageNonce, uint256 _queueIndex,
uint32 _gasLimit uint32 _gasLimit
) )
override override
public public
{ {
bytes memory xDomainCalldata = _getXDomainCalldata( // Verify that the message is in the queue:
_target, address canonicalTransactionChain = resolve("OVM_CanonicalTransactionChain");
_sender, Lib_OVMCodec.QueueElement memory element = iOVM_CanonicalTransactionChain(canonicalTransactionChain).getQueueElement(_queueIndex);
_message,
_messageNonce address l2CrossDomainMessenger = resolve("OVM_L2CrossDomainMessenger");
// Compute the transactionHash
bytes32 transactionHash = keccak256(
abi.encode(
address(this),
l2CrossDomainMessenger,
_gasLimit,
_message
)
); );
require( require(
sentMessages[keccak256(xDomainCalldata)] == true, transactionHash == element.transactionHash,
"Provided message has not already been sent." "Provided message has not been enqueued."
);
bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata(
_target,
_sender,
_message,
_queueIndex
); );
_sendXDomainMessage(xDomainCalldata, _gasLimit); _sendXDomainMessage(canonicalTransactionChain, l2CrossDomainMessenger, xDomainCalldata, _gasLimit);
} }
...@@ -364,18 +431,21 @@ contract OVM_L1CrossDomainMessenger is ...@@ -364,18 +431,21 @@ contract OVM_L1CrossDomainMessenger is
/** /**
* Sends a cross domain message. * Sends a cross domain message.
* @param _canonicalTransactionChain Address of the OVM_CanonicalTransactionChain instance.
* @param _l2CrossDomainMessenger Address of the OVM_L2CrossDomainMessenger instance.
* @param _message Message to send. * @param _message Message to send.
* @param _gasLimit OVM gas limit for the message. * @param _gasLimit OVM gas limit for the message.
*/ */
function _sendXDomainMessage( function _sendXDomainMessage(
address _canonicalTransactionChain,
address _l2CrossDomainMessenger,
bytes memory _message, bytes memory _message,
uint256 _gasLimit uint256 _gasLimit
) )
override
internal internal
{ {
iOVM_CanonicalTransactionChain(resolve("OVM_CanonicalTransactionChain")).enqueue( iOVM_CanonicalTransactionChain(_canonicalTransactionChain).enqueue(
resolve("OVM_L2CrossDomainMessenger"), _l2CrossDomainMessenger,
_gasLimit, _gasLimit,
_message _message
); );
......
...@@ -4,14 +4,15 @@ pragma experimental ABIEncoderV2; ...@@ -4,14 +4,15 @@ pragma experimental ABIEncoderV2;
/* Library Imports */ /* Library Imports */
import { Lib_AddressResolver } from "../../../libraries/resolver/Lib_AddressResolver.sol"; import { Lib_AddressResolver } from "../../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_CrossDomainUtils } from "../../../libraries/bridge/Lib_CrossDomainUtils.sol";
/* Interface Imports */ /* Interface Imports */
import { iOVM_L2CrossDomainMessenger } from "../../../iOVM/bridge/messaging/iOVM_L2CrossDomainMessenger.sol"; import { iOVM_L2CrossDomainMessenger } from "../../../iOVM/bridge/messaging/iOVM_L2CrossDomainMessenger.sol";
import { iOVM_L1MessageSender } from "../../../iOVM/predeploys/iOVM_L1MessageSender.sol"; import { iOVM_L1MessageSender } from "../../../iOVM/predeploys/iOVM_L1MessageSender.sol";
import { iOVM_L2ToL1MessagePasser } from "../../../iOVM/predeploys/iOVM_L2ToL1MessagePasser.sol"; import { iOVM_L2ToL1MessagePasser } from "../../../iOVM/predeploys/iOVM_L2ToL1MessagePasser.sol";
/* Contract Imports */ /* External Imports */
import { Abs_BaseCrossDomainMessenger } from "./Abs_BaseCrossDomainMessenger.sol"; import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
/* External Imports */ /* External Imports */
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
...@@ -26,11 +27,29 @@ import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.s ...@@ -26,11 +27,29 @@ import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.s
*/ */
contract OVM_L2CrossDomainMessenger is contract OVM_L2CrossDomainMessenger is
iOVM_L2CrossDomainMessenger, iOVM_L2CrossDomainMessenger,
Abs_BaseCrossDomainMessenger,
Lib_AddressResolver, Lib_AddressResolver,
ReentrancyGuard ReentrancyGuard
{ {
/*************
* Constants *
*************/
// The default x-domain message sender being set to a non-zero value makes
// deployment a bit more expensive, but in exchange the refund on every call to
// `relayMessage` by the L1 and L2 messengers will be higher.
address internal constant DEFAULT_XDOMAIN_SENDER = 0x000000000000000000000000000000000000dEaD;
/*************
* Variables *
*************/
mapping (bytes32 => bool) public relayedMessages;
mapping (bytes32 => bool) public successfulMessages;
mapping (bytes32 => bool) public sentMessages;
uint256 public messageNonce;
address internal xDomainMsgSender = DEFAULT_XDOMAIN_SENDER;
/*************** /***************
* Constructor * * Constructor *
***************/ ***************/
...@@ -38,18 +57,53 @@ contract OVM_L2CrossDomainMessenger is ...@@ -38,18 +57,53 @@ contract OVM_L2CrossDomainMessenger is
/** /**
* @param _libAddressManager Address of the Address Manager. * @param _libAddressManager Address of the Address Manager.
*/ */
constructor( constructor(address _libAddressManager) Lib_AddressResolver(_libAddressManager) ReentrancyGuard() {}
address _libAddressManager
)
Lib_AddressResolver(_libAddressManager)
ReentrancyGuard()
{}
/******************** /********************
* Public Functions * * Public Functions *
********************/ ********************/
function xDomainMessageSender()
public
override
view
returns (
address
)
{
require(xDomainMsgSender != DEFAULT_XDOMAIN_SENDER, "xDomainMessageSender is not set");
return xDomainMsgSender;
}
/**
* Sends a cross domain message to the target messenger.
* @param _target Target contract address.
* @param _message Message to send to the target.
* @param _gasLimit Gas limit for the provided message.
*/
function sendMessage(
address _target,
bytes memory _message,
uint32 _gasLimit
)
override
public
{
bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata(
_target,
msg.sender,
_message,
messageNonce
);
messageNonce += 1;
sentMessages[keccak256(xDomainCalldata)] = true;
_sendXDomainMessage(xDomainCalldata, _gasLimit);
emit SentMessage(xDomainCalldata);
}
/** /**
* Relays a cross domain message to a contract. * Relays a cross domain message to a contract.
* @inheritdoc iOVM_L2CrossDomainMessenger * @inheritdoc iOVM_L2CrossDomainMessenger
...@@ -69,7 +123,7 @@ contract OVM_L2CrossDomainMessenger is ...@@ -69,7 +123,7 @@ contract OVM_L2CrossDomainMessenger is
"Provided message could not be verified." "Provided message could not be verified."
); );
bytes memory xDomainCalldata = _getXDomainCalldata( bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata(
_target, _target,
_sender, _sender,
_message, _message,
...@@ -150,7 +204,6 @@ contract OVM_L2CrossDomainMessenger is ...@@ -150,7 +204,6 @@ contract OVM_L2CrossDomainMessenger is
bytes memory _message, bytes memory _message,
uint256 // _gasLimit uint256 // _gasLimit
) )
override
internal internal
{ {
iOVM_L2ToL1MessagePasser(resolve("OVM_L2ToL1MessagePasser")).passMessageToL1(_message); iOVM_L2ToL1MessagePasser(resolve("OVM_L2ToL1MessagePasser")).passMessageToL1(_message);
......
...@@ -22,7 +22,6 @@ contract OVM_L2ToL1MessagePasser is iOVM_L2ToL1MessagePasser { ...@@ -22,7 +22,6 @@ contract OVM_L2ToL1MessagePasser is iOVM_L2ToL1MessagePasser {
mapping (bytes32 => bool) public sentMessages; mapping (bytes32 => bool) public sentMessages;
/******************** /********************
* Public Functions * * Public Functions *
********************/ ********************/
......
...@@ -51,14 +51,14 @@ interface iOVM_L1CrossDomainMessenger is iAbs_BaseCrossDomainMessenger { ...@@ -51,14 +51,14 @@ interface iOVM_L1CrossDomainMessenger is iAbs_BaseCrossDomainMessenger {
* @param _target Target contract address. * @param _target Target contract address.
* @param _sender Original sender address. * @param _sender Original sender address.
* @param _message Message to send to the target. * @param _message Message to send to the target.
* @param _messageNonce Nonce for the provided message. * @param _queueIndex CTC Queue index for the message to replay.
* @param _gasLimit Gas limit for the provided message. * @param _gasLimit Gas limit for the provided message.
*/ */
function replayMessage( function replayMessage(
address _target, address _target,
address _sender, address _sender,
bytes memory _message, bytes memory _message,
uint256 _messageNonce, uint256 _queueIndex,
uint32 _gasLimit uint32 _gasLimit
) external; ) external;
} }
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_RLPReader } from "../rlp/Lib_RLPReader.sol";
/**
* @title Lib_CrossDomainUtils
*/
library Lib_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
);
}
}
...@@ -14,12 +14,16 @@ import { ...@@ -14,12 +14,16 @@ import {
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
DUMMY_BATCH_HEADERS, DUMMY_BATCH_HEADERS,
DUMMY_BATCH_PROOFS, DUMMY_BATCH_PROOFS,
FORCE_INCLUSION_PERIOD_SECONDS,
FORCE_INCLUSION_PERIOD_BLOCKS,
TrieTestGenerator, TrieTestGenerator,
getNextBlockNumber, getNextBlockNumber,
getXDomainCalldata, encodeXDomainCalldata,
} from '../../../../helpers' } from '../../../../helpers'
import { keccak256 } from 'ethers/lib/utils' import { keccak256 } from 'ethers/lib/utils'
const MAX_GAS_LIMIT = 8_000_000
const deployProxyXDomainMessenger = async ( const deployProxyXDomainMessenger = async (
addressManager: Contract, addressManager: Contract,
l1XDomainMessenger: Contract l1XDomainMessenger: Contract
...@@ -48,8 +52,13 @@ describe('OVM_L1CrossDomainMessenger', () => { ...@@ -48,8 +52,13 @@ describe('OVM_L1CrossDomainMessenger', () => {
let Mock__TargetContract: MockContract let Mock__TargetContract: MockContract
let Mock__OVM_L2CrossDomainMessenger: MockContract let Mock__OVM_L2CrossDomainMessenger: MockContract
let Mock__OVM_CanonicalTransactionChain: MockContract
let Mock__OVM_StateCommitmentChain: MockContract let Mock__OVM_StateCommitmentChain: MockContract
let Factory__OVM_CanonicalTransactionChain: ContractFactory
let Factory__OVM_ChainStorageContainer: ContractFactory
let Factory__OVM_L1CrossDomainMessenger: ContractFactory
let OVM_CanonicalTransactionChain: Contract
before(async () => { before(async () => {
Mock__TargetContract = await smockit( Mock__TargetContract = await smockit(
await ethers.getContractFactory('Helper_SimpleProxy') await ethers.getContractFactory('Helper_SimpleProxy')
...@@ -57,9 +66,6 @@ describe('OVM_L1CrossDomainMessenger', () => { ...@@ -57,9 +66,6 @@ describe('OVM_L1CrossDomainMessenger', () => {
Mock__OVM_L2CrossDomainMessenger = await smockit( Mock__OVM_L2CrossDomainMessenger = await smockit(
await ethers.getContractFactory('OVM_L2CrossDomainMessenger') await ethers.getContractFactory('OVM_L2CrossDomainMessenger')
) )
Mock__OVM_CanonicalTransactionChain = await smockit(
await ethers.getContractFactory('OVM_CanonicalTransactionChain')
)
Mock__OVM_StateCommitmentChain = await smockit( Mock__OVM_StateCommitmentChain = await smockit(
await ethers.getContractFactory('OVM_StateCommitmentChain') await ethers.getContractFactory('OVM_StateCommitmentChain')
) )
...@@ -69,23 +75,53 @@ describe('OVM_L1CrossDomainMessenger', () => { ...@@ -69,23 +75,53 @@ describe('OVM_L1CrossDomainMessenger', () => {
Mock__OVM_L2CrossDomainMessenger.address Mock__OVM_L2CrossDomainMessenger.address
) )
await setProxyTarget(
AddressManager,
'OVM_CanonicalTransactionChain',
Mock__OVM_CanonicalTransactionChain
)
await setProxyTarget( await setProxyTarget(
AddressManager, AddressManager,
'OVM_StateCommitmentChain', 'OVM_StateCommitmentChain',
Mock__OVM_StateCommitmentChain Mock__OVM_StateCommitmentChain
) )
})
let Factory__OVM_L1CrossDomainMessenger: ContractFactory Factory__OVM_CanonicalTransactionChain = await ethers.getContractFactory(
before(async () => { 'OVM_CanonicalTransactionChain'
)
Factory__OVM_ChainStorageContainer = await ethers.getContractFactory(
'OVM_ChainStorageContainer'
)
Factory__OVM_L1CrossDomainMessenger = await ethers.getContractFactory( Factory__OVM_L1CrossDomainMessenger = await ethers.getContractFactory(
'OVM_L1CrossDomainMessenger' 'OVM_L1CrossDomainMessenger'
) )
OVM_CanonicalTransactionChain = await Factory__OVM_CanonicalTransactionChain.deploy(
AddressManager.address,
FORCE_INCLUSION_PERIOD_SECONDS,
FORCE_INCLUSION_PERIOD_BLOCKS,
MAX_GAS_LIMIT
)
const batches = await Factory__OVM_ChainStorageContainer.deploy(
AddressManager.address,
'OVM_CanonicalTransactionChain'
)
const queue = await Factory__OVM_ChainStorageContainer.deploy(
AddressManager.address,
'OVM_CanonicalTransactionChain'
)
await AddressManager.setAddress(
'OVM_ChainStorageContainer:CTC:batches',
batches.address
)
await AddressManager.setAddress(
'OVM_ChainStorageContainer:CTC:queue',
queue.address
)
await AddressManager.setAddress(
'OVM_CanonicalTransactionChain',
OVM_CanonicalTransactionChain.address
)
}) })
let OVM_L1CrossDomainMessenger: Contract let OVM_L1CrossDomainMessenger: Contract
...@@ -117,6 +153,26 @@ describe('OVM_L1CrossDomainMessenger', () => { ...@@ -117,6 +153,26 @@ describe('OVM_L1CrossDomainMessenger', () => {
}) })
}) })
const getTransactionHash = (
sender: string,
target: string,
gasLimit: number,
data: string
): string => {
return keccak256(encodeQueueTransaction(sender, target, gasLimit, data))
}
const encodeQueueTransaction = (
sender: string,
target: string,
gasLimit: number,
data: string
): string => {
return ethers.utils.defaultAbiCoder.encode(
['address', 'address', 'uint256', 'bytes'],
[sender, target, gasLimit, data]
)
}
describe('sendMessage', () => { describe('sendMessage', () => {
const target = NON_ZERO_ADDRESS const target = NON_ZERO_ADDRESS
const message = NON_NULL_BYTES32 const message = NON_NULL_BYTES32
...@@ -127,13 +183,24 @@ describe('OVM_L1CrossDomainMessenger', () => { ...@@ -127,13 +183,24 @@ describe('OVM_L1CrossDomainMessenger', () => {
OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit) OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit)
).to.not.be.reverted ).to.not.be.reverted
expect( const calldata = encodeXDomainCalldata(
Mock__OVM_CanonicalTransactionChain.smocked.enqueue.calls[0] target,
).to.deep.equal([ await signer.getAddress(),
message,
0
)
const transactionHash = getTransactionHash(
OVM_L1CrossDomainMessenger.address,
Mock__OVM_L2CrossDomainMessenger.address, Mock__OVM_L2CrossDomainMessenger.address,
BigNumber.from(gasLimit), gasLimit,
getXDomainCalldata(target, await signer.getAddress(), message, 0), calldata
]) )
const queueLength = await OVM_CanonicalTransactionChain.getQueueLength()
const queueElement = await OVM_CanonicalTransactionChain.getQueueElement(
queueLength - 1
)
expect(queueElement[0]).to.equal(transactionHash)
}) })
it('should be able to send the same message twice', async () => { it('should be able to send the same message twice', async () => {
...@@ -150,27 +217,37 @@ describe('OVM_L1CrossDomainMessenger', () => { ...@@ -150,27 +217,37 @@ describe('OVM_L1CrossDomainMessenger', () => {
const message = NON_NULL_BYTES32 const message = NON_NULL_BYTES32
const gasLimit = 100_000 const gasLimit = 100_000
it('should revert if the message does not exist', async () => { it('should revert if given the wrong queue index', async () => {
await OVM_L1CrossDomainMessenger.sendMessage(target, message, 100_001)
const queueLength = await OVM_CanonicalTransactionChain.getQueueLength()
await expect( await expect(
OVM_L1CrossDomainMessenger.replayMessage( OVM_L1CrossDomainMessenger.replayMessage(
target, target,
await signer.getAddress(), await signer.getAddress(),
message, message,
0, queueLength - 1,
gasLimit gasLimit
) )
).to.be.revertedWith('Provided message has not already been sent.') ).to.be.revertedWith('Provided message has not been enqueued.')
}) })
it('should succeed if the message exists', async () => { it('should succeed if the message exists', async () => {
await OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit) await OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit)
const queueLength = await OVM_CanonicalTransactionChain.getQueueLength()
const calldata = encodeXDomainCalldata(
target,
await signer.getAddress(),
message,
queueLength - 1
)
await expect( await expect(
OVM_L1CrossDomainMessenger.replayMessage( OVM_L1CrossDomainMessenger.replayMessage(
target, Mock__OVM_L2CrossDomainMessenger.address,
await signer.getAddress(), await signer.getAddress(),
message, calldata,
0, queueLength - 1,
gasLimit gasLimit
) )
).to.not.be.reverted ).to.not.be.reverted
...@@ -190,7 +267,7 @@ describe('OVM_L1CrossDomainMessenger', () => { ...@@ -190,7 +267,7 @@ describe('OVM_L1CrossDomainMessenger', () => {
]) ])
sender = await signer.getAddress() sender = await signer.getAddress()
calldata = getXDomainCalldata(target, sender, message, 0) calldata = encodeXDomainCalldata(target, sender, message, 0)
const precompile = '0x4200000000000000000000000000000000000000' const precompile = '0x4200000000000000000000000000000000000000'
......
...@@ -11,7 +11,7 @@ import { ...@@ -11,7 +11,7 @@ import {
setProxyTarget, setProxyTarget,
NON_NULL_BYTES32, NON_NULL_BYTES32,
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
getXDomainCalldata, encodeXDomainCalldata,
getNextBlockNumber, getNextBlockNumber,
} from '../../../../helpers' } from '../../../../helpers'
import { solidityKeccak256 } from 'ethers/lib/utils' import { solidityKeccak256 } from 'ethers/lib/utils'
...@@ -89,7 +89,7 @@ describe('OVM_L2CrossDomainMessenger', () => { ...@@ -89,7 +89,7 @@ describe('OVM_L2CrossDomainMessenger', () => {
expect( expect(
Mock__OVM_L2ToL1MessagePasser.smocked.passMessageToL1.calls[0] Mock__OVM_L2ToL1MessagePasser.smocked.passMessageToL1.calls[0]
).to.deep.equal([ ).to.deep.equal([
getXDomainCalldata(target, await signer.getAddress(), message, 0), encodeXDomainCalldata(target, await signer.getAddress(), message, 0),
]) ])
}) })
...@@ -193,7 +193,7 @@ describe('OVM_L2CrossDomainMessenger', () => { ...@@ -193,7 +193,7 @@ describe('OVM_L2CrossDomainMessenger', () => {
await OVM_L2CrossDomainMessenger.successfulMessages( await OVM_L2CrossDomainMessenger.successfulMessages(
solidityKeccak256( solidityKeccak256(
['bytes'], ['bytes'],
[getXDomainCalldata(target, sender, message, 0)] [encodeXDomainCalldata(target, sender, message, 0)]
) )
) )
).to.be.true ).to.be.true
...@@ -211,7 +211,7 @@ describe('OVM_L2CrossDomainMessenger', () => { ...@@ -211,7 +211,7 @@ describe('OVM_L2CrossDomainMessenger', () => {
// Calculate xDomainCallData used for indexing // Calculate xDomainCallData used for indexing
// (within the first call to the L2 Messenger). // (within the first call to the L2 Messenger).
const xDomainCallData = getXDomainCalldata( const xDomainCallData = encodeXDomainCalldata(
OVM_L2CrossDomainMessenger.address, OVM_L2CrossDomainMessenger.address,
sender, sender,
reentrantMessage, reentrantMessage,
......
import { getContractInterface } from '../../../src/contract-defs' import { getContractInterface } from '../../../src/contract-defs'
export const getXDomainCalldata = ( export const encodeXDomainCalldata = (
target: string, target: string,
sender: string, sender: string,
message: string, message: string,
......
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