Asterisc is an alternative fault-proof VM for OP Stack that proves execution of a RISC-V program with an interactive fraud-proof.
The interface of the Asterisc binary is essentially the same as Cannon for op-challenger compatibility; therefore, the binary commands implementation is based on Cannon.
Deploy/run this at your own risk, this is highly experimental software
Read the docs to get started.
Install the dependencies by running the following:
git submodule update --remote --init
# to build rvgo target
make build-rvgo
# to build rvsol target
make build-rvsol
Refer to the docs directory.
# Build op-program server-mode and RISCV-client binaries.
make op-program-riscv
make op-program
# Build the asterisc go binary
make build-rvgo
# Transform RISCV op-program client binary into first VM state.
# This outputs state.bin.gz (VM state) and meta.json (for debug symbols).
./rvgo/bin/asterisc load-elf --path=./rvsol/lib/optimism/op-program/bin-riscv/op-program-client-riscv.elf
# Run asterisc emulator (with example inputs)
# Note that the server-mode op-program command is passed into asterisc (after the --),
# it runs as sub-process to provide the pre-image data.
#
# Note:
# - The L2 RPC is an archive L2 node on OP MAINNET.
# - The L1 RPC is a non-archive RPC, also change `--l1.rpckind` to reflect the correct L1 RPC type.
# - The network flag is only suitable for specific networks(https://github.com/ethereum-optimism/superchain-registry/blob/main/chainList.json). If you are running on the devnet, please use '--l2.genesis' to supply a path to the L2 devnet genesis file.
./rvgo/bin/asterisc run \
--pprof.cpu \
--info-at '%10000000' \
--proof-at '=<TRACE_INDEX>' \
--stop-at '=<STOP_INDEX>' \
--snapshot-at '%1000000000' \
--input ./state.bin.gz \
-- \
./rvsol/lib/optimism/op-program/bin/op-program \
--network <network name> \
--l1 <L1_URL> \
--l2 <L2_URL> \
--l1.head <L1_HEAD> \
--l2.claim <L2_CLAIM> \
--l2.head <L2_HEAD> \
--l2.blocknumber <L2_BLOCK_NUMBER> \
--l2.outputroot <L2_OUTPUT_ROOT>
--datadir /tmp/fpp-database \
--log.format terminal \
--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 `./rvgo/bin/asterisc run --help` for more options
- Spawn local devnet by running
make devnet-up
atrvsol/lib/optimism
- Run asterisc deployment script
deploy.sh
atrvsol/scripts
Check deployments/README.md for more details.
The Asterisc has multiple tests running on the Github Actions and CircleCI pipelines.
Refer to the following commands to run individual tests on your machine.
Checks correctness of fast and slow mode. Also differential fuzz tests with EVM mode. To run locally,
(cd rvsol && forge build)
(cd tests/go-tests && make bin bin/simple bin/minimal)
# Run go unit tests
go test -v ./rvgo/... -coverprofile=coverage.out -coverpkg=./rvgo/...
# Run fuzz tests
make fuzz
Checks correctness of RISCV.sol
. To run locally,
(cd rvgo/scripts/go-ffi && go build)
cd rvsol
forge test -vvv --ffi
Checks that RISCV.sol
+ asterisc can be used as a fault proof VM for OP Stack. To run locally,
make devnet-allocs
cd op-e2e
go test -v ./faultproofs -timeout 3600s
Interactively different parties can agree on a common prefix of the execution trace states, and the execution first step where things are different.
Asterisc produces the commitments to the memory, register, CSR and VM state during the execution trace, and emulates the disputed step inside the EVM to determine the correct state after.
Asterisc consists of two parts:
rvgo
: Go implementation of Risc-V emulator with tooling- Fast-mode: Emulate 1 instruction per step, directly on a VM state
- Slow-mode: Emulate 1 instruction per step, on a VM state-oracle.
- Tooling: merkleize VM state, collect access-list of a slow-mode step, diff VM merkle-trees
rvsol
: Solidity/Yul mirror of Go Risc-V slow-mode step that runs with access-list as input
All VM register state is compact enough to be proven as a single preimage, with a binary merkle-tree for 64-bit memory.
The use of YUL / "solidity assembly" is very convenient because:
- it offers pretty
switch
statements - it uses function calls, no operators, that can be mirrored exactly in Go
- it preserves underflow/overflow behavior: this is a feature, not a bug, when emulating an ALU and registers.
- it operates on unsigned word-sized integers only: no typed data, just like registers don't have types.
- In Go it is typed
U64
andU256
for sanity, but it's alluint256
words in slow mode.
- In Go it is typed
Emulating a program on top of a merkleized state structure is expensive. When bisecting a program trace, you only need to produce a commitment to a few intermediate states, not all of them.
Note that slow mode and fast mode ALUs can be implemented exactly the same, just with different u64/u256 implementations. The slow mode matches the smart-contract behavior 1:1 and is useful for building the memory merkle-proof and having a Go mirror of the smart-contract behavior for testing/debugging in general.
RV32I
support - 32 bit base instruction setFENCE
,ECALL
,EBREAK
are hardwired to implement a minimal subset of systemcalls of the linux kernel
RV64I
supportRV32M
+RV64M
: Multiplication supportRV32A
+RV64A
: Atomics supportRV{32,64}{D,F,Q}
: no-op: No floating points support (since no IEEE754 determinism with rounding modes etc., nor worth the complexity)Zifencei
:FENCE.I
no-op: No need forFENCE.I
Zicsr
: no-op: some support for Control-and-status registers may come later though.Ztso
: no-op: no need for Total Store OrderingRVC
: compact instructions - work-in-progress, to support Rust compiler output.- other: revert with error code on unrecognized instructions
Where necessary, the non-supported operations are no-ops that allow execution of the standard Go runtime, with disabled GC.
The primary purpose of Asterisc is to run a Go program to fraud-proof an optimistic rollup.
This program can include the go-ethereum EVM for an EVM-equivalent rollup fraud proof, but may also be a totally different RISC-V program.
If you are one of these bespoke other programs to fraud-proof, please upstream fixes, but do not expect support if you diverge from the general design direction:
- Simplicity and security with minimalism
- First-class Golang support
- Mirror Solidity and Go step implementations
Asterisc may be usable to fraud-proof Rust programs or bespoke execution-environments in the future, but doing so should stay stupid-simple & not negatively affect its primary purpose.
This project originally started as an experimental spare-time project by @protolambda, in January 2023. This started with support of proving single-threaded Go programs, offchain in Go and onchain in Yul. The project helped inform a multi-proof system, a critical step towards Stage 2 rollup security.
The end-game (pre-ZK) is for Ethereum L2 optimistic rollups to embed multiple fraud-proof modules to function as a "committee": if one of the members is corrupted due to a bug/vulnerability, then the system as a whole stays stable without rollbacks or human intervention. So Asterisc aims to complement other fraud-proof systems, and not to replace them.
Asterisc has been transferred to the Optimism GitHub org in January 2024, to push forward the multi-proof OP-Stack vision with collective Optimism engineering effort.
Cannon, originally by geohot
and
now maintained by Optimism does the same thing, but differently.
Cannon is 32-bit, runs MIPS, does not support threads, and memory reads/writes happen in a single execution step.
Asterisc aims to be an alternative to this, and more future-compatible: RISC-V is gaining adoption unlike MIPS, and with 64 bit operations and concurrent but deterministic threads it may support more programs.
Cartesi has a much larger scope of RISC-V fraud-proving a full machine, including a lot more features. More features = more complexity however, which can form a risk.
Asterisc aims to be more minimal, simple and easy to audit. By running a single process, and hardwiring only the necessary systemcalls, the complexity of supporting all the RISC-V instruction set extensions and running a full linux kernel or multi-process system is avoided.
A fraud-proof of Go with a web-assembly runtime is already being developed by Arbitrum, although with a business-source license and with a transformation to "WAVM": not generic enough to use it for other purposes.
Asterisc aims to be open for anyone to use with MIT license.
MIT, see LICENSE
file.