diff --git a/Cargo.lock b/Cargo.lock index d788cf7a6..22cee150e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,6 +50,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + [[package]] name = "bytes" version = "1.5.0" @@ -68,6 +74,22 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "gimli" version = "0.28.1" @@ -96,6 +118,7 @@ dependencies = [ "anyhow", "cfg-if", "kona-common", + "tempfile", "tokio", ] @@ -114,6 +137,12 @@ dependencies = [ "spinning_top", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + [[package]] name = "lock_api" version = "0.4.11" @@ -147,7 +176,7 @@ checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -189,7 +218,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -222,7 +251,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -231,6 +260,19 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustix" +version = "0.38.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +dependencies = [ + "bitflags 2.4.2", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -259,7 +301,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -282,6 +324,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "tokio" version = "1.36.0" @@ -298,7 +352,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -330,7 +384,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -339,13 +402,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -354,38 +432,80 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/README.md b/README.md index cc58a8f7f..5f1c34071 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ What's Kona?OverviewContributing • - BookCredits

@@ -46,11 +45,16 @@ verify an [L2 output root][g-output-root] from the L1 inputs it was [derived fro **`client` / `host` SDK** - [`common`](./crates/common): A suite of utilities for developing `client` programs to be ran on top of Fault Proof VMs. +- [`preimage`](./crates/preimage): High level interfaces to the [`PreimageOracle`][fpp-specs] ABI ## Book The [book][book] contains a more in-depth overview of the project, contributor guidelines, tutorials for getting started with building your own programs, and a reference for the libraries and tools provided by Kona. +## Development Status + +`kona` is currently in active development, and is not yet ready for use in production. + ## Credits `kona` is inspired by the work of several teams, namely [OP Labs][op-labs] and other contributors' work on the [`op-program`][op-program] and [BadBoiLabs][bad-boi-labs]'s work on [Cannon-rs][badboi-cannon-rs]. @@ -60,12 +64,12 @@ The [book][book] contains a more in-depth overview of the project, contributor g [cannon]: https://github.com/ethereum-optimism/optimism/tree/develop/cannon [cannon-rs]: https://github.com/anton-rs/cannon-rs [badboi-cannon-rs]: https://github.com/BadBoiLabs/cannon-rs -[asterisc]: https://github.com/protolambda/asterisc -[fpp-specs]: https://github.com/ethereum-optimism/optimism/blob/develop/specs/fault-proof.md#fault-proof-program +[asterisc]: https://github.com/etheruem-optimism/asterisc +[fpp-specs]: https://specs.optimism.io/experimental/fault-proof/index.html [book]: https://ethereum-optimism.github.io/kona/ [op-labs]: https://github.com/ethereum-optimism [bad-boi-labs]: https://github.com/BadBoiLabs -[g-output-root]: https://github.com/ethereum-optimism/optimism/blob/develop/specs/glossary.md#l2-output-root -[g-derivation-pipeline]: https://github.com/ethereum-optimism/optimism/blob/develop/specs/derivation.md#l2-chain-derivation-pipeline -[g-fault-proof-vm]: https://github.com/ethereum-optimism/optimism/blob/develop/specs/fault-proof.md#fault-proof-vm -[g-preimage-oracle]: https://github.com/ethereum-optimism/optimism/blob/develop/specs/fault-proof.md#pre-image-oracle +[g-output-root]: https://specs.optimism.io/glossary.html#l2-output-root +[g-derivation-pipeline]: https://specs.optimism.io/protocol/derivation.html#l2-chain-derivation-pipeline +[g-fault-proof-vm]: https://specs.optimism.io/experimental/fault-proof/index.html#fault-proof-vm +[g-preimage-oracle]: https://specs.optimism.io/experimental/fault-proof/index.html#pre-image-oracle diff --git a/bin/server/.gitkeep b/bin/host/.gitkeep similarity index 100% rename from bin/server/.gitkeep rename to bin/host/.gitkeep diff --git a/book/src/fpp-dev/env.md b/book/src/fpp-dev/env.md index 225c9bcaa..734eae7af 100644 --- a/book/src/fpp-dev/env.md +++ b/book/src/fpp-dev/env.md @@ -12,11 +12,11 @@ communication with the `host` (the FPVM), and other implementation-specific feat While the program is running on top of the FPVM, it is considered to be in the `client` role, while the VM is in the `host` role. The only way for the `client` and `host` to communicate with one another is synchronously through the {{#template ../../templates/glossary-link.md root=../ text=Preimage ABI ref=preimage-abi}} ([specification][preimage-specs]). -In order for the `client` to read from the `host`, special syscalls are modified within the FPVM to allow the `client` to request preparation of and read foreign data. +In order for the `client` to read from the `host`, the `read` and `write` syscalls are modified within the FPVM to allow the `client` to request preparation of and read foreign data. ### Reading -When the `client` wants to read data from the `host`, it must first send a "hint" to the `host` through the hint file descriptor, which is a request for the `host` to prepare the data for reading. The `host` will then +When the `client` wants to read data from the `host`, it must first send a "hint" to the `host` through the hint file descriptor, which signals a request for the `host` to prepare the data for reading. The `host` will then prepare the data, and send a hint acknowledgement back to the `client`. The `client` can then read the data from the host through the designated file descriptor. The preparation step ("hinting") is an optimization that allows the `host` to know ahead of time the intents of the `client` and the data it requires for execution. This can allow diff --git a/book/src/fpp-dev/epilogue.md b/book/src/fpp-dev/epilogue.md index b1f2fe324..bbf39f7a0 100644 --- a/book/src/fpp-dev/epilogue.md +++ b/book/src/fpp-dev/epilogue.md @@ -1 +1,3 @@ # Epilogue + +_TODO_ diff --git a/book/src/fpp-dev/execution.md b/book/src/fpp-dev/execution.md index a2e753b62..aa2d849cb 100644 --- a/book/src/fpp-dev/execution.md +++ b/book/src/fpp-dev/execution.md @@ -1 +1,3 @@ # Execution + +_TODO_ diff --git a/book/src/fpp-dev/prologue.md b/book/src/fpp-dev/prologue.md index d87c68b1d..9d118e39d 100644 --- a/book/src/fpp-dev/prologue.md +++ b/book/src/fpp-dev/prologue.md @@ -1 +1,3 @@ # Prologue + +_TODO_ diff --git a/book/src/fpp-dev/targets.md b/book/src/fpp-dev/targets.md index 0a084040a..51ef9609c 100644 --- a/book/src/fpp-dev/targets.md +++ b/book/src/fpp-dev/targets.md @@ -72,6 +72,26 @@ Cannon is based off of the `mips32r2` target architecture, supporting 55 instruc | `Logical` | `xor` | Bitwise XOR. | | `Logical` | `xori` | Bitwise XOR immediate. | +### Syscalls + +| \$v0 | system call | \$a0 | \$a1 | \$a2 | Effect | +| ---- | ----------- | --------------- | ---------- | ------------ | -------------------------------------------------------------------------------------------------------------------- | +| 4090 | mmap | uint32 addr | uint32 len | 🚫 | Allocates a page from the heap. See [heap](#heap) for details. | +| 4045 | brk | 🚫 | 🚫 | 🚫 | Returns a fixed address for the program break at `0x40000000` | +| 4120 | clone | 🚫 | 🚫 | 🚫 | Returns 1 | +| 4246 | exit_group | uint8 exit_code | 🚫 | 🚫 | Sets the Exited and ExitCode states to `true` and `$a0` respectively. | +| 4003 | read | uint32 fd | char \*buf | uint32 count | Similar behavior as Linux/MIPS with support for unaligned reads. See [I/O](#io) for more details. | +| 4004 | write | uint32 fd | char \*buf | uint32 count | Similar behavior as Linux/MIPS with support for unaligned writes. See [I/O](#io) for more details. | +| 4055 | fcntl | uint32 fd | int32 cmd | 🚫 | Similar behavior as Linux/MIPS. Only the `F_GETFL` (3) cmd is supported. Sets errno to `0x16` for all other commands | + +For all of the above syscalls, an error is indicated by setting the return +register (`$v0`) to `0xFFFFFFFF` (-1) and `errno` (`$a3`) is set accordingly. +The VM must not modify any register other than `$v0` and `$a3` during syscall handling. +For unsupported syscalls, the VM must do nothing except to zero out the syscall return (`$v0`) +and errno (`$a3`) registers. + +Note that the above syscalls have identical syscall numbers and ABIs as Linux/MIPS. + ## Asterisc (RISC-V) Asterisc is based off of the `rv64gc` target architecture, which defines the following extensions: @@ -96,6 +116,6 @@ programs to directly invoke a select few syscalls: 1. `WRITE` - Write the passed buffer to the passed file descriptor. 1. `READ` - Read the specified number of bytes from the passed file descriptor. -[asterisc-syscalls]: https://github.com/protolambda/asterisc +[asterisc-syscalls]: https://github.com/ethereum-optimism/asterisc {{#include ../links.md}} diff --git a/book/src/links.md b/book/src/links.md index 7c92b955b..3fc289bd8 100644 --- a/book/src/links.md +++ b/book/src/links.md @@ -1,15 +1,17 @@ + [op-stack]: https://github.com/ethereum-optimism/optimism [op-program]: https://github.com/ethereum-optimism/optimism/tree/develop/op-program [cannon]: https://github.com/ethereum-optimism/optimism/tree/develop/cannon [cannon-rs]: https://github.com/anton-rs/cannon-rs -[asterisc]: https://github.com/protolambda/asterisc -[fp-specs]: https://github.com/ethereum-optimism/optimism/blob/develop/specs/fault-proof.md -[fpp-specs]: https://github.com/ethereum-optimism/optimism/blob/develop/specs/fault-proof.md#fault-proof-program -[preimage-specs]: https://github.com/ethereum-optimism/optimism/blob/develop/specs/fault-proof.md#pre-image-oracle -[cannon-specs]: https://github.com/ethereum-optimism/optimism/blob/develop/specs/cannon-fault-proof-vm.md +[asterisc]: https://github.com/ethereum-optimism/asterisc +[fp-specs]: https://specs.optimism.io/experimental/fault-proof/index.html +[fpp-specs]: https://specs.optimism.io/experimental/fault-proof/index.html#fault-proof-program +[preimage-specs]: https://specs.optimism.io/experimental/fault-proof/index.html#pre-image-oracle +[cannon-specs]: https://specs.optimism.io/experimental/fault-proof/cannon-fault-proof-vm.html#cannon-fault-proof-virtual-machine + [kona]: https://github.com/ethereum-optimism/kona [book]: https://ethereum-optimism.github.io/kona/ [issues]: https://github.com/ethereum-optimism/kona/issues @@ -17,5 +19,6 @@ [contributing]: https://github.com/ethereum-optimism/kona/tree/main/CONTRIBUTING.md + [op-labs]: https://github.com/ethereum-optimism [bad-boi-labs]: https://github.com/BadBoiLabs diff --git a/crates/common/README.md b/crates/common/README.md index 91c060317..18506d321 100644 --- a/crates/common/README.md +++ b/crates/common/README.md @@ -3,10 +3,6 @@ This library offers utilities for developing verifiable `client` executables that may run on top of Fault Proof Virtual Machine targets. -## Usage - -The primary offerings of this crate are the `io` module and the global allocator wrapper. - - The `alloc_heap` macro allows for statically allocating a heap of a certain size, and all `client` programs will need to run it if they require heap allocation. The `alloc` crate can be used for programs targeting any FPVM, but is optional. diff --git a/crates/common/src/asterisc/syscall.rs b/crates/common/src/asterisc/syscall.rs index 078de7543..d26f4eb34 100644 --- a/crates/common/src/asterisc/syscall.rs +++ b/crates/common/src/asterisc/syscall.rs @@ -1,3 +1,5 @@ +//! Derived from the syscalls crate +//! //! Unsafe system call interface for the `riscv64` target architecture. //! //! List of RISC-V system calls: diff --git a/crates/common/src/cannon/io.rs b/crates/common/src/cannon/io.rs index c0e63aadb..ee833e774 100644 --- a/crates/common/src/cannon/io.rs +++ b/crates/common/src/cannon/io.rs @@ -8,7 +8,7 @@ pub struct CannonIO; /// Relevant system call numbers for the `MIPS32rel1` target architecture. /// -/// See [Cannon System Call Specification](https://github.com/ethereum-optimism/optimism/blob/develop/specs/cannon-fault-proof-vm.md#syscalls) +/// See [Cannon System Call Specification](https://specs.optimism.io/experimental/fault-proof/cannon-fault-proof-vm.html#syscalls) /// /// **Note**: This is not an exhaustive list of system calls available to the `client` program, /// only the ones necessary for the [BasicKernelInterface] trait implementation. If an extension trait for diff --git a/crates/common/src/cannon/syscall.rs b/crates/common/src/cannon/syscall.rs index 05c4f3233..c17294b68 100644 --- a/crates/common/src/cannon/syscall.rs +++ b/crates/common/src/cannon/syscall.rs @@ -1,4 +1,4 @@ -//! Taken from the syscalls crate +//! Derived from the syscalls crate //! //! MIPS has the following registers: //! diff --git a/crates/preimage/Cargo.toml b/crates/preimage/Cargo.toml index 7248ba2bc..63594a998 100644 --- a/crates/preimage/Cargo.toml +++ b/crates/preimage/Cargo.toml @@ -18,3 +18,4 @@ kona-common = { path = "../common" } [dev-dependencies] tokio = { version = "1.36.0", features = ["full"] } +tempfile = "3.10.0" diff --git a/crates/preimage/README.md b/crates/preimage/README.md index 889f35b73..56007b844 100644 --- a/crates/preimage/README.md +++ b/crates/preimage/README.md @@ -4,8 +4,4 @@ This crate offers a high-level API over the [`Preimage Oracle`][preimage-abi-spe `client` programs, and the `host` handles are `async` colored to allow for the `host` programs to reach out to external data sources to populate the `Preimage Oracle`. -## Usage - -_TODO_ - [preimage-abi-spec]: https://specs.optimism.io/experimental/fault-proof/index.html#pre-image-oracle diff --git a/crates/preimage/src/hint.rs b/crates/preimage/src/hint.rs index 7d613aa7f..ff6342878 100644 --- a/crates/preimage/src/hint.rs +++ b/crates/preimage/src/hint.rs @@ -1,4 +1,4 @@ -use crate::PipeHandle; +use crate::{traits::HintWriterClient, PipeHandle}; use alloc::vec; use anyhow::Result; @@ -13,10 +13,12 @@ impl HintWriter { pub fn new(pipe_handle: PipeHandle) -> Self { Self { pipe_handle } } +} +impl HintWriterClient for HintWriter { /// Write a hint to the host. This will overwrite any existing hint in the pipe, and block until all data has been /// written. - pub fn write(&self, hint: &str) -> Result<()> { + fn write(&self, hint: &str) -> Result<()> { // Form the hint into a byte buffer. The format is a 4-byte big-endian length prefix followed by the hint // string. let mut hint_bytes = vec![0u8; hint.len() + 4]; diff --git a/crates/preimage/src/key.rs b/crates/preimage/src/key.rs index c20a3b7a6..28e9ed8eb 100644 --- a/crates/preimage/src/key.rs +++ b/crates/preimage/src/key.rs @@ -1,6 +1,6 @@ //! Contains the [PreimageKey] type, which is used to identify preimages that may be fetched from the preimage oracle. -/// +/// #[derive(Debug, Default, Clone, Copy, Eq, PartialEq)] #[repr(u8)] pub enum PreimageKeyType { @@ -43,6 +43,17 @@ impl PreimageKey { Self { data, key_type } } + /// Creates a new local [PreimageKey] from a 64-bit local identifier. The local identifier will be written into the + /// low-order 8 bytes of the big-endian 31-byte data field. + pub fn new_local(local_ident: u64) -> Self { + let mut data = [0u8; 31]; + data[23..].copy_from_slice(&local_ident.to_be_bytes()); + Self { + data, + key_type: PreimageKeyType::Local, + } + } + /// Returns the [PreimageKeyType] for the [PreimageKey]. pub fn key_type(&self) -> PreimageKeyType { self.key_type @@ -63,7 +74,7 @@ mod test { use super::*; #[test] - fn test_local_key() { + fn test_preimage_keys() { let types = [PreimageKeyType::Local, PreimageKeyType::Keccak256]; for key_type in types { diff --git a/crates/preimage/src/lib.rs b/crates/preimage/src/lib.rs index 48ab8d7b9..c7b7a6b9f 100644 --- a/crates/preimage/src/lib.rs +++ b/crates/preimage/src/lib.rs @@ -22,3 +22,6 @@ pub use hint::HintWriter; mod pipe; pub use pipe::PipeHandle; + +mod traits; +pub use traits::{HintWriterClient, PreimageOracleClient}; diff --git a/crates/preimage/src/oracle.rs b/crates/preimage/src/oracle.rs index ccb9cc002..e319825ab 100644 --- a/crates/preimage/src/oracle.rs +++ b/crates/preimage/src/oracle.rs @@ -1,4 +1,4 @@ -use crate::{PipeHandle, PreimageKey}; +use crate::{traits::PreimageOracleClient, PipeHandle, PreimageKey}; use alloc::vec::Vec; use anyhow::{bail, Result}; @@ -6,94 +6,57 @@ use anyhow::{bail, Result}; #[derive(Debug, Clone, Copy)] pub struct OracleReader { pipe_handle: PipeHandle, - key: Option, - length: usize, - cursor: usize, } impl OracleReader { /// Create a new [OracleReader] from a [PipeHandle]. pub fn new(pipe_handle: PipeHandle) -> Self { - Self { - pipe_handle, - key: None, - length: 0, - cursor: 0, - } - } - - /// Return the current key stored in the global oracle reader - pub fn key(&self) -> Option { - self.key + Self { pipe_handle } } - /// Return the length of the current pre-image - pub fn length(&self) -> usize { - self.length - } + /// Set the preimage key for the global oracle reader. This will overwrite any existing key, and block until the + /// host has prepared the preimage and responded with the length of the preimage. + fn write_key(&mut self, key: PreimageKey) -> Result { + // Write the key to the host so that it can prepare the preimage. + let key_bytes: [u8; 32] = key.into(); + self.pipe_handle.write(&key_bytes)?; - /// Current position of the read cursor within the current pre-image - pub fn cursor(&self) -> usize { - self.cursor + // Read the length prefix and reset the cursor. + let mut length_buffer = [0u8; 8]; + self.pipe_handle.read_exact(&mut length_buffer)?; + Ok(u64::from_be_bytes(length_buffer) as usize) } +} - /// Get the data corresponding to the currently set key from the host. Return the data in a new heap allocated `Vec` - /// - /// Internally this reads self.length bytes from the ReadPreimage file descriptor into a new heap allocated `Vec` and returns it. - /// This is a high level way to interact with the preimage oracle but may not be the best way if heap allocations are not desirable. - pub fn get(&mut self, key: PreimageKey) -> Result> { - self.set_key(key)?; - let mut data_buffer = alloc::vec![0; self.length]; +impl PreimageOracleClient for OracleReader { + /// Get the data corresponding to the currently set key from the host. Return the data in a new heap allocated + /// `Vec` + fn get(&mut self, key: PreimageKey) -> Result> { + let length = self.write_key(key)?; + let mut data_buffer = alloc::vec![0; length]; // Grab a read lock on the preimage pipe to read the data. - self.cursor += self.pipe_handle.read_exact(&mut data_buffer)? as usize; + self.pipe_handle.read_exact(&mut data_buffer)?; Ok(data_buffer) } /// Get the data corresponding to the currently set key from the host. Write the data into the provided buffer - /// - /// # Panics - /// This will panic if the size of the buffer is not equal to the size of the preimage as reported by the host - pub fn get_exact(&mut self, key: PreimageKey, buf: &mut [u8]) -> Result<()> { - self.set_key(key)?; + fn get_exact(&mut self, key: PreimageKey, buf: &mut [u8]) -> Result<()> { + // Write the key to the host and read the length of the preimage. + let length = self.write_key(key)?; // Ensure the buffer is the correct size. - if buf.len() != self.length { + if buf.len() != length { bail!( "Buffer size {} does not match preimage size {}", buf.len(), - self.length + length ); } - // Grab a read lock on the preimage pipe to read the data. - self.cursor += self.pipe_handle.read_exact(buf)? as usize; - - Ok(()) - } - - /// Set the preimage key for the global oracle reader. This will overwrite any existing key, and block until all - /// data has been read from the host. - /// - /// Internally this sends the 32 bytes of the key to the host by writing into the WritePreimage file descriptor. - /// This may require several writes as the host may only accept a few bytes at a time. Once 32 bytes have been written - /// successfully the key is considered set. If it fails to write 32 bytes it will return an error. - /// Once it has written the key it will read the first 8 bytes of the ReadPreimage file descriptor which is the length - /// encoded as a big endian u64. This is stored in the oracle reader along with the read cursor position. - fn set_key(&mut self, key: PreimageKey) -> Result<()> { - // Set the active key. - self.key = Some(key); - - // Write the key to the host so that it can prepare the preimage. - let key_bytes: [u8; 32] = key.into(); - self.pipe_handle.write(&key_bytes)?; + self.pipe_handle.read_exact(buf)?; - // Read the length prefix and reset the cursor. - let mut length_buffer = [0u8; 8]; - self.pipe_handle.read_exact(&mut length_buffer)?; - self.length = u64::from_be_bytes(length_buffer) as usize; - self.cursor = 0; Ok(()) } } @@ -105,11 +68,8 @@ mod test { use super::*; use crate::PreimageKeyType; use kona_common::FileDescriptor; - use std::{ - borrow::ToOwned, - fs::{File, OpenOptions}, - os::fd::AsRawFd, - }; + use std::{fs::File, os::fd::AsRawFd}; + use tempfile::tempfile; /// Test struct containing the [OracleReader] and a [PipeHandle] for the host, plus the open [File]s. The [File]s /// are stored in this struct so that they are not dropped until the end of the test. @@ -123,32 +83,12 @@ mod test { _write_file: File, } - impl Drop for ClientAndHost { - fn drop(&mut self) { - std::fs::remove_file("/tmp/read.hex").unwrap(); - std::fs::remove_file("/tmp/write.hex").unwrap(); - } - } - - /// Helper for opening a file with the correct options. - fn open_options() -> OpenOptions { - File::options() - .create(true) - .read(true) - .write(true) - .truncate(true) - .to_owned() - } - /// Helper for creating a new [OracleReader] and [PipeHandle] for testing. The file channel is over two temporary /// files. /// /// TODO: Swap host pipe handle to oracle writer once it exists. fn client_and_host() -> ClientAndHost { - let (read_file, write_file) = ( - open_options().open("/tmp/read.hex").unwrap(), - open_options().open("/tmp/write.hex").unwrap(), - ); + let (read_file, write_file) = (tempfile().unwrap(), tempfile().unwrap()); let (read_fd, write_fd) = ( FileDescriptor::Wildcard(read_file.as_raw_fd().try_into().unwrap()), FileDescriptor::Wildcard(write_file.as_raw_fd().try_into().unwrap()), @@ -173,14 +113,9 @@ mod test { let (mut oracle_reader, host_handle) = (sys.oracle_reader, sys.host_handle); let client = tokio::task::spawn(async move { - let mut buf = [0u8; 10]; oracle_reader - .get_exact( - PreimageKey::new([0u8; 32], PreimageKeyType::Keccak256), - &mut buf, - ) - .unwrap(); - buf + .get(PreimageKey::new([0u8; 32], PreimageKeyType::Keccak256)) + .unwrap() }); let host = tokio::task::spawn(async move { let mut length_and_data: [u8; 8 + 10] = [0u8; 8 + 10]; diff --git a/crates/preimage/src/traits.rs b/crates/preimage/src/traits.rs new file mode 100644 index 000000000..a65a95973 --- /dev/null +++ b/crates/preimage/src/traits.rs @@ -0,0 +1,20 @@ +use crate::PreimageKey; +use alloc::vec::Vec; +use anyhow::Result; + +/// A [PreimageOracleClient] is a high-level interface to read data from the host, keyed by a [PreimageKey]. +pub trait PreimageOracleClient { + /// Get the data corresponding to the currently set key from the host. Return the data in a new heap allocated + /// `Vec` + fn get(&mut self, key: PreimageKey) -> Result>; + + /// Get the data corresponding to the currently set key from the host. Write the data into the provided buffer + fn get_exact(&mut self, key: PreimageKey, buf: &mut [u8]) -> Result<()>; +} + +/// A [HintWriterClient] is a high-level interface to the hint pipe. It provides a way to write hints to the host. +pub trait HintWriterClient { + /// Write a hint to the host. This will overwrite any existing hint in the pipe, and block until all data has been + /// written. + fn write(&self, hint: &str) -> Result<()>; +} diff --git a/examples/simple-revm/Cargo.toml b/examples/simple-revm/Cargo.toml index e389e2935..331f1d95f 100644 --- a/examples/simple-revm/Cargo.toml +++ b/examples/simple-revm/Cargo.toml @@ -14,3 +14,5 @@ panic = "abort" [profile.release] panic = "abort" +codegen-units = 1 +lto = "fat" diff --git a/examples/simple-revm/src/main.rs b/examples/simple-revm/src/main.rs index 6dfd36280..d56dc975f 100644 --- a/examples/simple-revm/src/main.rs +++ b/examples/simple-revm/src/main.rs @@ -4,14 +4,16 @@ use alloc::vec::Vec; use anyhow::{anyhow, bail, Result}; use kona_common::{io, FileDescriptor}; -use kona_preimage::{HintWriter, OracleReader, PipeHandle, PreimageKey, PreimageKeyType}; +use kona_preimage::{ + HintWriter, HintWriterClient, OracleReader, PipeHandle, PreimageKey, PreimageOracleClient, +}; use revm::{ db::{CacheDB, EmptyDB}, primitives::{ - address, b256, hex, keccak256, AccountInfo, Address, Bytecode, ExecutionResult, Output, - TransactTo, B256, + address, hex, keccak256, AccountInfo, Address, Bytecode, Bytes, ExecutionResult, Output, + TransactTo, }, - Evm, + Database, Evm, }; extern crate alloc; @@ -21,9 +23,9 @@ const HEAP_SIZE: usize = 0xFFFFFFF; const EVM_ID_ADDRESS: Address = address!("dead00000000000000000000000000000000beef"); const SHA2_PRECOMPILE: Address = address!("0000000000000000000000000000000000000002"); -const INPUT_KEY: B256 = b256!("0000000000000000000000000000000000000000000000000000000000000000"); -const DIGEST_KEY: B256 = b256!("0000000000000000000000000000000000000000000000000000000000000001"); -const CODE_KEY: B256 = b256!("0000000000000000000000000000000000000000000000000000000000000002"); +const INPUT_IDENT: u64 = 0; +const DIGEST_IDENT: u64 = 1; +const CODE_IDENT: u64 = 2; static CLIENT_PREIMAGE_PIPE: PipeHandle = PipeHandle::new(FileDescriptor::PreimageRead, FileDescriptor::PreimageWrite); @@ -52,17 +54,19 @@ pub extern "C" fn _start() { } /// Boot the program and load bootstrap information. +#[inline] fn boot(oracle: &mut OracleReader) -> Result<([u8; 32], Vec)> { let digest = oracle - .get(PreimageKey::new(*DIGEST_KEY, PreimageKeyType::Local))? + .get(PreimageKey::new_local(DIGEST_IDENT))? .try_into() .map_err(|_| anyhow!("Failed to convert digest to [u8; 32]"))?; - let code = oracle.get(PreimageKey::new(*CODE_KEY, PreimageKeyType::Local))?; + let code = oracle.get(PreimageKey::new_local(CODE_IDENT))?; Ok((digest, code)) } /// Call the SHA-256 precompile and assert that the input and output match the expected values +#[inline] fn run_evm( oracle: &mut OracleReader, hint_writer: &HintWriter, @@ -72,7 +76,7 @@ fn run_evm( // Send a hint for the preimage of the digest to the host so that it can prepare the preimage. hint_writer.write(&alloc::format!("sha2-preimage {}", hex::encode(digest)))?; // Get the preimage of `digest` from the host. - let input = oracle.get(PreimageKey::new(*INPUT_KEY, PreimageKeyType::Local))?; + let input = oracle.get(PreimageKey::new_local(INPUT_IDENT))?; let mut cache_db = CacheDB::new(EmptyDB::default()); @@ -94,16 +98,7 @@ fn run_evm( .build(); // Call EVM identity contract. - let ref_tx = evm - .transact() - .map_err(|e| anyhow!("Failed state transition: {}", e))?; - let value = match ref_tx.result { - ExecutionResult::Success { - output: Output::Call(value), - .. - } => value, - e => bail!("EVM Execution failed: {:?}", e), - }; + let value = call_evm(&mut evm)?; if value.as_ref() != evm.context.evm.env.tx.data.as_ref() { bail!(alloc::format!( "Expected: {} | Got: {}\n", @@ -113,9 +108,31 @@ fn run_evm( } // Set up SHA2 precompile call - evm.context.evm.env.tx.transact_to = TransactTo::Call(SHA2_PRECOMPILE); - + let mut evm = evm + .modify() + .modify_tx_env(|tx_env| tx_env.transact_to = TransactTo::Call(SHA2_PRECOMPILE)) + .build(); // Call SHA2 precompile. + let value = call_evm(&mut evm)?; + if value.as_ref() != digest.as_ref() { + bail!(alloc::format!( + "Expected: {} | Got: {}\n", + hex::encode(digest), + hex::encode(value) + )); + } + + Ok(()) +} + +/// Performs a read-only call with the current transction environment and returns the output, +/// or an error if the transaction failed. +#[inline] +fn call_evm(evm: &mut Evm<'_, (), DB>) -> Result +where + DB: Database, + ::Error: core::fmt::Display, +{ let ref_tx = evm .transact() .map_err(|e| anyhow!("Failed state transition: {}", e))?; @@ -126,15 +143,7 @@ fn run_evm( } => value, e => bail!("EVM Execution failed: {:?}", e), }; - if value.as_ref() != digest.as_ref() { - bail!(alloc::format!( - "Expected: {} | Got: {}\n", - hex::encode(digest), - hex::encode(value) - )); - } - - Ok(()) + Ok(value) } #[panic_handler]