Commit a99d44ae authored by Mark Tyneway's avatar Mark Tyneway

cleanup: delete actor-tests

The `actor-tests` package is no longer used and can be deleted.
parent d9d9bdbd
......@@ -79,7 +79,6 @@ jobs:
key: yarn-packages-v2-{{ checksum "yarn.lock" }}
paths:
- "node_modules"
- "packages/actor-tests/node_modules"
- "packages/balance-monitor/node_modules"
- "packages/chain-mon/node_modules"
- "packages/common-ts/node_modules"
......@@ -1116,13 +1115,6 @@ workflows:
- op-bindings-build:
requires:
- yarn-monorepo
- js-lint-test:
name: actor-tests-tests
coverage_flag: actor-tests-tests
package_name: actor-tests
dependencies: "(core-utils|sdk)"
requires:
- yarn-monorepo
- js-lint-test:
name: contracts-periphery-tests
coverage_flag: contracts-periphery-tests
......
# Legacy codebases
/l2geth @ethereum-optimism/legacy-reviewers
/packages/actor-tests @ethereum-optimism/legacy-reviewers
/packages/common-ts @ethereum-optimism/typescript-reviewers
/packages/contracts @ethereum-optimism/contract-reviewers
/packages/contracts-bedrock @ethereum-optimism/contract-reviewers
......
......@@ -8,14 +8,5 @@ steps:
- --cache=true
- --cache-ttl=48h
waitFor: ['-']
- name: 'gcr.io/kaniko-project/executor:latest'
args:
- --destination=us-central1-docker.pkg.dev/$PROJECT_ID/images/actor-tests-bedrock:$_TAG
- --destination=us-central1-docker.pkg.dev/$PROJECT_ID/images/actor-tests-bedrock:$COMMIT_SHA
- --dockerfile=./ops/docker/Dockerfile.packages
- --target=actor-tests-bedrock
- --cache=true
- --cache-ttl=48h
waitFor: ['-']
options:
machineType: N1_HIGHCPU_32
......@@ -34,7 +34,6 @@ flag_management:
- name: common-ts-tests
- name: contracts-tests
- name: core-utils-tests
- name: actor-tests-tests
- name: contracts-periphery-tests
- name: dtl-tests
- name: chain-mon-tests
......
......@@ -69,10 +69,6 @@ COPY ./packages ./packages
RUN yarn build
FROM base as actor-tests-bedrock
WORKDIR /opt/optimism/packages/actor-tests
ENTRYPOINT ["yarn", "run:bedrock"]
FROM base as fault-detector
WORKDIR /opt/optimism/packages/fault-detector
COPY ./ops/scripts/detector.sh .
......
module.exports = {
extends: '../../.eslintrc.js',
}
module.exports = {
...require('../../.prettierrc.js'),
}
# @eth-optimism/actor-tests
## 0.0.25
### Patch Changes
- Updated dependencies [f1e867177]
- Updated dependencies [a1b7ff9e3]
- Updated dependencies [197884eae]
- Updated dependencies [8133872ed]
- Updated dependencies [6eb05430d]
- Updated dependencies [afc2ab8c9]
- Updated dependencies [5063a69fb]
- Updated dependencies [aa854bdd8]
- @eth-optimism/contracts-bedrock@0.14.0
- @eth-optimism/sdk@2.1.0
## 0.0.24
### Patch Changes
- Updated dependencies [b16067a9f]
- Updated dependencies [be3315689]
- Updated dependencies [9a02079eb]
- Updated dependencies [98fbe9d22]
- @eth-optimism/contracts-bedrock@0.13.2
- @eth-optimism/sdk@2.0.2
## 0.0.23
### Patch Changes
- Updated dependencies [22c3885f5]
- Updated dependencies [66cafc00a]
- Updated dependencies [f52c07529]
- @eth-optimism/contracts-bedrock@0.13.1
- @eth-optimism/sdk@2.0.1
## 0.0.22
### Patch Changes
- Updated dependencies [cb19e2f9c]
- @eth-optimism/sdk@2.0.0
- @eth-optimism/contracts-bedrock@0.13.0
## 0.0.21
### Patch Changes
- Updated dependencies [80f2271f5]
- @eth-optimism/contracts-bedrock@0.12.1
- @eth-optimism/sdk@1.10.4
## 0.0.20
### Patch Changes
- Updated dependencies [7c0a2cc37]
- Updated dependencies [2865dd9b4]
- Updated dependencies [efc98d261]
- Updated dependencies [388f2c25a]
- @eth-optimism/contracts-bedrock@0.12.0
- @eth-optimism/sdk@1.10.3
## 0.0.19
### Patch Changes
- Updated dependencies [3c22333b8]
- Updated dependencies [5372c9f5b]
- @eth-optimism/contracts-bedrock@0.11.4
- @eth-optimism/sdk@1.10.2
## 0.0.18
### Patch Changes
- Updated dependencies [4964be480]
- @eth-optimism/contracts-bedrock@0.11.3
- @eth-optimism/sdk@1.10.1
## 0.0.17
### Patch Changes
- Updated dependencies [8784bc0bc]
- @eth-optimism/contracts-bedrock@0.11.2
- @eth-optimism/sdk@1.9.1
## 0.0.16
### Patch Changes
- Updated dependencies [fe80a9488]
- Updated dependencies [827fc7b04]
- Updated dependencies [a2166dcad]
- Updated dependencies [ff09ec22d]
- Updated dependencies [85dfa9fe2]
- Updated dependencies [ba8b94a60]
- Updated dependencies [d1f9098f9]
- Updated dependencies [0f8fc58ad]
- Updated dependencies [89f70c591]
- Updated dependencies [03940c3cb]
- @eth-optimism/contracts-bedrock@0.11.1
- @eth-optimism/sdk@1.9.0
## 0.0.15
### Patch Changes
- 1d3c749a2: Bumps the version of ts-node used
- 1d3c749a2: Updates the version of TypeScript
- Updated dependencies [43f33f39f]
- Updated dependencies [237a351f1]
- Updated dependencies [1d3c749a2]
- Updated dependencies [767585b07]
- Updated dependencies [c975c9620]
- Updated dependencies [1594678e0]
- Updated dependencies [1d3c749a2]
- Updated dependencies [136ea1785]
- Updated dependencies [4d13f0afe]
- Updated dependencies [7300a7ca7]
- @eth-optimism/contracts-bedrock@0.11.0
- @eth-optimism/sdk@1.8.0
- @eth-optimism/core-utils@0.12.0
## 0.0.14
### Patch Changes
- Updated dependencies [c025a1153]
- Updated dependencies [f8697a607]
- Updated dependencies [59adcaa09]
- Updated dependencies [c71500a7e]
- Updated dependencies [f49b71d50]
- Updated dependencies [1bfe79f20]
- Updated dependencies [ccaf5bc83]
- @eth-optimism/contracts-bedrock@0.10.0
- @eth-optimism/sdk@1.7.0
## 0.0.13
### Patch Changes
- Updated dependencies [52079cc12]
- Updated dependencies [13bfafb21]
- Updated dependencies [eeae96941]
- Updated dependencies [427831d86]
- @eth-optimism/contracts-bedrock@0.9.1
- @eth-optimism/sdk@1.6.11
## 0.0.12
### Patch Changes
- Updated dependencies [1e76cdb86]
- Updated dependencies [c02831144]
- Updated dependencies [d58b0a397]
- Updated dependencies [ff860ecf3]
- Updated dependencies [cc5adbc61]
- Updated dependencies [31c91ea74]
- Updated dependencies [87702c741]
- @eth-optimism/core-utils@0.11.0
- @eth-optimism/contracts-bedrock@0.9.0
- @eth-optimism/sdk@1.6.10
## 0.0.11
### Patch Changes
- Updated dependencies [db84317b]
- Updated dependencies [9b90c732]
- @eth-optimism/contracts-bedrock@0.8.3
- @eth-optimism/sdk@1.6.9
## 0.0.10
### Patch Changes
- Updated dependencies [7d7d9ba8]
- @eth-optimism/contracts-bedrock@0.8.2
- @eth-optimism/sdk@1.6.8
## 0.0.9
### Patch Changes
- Updated dependencies [35a7bb5e]
- Updated dependencies [b40913b1]
- Updated dependencies [a5e715c3]
- Updated dependencies [d18b8aa3]
- @eth-optimism/contracts-bedrock@0.8.1
- @eth-optimism/sdk@1.6.7
## 0.0.8
### Patch Changes
- Updated dependencies [6ed68fa3]
- Updated dependencies [3d4e8529]
- Updated dependencies [caf5dd3e]
- Updated dependencies [a6cbfee2]
- Updated dependencies [394a26ec]
- @eth-optimism/contracts-bedrock@0.8.0
- @eth-optimism/sdk@1.6.6
## 0.0.7
### Patch Changes
- Updated dependencies [cb5fed67]
- Updated dependencies [e2faaa8b]
- Updated dependencies [c427f0c0]
- Updated dependencies [e2faaa8b]
- Updated dependencies [d28ad592]
- Updated dependencies [76c8ee2d]
- @eth-optimism/contracts-bedrock@0.7.0
- @eth-optimism/sdk@1.6.5
## 0.0.6
### Patch Changes
- 7215f4ce: Bump ethers to 5.7.0 globally
- Updated dependencies [88dde7c8]
- Updated dependencies [7215f4ce]
- Updated dependencies [249a8ed6]
- Updated dependencies [7d7c4fdf]
- Updated dependencies [e164e22e]
- Updated dependencies [0bc1be45]
- Updated dependencies [af3e56b1]
- Updated dependencies [206f6033]
- Updated dependencies [88dde7c8]
- Updated dependencies [d7679ca4]
- Updated dependencies [8790156c]
- Updated dependencies [515685f4]
- @eth-optimism/contracts-bedrock@0.6.3
- @eth-optimism/core-utils@0.10.1
- @eth-optimism/sdk@1.6.4
## 0.0.5
### Patch Changes
- Updated dependencies [651a2883]
- @eth-optimism/contracts-bedrock@0.6.2
- @eth-optimism/sdk@1.6.3
## 0.0.4
### Patch Changes
- Updated dependencies [85232179]
- Updated dependencies [593f1cfb]
- Updated dependencies [cfa81f88]
- Updated dependencies [f78eb056]
- @eth-optimism/contracts-bedrock@0.6.1
- @eth-optimism/sdk@1.6.2
## 0.0.3
### Patch Changes
- Updated dependencies [7fdc490c]
- Updated dependencies [b27d0fa7]
- Updated dependencies [3d228a0e]
- Updated dependencies [dbfea116]
- Updated dependencies [63ef1949]
- @eth-optimism/contracts-bedrock@0.6.0
- @eth-optimism/sdk@1.6.1
- @eth-optimism/core-utils@0.10.0
## 0.0.2
### Patch Changes
- Updated dependencies [a095d544]
- Updated dependencies [cdf2163e]
- Updated dependencies [791f30bc]
- Updated dependencies [193befed]
- Updated dependencies [02420db0]
- Updated dependencies [94a8f287]
- Updated dependencies [3df66a9a]
- Updated dependencies [8323407f]
- Updated dependencies [3af9c7a9]
- Updated dependencies [7d03c5c0]
- Updated dependencies [fec22bfe]
- Updated dependencies [aa2949ef]
- Updated dependencies [9272253e]
- Updated dependencies [a1a73e64]
- Updated dependencies [f53c30b9]
- Updated dependencies [c025f418]
- Updated dependencies [329d21b6]
- Updated dependencies [35eafed0]
- Updated dependencies [3cde9205]
- @eth-optimism/contracts-bedrock@0.5.4
- @eth-optimism/sdk@1.6.0
(The MIT License)
Copyright 2020-2021 Optimism
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# Actor Tests
[![codecov](https://codecov.io/gh/ethereum-optimism/optimism/branch/develop/graph/badge.svg?token=0VTG7PG7YR&flag=actor-tests-tests)](https://codecov.io/gh/ethereum-optimism/optimism)
This README describes how to use the actor testing library to write new tests. If you're just looking for how to run test cases, check out the README [in the root of the repo](../README.md).
## Introduction
An "actor test" is a test case that simulates how a user might interact with Optimism. For example, an actor test might automate a user minting NFTs or swapping on Uniswap. Multiple actor tests are composed together in order to simulate real-world usage and help us optimize network performance under realistic load.
Actor tests are designed to catch race conditions, resource leaks, and performance regressions. They aren't a replacement for standard unit/integration tests, and aren't executed on every pull request since they take time to run.
This directory contains the actor testing framework as well as the tests themselves. The framework lives in `lib` and the tests live in this directory with `.test.ts` prefixes. Read on to find out more about how to use the framework to write actor tests of your own.
## CLI
Use the following command to run actor tests from the CLI:
```
ts-node actor-tests/lib/runner.ts -f <path-to-test-file> -c <concurrency> -r <num-runs> -t <time-to-run> --think-time <think time?>
```
You can also run `ts-node actor-tests/lib/runner.ts --help` for a full list of options with documentation.
**Arguments:**
- `path-to-test-file`: A path to the TS file containing the actor test.
- `concurrency`: How many workers to spawn.
- `run-for`: How long, in milliseconds, the worker should run.
- `think-time`: How long the runner should pause between test runs. Defaults to zero, meaning runs execute as fast as possible.
## Usage
### Test DSL
Actor tests are defined using a Mocha-like DSL. Follow along using the example below:
```typescript
import { actor, setupRun, setupActor, run } from './lib/convenience'
interface Context {
wallet: Wallet
}
actor('Value sender', () => {
let env: OptimismEnv
setupActor(async () => {
env = await OptimismEnv.new()
})
setupRun(async () => {
const wallet = Wallet.createRandom()
const tx = await env.l2Wallet.sendTransaction({
to: wallet.address,
value: utils.parseEther('0.01'),
})
await tx.wait()
return {
wallet: wallet.connect(env.l2Wallet.provider),
}
})
run(async (b, ctx: Context) => {
const randWallet = Wallet.createRandom().connect(env.l2Wallet.provider)
await b.bench('send funds', async () => {
const tx = await ctx.wallet.sendTransaction({
to: randWallet.address,
value: 0x42,
})
await tx.wait()
})
expect(await randWallet.getBalance()).to.deep.equal(BigNumber.from(0x42))
})
})
```
#### `actor(name: string, cb: () => void)`
Defines a new actor.
**Arguments:**
- `name`: Sets the actor's name. Used in logs and in outputted metrics.
- `cb`: The body of the actor. Cannot be async. All the other DSL methods (i.e. `setup*`, `run`) must be called within this callback.
#### `setupActor(cb: () => Promise<void>)`
Defines a setup method that gets called after the actor is instantiated but before any workers are spawned. Useful to set variables that need to be shared across all worker instances.
**Note:** Any variables set using `setupActor` must be thread-safe. Don't use `setupActor` to define a shared `Provider` instance, for example, since this will introduce nonce errors. Use `setupRun` to define a test context instead.
#### `setupRun(cb: () => Promise<T>)`
Defines a setup method that gets called inside each worker after it is instantiated but before any runs have executed. The value returned by the `setupRun` method becomes the worker's test context, which will be described in more detail below.
**Note:** While `setupRun` is called once in each worker, invocations of the `setupRun` callback are executed serially. This makes `setupRun` useful for nonce-dependent setup tasks like funding worker wallets.
#### `run<T>(cb: (b: Benchmarker, ctx: T) => Promise<void>)`
Defines what the actor actually does. The test runner will execute the `run` method multiple times depending on its configuration.
**Benchmarker**
Sections of the `run` method can be benchmarked using the `Benchmarker` argument to the `run` callback. Use the `Benchmarker` like this:
```typescript
b.bench('bench name', async () => {
// benchmarked code here
})
```
A summary of the benchmark's runtime and a count of how many times it succeeded/failed across each worker will be recorded in the run's metrics.
**Context**
The value returned by `setupRun` will be passed into the `ctx` argument to the `run` callback. Use the test context for values that need to be local to a particular worker. In the example, we use it to pass around the worker's wallet.
### Error Handling
Errors in setup methods cause the test process to crash. Errors in the `run` method are recorded in the test's metrics, and cause the run to be retried. The runtime of failed runs are not recorded.
It's useful to use `expect`/`assert` to make sure that actors are executing properly.
### Test Runner
The test runner is responsible for executing actor tests and managing their lifecycle. It can run in one of two modes:
1. Fixed run mode, which will execute the `run` method a fixed number of times.
2. Timed mode, which will will execute the `run` method as many times as possible until a period of time has elapsed.
Test lifecycle is as follows:
1. The runner collects all the actors it needs to run.
> Actors automatically register themselves with the default instance of the runner upon being `require()`d.
2. The runner executes each actor's `setupActor` method.
3. The runner spawns `n` workers.
4. The runner executes the `setupRun` method in each worker. The runner will wait for all `setupRun` methods to complete before continuing.
5. The runner executes the `run` method according to the mode described above.
## Metrics
The test runner prints metrics about each run to `stdout` on exit. This output can then be piped into Prometheus for visualization in Grafana or similar tools. Example metrics output might looks like:
```
# HELP actor_successful_bench_runs_total Count of total successful bench runs.
# TYPE actor_successful_bench_runs_total counter
actor_successful_bench_runs_total{actor_name="value_sender",bench_name="send_funds",worker_id="0"} 20
actor_successful_bench_runs_total{actor_name="value_sender",bench_name="send_funds",worker_id="1"} 20
# HELP actor_failed_bench_runs_total Count of total failed bench runs.
# TYPE actor_failed_bench_runs_total counter
# HELP actor_step_durations_ms_summary Summary of successful bench durations.
# TYPE actor_step_durations_ms_summary summary
actor_step_durations_ms_summary{quantile="0.5",actor_name="value_sender",bench_name="send_funds"} 1278.0819790065289
actor_step_durations_ms_summary{quantile="0.9",actor_name="value_sender",bench_name="send_funds"} 1318.4640210270882
actor_step_durations_ms_summary{quantile="0.95",actor_name="value_sender",bench_name="send_funds"} 1329.5195834636688
actor_step_durations_ms_summary{quantile="0.99",actor_name="value_sender",bench_name="send_funds"} 1338.0024159550667
actor_step_durations_ms_summary_sum{actor_name="value_sender",bench_name="send_funds"} 51318.10741400719
actor_step_durations_ms_summary_count{actor_name="value_sender",bench_name="send_funds"} 40
# HELP actor_successful_actor_runs_total Count of total successful actor runs.
# TYPE actor_successful_actor_runs_total counter
actor_successful_actor_runs_total{actor_name="value_sender"} 40
# HELP actor_failed_actor_runs_total Count of total failed actor runs.
# TYPE actor_failed_actor_runs_total counter
```
import { Wallet } from 'ethers'
import { expect } from 'chai'
import { actor, setupActor, run, setupRun } from '../lib/convenience'
import { devWalletsL2, l2Provider } from './utils'
import { Faucet } from '../lib/faucet'
interface Context {
wallet: Wallet
}
actor('Sender', () => {
let destWallet: Wallet
setupActor(async () => {
const devWallets = devWalletsL2()
destWallet = devWallets[0]
})
setupRun(async () => {
const faucet = new Faucet(process.env.FAUCET_URL, l2Provider)
const wallet = Wallet.createRandom().connect(l2Provider)
await faucet.drip(wallet.address)
return {
wallet,
}
})
run(async (b, ctx: Context, logger) => {
const { wallet } = ctx
logger.log(`Sending funds to ${destWallet.address}.`)
const tx = await wallet.sendTransaction({
to: destWallet.address,
value: 0x42,
})
logger.log(`Awaiting receipt for send tx ${tx.hash}.`)
const receipt = await tx.wait()
expect(receipt.status).to.eq(1)
logger.log(`Send completed in block ${receipt.blockNumber}.`)
})
})
{
"_format": "hh-sol-artifact-1",
"contractName": "ERC20",
"sourceName": "contracts/ERC20.sol",
"abi": [
{
"inputs": [
{
"internalType": "uint256",
"name": "_initialAmount",
"type": "uint256"
},
{
"internalType": "string",
"name": "_tokenName",
"type": "string"
},
{
"internalType": "uint8",
"name": "_decimalUnits",
"type": "uint8"
},
{
"internalType": "string",
"name": "_tokenSymbol",
"type": "string"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "_owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "_spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "_value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "_from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "_to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "_value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "_owner",
"type": "address"
},
{
"internalType": "address",
"name": "_spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "remaining",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "allowed",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "_value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_owner",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "balances",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "destroy",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_to",
"type": "address"
},
{
"internalType": "uint256",
"name": "_value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_from",
"type": "address"
},
{
"internalType": "address",
"name": "_to",
"type": "address"
},
{
"internalType": "uint256",
"name": "_value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
],
"bytecode": "0x60806040523480156200001157600080fd5b5060405162000a0c38038062000a0c833981016040819052620000349162000203565b336000908152602081815260409091208590556005859055835162000060916002919086019062000090565b506003805460ff191660ff841617905580516200008590600490602084019062000090565b5050505050620002cf565b8280546200009e9062000292565b90600052602060002090601f016020900481019282620000c257600085556200010d565b82601f10620000dd57805160ff19168380011785556200010d565b828001600101855582156200010d579182015b828111156200010d578251825591602001919060010190620000f0565b506200011b9291506200011f565b5090565b5b808211156200011b576000815560010162000120565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200015e57600080fd5b81516001600160401b03808211156200017b576200017b62000136565b604051601f8301601f19908116603f01168101908282118183101715620001a657620001a662000136565b81604052838152602092508683858801011115620001c357600080fd5b600091505b83821015620001e75785820183015181830184015290820190620001c8565b83821115620001f95760008385830101525b9695505050505050565b600080600080608085870312156200021a57600080fd5b845160208601519094506001600160401b03808211156200023a57600080fd5b62000248888389016200014c565b94506040870151915060ff821682146200026157600080fd5b6060870151919350808211156200027757600080fd5b5062000286878288016200014c565b91505092959194509250565b600181811c90821680620002a757607f821691505b60208210811415620002c957634e487b7160e01b600052602260045260246000fd5b50919050565b61072d80620002df6000396000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c80635c658165116100715780635c6581651461016357806370a082311461018e57806383197ef0146101b757806395d89b41146101bf578063a9059cbb146101c7578063dd62ed3e146101da57600080fd5b806306fdde03146100b9578063095ea7b3146100d757806318160ddd146100fa57806323b872dd1461011157806327e235e314610124578063313ce56714610144575b600080fd5b6100c1610213565b6040516100ce9190610574565b60405180910390f35b6100ea6100e53660046105e5565b6102a1565b60405190151581526020016100ce565b61010360055481565b6040519081526020016100ce565b6100ea61011f36600461060f565b61030d565b61010361013236600461064b565b60006020819052908152604090205481565b6003546101519060ff1681565b60405160ff90911681526020016100ce565b61010361017136600461066d565b600160209081526000928352604080842090915290825290205481565b61010361019c36600461064b565b6001600160a01b031660009081526020819052604090205490565b6101bd33ff5b005b6100c1610483565b6100ea6101d53660046105e5565b610490565b6101036101e836600461066d565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60028054610220906106a0565b80601f016020809104026020016040519081016040528092919081815260200182805461024c906106a0565b80156102995780601f1061026e57610100808354040283529160200191610299565b820191906000526020600020905b81548152906001019060200180831161027c57829003601f168201915b505050505081565b3360008181526001602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906102fc9086815260200190565b60405180910390a350600192915050565b6001600160a01b038316600081815260016020908152604080832033845282528083205493835290829052812054909190831180159061034d5750828110155b61038e5760405162461bcd60e51b815260206004820152600d60248201526c62616420616c6c6f77616e636560981b60448201526064015b60405180910390fd5b6001600160a01b038416600090815260208190526040812080548592906103b69084906106f1565b90915550506001600160a01b038516600090815260208190526040812080548592906103e3908490610709565b909155505060001981101561042b576001600160a01b038516600090815260016020908152604080832033845290915281208054859290610425908490610709565b90915550505b836001600160a01b0316856001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8560405161047091815260200190565b60405180910390a3506001949350505050565b60048054610220906106a0565b336000908152602081905260408120548211156104e65760405162461bcd60e51b8152602060048201526014602482015273696e73756666696369656e742062616c616e636560601b6044820152606401610385565b3360009081526020819052604081208054849290610505908490610709565b90915550506001600160a01b038316600090815260208190526040812080548492906105329084906106f1565b90915550506040518281526001600160a01b0384169033907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020016102fc565b600060208083528351808285015260005b818110156105a157858101830151858201604001528201610585565b818111156105b3576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b03811681146105e057600080fd5b919050565b600080604083850312156105f857600080fd5b610601836105c9565b946020939093013593505050565b60008060006060848603121561062457600080fd5b61062d846105c9565b925061063b602085016105c9565b9150604084013590509250925092565b60006020828403121561065d57600080fd5b610666826105c9565b9392505050565b6000806040838503121561068057600080fd5b610689836105c9565b9150610697602084016105c9565b90509250929050565b600181811c908216806106b457607f821691505b602082108114156106d557634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b60008219821115610704576107046106db565b500190565b60008282101561071b5761071b6106db565b50039056fea164736f6c6343000809000a",
"deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100b45760003560e01c80635c658165116100715780635c6581651461016357806370a082311461018e57806383197ef0146101b757806395d89b41146101bf578063a9059cbb146101c7578063dd62ed3e146101da57600080fd5b806306fdde03146100b9578063095ea7b3146100d757806318160ddd146100fa57806323b872dd1461011157806327e235e314610124578063313ce56714610144575b600080fd5b6100c1610213565b6040516100ce9190610574565b60405180910390f35b6100ea6100e53660046105e5565b6102a1565b60405190151581526020016100ce565b61010360055481565b6040519081526020016100ce565b6100ea61011f36600461060f565b61030d565b61010361013236600461064b565b60006020819052908152604090205481565b6003546101519060ff1681565b60405160ff90911681526020016100ce565b61010361017136600461066d565b600160209081526000928352604080842090915290825290205481565b61010361019c36600461064b565b6001600160a01b031660009081526020819052604090205490565b6101bd33ff5b005b6100c1610483565b6100ea6101d53660046105e5565b610490565b6101036101e836600461066d565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60028054610220906106a0565b80601f016020809104026020016040519081016040528092919081815260200182805461024c906106a0565b80156102995780601f1061026e57610100808354040283529160200191610299565b820191906000526020600020905b81548152906001019060200180831161027c57829003601f168201915b505050505081565b3360008181526001602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906102fc9086815260200190565b60405180910390a350600192915050565b6001600160a01b038316600081815260016020908152604080832033845282528083205493835290829052812054909190831180159061034d5750828110155b61038e5760405162461bcd60e51b815260206004820152600d60248201526c62616420616c6c6f77616e636560981b60448201526064015b60405180910390fd5b6001600160a01b038416600090815260208190526040812080548592906103b69084906106f1565b90915550506001600160a01b038516600090815260208190526040812080548592906103e3908490610709565b909155505060001981101561042b576001600160a01b038516600090815260016020908152604080832033845290915281208054859290610425908490610709565b90915550505b836001600160a01b0316856001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8560405161047091815260200190565b60405180910390a3506001949350505050565b60048054610220906106a0565b336000908152602081905260408120548211156104e65760405162461bcd60e51b8152602060048201526014602482015273696e73756666696369656e742062616c616e636560601b6044820152606401610385565b3360009081526020819052604081208054849290610505908490610709565b90915550506001600160a01b038316600090815260208190526040812080548492906105329084906106f1565b90915550506040518281526001600160a01b0384169033907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020016102fc565b600060208083528351808285015260005b818110156105a157858101830151858201604001528201610585565b818111156105b3576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b03811681146105e057600080fd5b919050565b600080604083850312156105f857600080fd5b610601836105c9565b946020939093013593505050565b60008060006060848603121561062457600080fd5b61062d846105c9565b925061063b602085016105c9565b9150604084013590509250925092565b60006020828403121561065d57600080fd5b610666826105c9565b9392505050565b6000806040838503121561068057600080fd5b610689836105c9565b9150610697602084016105c9565b90509250929050565b600181811c908216806106b457607f821691505b602082108114156106d557634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b60008219821115610704576107046106db565b500190565b60008282101561071b5761071b6106db565b50039056fea164736f6c6343000809000a",
"linkReferences": {},
"deployedLinkReferences": {}
}
import { Wallet, ContractFactory } from 'ethers'
import { actor, setupActor, run } from '../lib/convenience'
import { devWalletsL2 } from './utils'
import * as ERC20 from './contracts/ERC20.json'
actor('Deployer', () => {
let wallets: Wallet[]
setupActor(async () => {
wallets = devWalletsL2()
})
run(async (b, ctx, logger) => {
const sender = wallets[Math.floor(Math.random() * wallets.length)]
const contract = new ContractFactory(ERC20.abi, ERC20.bytecode).connect(
sender
)
logger.log(`Deploying contract with ${sender.address}.`)
const deployment = await contract.deploy(
Math.floor(1_000_000 * Math.random()),
'Test Token',
18,
'OP'
)
logger.log(
`Awaiting receipt for deployment tx ${deployment.deployTransaction.hash}.`
)
await deployment.deployed()
const receipt = await sender.provider.getTransactionReceipt(
deployment.deployTransaction.hash
)
logger.log(`Deployment completed in block ${receipt.blockNumber}.`)
})
})
import { utils, Wallet, constants } from 'ethers'
import {
CrossChainMessenger,
ETHBridgeAdapter,
StandardBridgeAdapter,
} from '@eth-optimism/sdk'
import { predeploys } from '@eth-optimism/contracts-bedrock'
import { sleep } from '@eth-optimism/core-utils'
import { actor, setupActor, run, setupRun } from '../lib/convenience'
import { l1Provider, l2Provider } from './utils'
import { Faucet } from '../lib/faucet'
interface Context {
wallet: Wallet
messenger: CrossChainMessenger
}
actor('Depositor', () => {
let contracts: any
setupActor(async () => {
contracts = require(process.env.CONTRACTS_JSON_PATH)
})
setupRun(async () => {
const wallet = Wallet.createRandom().connect(l1Provider)
const faucet = new Faucet(process.env.FAUCET_URL, l1Provider)
await faucet.drip(wallet.address)
return {
wallet,
messenger: new CrossChainMessenger({
l1SignerOrProvider: wallet.connect(l1Provider),
l2SignerOrProvider: wallet.connect(l2Provider),
l1ChainId: (await l1Provider.getNetwork()).chainId,
l2ChainId: (await l2Provider.getNetwork()).chainId,
bridges: {
Standard: {
Adapter: StandardBridgeAdapter,
l1Bridge: contracts.L1StandardBridgeProxy,
l2Bridge: predeploys.L2StandardBridge,
},
ETH: {
Adapter: ETHBridgeAdapter,
l1Bridge: contracts.L1StandardBridgeProxy,
l2Bridge: predeploys.L2StandardBridge,
},
},
contracts: {
l1: {
AddressManager: constants.AddressZero,
StateCommitmentChain: constants.AddressZero,
CanonicalTransactionChain: constants.AddressZero,
BondManager: constants.AddressZero,
L1StandardBridge: contracts.L1StandardBridgeProxy,
L1CrossDomainMessenger: contracts.L1CrossDomainMessengerProxy,
L2OutputOracle: contracts.L2OutputOracleProxy,
OptimismPortal: contracts.OptimismPortalProxy,
},
},
bedrock: true,
}),
}
})
run(async (b, ctx: Context, logger) => {
const { messenger } = ctx
const recipient = Wallet.createRandom().connect(l2Provider)
logger.log(`Depositing funds to ${recipient.address}.`)
const depositTx = await messenger.depositETH(utils.parseEther('0.000001'), {
recipient: recipient.address,
})
logger.log(`Awaiting receipt for deposit tx ${depositTx.hash}.`)
await depositTx.wait()
// Temporary until this is supported in the SDK.
for (let i = 0; i < 60; i++) {
const recipBal = await recipient.getBalance()
logger.log(`Polling L2 for deposit completion.`)
if (recipBal.eq(utils.parseEther('0.000001'))) {
logger.log('Deposit successful.')
return
}
await sleep(1000)
}
throw new Error('Timed out.')
})
})
import { providers, Wallet } from 'ethers'
const DEV_MNEMONIC =
'test test test test test test test test test test test junk'
export const l1Provider = new providers.JsonRpcProvider(process.env.L1_RPC)
export const l2Provider = new providers.JsonRpcProvider(process.env.L2_RPC)
export const devWalletsL2 = () => {
const wallets = []
for (let i = 0; i < 20; i++) {
wallets.push(
Wallet.fromMnemonic(DEV_MNEMONIC, `m/44'/60'/0'/0/${i}`).connect(
l2Provider
)
)
}
return wallets
}
import { performance } from 'perf_hooks'
import { Mutex } from 'async-mutex'
import { sleep } from '@eth-optimism/core-utils'
import {
sanitizeForMetrics,
benchDurationsSummary,
successfulBenchRunsTotal,
failedActorRunsTotal,
successfulActorRunsTotal,
failedBenchRunsTotal,
} from './metrics'
import { ActorLogger, WorkerLogger } from './logger'
// eslint-disable-next-line @typescript-eslint/no-empty-function
const asyncNoop = async () => {}
export type AsyncCB = () => Promise<void>
export interface Bencher {
bench: (name: string, cb: () => Promise<any>) => Promise<any>
}
export type RunCB<C> = (
b: Bencher,
ctx: C,
logger: WorkerLogger
) => Promise<void>
export interface RunOpts {
runs: number | null
runFor: number | null
concurrency: number
thinkTime: number
}
class Latch {
private n: number
private p: Promise<void>
private resolver: () => void
constructor(n: number) {
this.n = n
this.p = new Promise((resolve) => {
this.resolver = resolve
})
}
countDown() {
this.n--
if (this.n === 0) {
this.resolver()
}
}
wait() {
return this.p
}
}
export class Runner {
private readonly workerId: number
private readonly actor: Actor
private readonly mtx: Mutex
private readonly readyLatch: Latch
private readonly stepper: Bencher
private readonly logger: WorkerLogger
constructor(workerId: number, actor: Actor, mtx: Mutex, readyLatch: Latch) {
this.workerId = workerId
this.actor = actor
this.mtx = mtx
this.readyLatch = readyLatch
this.stepper = {
bench: this.bench,
}
this.logger = new WorkerLogger(this.actor.name, workerId)
}
bench = async (name: string, cb: () => Promise<void>) => {
const metricLabels = {
actor_name: sanitizeForMetrics(this.actor.name),
bench_name: sanitizeForMetrics(name),
}
const start = performance.now()
let res
try {
res = await cb()
} catch (e) {
failedBenchRunsTotal.inc({
...metricLabels,
worker_id: this.workerId,
})
throw e
}
benchDurationsSummary.observe(metricLabels, performance.now() - start)
successfulBenchRunsTotal.inc({
...metricLabels,
worker_id: this.workerId,
})
return res
}
async run(opts: RunOpts) {
const actor = this.actor
this.logger.log('Setting up.')
let ctx
try {
ctx = await this.mtx.runExclusive(this.actor.setupRun)
} finally {
this.readyLatch.countDown()
}
this.logger.log('Waiting for other workers to finish setup.')
await this.readyLatch.wait()
this.logger.log('Executing.')
const benchStart = performance.now()
let lastDurPrint = benchStart
let i = 0
const metricLabels = {
actor_name: sanitizeForMetrics(this.actor.name),
}
while (true) {
const now = performance.now()
if (
(opts.runs && i === opts.runs) ||
(opts.runFor && now - benchStart >= opts.runFor)
) {
this.logger.log(`Worker exited.`)
break
}
try {
await this.actor.run(this.stepper, ctx, this.logger)
} catch (e) {
console.error('Error in actor run:')
console.error(`Benchmark name: ${actor.name}`)
console.error(`Worker ID: ${this.workerId}`)
console.error(`Run index: ${i}`)
console.error('Stack trace:')
console.error(e)
failedActorRunsTotal.inc(metricLabels)
await sleep(1000)
continue
}
successfulActorRunsTotal.inc(metricLabels)
i++
if (
(opts.runs && (i % 10 === 0 || i === opts.runs)) ||
now - lastDurPrint > 10000
) {
this.logger.log(`Completed run ${i} of ${opts.runs}.`)
}
if (opts.runFor && now - lastDurPrint > 10000) {
const runningFor = Math.floor(now - benchStart)
this.logger.log(`Running for ${runningFor} of ${opts.runFor} ms.`)
lastDurPrint = now
}
if (opts.thinkTime > 0) {
await sleep(opts.thinkTime)
}
}
await this.mtx.runExclusive(() => this.actor.tearDownRun(ctx))
}
}
export class Actor {
public readonly name: string
private _setupEnv: AsyncCB = asyncNoop
private _tearDownEnv: AsyncCB = asyncNoop
private _setupRun: <C>() => Promise<C> = asyncNoop as any
private _tearDownRun: <C>(ctx: C) => Promise<void> = asyncNoop as any
// eslint-disable-next-line @typescript-eslint/no-empty-function
private _run: <C>(b: Bencher, ctx: C, logger: WorkerLogger) => Promise<void> =
asyncNoop
private logger: ActorLogger
constructor(name: string) {
this.name = name
this.logger = new ActorLogger(this.name)
}
get setupEnv(): AsyncCB {
return this._setupEnv
}
set setupEnv(value: AsyncCB) {
this._setupEnv = value
}
get tearDownEnv(): AsyncCB {
return this._tearDownEnv
}
set tearDownEnv(value: AsyncCB) {
this._tearDownEnv = value
}
get setupRun(): <C>() => Promise<C> {
return this._setupRun
}
set setupRun(value: () => Promise<any>) {
this._setupRun = value
}
get tearDownRun(): <C>(ctx: C) => Promise<void> {
return this._tearDownRun
}
set tearDownRun(value: (ctx: any) => Promise<any>) {
this._tearDownRun = value
}
get run(): RunCB<any> {
return this._run
}
set run(cb: RunCB<any>) {
this._run = cb
}
async exec(opts: RunOpts) {
this.logger.log('Setting up.')
try {
await this.setupEnv()
} catch (e) {
console.error(`Error in setupEnv hook for actor "${this.name}":`)
console.error(e)
return
}
this.logger.log('Starting.')
const parallelRuns = []
const mtx = new Mutex()
const latch = new Latch(opts.concurrency)
for (let i = 0; i < opts.concurrency; i++) {
const runner = new Runner(i, this, mtx, latch)
parallelRuns.push(runner.run(opts))
}
await Promise.all(parallelRuns)
this.logger.log('Tearing down.')
try {
await this.tearDownEnv()
} catch (e) {
console.error(`Error in after hook for benchmark "${this.name}":`)
console.error(e)
return
}
this.logger.log('Teardown complete.')
}
}
export class Runtime {
private actors: Actor[] = []
addActor(actor: Actor) {
this.actors.push(actor)
}
async run(opts: Partial<RunOpts>) {
opts = {
runs: 1,
concurrency: 1,
runFor: null,
thinkTime: 0,
...(opts || {}),
}
if (opts.runFor) {
opts.runs = null
}
for (const actor of this.actors) {
await actor.exec(opts as RunOpts)
}
}
}
import { AsyncCB, Actor, RunCB, Runtime } from './actor'
export const defaultRuntime = new Runtime()
let currBenchmark: Actor | null = null
export const actor = (name: string, cb: () => void) => {
if (currBenchmark) {
throw new Error('Cannot call actor within actor.')
}
currBenchmark = new Actor(name)
cb()
defaultRuntime.addActor(currBenchmark)
currBenchmark = null
}
export const setupActor = (cb: AsyncCB) => {
if (!currBenchmark) {
throw new Error('Cannot call setupEnv outside of actor.')
}
currBenchmark.setupEnv = cb
}
export const setupRun = (cb: () => Promise<any>) => {
if (!currBenchmark) {
throw new Error('Cannot call setupRun outside of actor.')
}
currBenchmark.setupRun = cb
}
export const tearDownRun = (cb: AsyncCB) => {
if (!currBenchmark) {
throw new Error('Cannot call tearDownRun outside of actor.')
}
currBenchmark.tearDownRun = cb
}
export const run = (cb: RunCB<any>) => {
if (!currBenchmark) {
throw new Error('Cannot call run outside of actor.')
}
currBenchmark.run = cb
}
export const retryOnGasTooLow = async (cb: () => Promise<any>) => {
while (true) {
try {
return await cb()
} catch (e) {
if (e.toString().includes('gas price too low')) {
await new Promise((resolve) => setTimeout(resolve, 100))
continue
}
throw e
}
}
}
import { ethers, providers } from 'ethers'
import { fetchJson, getAddress } from 'ethers/lib/utils'
export class Faucet {
private url: string
private provider: providers.Provider
constructor(url: string, provider: providers.Provider) {
this.url = url
this.provider = provider
}
public async drip(
recipient: string
): Promise<ethers.providers.TransactionReceipt> {
const res = await fetchJson(
`${this.url}/api/claim`,
JSON.stringify({
address: getAddress(recipient),
})
)
const txHash = res.tx_hash
return this.provider.waitForTransaction(txHash, 0, 30000)
}
}
import { sanitizeForMetrics } from './metrics'
abstract class Logger {
log(msg: string) {
const date = new Date()
process.stderr.write(`[${date.toISOString()}] ${msg}\n`)
}
}
export class ActorLogger extends Logger {
private readonly name: string
constructor(name: string) {
super()
this.name = name
}
log(msg: string) {
super.log(`[actor:${sanitizeForMetrics(this.name)}] ${msg}`)
}
}
export class WorkerLogger extends Logger {
private readonly name: string
private readonly workerId: number
constructor(name: string, workerId: number) {
super()
this.name = name
this.workerId = workerId
}
log(msg: string) {
super.log(
`[bench:${sanitizeForMetrics(this.name)}] [wid:${this.workerId}] ${msg}`
)
}
}
import fs from 'fs'
import http from 'http'
import url from 'url'
import client from 'prom-client'
export const metricsRegistry = new client.Registry()
const metricName = (name: string) => {
return `actor_${name}`
}
export const successfulBenchRunsTotal = new client.Counter({
name: metricName('successful_bench_runs_total'),
help: 'Count of total successful bench runs.',
labelNames: ['actor_name', 'bench_name', 'worker_id'] as const,
registers: [metricsRegistry],
})
export const failedBenchRunsTotal = new client.Counter({
name: metricName('failed_bench_runs_total'),
help: 'Count of total failed bench runs.',
labelNames: ['actor_name', 'bench_name', 'worker_id'] as const,
registers: [metricsRegistry],
})
export const benchDurationsSummary = new client.Summary({
name: metricName('step_durations_ms_summary'),
help: 'Summary of successful bench durations.',
percentiles: [0.5, 0.9, 0.95, 0.99],
labelNames: ['actor_name', 'bench_name'] as const,
registers: [metricsRegistry],
})
export const successfulActorRunsTotal = new client.Counter({
name: metricName('successful_actor_runs_total'),
help: 'Count of total successful actor runs.',
labelNames: ['actor_name'] as const,
registers: [metricsRegistry],
})
export const failedActorRunsTotal = new client.Counter({
name: metricName('failed_actor_runs_total'),
help: 'Count of total failed actor runs.',
labelNames: ['actor_name'] as const,
registers: [metricsRegistry],
})
export const sanitizeForMetrics = (input: string) => {
return input.toLowerCase().replace(/ /gi, '_')
}
export const dumpMetrics = async (filename: string) => {
const metrics = await metricsRegistry.metrics()
await fs.promises.writeFile(filename, metrics, {
flag: 'w+',
})
}
export const serveMetrics = (port: number) => {
const server = http.createServer(async (req, res) => {
const route = url.parse(req.url).pathname
if (route !== '/metrics') {
res.writeHead(404)
res.end()
return
}
res.setHeader('Content-Type', metricsRegistry.contentType)
res.end(await metricsRegistry.metrics())
})
server.listen(port)
}
import * as path from 'path'
import { Command } from 'commander'
import { defaultRuntime } from './convenience'
import { RunOpts } from './actor'
import { serveMetrics } from './metrics'
import pkg from '../package.json'
const program = new Command()
program.version(pkg.version)
program.name('actor-tests')
program
.requiredOption('-f, --file <file>', 'test file to run')
.option('-r, --runs <n>', 'number of runs. cannot be use with -t/--time')
.option(
'-t, --time <ms>',
'how long to run in milliseconds. cannot be used with -r/--runs'
)
.option('-c, --concurrency <n>', 'number of concurrent workers to spawn', '1')
.option('--think-time <n>', 'how long to wait between each run', '0')
.option(
'-s, --serve [port]',
'Serve metrics with optional port number',
'8545'
)
program.parse(process.argv)
const options = program.opts()
const testFile = options.file
const runsNum = Number(options.runs)
const timeNum = Number(options.time)
const concNum = Number(options.concurrency)
const thinkNum = Number(options.thinkTime)
const shouldServeMetrics = options.serve !== undefined
const metricsPort = options.serve || 8545
if (isNaN(runsNum) && isNaN(timeNum)) {
console.error('Must define either a number of runs or how long to run.')
process.exit(1)
}
if (isNaN(concNum) || concNum <= 0) {
console.error('Invalid concurrency value.')
process.exit(1)
}
if (isNaN(thinkNum) || thinkNum < 0) {
console.error('Invalid think time value.')
process.exit(1)
}
try {
require(path.resolve(path.join(process.cwd(), testFile)))
} catch (e) {
console.error(`Invalid test file ${testFile}:`)
console.error(e)
process.exit(1)
}
const opts: Partial<RunOpts> = {
runFor: timeNum,
concurrency: concNum,
thinkTime: thinkNum,
runs: runsNum,
}
if (shouldServeMetrics) {
process.stderr.write(`Serving metrics on http://0.0.0.0:${metricsPort}.\n`)
serveMetrics(metricsPort)
}
defaultRuntime
.run(opts)
.then(() => {
process.stderr.write('Run complete.\n')
process.exit(0)
})
.catch((err) => {
console.error('Error:')
console.error(err)
process.exit(1)
})
{
"name": "@eth-optimism/actor-tests",
"version": "0.0.25",
"description": "A library and suite of tests to stress test Optimism Bedrock.",
"license": "MIT",
"author": "",
"main": "index.js",
"directories": {
"lib": "lib"
},
"scripts": {
"lint": "yarn lint:fix && yarn lint:check",
"lint:check": "eslint . --max-warnings=0",
"lint:fix": "yarn lint:check --fix",
"pre-commit": "lint-staged",
"run:bedrock": "ts-node ./lib/runner.ts -f",
"test": "echo 'No tests specified.'",
"test:coverage": "yarn test"
},
"dependencies": {
"@eth-optimism/contracts-bedrock": "0.14.0",
"@eth-optimism/core-utils": "^0.12.0",
"@eth-optimism/sdk": "^2.1.0",
"@types/chai": "^4.2.18",
"@types/chai-as-promised": "^7.1.4",
"async-mutex": "^0.3.2",
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
"commander": "^8.3.0",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsdoc": "^35.1.2",
"eslint-plugin-prefer-arrow": "^1.2.3",
"eslint-plugin-prettier": "^3.4.0",
"ethers": "^5.7.0",
"prom-client": "^14.0.1",
"typescript": "^4.9.3"
}
}
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"resolveJsonModule": true
},
"include": [
"./lib/**/*.ts",
"./bedrock/**/*.(ts|json)",
"./package.json"
]
}
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