Skip to content
This repository has been archived by the owner on Dec 5, 2021. It is now read-only.

Commit

Permalink
Support the custom relayer
Browse files Browse the repository at this point in the history
  • Loading branch information
boyuan-chen committed May 28, 2021
1 parent 71a35fd commit 6aeebbb
Show file tree
Hide file tree
Showing 13 changed files with 487 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;

/* Library Imports */
import { Lib_OVMCodec } from "../../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_AddressManager } from "../../../libraries/resolver/Lib_AddressManager.sol";
import { Lib_SecureMerkleTrie } from "../../../libraries/trie/Lib_SecureMerkleTrie.sol";

/* Interface Imports */
import { iOVM_L1CrossDomainMessenger } from "../../../iOVM/bridge/messaging/iOVM_L1CrossDomainMessenger.sol";
import { iOVM_CanonicalTransactionChain } from "../../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
import { iOVM_StateCommitmentChain } from "../../../iOVM/chain/iOVM_StateCommitmentChain.sol";

/* Contract Imports */
import { Abs_BaseCrossDomainMessenger } from "./Abs_BaseCrossDomainMessenger.sol";

/**
* @title OVM_L1CrossDomainMessenger
* @dev The L1 Cross Domain Messenger contract sends messages from L1 to L2, and relays messages from L2 onto L1.
* In the event that a message sent from L1 to L2 is rejected for exceeding the L2 epoch gas limit, it can be resubmitted
* via this contract's replay function.
*
* Compiler used: solc
* Runtime target: EVM
*/
contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, Abs_BaseCrossDomainMessenger, Lib_AddressResolver {

/***************
* Constructor *
***************/

/**
* Pass a default zero address to the address resolver. This will be updated when initialized.
*/
constructor()
Lib_AddressResolver(address(0))
{}

/**
* @param _libAddressManager Address of the Address Manager.
*/
function initialize(
address _libAddressManager
)
public
{
require(address(libAddressManager) == address(0), "L1CrossDomainMessenger already intialized.");
libAddressManager = Lib_AddressManager(_libAddressManager);
xDomainMsgSender = DEFAULT_XDOMAIN_SENDER;
}


/**********************
* Function Modifiers *
**********************/

/**
* Modifier to enforce that, if configured, only the OVM_L2MessageRelayer contract may successfully call a method.
*/
modifier onlyRelayer() {
address relayer = resolve("OVM_L2MessageRelayer");
if (relayer != address(0)) {
require(
msg.sender == relayer,
"Only OVM_L2MessageRelayer can relay L2-to-L1 messages."
);
}
_;
}


/********************
* Public Functions *
********************/

/**
* Relays a cross domain message to a contract.
* @inheritdoc iOVM_L1CrossDomainMessenger
*/
function relayMessage(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce,
L2MessageInclusionProof memory _proof
)
override
public
onlyRelayer()
{
bytes memory xDomainCalldata = _getXDomainCalldata(
_target,
_sender,
_message,
_messageNonce
);

require(
_verifyXDomainMessage(
xDomainCalldata,
_proof
) == true,
"Provided message could not be verified."
);

bytes32 xDomainCalldataHash = keccak256(xDomainCalldata);

require(
successfulMessages[xDomainCalldataHash] == false,
"Provided message has already been received."
);

xDomainMsgSender = _sender;
(bool success, ) = _target.call(_message);
xDomainMsgSender = DEFAULT_XDOMAIN_SENDER;

// Mark the message as received if the call was successful. Ensures that a message can be
// relayed multiple times in the case that the call reverted.
if (success == true) {
successfulMessages[xDomainCalldataHash] = true;
emit RelayedMessage(xDomainCalldataHash);
}

// Store an identifier that can be used to prove that the given message was relayed by some
// user. Gives us an easy way to pay relayers for their work.
bytes32 relayId = keccak256(
abi.encodePacked(
xDomainCalldata,
msg.sender,
block.number
)
);
relayedMessages[relayId] = true;
}

/**
* Replays a cross domain message to the target messenger.
* @inheritdoc iOVM_L1CrossDomainMessenger
*/
function replayMessage(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce,
uint32 _gasLimit
)
override
public
{
bytes memory xDomainCalldata = _getXDomainCalldata(
_target,
_sender,
_message,
_messageNonce
);

require(
sentMessages[keccak256(xDomainCalldata)] == true,
"Provided message has not already been sent."
);

_sendXDomainMessage(xDomainCalldata, _gasLimit);
}


/**********************
* Internal Functions *
**********************/

/**
* Verifies that the given message is valid.
* @param _xDomainCalldata Calldata to verify.
* @param _proof Inclusion proof for the message.
* @return Whether or not the provided message is valid.
*/
function _verifyXDomainMessage(
bytes memory _xDomainCalldata,
L2MessageInclusionProof memory _proof
)
internal
view
returns (
bool
)
{
return (
_verifyStateRootProof(_proof)
&& _verifyStorageProof(_xDomainCalldata, _proof)
);
}

/**
* Verifies that the state root within an inclusion proof is valid.
* @param _proof Message inclusion proof.
* @return Whether or not the provided proof is valid.
*/
function _verifyStateRootProof(
L2MessageInclusionProof memory _proof
)
internal
view
returns (
bool
)
{
iOVM_StateCommitmentChain ovmStateCommitmentChain = iOVM_StateCommitmentChain(resolve("OVM_StateCommitmentChain"));

return (
ovmStateCommitmentChain.verifyStateCommitment(
_proof.stateRoot,
_proof.stateRootBatchHeader,
_proof.stateRootProof
)
);
}

/**
* Verifies that the storage proof within an inclusion proof is valid.
* @param _xDomainCalldata Encoded message calldata.
* @param _proof Message inclusion proof.
* @return Whether or not the provided proof is valid.
*/
function _verifyStorageProof(
bytes memory _xDomainCalldata,
L2MessageInclusionProof memory _proof
)
internal
view
returns (
bool
)
{
bytes32 storageKey = keccak256(
abi.encodePacked(
keccak256(
abi.encodePacked(
_xDomainCalldata,
resolve("OVM_L2CrossDomainMessenger")
)
),
uint256(0)
)
);

(
bool exists,
bytes memory encodedMessagePassingAccount
) = Lib_SecureMerkleTrie.get(
abi.encodePacked(0x4200000000000000000000000000000000000000),
_proof.stateTrieWitness,
_proof.stateRoot
);

require(
exists == true,
"Message passing predeploy has not been initialized or invalid proof provided."
);

Lib_OVMCodec.EVMAccount memory account = Lib_OVMCodec.decodeEVMAccount(
encodedMessagePassingAccount
);

return Lib_SecureMerkleTrie.verifyInclusionProof(
abi.encodePacked(storageKey),
abi.encodePacked(uint8(1)),
_proof.storageTrieWitness,
account.storageRoot
);
}

/**
* Sends a cross domain message.
* @param _message Message to send.
* @param _gasLimit OVM gas limit for the message.
*/
function _sendXDomainMessage(
bytes memory _message,
uint256 _gasLimit
)
override
internal
{
revert("Sending via this messenger is disabled");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types'

/* Imports: Internal */
import { getDeployedContract } from '../src/hardhat-deploy-ethers'

const deployFn: DeployFunction = async (hre) => {
const { deploy } = hre.deployments
const { deployer } = await hre.getNamedAccounts()

const Lib_AddressManager = await getDeployedContract(
hre,
'Lib_AddressManager',
{
signerOrProvider: deployer,
}
)

const result = await deploy('OVM_L1CustomCrossDomainMessenger', {
from: deployer,
args: [],
log: true,
})

if (!result.newlyDeployed) {
return
}

const OVM_L1CustomCrossDomainMessenger = await getDeployedContract(
hre,
'OVM_L1CustomCrossDomainMessenger',
{
signerOrProvider: deployer,
}
)

// NOTE: this initialization is *not* technically required (we only need to initialize the proxy)
// but it feels safer to initialize this anyway. Otherwise someone else could come along and
// initialize this.
await OVM_L1CustomCrossDomainMessenger.initialize(Lib_AddressManager.address)

const libAddressManager = await OVM_L1CustomCrossDomainMessenger.libAddressManager()
if (libAddressManager !== Lib_AddressManager.address) {
throw new Error(
`\n**FATAL ERROR. THIS SHOULD NEVER HAPPEN. CHECK YOUR DEPLOYMENT.**:\n` +
`OVM_L1CustomCrossDomainMessenger could not be succesfully initialized.\n` +
`Attempted to set Lib_AddressManager to: ${Lib_AddressManager.address}\n` +
`Actual address after initialization: ${libAddressManager}\n` +
`This could indicate a compromised deployment.`
)
}

await Lib_AddressManager.setAddress(
'OVM_L1CustomCrossDomainMessenger',
result.address
)
}

deployFn.dependencies = ['Lib_AddressManager']
deployFn.tags = ['OVM_L1CustomCrossDomainMessenger']

export default deployFn
1 change: 0 additions & 1 deletion packages/omgx/alt_messenger/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ ADDRESS_MANAGER_ADDRESS= <address manager contract address>
L1_NODE_WEB3_URL= <l1 node url>
L2_NODE_WEB3_URL= <l2 node url>
L1_WALLET_KEY= <l1 wallet key>
L1_MESSENGER_ADDRESS= <l1 custom messenger address>
L1_TARGET= <target contract to allow relays to, set to 0x0 to skip>
```

Expand Down
1 change: 0 additions & 1 deletion packages/omgx/alt_messenger/relayer_service/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@ ADDRESS_MANAGER_ADDRESS=0x5FbDB2315678afecb367f032d93F642f64180aa3
L1_NODE_WEB3_URL=http://localhost:9545
L2_NODE_WEB3_URL=http://localhost:8545
L1_WALLET_KEY=0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97
L1_MESSENGER_ADDRESS=0x99bbA657f2BbC93c02D617f8bA121cB8Fc104Acf
L1_TARGET=0x0
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM node:14-buster as base

RUN apt-get update && apt-get install -y bash curl jq

FROM base as build

RUN apt-get update && apt-get install -y bash git python build-essential

ADD . /opt/optimism-ts-services
RUN cd /opt/optimism-ts-services && yarn install && yarn build

FROM base

RUN apt-get update && apt-get install -y bash curl jq

COPY --from=build /opt/optimism-ts-services /opt/optimism-ts-services

COPY wait-for-l1-and-l2.sh /opt/
RUN chmod +x /opt/wait-for-l1-and-l2.sh

RUN ln -s /opt/optimism-ts-services/exec/run-message-relayer.js /usr/local/bin/
RUN chmod +x /usr/local/bin/run-message-relayer.js

ENTRYPOINT ["/opt/wait-for-l1-and-l2.sh", "run-message-relayer.js"]
Loading

0 comments on commit 6aeebbb

Please sign in to comment.