Commit f4343792 authored by Liam Horne's avatar Liam Horne Committed by GitHub

Merge pull request #993 from ethereum-optimism/regenesis/0.4.0

Regenesis/0.4.0
parents 42decb6e 6ad1dcb9
---
'@eth-optimism/integration-tests': patch
'@eth-optimism/contracts': patch
---
Adds ERC1271 support to default contract account
---
'@eth-optimism/contracts': patch
---
Update the ECDSAContractAccount import path in the `contract-data.ts` file for connecting ethers contracts to the L2 contracts
---
'@eth-optimism/l2geth': patch
---
Add new config `ROLLUP_GAS_PRICE_ORACLE_OWNER_ADDRESS` to set the owner of the gas price oracle at runtime
---
'@eth-optimism/contracts': minor
---
Disables EOA contract upgrades until further notice
---
'@eth-optimism/contracts': patch
---
Apply consistent styling to constants
---
'@eth-optimism/l2geth': patch
'@eth-optimism/data-transport-layer': patch
---
Add extra overflow protection for the DTL types
---
'@eth-optimism/batch-submitter': patch
'@eth-optimism/contracts': patch
---
Use dashes instead of colons in contract names
---
'@eth-optimism/contracts': patch
---
Replaces RingBuffer with a simpler Buffer library
---
'@eth-optimism/contracts': minor
'@eth-optimism/integration-tests': patch
'@eth-optimism/l2geth': patch
---
Add a new Standard Token Bridge, to handle deposits and withdrawals of any ERC20 token.
For projects developing a custom bridge, if you were previously importing `iAbs_BaseCrossDomainMessenger`, you should now
import `iOVM_CrossDomainMessenger`.
---
'@eth-optimism/contracts': minor
'@eth-optimism/data-transport-layer': minor
---
Update AddressSet event to speed search up a bit. Breaks AddressSet API.
---
'@eth-optimism/integration-tests': patch
'@eth-optimism/contracts': patch
---
Add WETH9 compatible deposit and withdraw functions to OVM_ETH
---
'@eth-optimism/data-transport-layer': patch
---
Removes a function that was previously used for backwards compatibility but is no longer necessary
---
'@eth-optimism/contracts': patch
---
Deploy new Goerli contracts at d3e743aa7a406c583f7d76f4fda607f592d03e47
---
'@eth-optimism/l2geth': patch
---
Prevent overflows in abi encoding of ovm codec transaction from geth types.Transaction
---
'@eth-optimism/l2geth': patch
---
Update queueOrigin type
---
'@eth-optimism/contracts': patch
---
ECDSA account interface contract moved to predeploys dir
---
'@eth-optimism/contracts': patch
---
Deploy v0.4.0 rc to Kovan
---
'@eth-optimism/contracts': patch
---
Moved contracts in the "accounts" folder into the "predeploys" folder
---
'@eth-optimism/data-transport-layer': patch
---
Have DTL log failed HTTP requests as ERROR instead of INFO
---
'@eth-optimism/contracts': patch
---
Use predeploy constants lib for EM wrapper
---
'@eth-optimism/data-transport-layer': patch
---
Updates the DTL to use the same L2 chain ID everywhere
---
'@eth-optimism/l2geth': patch
---
Removes config options that are no longer required. `ROLLUP_DATAPRICE`, `ROLLUP_EXECUTION_PRICE`, `ROLLUP_GAS_PRICE_ORACLE_ADDRESS` and `ROLLUP_ENABLE_L2_GAS_POLLING`. The oracle was moved to a predeploy 0x42.. address and polling is always enabled as it no longer needs to be backwards compatible
---
'@eth-optimism/contracts': patch
---
Adds a temporary way to fund hardhat accounts when testing locally
---
'@eth-optimism/integration-tests': minor
'@eth-optimism/l2geth': minor
'@eth-optimism/contracts': minor
---
Add support for ovmCALL with nonzero ETH value
---
'@eth-optimism/contracts': patch
---
Removes one-off GasPriceOracle deployment file
---
'@eth-optimism/contracts': patch
---
Updates the deployment of the L1MultiMessageRelayer to NOT set the OVM_L2MessageRelayer address in the AddressManager
---
'@eth-optimism/l2geth': patch
---
Removes the gas refund for unused gas in geth since it is instead managed in the smart contracts
---
'@eth-optimism/contracts': patch
---
Disable upgradability from the ECDSA account instead of the EOA proxy.
---
'@eth-optimism/integration-tests': patch
'@eth-optimism/l2geth': patch
'@eth-optimism/contracts': patch
---
Adds new SequencerFeeVault contract to store generated fees
---
'@eth-optimism/l2geth': patch
---
Removes the SignatureHashType from l2geth as it is deprecated and no longer required.
---
'@eth-optimism/contracts': patch
---
Token gateways pass additional information: sender and arbitrary data.
---
'@eth-optimism/contracts': patch
---
Do not RLP decode the transaction in the OVM_ECDSAContractAccount
---
'@eth-optimism/contracts': patch
---
Introduce the L1ChugSplashProxy contract
......@@ -45,18 +45,18 @@ jobs:
run: docker-compose run integration_tests
# Examples Tests
- name: Test & deploy hardhat-example on Ethereum (regression)
working-directory: ./examples/hardhat
run: |
yarn
yarn deploy
yarn test:integration
- name: Test & deploy hardhat-example on Optimistic Ethereum
working-directory: ./examples/hardhat
run: |
yarn deploy:ovm
yarn test:integration:ovm
# - name: Test & deploy hardhat-example on Ethereum (regression)
# working-directory: ./examples/hardhat
# run: |
# yarn
# yarn deploy
# yarn test:integration
# - name: Test & deploy hardhat-example on Optimistic Ethereum
# working-directory: ./examples/hardhat
# run: |
# yarn deploy:ovm
# yarn test:integration:ovm
- name: Test & deploy waffle-example on Ethereum (regression)
working-directory: ./examples/waffle
......@@ -86,13 +86,13 @@ jobs:
yarn test:integration:ovm
yarn deploy:ovm
- name: Test l1-l2-deposit-withdrawal example on Optimistic Ethereum with cross-domain message passing
working-directory: ./examples/l1-l2-deposit-withdrawal
run: |
yarn
yarn compile
yarn compile:ovm
yarn test:integration:ovm
# - name: Test l1-l2-deposit-withdrawal example on Optimistic Ethereum with cross-domain message passing
# working-directory: ./examples/l1-l2-deposit-withdrawal
# run: |
# yarn
# yarn compile
# yarn compile:ovm
# yarn test:integration:ovm
- name: Collect docker logs on failure
if: failure()
......@@ -110,4 +110,4 @@ jobs:
uses: actions/upload-artifact@master
with:
name: logs.tgz
path: ./logs.tgz
\ No newline at end of file
path: ./logs.tgz
......@@ -21,6 +21,7 @@ jobs:
message-relayer: ${{ steps.packages.outputs.message-relayer }}
data-transport-layer: ${{ steps.packages.outputs.data-transport-layer }}
contracts: ${{ steps.packages.outputs.contracts }}
canary-docker-tag: ${{ steps.docker-image-name.outputs.canary-docker-tag }}
steps:
- name: Check out source code
......@@ -72,9 +73,9 @@ jobs:
node ops/scripts/ci-versions.js ${{ toJSON(steps.changesets.outputs.publishedPackages) }}
- name: Docker Image Name
id: docker_image_name
id: docker-image-name
run: |
if [ -z "${CUSTOM_IMAGE_NAME}" ]
if [ ${CUSTOM_IMAGE_NAME} == '' ]
then
echo "::set-output name=canary-docker-tag::${GITHUB_SHA::8}"
else
......@@ -89,7 +90,7 @@ jobs:
# while also allowing for parallelization (i.e. `l2geth` not depending on `builder`)
# and all jobs executing in parallel once `builder` is built
l2geth:
name: Publish L2Geth Version ${{ needs.canary-publish.outputs.l2geth }}
name: Publish L2Geth Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
if: needs.canary-publish.outputs.l2geth != ''
runs-on: ubuntu-latest
......@@ -112,7 +113,7 @@ jobs:
context: .
file: ./ops/docker/Dockerfile.geth
push: true
tags: ethereumoptimism/l2geth:${{ steps.docker_image_name.outputs.canary-docker-tag }}
tags: ethereumoptimism/l2geth:${{ needs.canary-publish.outputs.canary-docker-tag }}
# pushes the base builder image to dockerhub
builder:
......@@ -127,6 +128,8 @@ jobs:
data-transport-layer: ${{ needs.canary-publish.outputs.data-transport-layer }}
contracts: ${{ needs.canary-publish.outputs.contracts }}
integration-tests: ${{ needs.canary-publish.outputs.integration-tests }}
canary-docker-tag: ${{ needs.canary-publish.outputs.canary-docker-tag }}
steps:
- name: Checkout
......@@ -149,7 +152,7 @@ jobs:
tags: ethereumoptimism/builder
message-relayer:
name: Publish Message Relayer Version ${{ needs.builder.outputs.message-relayer }}
name: Publish Message Relayer Version ${{ needs.builder.outputs.canary-docker-tag }}
needs: builder
if: needs.builder.outputs.message-relayer != ''
runs-on: ubuntu-latest
......@@ -172,10 +175,10 @@ jobs:
context: .
file: ./ops/docker/Dockerfile.message-relayer
push: true
tags: ethereumoptimism/message-relayer:${{ steps.docker_image_name.outputs.canary-docker-tag }}
tags: ethereumoptimism/message-relayer:${{ needs.builder.outputs.canary-docker-tag }}
batch-submitter:
name: Publish Batch Submitter Version ${{ needs.builder.outputs.batch-submitter }}
name: Publish Batch Submitter Version ${{ needs.builder.outputs.canary-docker-tag }}
needs: builder
if: needs.builder.outputs.batch-submitter != ''
runs-on: ubuntu-latest
......@@ -198,10 +201,10 @@ jobs:
context: .
file: ./ops/docker/Dockerfile.batch-submitter
push: true
tags: ethereumoptimism/batch-submitter:${{ steps.docker_image_name.outputs.canary-docker-tag }}
tags: ethereumoptimism/batch-submitter:${{ needs.builder.outputs.canary-docker-tag }}
data-transport-layer:
name: Publish Data Transport Layer Version ${{ needs.builder.outputs.data-transport-layer }}
name: Publish Data Transport Layer Version ${{ needs.builder.outputs.canary-docker-tag }}
needs: builder
if: needs.builder.outputs.data-transport-layer != ''
runs-on: ubuntu-latest
......@@ -224,10 +227,10 @@ jobs:
context: .
file: ./ops/docker/Dockerfile.data-transport-layer
push: true
tags: ethereumoptimism/data-transport-layer:${{ steps.docker_image_name.outputs.canary-docker-tag }}
tags: ethereumoptimism/data-transport-layer:${{ needs.builder.outputs.canary-docker-tag }}
contracts:
name: Publish Deployer Version ${{ needs.builder.outputs.contracts }}
name: Publish Deployer Version ${{ needs.builder.outputs.canary-docker-tag }}
needs: builder
if: needs.builder.outputs.contracts != ''
runs-on: ubuntu-latest
......@@ -250,7 +253,7 @@ jobs:
context: .
file: ./ops/docker/Dockerfile.deployer
push: true
tags: ethereumoptimism/deployer:${{ steps.docker_image_name.outputs.canary-docker-tag }}
tags: ethereumoptimism/deployer:${{ needs.builder.outputs.canary-docker-tag }}
integration_tests:
name: Publish Integration tests ${{ needs.builder.outputs.integration-tests }}
......@@ -276,4 +279,4 @@ jobs:
context: .
file: ./ops/docker/Dockerfile.integration-tests
push: true
tags: ethereumoptimism/integration-tests:${{ steps.docker_image_name.outputs.canary-docker-tag }}
tags: ethereumoptimism/integration-tests:${{ needs.builder.outputs.canary-docker-tag }}
......@@ -24,3 +24,4 @@ packages/data-transport-layer/db
*.swp
.env
*.log
......@@ -11,7 +11,7 @@ const factory = (name, ovm = false) => {
}
const factory__L1_ERC20 = factory('ERC20')
const factory__L2_ERC20 = factory('L2DepositedERC20', true)
const factory__L1_ERC20Gateway = getContractFactory('OVM_L1ERC20Gateway')
const factory__L1StandardBridge = getContractFactory('OVM_L1StandardBridge')
describe(`L1 <> L2 Deposit and Withdrawal`, () => {
......@@ -50,7 +50,7 @@ describe(`L1 <> L2 Deposit and Withdrawal`, () => {
let L1_ERC20,
L2_ERC20,
L1_ERC20Gateway
L1StandardBridge
before(`deploy contracts`, async () => {
// Deploy an ERC20 token on L1.
......@@ -75,8 +75,8 @@ describe(`L1 <> L2 Deposit and Withdrawal`, () => {
await L2_ERC20.deployTransaction.wait()
// Create a gateway that connects the two contracts.
L1_ERC20Gateway = await factory__L1_ERC20Gateway.connect(l1Wallet).deploy(
// Create a bridge that connects the two contracts.
L1StandardBridge = await factory__L1StandardBridge.connect(l1Wallet).deploy(
L1_ERC20.address,
L2_ERC20.address,
l1MessengerAddress,
......@@ -85,12 +85,12 @@ describe(`L1 <> L2 Deposit and Withdrawal`, () => {
}
)
await L1_ERC20Gateway.deployTransaction.wait()
await L1StandardBridge.deployTransaction.wait()
})
describe('Initialization and initial balances', async () => {
it(`should initialize L2 ERC20`, async () => {
const tx = await L2_ERC20.init(L1_ERC20Gateway.address, { gasPrice: 0 })
const tx = await L2_ERC20.init(L1StandardBridge.address, { gasPrice: 0 })
await tx.wait()
const txHashPrefix = tx.hash.slice(0, 2)
expect(txHashPrefix).to.eq('0x')
......@@ -107,15 +107,15 @@ describe(`L1 <> L2 Deposit and Withdrawal`, () => {
describe('L1 to L2 deposit', async () => {
let l1Tx1
it(`should approve 1234 tokens for ERC20 gateway`, async () => {
const tx = await L1_ERC20.approve(L1_ERC20Gateway.address, 1234)
it(`should approve 1234 tokens for ERC20 bridge`, async () => {
const tx = await L1_ERC20.approve(L1StandardBridge.address, 1234)
await tx.wait()
const txHashPrefix = tx.hash.slice(0, 2)
expect(txHashPrefix).to.eq('0x')
})
it(`should deposit 1234 tokens into L2 ERC20`, async () => {
l1Tx1 = await L1_ERC20Gateway.deposit(1234, { gasPrice: 0 })
l1Tx1 = await L1StandardBridge.deposit(1234, { gasPrice: 0 })
await l1Tx1.wait()
const txHashPrefix = l1Tx1.hash.slice(0, 2)
expect(txHashPrefix).to.eq('0x')
......
......@@ -2,10 +2,16 @@
pragma solidity >=0.7.0;
contract TestOOG {
constructor() {
function runOutOfGas() public {
bytes32 h;
for (uint256 i = 0; i < 100000; i++) {
h = keccak256(abi.encodePacked(h));
}
}
}
contract TestOOGInConstructor is TestOOG {
constructor() {
runOutOfGas();
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0;
contract ValueContext {
function getSelfBalance() external payable returns(uint256) {
uint selfBalance;
assembly {
selfBalance := selfbalance()
}
return selfBalance;
}
function getAddressThisBalance() external view returns(uint256) {
return address(this).balance;
}
function getBalance(
address _address
) external payable returns(uint256) {
return _address.balance;
}
function getCallValue() public payable returns(uint256) {
return msg.value;
}
}
contract ValueCalls is ValueContext {
receive() external payable {}
function nonPayable() external {}
function simpleSend(
address _address,
uint _value
) external payable returns (bool, bytes memory) {
return sendWithData(_address, _value, hex"");
}
function sendWithDataAndGas(
address _address,
uint _value,
uint _gasLimit,
bytes memory _calldata
) public returns (bool, bytes memory) {
return _address.call{value: _value, gas: _gasLimit}(_calldata);
}
function sendWithData(
address _address,
uint _value,
bytes memory _calldata
) public returns (bool, bytes memory) {
return _address.call{value: _value}(_calldata);
}
function verifyCallValueAndRevert(
uint256 _expectedValue
) external payable {
bool correct = _checkCallValue(_expectedValue);
// do the opposite of expected if the value is wrong.
if (correct) {
revert("expected revert");
} else {
return;
}
}
function verifyCallValueAndReturn(
uint256 _expectedValue
) external payable {
bool correct = _checkCallValue(_expectedValue);
// do the opposite of expected if the value is wrong.
if (correct) {
return;
} else {
revert("unexpected revert");
}
}
function delegateCallToCallValue(
address _valueContext
) public payable returns(bool, bytes memory) {
bytes memory data = abi.encodeWithSelector(ValueContext.getCallValue.selector);
return _valueContext.delegatecall(data);
}
function delegateCallToAddressThisBalance(
address _valueContext
) public payable returns(bool, bytes memory) {
bytes memory data = abi.encodeWithSelector(ValueContext.getAddressThisBalance.selector);
return _valueContext.delegatecall(data);
}
function _checkCallValue(
uint256 _expectedValue
) internal returns(bool) {
return getCallValue() == _expectedValue;
}
}
contract ValueGasMeasurer {
function measureGasOfTransferingEthViaCall(
address target,
uint256 value,
uint256 gasLimit
) public returns(uint256) {
uint256 gasBefore = gasleft();
assembly {
pop(call(gasLimit, target, value, 0, 0, 0, 0))
}
return gasBefore - gasleft();
}
}
contract PayableConstant {
function returnValue() external payable returns(uint256) {
return 42;
}
}
contract SendETHAwayAndDelegateCall {
function emptySelfAndDelegateCall(
address _delegateTo,
bytes memory _data
) public payable returns (bool, bytes memory) {
address(0).call{value: address(this).balance}(_data);
return _delegateTo.delegatecall(_data);
}
}
......@@ -20,7 +20,7 @@ const config: HardhatUserConfig = {
},
solidity: '0.7.6',
ovm: {
solcVersion: '0.7.6',
solcVersion: '0.7.6+commit.3b061308',
},
gasReporter: {
enabled: enableGasReport,
......
import chai, { expect } from 'chai'
import chaiAsPromised from 'chai-as-promised'
chai.use(chaiAsPromised)
import { BigNumber, utils } from 'ethers'
import { OptimismEnv } from './shared/env'
/* Imports: External */
import { BigNumber, Contract, utils } from 'ethers'
import { TxGasLimit, TxGasPrice } from '@eth-optimism/core-utils'
import { predeploys, getContractInterface } from '@eth-optimism/contracts'
/* Imports: Internal */
import { OptimismEnv } from './shared/env'
import { Direction } from './shared/watcher-utils'
describe('Fee Payment Integration Tests', async () => {
let env: OptimismEnv
const other = '0x1234123412341234123412341234123412341234'
let env: OptimismEnv
before(async () => {
env = await OptimismEnv.new()
})
let ovmSequencerFeeVault: Contract
before(async () => {
ovmSequencerFeeVault = new Contract(
predeploys.OVM_SequencerFeeVault,
getContractInterface('OVM_SequencerFeeVault'),
env.l2Wallet
)
})
it(`Should return a gasPrice of ${TxGasPrice.toString()} wei`, async () => {
const gasPrice = await env.l2Wallet.getGasPrice()
expect(gasPrice).to.deep.eq(TxGasPrice)
......@@ -36,6 +51,9 @@ describe('Fee Payment Integration Tests', async () => {
it('Paying a nonzero but acceptable gasPrice fee', async () => {
const amount = utils.parseEther('0.5')
const balanceBefore = await env.l2Wallet.getBalance()
const feeVaultBalanceBefore = await env.l2Wallet.provider.getBalance(
ovmSequencerFeeVault.address
)
expect(balanceBefore.gt(amount))
const tx = await env.ovmEth.transfer(other, amount)
......@@ -43,10 +61,53 @@ describe('Fee Payment Integration Tests', async () => {
expect(receipt.status).to.eq(1)
const balanceAfter = await env.l2Wallet.getBalance()
const feeVaultBalanceAfter = await env.l2Wallet.provider.getBalance(
ovmSequencerFeeVault.address
)
const expectedFeePaid = tx.gasPrice.mul(tx.gasLimit)
// The fee paid MUST be the receipt.gasUsed, and not the tx.gasLimit
// https://github.com/ethereum-optimism/optimism/blob/0de7a2f9c96a7c4860658822231b2d6da0fefb1d/packages/contracts/contracts/optimistic-ethereum/OVM/accounts/OVM_ECDSAContractAccount.sol#L103
expect(balanceBefore.sub(balanceAfter)).to.be.deep.eq(
tx.gasPrice.mul(tx.gasLimit).add(amount)
expect(balanceBefore.sub(balanceAfter)).to.deep.equal(
expectedFeePaid.add(amount)
)
// Make sure the fee was transferred to the vault.
expect(feeVaultBalanceAfter.sub(feeVaultBalanceBefore)).to.deep.equal(
expectedFeePaid
)
})
it('should not be able to withdraw fees before the minimum is met', async () => {
await expect(ovmSequencerFeeVault.withdraw()).to.be.rejected
})
it('should be able to withdraw fees back to L1 once the minimum is met', async () => {
const l1FeeWallet = await ovmSequencerFeeVault.l1FeeWallet()
const balanceBefore = await env.l1Wallet.provider.getBalance(l1FeeWallet)
// Transfer the minimum required to withdraw.
await env.ovmEth.transfer(
ovmSequencerFeeVault.address,
await ovmSequencerFeeVault.MIN_WITHDRAWAL_AMOUNT()
)
const vaultBalance = await env.ovmEth.balanceOf(
ovmSequencerFeeVault.address
)
// Submit the withdrawal.
const withdrawTx = await ovmSequencerFeeVault.withdraw({
gasPrice: 0, // Need a gasprice of 0 or the balances will include the fee paid during this tx.
})
// Wait for the withdrawal to be relayed to L1.
await env.waitForXDomainTransaction(withdrawTx, Direction.L2ToL1)
// Balance difference should be equal to old L2 balance.
const balanceAfter = await env.l1Wallet.provider.getBalance(l1FeeWallet)
expect(balanceAfter.sub(balanceBefore)).to.deep.equal(
BigNumber.from(vaultBalance)
)
})
})
This diff is collapsed.
This diff is collapsed.
import { expect } from 'chai'
import { ethers } from 'hardhat'
/* Imports: External */
import { Contract, Wallet } from 'ethers'
import { OptimismEnv } from './shared/env'
import { DEFAULT_TRANSACTION } from './shared/utils'
import { getContractInterface } from '@eth-optimism/contracts'
describe('ECDSAContractAccount', () => {
let l2Wallet: Wallet
before(async () => {
const env = await OptimismEnv.new()
l2Wallet = env.l2Wallet
})
let ProxyEOA: Contract
let messageHash: string
let signature: string
before(async () => {
// Send a transaction to ensure there is a ProxyEOA deployed at l2Wallet.address
const result = await l2Wallet.sendTransaction(DEFAULT_TRANSACTION)
await result.wait()
ProxyEOA = new Contract(
l2Wallet.address,
getContractInterface('OVM_ECDSAContractAccount'),
l2Wallet
)
const message = '0x42'
messageHash = ethers.utils.hashMessage(message)
signature = await l2Wallet.signMessage(message)
})
it('should correctly evaluate isValidSignature from this wallet', async () => {
const isValid = await ProxyEOA.isValidSignature(messageHash, signature)
expect(isValid).to.equal('0x1626ba7e')
})
it('should correctly evaluate isValidSignature from other wallet', async () => {
const otherWallet = Wallet.createRandom().connect(l2Wallet.provider)
const isValid = await ProxyEOA.connect(otherWallet).isValidSignature(
messageHash,
signature
)
expect(isValid).to.equal('0x1626ba7e')
})
})
......@@ -86,8 +86,6 @@ describe('Queue Ingestion', () => {
// The `to` addresses are defined in the previous test and
// increment sequentially.
expect(tx.to).to.be.equal('0x' + `${i}`.repeat(40))
// The transaction type is EIP155
expect(tx.txType).to.be.equal('EIP155')
// The queue origin is Layer 1
expect(tx.queueOrigin).to.be.equal('l1')
// the L1TxOrigin is equal to the Layer one from
......
......@@ -4,10 +4,16 @@ import {
TxGasPrice,
toRpcHexString,
} from '@eth-optimism/core-utils'
import { Wallet, BigNumber, Contract } from 'ethers'
import { Wallet, BigNumber, Contract, ContractFactory } from 'ethers'
import { ethers } from 'hardhat'
import chai, { expect } from 'chai'
import { sleep, l2Provider, l1Provider } from './shared/utils'
import {
sleep,
l2Provider,
DEFAULT_TRANSACTION,
fundUser,
expectApprox,
} from './shared/utils'
import chaiAsPromised from 'chai-as-promised'
import { OptimismEnv } from './shared/env'
import {
......@@ -22,14 +28,6 @@ describe('Basic RPC tests', () => {
let env: OptimismEnv
let wallet: Wallet
const DEFAULT_TRANSACTION = {
to: '0x' + '1234'.repeat(10),
gasLimit: 33600000000001,
gasPrice: 0,
data: '0x',
value: 0,
}
const provider = injectL2Context(l2Provider)
let Reverter: Contract
......@@ -154,7 +152,7 @@ describe('Basic RPC tests', () => {
})
it('should correctly report OOG for contract creations', async () => {
const factory = await ethers.getContractFactory('TestOOG')
const factory = await ethers.getContractFactory('TestOOGInConstructor')
await expect(factory.connect(wallet).deploy()).to.be.rejectedWith(
'gas required exceeds allowance'
......@@ -207,6 +205,32 @@ describe('Basic RPC tests', () => {
'Contract creation code contains unsafe opcodes. Did you use the right compiler or pass an unsafe constructor argument?'
)
})
it('should allow eth_calls with nonzero value', async () => {
// Deploy a contract to check msg.value of the call
const Factory__ValueContext: ContractFactory = await ethers.getContractFactory(
'ValueContext',
wallet
)
const ValueContext: Contract = await Factory__ValueContext.deploy()
await ValueContext.deployTransaction.wait()
// Fund account to call from
const from = wallet.address
const value = 15
await fundUser(env.watcher, env.l1Bridge, value, from)
// Do the call and check msg.value
const data = ValueContext.interface.encodeFunctionData('getCallValue')
const res = await provider.call({
to: ValueContext.address,
from,
data,
value,
})
expect(res).to.eq(BigNumber.from(value))
})
})
describe('eth_getTransactionReceipt', () => {
......@@ -236,7 +260,7 @@ describe('Basic RPC tests', () => {
it('correctly exposes revert data for contract creations', async () => {
const req: TransactionRequest = {
...revertingDeployTx,
gasLimit: 17700899, // override gas estimation
gasLimit: 27700899, // override gas estimation
}
const tx = await wallet.sendTransaction(req)
......@@ -264,7 +288,6 @@ describe('Basic RPC tests', () => {
await result.wait()
const transaction = (await provider.getTransaction(result.hash)) as any
expect(transaction.txType).to.equal('EIP155')
expect(transaction.queueOrigin).to.equal('sequencer')
expect(transaction.transactionIndex).to.be.eq(0)
expect(transaction.gasLimit).to.be.deep.eq(BigNumber.from(tx.gasLimit))
......@@ -285,7 +308,6 @@ describe('Basic RPC tests', () => {
expect(block.number).to.not.equal(0)
expect(typeof block.stateRoot).to.equal('string')
expect(block.transactions.length).to.equal(1)
expect(block.transactions[0].txType).to.equal('EIP155')
expect(block.transactions[0].queueOrigin).to.equal('sequencer')
expect(block.transactions[0].l1TxOrigin).to.equal(null)
})
......@@ -355,7 +377,8 @@ describe('Basic RPC tests', () => {
to: DEFAULT_TRANSACTION.to,
value: 0,
})
expect(estimate).to.be.eq(5920012)
// Expect gas to be less than or equal to the target plus 1%
expectApprox(estimate, 5920012, 1)
})
it('should return a gas estimate that grows with the size of data', async () => {
......
......@@ -9,7 +9,8 @@ import {
l2Wallet,
fundUser,
getOvmEth,
getGateway,
getL1Bridge,
getL2Bridge,
} from './utils'
import {
initWatcher,
......@@ -23,12 +24,13 @@ import { TransactionResponse } from '@ethersproject/providers'
export class OptimismEnv {
// L1 Contracts
addressManager: Contract
gateway: Contract
l1Bridge: Contract
l1Messenger: Contract
ctc: Contract
// L2 Contracts
ovmEth: Contract
l2Bridge: Contract
l2Messenger: Contract
// The L1 <> L2 State watcher
......@@ -40,9 +42,10 @@ export class OptimismEnv {
constructor(args: any) {
this.addressManager = args.addressManager
this.gateway = args.gateway
this.l1Bridge = args.l1Bridge
this.l1Messenger = args.l1Messenger
this.ovmEth = args.ovmEth
this.l2Bridge = args.l2Bridge
this.l2Messenger = args.l2Messenger
this.watcher = args.watcher
this.l1Wallet = args.l1Wallet
......@@ -53,18 +56,18 @@ export class OptimismEnv {
static async new(): Promise<OptimismEnv> {
const addressManager = getAddressManager(l1Wallet)
const watcher = await initWatcher(l1Provider, l2Provider, addressManager)
const gateway = await getGateway(l1Wallet, addressManager)
const l1Bridge = await getL1Bridge(l1Wallet, addressManager)
// fund the user if needed
const balance = await l2Wallet.getBalance()
if (balance.isZero()) {
await fundUser(watcher, gateway, utils.parseEther('10'))
await fundUser(watcher, l1Bridge, utils.parseEther('20'))
}
const ovmEth = getOvmEth(l2Wallet)
const l1Messenger = getContractFactory('iOVM_L1CrossDomainMessenger')
.connect(l1Wallet)
.attach(watcher.l1.messengerAddress)
const ovmEth = getOvmEth(l2Wallet)
const l2Bridge = await getL2Bridge(l2Wallet)
const l2Messenger = getContractFactory('iOVM_L2CrossDomainMessenger')
.connect(l2Wallet)
.attach(watcher.l2.messengerAddress)
......@@ -78,10 +81,11 @@ export class OptimismEnv {
return new OptimismEnv({
addressManager,
gateway,
l1Bridge,
ctc,
l1Messenger,
ovmEth,
l2Bridge,
l2Messenger,
watcher,
l1Wallet,
......
import { expect } from 'chai'
import { Direction, waitForXDomainTransaction } from './watcher-utils'
import {
getContractFactory,
getContractInterface,
predeploys,
} from '@eth-optimism/contracts'
import { injectL2Context, remove0x, Watcher } from '@eth-optimism/core-utils'
import {
......@@ -59,7 +62,7 @@ export const l2Wallet = l1Wallet.connect(l2Provider)
// Predeploys
export const PROXY_SEQUENCER_ENTRYPOINT_ADDRESS =
'0x4200000000000000000000000000000000000004'
export const OVM_ETH_ADDRESS = '0x4200000000000000000000000000000000000006'
export const OVM_ETH_ADDRESS = predeploys.OVM_ETH
export const getAddressManager = (provider: any) => {
return getContractFactory('Lib_AddressManager')
......@@ -67,24 +70,37 @@ export const getAddressManager = (provider: any) => {
.attach(env.ADDRESS_MANAGER)
}
// Gets the gateway using the proxy if available
export const getGateway = async (wallet: Wallet, AddressManager: Contract) => {
const l1GatewayInterface = getContractInterface('OVM_L1ETHGateway')
const ProxyGatewayAddress = await AddressManager.getAddress(
'Proxy__OVM_L1ETHGateway'
// Gets the bridge contract
export const getL1Bridge = async (wallet: Wallet, AddressManager: Contract) => {
const l1BridgeInterface = getContractInterface('OVM_L1StandardBridge')
const ProxyBridgeAddress = await AddressManager.getAddress(
'Proxy__OVM_L1StandardBridge'
)
const addressToUse =
ProxyGatewayAddress !== constants.AddressZero
? ProxyGatewayAddress
: await AddressManager.getAddress('OVM_L1ETHGateway')
const OVM_L1ETHGateway = new Contract(
addressToUse,
l1GatewayInterface,
if (
!utils.isAddress(ProxyBridgeAddress) ||
ProxyBridgeAddress === constants.AddressZero
) {
throw new Error('Proxy__OVM_L1StandardBridge not found')
}
const OVM_L1StandardBridge = new Contract(
ProxyBridgeAddress,
l1BridgeInterface,
wallet
)
return OVM_L1StandardBridge
}
export const getL2Bridge = async (wallet: Wallet) => {
const L2BridgeInterface = getContractInterface('OVM_L2StandardBridge')
return OVM_L1ETHGateway
const OVM_L2StandardBridge = new Contract(
predeploys.OVM_L2StandardBridge,
L2BridgeInterface,
wallet
)
return OVM_L2StandardBridge
}
export const getOvmEth = (wallet: Wallet) => {
......@@ -99,14 +115,15 @@ export const getOvmEth = (wallet: Wallet) => {
export const fundUser = async (
watcher: Watcher,
gateway: Contract,
bridge: Contract,
amount: BigNumberish,
recipient?: string
) => {
const value = BigNumber.from(amount)
const tx = recipient
? gateway.depositTo(recipient, { value })
: gateway.deposit({ value })
? bridge.depositETHTo(recipient, 1_300_000, '0x', { value })
: bridge.depositETH(1_300_000, '0x', { value })
await waitForXDomainTransaction(watcher, tx, Direction.L1ToL2)
}
......@@ -117,6 +134,46 @@ export const encodeSolidityRevertMessage = (_reason: string): string => {
return '0x08c379a0' + remove0x(abiCoder.encode(['string'], [_reason]))
}
export const DEFAULT_TRANSACTION = {
to: '0x' + '1234'.repeat(10),
gasLimit: 33600000000001,
gasPrice: 0,
data: '0x',
value: 0,
}
export const expectApprox = (
actual: BigNumber | number,
target: BigNumber | number,
upperDeviation: number,
lowerDeviation: number = 100
) => {
actual = BigNumber.from(actual)
target = BigNumber.from(target)
const validDeviations =
upperDeviation >= 0 &&
upperDeviation <= 100 &&
lowerDeviation >= 0 &&
lowerDeviation <= 100
if (!validDeviations) {
throw new Error(
'Upper and lower deviation percentage arguments should be between 0 and 100'
)
}
const upper = target.mul(100 + upperDeviation).div(100)
const lower = target.mul(100 - lowerDeviation).div(100)
expect(
actual.lte(upper),
`Actual value is more than ${upperDeviation}% greater than target`
).to.be.true
expect(
actual.gte(lower),
`Actual value is more than ${lowerDeviation}% less than target`
).to.be.true
}
export const waitForL2Geth = async (
provider: providers.JsonRpcProvider
): Promise<providers.JsonRpcProvider> => {
......
......@@ -48,8 +48,9 @@ $ USING_OVM=true ./build/bin/geth \
--rollup.pollinterval 3s \
--eth1.networkid $LAYER1_NETWORK_ID \
--eth1.chainid $LAYER1_CHAIN_ID \
--eth1.l1gatewayaddress $ETH1_L1_GATEWAY_ADDRESS \
--eth1.l1standardbridgeaddress $ETH1_L1_STANDARD_BRIDGE_ADDRESS \
--eth1.l1crossdomainmessengeraddress $ETH1_L1_CROSS_DOMAIN_MESSENGER_ADDRESS \
--eth1.l1feewalletaddress $ETH1_L1_FEE_WALLET_ADDRESS \
--eth1.addressresolveraddress $ETH1_ADDRESS_RESOLVER_ADDRESS \
--eth1.ctcdeploymentheight $CTC_DEPLOY_HEIGHT \
--eth1.syncservice \
......
......@@ -601,10 +601,9 @@ func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
func (m callmsg) Data() []byte { return m.CallMsg.Data }
func (m callmsg) L1MessageSender() *common.Address { return m.CallMsg.L1MessageSender }
func (m callmsg) L1BlockNumber() *big.Int { return m.CallMsg.L1BlockNumber }
func (m callmsg) QueueOrigin() *big.Int { return m.CallMsg.QueueOrigin }
func (m callmsg) SignatureHashType() types.SignatureHashType { return m.CallMsg.SignatureHashType }
func (m callmsg) L1MessageSender() *common.Address { return m.CallMsg.L1MessageSender }
func (m callmsg) L1BlockNumber() *big.Int { return m.CallMsg.L1BlockNumber }
func (m callmsg) QueueOrigin() types.QueueOrigin { return m.CallMsg.QueueOrigin }
// filterBackend implements filters.Backend to support filtering for logs without
// taking bloom-bits acceleration structures into account.
......
......@@ -153,7 +153,8 @@ var (
utils.Eth1SyncServiceEnable,
utils.Eth1CanonicalTransactionChainDeployHeightFlag,
utils.Eth1L1CrossDomainMessengerAddressFlag,
utils.Eth1ETHGatewayAddressFlag,
utils.Eth1L1FeeWalletAddressFlag,
utils.Eth1StandardBridgeAddressFlag,
utils.Eth1ChainIdFlag,
utils.RollupClientHttpFlag,
utils.RollupEnableVerifierFlag,
......@@ -163,12 +164,9 @@ var (
utils.RollupStateDumpPathFlag,
utils.RollupDiffDbFlag,
utils.RollupMaxCalldataSizeFlag,
utils.RollupDataPriceFlag,
utils.RollupExecutionPriceFlag,
utils.RollupBackendFlag,
utils.RollupEnableL2GasPollingFlag,
utils.RollupGasPriceOracleAddressFlag,
utils.RollupEnforceFeesFlag,
utils.GasPriceOracleOwnerAddress,
}
rpcFlags = []cli.Flag{
......
......@@ -68,7 +68,8 @@ var AppHelpFlagGroups = []flagGroup{
utils.Eth1SyncServiceEnable,
utils.Eth1CanonicalTransactionChainDeployHeightFlag,
utils.Eth1L1CrossDomainMessengerAddressFlag,
utils.Eth1ETHGatewayAddressFlag,
utils.Eth1L1FeeWalletAddressFlag,
utils.Eth1StandardBridgeAddressFlag,
utils.Eth1ChainIdFlag,
utils.RollupClientHttpFlag,
utils.RollupAddressManagerOwnerAddressFlag,
......@@ -78,12 +79,9 @@ var AppHelpFlagGroups = []flagGroup{
utils.RollupStateDumpPathFlag,
utils.RollupDiffDbFlag,
utils.RollupMaxCalldataSizeFlag,
utils.RollupDataPriceFlag,
utils.RollupExecutionPriceFlag,
utils.RollupBackendFlag,
utils.RollupEnableL2GasPollingFlag,
utils.RollupGasPriceOracleAddressFlag,
utils.RollupEnforceFeesFlag,
utils.GasPriceOracleOwnerAddress,
},
},
{
......
......@@ -823,11 +823,17 @@ var (
Value: "0x0000000000000000000000000000000000000000",
EnvVar: "ETH1_L1_CROSS_DOMAIN_MESSENGER_ADDRESS",
}
Eth1ETHGatewayAddressFlag = cli.StringFlag{
Name: "eth1.l1ethgatewayaddress",
Usage: "Deployment address of the Ethereum gateway",
Eth1L1FeeWalletAddressFlag = cli.StringFlag{
Name: "eth1.l1feewalletaddress",
Usage: "Address of the L1 wallet that will collect fees",
Value: "0x0000000000000000000000000000000000000000",
EnvVar: "ETH1_L1_ETH_GATEWAY_ADDRESS",
EnvVar: "ETH1_L1_FEE_WALLET_ADDRESS",
}
Eth1StandardBridgeAddressFlag = cli.StringFlag{
Name: "eth1.l1standardbridgeaddress",
Usage: "Deployment address of the Standard Bridge",
Value: "0x0000000000000000000000000000000000000000",
EnvVar: "ETH1_L1_STANDARD_BRIDGE_ADDRESS",
}
Eth1ChainIdFlag = cli.Uint64Flag{
Name: "eth1.chainid",
......@@ -858,7 +864,6 @@ var (
Value: "l1",
EnvVar: "ROLLUP_BACKEND",
}
// Flag to enable verifier mode
RollupEnableVerifierFlag = cli.BoolFlag{
Name: "rollup.verifier",
Usage: "Enable the verifier",
......@@ -888,34 +893,16 @@ var (
Value: eth.DefaultConfig.Rollup.MaxCallDataSize,
EnvVar: "ROLLUP_MAX_CALLDATA_SIZE",
}
RollupDataPriceFlag = BigFlag{
Name: "rollup.dataprice",
Usage: "The L1 calldata price to use for the sequencer fees",
Value: eth.DefaultConfig.Rollup.DataPrice,
EnvVar: "ROLLUP_DATAPRICE",
}
RollupExecutionPriceFlag = BigFlag{
Name: "rollup.executionprice",
Usage: "The execution gas price to use for the sequencer fees",
Value: eth.DefaultConfig.Rollup.ExecutionPrice,
EnvVar: "ROLLUP_EXECUTIONPRICE",
}
RollupGasPriceOracleAddressFlag = cli.StringFlag{
Name: "rollup.gaspriceoracleaddress",
Usage: "Address of the rollup gas price oracle",
Value: "0x0000000000000000000000000000000000000000",
EnvVar: "ROLLUP_GAS_PRICE_ORACLE_ADDRESS",
}
RollupEnableL2GasPollingFlag = cli.BoolFlag{
Name: "rollup.enablel2gaspolling",
Usage: "Poll for the L2 gas price from the L2 state",
EnvVar: "ROLLUP_ENABLE_L2_GAS_POLLING",
}
RollupEnforceFeesFlag = cli.BoolFlag{
Name: "rollup.enforcefeesflag",
Usage: "Disable transactions with 0 gas price",
EnvVar: "ROLLUP_ENFORCE_FEES",
}
GasPriceOracleOwnerAddress = cli.StringFlag{
Name: "rollup.gaspriceoracleowneraddress",
Usage: "Owner of the OVM_GasPriceOracle",
EnvVar: "ROLLUP_GAS_PRICE_ORACLE_OWNER_ADDRESS",
}
)
// MakeDataDir retrieves the currently requested data directory, terminating
......@@ -1149,9 +1136,13 @@ func setEth1(ctx *cli.Context, cfg *rollup.Config) {
addr := ctx.GlobalString(Eth1L1CrossDomainMessengerAddressFlag.Name)
cfg.L1CrossDomainMessengerAddress = common.HexToAddress(addr)
}
if ctx.GlobalIsSet(Eth1ETHGatewayAddressFlag.Name) {
addr := ctx.GlobalString(Eth1ETHGatewayAddressFlag.Name)
cfg.L1ETHGatewayAddress = common.HexToAddress(addr)
if ctx.GlobalIsSet(Eth1L1FeeWalletAddressFlag.Name) {
addr := ctx.GlobalString(Eth1L1FeeWalletAddressFlag.Name)
cfg.L1FeeWalletAddress = common.HexToAddress(addr)
}
if ctx.GlobalIsSet(Eth1StandardBridgeAddressFlag.Name) {
addr := ctx.GlobalString(Eth1StandardBridgeAddressFlag.Name)
cfg.L1StandardBridgeAddress = common.HexToAddress(addr)
}
if ctx.GlobalIsSet(Eth1ChainIdFlag.Name) {
cfg.Eth1ChainId = ctx.GlobalUint64(Eth1ChainIdFlag.Name)
......@@ -1189,11 +1180,9 @@ func setRollup(ctx *cli.Context, cfg *rollup.Config) {
if ctx.GlobalIsSet(RollupTimstampRefreshFlag.Name) {
cfg.TimestampRefreshThreshold = ctx.GlobalDuration(RollupTimstampRefreshFlag.Name)
}
if ctx.GlobalIsSet(RollupDataPriceFlag.Name) {
cfg.DataPrice = GlobalBig(ctx, RollupDataPriceFlag.Name)
}
if ctx.GlobalIsSet(RollupExecutionPriceFlag.Name) {
cfg.ExecutionPrice = GlobalBig(ctx, RollupExecutionPriceFlag.Name)
if ctx.GlobalIsSet(GasPriceOracleOwnerAddress.Name) {
addr := ctx.GlobalString(GasPriceOracleOwnerAddress.Name)
cfg.GasPriceOracleOwnerAddress = common.HexToAddress(addr)
}
if ctx.GlobalIsSet(RollupBackendFlag.Name) {
val := ctx.GlobalString(RollupBackendFlag.Name)
......@@ -1204,13 +1193,6 @@ func setRollup(ctx *cli.Context, cfg *rollup.Config) {
}
cfg.Backend = backend
}
if ctx.GlobalIsSet(RollupGasPriceOracleAddressFlag.Name) {
addr := ctx.GlobalString(RollupGasPriceOracleAddressFlag.Name)
cfg.GasPriceOracleAddress = common.HexToAddress(addr)
}
if ctx.GlobalIsSet(RollupEnableL2GasPollingFlag.Name) {
cfg.EnableL2GasPolling = true
}
if ctx.GlobalIsSet(RollupEnforceFeesFlag.Name) {
cfg.EnforceFees = true
}
......@@ -1778,10 +1760,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
gasLimit = params.GenesisGasLimit
}
xdomainAddress := cfg.Rollup.L1CrossDomainMessengerAddress
l1FeeWalletAddress := cfg.Rollup.L1FeeWalletAddress
addrManagerOwnerAddress := cfg.Rollup.AddressManagerOwnerAddress
l1ETHGatewayAddress := cfg.Rollup.L1ETHGatewayAddress
l1StandardBridgeAddress := cfg.Rollup.L1StandardBridgeAddress
gpoOwnerAddress := cfg.Rollup.GasPriceOracleOwnerAddress
stateDumpPath := cfg.Rollup.StateDumpPath
cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.GlobalInt(DeveloperPeriodFlag.Name)), developer.Address, xdomainAddress, l1ETHGatewayAddress, addrManagerOwnerAddress, stateDumpPath, chainID, gasLimit)
cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.GlobalInt(DeveloperPeriodFlag.Name)), developer.Address, xdomainAddress, l1StandardBridgeAddress, addrManagerOwnerAddress, gpoOwnerAddress, l1FeeWalletAddress, stateDumpPath, chainID, gasLimit)
if !ctx.GlobalIsSet(MinerGasPriceFlag.Name) && !ctx.GlobalIsSet(MinerLegacyGasPriceFlag.Name) {
cfg.Miner.GasPrice = big.NewInt(1)
}
......
......@@ -98,7 +98,7 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester {
t.Fatalf("failed to create node: %v", err)
}
ethConf := &eth.Config{
Genesis: core.DeveloperGenesisBlock(15, common.Address{}, common.Address{}, common.Address{}, common.Address{}, "", nil, 12000000),
Genesis: core.DeveloperGenesisBlock(15, common.Address{}, common.Address{}, common.Address{}, common.Address{}, common.Address{}, common.Address{}, "", nil, 12000000),
Miner: miner.Config{
Etherbase: common.HexToAddress(testAddress),
},
......
......@@ -69,9 +69,11 @@ type Genesis struct {
// OVM Specific, used to initialize the l1XDomainMessengerAddress
// in the genesis state
L1FeeWalletAddress common.Address `json:"-"`
L1CrossDomainMessengerAddress common.Address `json:"-"`
AddressManagerOwnerAddress common.Address `json:"-"`
L1ETHGatewayAddress common.Address `json:"-"`
GasPriceOracleOwnerAddress common.Address `json:"-"`
L1StandardBridgeAddress common.Address `json:"-"`
ChainID *big.Int `json:"-"`
}
......@@ -266,7 +268,7 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
}
// ApplyOvmStateToState applies the initial OVM state to a state object.
func ApplyOvmStateToState(statedb *state.StateDB, stateDump *dump.OvmDump, l1XDomainMessengerAddress common.Address, l1ETHGatewayAddress common.Address, addrManagerOwnerAddress common.Address, chainID *big.Int, gasLimit uint64) {
func ApplyOvmStateToState(statedb *state.StateDB, stateDump *dump.OvmDump, l1XDomainMessengerAddress, l1StandardBridgeAddress, addrManagerOwnerAddress, gpoOwnerAddress, l1FeeWalletAddress common.Address, chainID *big.Int, gasLimit uint64) {
if len(stateDump.Accounts) == 0 {
return
}
......@@ -299,22 +301,13 @@ func ApplyOvmStateToState(statedb *state.StateDB, stateDump *dump.OvmDump, l1XDo
l1MessengerValue := common.BytesToHash(l1XDomainMessengerAddress.Bytes())
statedb.SetState(AddressManager.Address, l1MessengerSlot, l1MessengerValue)
}
OVM_ETH, ok := stateDump.Accounts["OVM_ETH"]
OVM_L2StandardBridge, ok := stateDump.Accounts["OVM_L2StandardBridge"]
if ok {
log.Info("Setting OVM_L1ETHGateway in OVM_ETH", "address", l1ETHGatewayAddress.Hex())
if strings.Contains(OVM_ETH.Code, "a84ce98") {
// Set the gateway of OVM_ETH at new dump
log.Info("Detected current OVM_ETH dump, setting slot 0x1 ")
l1GatewaySlot := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001")
l1GatewayValue := common.BytesToHash(l1ETHGatewayAddress.Bytes())
statedb.SetState(OVM_ETH.Address, l1GatewaySlot, l1GatewayValue)
} else {
// Set the gateway of OVM_ETH at legacy slot
log.Info("Detected legacy OVM_ETH dump, setting slot 0x8")
l1GatewaySlot := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000008")
l1GatewayValue := common.BytesToHash(l1ETHGatewayAddress.Bytes())
statedb.SetState(OVM_ETH.Address, l1GatewaySlot, l1GatewayValue)
}
log.Info("Setting OVM_L1StandardBridge in OVM_L2StandardBridge", "address", l1StandardBridgeAddress.Hex())
// Set the gateway of OVM_L2StandardBridge at new dump
l1BridgeSlot := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001")
l1BridgeValue := common.BytesToHash(l1StandardBridgeAddress.Bytes())
statedb.SetState(OVM_L2StandardBridge.Address, l1BridgeSlot, l1BridgeValue)
}
ExecutionManager, ok := stateDump.Accounts["OVM_ExecutionManager"]
if ok {
......@@ -330,6 +323,20 @@ func ApplyOvmStateToState(statedb *state.StateDB, stateDump *dump.OvmDump, l1XDo
maxTxGasLimitValue := common.BytesToHash(new(big.Int).SetUint64(gasLimit).Bytes())
statedb.SetState(ExecutionManager.Address, maxTxGasLimitSlot, maxTxGasLimitValue)
}
OVM_SequencerFeeVault, ok := stateDump.Accounts["OVM_SequencerFeeVault"]
if ok {
log.Info("Setting l1FeeWallet in OVM_SequencerFeeVault", "wallet", l1FeeWalletAddress.Hex())
l1FeeWalletSlot := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000")
l1FeeWalletValue := common.BytesToHash(l1FeeWalletAddress.Bytes())
statedb.SetState(OVM_SequencerFeeVault.Address, l1FeeWalletSlot, l1FeeWalletValue)
GasPriceOracle, ok := stateDump.Accounts["OVM_GasPriceOracle"]
if ok {
ownerSlot := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000")
ownerValue := common.BytesToHash(gpoOwnerAddress.Bytes())
statedb.SetState(GasPriceOracle.Address, ownerSlot, ownerValue)
log.Info("Setting GasPriceOracle Owner", "owner", gpoOwnerAddress.Hex())
}
}
}
// ToBlock creates the genesis block and writes state of a genesis specification
......@@ -342,7 +349,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
if vm.UsingOVM {
// OVM_ENABLED
ApplyOvmStateToState(statedb, g.Config.StateDump, g.L1CrossDomainMessengerAddress, g.L1ETHGatewayAddress, g.AddressManagerOwnerAddress, g.ChainID, g.GasLimit)
ApplyOvmStateToState(statedb, g.Config.StateDump, g.L1CrossDomainMessengerAddress, g.L1StandardBridgeAddress, g.AddressManagerOwnerAddress, g.GasPriceOracleOwnerAddress, g.L1FeeWalletAddress, g.ChainID, g.GasLimit)
}
for addr, account := range g.Alloc {
......@@ -469,7 +476,7 @@ func DefaultGoerliGenesisBlock() *Genesis {
}
// DeveloperGenesisBlock returns the 'geth --dev' genesis block.
func DeveloperGenesisBlock(period uint64, faucet, l1XDomainMessengerAddress common.Address, l1ETHGatewayAddress common.Address, addrManagerOwnerAddress common.Address, stateDumpPath string, chainID *big.Int, gasLimit uint64) *Genesis {
func DeveloperGenesisBlock(period uint64, faucet, l1XDomainMessengerAddress common.Address, l1StandardBridgeAddress common.Address, addrManagerOwnerAddress, gpoOwnerAddress, l1FeeWalletAddress common.Address, stateDumpPath string, chainID *big.Int, gasLimit uint64) *Genesis {
// Override the default period to the user requested one
config := *params.AllCliqueProtocolChanges
config.Clique.Period = period
......@@ -525,8 +532,10 @@ func DeveloperGenesisBlock(period uint64, faucet, l1XDomainMessengerAddress comm
common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
},
L1CrossDomainMessengerAddress: l1XDomainMessengerAddress,
L1FeeWalletAddress: l1FeeWalletAddress,
AddressManagerOwnerAddress: addrManagerOwnerAddress,
L1ETHGatewayAddress: l1ETHGatewayAddress,
GasPriceOracleOwnerAddress: gpoOwnerAddress,
L1StandardBridgeAddress: l1StandardBridgeAddress,
ChainID: config.ChainID,
}
}
......
......@@ -430,7 +430,7 @@ func TestBlockMetaStorage(t *testing.T) {
index1 := uint64(1)
tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
tx1Meta := types.NewTransactionMeta(nil, 0, nil, types.SighashEIP155, types.QueueOriginSequencer, &index1, nil, nil)
tx1Meta := types.NewTransactionMeta(nil, 0, nil, types.QueueOriginSequencer, &index1, nil, nil)
tx1.SetTransactionMeta(tx1Meta)
WriteTransactionMeta(db, index1, tx1.GetMeta())
......@@ -442,9 +442,6 @@ func TestBlockMetaStorage(t *testing.T) {
if meta.L1BlockNumber != nil {
t.Fatalf("Could not recover L1BlockNumber")
}
if meta.SignatureHashType != types.SighashEIP155 {
t.Fatalf("Could not recover sighash type")
}
if meta.Index == nil {
t.Fatalf("Could not recover index")
}
......@@ -464,7 +461,7 @@ func TestBlockMetaStorage(t *testing.T) {
index2 := uint64(2)
tx2 := types.NewTransaction(2, common.HexToAddress("0x02"), big.NewInt(2), 2, big.NewInt(2), nil)
tx2Meta := types.NewTransactionMeta(l1BlockNumber, 0, &addr, types.SighashEthSign, types.QueueOriginSequencer, nil, nil, nil)
tx2Meta := types.NewTransactionMeta(l1BlockNumber, 0, &addr, types.QueueOriginSequencer, nil, nil, nil)
tx2.SetTransactionMeta(tx2Meta)
WriteTransactionMeta(db, index2, tx2.GetMeta())
......@@ -477,7 +474,4 @@ func TestBlockMetaStorage(t *testing.T) {
if meta2.L1BlockNumber.Cmp(l1BlockNumber) != 0 {
t.Fatalf("Could not recover L1BlockNumber")
}
if meta2.SignatureHashType != types.SighashEthSign {
t.Fatalf("Could not recover sighash type")
}
}
......@@ -73,15 +73,15 @@ func TestLookupStorage(t *testing.T) {
l1BlockNumber2 := big.NewInt(2)
tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
tx1Meta := types.NewTransactionMeta(l1BlockNumber1, 0, &sender1, types.SighashEIP155, types.QueueOriginSequencer, nil, nil, nil)
tx1Meta := types.NewTransactionMeta(l1BlockNumber1, 0, &sender1, types.QueueOriginSequencer, nil, nil, nil)
tx1.SetTransactionMeta(tx1Meta)
tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
tx2Meta := types.NewTransactionMeta(l1BlockNumber2, 0, &sender2, types.SighashEIP155, types.QueueOriginSequencer, nil, nil, nil)
tx2Meta := types.NewTransactionMeta(l1BlockNumber2, 0, &sender2, types.QueueOriginSequencer, nil, nil, nil)
tx2.SetTransactionMeta(tx2Meta)
tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
tx3Meta := types.NewTransactionMeta(l1BlockNumber1, 0, nil, types.SighashEIP155, types.QueueOriginSequencer, nil, nil, nil)
tx3Meta := types.NewTransactionMeta(l1BlockNumber1, 0, nil, types.QueueOriginSequencer, nil, nil, nil)
tx3.SetTransactionMeta(tx3Meta)
txs := []*types.Transaction{tx1, tx2, tx3}
......
......@@ -269,7 +269,7 @@ func (s *StateDB) GetBalance(addr common.Address) *big.Int {
func (s *StateDB) GetOVMBalance(addr common.Address) *big.Int {
eth := common.HexToAddress("0x4200000000000000000000000000000000000006")
position := big.NewInt(5)
position := big.NewInt(0)
hasher := sha3.NewLegacyKeccak256()
hasher.Write(common.LeftPadBytes(addr.Bytes(), 32))
hasher.Write(common.LeftPadBytes(position.Bytes(), 32))
......
......@@ -77,8 +77,7 @@ type Message interface {
Data() []byte
L1MessageSender() *common.Address
L1BlockNumber() *big.Int
QueueOrigin() *big.Int
SignatureHashType() types.SignatureHashType
QueueOrigin() types.QueueOrigin
}
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
......@@ -185,9 +184,7 @@ func (st *StateTransition) preCheck() error {
if nonce < st.msg.Nonce() {
if vm.UsingOVM {
// The nonce never increments for L1ToL2 txs
qo := st.msg.QueueOrigin()
l1ToL2 := uint64(types.QueueOriginL1ToL2)
if qo != nil && qo.Uint64() == l1ToL2 {
if st.msg.QueueOrigin() == types.QueueOriginL1ToL2 {
return st.buyGas()
}
}
......@@ -283,6 +280,9 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
}
func (st *StateTransition) refundGas() {
if vm.UsingOVM {
return
}
// Apply refund counter, capped to half of the used gas.
refund := st.gasUsed() / 2
if refund > st.state.GetRefund() {
......
......@@ -26,7 +26,7 @@ func toExecutionManagerRun(evm *vm.EVM, msg Message) (Message, error) {
tx := ovmTransaction{
evm.Context.Time,
msg.L1BlockNumber(),
uint8(msg.QueueOrigin().Uint64()),
uint8(msg.QueueOrigin()),
*msg.L1MessageSender(),
*msg.To(),
big.NewInt(int64(msg.Gas())),
......@@ -73,8 +73,7 @@ func AsOvmMessage(tx *types.Transaction, signer types.Signer, decompressor commo
// sequencer entrypoint. The calldata is expected to be in the
// correct format when deserialized from the EVM events, see
// rollup/sync_service.go.
qo := msg.QueueOrigin()
if qo != nil && qo.Uint64() == uint64(types.QueueOriginL1ToL2) {
if msg.QueueOrigin() == types.QueueOriginL1ToL2 {
return msg, nil
}
......@@ -101,13 +100,18 @@ func EncodeSimulatedMessage(msg Message, timestamp, blockNumber *big.Int, execut
to = &common.Address{0}
}
value := msg.Value()
if value == nil {
value = common.Big0
}
tx := ovmTransaction{
timestamp,
blockNumber,
uint8(msg.QueueOrigin().Uint64()),
uint8(msg.QueueOrigin()),
*msg.L1MessageSender(),
*to,
big.NewInt(int64(msg.Gas())),
new(big.Int).SetUint64(msg.Gas()),
msg.Data(),
}
......@@ -115,6 +119,7 @@ func EncodeSimulatedMessage(msg Message, timestamp, blockNumber *big.Int, execut
var args = []interface{}{
tx,
from,
value,
stateManager.Address,
}
......@@ -139,11 +144,6 @@ func modMessage(
data []byte,
gasLimit uint64,
) (Message, error) {
queueOrigin, err := getQueueOrigin(msg.QueueOrigin())
if err != nil {
return nil, err
}
outmsg := types.NewMessage(
from,
to,
......@@ -155,23 +155,8 @@ func modMessage(
false,
msg.L1MessageSender(),
msg.L1BlockNumber(),
queueOrigin,
msg.SignatureHashType(),
msg.QueueOrigin(),
)
return outmsg, nil
}
func getQueueOrigin(
queueOrigin *big.Int,
) (types.QueueOrigin, error) {
if queueOrigin.Cmp(big.NewInt(0)) == 0 {
return types.QueueOriginSequencer, nil
} else if queueOrigin.Cmp(big.NewInt(1)) == 0 {
return types.QueueOriginL1ToL2, nil
} else if queueOrigin.Cmp(big.NewInt(2)) == 0 {
return types.QueueOriginL1ToL2, nil
} else {
return types.QueueOriginSequencer, fmt.Errorf("invalid queue origin: %d", queueOrigin)
}
}
......@@ -13,8 +13,8 @@ import (
var _ = (*txdataMarshaling)(nil)
// TransactionMarshalJSON marshals as JSON.
func (t txdata) TransactionMarshalJSON() ([]byte, error) {
// MarshalJSON marshals as JSON.
func (t txdata) MarshalJSON() ([]byte, error) {
type txdata struct {
AccountNonce hexutil.Uint64 `json:"nonce" gencodec:"required"`
Price *hexutil.Big `json:"gasPrice" gencodec:"required"`
......@@ -41,8 +41,8 @@ func (t txdata) TransactionMarshalJSON() ([]byte, error) {
return json.Marshal(&enc)
}
// TransactionUnmarshalJSON unmarshals from JSON.
func (t *txdata) TransactionUnmarshalJSON(input []byte) error {
// UnmarshalJSON unmarshals from JSON.
func (t *txdata) UnmarshalJSON(input []byte) error {
type txdata struct {
AccountNonce *hexutil.Uint64 `json:"nonce" gencodec:"required"`
Price *hexutil.Big `json:"gasPrice" gencodec:"required"`
......
......@@ -13,29 +13,35 @@ import (
// MarshalJSON marshals as JSON.
func (t TransactionMeta) MarshalJSON() ([]byte, error) {
type TransactionMeta struct {
L1BlockNumber *big.Int `json:"l1BlockNumber"`
L1MessageSender *common.Address `json:"l1MessageSender" gencodec:"required"`
SignatureHashType SignatureHashType `json:"signatureHashType" gencodec:"required"`
QueueOrigin *big.Int `json:"queueOrigin" gencodec:"required"`
Index *uint64 `json:"index" gencodec:"required"`
L1BlockNumber *big.Int `json:"l1BlockNumber"`
L1Timestamp uint64 `json:"l1Timestamp"`
L1MessageSender *common.Address `json:"l1MessageSender" gencodec:"required"`
QueueOrigin QueueOrigin `json:"queueOrigin" gencodec:"required"`
Index *uint64 `json:"index" gencodec:"required"`
QueueIndex *uint64 `json:"queueIndex" gencodec:"required"`
RawTransaction []byte `json:"rawTransaction" gencodec:"required"`
}
var enc TransactionMeta
enc.L1BlockNumber = t.L1BlockNumber
enc.L1Timestamp = t.L1Timestamp
enc.L1MessageSender = t.L1MessageSender
enc.SignatureHashType = t.SignatureHashType
enc.QueueOrigin = t.QueueOrigin
enc.Index = t.Index
enc.QueueIndex = t.QueueIndex
enc.RawTransaction = t.RawTransaction
return json.Marshal(&enc)
}
// UnmarshalJSON unmarshals from JSON.
func (t *TransactionMeta) UnmarshalJSON(input []byte) error {
type TransactionMeta struct {
L1BlockNumber *big.Int `json:"l1BlockNumber"`
L1MessageSender *common.Address `json:"l1MessageSender" gencodec:"required"`
SignatureHashType *SignatureHashType `json:"signatureHashType" gencodec:"required"`
QueueOrigin *big.Int `json:"queueOrigin" gencodec:"required"`
Index *uint64 `json:"index" gencodec:"required"`
L1BlockNumber *big.Int `json:"l1BlockNumber"`
L1Timestamp *uint64 `json:"l1Timestamp"`
L1MessageSender *common.Address `json:"l1MessageSender" gencodec:"required"`
QueueOrigin *QueueOrigin `json:"queueOrigin" gencodec:"required"`
Index *uint64 `json:"index" gencodec:"required"`
QueueIndex *uint64 `json:"queueIndex" gencodec:"required"`
RawTransaction []byte `json:"rawTransaction" gencodec:"required"`
}
var dec TransactionMeta
if err := json.Unmarshal(input, &dec); err != nil {
......@@ -44,21 +50,28 @@ func (t *TransactionMeta) UnmarshalJSON(input []byte) error {
if dec.L1BlockNumber != nil {
t.L1BlockNumber = dec.L1BlockNumber
}
if dec.L1Timestamp != nil {
t.L1Timestamp = *dec.L1Timestamp
}
if dec.L1MessageSender == nil {
return errors.New("missing required field 'l1MessageSender' for TransactionMeta")
}
t.L1MessageSender = dec.L1MessageSender
if dec.SignatureHashType == nil {
return errors.New("missing required field 'signatureHashType' for TransactionMeta")
}
t.SignatureHashType = *dec.SignatureHashType
if dec.QueueOrigin == nil {
return errors.New("missing required field 'queueOrigin' for TransactionMeta")
}
t.QueueOrigin = dec.QueueOrigin
t.QueueOrigin = *dec.QueueOrigin
if dec.Index == nil {
return errors.New("missing required field 'index' for TransactionMeta")
}
t.Index = dec.Index
if dec.QueueIndex == nil {
return errors.New("missing required field 'queueIndex' for TransactionMeta")
}
t.QueueIndex = dec.QueueIndex
if dec.RawTransaction == nil {
return errors.New("missing required field 'rawTransaction' for TransactionMeta")
}
t.RawTransaction = dec.RawTransaction
return nil
}
......@@ -36,15 +36,6 @@ var (
ErrInvalidSig = errors.New("invalid transaction v, r, s values")
)
// TODO(mark): migrate from sighash type to type
type SignatureHashType uint8
const (
SighashEIP155 SignatureHashType = 0
SighashEthSign SignatureHashType = 1
CreateEOA SignatureHashType = 2
)
type Transaction struct {
data txdata
meta TransactionMeta
......@@ -86,7 +77,6 @@ func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit u
return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data)
}
// TODO: cannot deploy contracts with SighashEthSign right until SighashEIP155 is no longer hardcoded
func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data)
}
......@@ -96,7 +86,7 @@ func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit
data = common.CopyBytes(data)
}
meta := NewTransactionMeta(nil, 0, nil, SighashEIP155, QueueOriginSequencer, nil, nil, nil)
meta := NewTransactionMeta(nil, 0, nil, QueueOriginSequencer, nil, nil, nil)
d := txdata{
AccountNonce: nonce,
......@@ -197,12 +187,12 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
// MarshalJSON encodes the web3 RPC transaction format.
func (tx *Transaction) MarshalJSON() ([]byte, error) {
return tx.data.TransactionMarshalJSON()
return tx.data.MarshalJSON()
}
// UnmarshalJSON decodes the web3 RPC transaction format.
func (tx *Transaction) UnmarshalJSON(input []byte) error {
err := tx.data.TransactionUnmarshalJSON(input)
err := tx.data.UnmarshalJSON(input)
if err != nil {
return err
}
......@@ -232,14 +222,7 @@ func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amo
func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce }
func (tx *Transaction) CheckNonce() bool { return true }
func (tx *Transaction) SetNonce(nonce uint64) { tx.data.AccountNonce = nonce }
func (tx *Transaction) SignatureHashType() SignatureHashType { return tx.meta.SignatureHashType }
func (tx *Transaction) SetSignatureHashType(sighashType SignatureHashType) {
tx.meta.SignatureHashType = sighashType
}
func (tx *Transaction) IsEthSignSighash() bool {
return tx.SignatureHashType() == SighashEthSign
}
func (tx *Transaction) SetNonce(nonce uint64) { tx.data.AccountNonce = nonce }
// To returns the recipient address of the transaction.
// It returns nil if the transaction is a contract creation.
......@@ -271,13 +254,9 @@ func (tx *Transaction) L1BlockNumber() *big.Int {
return &l1BlockNumber
}
// QueueOrigin returns the Queue Origin of the transaction if it exists.
func (tx *Transaction) QueueOrigin() *big.Int {
if tx.meta.QueueOrigin == nil {
return nil
}
queueOrigin := *tx.meta.QueueOrigin
return &queueOrigin
// QueueOrigin returns the Queue Origin of the transaction
func (tx *Transaction) QueueOrigin() QueueOrigin {
return tx.meta.QueueOrigin
}
// Hash hashes the RLP encoding of tx.
......@@ -319,10 +298,9 @@ func (tx *Transaction) AsMessage(s Signer) (Message, error) {
data: tx.data.Payload,
checkNonce: true,
l1MessageSender: tx.meta.L1MessageSender,
l1BlockNumber: tx.meta.L1BlockNumber,
signatureHashType: tx.meta.SignatureHashType,
queueOrigin: tx.meta.QueueOrigin,
l1MessageSender: tx.meta.L1MessageSender,
l1BlockNumber: tx.meta.L1BlockNumber,
queueOrigin: tx.meta.QueueOrigin,
}
var err error
......@@ -535,13 +513,12 @@ type Message struct {
data []byte
checkNonce bool
l1MessageSender *common.Address
l1BlockNumber *big.Int
signatureHashType SignatureHashType
queueOrigin *big.Int
l1MessageSender *common.Address
l1BlockNumber *big.Int
queueOrigin QueueOrigin
}
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool, l1MessageSender *common.Address, l1BlockNumber *big.Int, queueOrigin QueueOrigin, signatureHashType SignatureHashType) Message {
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool, l1MessageSender *common.Address, l1BlockNumber *big.Int, queueOrigin QueueOrigin) Message {
return Message{
from: from,
to: to,
......@@ -552,10 +529,9 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b
data: data,
checkNonce: checkNonce,
l1BlockNumber: l1BlockNumber,
l1MessageSender: l1MessageSender,
signatureHashType: signatureHashType,
queueOrigin: big.NewInt(int64(queueOrigin)),
l1BlockNumber: l1BlockNumber,
l1MessageSender: l1MessageSender,
queueOrigin: queueOrigin,
}
}
......@@ -568,7 +544,6 @@ func (m Message) Nonce() uint64 { return m.nonce }
func (m Message) Data() []byte { return m.data }
func (m Message) CheckNonce() bool { return m.checkNonce }
func (m Message) L1MessageSender() *common.Address { return m.l1MessageSender }
func (m Message) L1BlockNumber() *big.Int { return m.l1BlockNumber }
func (m Message) SignatureHashType() SignatureHashType { return m.signatureHashType }
func (m Message) QueueOrigin() *big.Int { return m.queueOrigin }
func (m Message) L1MessageSender() *common.Address { return m.l1MessageSender }
func (m Message) L1BlockNumber() *big.Int { return m.l1BlockNumber }
func (m Message) QueueOrigin() QueueOrigin { return m.queueOrigin }
......@@ -12,7 +12,7 @@ import (
"github.com/ethereum/go-ethereum/common"
)
type QueueOrigin int64
type QueueOrigin uint8
const (
// Possible `queue_origin` values
......@@ -20,14 +20,24 @@ const (
QueueOriginL1ToL2 QueueOrigin = 1
)
func (q QueueOrigin) String() string {
switch q {
case QueueOriginSequencer:
return "sequencer"
case QueueOriginL1ToL2:
return "l1"
default:
return ""
}
}
//go:generate gencodec -type TransactionMeta -out gen_tx_meta_json.go
type TransactionMeta struct {
L1BlockNumber *big.Int `json:"l1BlockNumber"`
L1Timestamp uint64 `json:"l1Timestamp"`
L1MessageSender *common.Address `json:"l1MessageSender" gencodec:"required"`
SignatureHashType SignatureHashType `json:"signatureHashType" gencodec:"required"`
QueueOrigin *big.Int `json:"queueOrigin" gencodec:"required"`
L1BlockNumber *big.Int `json:"l1BlockNumber"`
L1Timestamp uint64 `json:"l1Timestamp"`
L1MessageSender *common.Address `json:"l1MessageSender" gencodec:"required"`
QueueOrigin QueueOrigin `json:"queueOrigin" gencodec:"required"`
// The canonical transaction chain index
Index *uint64 `json:"index" gencodec:"required"`
// The queue index, nil for queue origin sequencer transactions
......@@ -36,22 +46,20 @@ type TransactionMeta struct {
}
// NewTransactionMeta creates a TransactionMeta
func NewTransactionMeta(l1BlockNumber *big.Int, l1timestamp uint64, l1MessageSender *common.Address, sighashType SignatureHashType, queueOrigin QueueOrigin, index *uint64, queueIndex *uint64, rawTransaction []byte) *TransactionMeta {
func NewTransactionMeta(l1BlockNumber *big.Int, l1timestamp uint64, l1MessageSender *common.Address, queueOrigin QueueOrigin, index *uint64, queueIndex *uint64, rawTransaction []byte) *TransactionMeta {
return &TransactionMeta{
L1BlockNumber: l1BlockNumber,
L1Timestamp: l1timestamp,
L1MessageSender: l1MessageSender,
SignatureHashType: sighashType,
QueueOrigin: big.NewInt(int64(queueOrigin)),
Index: index,
QueueIndex: queueIndex,
RawTransaction: rawTransaction,
L1BlockNumber: l1BlockNumber,
L1Timestamp: l1timestamp,
L1MessageSender: l1MessageSender,
QueueOrigin: queueOrigin,
Index: index,
QueueIndex: queueIndex,
RawTransaction: rawTransaction,
}
}
// TxMetaDecode deserializes bytes as a TransactionMeta struct.
// The schema is:
// varbytes(SignatureHashType) ||
// varbytes(L1BlockNumber) ||
// varbytes(L1MessageSender) ||
// varbytes(QueueOrigin) ||
......@@ -61,14 +69,6 @@ func TxMetaDecode(input []byte) (*TransactionMeta, error) {
meta := TransactionMeta{}
b := bytes.NewReader(input)
sb, err := common.ReadVarBytes(b, 0, 1024, "SignatureHashType")
if err != nil {
return nil, err
}
var sighashType SignatureHashType
binary.Read(bytes.NewReader(sb), binary.LittleEndian, &sighashType)
meta.SignatureHashType = sighashType
lb, err := common.ReadVarBytes(b, 0, 1024, "l1BlockNumber")
if err != nil {
return nil, err
......@@ -94,7 +94,7 @@ func TxMetaDecode(input []byte) (*TransactionMeta, error) {
}
if !isNullValue(qo) {
queueOrigin := new(big.Int).SetBytes(qo)
meta.QueueOrigin = queueOrigin
meta.QueueOrigin = QueueOrigin(queueOrigin.Uint64())
}
l, err := common.ReadVarBytes(b, 0, 1024, "L1Timestamp")
......@@ -138,10 +138,6 @@ func TxMetaDecode(input []byte) (*TransactionMeta, error) {
func TxMetaEncode(meta *TransactionMeta) []byte {
b := new(bytes.Buffer)
s := new(bytes.Buffer)
binary.Write(s, binary.LittleEndian, &meta.SignatureHashType)
common.WriteVarBytes(b, 0, s.Bytes())
L1BlockNumber := meta.L1BlockNumber
if L1BlockNumber == nil {
common.WriteVarBytes(b, 0, getNullValue())
......@@ -161,13 +157,9 @@ func TxMetaEncode(meta *TransactionMeta) []byte {
}
queueOrigin := meta.QueueOrigin
if queueOrigin == nil {
common.WriteVarBytes(b, 0, getNullValue())
} else {
q := new(bytes.Buffer)
binary.Write(q, binary.LittleEndian, queueOrigin.Bytes())
common.WriteVarBytes(b, 0, q.Bytes())
}
q := new(bytes.Buffer)
binary.Write(q, binary.LittleEndian, queueOrigin)
common.WriteVarBytes(b, 0, q.Bytes())
l := new(bytes.Buffer)
binary.Write(l, binary.LittleEndian, &meta.L1Timestamp)
......
......@@ -17,7 +17,6 @@ var (
l1BlockNumber *big.Int
l1Timestamp uint64
msgSender *common.Address
sighashType SignatureHashType
queueOrigin QueueOrigin
rawTransaction []byte
}{
......@@ -25,7 +24,6 @@ var (
l1BlockNumber: l1BlockNumber,
l1Timestamp: 100,
msgSender: &addr,
sighashType: SighashEthSign,
queueOrigin: QueueOriginL1ToL2,
rawTransaction: []byte{255, 255, 255, 255},
},
......@@ -33,7 +31,6 @@ var (
l1BlockNumber: nil,
l1Timestamp: 45,
msgSender: &addr,
sighashType: SighashEthSign,
queueOrigin: QueueOriginL1ToL2,
rawTransaction: []byte{42, 69, 42, 69},
},
......@@ -41,7 +38,6 @@ var (
l1BlockNumber: l1BlockNumber,
l1Timestamp: 0,
msgSender: nil,
sighashType: SighashEthSign,
queueOrigin: QueueOriginSequencer,
rawTransaction: []byte{0, 0, 0, 0},
},
......@@ -49,7 +45,6 @@ var (
l1BlockNumber: l1BlockNumber,
l1Timestamp: 0,
msgSender: &addr,
sighashType: SighashEthSign,
queueOrigin: QueueOriginSequencer,
rawTransaction: []byte{0, 0, 0, 0},
},
......@@ -57,7 +52,6 @@ var (
l1BlockNumber: nil,
l1Timestamp: 0,
msgSender: nil,
sighashType: SighashEthSign,
queueOrigin: QueueOriginL1ToL2,
rawTransaction: []byte{0, 0, 0, 0},
},
......@@ -65,30 +59,15 @@ var (
l1BlockNumber: l1BlockNumber,
l1Timestamp: 0,
msgSender: &addr,
sighashType: SighashEthSign,
queueOrigin: QueueOriginL1ToL2,
rawTransaction: []byte{0, 0, 0, 0},
},
}
txMetaSighashEncodeTests = []struct {
input SignatureHashType
output SignatureHashType
}{
{
input: SighashEIP155,
output: SighashEIP155,
},
{
input: SighashEthSign,
output: SighashEthSign,
},
}
)
func TestTransactionMetaEncode(t *testing.T) {
for _, test := range txMetaSerializationTests {
txmeta := NewTransactionMeta(test.l1BlockNumber, test.l1Timestamp, test.msgSender, test.sighashType, test.queueOrigin, nil, nil, test.rawTransaction)
txmeta := NewTransactionMeta(test.l1BlockNumber, test.l1Timestamp, test.msgSender, test.queueOrigin, nil, nil, test.rawTransaction)
encoded := TxMetaEncode(txmeta)
decoded, err := TxMetaDecode(encoded)
......@@ -103,22 +82,6 @@ func TestTransactionMetaEncode(t *testing.T) {
}
}
func TestTransactionSighashEncode(t *testing.T) {
for _, test := range txMetaSighashEncodeTests {
txmeta := NewTransactionMeta(l1BlockNumber, 0, &addr, test.input, QueueOriginSequencer, nil, nil, nil)
encoded := TxMetaEncode(txmeta)
decoded, err := TxMetaDecode(encoded)
if err != nil {
t.Fatal(err)
}
if decoded.SignatureHashType != test.output {
t.Fatal("SighashTypes do not match")
}
}
}
func isTxMetaEqual(meta1 *TransactionMeta, meta2 *TransactionMeta) bool {
// Maybe can just return this
if !reflect.DeepEqual(meta1, meta2) {
......@@ -149,18 +112,7 @@ func isTxMetaEqual(meta1 *TransactionMeta, meta2 *TransactionMeta) bool {
}
}
if meta1.SignatureHashType != meta2.SignatureHashType {
return false
}
if meta1.QueueOrigin == nil || meta2.QueueOrigin == nil {
// Note: this only works because it is the final comparison
if meta1.QueueOrigin == nil && meta2.QueueOrigin == nil {
return true
}
}
if !bytes.Equal(meta1.QueueOrigin.Bytes(), meta2.QueueOrigin.Bytes()) {
if meta1.QueueOrigin != meta2.QueueOrigin {
return false
}
......
......@@ -263,8 +263,4 @@ func TestOVMMetaDataHash(t *testing.T) {
if emptyTx.Hash() != emptyTxEmptyL1Sender.Hash() {
t.Errorf("L1MessageSender, should not affect the hash, want %x, got %x with L1MessageSender", emptyTx.Hash(), emptyTxEmptyL1Sender.Hash())
}
if emptyTx.Hash() != emptyTxSighashEthSign.Hash() {
t.Errorf("SignatureHashType, should not affect the hash, want %x, got %x with SighashEthSign", emptyTx.Hash(), emptyTxSighashEthSign.Hash())
}
}
......@@ -200,6 +200,7 @@ type Context struct {
OvmSafetyChecker dump.OvmDumpAccount
OvmL2CrossDomainMessenger dump.OvmDumpAccount
OvmETH dump.OvmDumpAccount
OvmL2StandardBridge dump.OvmDumpAccount
}
// EVM is the Ethereum Virtual Machine base object and provides
......@@ -252,6 +253,7 @@ func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmCon
ctx.OvmSafetyChecker = chainConfig.StateDump.Accounts["OVM_SafetyChecker"]
ctx.OvmL2CrossDomainMessenger = chainConfig.StateDump.Accounts["OVM_L2CrossDomainMessenger"]
ctx.OvmETH = chainConfig.StateDump.Accounts["OVM_ETH"]
ctx.OvmL2StandardBridge = chainConfig.StateDump.Accounts["OVM_L2StandardBridge"]
}
id := make([]byte, 4)
......@@ -404,7 +406,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
if evm.Context.IsL1ToL2Message && evm.depth == 3 {
var isValidMessageTarget = true
// 0x420... addresses are not valid targets except for the ETH predeploy.
if bytes.HasPrefix(addr.Bytes(), fortyTwoPrefix) && addr != evm.Context.OvmETH.Address {
if bytes.HasPrefix(addr.Bytes(), fortyTwoPrefix) && addr != evm.Context.OvmL2StandardBridge.Address {
isValidMessageTarget = false
}
// 0xdead... addresses are not valid targets.
......
......@@ -321,14 +321,6 @@ func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction)
if len(signedTx.Data()) > b.MaxCallDataSize {
return fmt.Errorf("Calldata cannot be larger than %d, sent %d", b.MaxCallDataSize, len(signedTx.Data()))
}
// If there is a value field set then reject transactions that
// contain calldata. The feature of sending transactions with value
// and calldata will be added in the future.
if signedTx.Value().Cmp(common.Big0) != 0 {
if len(signedTx.Data()) > 0 {
return errors.New("Cannot send transactions with value and calldata")
}
}
}
return b.eth.syncService.ValidateAndApplySequencerTransaction(signedTx)
}
......
......@@ -226,7 +226,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock)
eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
log.Info("Backend Config", "max-calldata-size", config.Rollup.MaxCallDataSize, "gas-limit", config.Rollup.GasLimit, "is-verifier", config.Rollup.IsVerifier, "using-ovm", vm.UsingOVM, "data-price", config.Rollup.DataPrice, "execution-price", config.Rollup.ExecutionPrice)
log.Info("Backend Config", "max-calldata-size", config.Rollup.MaxCallDataSize, "gas-limit", config.Rollup.GasLimit, "is-verifier", config.Rollup.IsVerifier, "using-ovm", vm.UsingOVM)
eth.APIBackend = &EthAPIBackend{ctx.ExtRPCEnabled(), eth, nil, nil, config.Rollup.IsVerifier, config.Rollup.GasLimit, vm.UsingOVM, config.Rollup.MaxCallDataSize}
gpoParams := config.GPO
if gpoParams.Default == nil {
......@@ -234,7 +234,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
}
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams)
// create the Rollup GPO and allow the API backend and the sync service to access it
rollupGpo := gasprice.NewRollupOracle(config.Rollup.DataPrice, config.Rollup.ExecutionPrice)
rollupGpo := gasprice.NewRollupOracle()
eth.APIBackend.rollupGpo = rollupGpo
eth.syncService.RollupGpo = rollupGpo
return eth, nil
......
......@@ -81,8 +81,6 @@ var DefaultConfig = Config{
// is additional overhead that is unaccounted. Round down to 127000 for
// safety.
MaxCallDataSize: 127000,
DataPrice: big.NewInt(100 * params.GWei),
ExecutionPrice: big.NewInt(0),
},
DiffDbCache: 256,
}
......
......@@ -17,10 +17,12 @@ type RollupOracle struct {
}
// NewRollupOracle returns an initialized RollupOracle
func NewRollupOracle(l1GasPrice *big.Int, l2GasPrice *big.Int) *RollupOracle {
func NewRollupOracle() *RollupOracle {
return &RollupOracle{
l1GasPrice: l1GasPrice,
l2GasPrice: l2GasPrice,
l1GasPrice: new(big.Int),
l2GasPrice: new(big.Int),
l1GasPriceLock: sync.RWMutex{},
l2GasPriceLock: sync.RWMutex{},
}
}
......
......@@ -120,10 +120,9 @@ type CallMsg struct {
Value *big.Int // amount of wei sent along with the call
Data []byte // input data, usually an ABI-encoded contract method invocation
L1MessageSender *common.Address
L1BlockNumber *big.Int
QueueOrigin *big.Int
SignatureHashType types.SignatureHashType
L1MessageSender *common.Address
L1BlockNumber *big.Int
QueueOrigin types.QueueOrigin
}
// A ContractCaller provides contract calls, essentially transactions that are executed by
......
......@@ -933,7 +933,7 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo
// Create new call message
var msg core.Message
msg = types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, false, &addr, nil, types.QueueOriginSequencer, 0)
msg = types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, false, &addr, nil, types.QueueOriginSequencer)
if vm.UsingOVM {
cfg := b.ChainConfig()
executionManager := cfg.StateDump.Accounts["OVM_ExecutionManager"]
......@@ -1364,14 +1364,8 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
if meta.L1BlockNumber != nil {
result.L1BlockNumber = (*hexutil.Big)(meta.L1BlockNumber)
}
if meta.QueueOrigin != nil {
switch meta.QueueOrigin.Uint64() {
case uint64(types.QueueOriginSequencer):
result.QueueOrigin = "sequencer"
case uint64(types.QueueOriginL1ToL2):
result.QueueOrigin = "l1"
}
}
result.QueueOrigin = fmt.Sprint(meta.QueueOrigin)
if meta.Index != nil {
index := (hexutil.Uint64)(*meta.Index)
......@@ -1381,15 +1375,6 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
queueIndex := (hexutil.Uint64)(*meta.QueueIndex)
result.QueueIndex = &queueIndex
}
switch meta.SignatureHashType {
case types.SighashEthSign:
result.TxType = "EthSign"
case types.SighashEIP155:
result.TxType = "EIP155"
case types.CreateEOA:
result.TxType = "CreateEOA"
}
}
return result
}
......@@ -1622,9 +1607,8 @@ type SendTxArgs struct {
Data *hexutil.Bytes `json:"data"`
Input *hexutil.Bytes `json:"input"`
L1BlockNumber *big.Int `json:"l1BlockNumber"`
L1MessageSender *common.Address `json:"l1MessageSender"`
SignatureHashType types.SignatureHashType `json:"signatureHashType"`
L1BlockNumber *big.Int `json:"l1BlockNumber"`
L1MessageSender *common.Address `json:"l1MessageSender"`
}
// setDefaults is a helper function that fills in default values for unspecified tx fields.
......@@ -1697,13 +1681,13 @@ func (args *SendTxArgs) toTransaction() *types.Transaction {
if args.To == nil {
tx := types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
raw, _ := rlp.EncodeToBytes(tx)
txMeta := types.NewTransactionMeta(args.L1BlockNumber, 0, nil, types.SighashEIP155, types.QueueOriginSequencer, nil, nil, raw)
txMeta := types.NewTransactionMeta(args.L1BlockNumber, 0, nil, types.QueueOriginSequencer, nil, nil, raw)
tx.SetTransactionMeta(txMeta)
return tx
}
tx := types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
raw, _ := rlp.EncodeToBytes(tx)
txMeta := types.NewTransactionMeta(args.L1BlockNumber, 0, args.L1MessageSender, args.SignatureHashType, types.QueueOriginSequencer, nil, nil, raw)
txMeta := types.NewTransactionMeta(args.L1BlockNumber, 0, args.L1MessageSender, types.QueueOriginSequencer, nil, nil, raw)
tx.SetTransactionMeta(txMeta)
return tx
}
......@@ -1800,7 +1784,7 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod
return common.Hash{}, err
}
// L1Timestamp and L1BlockNumber will be set right before execution
txMeta := types.NewTransactionMeta(nil, 0, nil, types.SighashEIP155, types.QueueOriginSequencer, nil, nil, encodedTx)
txMeta := types.NewTransactionMeta(nil, 0, nil, types.QueueOriginSequencer, nil, nil, encodedTx)
tx.SetTransactionMeta(txMeta)
return SubmitTransaction(ctx, s.b, tx)
}
......@@ -2157,7 +2141,6 @@ func (api *PrivateDebugAPI) IngestTransactions(txs []*RPCTransaction) error {
l1Timestamp := uint64(tx.L1Timestamp)
rawTransaction := tx.RawTransaction
sighashType := types.SighashEIP155
var queueOrigin types.QueueOrigin
switch tx.QueueOrigin {
case "sequencer":
......@@ -2176,14 +2159,13 @@ func (api *PrivateDebugAPI) IngestTransactions(txs []*RPCTransaction) error {
}
meta := types.TransactionMeta{
L1BlockNumber: l1BlockNumber,
L1Timestamp: l1Timestamp,
L1MessageSender: tx.L1TxOrigin,
SignatureHashType: sighashType,
QueueOrigin: big.NewInt(int64(queueOrigin)),
Index: (*uint64)(tx.Index),
QueueIndex: (*uint64)(tx.QueueIndex),
RawTransaction: rawTransaction,
L1BlockNumber: l1BlockNumber,
L1Timestamp: l1Timestamp,
L1MessageSender: tx.L1TxOrigin,
QueueOrigin: queueOrigin,
Index: (*uint64)(tx.Index),
QueueIndex: (*uint64)(tx.QueueIndex),
RawTransaction: rawTransaction,
}
transaction.SetTransactionMeta(&meta)
transactions[i] = transaction
......
......@@ -128,7 +128,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
from := statedb.GetOrNewStateObject(bankAddr)
from.SetBalance(math.MaxBig256)
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false, nil, nil, types.QueueOriginSequencer, 0)}
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false, nil, nil, types.QueueOriginSequencer)}
context := core.NewEVMContext(msg, header, bc, nil)
vmenv := vm.NewEVM(context, statedb, config, vm.Config{})
......@@ -142,7 +142,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
header := lc.GetHeaderByHash(bhash)
state := light.NewState(ctx, header, lc.Odr())
state.SetBalance(bankAddr, math.MaxBig256)
msg := callmsg{types.NewMessage(bankAddr, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false, nil, nil, types.QueueOriginSequencer, 0)}
msg := callmsg{types.NewMessage(bankAddr, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false, nil, nil, types.QueueOriginSequencer)}
context := core.NewEVMContext(msg, header, lc, nil)
vmenv := vm.NewEVM(context, state, config, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxUint64)
......
......@@ -194,7 +194,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
// Perform read-only call.
st.SetBalance(testBankAddress, math.MaxBig256)
msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, new(big.Int), data, false, nil, nil, types.QueueOriginSequencer, 0)}
msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, new(big.Int), data, false, nil, nil, types.QueueOriginSequencer)}
context := core.NewEVMContext(msg, header, chain, nil)
vmenv := vm.NewEVM(context, st, config, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxUint64)
......
......@@ -869,7 +869,7 @@ func (w *worker) commitNewTx(tx *types.Transaction) error {
// transactions as the timestamp cannot be malleated
if parent.Time() > tx.L1Timestamp() {
log.Error("Monotonicity violation", "index", num)
if tx.QueueOrigin().Uint64() == uint64(types.QueueOriginSequencer) {
if tx.QueueOrigin() == types.QueueOriginSequencer {
tx.SetL1Timestamp(parent.Time())
prev := parent.Transactions()
if len(prev) == 1 {
......@@ -1073,7 +1073,7 @@ func (w *worker) commit(uncles []*types.Header, interval func(), update bool, st
bn = new(big.Int)
}
log.Info("New block", "index", block.Number().Uint64()-uint64(1), "l1-timestamp", tx.L1Timestamp(), "l1-blocknumber", bn.Uint64(), "tx-hash", tx.Hash().Hex(),
"queue-orign", tx.QueueOrigin(), "type", tx.SignatureHashType(), "gas", block.GasUsed(), "fees", feesEth, "elapsed", common.PrettyDuration(time.Since(start)))
"queue-orign", tx.QueueOrigin(), "gas", block.GasUsed(), "fees", feesEth, "elapsed", common.PrettyDuration(time.Since(start)))
case <-w.exitCh:
log.Info("Worker has exited")
......
......@@ -74,7 +74,6 @@ type transaction struct {
Origin *common.Address `json:"origin"`
Data hexutil.Bytes `json:"data"`
QueueOrigin string `json:"queueOrigin"`
Type string `json:"type"`
QueueIndex *uint64 `json:"queueIndex"`
Decoded *decoded `json:"decoded"`
}
......@@ -105,8 +104,8 @@ type decoded struct {
Signature signature `json:"sig"`
Value hexutil.Uint64 `json:"value"`
GasLimit uint64 `json:"gasLimit,string"`
GasPrice uint64 `json:"gasPrice"`
Nonce uint64 `json:"nonce"`
GasPrice uint64 `json:"gasPrice,string"`
Nonce uint64 `json:"nonce,string"`
Target *common.Address `json:"target"`
Data hexutil.Bytes `json:"data"`
}
......@@ -241,7 +240,6 @@ func enqueueToTransaction(enqueue *Enqueue) (*types.Transaction, error) {
blockNumber,
timestamp,
&origin,
types.SighashEIP155,
types.QueueOriginL1ToL2,
enqueue.Index,
enqueue.QueueIndex,
......@@ -327,7 +325,6 @@ func batchedTransactionToTransaction(res *transaction, signer *types.EIP155Signe
} else {
return nil, fmt.Errorf("Unknown queue origin: %s", res.QueueOrigin)
}
sighashType := types.SighashEIP155
// Transactions that have been decoded are
// Queue Origin Sequencer transactions
if res.Decoded != nil {
......@@ -352,7 +349,6 @@ func batchedTransactionToTransaction(res *transaction, signer *types.EIP155Signe
new(big.Int).SetUint64(res.BlockNumber),
res.Timestamp,
res.Origin,
sighashType,
queueOrigin,
&res.Index,
res.QueueIndex,
......@@ -393,7 +389,6 @@ func batchedTransactionToTransaction(res *transaction, signer *types.EIP155Signe
new(big.Int).SetUint64(res.BlockNumber),
res.Timestamp,
origin,
sighashType,
queueOrigin,
&res.Index,
res.QueueIndex,
......
......@@ -21,9 +21,10 @@ type Config struct {
// HTTP endpoint of the data transport layer
RollupClientHttp string
L1CrossDomainMessengerAddress common.Address
L1FeeWalletAddress common.Address
AddressManagerOwnerAddress common.Address
L1ETHGatewayAddress common.Address
GasPriceOracleAddress common.Address
GasPriceOracleOwnerAddress common.Address
L1StandardBridgeAddress common.Address
// Turns on checking of state for L2 gas price
EnableL2GasPolling bool
// Deployment Height of the canonical transaction chain
......@@ -34,10 +35,6 @@ type Config struct {
PollInterval time.Duration
// Interval for updating the timestamp
TimestampRefreshThreshold time.Duration
// The gas price to use when estimating L1 calldata publishing costs
DataPrice *big.Int
// The gas price to use for L2 congestion costs
ExecutionPrice *big.Int
// Represents the source of the transactions that is being synced
Backend Backend
// Only accept transactions with fees
......
......@@ -31,6 +31,7 @@ var errShortRemoteTip = errors.New("Unexpected remote less than tip")
// L2GasPrice slot refers to the storage slot that the execution price is stored
// in the L2 predeploy contract, the GasPriceOracle
var l2GasPriceSlot = common.BigToHash(big.NewInt(1))
var l2GasPriceOracleAddress = common.HexToAddress("0x420000000000000000000000000000000000000F")
// SyncService implements the main functionality around pulling in transactions
// and executing them. It can be configured to run in both sequencer mode and in
......@@ -57,8 +58,6 @@ type SyncService struct {
timestampRefreshThreshold time.Duration
chainHeadCh chan core.ChainHeadEvent
backend Backend
gpoAddress common.Address
enableL2GasPolling bool
enforceFees bool
}
......@@ -112,8 +111,6 @@ func NewSyncService(ctx context.Context, cfg Config, txpool *core.TxPool, bc *co
pollInterval: pollInterval,
timestampRefreshThreshold: timestampRefreshThreshold,
backend: cfg.Backend,
gpoAddress: cfg.GasPriceOracleAddress,
enableL2GasPolling: cfg.EnableL2GasPolling,
enforceFees: cfg.EnforceFees,
}
......@@ -435,11 +432,6 @@ func (s *SyncService) updateL1GasPrice() error {
// price oracle at the state that corresponds to the state root. If no state
// root is passed in, then the tip is used.
func (s *SyncService) updateL2GasPrice(hash *common.Hash) error {
// TODO(mark): this is temporary and will be able to be rmoved when the
// OVM_GasPriceOracle is moved into the predeploy contracts
if !s.enableL2GasPolling {
return nil
}
var state *state.StateDB
var err error
if hash != nil {
......@@ -450,7 +442,7 @@ func (s *SyncService) updateL2GasPrice(hash *common.Hash) error {
if err != nil {
return err
}
result := state.GetState(s.gpoAddress, l2GasPriceSlot)
result := state.GetState(l2GasPriceOracleAddress, l2GasPriceSlot)
s.RollupGpo.SetL2GasPrice(result.Big())
return nil
}
......@@ -649,7 +641,7 @@ func (s *SyncService) applyTransactionToTip(tx *types.Transaction) error {
// Queue Origin L1 to L2 transactions must have a timestamp that is set by
// the L1 block that holds the transaction. This should never happen but is
// a sanity check to prevent fraudulent execution.
if tx.QueueOrigin().Uint64() == uint64(types.QueueOriginL1ToL2) {
if tx.QueueOrigin() == types.QueueOriginL1ToL2 {
if tx.L1Timestamp() == 0 {
return fmt.Errorf("Queue origin L1 to L2 transaction without a timestamp: %s", tx.Hash().Hex())
}
......@@ -676,7 +668,7 @@ func (s *SyncService) applyTransactionToTip(tx *types.Transaction) error {
bn := tx.L1BlockNumber()
s.SetLatestL1Timestamp(ts)
s.SetLatestL1BlockNumber(bn.Uint64())
log.Debug("Updating OVM context based on new transaction", "timestamp", ts, "blocknumber", bn.Uint64(), "queue-origin", tx.QueueOrigin().Uint64())
log.Debug("Updating OVM context based on new transaction", "timestamp", ts, "blocknumber", bn.Uint64(), "queue-origin", tx.QueueOrigin())
} else if tx.L1Timestamp() < s.GetLatestL1Timestamp() {
log.Error("Timestamp monotonicity violation", "hash", tx.Hash().Hex())
}
......@@ -795,11 +787,8 @@ func (s *SyncService) ValidateAndApplySequencerTransaction(tx *types.Transaction
log.Trace("Sequencer transaction validation", "hash", tx.Hash().Hex())
qo := tx.QueueOrigin()
if qo == nil {
return errors.New("invalid transaction with no queue origin")
}
if qo.Uint64() != uint64(types.QueueOriginSequencer) {
return fmt.Errorf("invalid transaction with queue origin %d", qo.Uint64())
if qo != types.QueueOriginSequencer {
return fmt.Errorf("invalid transaction with queue origin %d", qo)
}
err := s.txpool.ValidateTx(tx)
if err != nil {
......
......@@ -121,7 +121,6 @@ func TestSyncServiceTransactionEnqueued(t *testing.T) {
l1BlockNumber,
timestamp,
&l1TxOrigin,
types.SighashEIP155,
types.QueueOriginL1ToL2,
&index,
&queueIndex,
......@@ -178,7 +177,6 @@ func TestTransactionToTipNoIndex(t *testing.T) {
l1BlockNumber,
timestamp,
&l1TxOrigin,
types.SighashEIP155,
types.QueueOriginL1ToL2,
nil, // The index is `nil`, expect it to be set afterwards
nil,
......@@ -518,8 +516,6 @@ func TestSyncServiceL2GasPrice(t *testing.T) {
if err != nil {
t.Fatal(err)
}
service.enableL2GasPolling = true
service.gpoAddress = common.HexToAddress("0xF20b338752976878754518183873602902360704")
price, err := service.RollupGpo.SuggestL2GasPrice(context.Background())
if err != nil {
......@@ -535,7 +531,7 @@ func TestSyncServiceL2GasPrice(t *testing.T) {
t.Fatal("Cannot get state db")
}
l2GasPrice := big.NewInt(100000000000)
state.SetState(service.gpoAddress, l2GasPriceSlot, common.BigToHash(l2GasPrice))
state.SetState(l2GasPriceOracleAddress, l2GasPriceSlot, common.BigToHash(l2GasPrice))
root, _ := state.Commit(false)
service.updateL2GasPrice(&root)
......@@ -571,7 +567,6 @@ func TestSyncServiceSync(t *testing.T) {
l1BlockNumber,
timestamp,
&l1TxOrigin,
types.SighashEIP155,
types.QueueOriginL1ToL2,
&index,
&queueIndex,
......@@ -623,7 +618,6 @@ func TestInitializeL1ContextPostGenesis(t *testing.T) {
l1BlockNumber,
timestamp,
&l1TxOrigin,
types.SighashEIP155,
types.QueueOriginL1ToL2,
&index,
&queueIndex,
......@@ -699,7 +693,7 @@ func newTestSyncService(isVerifier bool) (*SyncService, chan core.NewTxsEvent, e
return nil, nil, nil, fmt.Errorf("Cannot initialize syncservice: %w", err)
}
service.RollupGpo = gasprice.NewRollupOracle(big.NewInt(0), big.NewInt(0))
service.RollupGpo = gasprice.NewRollupOracle()
txCh := make(chan core.NewTxsEvent, 1)
sub := service.SubscribeNewTxsEvent(txCh)
......@@ -721,7 +715,7 @@ type mockClient struct {
func setupMockClient(service *SyncService, responses map[string]interface{}) {
client := newMockClient(responses)
service.client = client
service.RollupGpo = gasprice.NewRollupOracle(big.NewInt(0), big.NewInt(0))
service.RollupGpo = gasprice.NewRollupOracle()
}
func newMockClient(responses map[string]interface{}) *mockClient {
......@@ -871,7 +865,6 @@ func mockTx() *types.Transaction {
l1BlockNumber,
timestamp,
&l1TxOrigin,
types.SighashEIP155,
types.QueueOriginSequencer,
nil,
nil,
......
......@@ -6,10 +6,10 @@ REPO=$DIR/..
IS_VERIFIER=
ROLLUP_SYNC_SERVICE_ENABLE=true
DATADIR=$HOME/.ethereum
TARGET_GAS_LIMIT=9000000
TARGET_GAS_LIMIT=11000000
CHAIN_ID=10
ETH1_CTC_DEPLOYMENT_HEIGHT=12410807
ETH1_L1_GATEWAY_ADDRESS=0xe681F80966a8b1fFadECf8068bD6F99034791c95
ETH1_L1_STANDARD_BRIDGE_ADDRESS=0xe681F80966a8b1fFadECf8068bD6F99034791c95
ETH1_L1_CROSS_DOMAIN_MESSENGER_ADDRESS=0x902e5fF5A99C4eC1C21bbab089fdabE32EF0A5DF
ADDRESS_MANAGER_OWNER_ADDRESS=0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A
ROLLUP_STATE_DUMP_PATH=https://storage.googleapis.com/optimism/mainnet/4.json
......@@ -33,6 +33,7 @@ CLI Arguments:
--eth1.chainid - eth1 chain id
--eth1.ctcdeploymentheight - eth1 ctc deploy height
--eth1.l1crossdomainmessengeraddress - eth1 l1 xdomain messenger address
--eth1.l1feewalletaddress - eth l1 fee wallet address
--rollup.statedumppath - http path to the initial state dump
--rollup.clienthttp - rollup client http
--rollup.pollinterval - polling interval for the rollup client
......@@ -109,27 +110,27 @@ while (( "$#" )); do
exit 1
fi
;;
--eth1.l1gatewayaddress)
--eth1.l1crossdomainmessengeraddress)
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
ETH1_L1_GATEWAY_ADDRESS="$2"
ETH1_L1_CROSS_DOMAIN_MESSENGER_ADDRESS="$2"
shift 2
else
echo "Error: Argument for $1 is missing" >&2
exit 1
fi
;;
--eth1.l1crossdomainmessengeraddress)
--eth1.l1feewalletaddress)
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
ETH1_L1_CROSS_DOMAIN_MESSENGER_ADDRESS="$2"
ETH1_L1_FEE_WALLET_ADDRESS="$2"
shift 2
else
echo "Error: Argument for $1 is missing" >&2
exit 1
fi
;;
--eth1.l1ethgatewayaddress)
--eth1.l1standardbridgeaddress)
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
ETH1_L1_ETH_GATEWAY_ADDRESS="$2"
ETH1_L1_STANDARD_BRIDGE_ADDRESS="$2"
shift 2
else
echo "Error: Argument for $1 is missing" >&2
......@@ -230,10 +231,11 @@ if [[ ! -z "$ROLLUP_SYNC_SERVICE_ENABLE" ]]; then
fi
cmd="$cmd --datadir $DATADIR"
cmd="$cmd --eth1.l1crossdomainmessengeraddress $ETH1_L1_CROSS_DOMAIN_MESSENGER_ADDRESS"
cmd="$cmd --eth1.l1feewalletaddress $ETH1_L1_FEE_WALLET_ADDRESS"
cmd="$cmd --rollup.addressmanagerowneraddress $ADDRESS_MANAGER_OWNER_ADDRESS"
cmd="$cmd --rollup.statedumppath $ROLLUP_STATE_DUMP_PATH"
cmd="$cmd --eth1.ctcdeploymentheight $ETH1_CTC_DEPLOYMENT_HEIGHT"
cmd="$cmd --eth1.l1ethgatewayaddress $ETH1_L1_GATEWAY_ADDRESS"
cmd="$cmd --eth1.l1standardbridgeaddress $ETH1_L1_STANDARD_BRIDGE_ADDRESS"
cmd="$cmd --rollup.clienthttp $ROLLUP_CLIENT_HTTP"
cmd="$cmd --rollup.pollinterval $ROLLUP_POLL_INTERVAL"
cmd="$cmd --rollup.timestamprefresh $ROLLUP_TIMESTAMP_REFRESH"
......
......@@ -78,10 +78,9 @@ type SendTxArgs struct {
Data *hexutil.Bytes `json:"data"`
Input *hexutil.Bytes `json:"input"`
L1MessageSender *common.MixedcaseAddress `json:"l1MessageSender"`
L1BlockNumber *big.Int `json:"l1BlockNumber"`
SignatureHashType types.SignatureHashType `json:"signatureHashType"`
QueueOrigin types.QueueOrigin `json:"queueOrigin"`
L1MessageSender *common.MixedcaseAddress `json:"l1MessageSender"`
L1BlockNumber *big.Int `json:"l1BlockNumber"`
QueueOrigin types.QueueOrigin `json:"queueOrigin"`
}
func (args SendTxArgs) String() string {
......@@ -111,7 +110,7 @@ func (args *SendTxArgs) toTransaction() *types.Transaction {
}
tx := types.NewTransaction(uint64(args.Nonce), args.To.Address(), (*big.Int)(&args.Value), (uint64)(args.Gas), (*big.Int)(&args.GasPrice), input)
txMeta := types.NewTransactionMeta(l1BlockNumber, 0, l1MessageSender, args.SignatureHashType, args.QueueOrigin, nil, nil, nil)
txMeta := types.NewTransactionMeta(l1BlockNumber, 0, l1MessageSender, args.QueueOrigin, nil, nil, nil)
tx.SetTransactionMeta(txMeta)
return tx
}
......@@ -279,7 +279,7 @@ func (tx *stTransaction) toMessage(ps stPostState) (core.Message, error) {
return nil, fmt.Errorf("invalid tx data %q", dataHex)
}
msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, data, true, nil, nil, types.QueueOriginSequencer, 0)
msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, data, true, nil, nil, types.QueueOriginSequencer)
return msg, nil
}
......
......@@ -33,5 +33,5 @@ IPC_DISABLE=true
NETWORK_ID=420
NO_USB=true
NO_DISCOVER=true
TARGET_GAS_LIMIT=9000000
TARGET_GAS_LIMIT=11000000
USING_OVM=true
......@@ -21,9 +21,9 @@ if [[ ! -z "$URL" ]]; then
envSet ROLLUP_ADDRESS_MANAGER_OWNER_ADDRESS Deployer
# set the address to the proxy gateway if possible
envSet ETH1_L1_ETH_GATEWAY_ADDRESS Proxy__OVM_L1ETHGateway
if [ $ETH1_L1_ETH_GATEWAY_ADDRESS == null ]; then
envSet ETH1_L1_ETH_GATEWAY_ADDRESS OVM_L1ETHGateway
envSet ETH1_L1_STANDARD_BRIDGE_ADDRESS Proxy__OVM_L1StandardBridge
if [ $ETH1_L1_STANDARD_BRIDGE_ADDRESS == null ]; then
envSet ETH1_L1_STANDARD_BRIDGE_ADDRESS OVM_L1StandardBridge
fi
fi
......
......@@ -622,7 +622,7 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
)
const addr = await manager.getAddress(
'OVM_ChainStorageContainer:CTC:batches'
'OVM_ChainStorageContainer-CTC-batches'
)
const container = new Contract(
addr,
......
......@@ -9,7 +9,7 @@ import sinon from 'sinon'
import { Web3Provider } from '@ethersproject/providers'
import scc from '@eth-optimism/contracts/artifacts/contracts/optimistic-ethereum/OVM/chain/OVM_StateCommitmentChain.sol/OVM_StateCommitmentChain.json'
import { getContractInterface } from '@eth-optimism/contracts'
import { getContractInterface, predeploys } from '@eth-optimism/contracts'
import { smockit, MockContract } from '@eth-optimism/smock'
/* Internal Imports */
......@@ -37,7 +37,6 @@ import {
} from '@eth-optimism/core-utils'
import { Logger, Metrics } from '@eth-optimism/common-ts'
const DECOMPRESSION_ADDRESS = '0x4200000000000000000000000000000000000008'
const DUMMY_ADDRESS = '0x' + '00'.repeat(20)
const EXAMPLE_STATE_ROOT =
'0x16b7f83f409c7195b1f4fde5652f1b54a4477eacb6db7927691becafba5f8801'
......@@ -97,7 +96,7 @@ describe('BatchSubmitter', () => {
)
await AddressManager.setAddress(
'OVM_DecompressionPrecompileAddress',
DECOMPRESSION_ADDRESS
predeploys.OVM_SequencerEntrypoint
)
Mock__OVM_ExecutionManager = await smockit(
......@@ -483,7 +482,7 @@ describe('Batch Submitter with Ganache', () => {
gasPrices.push(gasPrice)
const tx = signer.sendTransaction({
to: DECOMPRESSION_ADDRESS,
to: predeploys.OVM_SequencerEntrypoint,
value: 88,
nonce: 0,
gasPrice,
......
......@@ -5,6 +5,7 @@ import * as mkdirp from 'mkdirp'
const env = process.env
const CHAIN_ID = env.CHAIN_ID || '420'
const GAS_PRICE_ORACLE_OWNER = env.GAS_PRICE_ORACLE_OWNER || '0x' + 'FF'.repeat(20)
/* Internal Imports */
import { makeStateDump } from '../src/state-dump/make-dump'
......@@ -18,6 +19,10 @@ import { RollupDeployConfig } from '../src/contract-deployment'
ovmGlobalContext: {
ovmCHAINID: parseInt(CHAIN_ID, 10),
},
gasPriceOracleConfig: {
owner: GAS_PRICE_ORACLE_OWNER,
initialGasPrice: 0,
}
}
const dump = await makeStateDump(config as RollupDeployConfig)
......
This diff is collapsed.
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
/**
* @title iL1ChugSplashDeployer
*/
interface iL1ChugSplashDeployer {
function isUpgrading()
external
view
returns (
bool
);
}
// 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,18 @@ 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_OVMCodec } from "../../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressManager } from "../../../libraries/resolver/Lib_AddressManager.sol";
import { Lib_SecureMerkleTrie } from "../../../libraries/trie/Lib_SecureMerkleTrie.sol";
import { Lib_PredeployAddresses } from "../../../libraries/constants/Lib_PredeployAddresses.sol";
import { Lib_CrossDomainUtils } from "../../../libraries/bridge/Lib_CrossDomainUtils.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";
/* External Imports */
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
......@@ -32,7 +31,6 @@ import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/
*/
contract OVM_L1CrossDomainMessenger is
iOVM_L1CrossDomainMessenger,
Abs_BaseCrossDomainMessenger,
Lib_AddressResolver,
OwnableUpgradeable,
PausableUpgradeable,
......@@ -51,11 +49,24 @@ contract OVM_L1CrossDomainMessenger is
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 *
**********************/
mapping (bytes32 => bool) public blockedMessages;
mapping (bytes32 => bool) public relayedMessages;
mapping (bytes32 => bool) public successfulMessages;
address internal xDomainMsgSender = DEFAULT_XDOMAIN_SENDER;
/***************
* Constructor *
......@@ -155,6 +166,48 @@ contract OVM_L1CrossDomainMessenger is
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.
* @inheritdoc iOVM_L1CrossDomainMessenger
......@@ -172,7 +225,7 @@ contract OVM_L1CrossDomainMessenger is
onlyRelayer
whenNotPaused
{
bytes memory xDomainCalldata = _getXDomainCalldata(
bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata(
_target,
_sender,
_message,
......@@ -232,25 +285,40 @@ contract OVM_L1CrossDomainMessenger is
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce,
uint256 _queueIndex,
uint32 _gasLimit
)
override
public
{
bytes memory xDomainCalldata = _getXDomainCalldata(
_target,
_sender,
_message,
_messageNonce
// Verify that the message is in the queue:
address canonicalTransactionChain = resolve("OVM_CanonicalTransactionChain");
Lib_OVMCodec.QueueElement memory element = iOVM_CanonicalTransactionChain(canonicalTransactionChain).getQueueElement(_queueIndex);
address l2CrossDomainMessenger = resolve("OVM_L2CrossDomainMessenger");
// Compute the transactionHash
bytes32 transactionHash = keccak256(
abi.encode(
address(this),
l2CrossDomainMessenger,
_gasLimit,
_message
)
);
require(
sentMessages[keccak256(xDomainCalldata)] == true,
"Provided message has not already been sent."
transactionHash == element.transactionHash,
"Provided message has not been enqueued."
);
bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata(
_target,
_sender,
_message,
_queueIndex
);
_sendXDomainMessage(xDomainCalldata, _gasLimit);
_sendXDomainMessage(canonicalTransactionChain, l2CrossDomainMessenger, xDomainCalldata, _gasLimit);
}
......@@ -340,7 +408,7 @@ contract OVM_L1CrossDomainMessenger is
bool exists,
bytes memory encodedMessagePassingAccount
) = Lib_SecureMerkleTrie.get(
abi.encodePacked(0x4200000000000000000000000000000000000000),
abi.encodePacked(Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER),
_proof.stateTrieWitness,
_proof.stateRoot
);
......@@ -364,18 +432,21 @@ contract OVM_L1CrossDomainMessenger is
/**
* 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 _gasLimit OVM gas limit for the message.
*/
function _sendXDomainMessage(
address _canonicalTransactionChain,
address _l2CrossDomainMessenger,
bytes memory _message,
uint256 _gasLimit
)
override
internal
{
iOVM_CanonicalTransactionChain(resolve("OVM_CanonicalTransactionChain")).enqueue(
resolve("OVM_L2CrossDomainMessenger"),
iOVM_CanonicalTransactionChain(_canonicalTransactionChain).enqueue(
_l2CrossDomainMessenger,
_gasLimit,
_message
);
......
......@@ -4,14 +4,15 @@ pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_AddressResolver } from "../../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_CrossDomainUtils } from "../../../libraries/bridge/Lib_CrossDomainUtils.sol";
/* Interface Imports */
import { iOVM_L2CrossDomainMessenger } from "../../../iOVM/bridge/messaging/iOVM_L2CrossDomainMessenger.sol";
import { iOVM_L1MessageSender } from "../../../iOVM/predeploys/iOVM_L1MessageSender.sol";
import { iOVM_L2ToL1MessagePasser } from "../../../iOVM/predeploys/iOVM_L2ToL1MessagePasser.sol";
/* Contract Imports */
import { Abs_BaseCrossDomainMessenger } from "./Abs_BaseCrossDomainMessenger.sol";
/* External Imports */
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
/* External Imports */
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
......@@ -26,11 +27,29 @@ import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.s
*/
contract OVM_L2CrossDomainMessenger is
iOVM_L2CrossDomainMessenger,
Abs_BaseCrossDomainMessenger,
Lib_AddressResolver,
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 *
***************/
......@@ -38,18 +57,53 @@ contract OVM_L2CrossDomainMessenger is
/**
* @param _libAddressManager Address of the Address Manager.
*/
constructor(
address _libAddressManager
)
Lib_AddressResolver(_libAddressManager)
ReentrancyGuard()
{}
constructor(address _libAddressManager) Lib_AddressResolver(_libAddressManager) ReentrancyGuard() {}
/********************
* 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.
* @inheritdoc iOVM_L2CrossDomainMessenger
......@@ -69,7 +123,7 @@ contract OVM_L2CrossDomainMessenger is
"Provided message could not be verified."
);
bytes memory xDomainCalldata = _getXDomainCalldata(
bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata(
_target,
_sender,
_message,
......@@ -150,7 +204,6 @@ contract OVM_L2CrossDomainMessenger is
bytes memory _message,
uint256 // _gasLimit
)
override
internal
{
iOVM_L2ToL1MessagePasser(resolve("OVM_L2ToL1MessagePasser")).passMessageToL1(_message);
......
// SPDX-License-Identifier: MIT
// @unsupported: ovm
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_L1TokenGateway } from "../../../iOVM/bridge/tokens/iOVM_L1TokenGateway.sol";
import { iOVM_L2DepositedToken } from "../../../iOVM/bridge/tokens/iOVM_L2DepositedToken.sol";
/* Library Imports */
import { OVM_CrossDomainEnabled } from "../../../libraries/bridge/OVM_CrossDomainEnabled.sol";
/**
* @title Abs_L1TokenGateway
* @dev An L1 Token Gateway is a contract which stores deposited L1 funds that are in use on L2.
* It synchronizes a corresponding L2 representation of the "deposited token", informing it
* of new deposits and releasing L1 funds when there are newly finalized withdrawals.
*
* NOTE: This abstract contract gives all the core functionality of an L1 token gateway,
* but provides easy hooks in case developers need extensions in child contracts.
* In many cases, the default OVM_L1ERC20Gateway will suffice.
*
* Compiler used: solc
* Runtime target: EVM
*/
abstract contract Abs_L1TokenGateway is iOVM_L1TokenGateway, OVM_CrossDomainEnabled {
/********************************
* External Contract References *
********************************/
address public l2DepositedToken;
/***************
* Constructor *
***************/
/**
* @param _l2DepositedToken iOVM_L2DepositedToken-compatible address on the chain being deposited into.
* @param _l1messenger L1 Messenger address being used for cross-chain communications.
*/
constructor(
address _l2DepositedToken,
address _l1messenger
)
OVM_CrossDomainEnabled(_l1messenger)
{
l2DepositedToken = _l2DepositedToken;
}
/********************************
* Overridable Accounting logic *
********************************/
// Default gas value which can be overridden if more complex logic runs on L2.
uint32 internal constant DEFAULT_FINALIZE_DEPOSIT_L2_GAS = 1200000;
/**
* @dev Core logic to be performed when a withdrawal is finalized on L1.
* In most cases, this will simply send locked funds to the withdrawer.
*
* param _to Address being withdrawn to.
* param _amount Amount being withdrawn.
*/
function _handleFinalizeWithdrawal(
address, // _to,
uint256 // _amount
)
internal
virtual
{
revert("Implement me in child contracts");
}
/**
* @dev Core logic to be performed when a deposit is initiated on L1.
* In most cases, this will simply send locked funds to the withdrawer.
*
* param _from Address being deposited from on L1.
* param _to Address being deposited into on L2.
* param _amount Amount being deposited.
*/
function _handleInitiateDeposit(
address, // _from,
address, // _to,
uint256 // _amount
)
internal
virtual
{
revert("Implement me in child contracts");
}
/**
* @dev Overridable getter for the L2 gas limit, in the case it may be
* dynamic, and the above public constant does not suffice.
*
*/
function getFinalizeDepositL2Gas()
public
view
virtual
returns(
uint32
)
{
return DEFAULT_FINALIZE_DEPOSIT_L2_GAS;
}
/**************
* Depositing *
**************/
/**
* @dev deposit an amount of the ERC20 to the caller's balance on L2
* @param _amount Amount of the ERC20 to deposit
*/
function deposit(
uint _amount
)
external
override
virtual
{
_initiateDeposit(msg.sender, msg.sender, _amount);
}
/**
* @dev deposit an amount of ERC20 to a recipients's balance on L2
* @param _to L2 address to credit the withdrawal to
* @param _amount Amount of the ERC20 to deposit
*/
function depositTo(
address _to,
uint _amount
)
external
override
virtual
{
_initiateDeposit(msg.sender, _to, _amount);
}
/**
* @dev Performs the logic for deposits by informing the L2 Deposited Token
* contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom)
*
* @param _from Account to pull the deposit from on L1
* @param _to Account to give the deposit to on L2
* @param _amount Amount of the ERC20 to deposit.
*/
function _initiateDeposit(
address _from,
address _to,
uint _amount
)
internal
{
// Call our deposit accounting handler implemented by child contracts.
_handleInitiateDeposit(
_from,
_to,
_amount
);
// Construct calldata for l2DepositedToken.finalizeDeposit(_to, _amount)
bytes memory data = abi.encodeWithSelector(
iOVM_L2DepositedToken.finalizeDeposit.selector,
_to,
_amount
);
// Send calldata into L2
sendCrossDomainMessage(
l2DepositedToken,
data,
getFinalizeDepositL2Gas()
);
emit DepositInitiated(_from, _to, _amount);
}
/*************************
* Cross-chain Functions *
*************************/
/**
* @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the
* L1 ERC20 token.
* This call will fail if the initialized withdrawal from L2 has not been finalized.
*
* @param _to L1 address to credit the withdrawal to
* @param _amount Amount of the ERC20 to withdraw
*/
function finalizeWithdrawal(
address _to,
uint _amount
)
external
override
virtual
onlyFromCrossDomainAccount(l2DepositedToken)
{
// Call our withdrawal accounting handler implemented by child contracts.
_handleFinalizeWithdrawal(
_to,
_amount
);
emit WithdrawalFinalized(_to, _amount);
}
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_L2DepositedToken } from "../../../iOVM/bridge/tokens/iOVM_L2DepositedToken.sol";
import { iOVM_L1TokenGateway } from "../../../iOVM/bridge/tokens/iOVM_L1TokenGateway.sol";
/* Library Imports */
import { OVM_CrossDomainEnabled } from "../../../libraries/bridge/OVM_CrossDomainEnabled.sol";
/**
* @title Abs_L2DepositedToken
* @dev An L2 Deposited Token is an L2 representation of funds which were deposited from L1.
* Usually contract mints new tokens when it hears about deposits into the L1 ERC20 gateway.
* This contract also burns the tokens intended for withdrawal, informing the L1 gateway to release L1 funds.
*
* NOTE: This abstract contract gives all the core functionality of a deposited token implementation except for the
* token's internal accounting itself. This gives developers an easy way to implement children with their own token code.
*
* Compiler used: optimistic-solc
* Runtime target: OVM
*/
abstract contract Abs_L2DepositedToken is iOVM_L2DepositedToken, OVM_CrossDomainEnabled {
/*******************
* Contract Events *
*******************/
event Initialized(iOVM_L1TokenGateway _l1TokenGateway);
/********************************
* External Contract References *
********************************/
iOVM_L1TokenGateway public l1TokenGateway;
/********************************
* Constructor & Initialization *
********************************/
/**
* @param _l2CrossDomainMessenger L1 Messenger address being used for cross-chain communications.
*/
constructor(
address _l2CrossDomainMessenger
)
OVM_CrossDomainEnabled(_l2CrossDomainMessenger)
{}
/**
* @dev Initialize this contract with the L1 token gateway address.
* The flow: 1) this contract gets deployed on L2, 2) the L1
* gateway is deployed with addr from (1), 3) L1 gateway address passed here.
*
* @param _l1TokenGateway Address of the corresponding L1 gateway deployed to the main chain
*/
function init(
iOVM_L1TokenGateway _l1TokenGateway
)
public
{
require(address(l1TokenGateway) == address(0), "Contract has already been initialized");
l1TokenGateway = _l1TokenGateway;
emit Initialized(l1TokenGateway);
}
/**********************
* Function Modifiers *
**********************/
modifier onlyInitialized() {
require(address(l1TokenGateway) != address(0), "Contract has not yet been initialized");
_;
}
/********************************
* Overridable Accounting logic *
********************************/
// Default gas value which can be overridden if more complex logic runs on L1.
uint32 internal constant DEFAULT_FINALIZE_WITHDRAWAL_L1_GAS = 100000;
/**
* @dev Core logic to be performed when a withdrawal from L2 is initialized.
* In most cases, this will simply burn the withdrawn L2 funds.
*
* param _to Address being withdrawn to
* param _amount Amount being withdrawn
*/
function _handleInitiateWithdrawal(
address, // _to,
uint // _amount
)
internal
virtual
{
revert("Accounting must be implemented by child contract.");
}
/**
* @dev Core logic to be performed when a deposit from L2 is finalized on L2.
* In most cases, this will simply _mint() to credit L2 funds to the recipient.
*
* param _to Address being deposited to on L2
* param _amount Amount which was deposited on L1
*/
function _handleFinalizeDeposit(
address, // _to
uint // _amount
)
internal
virtual
{
revert("Accounting must be implemented by child contract.");
}
/**
* @dev Overridable getter for the *L1* gas limit of settling the withdrawal, in the case it may be
* dynamic, and the above public constant does not suffice.
*/
function getFinalizeWithdrawalL1Gas()
public
view
virtual
returns(
uint32
)
{
return DEFAULT_FINALIZE_WITHDRAWAL_L1_GAS;
}
/***************
* Withdrawing *
***************/
/**
* @dev initiate a withdraw of some tokens to the caller's account on L1
* @param _amount Amount of the token to withdraw
*/
function withdraw(
uint _amount
)
external
override
virtual
onlyInitialized()
{
_initiateWithdrawal(msg.sender, _amount);
}
/**
* @dev initiate a withdraw of some token to a recipient's account on L1
* @param _to L1 adress to credit the withdrawal to
* @param _amount Amount of the token to withdraw
*/
function withdrawTo(
address _to,
uint _amount
)
external
override
virtual
onlyInitialized()
{
_initiateWithdrawal(_to, _amount);
}
/**
* @dev Performs the logic for deposits by storing the token and informing the L2 token Gateway of the deposit.
*
* @param _to Account to give the withdrawal to on L1
* @param _amount Amount of the token to withdraw
*/
function _initiateWithdrawal(
address _to,
uint _amount
)
internal
{
// Call our withdrawal accounting handler implemented by child contracts (usually a _burn)
_handleInitiateWithdrawal(_to, _amount);
// Construct calldata for l1TokenGateway.finalizeWithdrawal(_to, _amount)
bytes memory data = abi.encodeWithSelector(
iOVM_L1TokenGateway.finalizeWithdrawal.selector,
_to,
_amount
);
// Send message up to L1 gateway
sendCrossDomainMessage(
address(l1TokenGateway),
data,
getFinalizeWithdrawalL1Gas()
);
emit WithdrawalInitiated(msg.sender, _to, _amount);
}
/************************************
* Cross-chain Function: Depositing *
************************************/
/**
* @dev Complete a deposit from L1 to L2, and credits funds to the recipient's balance of this
* L2 token.
* This call will fail if it did not originate from a corresponding deposit in OVM_l1TokenGateway.
*
* @param _to Address to receive the withdrawal at
* @param _amount Amount of the token to withdraw
*/
function finalizeDeposit(
address _to,
uint _amount
)
external
override
virtual
onlyInitialized()
onlyFromCrossDomainAccount(address(l1TokenGateway))
{
_handleFinalizeDeposit(_to, _amount);
emit DepositFinalized(_to, _amount);
}
}
// SPDX-License-Identifier: MIT
// @unsupported: ovm
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_L1TokenGateway } from "../../../iOVM/bridge/tokens/iOVM_L1TokenGateway.sol";
import { Abs_L1TokenGateway } from "./Abs_L1TokenGateway.sol";
import { iOVM_ERC20 } from "../../../iOVM/predeploys/iOVM_ERC20.sol";
/**
* @title OVM_L1ERC20Gateway
* @dev The L1 ERC20 Gateway is a contract which stores deposited L1 funds that are in use on L2.
* It synchronizes a corresponding L2 ERC20 Gateway, informing it of deposits, and listening to it
* for newly finalized withdrawals.
*
* NOTE: This contract extends Abs_L1TokenGateway, which is where we
* takes care of most of the initialization and the cross-chain logic.
* If you are looking to implement your own deposit/withdrawal contracts, you
* may also want to extend the abstract contract in a similar manner.
*
* Compiler used: solc
* Runtime target: EVM
*/
contract OVM_L1ERC20Gateway is Abs_L1TokenGateway {
/********************************
* External Contract References *
********************************/
iOVM_ERC20 public l1ERC20;
/***************
* Constructor *
***************/
/**
* @param _l1ERC20 L1 ERC20 address this contract stores deposits for
* @param _l2DepositedERC20 L2 Gateway address on the chain being deposited into
*/
constructor(
iOVM_ERC20 _l1ERC20,
address _l2DepositedERC20,
address _l1messenger
)
Abs_L1TokenGateway(
_l2DepositedERC20,
_l1messenger
)
{
l1ERC20 = _l1ERC20;
}
/**************
* Accounting *
**************/
/**
* @dev When a deposit is initiated on L1, the L1 Gateway
* transfers the funds to itself for future withdrawals
*
* @param _from L1 address ETH is being deposited from
* param _to L2 address that the ETH is being deposited to
* @param _amount Amount of ERC20 to send
*/
function _handleInitiateDeposit(
address _from,
address, // _to,
uint256 _amount
)
internal
override
{
// Hold on to the newly deposited funds
l1ERC20.transferFrom(
_from,
address(this),
_amount
);
}
/**
* @dev When a withdrawal is finalized on L1, the L1 Gateway
* transfers the funds to the withdrawer
*
* @param _to L1 address that the ERC20 is being withdrawn to
* @param _amount Amount of ERC20 to send
*/
function _handleFinalizeWithdrawal(
address _to,
uint _amount
)
internal
override
{
// Transfer withdrawn funds out to withdrawer
l1ERC20.transfer(_to, _amount);
}
}
// SPDX-License-Identifier: MIT
// @unsupported: ovm
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_L1ETHGateway } from "../../../iOVM/bridge/tokens/iOVM_L1ETHGateway.sol";
import { iOVM_L2DepositedToken } from "../../../iOVM/bridge/tokens/iOVM_L2DepositedToken.sol";
/* Library Imports */
import { OVM_CrossDomainEnabled } from "../../../libraries/bridge/OVM_CrossDomainEnabled.sol";
import { Lib_AddressResolver } from "../../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_AddressManager } from "../../../libraries/resolver/Lib_AddressManager.sol";
/**
* @title OVM_L1ETHGateway
* @dev The L1 ETH Gateway is a contract which stores deposited ETH that is in use on L2.
*
* Compiler used: solc
* Runtime target: EVM
*/
contract OVM_L1ETHGateway is iOVM_L1ETHGateway, OVM_CrossDomainEnabled, Lib_AddressResolver {
/********************
* Public Constants *
********************/
uint32 public constant override getFinalizeDepositL2Gas = 1200000;
/********************************
* External Contract References *
********************************/
address public ovmEth;
/***************
* Constructor *
***************/
// This contract lives behind a proxy, so the constructor parameters will go unused.
constructor()
OVM_CrossDomainEnabled(address(0))
Lib_AddressResolver(address(0))
public
{}
/******************
* Initialization *
******************/
/**
* @param _libAddressManager Address manager for this OE deployment
* @param _ovmEth L2 OVM_ETH implementation of iOVM_DepositedToken
*/
function initialize(
address _libAddressManager,
address _ovmEth
)
public
{
require(libAddressManager == Lib_AddressManager(0), "Contract has already been initialized.");
libAddressManager = Lib_AddressManager(_libAddressManager);
ovmEth = _ovmEth;
messenger = resolve("Proxy__OVM_L1CrossDomainMessenger");
}
/**************
* Depositing *
**************/
receive()
external
payable
{
_initiateDeposit(msg.sender, msg.sender);
}
/**
* @dev deposit an amount of the ETH to the caller's balance on L2
*/
function deposit()
external
override
payable
{
_initiateDeposit(msg.sender, msg.sender);
}
/**
* @dev deposit an amount of ETH to a recipients's balance on L2
* @param _to L2 address to credit the withdrawal to
*/
function depositTo(
address _to
)
external
override
payable
{
_initiateDeposit(msg.sender, _to);
}
/**
* @dev Performs the logic for deposits by storing the ETH and informing the L2 ETH Gateway of the deposit.
*
* @param _from Account to pull the deposit from on L1
* @param _to Account to give the deposit to on L2
*/
function _initiateDeposit(
address _from,
address _to
)
internal
{
// Construct calldata for l2ETHGateway.finalizeDeposit(_to, _amount)
bytes memory data =
abi.encodeWithSelector(
iOVM_L2DepositedToken.finalizeDeposit.selector,
_to,
msg.value
);
// Send calldata into L2
sendCrossDomainMessage(
ovmEth,
data,
getFinalizeDepositL2Gas
);
emit DepositInitiated(_from, _to, msg.value);
}
/*************************
* Cross-chain Functions *
*************************/
/**
* @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the
* L1 ETH token.
* Since only the xDomainMessenger can call this function, it will never be called before the withdrawal is finalized.
*
* @param _to L1 address to credit the withdrawal to
* @param _amount Amount of the ETH to withdraw
*/
function finalizeWithdrawal(
address _to,
uint256 _amount
)
external
override
onlyFromCrossDomainAccount(ovmEth)
{
_safeTransferETH(_to, _amount);
emit WithdrawalFinalized(_to, _amount);
}
/**********************************
* Internal Functions: Accounting *
**********************************/
/**
* @dev Internal accounting function for moving around L1 ETH.
*
* @param _to L1 address to transfer ETH to
* @param _value Amount of ETH to send to
*/
function _safeTransferETH(
address _to,
uint256 _value
)
internal
{
(bool success, ) = _to.call{value: _value}(new bytes(0));
require(success, 'TransferHelper::safeTransferETH: ETH transfer failed');
}
/*****************************
* Temporary - Migrating ETH *
*****************************/
/**
* @dev Migrates entire ETH balance to another gateway
* @param _to Gateway Proxy address to migrate ETH to
*/
function migrateEth(address payable _to) external {
address owner = Lib_AddressManager(libAddressManager).owner();
require(msg.sender == owner, "Only the owner can migrate ETH");
uint256 balance = address(this).balance;
OVM_L1ETHGateway(_to).donateETH{value:balance}();
}
/**
* @dev Adds ETH balance to the account. This is meant to allow for ETH
* to be migrated from an old gateway to a new gateway
*/
function donateETH() external payable {}
}
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_L1TokenGateway } from "../../../iOVM/bridge/tokens/iOVM_L1TokenGateway.sol";
/* Contract Imports */
import { UniswapV2ERC20 } from "../../../libraries/standards/UniswapV2ERC20.sol";
/* Library Imports */
import { Abs_L2DepositedToken } from "./Abs_L2DepositedToken.sol";
/**
* @title OVM_L2DepositedERC20
* @dev The L2 Deposited ERC20 is an ERC20 implementation which represents L1 assets deposited into L2.
* This contract mints new tokens when it hears about deposits into the L1 ERC20 gateway.
* This contract also burns the tokens intended for withdrawal, informing the L1 gateway to release L1 funds.
*
* NOTE: This contract implements the Abs_L2DepositedToken contract using Uniswap's ERC20 as the implementation.
* Alternative implementations can be used in this similar manner.
*
* Compiler used: optimistic-solc
* Runtime target: OVM
*/
contract OVM_L2DepositedERC20 is Abs_L2DepositedToken, UniswapV2ERC20 {
/***************
* Constructor *
***************/
/**
* @param _l2CrossDomainMessenger Cross-domain messenger used by this contract.
* @param _name ERC20 name
* @param _symbol ERC20 symbol
*/
constructor(
address _l2CrossDomainMessenger,
string memory _name,
string memory _symbol
)
Abs_L2DepositedToken(_l2CrossDomainMessenger)
UniswapV2ERC20(_name, _symbol)
{}
// When a withdrawal is initiated, we burn the withdrawer's funds to prevent subsequent L2 usage.
function _handleInitiateWithdrawal(
address, // _to,
uint _amount
)
internal
override
{
_burn(msg.sender, _amount);
}
// When a deposit is finalized, we credit the account on L2 with the same amount of tokens.
function _handleFinalizeDeposit(
address _to,
uint _amount
)
internal
override
{
_mint(_to, _amount);
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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