Commit f106ee49 authored by Ori Pomerantz's avatar Ori Pomerantz Committed by GitHub

Merge branch 'develop' into qbzzt/230608-docs-opstack-freeze-code

parents 35ee7718 818bc9f4

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

{
"$schema": "https://unpkg.com/@changesets/config@1.6.0/schema.json",
"changelog": "@changesets/cli/changelog",
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
"changelog": ["@changesets/changelog-github", { "repo": "ethereum-optimism/optimism" }],
"commit": false,
"linked": [],
"fixed": [],
"linked": [["@eth-optimism/contracts-bedrock", "@eth-optimism/contracts-ts"]],
"access": "public",
"baseBranch": "master",
"baseBranch": "develop",
"updateInternalDependencies": "patch",
"ignore": []
}
---
'@eth-optimism/message-relayer': major
'@eth-optimism/fault-detector': major
'@eth-optimism/sdk': major
---
Make optimism/sdk default to bedrock mode
---
'@eth-optimism/fault-detector': patch
---
Add better source maps and developer support
This diff is collapsed.
......@@ -12,6 +12,3 @@ build/_workspace
build/bin
build/_bin
tests/testdata
l2geth/signer/fourbyte
l2geth/cmd/puppeth
l2geth/cmd/clef
3b1129b5bc43ba22a9bcf4e4323c5a9df0023140
# Legacy codebases
/l2geth @ethereum-optimism/legacy-reviewers
/packages/common-ts @ethereum-optimism/typescript-reviewers
/packages/contracts @ethereum-optimism/contract-reviewers
/packages/contracts-bedrock @ethereum-optimism/contract-reviewers
/packages/contracts-periphery @ethereum-optimism/contract-reviewers
/packages/core-utils @ethereum-optimism/legacy-reviewers
/packages/chain-mon @smartcontracts
/packages/fault-detector @ethereum-optimism/devxpod
/packages/hardhat-deploy-config @ethereum-optimism/legacy-reviewers
/packages/migration-data @ethereum-optimism/legacy-reviewers
/packages/chain-mon @ethereum-optimism/devxpod
/packages/replica-healthcheck @ethereum-optimism/legacy-reviewers
/packages/sdk @ethereum-optimism/devxpod
/packages/atst @ethereum-optimism/devxpod
# Bedrock codebases
/bedrock-devnet @ethereum-optimism/go-reviewers
/cannon @ethereum-optimism/go-reviewers
/op-batcher @ethereum-optimism/go-reviewers
/op-chain-ops @ethereum-optimism/go-reviewers
/op-e2e @ethereum-optimism/go-reviewers
......@@ -39,3 +35,11 @@
/infra @ethereum-optimism/infra-reviewers
/specs @ethereum-optimism/contract-reviewers @ethereum-optimism/go-reviewers
/endpoint-monitor @ethereum-optimism/infra-reviewers
# Don't add owners if only package.json is updated
/packages/*/package.json
/*/package.json
# JavaScript Releases
/packages/*/CHANGELOG.md @ethereum-optimism/release-managers
/*/CHANGELOG.md @ethereum-optimism/release-managers
name: Setup
description: Common setup steps used by our workflows
runs:
using: composite
steps:
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8.6.5
- name: Setup node 16.x
uses: actions/setup-node@v3
with:
node-version-file: .nvmrc
registry-url: https://registry.npmjs.org
cache: pnpm
- name: Setup foundry
uses: foundry-rs/foundry-toolchain@v1
- name: Install node dependencies
shell: bash
run: pnpm install --frozen-lockfile
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v3
with:
main-branch-name: "develop"
- run: |
echo "nx using following shas:"
echo "BASE: ${{ env.NX_BASE }}"
echo "HEAD: ${{ env.NX_HEAD }}"
shell: bash
---
C-Protocol-Critical:
- 'packages/contracts-bedrock/**/*.sol'
- 'l2geth/**/*.go'
This diff is collapsed.
name: Publish Docker images (canary)
on:
# enable users to manually trigger with workflow_dispatch
workflow_dispatch:
inputs:
customImageName:
description: 'Custom Docker Image Tag (keep empty for git hash)'
required: false
default: '0.0.0-rc-0'
jobs:
canary-publish:
name: Publish Packages (canary)
runs-on: ubuntu-latest
# map the step outputs to job outputs
outputs:
fault-mon: ${{ steps.packages.outputs.fault-mon }}
balance-mon: ${{ steps.packages.outputs.balance-mon }}
drippie-mon: ${{ steps.packages.outputs.drippie-mon }}
wd-mon: ${{ steps.packages.outputs.wd-mon }}
replica-healthcheck: ${{ steps.packages.outputs.replica-healthcheck }}
canary-docker-tag: ${{ steps.docker-image-name.outputs.canary-docker-tag }}
op-exporter: ${{ steps.packages.outputs.op-exporter }}
endpoint-monitor: ${{ steps.packages.outputs.endpoint-monitor }}
steps:
- name: Check out source code
uses: actions/checkout@v2
with:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0
- name: Docker Image Name
id: docker-image-name
run: |
if [ ${CUSTOM_IMAGE_NAME} == '' ]
then
echo "::set-output name=canary-docker-tag::${GITHUB_SHA::8}"
else
echo "::set-output name=canary-docker-tag::prerelease-${CUSTOM_IMAGE_NAME}"
fi
env:
CUSTOM_IMAGE_NAME: ${{ github.event.inputs.customImageName }}
fault-mon:
name: Publish fault-mon Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
if: needs.canary-publish.outputs.fault-mon != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.packages
target: fault-mon
push: true
tags: ethereumoptimism/fault-mon:${{ needs.canary-publish.outputs.canary-docker-tag }}
balance-mon:
name: Publish Balance Monitor Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
if: needs.canary-publish.outputs.balance-mon != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.packages
target: balance-mon
push: true
tags: ethereumoptimism/balance-mon:${{ needs.canary-publish.outputs.canary-docker-tag }}
drippie-mon:
name: Publish Drippie Monitor Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
if: needs.canary-publish.outputs.drippie-mon != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.packages
target: drippie-mon
push: true
tags: ethereumoptimism/drippie-mon:${{ needs.canary-publish.outputs.canary-docker-tag }}
wd-mon:
name: Publish Withdrawal Monitor Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
if: needs.canary-publish.outputs.wd-mon != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.packages
target: wd-mon
push: true
tags: ethereumoptimism/wd-mon:${{ needs.canary-publish.outputs.canary-docker-tag }}
replica-healthcheck:
name: Publish Replica Healthcheck Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
if: needs.canary-publish.outputs.replica-healthcheck != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.packages
target: replica-healthcheck
push: true
tags: ethereumoptimism/replica-healthcheck:${{ needs.canary-publish.outputs.canary-docker-tag }}
op-exporter:
name: Publish op-exporter Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
if: needs.canary-publish.outputs.op-exporter != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Set build args
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./op-exporter/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./op-exporter/Dockerfile=
push: true
tags: ethereumoptimism/op-exporter:${{ needs.canary-publish.outputs.op-exporter }}
build-args: |
GITDATE=${{ steps.build_args.outputs.GITDATE }}
GITCOMMIT=${{ steps.build_args.outputs.GITCOMMIT }}
GITVERSION=${{ steps.build_args.outputs.GITVERSION }}
endpoint-monitor:
name: Publish endpoint-monitor Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
if: needs.canary-publish.outputs.endpoint-monitor != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Set build args
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./endpoint-monitor/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./endpoint-monitor/Dockerfile
push: true
tags: ethereumoptimism/endpoint-monitor:${{ needs.canary-publish.outputs.endpoint-monitor }}
build-args: |
GITDATE=${{ steps.build_args.outputs.GITDATE }}
GITCOMMIT=${{ steps.build_args.outputs.GITCOMMIT }}
GITVERSION=${{ steps.build_args.outputs.GITVERSION }}
name: Release snapshot
# Releases a snapshot release when new commits merge to develop
# This ensures the release process is working as expected as well as always gives us a release
# we should move to a single branch in near future
on:
workflow_dispatch:
push:
branches:
- develop
jobs:
snapshot-snapshot:
name: Publish snapshot release to npm
if: github.repository == 'ethereum-optimism/optimism'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: recursive
fetch-depth: 0
- name: Setup
uses: ./.github/actions/setup
- name: Set deployment token
run: npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish snapshots
uses: seek-oss/changesets-snapshot@v0
with:
pre-publish: pnpm nx run-many --target=build --skip-nx-cache
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
name: Release
name: Release and version
on:
workflow_dispatch:
push:
branches:
- master
- develop
# Always wait for previous release to finish before releasing again
concurrency: ${{ github.workflow }}-${{ github.ref }}
......@@ -12,52 +13,33 @@ jobs:
release:
name: Release
runs-on: ubuntu-latest
if: github.repository == 'ethereum-optimism/optimism'
# map the step outputs to job outputs
outputs:
l2geth: ${{ steps.packages.outputs.l2geth }}
fault-detector: ${{ steps.packages.outputs.fault-detector }}
fault-mon: ${{ steps.packages.outputs.fault-mon }}
balance-mon: ${{ steps.packages.outputs.drippie-mon }}
drippie-mon: ${{ steps.packages.outputs.drippie-mon }}
wd-mon: ${{ steps.packages.outputs.wd-mon }}
contracts: ${{ steps.packages.outputs.contracts }}
contracts-bedrock: ${{ steps.packages.outputs.contracts-bedrock }}
replica-healthcheck: ${{ steps.packages.outputs.replica-healthcheck }}
hardhat-node: ${{ steps.packages.outputs.hardhat-node }}
op-exporter: ${{ steps.packages.outputs.op-exporter }}
foundry: ${{ steps.packages.outputs.foundry }}
endpoint-monitor: ${{ steps.packages.outputs.endpoint-monitor }}
# Permissions necessary for Changesets to push a new branch and open PRs
# (for automated Version Packages PRs), and request the JWT for provenance.
# More info: https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#adding-permissions-settings
permissions:
contents: write
pull-requests: write
id-token: write
steps:
- name: Checkout Repo
uses: actions/checkout@master
uses: actions/checkout@v3
with:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0
ref: "develop"
- name: Setup Node.js 16.x
uses: actions/setup-node@master
with:
node-version: 16.x
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install Dependencies
run: yarn --frozen-lockfile
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
- name: Setup
uses: ./.github/actions/setup
# Makes a pr to publish the changesets that when
# merged will publish to npm
......@@ -67,7 +49,8 @@ jobs:
id: changesets
with:
createGithubReleases: false
publish: yarn release
publish: pnpm release:publish
version: pnpm release:version
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
......@@ -80,84 +63,6 @@ jobs:
run: |
node ops/scripts/ci-versions.js ${{ toJSON(steps.changesets.outputs.publishedPackages) }}
l2geth:
name: Publish L2Geth Version ${{ needs.release.outputs.l2geth }}
needs: release
if: needs.release.outputs.l2geth != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Publish L2Geth
uses: docker/build-push-action@v2
with:
context: .
file: ./l2geth/Dockerfile
push: true
tags: ethereumoptimism/l2geth:${{ needs.release.outputs.l2geth }},ethereumoptimism/l2geth:latest
hardhat-node:
name: Publish Hardhat Node ${{ needs.release.outputs.hardhat-node }}
needs: release
if: needs.release.outputs.hardhat-node != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Publish Hardhat Node
uses: docker/build-push-action@v2
with:
context: ./ops/docker/hardhat
file: ./ops/docker/hardhat/Dockerfile
push: true
tags: ethereumoptimism/hardhat-node:${{ needs.release.outputs.hardhat-node }},ethereumoptimism/hardhat-node:latest
foundry:
name: Publish foundry ${{ needs.release.outputs.foundry }}
needs: release
if: needs.release.outputs.foundry != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Publish foundry
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/foundry/Dockerfile
push: true
tags: ethereumoptimism/foundry:${{ needs.release.outputs.foundry }},ethereumoptimism/foundry:latest
op-exporter:
name: Publish op-exporter Version ${{ needs.release.outputs.op-exporter}}
needs: release
......@@ -195,10 +100,10 @@ jobs:
GITCOMMIT=${{ steps.build_args.outputs.GITCOMMIT }}
GITVERSION=${{ steps.build_args.outputs.GITVERSION }}
fault-detector:
name: Publish Fault Detector Version ${{ needs.release.outputs.fault-detector }}
fault-mon:
name: Publish fault-mon Version ${{ needs.release.outputs.fault-mon }}
needs: release
if: needs.release.outputs.fault-detector != ''
if: needs.release.outputs.fault-mon != ''
runs-on: ubuntu-latest
steps:
......@@ -218,9 +123,9 @@ jobs:
with:
context: .
file: ./ops/docker/Dockerfile.packages
target: fault-detector
target: fault-mon
push: true
tags: ethereumoptimism/fault-detector:${{ needs.release.outputs.fault-detector }},ethereumoptimism/fault-detector:latest
tags: ethereumoptimism/fault-mon:${{ needs.release.outputs.fault-mon }},ethereumoptimism/fault-mon:latest
wd-mon:
name: Publish Withdrawal Monitor Version ${{ needs.release.outputs.wd-mon }}
......@@ -303,60 +208,6 @@ jobs:
push: true
tags: ethereumoptimism/drippie-mon:${{ needs.release.outputs.drippie-mon }},ethereumoptimism/drippie-mon:latest
contracts:
name: Publish Deployer Version ${{ needs.release.outputs.contracts }}
needs: release
if: needs.release.outputs.contracts != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.packages
target: deployer
push: true
tags: ethereumoptimism/deployer:${{ needs.release.outputs.contracts }},ethereumoptimism/deployer:latest
contracts-bedrock:
name: Publish deployer-bedrock Version ${{ needs.release.outputs.contracts-bedrock }}
needs: release
if: needs.release.outputs.contracts != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.packages
target: deployer-bedrock
push: true
tags: ethereumoptimism/deployer-bedrock:${{ needs.release.outputs.contracts-bedrock }},ethereumoptimism/deployer-bedrock:latest
replica-healthcheck:
name: Publish Replica Healthcheck Version ${{ needs.release.outputs.replica-healthcheck }}
needs: release
......
......@@ -18,12 +18,16 @@ on:
required: true
type: choice
options:
- ci-builder
- fault-detector
- indexer
- op-node
- op-batcher
- op-proposer
- op-ufm
- proxyd
- indexer
- fault-detector
- ci-builder
prerelease:
description: Increment major/minor/patch as prerelease?
required: false
......
......@@ -17,12 +17,6 @@ dist
artifacts
cache
l2geth/build/bin
packages/contracts-periphery/coverage*
packages/contracts-periphery/@openzeppelin*
packages/contracts-periphery/hardhat*
packages/contracts-periphery/forge-artifacts*
packages/contracts-bedrock/deployments/devnetL1
packages/contracts-bedrock/deployments/anvil
......
[submodule "tests"]
path = l2geth/tests/testdata
url = https://github.com/ethereum/tests
[submodule "packages/contracts-periphery/lib/multicall"]
path = packages/contracts-periphery/lib/multicall
url = https://github.com/mds1/multicall
[submodule "lib/multicall"]
branch = v3.1.0
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn lerna run --concurrency 1 --stream pre-commit --since HEAD --exclude-dependents
pnpm nx affected --target=pre-commit
# run prefoo scripts
# npm and yarn support this as a default pnpm defaults to false
enable-pre-post-scripts=true
lockfile=true
prefer-workspace-packages=true
module.exports = {
$schema: 'http://json.schemastore.org/prettierrc',
plugins: ['prettier-plugin-solidity'],
trailingComma: 'es5',
tabWidth: 2,
semi: false,
......
......@@ -18,6 +18,7 @@ tests/
# Semgrep-action log folder
.semgrep_logs/
l2geth/
op-bindings/bindings
packages/*/node_modules
packages/*/test
......@@ -16,17 +16,9 @@
"directory": "packages/contracts",
"changeProcessCWD": true
},
{
"directory": "packages/contracts-periphery",
"changeProcessCWD": true
},
{
"directory": "packages/chain-mon",
"changeProcessCWD": true
},
{
"directory": "packages/fault-detector",
"changeProcessCWD": true
}
],
"eslint.nodePath": "./node_modules/eslint/bin/",
......
This diff is collapsed.
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
yarn-path ".yarn/releases/yarn-1.22.19.cjs"
......@@ -43,7 +43,7 @@ You *must* include a `changeset` file in your PR when making a change that would
Adding a `changeset` file is easy:
1. Navigate to the root of the monorepo.
2. Run `yarn changeset`. You'll be prompted to select packages to include in the changeset. Use the arrow keys to move the cursor up and down, hit the `spacebar` to select a package, and hit `enter` to confirm your selection. Select *all* packages that require a new release as a result of your PR.
2. Run `pnpm changeset`. You'll be prompted to select packages to include in the changeset. Use the arrow keys to move the cursor up and down, hit the `spacebar` to select a package, and hit `enter` to confirm your selection. Select *all* packages that require a new release as a result of your PR.
3. Once you hit `enter` you'll be prompted to decide whether your selected packages need a `major`, `minor`, or `patch` release. We follow the [Semantic Versioning](https://semver.org/) scheme. Please avoid using `major` releases for any packages that are still in version `0.y.z`.
4. Commit your changeset and push it into your PR. The changeset bot will notice your changeset file and leave a little comment to this effect on GitHub.
5. Voilà, c'est fini!
......@@ -63,7 +63,7 @@ You'll need the following:
* [Git](https://git-scm.com/downloads)
* [NodeJS](https://nodejs.org/en/download/)
* [Node Version Manager](https://github.com/nvm-sh/nvm)
* [Yarn](https://classic.yarnpkg.com/en/docs/install)
* [pnpm](https://pnpm.io/installation)
* [Docker](https://docs.docker.com/get-docker/)
* [Docker Compose](https://docs.docker.com/compose/install/)
* [Go](https://go.dev/dl/)
......@@ -86,10 +86,10 @@ Install node v16.16.0 with [nvm](https://github.com/nvm-sh/nvm)
nvm use
```
### Install node modules with Yarn
### Install node modules with pnpm
```bash
yarn install
pnpm i
```
### Building the TypeScript packages
......@@ -101,8 +101,8 @@ and compile the smart contracts. Install foundry [here](https://getfoundry.sh/).
To build all of the [TypeScript packages](./packages), run:
```bash
yarn clean
yarn build
pnpm clean
pnpm build
```
Packages compiled when on one branch may not be compatible with packages on a different branch.
......@@ -120,23 +120,6 @@ export DOCKER_BUILDKIT=1
docker-compose build
```
This will build the following containers:
* [`l1_chain`](https://hub.docker.com/r/ethereumoptimism/hardhat): simulated L1 chain using hardhat-evm as a backend
* [`deployer`](https://hub.docker.com/r/ethereumoptimism/deployer): process that deploys L1 smart contracts to the L1 chain
* [`l2geth`](https://hub.docker.com/r/ethereumoptimism/l2geth): L2 geth node running in Sequencer mode
* [`verifier`](https://hub.docker.com/r/ethereumoptimism/go-ethereum): L2 geth node running in Verifier mode
If you want to make a change to a container, you'll need to take it down and rebuild it.
For example, if you make a change in l2geth:
```bash
cd ops
docker-compose stop -- l2geth
docker-compose build -- l2geth
docker-compose start l2geth
```
Source code changes can have an impact on more than one container.
**If you're unsure about which containers to rebuild, just rebuild them all**:
......@@ -153,8 +136,8 @@ Finally, **if you're running into weird problems and nothing seems to be working
```bash
cd optimism
yarn clean
yarn build
pnpm clean
pnpm build
cd ops
docker-compose down -v
docker-compose build
......@@ -179,14 +162,14 @@ Before running tests: **follow the above instructions to get everything built.**
Run unit tests for all packages in parallel via:
```bash
yarn test
pnpm test
```
To run unit tests for a specific package:
```bash
cd packages/package-to-test
yarn test
pnpm test
```
#### Running contract static analysis
......@@ -198,5 +181,5 @@ To run `slither` locally, do:
```bash
cd packages/contracts
pip3 install slither-analyzer
yarn test:slither
pnpm test:slither
```
......@@ -12,8 +12,8 @@ build-ts: submodules
if [ -n "$$NVM_DIR" ]; then \
. $$NVM_DIR/nvm.sh && nvm use; \
fi
yarn install
yarn build
pnpm install
pnpm build
.PHONY: build-ts
submodules:
......@@ -74,11 +74,11 @@ nuke: clean devnet-clean
.PHONY: nuke
devnet-up:
@bash ./ops-bedrock/devnet-up.sh
PYTHONPATH=./bedrock-devnet python3 ./bedrock-devnet/main.py --monorepo-dir=.
.PHONY: devnet-up
devnet-up-deploy:
PYTHONPATH=./bedrock-devnet python3 ./bedrock-devnet/main.py --monorepo-dir=.
PYTHONPATH=./bedrock-devnet python3 ./bedrock-devnet/main.py --monorepo-dir=. --deploy
.PHONY: devnet-up-deploy
devnet-down:
......@@ -102,7 +102,7 @@ test-unit:
make -C ./op-proposer test
make -C ./op-batcher test
make -C ./op-e2e test
yarn test
pnpm test
.PHONY: test-unit
test-integration:
......@@ -130,4 +130,6 @@ update-op-geth:
.PHONY: update-op-geth
bedrock-markdown-links:
docker run --init -it -v `pwd`:/input lycheeverse/lychee --verbose --no-progress --exclude-loopback --exclude twitter.com --exclude explorer.optimism.io --exclude-mail /input/README.md "/input/specs/**/*.md"
docker run --init -it -v `pwd`:/input lycheeverse/lychee --verbose --no-progress --exclude-loopback \
--exclude twitter.com --exclude explorer.optimism.io --exclude linux-mips.org \
--exclude-mail /input/README.md "/input/specs/**/*.md"
......@@ -52,10 +52,8 @@ Refer to the Directory Structure section below to understand which packages are
├── <a href="./packages">packages</a>
│ ├── <a href="./packages/common-ts">common-ts</a>: Common tools for building apps in TypeScript
│ ├── <a href="./packages/contracts-bedrock">contracts-bedrock</a>: Bedrock smart contracts.
│ ├── <a href="./packages/contracts-periphery">contracts-periphery</a>: Peripheral contracts for Optimism
│ ├── <a href="./packages/core-utils">core-utils</a>: Low-level utilities that make building Optimism easier
│ ├── <a href="./packages/chain-mon">chain-mon</a>: Chain monitoring services
│ ├── <a href="./packages/fault-detector">fault-detector</a>: Service for detecting Sequencer faults
│ ├── <a href="./packages/replica-healthcheck">replica-healthcheck</a>: Service for monitoring the health of a replica node
│ └── <a href="./packages/sdk">sdk</a>: provides a set of tools for interacting with Optimism
├── <a href="./op-bindings">op-bindings</a>: Go bindings for Bedrock smart contracts.
......@@ -79,14 +77,11 @@ Refer to the Directory Structure section below to understand which packages are
~~ Pre-BEDROCK ~~
├── <a href="./packages">packages</a>
│ ├── <a href="./packages/common-ts">common-ts</a>: Common tools for building apps in TypeScript
│ ├── <a href="./packages/contracts-periphery">contracts-periphery</a>: Peripheral contracts for Optimism
│ ├── <a href="./packages/core-utils">core-utils</a>: Low-level utilities that make building Optimism easier
│ ├── <a href="./packages/chain-mon">chain-mon</a>: Chain monitoring services
│ ├── <a href="./packages/fault-detector">fault-detector</a>: Service for detecting Sequencer faults
│ ├── <a href="./packages/replica-healthcheck">replica-healthcheck</a>: Service for monitoring the health of a replica node
│ └── <a href="./packages/sdk">sdk</a>: provides a set of tools for interacting with Optimism
├── <a href="./indexer">indexer</a>: indexes and syncs transactions
├── <a href="./l2geth">l2geth</a>: Optimism client software, a fork of <a href="https://github.com/ethereum/go-ethereum/tree/v1.9.10">geth v1.9.10</a>
├── <a href="./op-exporter">op-exporter</a>: A prometheus exporter to collect/serve metrics from an Optimism node
├── <a href="./proxyd">proxyd</a>: Configurable RPC request router and proxy
└── <a href="./technical-documents">technical-documents</a>: audits and post-mortem documents
......@@ -139,7 +134,7 @@ See table in the **Active Branches** section above to find the right branch to t
We use [changesets](https://github.com/changesets/changesets) to mark packages for new releases.
When merging commits to the `develop` branch you MUST include a changeset file if your change would require that a new version of a package be released.
To add a changeset, run the command `yarn changeset` in the root of this monorepo.
To add a changeset, run the command `pnpm changeset` in the root of this monorepo.
You will be presented with a small prompt to select the packages to be released, the scope of the release (major, minor, or patch), and the reason for the release.
Comments within changeset files will be automatically included in the changelog of the package.
......@@ -160,6 +155,4 @@ It's strongly recommended to avoid merging PRs into develop during an active rel
## License
Code forked from [`go-ethereum`](https://github.com/ethereum/go-ethereum) under the name [`l2geth`](https://github.com/ethereum-optimism/optimism/tree/master/l2geth) is licensed under the [GNU GPLv3](https://gist.github.com/kn9ts/cbe95340d29fc1aaeaa5dd5c059d2e60) in accordance with the [original license](https://github.com/ethereum/go-ethereum/blob/master/COPYING).
All other files within this repository are licensed under the [MIT License](https://github.com/ethereum-optimism/optimism/blob/master/LICENSE) unless stated otherwise.
This diff is collapsed.
node_modules
artifacts
cache
.*.swp
venv
.idea
*.log
example/bin
contracts/out
state.json
*.json
*.pprof
*.out
bin
MIT License
Copyright (c) 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.
GITCOMMIT := $(shell git rev-parse HEAD)
GITDATE := $(shell git show -s --format='%ct')
VERSION := v0.0.0
LDFLAGSSTRING +=-X main.GitCommit=$(GITCOMMIT)
LDFLAGSSTRING +=-X main.GitDate=$(GITDATE)
LDFLAGSSTRING +=-X github.com/ethereum-optimism/optimism/op-program/version.Version=$(VERSION)
LDFLAGSSTRING +=-X github.com/ethereum-optimism/optimism/op-program/version.Meta=$(VERSION_META)
LDFLAGS := -ldflags "$(LDFLAGSSTRING)"
cannon:
env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/cannon .
clean:
rm -rf bin
elf:
make -C ./example elf
test: elf
go test -v ./...
lint:
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell,errorlint --timeout 5m -e "errors.As" -e "errors.Is"
.PHONY: \
cannon \
clean \
test \
lint
<!--![cannon](https://upload.wikimedia.org/wikipedia/commons/8/80/Cannon%2C_Château_du_Haut-Koenigsbourg%2C_France.jpg)-->
<!--![cannon](https://cdn1.epicgames.com/ue/product/Featured/SCIFIWEAPONBUNDLE_featured-894x488-83fbc936b6d86edcbbe892b1a6780224.png)-->
<!--![cannon](https://static.wikia.nocookie.net/ageofempires/images/8/80/Bombard_cannon_aoe2DE.png/revision/latest/top-crop/width/360/height/360?cb=20200331021834)-->
<!--![cannon](https://paradacreativa.es/wp-content/uploads/2021/05/Canon-orbital-GTA-01.jpg)-->
---
Cannon *(cannon cannon cannon)* is an onchain MIPS instruction emulator.
Cannon supports EVM-equivalent fault proofs by enabling Geth to run onchain,
one instruction at a time, as part of an interactive dispute game.
* It's Go code
* ...that runs an EVM
* ...emulating a MIPS machine
* ...running compiled Go code
* ...that runs an EVM
For more information, see [Docs](./docs/README.md).
## Usage
```shell
# Build op-program server-mode and MIPS-client binaries.
cd ../op-program
make op-program # build
# Switch back to cannon, and build the CLI
cd ../cannon
make cannon
# Transform MIPS op-program client binary into first VM state.
# This outputs state.json (VM state) and meta.json (for debug symbols).
./bin/cannon load-elf --path=../op-program/bin/op-program-client.elf
# Run cannon emulator (with example inputs)
# Note that the server-mode op-program command is passed into cannon (after the --),
# it runs as sub-process to provide the pre-image data.
#
# Note:
# - The L2 RPC is an archive L2 node on OP goerli.
# - The L1 RPC is a non-archive RPC, also change `--l1.rpckind` to reflect the correct L1 RPC type.
./bin/cannon run
--pprof.cpu
--info-at '%10000000'
--proof-at never
--input ./state.json
--
../op-program/bin/op-program
--l2 http://127.0.0.1:8745
--l1 http://127.0.0.1:8645
--l1.trustrpc
--l1.rpckind debug_geth
--log.format terminal
--l2.head 0xedc79de4d616a9100fdd42192224580daee81ea3d6303de8089d48a6c1bf4816
--network goerli
--l1.head 0x204f815790ca3bb43526ad60ebcc64784ec809bdc3550e82b54a0172f981efab
--l2.claim 0x530658ab1b1b3ff4829731fc8d5955f0e6b8410db2cd65b572067ba58df1f2b9
--l2.blocknumber 8813570
--datadir /tmp/fpp-database
--server
# Add --proof-at '=12345' (or pick other pattern, see --help)
# to pick a step to build a proof for (e.g. exact step, every N steps, etc.)
# Also see `./bin/cannon run --help` for more options
```
## Contracts
The Cannon contracts:
- `MIPS.sol`: A MIPS emulator implementation, to run a single instruction onchain, with merkleized VM memory.
- `PreimageOracle.sol`: implements the pre-image oracle ABI, to support the instruction execution pre-image requests.
The smart-contracts are integrated into the Optimism monorepo contracts:
[`../packages/contracts-bedrock/contracts/cannon`](../packages/contracts-bedrock/contracts/cannon)
## `mipsevm`
`mipsevm` is Go tooling to test the onchain MIPS implementation, and generate proof data.
## `example`
Example programs that can be run and proven with Cannon.
Optional dependency, but required for `mipsevm` Go tests.
See [`example/Makefile`](./example/Makefile) for building the example MIPS binaries.
## License
MIT, see [`LICENSE`](./LICENSE) file.
**Note: This code is unaudited.**
In NO WAY should it be used to secure any monetary value before testing and auditing.
This is experimental software, and should be treated as such.
The authors of this project make no guarantees of security of ANY KIND.
package cmd
import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
)
func loadJSON[X any](inputPath string) (*X, error) {
if inputPath == "" {
return nil, errors.New("no path specified")
}
f, err := os.OpenFile(inputPath, os.O_RDONLY, 0)
if err != nil {
return nil, fmt.Errorf("failed to open file %q: %w", inputPath, err)
}
defer f.Close()
var state X
if err := json.NewDecoder(f).Decode(&state); err != nil {
return nil, fmt.Errorf("failed to decode file %q: %w", inputPath, err)
}
return &state, nil
}
func writeJSON[X any](outputPath string, value X, outIfEmpty bool) error {
var out io.Writer
if outputPath != "" {
f, err := os.OpenFile(outputPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
return fmt.Errorf("failed to open output file: %w", err)
}
defer f.Close()
out = f
} else if outIfEmpty {
out = os.Stdout
} else {
return nil
}
enc := json.NewEncoder(out)
if err := enc.Encode(value); err != nil {
return fmt.Errorf("failed to encode to JSON: %w", err)
}
_, err := out.Write([]byte{'\n'})
if err != nil {
return fmt.Errorf("failed to append new-line: %w", err)
}
return nil
}
package cmd
import (
"debug/elf"
"fmt"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
)
var (
LoadELFPathFlag = &cli.PathFlag{
Name: "path",
Usage: "Path to 32-bit big-endian MIPS ELF file",
TakesFile: true,
Required: true,
}
LoadELFPatchFlag = &cli.StringSliceFlag{
Name: "patch",
Usage: "Type of patching to do",
Value: cli.NewStringSlice("go", "stack"),
Required: false,
}
LoadELFOutFlag = &cli.PathFlag{
Name: "out",
Usage: "Output path to write JSON state to. State is dumped to stdout if set to empty string.",
Value: "state.json",
Required: false,
}
LoadELFMetaFlag = &cli.PathFlag{
Name: "meta",
Usage: "Write metadata file, for symbol lookup during program execution. None if empty.",
Value: "meta.json",
Required: false,
}
)
func LoadELF(ctx *cli.Context) error {
elfPath := ctx.Path(LoadELFPathFlag.Name)
elfProgram, err := elf.Open(elfPath)
if err != nil {
return fmt.Errorf("failed to open ELF file %q: %w", elfPath, err)
}
if elfProgram.Machine != elf.EM_MIPS {
return fmt.Errorf("ELF is not big-endian MIPS R3000, but got %q", elfProgram.Machine.String())
}
state, err := mipsevm.LoadELF(elfProgram)
if err != nil {
return fmt.Errorf("failed to load ELF data into VM state: %w", err)
}
for _, typ := range ctx.StringSlice(LoadELFPatchFlag.Name) {
switch typ {
case "stack":
err = mipsevm.PatchStack(state)
case "go":
err = mipsevm.PatchGo(elfProgram, state)
default:
return fmt.Errorf("unrecognized form of patching: %q", typ)
}
if err != nil {
return fmt.Errorf("failed to apply patch %s: %w", typ, err)
}
}
meta, err := mipsevm.MakeMetadata(elfProgram)
if err != nil {
return fmt.Errorf("failed to compute program metadata: %w", err)
}
if err := writeJSON[*mipsevm.Metadata](ctx.Path(LoadELFMetaFlag.Name), meta, false); err != nil {
return fmt.Errorf("failed to output metadata: %w", err)
}
return writeJSON[*mipsevm.State](ctx.Path(LoadELFOutFlag.Name), state, true)
}
var LoadELFCommand = &cli.Command{
Name: "load-elf",
Usage: "Load ELF file into Cannon JSON state",
Description: "Load ELF file into Cannon JSON state, optionally patch out functions",
Action: LoadELF,
Flags: []cli.Flag{
LoadELFPathFlag,
LoadELFPatchFlag,
LoadELFOutFlag,
LoadELFMetaFlag,
},
}
package cmd
import (
"io"
"github.com/ethereum/go-ethereum/log"
)
func Logger(w io.Writer, lvl log.Lvl) log.Logger {
h := log.StreamHandler(w, log.LogfmtFormat())
h = log.SyncHandler(h)
h = log.LvlFilterHandler(lvl, h)
l := log.New()
l.SetHandler(h)
return l
}
package cmd
import (
"fmt"
"strconv"
"strings"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
)
type StepMatcher func(st *mipsevm.State) bool
type StepMatcherFlag struct {
repr string
matcher StepMatcher
}
func MustStepMatcherFlag(pattern string) *StepMatcherFlag {
out := new(StepMatcherFlag)
if err := out.Set(pattern); err != nil {
panic(err)
}
return out
}
func (m *StepMatcherFlag) Set(value string) error {
m.repr = value
if value == "" || value == "never" {
m.matcher = func(st *mipsevm.State) bool {
return false
}
} else if value == "always" {
m.matcher = func(st *mipsevm.State) bool {
return true
}
} else if strings.HasPrefix(value, "=") {
when, err := strconv.ParseUint(value[1:], 0, 64)
if err != nil {
return fmt.Errorf("failed to parse step number: %w", err)
}
m.matcher = func(st *mipsevm.State) bool {
return st.Step == when
}
} else if strings.HasPrefix(value, "%") {
when, err := strconv.ParseUint(value[1:], 0, 64)
if err != nil {
return fmt.Errorf("failed to parse step interval number: %w", err)
}
m.matcher = func(st *mipsevm.State) bool {
return st.Step%when == 0
}
} else {
return fmt.Errorf("unrecognized step matcher: %q", value)
}
return nil
}
func (m *StepMatcherFlag) String() string {
return m.repr
}
func (m *StepMatcherFlag) Matcher() StepMatcher {
if m.matcher == nil { // Set(value) is not called for omitted inputs, default to never matching.
return func(st *mipsevm.State) bool {
return false
}
}
return m.matcher
}
package cmd
import (
"fmt"
"os"
"os/exec"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli/v2"
"github.com/pkg/profile"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
)
var (
RunInputFlag = &cli.PathFlag{
Name: "input",
Usage: "path of input JSON state. Stdin if left empty.",
TakesFile: true,
Value: "state.json",
Required: true,
}
RunOutputFlag = &cli.PathFlag{
Name: "output",
Usage: "path of output JSON state. Stdout if left empty.",
TakesFile: true,
Value: "out.json",
Required: false,
}
patternHelp = "'never' (default), 'always', '=123' at exactly step 123, '%123' for every 123 steps"
RunProofAtFlag = &cli.GenericFlag{
Name: "proof-at",
Usage: "step pattern to output proof at: " + patternHelp,
Value: new(StepMatcherFlag),
Required: false,
}
RunProofFmtFlag = &cli.StringFlag{
Name: "proof-fmt",
Usage: "format for proof data output file names. Proof data is written to stdout if empty.",
Value: "proof-%d.json",
Required: false,
}
RunSnapshotAtFlag = &cli.GenericFlag{
Name: "snapshot-at",
Usage: "step pattern to output snapshots at: " + patternHelp,
Value: new(StepMatcherFlag),
Required: false,
}
RunSnapshotFmtFlag = &cli.StringFlag{
Name: "snapshot-fmt",
Usage: "format for snapshot output file names.",
Value: "state-%d.json",
Required: false,
}
RunStopAtFlag = &cli.GenericFlag{
Name: "stop-at",
Usage: "step pattern to stop at: " + patternHelp,
Value: new(StepMatcherFlag),
Required: false,
}
RunMetaFlag = &cli.PathFlag{
Name: "meta",
Usage: "path to metadata file for symbol lookup for enhanced debugging info durign execution.",
Value: "meta.json",
Required: false,
}
RunInfoAtFlag = &cli.GenericFlag{
Name: "info-at",
Usage: "step pattern to print info at: " + patternHelp,
Value: MustStepMatcherFlag("%100000"),
Required: false,
}
RunPProfCPU = &cli.BoolFlag{
Name: "pprof.cpu",
Usage: "enable pprof cpu profiling",
}
)
type Proof struct {
Step uint64 `json:"step"`
Pre common.Hash `json:"pre"`
Post common.Hash `json:"post"`
StateData hexutil.Bytes `json:"state-data"`
ProofData hexutil.Bytes `json:"proof-data"`
OracleKey hexutil.Bytes `json:"oracle-key,omitempty"`
OracleValue hexutil.Bytes `json:"oracle-value,omitempty"`
StepInput hexutil.Bytes `json:"step-input"`
OracleInput hexutil.Bytes `json:"oracle-input"`
}
type rawHint string
func (rh rawHint) Hint() string {
return string(rh)
}
type rawKey [32]byte
func (rk rawKey) PreimageKey() [32]byte {
return rk
}
type ProcessPreimageOracle struct {
pCl *preimage.OracleClient
hCl *preimage.HintWriter
cmd *exec.Cmd
}
func NewProcessPreimageOracle(name string, args []string) (*ProcessPreimageOracle, error) {
if name == "" {
return &ProcessPreimageOracle{}, nil
}
pClientRW, pOracleRW, err := preimage.CreateBidirectionalChannel()
if err != nil {
return nil, err
}
hClientRW, hOracleRW, err := preimage.CreateBidirectionalChannel()
if err != nil {
return nil, err
}
cmd := exec.Command(name, args...) // nosemgrep
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.ExtraFiles = []*os.File{
hOracleRW.Reader(),
hOracleRW.Writer(),
pOracleRW.Reader(),
pOracleRW.Writer(),
}
out := &ProcessPreimageOracle{
pCl: preimage.NewOracleClient(pClientRW),
hCl: preimage.NewHintWriter(hClientRW),
cmd: cmd,
}
return out, nil
}
func (p *ProcessPreimageOracle) Hint(v []byte) {
if p.hCl == nil { // no hint processor
return
}
p.hCl.Hint(rawHint(v))
}
func (p *ProcessPreimageOracle) GetPreimage(k [32]byte) []byte {
if p.pCl == nil {
panic("no pre-image retriever available")
}
return p.pCl.Get(rawKey(k))
}
func (p *ProcessPreimageOracle) Start() error {
if p.cmd == nil {
return nil
}
return p.cmd.Start()
}
func (p *ProcessPreimageOracle) Close() error {
if p.cmd == nil {
return nil
}
_ = p.cmd.Process.Signal(os.Interrupt)
// Go 1.20 feature, to introduce later
//p.cmd.WaitDelay = time.Second * 10
err := p.cmd.Wait()
if err, ok := err.(*exec.ExitError); ok {
if err.Success() {
return nil
}
}
return err
}
type StepFn func(proof bool) (*mipsevm.StepWitness, error)
func Guard(proc *os.ProcessState, fn StepFn) StepFn {
return func(proof bool) (*mipsevm.StepWitness, error) {
wit, err := fn(proof)
if err != nil {
if proc.Exited() {
return nil, fmt.Errorf("pre-image server exited with code %d, resulting in err %w", proc.ExitCode(), err)
} else {
return nil, err
}
}
return wit, nil
}
}
var _ mipsevm.PreimageOracle = (*ProcessPreimageOracle)(nil)
func Run(ctx *cli.Context) error {
if ctx.Bool(RunPProfCPU.Name) {
defer profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.CPUProfile).Stop()
}
state, err := loadJSON[mipsevm.State](ctx.Path(RunInputFlag.Name))
if err != nil {
return err
}
l := Logger(os.Stderr, log.LvlInfo)
outLog := &mipsevm.LoggingWriter{Name: "program std-out", Log: l}
errLog := &mipsevm.LoggingWriter{Name: "program std-err", Log: l}
// split CLI args after first '--'
args := ctx.Args().Slice()
for i, arg := range args {
if arg == "--" {
args = args[i+1:]
break
}
}
if len(args) == 0 {
args = []string{""}
}
po, err := NewProcessPreimageOracle(args[0], args[1:])
if err != nil {
return fmt.Errorf("failed to create pre-image oracle process: %w", err)
}
if err := po.Start(); err != nil {
return fmt.Errorf("failed to start pre-image oracle server: %w", err)
}
defer func() {
if err := po.Close(); err != nil {
l.Error("failed to close pre-image server", "err", err)
}
}()
stopAt := ctx.Generic(RunStopAtFlag.Name).(*StepMatcherFlag).Matcher()
proofAt := ctx.Generic(RunProofAtFlag.Name).(*StepMatcherFlag).Matcher()
snapshotAt := ctx.Generic(RunSnapshotAtFlag.Name).(*StepMatcherFlag).Matcher()
infoAt := ctx.Generic(RunInfoAtFlag.Name).(*StepMatcherFlag).Matcher()
var meta *mipsevm.Metadata
if metaPath := ctx.Path(RunMetaFlag.Name); metaPath == "" {
l.Info("no metadata file specified, defaulting to empty metadata")
meta = &mipsevm.Metadata{Symbols: nil} // provide empty metadata by default
} else {
if m, err := loadJSON[mipsevm.Metadata](metaPath); err != nil {
return fmt.Errorf("failed to load metadata: %w", err)
} else {
meta = m
}
}
us := mipsevm.NewInstrumentedState(state, po, outLog, errLog)
proofFmt := ctx.String(RunProofFmtFlag.Name)
snapshotFmt := ctx.String(RunSnapshotFmtFlag.Name)
stepFn := us.Step
if po.cmd != nil {
stepFn = Guard(po.cmd.ProcessState, stepFn)
}
start := time.Now()
startStep := state.Step
// avoid symbol lookups every instruction by preparing a matcher func
sleepCheck := meta.SymbolMatcher("runtime.notesleep")
for !state.Exited {
if state.Step%100 == 0 { // don't do the ctx err check (includes lock) too often
if err := ctx.Context.Err(); err != nil {
return err
}
}
step := state.Step
if infoAt(state) {
delta := time.Since(start)
l.Info("processing",
"step", step,
"pc", mipsevm.HexU32(state.PC),
"insn", mipsevm.HexU32(state.Memory.GetMemory(state.PC)),
"ips", float64(step-startStep)/(float64(delta)/float64(time.Second)),
"pages", state.Memory.PageCount(),
"mem", state.Memory.Usage(),
"name", meta.LookupSymbol(state.PC),
)
}
if sleepCheck(state.PC) { // don't loop forever when we get stuck because of an unexpected bad program
return fmt.Errorf("got stuck in Go sleep at step %d", step)
}
if stopAt(state) {
break
}
if snapshotAt(state) {
if err := writeJSON[*mipsevm.State](fmt.Sprintf(snapshotFmt, step), state, false); err != nil {
return fmt.Errorf("failed to write state snapshot: %w", err)
}
}
if proofAt(state) {
preStateHash := crypto.Keccak256Hash(state.EncodeWitness())
witness, err := stepFn(true)
if err != nil {
return fmt.Errorf("failed at proof-gen step %d (PC: %08x): %w", step, state.PC, err)
}
postStateHash := crypto.Keccak256Hash(state.EncodeWitness())
proof := &Proof{
Step: step,
Pre: preStateHash,
Post: postStateHash,
StateData: witness.State,
ProofData: witness.MemProof,
StepInput: witness.EncodeStepInput(),
}
if witness.HasPreimage() {
inp, err := witness.EncodePreimageOracleInput()
if err != nil {
return fmt.Errorf("failed to encode pre-image oracle input: %w", err)
}
proof.OracleInput = inp
proof.OracleKey = witness.PreimageKey[:]
proof.OracleValue = witness.PreimageValue
}
if err := writeJSON[*Proof](fmt.Sprintf(proofFmt, step), proof, true); err != nil {
return fmt.Errorf("failed to write proof data: %w", err)
}
} else {
_, err = stepFn(false)
if err != nil {
return fmt.Errorf("failed at step %d (PC: %08x): %w", step, state.PC, err)
}
}
}
if err := writeJSON[*mipsevm.State](ctx.Path(RunOutputFlag.Name), state, true); err != nil {
return fmt.Errorf("failed to write state output: %w", err)
}
return nil
}
var RunCommand = &cli.Command{
Name: "run",
Usage: "Run VM step(s) and generate proof data to replicate onchain.",
Description: "Run VM step(s) and generate proof data to replicate onchain. See flags to match when to output a proof, a snapshot, or to stop early.",
Action: Run,
Flags: []cli.Flag{
RunInputFlag,
RunOutputFlag,
RunProofAtFlag,
RunProofFmtFlag,
RunSnapshotAtFlag,
RunSnapshotFmtFlag,
RunStopAtFlag,
RunMetaFlag,
RunInfoAtFlag,
RunPProfCPU,
},
}
# Cannon Overview
Cannon has two main components:
- Onchain `MIPS.sol`: EVM implementation to verify execution of a single MIPS instruction.
- Offchain `mipsevm`: Go implementation to produce a proof for any MIPS instruction to verify onchain.
Between these two modes of operation, the [witness data](#witness-data) is essential,
to reproduce the same instruction onchain as offchain.
## Onchain `MIPS.sol`
This is an onchain implementation of big-endian 32-bit MIPS instruction execution.
This covers MIPS III, R3000, as required by the `mips` Go compiler/runtime target.
The systemcall instruction is implemented to simulate a minimal subset of the Linux kernel,
just enough to serve the needs of a basic Go program:
allocate memory, read/write to certain file-descriptors, and exit.
Note that this does not include concurrency related system calls: when running Go programs,
the GC has to be disabled, since it runs concurrently.
This is done by patching out specific runtime functions that start the GC,
by simply inserting jumps to the return-address, before the functions spin up any additional threads.
## Offchain `mipsevm`
This is an instrumented emulator, also running big-endian 32-bit MIPS, that executes one step at a time,
and maintains the same state as used for onchain execution.
The difference is that it has access to the full memory, and pre-image oracle.
And as it executes each step, it can optionally produce the witness data for the step, to repeat it onchain.
The Cannon CLI is used to load a program into an initial state,
transition it N steps quickly without witness generation, and 1 step while producing a witness.
`mipsevm` is instrumented for proof generation and handles delay-slots by isolating each individual instruction
and tracking `nextPC` to emulate the delayed `PC` changes after delay-slot execution.
## Witness Data
There are 3 types of witness data involved in onchain execution:
- [Packed State](#packed-state)
- [Memory proofs](#memory-proofs)
- [Pre-image data](#pre-image-data)
### Packed State
The Packed State is provided in every executed onchain instruction.
See [Cannon VM Specs](../../specs/cannon-fault-proof-vm.md#state) for
details on the state structure.
The packed state is small! The `State` data can be packed in such a small amount of EVM words,
that it is more efficient to fully provide it, than to create a proof for each individual part involved in execution.
The program stops changing the state when `exited` is set to true. The exit-code is remembered,
to determine if the program is successful, or panicked/exited in some unexpected way.
This outcome can be used to determine truthiness of claims that are verified as part of the program execution.
### Memory proofs
Simplicity is key here. Previous versions of Cannon used to overlap memory and register state,
and represent it with a Merkle Patricia Trie.
This added a lot of complexity, as there would be multiple dynamically-sized MPT proofs,
with read and write operations, during a single instruction.
Instead, memory in Cannon is now represented as a binary merkle tree:
The tree has a fixed-depth of 27 levels, with leaf values of 32 bytes each.
This spans the full 32-bit address space: `2**27 * 32 = 2**32`.
Each leaf contains the memory at that part of the tree.
The tree is efficiently allocated, since the root of fully zeroed sub-trees
can be computed without actually creating the full-subtree: `zero_hash[d] = hash(zero_hash[d-1], zero_hash[d-1])`,
until the base-case of `zero_hash[0] == bytes32(0)`.
Nodes in this memory tree are combined as: `out = keccak256(left ++ right)`, where `++` is concatenation,
and `left` and `right` are the 32-byte outputs of the respective sub-trees or the leaf values themselves.
Individual leaf nodes are not hashed.
The proof format is a concatenated byte array of 28 `bytes32`. The first `bytes32` is the leaf value,
and the remaining 27 `bytes32` are the sibling nodes up till the root of the tree,
along the branch of the leaf value, starting from the bottom.
To verify the proof, start with the leaf value as `node`, and combine it with respective sibling values:
`node = keccak256(node ++ sibling)` or `node = keccak256(sibling ++ node)`,
depending the position of `node` at that level of the tree.
During the onchain execution, each instruction only executes 1 or 2 memory reads, followed by 0 or 1 writes,
where the write is over the same memory as was last read.
The memory access is specifically:
- instruction (4 byte) read at `PC`
- load or syscall mem read, always aligned 4 bytes, read at any `addr`
- store or syscall mem write, always aligned 4 bytes, at ths same `addr`
Writing only once, at the last read leaf, also means that the leaf can be safely updated and the same proof-data
that was used to verify the read, can be used to reconstruct the new `memRoot` of the memory tree,
since all sibling data that is combined with the new leaf value was already authenticated during the read part.
### Pre-image data
Pre-image data is accessed through syscalls exclusively.
The OP-stack fault-proof [Pre-image Oracle specs](../../specs/fault-proof.md#pre-image-oracle)
define the ABI for communicating pre-images.
This ABI is implemented by the VM by intercepting the `read`/`write` syscalls to specific file descriptors. See [Cannon VM Specs](../../specs/cannon-fault-proof-vm.md#io) for more details.
The data is loaded into `PreimageOracle.sol` using the respective loading function based on the pre-image type.
And then retrieved during execution of the `read` syscall.
Note that although the oracle provides up to 32 bytes of the pre-image,
Cannon only supports reading at most 4 bytes at a time, to unify the memory operations with regular load/stores.
## Usage in Dispute Game
The onchain MIPS execution functions as base-case of an interactive dispute game.
The dispute game narrows down a sequence of disputed instructions to a single disputed instruction.
This instruction can then be executed onchain to determine objective truth.
The Dispute Game itself is out of scope for Cannon: Cannon is just one example of a fault-proof VM that
can be used to resolve disputes.
all: elf dump
.PHONY: elf
elf: $(patsubst %/go.mod,bin/%.elf,$(wildcard */go.mod))
.PHONY: dump
dump: $(patsubst %/go.mod,bin/%.dump,$(wildcard */go.mod))
bin:
mkdir bin
# take any directory with a go mod, and build an ELF
# verify output with: readelf -h bin/<name>.elf
# result is mips32, big endian, R3000
bin/%.elf: bin
cd $(@:bin/%.elf=%) && GOOS=linux GOARCH=mips GOMIPS=softfloat go build -o ../$@ .
# take any ELF and dump it
# TODO: currently have the little-endian toolchain, but should use the big-endian one. The -EB compat flag works though.
bin/%.dump: bin/%.elf
mipsel-linux-gnu-objdump -D --disassembler-options=no-aliases --wide --source -m mips:3000 -EB $(@:%.dump=%.elf) > $@
module claim
go 1.20
require github.com/ethereum-optimism/optimism v0.0.0
require (
golang.org/x/crypto v0.8.0 // indirect
golang.org/x/sys v0.7.0 // indirect
)
replace github.com/ethereum-optimism/optimism v0.0.0 => ../../..
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
package main
import (
"encoding/binary"
"fmt"
"os"
"github.com/ethereum-optimism/optimism/op-preimage"
)
type rawHint string
func (rh rawHint) Hint() string {
return string(rh)
}
func main() {
_, _ = os.Stderr.Write([]byte("started!"))
po := preimage.NewOracleClient(preimage.ClientPreimageChannel())
hinter := preimage.NewHintWriter(preimage.ClientHinterChannel())
preHash := *(*[32]byte)(po.Get(preimage.LocalIndexKey(0)))
diffHash := *(*[32]byte)(po.Get(preimage.LocalIndexKey(1)))
claimData := *(*[8]byte)(po.Get(preimage.LocalIndexKey(2)))
// Hints are used to indicate which things the program will access,
// so the server can be prepared to serve the corresponding pre-images.
hinter.Hint(rawHint(fmt.Sprintf("fetch-state %x", preHash)))
pre := po.Get(preimage.Keccak256Key(preHash))
// Multiple pre-images may be fetched based on a hint.
// E.g. when we need all values of a merkle-tree.
hinter.Hint(rawHint(fmt.Sprintf("fetch-diff %x", diffHash)))
diff := po.Get(preimage.Keccak256Key(diffHash))
diffPartA := po.Get(preimage.Keccak256Key(*(*[32]byte)(diff[:32])))
diffPartB := po.Get(preimage.Keccak256Key(*(*[32]byte)(diff[32:])))
// Example state-transition function: s' = s*a + b
s := binary.BigEndian.Uint64(pre)
a := binary.BigEndian.Uint64(diffPartA)
b := binary.BigEndian.Uint64(diffPartB)
fmt.Printf("computing %d * %d + %d\n", s, a, b)
sOut := s*a + b
sClaim := binary.BigEndian.Uint64(claimData[:])
if sOut != sClaim {
fmt.Printf("claim %d is bad! Correct result is %d\n", sOut, sClaim)
os.Exit(1)
} else {
fmt.Printf("claim %d is good!\n", sOut)
os.Exit(0)
}
}
package main
import "os"
func main() {
_, _ = os.Stdout.Write([]byte("hello world!\n"))
}
package main
import (
"context"
"errors"
"fmt"
"os"
"os/signal"
"syscall"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/cannon/cmd"
)
func main() {
app := cli.NewApp()
app.Name = "cannon"
app.Usage = "MIPS Fault Proof tool"
app.Description = "MIPS Fault Proof tool"
app.Commands = []*cli.Command{
cmd.LoadELFCommand,
cmd.RunCommand,
}
ctx, cancel := context.WithCancel(context.Background())
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
go func() {
for {
<-c
cancel()
fmt.Println("\r\nExiting...")
}
}()
err := app.RunContext(ctx, os.Args)
if err != nil {
if errors.Is(err, ctx.Err()) {
_, _ = fmt.Fprintf(os.Stderr, "command interrupted")
os.Exit(130)
} else {
_, _ = fmt.Fprintf(os.Stderr, "error: %v", err)
os.Exit(1)
}
}
}
mipsevm
mipsevm.test
*.prof
# `mipsevm`
Supported 55 instructions:
```
'addi', 'addiu', 'addu', 'and', 'andi',
'b', 'beq', 'beqz', 'bgez', 'bgtz', 'blez', 'bltz', 'bne', 'bnez',
'clz', 'divu',
'j', 'jal', 'jalr', 'jr',
'lb', 'lbu', 'lui', 'lw', 'lwr',
'mfhi', 'mflo', 'move', 'movn', 'movz', 'mtlo', 'mul', 'multu',
'negu', 'nop', 'not', 'or', 'ori',
'sb', 'sll', 'sllv', 'slt', 'slti', 'sltiu', 'sltu', 'sra', 'srl', 'srlv', 'subu', 'sw', 'swr', 'sync', 'syscall',
'xor', 'xori'
```
To run:
1. Load a program into a state, e.g. using `LoadELF`.
2. Patch the program if necessary: e.g. using `PatchGo` for Go programs, `PatchStack` for empty initial stack, etc.
4. Implement the `PreimageOracle` interface
5. Instrument the emulator with the state, and pre-image oracle, using `NewInstrumentedState`
6. Step through the instrumented state with `Step(proof)`,
where `proof==true` if witness data should be generated. Steps are faster with `proof==false`.
7. Optionally repeat the step on-chain by calling `MIPS.sol` and `PreimageOracle.sol`, using the above witness data.
package mipsevm
import (
"encoding/binary"
"encoding/json"
"fmt"
"math/big"
"os"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-chain-ops/srcmap"
)
var (
StepBytes4 = crypto.Keccak256([]byte("step(bytes,bytes)"))[:4]
CheatBytes4 = crypto.Keccak256([]byte("cheat(uint256,bytes32,bytes32,uint256)"))[:4]
LoadKeccak256PreimagePartBytes4 = crypto.Keccak256([]byte("loadKeccak256PreimagePart(uint256,bytes)"))[:4]
)
// LoadContracts loads the Cannon contracts, from op-bindings package
func LoadContracts() (*Contracts, error) {
var mips, oracle Contract
mips.DeployedBytecode.Object = hexutil.MustDecode(bindings.MIPSDeployedBin)
mips.DeployedBytecode.SourceMap = bindings.MIPSDeployedSourceMap
oracle.DeployedBytecode.Object = hexutil.MustDecode(bindings.PreimageOracleDeployedBin)
oracle.DeployedBytecode.SourceMap = bindings.PreimageOracleDeployedSourceMap
return &Contracts{
MIPS: &mips,
Oracle: &oracle,
}, nil
}
// LoadContractsFromFiles loads the Cannon contracts, from local filesystem
func LoadContractsFromFiles() (*Contracts, error) {
mips, err := LoadContract("MIPS")
if err != nil {
return nil, err
}
oracle, err := LoadContract("PreimageOracle")
if err != nil {
return nil, err
}
return &Contracts{
MIPS: mips,
Oracle: oracle,
}, nil
}
func LoadContract(name string) (*Contract, error) {
dat, err := os.ReadFile(fmt.Sprintf("../../packages/contracts-bedrock/forge-artifacts/%s.sol/%s.json", name, name))
if err != nil {
return nil, fmt.Errorf("failed to read contract JSON definition of %q: %w", name, err)
}
var out Contract
if err := json.Unmarshal(dat, &out); err != nil {
return nil, fmt.Errorf("failed to parse contract JSON definition of %q: %w", name, err)
}
return &out, nil
}
type Contract struct {
DeployedBytecode struct {
Object hexutil.Bytes `json:"object"`
SourceMap string `json:"sourceMap"`
} `json:"deployedBytecode"`
// ignore abi,bytecode,etc.
}
func (c *Contract) SourceMap(sourcePaths []string) (*srcmap.SourceMap, error) {
return srcmap.ParseSourceMap(sourcePaths, c.DeployedBytecode.Object, c.DeployedBytecode.SourceMap)
}
type Contracts struct {
MIPS *Contract
Oracle *Contract
}
type Addresses struct {
MIPS common.Address
Oracle common.Address
Sender common.Address
FeeRecipient common.Address
}
func NewEVMEnv(contracts *Contracts, addrs *Addresses) (*vm.EVM, *state.StateDB) {
chainCfg := params.MainnetChainConfig
offsetBlocks := uint64(1000) // blocks after shanghai fork
bc := &testChain{startTime: *chainCfg.ShanghaiTime + offsetBlocks*12}
header := bc.GetHeader(common.Hash{}, 17034870+offsetBlocks)
db := rawdb.NewMemoryDatabase()
statedb := state.NewDatabase(db)
state, err := state.New(types.EmptyRootHash, statedb, nil)
if err != nil {
panic(fmt.Errorf("failed to create memory state db: %w", err))
}
blockContext := core.NewEVMBlockContext(header, bc, nil, chainCfg, state)
vmCfg := vm.Config{}
env := vm.NewEVM(blockContext, vm.TxContext{}, state, chainCfg, vmCfg)
// pre-deploy the contracts
env.StateDB.SetCode(addrs.MIPS, contracts.MIPS.DeployedBytecode.Object)
env.StateDB.SetCode(addrs.Oracle, contracts.Oracle.DeployedBytecode.Object)
env.StateDB.SetState(addrs.MIPS, common.Hash{}, addrs.Oracle.Hash())
rules := env.ChainConfig().Rules(header.Number, true, header.Time)
env.StateDB.Prepare(rules, addrs.Sender, addrs.FeeRecipient, &addrs.MIPS, vm.ActivePrecompiles(rules), nil)
return env, state
}
type testChain struct {
startTime uint64
}
func (d *testChain) Engine() consensus.Engine {
return ethash.NewFullFaker()
}
func (d *testChain) GetHeader(h common.Hash, n uint64) *types.Header {
parentHash := common.Hash{0: 0xff}
binary.BigEndian.PutUint64(parentHash[1:], n-1)
return &types.Header{
ParentHash: parentHash,
UncleHash: types.EmptyUncleHash,
Coinbase: common.Address{},
Root: common.Hash{},
TxHash: types.EmptyTxsHash,
ReceiptHash: types.EmptyReceiptsHash,
Bloom: types.Bloom{},
Difficulty: big.NewInt(0),
Number: new(big.Int).SetUint64(n),
GasLimit: 30_000_000,
GasUsed: 0,
Time: d.startTime + n*12,
Extra: nil,
MixDigest: common.Hash{},
Nonce: types.BlockNonce{},
BaseFee: big.NewInt(7),
WithdrawalsHash: &types.EmptyWithdrawalsHash,
}
}
This diff is collapsed.
package mipsevm
import (
"io"
)
type PreimageOracle interface {
Hint(v []byte)
GetPreimage(k [32]byte) []byte
}
type InstrumentedState struct {
state *State
stdOut io.Writer
stdErr io.Writer
lastMemAccess uint32
memProofEnabled bool
memProof [28 * 32]byte
preimageOracle PreimageOracle
// cached pre-image data, including 8 byte length prefix
lastPreimage []byte
// key for above preimage
lastPreimageKey [32]byte
// offset we last read from, or max uint32 if nothing is read this step
lastPreimageOffset uint32
}
const (
fdStdin = 0
fdStdout = 1
fdStderr = 2
fdHintRead = 3
fdHintWrite = 4
fdPreimageRead = 5
fdPreimageWrite = 6
)
const (
MipsEBADF = 0x9
MipsEINVAL = 0x16
)
func NewInstrumentedState(state *State, po PreimageOracle, stdOut, stdErr io.Writer) *InstrumentedState {
return &InstrumentedState{
state: state,
stdOut: stdOut,
stdErr: stdErr,
preimageOracle: po,
}
}
func (m *InstrumentedState) Step(proof bool) (wit *StepWitness, err error) {
m.memProofEnabled = proof
m.lastMemAccess = ^uint32(0)
m.lastPreimageOffset = ^uint32(0)
if proof {
insnProof := m.state.Memory.MerkleProof(m.state.PC)
wit = &StepWitness{
State: m.state.EncodeWitness(),
MemProof: insnProof[:],
}
}
err = m.mipsStep()
if err != nil {
return nil, err
}
if proof {
wit.MemProof = append(wit.MemProof, m.memProof[:]...)
if m.lastPreimageOffset != ^uint32(0) {
wit.PreimageOffset = m.lastPreimageOffset
wit.PreimageKey = m.lastPreimageKey
wit.PreimageValue = m.lastPreimage
}
}
return
}
package mipsevm
import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
)
// LoggingWriter is a simple util to wrap a logger,
// and expose an io Writer interface,
// for the program running within the VM to write to.
type LoggingWriter struct {
Name string
Log log.Logger
}
func logAsText(b string) bool {
for _, c := range b {
if (c < 0x20 || c >= 0x7F) && (c != '\n' && c != '\t') {
return false
}
}
return true
}
func (lw *LoggingWriter) Write(b []byte) (int, error) {
t := string(b)
if logAsText(t) {
lw.Log.Info("", "text", t)
} else {
lw.Log.Info("", "data", hexutil.Bytes(b))
}
return len(b), nil
}
package mipsevm
import (
"encoding/binary"
"encoding/json"
"fmt"
"io"
"math/bits"
"sort"
"github.com/ethereum/go-ethereum/crypto"
)
// Note: 2**12 = 4 KiB, the min phys page size in the Go runtime.
const (
PageAddrSize = 12
PageKeySize = 32 - PageAddrSize
PageSize = 1 << PageAddrSize
PageAddrMask = PageSize - 1
MaxPageCount = 1 << PageKeySize
PageKeyMask = MaxPageCount - 1
)
func HashPair(left, right [32]byte) [32]byte {
out := crypto.Keccak256Hash(left[:], right[:])
//fmt.Printf("0x%x 0x%x -> 0x%x\n", left, right, out)
return out
}
var zeroHashes = func() [256][32]byte {
// empty parts of the tree are all zero. Precompute the hash of each full-zero range sub-tree level.
var out [256][32]byte
for i := 1; i < 256; i++ {
out[i] = HashPair(out[i-1], out[i-1])
}
return out
}()
type Memory struct {
// generalized index -> merkle root or nil if invalidated
nodes map[uint64]*[32]byte
// pageIndex -> cached page
pages map[uint32]*CachedPage
// Note: since we don't de-alloc pages, we don't do ref-counting.
// Once a page exists, it doesn't leave memory
// two caches: we often read instructions from one page, and do memory things with another page.
// this prevents map lookups each instruction
lastPageKeys [2]uint32
lastPage [2]*CachedPage
}
func NewMemory() *Memory {
return &Memory{
nodes: make(map[uint64]*[32]byte),
pages: make(map[uint32]*CachedPage),
lastPageKeys: [2]uint32{^uint32(0), ^uint32(0)}, // default to invalid keys, to not match any pages
}
}
func (m *Memory) PageCount() int {
return len(m.pages)
}
func (m *Memory) ForEachPage(fn func(pageIndex uint32, page *Page) error) error {
for pageIndex, cachedPage := range m.pages {
if err := fn(pageIndex, cachedPage.Data); err != nil {
return err
}
}
return nil
}
func (m *Memory) Invalidate(addr uint32) {
// addr must be aligned to 4 bytes
if addr&0x3 != 0 {
panic(fmt.Errorf("unaligned memory access: %x", addr))
}
// find page, and invalidate addr within it
if p, ok := m.pageLookup(addr >> PageAddrSize); ok {
prevValid := p.Ok[1]
p.Invalidate(addr & PageAddrMask)
if !prevValid { // if the page was already invalid before, then nodes to mem-root will also still be.
return
}
} else { // no page? nothing to invalidate
return
}
// find the gindex of the first page covering the address
gindex := ((uint64(1) << 32) | uint64(addr)) >> PageAddrSize
for gindex > 0 {
m.nodes[gindex] = nil
gindex >>= 1
}
}
func (m *Memory) MerkleizeSubtree(gindex uint64) [32]byte {
l := uint64(bits.Len64(gindex))
if l > 28 {
panic("gindex too deep")
}
if l > PageKeySize {
depthIntoPage := l - 1 - PageKeySize
pageIndex := (gindex >> depthIntoPage) & PageKeyMask
if p, ok := m.pages[uint32(pageIndex)]; ok {
pageGindex := (1 << depthIntoPage) | (gindex & ((1 << depthIntoPage) - 1))
return p.MerkleizeSubtree(pageGindex)
} else {
return zeroHashes[28-l] // page does not exist
}
}
if l > PageKeySize+1 {
panic("cannot jump into intermediate node of page")
}
n, ok := m.nodes[gindex]
if !ok {
// if the node doesn't exist, the whole sub-tree is zeroed
return zeroHashes[28-l]
}
if n != nil {
return *n
}
left := m.MerkleizeSubtree(gindex << 1)
right := m.MerkleizeSubtree((gindex << 1) | 1)
r := HashPair(left, right)
m.nodes[gindex] = &r
return r
}
func (m *Memory) MerkleProof(addr uint32) (out [28 * 32]byte) {
proof := m.traverseBranch(1, addr, 0)
// encode the proof
for i := 0; i < 28; i++ {
copy(out[i*32:(i+1)*32], proof[i][:])
}
return out
}
func (m *Memory) traverseBranch(parent uint64, addr uint32, depth uint8) (proof [][32]byte) {
if depth == 32-5 {
proof = make([][32]byte, 0, 32-5+1)
proof = append(proof, m.MerkleizeSubtree(parent))
return
}
if depth > 32-5 {
panic("traversed too deep")
}
self := parent << 1
sibling := self | 1
if addr&(1<<(31-depth)) != 0 {
self, sibling = sibling, self
}
proof = m.traverseBranch(self, addr, depth+1)
siblingNode := m.MerkleizeSubtree(sibling)
proof = append(proof, siblingNode)
return
}
func (m *Memory) MerkleRoot() [32]byte {
return m.MerkleizeSubtree(1)
}
func (m *Memory) pageLookup(pageIndex uint32) (*CachedPage, bool) {
// hit caches
if pageIndex == m.lastPageKeys[0] {
return m.lastPage[0], true
}
if pageIndex == m.lastPageKeys[1] {
return m.lastPage[1], true
}
p, ok := m.pages[pageIndex]
// only cache existing pages.
if ok {
m.lastPageKeys[1] = m.lastPageKeys[0]
m.lastPage[1] = m.lastPage[0]
m.lastPageKeys[0] = pageIndex
m.lastPage[0] = p
}
return p, ok
}
func (m *Memory) SetMemory(addr uint32, v uint32) {
// addr must be aligned to 4 bytes
if addr&0x3 != 0 {
panic(fmt.Errorf("unaligned memory access: %x", addr))
}
pageIndex := addr >> PageAddrSize
pageAddr := addr & PageAddrMask
p, ok := m.pageLookup(pageIndex)
if !ok {
// allocate the page if we have not already.
// Go may mmap relatively large ranges, but we only allocate the pages just in time.
p = m.AllocPage(pageIndex)
} else {
m.Invalidate(addr) // invalidate this branch of memory, now that the value changed
}
binary.BigEndian.PutUint32(p.Data[pageAddr:pageAddr+4], v)
}
func (m *Memory) GetMemory(addr uint32) uint32 {
// addr must be aligned to 4 bytes
if addr&0x3 != 0 {
panic(fmt.Errorf("unaligned memory access: %x", addr))
}
p, ok := m.pageLookup(addr >> PageAddrSize)
if !ok {
return 0
}
pageAddr := addr & PageAddrMask
return binary.BigEndian.Uint32(p.Data[pageAddr : pageAddr+4])
}
func (m *Memory) AllocPage(pageIndex uint32) *CachedPage {
p := &CachedPage{Data: new(Page)}
m.pages[pageIndex] = p
// make nodes to root
k := (1 << PageKeySize) | uint64(pageIndex)
for k > 0 {
m.nodes[k] = nil
k >>= 1
}
return p
}
type pageEntry struct {
Index uint32 `json:"index"`
Data *Page `json:"data"`
}
func (m *Memory) MarshalJSON() ([]byte, error) { // nosemgrep
pages := make([]pageEntry, 0, len(m.pages))
for k, p := range m.pages {
pages = append(pages, pageEntry{
Index: k,
Data: p.Data,
})
}
sort.Slice(pages, func(i, j int) bool {
return pages[i].Index < pages[j].Index
})
return json.Marshal(pages)
}
func (m *Memory) UnmarshalJSON(data []byte) error {
var pages []pageEntry
if err := json.Unmarshal(data, &pages); err != nil {
return err
}
m.nodes = make(map[uint64]*[32]byte)
m.pages = make(map[uint32]*CachedPage)
m.lastPageKeys = [2]uint32{^uint32(0), ^uint32(0)}
m.lastPage = [2]*CachedPage{nil, nil}
for i, p := range pages {
if _, ok := m.pages[p.Index]; ok {
return fmt.Errorf("cannot load duplicate page, entry %d, page index %d", i, p.Index)
}
m.AllocPage(p.Index).Data = p.Data
}
return nil
}
func (m *Memory) SetMemoryRange(addr uint32, r io.Reader) error {
for {
pageIndex := addr >> PageAddrSize
pageAddr := addr & PageAddrMask
p, ok := m.pageLookup(pageIndex)
if !ok {
p = m.AllocPage(pageIndex)
}
p.InvalidateFull()
n, err := r.Read(p.Data[pageAddr:])
if err != nil {
if err == io.EOF {
return nil
}
return err
}
addr += uint32(n)
}
}
type memReader struct {
m *Memory
addr uint32
count uint32
}
func (r *memReader) Read(dest []byte) (n int, err error) {
if r.count == 0 {
return 0, io.EOF
}
// Keep iterating over memory until we have all our data.
// It may wrap around the address range, and may not be aligned
endAddr := r.addr + r.count
pageIndex := r.addr >> PageAddrSize
start := r.addr & PageAddrMask
end := uint32(PageSize)
if pageIndex == (endAddr >> PageAddrSize) {
end = endAddr & PageAddrMask
}
p, ok := r.m.pageLookup(pageIndex)
if ok {
n = copy(dest, p.Data[start:end])
} else {
n = copy(dest, make([]byte, end-start)) // default to zeroes
}
r.addr += uint32(n)
r.count -= uint32(n)
return n, nil
}
func (m *Memory) ReadMemoryRange(addr uint32, count uint32) io.Reader {
return &memReader{m: m, addr: addr, count: count}
}
func (m *Memory) Usage() string {
total := uint64(len(m.pages)) * PageSize
const unit = 1024
if total < unit {
return fmt.Sprintf("%d B", total)
}
div, exp := uint64(unit), 0
for n := total / unit; n >= unit; n /= unit {
div *= unit
exp++
}
// KiB, MiB, GiB, TiB, ...
return fmt.Sprintf("%.1f %ciB", float64(total)/float64(div), "KMGTPE"[exp])
}
package mipsevm
import (
"bytes"
"crypto/rand"
"encoding/binary"
"encoding/json"
"io"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestMemoryMerkleProof(t *testing.T) {
t.Run("nearly empty tree", func(t *testing.T) {
m := NewMemory()
m.SetMemory(0x10000, 0xaabbccdd)
proof := m.MerkleProof(0x10000)
require.Equal(t, uint32(0xaabbccdd), binary.BigEndian.Uint32(proof[:4]))
for i := 0; i < 32-5; i++ {
require.Equal(t, zeroHashes[i][:], proof[32+i*32:32+i*32+32], "empty siblings")
}
})
t.Run("fuller tree", func(t *testing.T) {
m := NewMemory()
m.SetMemory(0x10000, 0xaabbccdd)
m.SetMemory(0x80004, 42)
m.SetMemory(0x13370000, 123)
root := m.MerkleRoot()
proof := m.MerkleProof(0x80004)
require.Equal(t, uint32(42), binary.BigEndian.Uint32(proof[4:8]))
node := *(*[32]byte)(proof[:32])
path := uint32(0x80004) >> 5
for i := 32; i < len(proof); i += 32 {
sib := *(*[32]byte)(proof[i : i+32])
if path&1 != 0 {
node = HashPair(sib, node)
} else {
node = HashPair(node, sib)
}
path >>= 1
}
require.Equal(t, root, node, "proof must verify")
})
}
func TestMemoryMerkleRoot(t *testing.T) {
t.Run("empty", func(t *testing.T) {
m := NewMemory()
root := m.MerkleRoot()
require.Equal(t, zeroHashes[32-5], root, "fully zeroed memory should have expected zero hash")
})
t.Run("empty page", func(t *testing.T) {
m := NewMemory()
m.SetMemory(0xF000, 0)
root := m.MerkleRoot()
require.Equal(t, zeroHashes[32-5], root, "fully zeroed memory should have expected zero hash")
})
t.Run("single page", func(t *testing.T) {
m := NewMemory()
m.SetMemory(0xF000, 1)
root := m.MerkleRoot()
require.NotEqual(t, zeroHashes[32-5], root, "non-zero memory")
})
t.Run("repeat zero", func(t *testing.T) {
m := NewMemory()
m.SetMemory(0xF000, 0)
m.SetMemory(0xF004, 0)
root := m.MerkleRoot()
require.Equal(t, zeroHashes[32-5], root, "zero still")
})
t.Run("two empty pages", func(t *testing.T) {
m := NewMemory()
m.SetMemory(PageSize*3, 0)
m.SetMemory(PageSize*10, 0)
root := m.MerkleRoot()
require.Equal(t, zeroHashes[32-5], root, "zero still")
})
t.Run("random few pages", func(t *testing.T) {
m := NewMemory()
m.SetMemory(PageSize*3, 1)
m.SetMemory(PageSize*5, 42)
m.SetMemory(PageSize*6, 123)
p3 := m.MerkleizeSubtree((1 << PageKeySize) | 3)
p5 := m.MerkleizeSubtree((1 << PageKeySize) | 5)
p6 := m.MerkleizeSubtree((1 << PageKeySize) | 6)
z := zeroHashes[PageAddrSize-5]
r1 := HashPair(
HashPair(
HashPair(z, z), // 0,1
HashPair(z, p3), // 2,3
),
HashPair(
HashPair(z, p5), // 4,5
HashPair(p6, z), // 6,7
),
)
r2 := m.MerkleizeSubtree(1 << (PageKeySize - 3))
require.Equal(t, r1, r2, "expecting manual page combination to match subtree merkle func")
})
t.Run("invalidate page", func(t *testing.T) {
m := NewMemory()
m.SetMemory(0xF000, 0)
require.Equal(t, zeroHashes[32-5], m.MerkleRoot(), "zero at first")
m.SetMemory(0xF004, 1)
require.NotEqual(t, zeroHashes[32-5], m.MerkleRoot(), "non-zero")
m.SetMemory(0xF004, 0)
require.Equal(t, zeroHashes[32-5], m.MerkleRoot(), "zero again")
})
}
func TestMemoryReadWrite(t *testing.T) {
t.Run("large random", func(t *testing.T) {
m := NewMemory()
data := make([]byte, 20_000)
_, err := rand.Read(data[:])
require.NoError(t, err)
require.NoError(t, m.SetMemoryRange(0, bytes.NewReader(data)))
for _, i := range []uint32{0, 4, 1000, 20_000 - 4} {
v := m.GetMemory(i)
expected := binary.BigEndian.Uint32(data[i : i+4])
require.Equalf(t, expected, v, "read at %d", i)
}
})
t.Run("repeat range", func(t *testing.T) {
m := NewMemory()
data := []byte(strings.Repeat("under the big bright yellow sun ", 40))
require.NoError(t, m.SetMemoryRange(0x1337, bytes.NewReader(data)))
res, err := io.ReadAll(m.ReadMemoryRange(0x1337-10, uint32(len(data)+20)))
require.NoError(t, err)
require.Equal(t, make([]byte, 10), res[:10], "empty start")
require.Equal(t, data, res[10:len(res)-10], "result")
require.Equal(t, make([]byte, 10), res[len(res)-10:], "empty end")
})
t.Run("read-write", func(t *testing.T) {
m := NewMemory()
m.SetMemory(12, 0xAABBCCDD)
require.Equal(t, uint32(0xAABBCCDD), m.GetMemory(12))
m.SetMemory(12, 0xAABB1CDD)
require.Equal(t, uint32(0xAABB1CDD), m.GetMemory(12))
})
t.Run("unaligned read", func(t *testing.T) {
m := NewMemory()
m.SetMemory(12, 0xAABBCCDD)
m.SetMemory(16, 0x11223344)
require.Panics(t, func() {
m.GetMemory(13)
})
require.Panics(t, func() {
m.GetMemory(14)
})
require.Panics(t, func() {
m.GetMemory(15)
})
require.Equal(t, uint32(0x11223344), m.GetMemory(16))
require.Equal(t, uint32(0), m.GetMemory(20))
require.Equal(t, uint32(0xAABBCCDD), m.GetMemory(12))
})
t.Run("unaligned write", func(t *testing.T) {
m := NewMemory()
m.SetMemory(12, 0xAABBCCDD)
require.Panics(t, func() {
m.SetMemory(13, 0x11223344)
})
require.Panics(t, func() {
m.SetMemory(14, 0x11223344)
})
require.Panics(t, func() {
m.SetMemory(15, 0x11223344)
})
require.Equal(t, uint32(0xAABBCCDD), m.GetMemory(12))
})
}
func TestMemoryJSON(t *testing.T) {
m := NewMemory()
m.SetMemory(8, 123)
dat, err := json.Marshal(m)
require.NoError(t, err)
var res Memory
require.NoError(t, json.Unmarshal(dat, &res))
require.Equal(t, uint32(123), res.GetMemory(8))
}
package mipsevm
import (
"debug/elf"
"fmt"
"sort"
)
type Symbol struct {
Name string `json:"name"`
Start uint32 `json:"start"`
Size uint32 `json:"size"`
}
type Metadata struct {
Symbols []Symbol `json:"symbols"`
}
func MakeMetadata(elfProgram *elf.File) (*Metadata, error) {
syms, err := elfProgram.Symbols()
if err != nil {
return nil, fmt.Errorf("failed to load symbols table: %w", err)
}
// Make sure the table is sorted, Go outputs mostly sorted data, except some internal functions
sort.Slice(syms, func(i, j int) bool {
return syms[i].Value < syms[j].Value
})
out := &Metadata{Symbols: make([]Symbol, len(syms))}
for i, s := range syms {
out.Symbols[i] = Symbol{Name: s.Name, Start: uint32(s.Value), Size: uint32(s.Size)}
}
return out, nil
}
func (m *Metadata) LookupSymbol(addr uint32) string {
if len(m.Symbols) == 0 {
return "!unknown"
}
// find first symbol with higher start. Or n if no such symbol exists
i := sort.Search(len(m.Symbols), func(i int) bool {
return m.Symbols[i].Start > addr
})
if i == 0 {
return "!start"
}
out := &m.Symbols[i-1]
if out.Start+out.Size < addr { // addr may be pointing to a gap between symbols
return "!gap"
}
return out.Name
}
func (m *Metadata) SymbolMatcher(name string) func(addr uint32) bool {
for _, s := range m.Symbols {
if s.Name == name {
start := s.Start
end := s.Start + s.Size
return func(addr uint32) bool {
return addr >= start && addr < end
}
}
}
return func(addr uint32) bool {
return false
}
}
// HexU32 to lazy-format integer attributes for logging
type HexU32 uint32
func (v HexU32) String() string {
return fmt.Sprintf("%08x", uint32(v))
}
func (v HexU32) MarshalText() ([]byte, error) {
return []byte(v.String()), nil
}
This diff is collapsed.
# OpenMIPS test vectors
Tests from https://github.com/grantae/OpenMIPS/tree/d606b35e9d5260aef20de2a58660c8303a681e9c/software/test/macro/tests
OpenMIPS is licensed LGPLv3 (as seen in the root of the repository), see [`LICENSE`](./LICENSE) file.
Note that some build-system files from 2014/2015 in that repository by the same author are marked as BSD licensed,
but the build-system is not used here.
Requires https://github.com/sergev/LiteBSD/releases/download/tools/gcc-4.8.1-mips-macosx.tgz to build
#!/usr/bin/env python3
import os
import sys
import tempfile
from capstone import *
from elftools.elf.elffile import ELFFile
md = Cs(CS_ARCH_MIPS, CS_MODE_32 + CS_MODE_BIG_ENDIAN)
def maketest(d, out):
with tempfile.NamedTemporaryFile() as nf:
print("building", d, "->", out)
# which mips is go
ret = os.system("mips-linux-gnu-as -defsym big_endian=1 -march=mips32r2 -o %s %s" % (nf.name, d))
assert(ret == 0)
nf.seek(0)
elffile = ELFFile(nf)
#print(elffile)
for sec in elffile.iter_sections():
#print(sec, sec.name, sec.data())
if sec.name == ".test":
with open(out, "wb") as f:
# jump to 0xdead0000 when done
#data = b"\x24\x1f\xde\xad\x00\x1f\xfc\x00" + sec.data()
data = sec.data()
for dd in md.disasm(data, 0):
print(dd)
f.write(data)
if __name__ == "__main__":
os.makedirs("/tmp/mips", exist_ok=True)
if len(sys.argv) > 2:
maketest(sys.argv[1], sys.argv[2])
else:
for d in os.listdir("test/"):
if not d.endswith(".asm"):
continue
maketest("test/"+d, "test/bin/"+(d.replace(".asm", ".bin")))
###############################################################################
# File : add.asm
# Project : MIPS32 MUX
# Author: : Grant Ayers (ayers@cs.stanford.edu)
#
# Standards/Formatting:
# MIPS gas, soft tab, 80 column
#
# Description:
# Test the functionality of the 'add' instruction.
#
###############################################################################
.section .test, "x"
.balign 4
.set noreorder
.global test
.ent test
test:
lui $s0, 0xbfff # Load the base address 0xbffffff0
ori $s0, 0xfff0
ori $s1, $0, 1 # Prepare the 'done' status
#### Test code start ####
lui $t0, 0xffff # A = 0xfffffffd (-3)
ori $t0, 0xfffd
ori $t1, $0, 0x3 # B = 0x3
add $t2, $t0, $t1 # C = A + B = 0
sltiu $v0, $t2, 1 # D = 1 if C == 0
#### Test code end ####
sw $v0, 8($s0) # Set the test result
sw $s1, 4($s0) # Set 'done'
$done:
jr $ra
nop
.end test
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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