Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
685c8f25
Unverified
Commit
685c8f25
authored
Jun 18, 2021
by
Kelvin Fichter
Committed by
Kelvin Fichter
Jul 06, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat[integration-tests]: make tests work for prod networks
parent
9ed1e38b
Changes
14
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
368 additions
and
208 deletions
+368
-208
.env.example
integration-tests/.env.example
+6
-0
README.md
integration-tests/README.md
+31
-0
hardhat.config.ts
integration-tests/hardhat.config.ts
+8
-3
package.json
integration-tests/package.json
+3
-0
basic-l1-l2-communication.spec.ts
integration-tests/test/basic-l1-l2-communication.spec.ts
+7
-4
fee-payment.spec.ts
integration-tests/test/fee-payment.spec.ts
+18
-9
native-eth.spec.ts
integration-tests/test/native-eth.spec.ts
+61
-39
ovmcontext.spec.ts
integration-tests/test/ovmcontext.spec.ts
+43
-62
queue-ingestion.spec.ts
integration-tests/test/queue-ingestion.spec.ts
+41
-75
rpc.spec.ts
integration-tests/test/rpc.spec.ts
+13
-5
env.ts
integration-tests/test/shared/env.ts
+108
-2
utils.ts
integration-tests/test/shared/utils.ts
+21
-9
Dockerfile.integration-tests
ops/docker/Dockerfile.integration-tests
+3
-0
yarn.lock
yarn.lock
+5
-0
No files found.
integration-tests/.env.example
0 → 100644
View file @
685c8f25
# only need to fill these out if you want to test against a prod network
PRIVATE_KEY=
L1_URL=
L2_URL=
ADDRESS_MANAGER=
L2_CHAINID=
integration-tests/README.md
0 → 100644
View file @
685c8f25
# @eth-optimism/integration-tests
## Setup
Follow installation + build instructions in the
[
primary README
](
../README.md
)
.
Then, run:
```
bash
yarn build:integration
```
## Running tests
### Testing a live network
Create an
`.env`
file and fill it out.
Look at
`.env.example`
to know which variables to include.
Once you have your environment set up, run:
```
bash
yarn
test
:integration:live
```
You can also set environment variables on the command line instead of inside
`.env`
if you want:
```
bash
L1_URL
=
whatever
L2_URL
=
whatever yarn
test
:integration:live
```
Note that this can take an extremely long time (~1hr).
integration-tests/hardhat.config.ts
View file @
685c8f25
...
...
@@ -9,14 +9,19 @@ import 'hardhat-gas-reporter'
const
enableGasReport
=
!!
process
.
env
.
ENABLE_GAS_REPORT
const
config
:
HardhatUserConfig
=
{
mocha
:
{
timeout
:
20000
,
},
networks
:
{
optimism
:
{
url
:
process
.
env
.
L2_URL
||
'
http://localhost:8545
'
,
ovm
:
true
,
},
'
optimism-live
'
:
{
url
:
process
.
env
.
L2_URL
||
'
http://localhost:8545
'
,
ovm
:
true
,
timeout
:
150000
,
},
},
mocha
:
{
timeout
:
50000
,
},
solidity
:
'
0.7.6
'
,
ovm
:
{
...
...
integration-tests/package.json
View file @
685c8f25
...
...
@@ -13,6 +13,7 @@
"build:contracts"
:
"hardhat compile"
,
"build:contracts:ovm"
:
"hardhat compile --network optimism"
,
"test:integration"
:
"hardhat --network optimism test"
,
"test:integration:live"
:
"IS_LIVE_NETWORK=true hardhat --network optimism-live test"
,
"test:sync"
:
"hardhat --network optimism test sync-tests/*.spec.ts --no-compile"
,
"clean"
:
"rimraf cache artifacts artifacts-ovm cache-ovm"
},
...
...
@@ -20,6 +21,7 @@
"@eth-optimism/contracts"
:
"^0.4.2"
,
"@eth-optimism/core-utils"
:
"^0.5.0"
,
"@eth-optimism/hardhat-ovm"
:
"^0.2.2"
,
"@eth-optimism/message-relayer"
:
"^0.1.6"
,
"@ethersproject/providers"
:
"^5.0.24"
,
"@nomiclabs/hardhat-ethers"
:
"^2.0.2"
,
"@nomiclabs/hardhat-waffle"
:
"^2.0.1"
,
...
...
@@ -33,6 +35,7 @@
"chai"
:
"^4.3.3"
,
"chai-as-promised"
:
"^7.1.1"
,
"docker-compose"
:
"^0.23.8"
,
"dotenv"
:
"^10.0.0"
,
"envalid"
:
"^7.1.0"
,
"babel-eslint"
:
"^10.1.0"
,
"eslint"
:
"^7.27.0"
,
...
...
integration-tests/test/basic-l1-l2-communication.spec.ts
View file @
685c8f25
...
...
@@ -3,13 +3,13 @@ import { expect } from 'chai'
/* Imports: External */
import
{
Contract
,
ContractFactory
}
from
'
ethers
'
import
{
predeploys
,
getContractInterface
}
from
'
@eth-optimism/contracts
'
import
{
Direction
}
from
'
./shared/watcher-utils
'
/* Imports: Internal */
import
l1SimpleStorageJson
from
'
../artifacts/contracts/SimpleStorage.sol/SimpleStorage.json
'
import
l2SimpleStorageJson
from
'
../artifacts-ovm/contracts/SimpleStorage.sol/SimpleStorage.json
'
import
l2ReverterJson
from
'
../artifacts-ovm/contracts/Reverter.sol/Reverter.json
'
import
{
OptimismEnv
}
from
'
./shared/env
'
import
{
Direction
}
from
'
./shared/watcher-utils
'
import
{
OptimismEnv
,
useDynamicTimeoutForWithdrawals
}
from
'
./shared/env
'
describe
(
'
Basic L1<>L2 Communication
'
,
async
()
=>
{
let
Factory__L1SimpleStorage
:
ContractFactory
...
...
@@ -49,7 +49,9 @@ describe('Basic L1<>L2 Communication', async () => {
})
describe
(
'
L2 => L1
'
,
()
=>
{
it
(
'
should be able to perform a withdrawal from L2 -> L1
'
,
async
()
=>
{
it
(
'
should be able to perform a withdrawal from L2 -> L1
'
,
async
function
()
{
await
useDynamicTimeoutForWithdrawals
(
this
,
env
)
const
value
=
`0x
${
'
77
'
.
repeat
(
32
)}
`
// Send L2 -> L1 message.
...
...
@@ -58,7 +60,8 @@ describe('Basic L1<>L2 Communication', async () => {
L1SimpleStorage
.
interface
.
encodeFunctionData
(
'
setValue
'
,
[
value
]),
5000000
)
await
transaction
.
wait
()
await
env
.
relayXDomainMessages
(
transaction
)
await
env
.
waitForXDomainTransaction
(
transaction
,
Direction
.
L2ToL1
)
expect
(
await
L1SimpleStorage
.
msgSender
()).
to
.
equal
(
...
...
integration-tests/test/fee-payment.spec.ts
View file @
685c8f25
...
...
@@ -3,11 +3,12 @@ import chaiAsPromised from 'chai-as-promised'
chai
.
use
(
chaiAsPromised
)
/* Imports: External */
import
{
BigNumber
,
Contract
,
utils
}
from
'
ethers
'
import
{
ethers
,
BigNumber
,
Contract
,
utils
}
from
'
ethers
'
import
{
TxGasLimit
,
TxGasPrice
}
from
'
@eth-optimism/core-utils
'
import
{
predeploys
,
getContractInterface
}
from
'
@eth-optimism/contracts
'
/* Imports: Internal */
import
{
IS_LIVE_NETWORK
}
from
'
./shared/utils
'
import
{
OptimismEnv
}
from
'
./shared/env
'
import
{
Direction
}
from
'
./shared/watcher-utils
'
...
...
@@ -36,11 +37,11 @@ describe('Fee Payment Integration Tests', async () => {
it
(
'
Should estimateGas with recoverable L2 gasLimit
'
,
async
()
=>
{
const
gas
=
await
env
.
ovmEth
.
estimateGas
.
transfer
(
other
,
utils
.
parseEther
(
'
0.
5
'
)
utils
.
parseEther
(
'
0.
0000001
'
)
)
const
tx
=
await
env
.
ovmEth
.
populateTransaction
.
transfer
(
other
,
utils
.
parseEther
(
'
0.
5
'
)
utils
.
parseEther
(
'
0.
0000001
'
)
)
const
executionGas
=
await
(
env
.
ovmEth
.
provider
as
any
).
send
(
'
eth_estimateExecutionGas
'
,
...
...
@@ -51,7 +52,7 @@ describe('Fee Payment Integration Tests', async () => {
})
it
(
'
Paying a nonzero but acceptable gasPrice fee
'
,
async
()
=>
{
const
amount
=
utils
.
parseEther
(
'
0.
5
'
)
const
amount
=
utils
.
parseEther
(
'
0.
0000001
'
)
const
balanceBefore
=
await
env
.
l2Wallet
.
getBalance
()
const
feeVaultBalanceBefore
=
await
env
.
l2Wallet
.
provider
.
getBalance
(
ovmSequencerFeeVault
.
address
...
...
@@ -84,15 +85,23 @@ describe('Fee Payment Integration Tests', async () => {
await
expect
(
ovmSequencerFeeVault
.
withdraw
()).
to
.
be
.
rejected
})
it
(
'
should be able to withdraw fees back to L1 once the minimum is met
'
,
async
()
=>
{
it
(
'
should be able to withdraw fees back to L1 once the minimum is met
'
,
async
function
()
{
const
l1FeeWallet
=
await
ovmSequencerFeeVault
.
l1FeeWallet
()
const
balanceBefore
=
await
env
.
l1Wallet
.
provider
.
getBalance
(
l1FeeWallet
)
const
withdrawalAmount
=
await
ovmSequencerFeeVault
.
MIN_WITHDRAWAL_AMOUNT
()
const
l2WalletBalance
=
await
env
.
l2Wallet
.
getBalance
()
if
(
IS_LIVE_NETWORK
&&
l2WalletBalance
.
lt
(
withdrawalAmount
))
{
console
.
log
(
`NOTICE: must have at least
${
ethers
.
utils
.
formatEther
(
withdrawalAmount
)}
ETH on L2 to execute this test, skipping`
)
this
.
skip
()
}
// Transfer the minimum required to withdraw.
await
env
.
ovmEth
.
transfer
(
ovmSequencerFeeVault
.
address
,
await
ovmSequencerFeeVault
.
MIN_WITHDRAWAL_AMOUNT
()
)
await
env
.
ovmEth
.
transfer
(
ovmSequencerFeeVault
.
address
,
withdrawalAmount
)
const
vaultBalance
=
await
env
.
ovmEth
.
balanceOf
(
ovmSequencerFeeVault
.
address
...
...
integration-tests/test/native-eth.spec.ts
View file @
685c8f25
import
{
predeploys
}
from
'
@eth-optimism/contracts
'
import
{
expect
}
from
'
chai
'
/* Imports: External */
import
{
Wallet
,
utils
,
BigNumber
}
from
'
ethers
'
import
{
predeploys
}
from
'
@eth-optimism/contracts
'
/* Imports: Internal */
import
{
Direction
}
from
'
./shared/watcher-utils
'
import
{
...
...
@@ -9,7 +12,7 @@ import {
fundUser
,
PROXY_SEQUENCER_ENTRYPOINT_ADDRESS
,
}
from
'
./shared/utils
'
import
{
OptimismEnv
}
from
'
./shared/env
'
import
{
OptimismEnv
,
useDynamicTimeoutForWithdrawals
}
from
'
./shared/env
'
const
DEFAULT_TEST_GAS_L1
=
330
_000
const
DEFAULT_TEST_GAS_L2
=
1
_300_000
...
...
@@ -53,7 +56,7 @@ describe('Native ETH Integration Tests', async () => {
describe
(
'
estimateGas
'
,
()
=>
{
it
(
'
Should estimate gas for ETH transfer
'
,
async
()
=>
{
const
amount
=
utils
.
parseEther
(
'
0.
5
'
)
const
amount
=
utils
.
parseEther
(
'
0.
0000001
'
)
const
addr
=
'
0x
'
+
'
1234
'
.
repeat
(
10
)
const
gas
=
await
env
.
ovmEth
.
estimateGas
.
transfer
(
addr
,
amount
)
// Expect gas to be less than or equal to the target plus 1%
...
...
@@ -61,7 +64,7 @@ describe('Native ETH Integration Tests', async () => {
})
it
(
'
Should estimate gas for ETH withdraw
'
,
async
()
=>
{
const
amount
=
utils
.
parseEther
(
'
0.
5
'
)
const
amount
=
utils
.
parseEther
(
'
0.
0000001
'
)
const
gas
=
await
env
.
l2Bridge
.
estimateGas
.
withdraw
(
predeploys
.
OVM_ETH
,
amount
,
...
...
@@ -186,14 +189,13 @@ describe('Native ETH Integration Tests', async () => {
await
expect
(
env
.
l1Bridge
.
depositETH
(
DEFAULT_TEST_GAS_L2
,
data
,
{
value
:
depositAmount
,
gasLimit
:
4
_000_000
,
})
).
to
.
be
.
revertedWith
(
'
Transaction data size exceeds maximum for rollup transaction.
'
)
).
to
.
be
.
reverted
})
it
(
'
withdraw
'
,
async
()
=>
{
it
(
'
withdraw
'
,
async
function
()
{
await
useDynamicTimeoutForWithdrawals
(
this
,
env
)
const
withdrawAmount
=
BigNumber
.
from
(
3
)
const
preBalances
=
await
getBalances
(
env
)
expect
(
...
...
@@ -201,31 +203,43 @@ describe('Native ETH Integration Tests', async () => {
'
Cannot run withdrawal test before any deposits...
'
)
const
receipts
=
await
env
.
waitForXDomainTransaction
(
env
.
l2Bridge
.
withdraw
(
const
transaction
=
await
env
.
l2Bridge
.
withdraw
(
predeploys
.
OVM_ETH
,
withdrawAmount
,
DEFAULT_TEST_GAS_L2
,
'
0xFFFF
'
),
)
await
transaction
.
wait
()
await
env
.
relayXDomainMessages
(
transaction
)
const
receipts
=
await
env
.
waitForXDomainTransaction
(
transaction
,
Direction
.
L2ToL1
)
const
fee
=
receipts
.
tx
.
gasLimit
.
mul
(
receipts
.
tx
.
gasPrice
)
const
postBalances
=
await
getBalances
(
env
)
expect
(
postBalances
.
l1BridgeBalance
).
to
.
deep
.
eq
(
preBalances
.
l1BridgeBalance
.
sub
(
withdrawAmount
)
// Approximate because there's a fee related to relaying the L2 => L1 message and it throws off the math.
expectApprox
(
postBalances
.
l1BridgeBalance
,
preBalances
.
l1BridgeBalance
.
sub
(
withdrawAmount
),
{
upperPercentDeviation
:
1
}
)
expect
(
postBalances
.
l2UserBalance
).
to
.
deep
.
eq
(
preBalances
.
l2UserBalance
.
sub
(
withdrawAmount
.
add
(
fee
))
expectApprox
(
postBalances
.
l2UserBalance
,
preBalances
.
l2UserBalance
.
sub
(
withdrawAmount
.
add
(
fee
)),
{
upperPercentDeviation
:
1
}
)
expect
(
postBalances
.
l1UserBalance
).
to
.
deep
.
eq
(
preBalances
.
l1UserBalance
.
add
(
withdrawAmount
)
expectApprox
(
postBalances
.
l1UserBalance
,
preBalances
.
l1UserBalance
.
add
(
withdrawAmount
),
{
upperPercentDeviation
:
1
}
)
})
it
(
'
withdrawTo
'
,
async
()
=>
{
it
(
'
withdrawTo
'
,
async
function
()
{
await
useDynamicTimeoutForWithdrawals
(
this
,
env
)
const
withdrawAmount
=
BigNumber
.
from
(
3
)
const
preBalances
=
await
getBalances
(
env
)
...
...
@@ -235,14 +249,17 @@ describe('Native ETH Integration Tests', async () => {
'
Cannot run withdrawal test before any deposits...
'
)
const
receipts
=
await
env
.
waitForXDomainTransaction
(
env
.
l2Bridge
.
withdrawTo
(
const
transaction
=
await
env
.
l2Bridge
.
withdrawTo
(
predeploys
.
OVM_ETH
,
l1Bob
.
address
,
withdrawAmount
,
DEFAULT_TEST_GAS_L2
,
'
0xFFFF
'
),
)
await
transaction
.
wait
()
await
env
.
relayXDomainMessages
(
transaction
)
const
receipts
=
await
env
.
waitForXDomainTransaction
(
transaction
,
Direction
.
L2ToL1
)
const
fee
=
receipts
.
tx
.
gasLimit
.
mul
(
receipts
.
tx
.
gasPrice
)
...
...
@@ -260,7 +277,9 @@ describe('Native ETH Integration Tests', async () => {
)
})
it
(
'
deposit, transfer, withdraw
'
,
async
()
=>
{
it
(
'
deposit, transfer, withdraw
'
,
async
function
()
{
await
useDynamicTimeoutForWithdrawals
(
this
,
env
)
// 1. deposit
const
amount
=
utils
.
parseEther
(
'
1
'
)
await
env
.
waitForXDomainTransaction
(
...
...
@@ -282,15 +301,18 @@ describe('Native ETH Integration Tests', async () => {
// 3. do withdrawal
const
withdrawnAmount
=
utils
.
parseEther
(
'
0.95
'
)
const
receipts
=
await
env
.
waitForXDomainTransaction
(
env
.
l2Bridge
const
transaction
=
await
env
.
l2Bridge
.
connect
(
other
)
.
withdraw
(
predeploys
.
OVM_ETH
,
withdrawnAmount
,
DEFAULT_TEST_GAS_L1
,
'
0xFFFF
'
),
)
await
transaction
.
wait
()
await
env
.
relayXDomainMessages
(
transaction
)
const
receipts
=
await
env
.
waitForXDomainTransaction
(
transaction
,
Direction
.
L2ToL1
)
...
...
integration-tests/test/ovmcontext.spec.ts
View file @
685c8f25
import
{
expect
}
from
'
chai
'
/* Imports: External */
import
{
ethers
}
from
'
hardhat
'
import
{
injectL2Context
}
from
'
@eth-optimism/core-utils
'
import
{
expect
}
from
'
chai
'
import
{
sleep
,
l2Provider
,
l1Provider
,
getAddressManager
,
}
from
'
./shared/utils
'
import
{
Contract
,
BigNumber
}
from
'
ethers
'
/* Imports: Internal */
import
{
l2Provider
,
l1Provider
,
IS_LIVE_NETWORK
}
from
'
./shared/utils
'
import
{
OptimismEnv
}
from
'
./shared/env
'
import
{
getContractFactory
}
from
'
@eth-optimism/contracts
'
import
{
Contract
,
ContractFactory
,
Wallet
,
BigNumber
}
from
'
ethers
'
import
{
Direction
}
from
'
./shared/watcher-utils
'
/**
* These tests cover the OVM execution contexts. In the OVM execution
...
...
@@ -17,73 +16,51 @@ import { Contract, ContractFactory, Wallet, BigNumber } from 'ethers'
* must be equal to the blocknumber/timestamp of the L1 transaction.
*/
describe
(
'
OVM Context: Layer 2 EVM Context
'
,
()
=>
{
let
address
:
string
let
CanonicalTransactionChain
:
Contract
let
OVMMulticall
:
Contract
let
OVMContextStorage
:
Contract
const
L1Provider
=
l1Provider
const
L2Provider
=
injectL2Context
(
l2Provider
)
let
env
:
OptimismEnv
before
(
async
()
=>
{
const
env
=
await
OptimismEnv
.
new
()
// Create providers and signers
const
l1Wallet
=
env
.
l1Wallet
const
l2Wallet
=
env
.
l2Wallet
const
addressManager
=
env
.
addressManager
env
=
await
OptimismEnv
.
new
()
})
// deploy the contract
let
OVMMulticall
:
Contract
let
OVMContextStorage
:
Contract
beforeEach
(
async
()
=>
{
const
OVMContextStorageFactory
=
await
ethers
.
getContractFactory
(
'
OVMContextStorage
'
,
l2Wallet
)
OVMContextStorage
=
await
OVMContextStorageFactory
.
deploy
()
const
receipt
=
await
OVMContextStorage
.
deployTransaction
.
wait
()
address
=
OVMContextStorage
.
address
const
ctcAddress
=
await
addressManager
.
getAddress
(
'
OVM_CanonicalTransactionChain
'
env
.
l2Wallet
)
const
CanonicalTransactionChainFactory
=
getContractFactory
(
'
OVM_CanonicalTransactionChain
'
)
CanonicalTransactionChain
=
CanonicalTransactionChainFactory
.
connect
(
l1Wallet
).
attach
(
ctcAddress
)
const
OVMMulticallFactory
=
await
ethers
.
getContractFactory
(
'
OVMMulticall
'
,
l2Wallet
env
.
l2Wallet
)
OVMContextStorage
=
await
OVMContextStorageFactory
.
deploy
()
await
OVMContextStorage
.
deployTransaction
.
wait
()
OVMMulticall
=
await
OVMMulticallFactory
.
deploy
()
await
OVMMulticall
.
deployTransaction
.
wait
()
})
it
(
'
Enqueue: `block.number` and `block.timestamp` have L1 values
'
,
async
()
=>
{
for
(
let
i
=
0
;
i
<
5
;
i
++
)
{
const
l2Tip
=
await
L2Provider
.
getBlock
(
'
latest
'
)
const
tx
=
await
CanonicalTransactionChain
.
enqueue
(
let
numTxs
=
5
if
(
IS_LIVE_NETWORK
)
{
// Tests take way too long if we don't reduce the number of txs here.
numTxs
=
1
}
it
(
'
enqueue: `block.number` and `block.timestamp` have L1 values
'
,
async
()
=>
{
for
(
let
i
=
0
;
i
<
numTxs
;
i
++
)
{
const
tx
=
await
env
.
l1Messenger
.
sendMessage
(
OVMContextStorage
.
address
,
500
_000
,
'
0x
'
'
0x
'
,
2
_000_000
)
// Wait for the enqueue to be ingested
while
(
true
)
{
const
tip
=
await
L2Provider
.
getBlock
(
'
latest
'
)
if
(
tip
.
number
===
l2Tip
.
number
+
1
)
{
break
}
await
sleep
(
500
)
}
const
receipt
=
await
tx
.
wait
()
// Get the receipt
const
receipt
=
await
tx
.
wait
()
// The transaction did not revert
expect
(
receipt
.
status
).
to
.
equal
(
1
)
await
env
.
waitForXDomainTransaction
(
tx
,
Direction
.
L1ToL2
)
// Get the L1 block that the enqueue transaction was in so that
// the timestamp can be compared against the layer two contract
const
block
=
await
l1Provider
.
getBlock
(
receipt
.
blockNumber
)
...
...
@@ -96,14 +73,18 @@ describe('OVM Context: Layer 2 EVM Context', () => {
const
timestamp
=
await
OVMContextStorage
.
timestamps
(
i
)
expect
(
block
.
timestamp
).
to
.
deep
.
equal
(
timestamp
.
toNumber
())
}
})
})
.
timeout
(
150000
)
// this specific test takes a while because it involves L1 to L2 txs
it
(
'
should set correct OVM Context for `eth_call`
'
,
async
()
=>
{
const
tip
=
await
L2Provider
.
getBlockWithTransactions
(
'
latest
'
)
const
start
=
Math
.
max
(
0
,
tip
.
number
-
5
)
for
(
let
i
=
0
;
i
<
numTxs
;
i
++
)
{
// Make an empty transaction to bump the latest block number.
const
dummyTx
=
await
env
.
l2Wallet
.
sendTransaction
({
to
:
`0x
${
'
11
'
.
repeat
(
20
)}
`
,
data
:
'
0x
'
,
})
await
dummyTx
.
wait
()
for
(
let
i
=
start
;
i
<
tip
.
number
;
i
++
)
{
const
block
=
await
L2Provider
.
getBlockWithTransactions
(
i
)
const
block
=
await
L2Provider
.
getBlockWithTransactions
(
'
latest
'
)
const
[,
returnData
]
=
await
OVMMulticall
.
callStatic
.
aggregate
(
[
[
...
...
@@ -117,7 +98,7 @@ describe('OVM Context: Layer 2 EVM Context', () => {
OVMMulticall
.
interface
.
encodeFunctionData
(
'
getCurrentBlockNumber
'
),
],
],
{
blockTag
:
i
}
{
blockTag
:
block
.
number
}
)
const
timestamp
=
BigNumber
.
from
(
returnData
[
0
])
...
...
integration-tests/test/queue-ingestion.spec.ts
View file @
685c8f25
import
{
expect
}
from
'
chai
'
/* Imports: Internal */
import
{
providers
}
from
'
ethers
'
import
{
injectL2Context
}
from
'
@eth-optimism/core-utils
'
import
{
sleep
}
from
'
./shared/utils
'
import
{
OptimismEnv
}
from
'
./shared/env
'
/* Imports: External */
import
{
providers
}
from
'
ethers
'
import
{
expect
}
from
'
chai
'
import
{
OptimismEnv
}
from
'
./shared/env
'
import
{
Direction
}
from
'
./shared/watcher-utils
'
// This test ensures that the transactions which get `enqueue`d get
// added to the L2 blocks by the Sync Service (which queries the DTL)
describe
(
'
Queue Ingestion
'
,
()
=>
{
const
RETRIES
=
20
const
numTxs
=
5
let
startBlock
:
number
let
endBlock
:
number
let
env
:
OptimismEnv
let
l2Provider
:
providers
.
JsonRpcProvider
const
receipts
=
[]
before
(
async
()
=>
{
env
=
await
OptimismEnv
.
new
()
l2Provider
=
injectL2Context
(
env
.
l2Wallet
.
provider
as
any
)
})
// The transactions are enqueue'd with a `to` address of i.repeat(40)
// meaning that the `to` value is different each iteration in a deterministic
// way. They need to be inserted into the L2 chain in an ascending order.
// Keep track of the receipts so that the blockNumber can be compared
// against the `L1BlockNumber` on the tx objects.
before
(
async
()
=>
{
// Keep track of the L2 tip before submitting any transactions so that
// the subsequent transactions can be queried for in the next test
startBlock
=
(
await
l2Provider
.
getBlockNumber
())
+
1
endBlock
=
startBlock
+
numTxs
-
1
// Enqueue some transactions by building the calldata and then sending
// the transaction to Layer 1
for
(
let
i
=
0
;
i
<
numTxs
;
i
++
)
{
const
input
=
[
'
0x
'
+
`
${
i
}
`
.
repeat
(
40
),
500
_000
,
`0x0
${
i
}
`
]
const
calldata
=
env
.
ctc
.
interface
.
encodeFunctionData
(
'
enqueue
'
,
input
)
const
txResponse
=
await
env
.
l1Wallet
.
sendTransaction
({
data
:
calldata
,
to
:
env
.
ctc
.
address
,
})
const
receipt
=
await
txResponse
.
wait
()
receipts
.
push
(
receipt
)
}
})
// The batch submitter will notice that there are transactions
// that are in the queue and submit them. L2 will pick up the
// sequencer batch appended event and play the transactions.
it
(
'
should order transactions correctly
'
,
async
()
=>
{
// Wait until each tx from the previous test has
// been executed
let
i
:
number
for
(
i
=
0
;
i
<
RETRIES
;
i
++
)
{
const
tip
=
await
l2Provider
.
getBlockNumber
()
if
(
tip
>=
endBlock
)
{
break
}
await
sleep
(
1000
)
}
const
numTxs
=
5
if
(
i
===
RETRIES
)
{
throw
new
Error
(
'
timed out waiting for queued transactions to be inserted
'
// Enqueue some transactions by building the calldata and then sending
// the transaction to Layer 1
const
txs
=
[]
for
(
let
i
=
0
;
i
<
numTxs
;
i
++
)
{
const
tx
=
await
env
.
l1Messenger
.
sendMessage
(
`0x
${
`
${
i
}
`
.
repeat
(
40
)}
`
,
`0x0
${
i
}
`
,
1
_000_000
)
await
tx
.
wait
()
txs
.
push
(
tx
)
}
const
from
=
await
env
.
l1Wallet
.
getAddress
()
// Keep track of an index into the receipts list and
// increment it for each block fetched.
let
receiptIndex
=
0
// Fetch blocks
for
(
i
=
0
;
i
<
numTxs
;
i
++
)
{
const
block
=
await
l2Provider
.
getBlock
(
startBlock
+
i
)
const
hash
=
block
.
transactions
[
0
]
// Use as any hack because additional properties are
// added to the transaction response
const
tx
=
await
(
l2Provider
.
getTransaction
(
hash
)
as
any
)
for
(
let
i
=
0
;
i
<
numTxs
;
i
++
)
{
const
l1Tx
=
txs
[
i
]
const
l1TxReceipt
=
await
txs
[
i
].
wait
()
const
receipt
=
await
env
.
waitForXDomainTransaction
(
l1Tx
,
Direction
.
L1ToL2
)
const
l2Tx
=
(
await
l2Provider
.
getTransaction
(
receipt
.
remoteTx
.
hash
))
as
any
const
params
=
env
.
l2Messenger
.
interface
.
decodeFunctionData
(
'
relayMessage
'
,
l2Tx
.
data
)
// The `to` addresses are defined in the previous test and
// increment sequentially.
expect
(
tx
.
to
).
to
.
be
.
equal
(
'
0x
'
+
`
${
i
}
`
.
repeat
(
40
))
// The queue origin is Layer 1
expect
(
tx
.
queueOrigin
).
to
.
be
.
equal
(
'
l1
'
)
// the L1TxOrigin is equal to the Layer one from
expect
(
tx
.
l1TxOrigin
).
to
.
be
.
equal
(
from
.
toLowerCase
())
expect
(
typeof
tx
.
l1BlockNumber
).
to
.
be
.
equal
(
'
number
'
)
// Get the receipt and increment the recept index
const
receipt
=
receipts
[
receiptIndex
++
]
expect
(
tx
.
l1BlockNumber
).
to
.
be
.
equal
(
receipt
.
blockNumber
)
expect
(
params
.
_sender
.
toLowerCase
()).
to
.
equal
(
env
.
l1Wallet
.
address
.
toLowerCase
()
)
expect
(
params
.
_target
).
to
.
equal
(
'
0x
'
+
`
${
i
}
`
.
repeat
(
40
))
expect
(
l2Tx
.
queueOrigin
).
to
.
equal
(
'
l1
'
)
expect
(
l2Tx
.
l1TxOrigin
.
toLowerCase
()).
to
.
equal
(
env
.
l1Messenger
.
address
.
toLowerCase
()
)
expect
(
l2Tx
.
l1BlockNumber
).
to
.
equal
(
l1TxReceipt
.
blockNumber
)
}
})
})
.
timeout
(
100
_000
)
})
integration-tests/test/rpc.spec.ts
View file @
685c8f25
...
...
@@ -2,7 +2,6 @@ import {
injectL2Context
,
TxGasLimit
,
TxGasPrice
,
toRpcHexString
,
}
from
'
@eth-optimism/core-utils
'
import
{
Wallet
,
BigNumber
,
Contract
,
ContractFactory
}
from
'
ethers
'
import
{
ethers
}
from
'
hardhat
'
...
...
@@ -13,6 +12,8 @@ import {
DEFAULT_TRANSACTION
,
fundUser
,
expectApprox
,
L2_CHAINID
,
IS_LIVE_NETWORK
,
}
from
'
./shared/utils
'
import
chaiAsPromised
from
'
chai-as-promised
'
import
{
OptimismEnv
}
from
'
./shared/env
'
...
...
@@ -132,10 +133,9 @@ describe('Basic RPC tests', () => {
gasPrice
:
TxGasPrice
,
}
const
fee
=
tx
.
gasPrice
.
mul
(
tx
.
gasLimit
)
const
gasLimit
=
5920001
await
expect
(
env
.
l2Wallet
.
sendTransaction
(
tx
)).
to
.
be
.
rejectedWith
(
`fee too low:
${
fee
}
, use at least tx.gasLimit =
${
gasLimit
}
and tx.gasPrice =
${
TxGasPrice
.
toString
()}
`
`fee too low:
${
fee
}
, use at least tx.gasLimit =`
)
})
...
...
@@ -317,7 +317,15 @@ describe('Basic RPC tests', () => {
// canonical transaction chain. This test catches this by
// querying for the latest block and then waits and then queries
// the latest block again and then asserts that they are the same.
it
(
'
should return the same result when new transactions are not applied
'
,
async
()
=>
{
//
// Needs to be skipped on Prod networks because this test doesn't work when
// other people are sending transactions to the Sequencer at the same time
// as this test is running.
it
(
'
should return the same result when new transactions are not applied
'
,
async
function
()
{
if
(
IS_LIVE_NETWORK
)
{
this
.
skip
()
}
// Get latest block once to start.
const
prev
=
await
provider
.
getBlockWithTransactions
(
'
latest
'
)
...
...
@@ -341,7 +349,7 @@ describe('Basic RPC tests', () => {
describe
(
'
eth_chainId
'
,
()
=>
{
it
(
'
should get the correct chainid
'
,
async
()
=>
{
const
{
chainId
}
=
await
provider
.
getNetwork
()
expect
(
chainId
).
to
.
be
.
eq
(
420
)
expect
(
chainId
).
to
.
be
.
eq
(
L2_CHAINID
)
})
})
...
...
integration-tests/test/shared/env.ts
View file @
685c8f25
/* Imports: External */
import
{
Contract
,
utils
,
Wallet
}
from
'
ethers
'
import
{
TransactionResponse
}
from
'
@ethersproject/providers
'
import
{
getContractFactory
,
predeploys
}
from
'
@eth-optimism/contracts
'
import
{
Watcher
}
from
'
@eth-optimism/core-utils
'
import
{
Contract
,
utils
,
Wallet
}
from
'
ethers
'
import
{
getMessagesAndProofsForL2Transaction
}
from
'
@eth-optimism/message-relayer
'
/* Imports: Internal */
import
{
getAddressManager
,
l1Provider
,
...
...
@@ -11,6 +16,8 @@ import {
getOvmEth
,
getL1Bridge
,
getL2Bridge
,
IS_LIVE_NETWORK
,
sleep
,
}
from
'
./utils
'
import
{
initWatcher
,
...
...
@@ -18,7 +25,6 @@ import {
Direction
,
waitForXDomainTransaction
,
}
from
'
./watcher-utils
'
import
{
TransactionResponse
}
from
'
@ethersproject/providers
'
/// Helper class for instantiating a test environment with a funded account
export
class
OptimismEnv
{
...
...
@@ -27,6 +33,7 @@ export class OptimismEnv {
l1Bridge
:
Contract
l1Messenger
:
Contract
ctc
:
Contract
scc
:
Contract
// L2 Contracts
ovmEth
:
Contract
...
...
@@ -53,6 +60,7 @@ export class OptimismEnv {
this
.
l1Wallet
=
args
.
l1Wallet
this
.
l2Wallet
=
args
.
l2Wallet
this
.
ctc
=
args
.
ctc
this
.
scc
=
args
.
scc
}
static
async
new
():
Promise
<
OptimismEnv
>
{
...
...
@@ -85,10 +93,18 @@ export class OptimismEnv {
.
connect
(
l2Wallet
)
.
attach
(
predeploys
.
OVM_GasPriceOracle
)
const
sccAddress
=
await
addressManager
.
getAddress
(
'
OVM_StateCommitmentChain
'
)
const
scc
=
getContractFactory
(
'
OVM_StateCommitmentChain
'
)
.
connect
(
l1Wallet
)
.
attach
(
sccAddress
)
return
new
OptimismEnv
({
addressManager
,
l1Bridge
,
ctc
,
scc
,
l1Messenger
,
ovmEth
,
gasPriceOracle
,
...
...
@@ -106,4 +122,94 @@ export class OptimismEnv {
):
Promise
<
CrossDomainMessagePair
>
{
return
waitForXDomainTransaction
(
this
.
watcher
,
tx
,
direction
)
}
/**
* Relays all L2 => L1 messages found in a given L2 transaction.
*
* @param tx Transaction to find messages in.
*/
async
relayXDomainMessages
(
tx
:
Promise
<
TransactionResponse
>
|
TransactionResponse
):
Promise
<
void
>
{
tx
=
await
tx
let
messagePairs
=
[]
while
(
true
)
{
try
{
messagePairs
=
await
getMessagesAndProofsForL2Transaction
(
l1Provider
,
l2Provider
,
this
.
scc
.
address
,
predeploys
.
OVM_L2CrossDomainMessenger
,
tx
.
hash
)
break
}
catch
(
err
)
{
if
(
err
.
message
.
includes
(
'
unable to find state root batch for tx
'
))
{
await
sleep
(
5000
)
}
else
{
throw
err
}
}
}
for
(
const
{
message
,
proof
}
of
messagePairs
)
{
while
(
true
)
{
try
{
const
result
=
await
this
.
l1Messenger
.
connect
(
this
.
l1Wallet
)
.
relayMessage
(
message
.
target
,
message
.
sender
,
message
.
message
,
message
.
messageNonce
,
proof
)
await
result
.
wait
()
break
}
catch
(
err
)
{
if
(
err
.
message
.
includes
(
'
execution failed due to an exception
'
))
{
await
sleep
(
5000
)
}
else
if
(
err
.
message
.
includes
(
'
message has already been received
'
)
)
{
break
}
else
{
throw
err
}
}
}
}
}
}
/**
* Sets the timeout of a test based on the challenge period of the current network. If the
* challenge period is greater than 60s (e.g., on Mainnet) then we skip this test entirely.
*
* @param testctx Function context of the test to modify (i.e. `this` when inside a test).
* @param env Optimism environment used to resolve the StateCommitmentChain.
*/
export
const
useDynamicTimeoutForWithdrawals
=
async
(
testctx
:
any
,
env
:
OptimismEnv
)
=>
{
if
(
!
IS_LIVE_NETWORK
)
{
return
}
const
challengePeriod
=
await
env
.
scc
.
FRAUD_PROOF_WINDOW
()
if
(
challengePeriod
.
gt
(
60
))
{
console
.
log
(
`WARNING: challenge period is greater than 60s (
${
challengePeriod
.
toString
()}
s), skipping test`
)
testctx
.
skip
()
}
// 60s for state root batch to be published + (challenge period x 4)
const
timeoutMs
=
60000
+
challengePeriod
.
toNumber
()
*
1000
*
4
console
.
log
(
`NOTICE: inside a withdrawal test on a prod network, dynamically setting timeout to
${
timeoutMs
}
ms`
)
testctx
.
timeout
(
timeoutMs
)
}
integration-tests/test/shared/utils.ts
View file @
685c8f25
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
'
/* Imports: External */
import
{
Contract
,
Wallet
,
...
...
@@ -17,10 +10,24 @@ import {
BigNumber
,
utils
,
}
from
'
ethers
'
import
{
cleanEnv
,
str
,
num
}
from
'
envalid
'
import
{
getContractFactory
,
getContractInterface
,
predeploys
,
}
from
'
@eth-optimism/contracts
'
import
{
injectL2Context
,
remove0x
,
Watcher
}
from
'
@eth-optimism/core-utils
'
import
{
cleanEnv
,
str
,
num
,
bool
}
from
'
envalid
'
import
dotenv
from
'
dotenv
'
/* Imports: Internal */
import
{
Direction
,
waitForXDomainTransaction
}
from
'
./watcher-utils
'
export
const
GWEI
=
BigNumber
.
from
(
1
e9
)
if
(
process
.
env
.
IS_LIVE_NETWORK
===
'
true
'
)
{
dotenv
.
config
()
}
const
env
=
cleanEnv
(
process
.
env
,
{
L1_URL
:
str
({
default
:
'
http://localhost:9545
'
}),
L2_URL
:
str
({
default
:
'
http://localhost:8545
'
}),
...
...
@@ -37,6 +44,8 @@ const env = cleanEnv(process.env, {
ADDRESS_MANAGER
:
str
({
default
:
'
0x5FbDB2315678afecb367f032d93F642f64180aa3
'
,
}),
L2_CHAINID
:
num
({
default
:
420
}),
IS_LIVE_NETWORK
:
bool
({
default
:
false
}),
})
// The hardhat instance
...
...
@@ -64,6 +73,9 @@ export const PROXY_SEQUENCER_ENTRYPOINT_ADDRESS =
'
0x4200000000000000000000000000000000000004
'
export
const
OVM_ETH_ADDRESS
=
predeploys
.
OVM_ETH
export
const
L2_CHAINID
=
env
.
L2_CHAINID
export
const
IS_LIVE_NETWORK
=
env
.
IS_LIVE_NETWORK
export
const
getAddressManager
=
(
provider
:
any
)
=>
{
return
getContractFactory
(
'
Lib_AddressManager
'
)
.
connect
(
provider
)
...
...
ops/docker/Dockerfile.integration-tests
View file @
685c8f25
...
...
@@ -13,6 +13,9 @@ COPY --from=builder /optimism/node_modules ./node_modules
COPY --from=builder /optimism/packages/core-utils/package.json ./packages/core-utils/package.json
COPY --from=builder /optimism/packages/core-utils/dist ./packages/core-utils/dist
COPY --from=builder /optimism/packages/message-relayer/package.json ./packages/message-relayer/package.json
COPY --from=builder /optimism/packages/message-relayer/dist ./packages/message-relayer/dist
COPY --from=builder /optimism/packages/hardhat-ovm/package.json ./packages/hardhat-ovm/package.json
COPY --from=builder /optimism/packages/hardhat-ovm/dist ./packages/hardhat-ovm/dist
...
...
yarn.lock
View file @
685c8f25
...
...
@@ -5636,6 +5636,11 @@ dot-prop@^6.0.1:
dependencies:
is-obj "^2.0.0"
dotenv@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
dotenv@^8.2.0:
version "8.6.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b"
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment